backport default tls-sprint
authorsylvain.thenault@logilab.fr
Mon, 04 May 2009 13:09:48 +0200
branchtls-sprint
changeset 1641 2c80b09d8d86
parent 1640 65b60f177eb1 (diff)
parent 1618 70c0c84e1c25 (current diff)
child 1642 12a98b17fb05
backport default
cwvreg.py
doc/book/en/B0012-schema-definition.en.txt
i18n/en.po
i18n/es.po
i18n/fr.po
vregistry.py
web/data/cubicweb.preferences.js
web/views/baseviews.py
web/views/cwproperties.py
web/views/igeocodable.py
web/views/management.py
web/views/startup.py
web/widgets.py
--- a/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -74,7 +74,7 @@
             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 
+        # should be emptied on commit/rollback of the server session / web
         # connection
         self.local_perm_cache = {}
 
@@ -82,14 +82,14 @@
         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, vreg=self.vreg, rset=rset):
-            return self.vreg.etype_class(etype)(self, rset, row, col)
+        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)
 
@@ -114,7 +114,7 @@
             return None
 
     # url generation methods ##################################################
-    
+
     def build_url(self, method, base_url=None, **kwargs):
         """return an absolute URL using params dictionary key/values as URL
         parameters. Values are automatically URL quoted, and the
@@ -130,7 +130,7 @@
         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"""
@@ -154,7 +154,7 @@
 
     def url_unquote(self, quoted):
         """returns a unicode unquoted string
-        
+
         decoding is based on `self.encoding` which is the encoding
         used in `url_quote`
         """
@@ -164,10 +164,10 @@
             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"""
@@ -197,31 +197,44 @@
         return False
 
     # abstract methods to override according to the web front-end #############
-    
+
     def base_url(self):
         """return the root url of the application"""
         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 = {'Eetype': 'EEType',
-                  'Ertype': 'ERType',
-                  'Efrdef': 'EFRDef',
-                  'Enfrdef': 'ENFRDef',
-                  'Econstraint': 'EConstraint',
-                  'Econstrainttype': 'EConstraintType',
-                  'Epermission': 'EPermission',
-                  'Egroup': 'EGroup',
-                  'Euser': 'EUser',
-                  'Eproperty': 'EProperty',
+# XXX 2.45 is allowing nicer entity type names, use this map for bw compat
+ETYPE_NAME_MAP = {# 3.2 migration
+                  'ECache': 'CWCache',
+                  'EUser': 'CWUser',
+                  'EGroup': 'CWGroup',
+                  'EProperty': 'CWProperty',
+                  'EFRDef': 'CWAttribute',
+                  'ENFRDef': 'CWRelation',
+                  'ERType': 'CWRType',
+                  'EEType': 'CWEType',
+                  'EConstraintType': 'CWConstraintType',
+                  'EConstraint': 'CWConstraint',
+                  'EPermission': 'CWPermission',
+                   # 2.45 migration
+                  'Eetype': 'CWEType',
+                  'Ertype': 'CWRType',
+                  'Efrdef': 'CWAttribute',
+                  'Enfrdef': 'CWRelation',
+                  'Econstraint': 'CWConstraint',
+                  'Econstrainttype': 'CWConstraintType',
+                  'Epermission': 'CWPermission',
+                  'Egroup': 'CWGroup',
+                  'Euser': 'CWUser',
+                  'Eproperty': 'CWProperty',
                   'Emailaddress': 'EmailAddress',
                   'Rqlexpression': 'RQLExpression',
                   'Trinfo': 'TrInfo',
@@ -255,7 +268,7 @@
                     'ezone': 'zone',
                     'i18ncontent': 'i18ncontent',
                     'svnfile': 'vcsfile',
-                    
+
                     'eclassschemes': 'keyword',
                     'eclassfolders': 'folder',
                     'eclasstags': 'tag',
@@ -269,11 +282,6 @@
                     'agueol': 'agueol',
                     'docaster': 'docaster',
                     'asteretud': 'asteretud',
-                    
-                    # XXX temp
-                    'keywords': 'keyword',
-                    'folders': 'folder',
-                    'tags': 'tag',
                     }
 
 def neg_role(role):
@@ -292,4 +300,4 @@
         return obj.target
     except AttributeError:
         return neg_role(obj.role)
-        
+
--- a/__pkginfo__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/__pkginfo__.py	Mon May 04 13:09:48 2009 +0200
@@ -6,7 +6,7 @@
 distname = "cubicweb"
 modname = "cubicweb"
 
-numversion = (3, 1, 5)
+numversion = (3, 2, 0)
 version = '.'.join(str(num) for num in numversion)
 
 license = 'LGPL v2'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/appobject.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,322 @@
+"""Base class for dynamically loaded objects manipulated in the web interface
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from datetime import datetime, timedelta
+
+from logilab.common.decorators import classproperty
+from logilab.common.deprecation import obsolete
+
+from rql.nodes import VariableRef, SubQuery
+from rql.stmts import Union, Select
+
+from cubicweb import Unauthorized, NoSelectableObject
+from cubicweb.vregistry import VObject, AndSelector
+from cubicweb.selectors import yes
+from cubicweb.utils import UStringIO, ustrftime
+
+ONESECOND = timedelta(0, 1, 0)
+
+class Cache(dict):
+    def __init__(self):
+        super(Cache, self).__init__()
+        self.cache_creation_date = None
+        self.latest_cache_lookup = datetime.now()
+
+CACHE_REGISTRY = {}
+
+class AppRsetObject(VObject):
+    """This is the base class for CubicWeb application objects
+    which are selected according to a request and result set.
+
+    Classes are kept in the vregistry and instantiation is done at selection
+    time.
+
+    At registration time, the following attributes are set on the class:
+    :vreg:
+      the application's registry
+    :schema:
+      the application's schema
+    :config:
+      the application's configuration
+
+    At instantiation time, the following attributes are set on the instance:
+    :req:
+      current request
+    :rset:
+      result set on which the object is applied
+    """
+    __select__ = yes()
+
+    @classmethod
+    def registered(cls, vreg):
+        super(AppRsetObject, cls).registered(vreg)
+        cls.vreg = vreg
+        cls.schema = vreg.schema
+        cls.config = vreg.config
+        cls.register_properties()
+        return cls
+
+    @classmethod
+    def vreg_initialization_completed(cls):
+        pass
+
+    @classmethod
+    def selected(cls, *args, **kwargs):
+        """by default web app objects are usually instantiated on
+        selection according to a request, a result set, and optional
+        row and col
+        """
+        assert len(args) <= 2
+        return cls(*args, **kwargs)
+
+    # 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`
+    #
+    # 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 = {}
+
+    @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):
+        return '%s.%s.%s' % (cls.__registry__, cls.id, propid)
+
+    @classproperty
+    @obsolete('use __select__ and & or | operators')
+    def __selectors__(cls):
+        selector = cls.__select__
+        if isinstance(selector, AndSelector):
+            return tuple(selector.selectors)
+        if not isinstance(selector, tuple):
+            selector = (selector,)
+        return selector
+
+    def __init__(self, req=None, rset=None, row=None, col=None, **extra):
+        super(AppRsetObject, self).__init__()
+        self.req = req
+        self.rset = rset
+        self.row = row
+        self.col = col
+        self.extra_kwargs = extra
+
+    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()
+            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
+
+    def propval(self, propid):
+        assert self.req
+        return self.req.property_value(self.propkey(propid))
+
+    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.select_component('navigation', self.req, 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
+
+    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
+
+    def view(self, __vid, rset=None, __fallback_vid=None, **kwargs):
+        """shortcut to self.vreg.render method avoiding to pass self.req"""
+        try:
+            view = self.vreg.select_view(__vid, self.req, rset, **kwargs)
+        except NoSelectableObject:
+            if __fallback_vid is None:
+                raise
+            view = self.vreg.select_view(__fallback_vid, self.req, rset, **kwargs)
+        return view.dispatch(**kwargs)
+
+    # url generation methods ##################################################
+
+    controller = 'view'
+
+    def build_url(self, method=None, **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.
+        """
+        # 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)
+
+    # various resources accessors #############################################
+
+    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
+
+    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
+        """
+        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 #######################################################
+
+    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()
+
+    def format_date(self, date, date_format=None, time=False):
+        """return a string for a date time according to application'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''
+
+    def format_time(self, time):
+        """return a string for a time according to application's
+        configuration
+        """
+        if time:
+            return ustrftime(time, self.req.property_value('ui.time-format'))
+        return u''
+
+    def format_float(self, num):
+        """return a string for floating point number according to application's
+        configuration
+        """
+        if num:
+            return self.req.property_value('ui.float-format') % num
+        return u''
+
+    # 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'))
+
+
+class AppObject(AppRsetObject):
+    """base class for application objects which are not selected
+    according to a result set, only by their identifier.
+
+    Those objects may not have req, rset and cursor set.
+    """
+
+    @classmethod
+    def selected(cls, *args, **kwargs):
+        """by default web app objects are usually instantiated on
+        selection
+        """
+        return cls(*args, **kwargs)
+
+    def __init__(self, req=None, rset=None, **kwargs):
+        self.req = req
+        self.rset = rset
+        self.__dict__.update(kwargs)
--- a/common/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -2,7 +2,7 @@
 hg stserver side and on the client side
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
--- a/common/appobject.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/appobject.py	Mon May 04 13:09:48 2009 +0200
@@ -1,463 +1,5 @@
-"""Base class for dynamically loaded objects manipulated in the web interface
-
-:organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
+"""pre 3.2 bw compat"""
+# pylint: disable-msg=W0614,W0401
 from warnings import warn
-
-from mx.DateTime import now, oneSecond
-from simplejson import dumps
-
-from logilab.common.deprecation import obsolete
-
-from rql.nodes import VariableRef, SubQuery
-from rql.stmts import Union, Select
-
-from cubicweb import Unauthorized
-from cubicweb.vregistry import VObject
-from cubicweb.common.utils import UStringIO
-from cubicweb.common.uilib import html_escape, ustrftime
-from cubicweb.common.registerers import yes_registerer, priority_registerer
-from cubicweb.common.selectors import yes
-
-_MARKER = object()
-
-
-class Cache(dict):    
-    def __init__(self):
-        super(Cache, self).__init__()
-        self.cache_creation_date = None
-        self.latest_cache_lookup = now()
-    
-CACHE_REGISTRY = {}
-
-class AppRsetObject(VObject):
-    """This is the base class for CubicWeb application objects
-    which are selected according to a request and result set.
-    
-    Classes are kept in the vregistry and instantiation is done at selection
-    time.
-    
-    At registration time, the following attributes are set on the class:
-    :vreg:
-      the application's registry
-    :schema:
-      the application's schema
-    :config:
-      the application's configuration
-
-    At instantiation time, the following attributes are set on the instance:
-    :req:
-      current request
-    :rset:
-      result set on which the object is applied
-    """
-
-    @classmethod
-    def registered(cls, vreg):
-        cls.vreg = vreg
-        cls.schema = vreg.schema
-        cls.config = vreg.config
-        cls.register_properties()
-        return cls
-
-    @classmethod
-    def selected(cls, req, rset, row=None, col=None, **kwargs):
-        """by default web app objects are usually instantiated on
-        selection according to a request, a result set, and optional
-        row and col
-        """
-        instance = cls(req, rset)
-        instance.row = row
-        instance.col = col
-        return instance
-
-    # Eproperties definition:
-    # key: id of the property (the actual EProperty 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 = {}
-    
-    @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):
-        return '%s.%s.%s' % (cls.__registry__, cls.id, propid)
-            
-        
-    def __init__(self, req, rset):
-        super(AppRsetObject, self).__init__()
-        self.req = req
-        self.rset = rset
-
-    @property
-    def cursor(self): # XXX deprecate in favor of req.cursor?
-        msg = '.cursor is deprecated, use req.execute (or req.cursor if necessary)'
-        warn(msg, DeprecationWarning, stacklevel=2)
-        return self.req.cursor
-        
-    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()
-            CACHE_REGISTRY[cachename] = cache
-        _now = now()
-        if _now > cache.latest_cache_lookup + oneSecond:
-            ecache = self.req.execute('Any C,T WHERE C is ECache, 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.empty()
-                cache.cache_creation_date = _now
-        return cache
-
-    def propval(self, propid):
-        assert self.req
-        return self.req.property_value(self.propkey(propid))
-
-    
-    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.select_component('navigation', self.req, 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
-    
-    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
-    
-    # url generation methods ##################################################
-    
-    controller = 'view'
-    
-    def build_url(self, method=None, **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.
-        """
-        # 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)
-
-    # various resources accessors #############################################
-
-    def etype_rset(self, etype, size=1):
-        """return a fake result set for a particular entity type"""
-        msg = '.etype_rset is deprecated, use req.etype_rset'
-        warn(msg, DeprecationWarning, stacklevel=2)
-        return self.req.etype_rset(etype, size=1)
-
-    def eid_rset(self, eid, etype=None):
-        """return a result set for the given eid"""
-        msg = '.eid_rset is deprecated, use req.eid_rset'
-        warn(msg, DeprecationWarning, stacklevel=2)
-        return self.req.eid_rset(eid, etype)
-    
-    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
-
-    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.req.add_js('cubicweb.ajax.js')
-        if nonify:
-            # XXX < 2.48.3 bw compat
-            warn('nonify argument is deprecated', DeprecationWarning, stacklevel=2)
-            _cb = cb
-            def cb(*args):
-                _cb(*args)
-        cbname = self.req.register_onetime_callback(cb, *args)
-        msg = dumps(msg or '') 
-        return "javascript:userCallbackThenReloadPage('%s', %s)" % (
-            cbname, msg)
-
-    # formating methods #######################################################
-
-    def tal_render(self, template, variables):
-        """render a precompiled page template with variables in the given
-        dictionary as context
-        """
-        from cubicweb.common.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()
-
-    def format_date(self, date, date_format=None, time=False):
-        """return a string for a mx date time according to application'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''
-
-    def format_time(self, time):
-        """return a string for a mx date time according to application's
-        configuration
-        """
-        if time:
-            return ustrftime(time, self.req.property_value('ui.time-format'))
-        return u''
-
-    def format_float(self, num):
-        """return a string for floating point number according to application's
-        configuration
-        """
-        if num:
-            return self.req.property_value('ui.float-format') % num
-        return u''
-    
-    # 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'))
-
-    # .accepts handling utilities #############################################
-    
-    accepts = ('Any',)
-
-    @classmethod
-    def accept_rset(cls, req, rset, row, col):
-        """apply the following rules:
-        * if row is None, return the sum of values returned by the method
-          for each entity's type in the result set. If any score is 0,
-          return 0.
-        * if row is specified, return the value returned by the method with
-          the entity's type of this row
-        """
-        if row is None:
-            score = 0
-            for etype in rset.column_types(0):
-                accepted = cls.accept(req.user, etype)
-                if not accepted:
-                    return 0
-                score += accepted
-            return score
-        return cls.accept(req.user, rset.description[row][col or 0])
-        
-    @classmethod
-    def accept(cls, user, etype):
-        """score etype, returning better score on exact match"""
-        if 'Any' in cls.accepts:
-            return 1
-        eschema = cls.schema.eschema(etype)
-        matching_types = [e.type for e in eschema.ancestors()]
-        matching_types.append(etype)
-        for index, basetype in enumerate(matching_types):
-            if basetype in cls.accepts:
-                return 2 + index
-        return 0
-    
-    # .rtype  handling utilities ##############################################
-    
-    @classmethod
-    def relation_possible(cls, etype):
-        """tell if a relation with etype entity is possible according to 
-        mixed class'.etype, .rtype and .target attributes
-
-        XXX should probably be moved out to a function
-        """
-        schema = cls.schema
-        rtype = cls.rtype
-        eschema = schema.eschema(etype)
-        if hasattr(cls, 'role'):
-            role = cls.role
-        elif cls.target == 'subject':
-            role = 'object'
-        else:
-            role = 'subject'
-        # check if this relation is possible according to the schema
-        try:
-            if role == 'object':
-                rschema = eschema.object_relation(rtype)
-            else:
-                rschema = eschema.subject_relation(rtype)
-        except KeyError:
-            return False            
-        if hasattr(cls, 'etype'):
-            letype = cls.etype
-            try:
-                if role == 'object':
-                    return etype in rschema.objects(letype)
-                else:
-                    return etype in rschema.subjects(letype)
-            except KeyError, ex:
-                return False
-        return True
-
-    
-    # XXX deprecated (since 2.43) ##########################
-    
-    @obsolete('use req.datadir_url')
-    def datadir_url(self):
-        """return url of the application's data directory"""
-        return self.req.datadir_url
-
-    @obsolete('use req.external_resource()')
-    def external_resource(self, rid, default=_MARKER):
-        return self.req.external_resource(rid, default)
-
-        
-class AppObject(AppRsetObject):
-    """base class for application objects which are not selected
-    according to a result set, only by their identifier.
-    
-    Those objects may not have req, rset and cursor set.
-    """
-    
-    @classmethod
-    def selected(cls, *args, **kwargs):
-        """by default web app objects are usually instantiated on
-        selection
-        """
-        return cls(*args, **kwargs)
-
-    def __init__(self, req=None, rset=None, **kwargs):
-        self.req = req
-        self.rset = rset
-        self.__dict__.update(kwargs)
-
-
-class ReloadableMixIn(object):
-    """simple mixin for reloadable parts of UI"""
-    
-    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.req.add_js('cubicweb.ajax.js')
-        if nonify:
-            _cb = cb
-            def cb(*args):
-                _cb(*args)
-        cbname = self.req.register_onetime_callback(cb, *args)
-        return self.build_js(cbname, html_escape(msg or ''))
-        
-    def build_update_js_call(self, cbname, msg):
-        rql = html_escape(self.rset.printable_rql())
-        return "javascript:userCallbackThenUpdateUI('%s', '%s', '%s', '%s', '%s', '%s')" % (
-            cbname, self.id, rql, msg, self.__registry__, self.div_id())
-    
-    def build_reload_js_call(self, cbname, msg):
-        return "javascript:userCallbackThenReloadPage('%s', '%s')" % (cbname, msg)
-
-    build_js = build_update_js_call # expect updatable component by default
-    
-    def div_id(self):
-        return ''
-
-
-class ComponentMixIn(ReloadableMixIn):
-    """simple mixin for component object"""
-    __registry__ = 'components'
-    __registerer__ = yes_registerer
-    __selectors__ = (yes,)
-    __select__ = classmethod(*__selectors__)
-
-    def div_class(self):
-        return '%s %s' % (self.propval('htmlclass'), self.id)
-
-    def div_id(self):
-        return '%sComponent' % self.id
-
-
-class Component(ComponentMixIn, AppObject):
-    """base class for non displayable components
-    """
-
-class SingletonComponent(Component):
-    """base class for non displayable unique components
-    """
-    __registerer__ = priority_registerer
+warn('moved to cubicweb.appobject', DeprecationWarning, stacklevel=2)
+from cubicweb.appobject import *
--- a/common/entity.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/entity.py	Mon May 04 13:09:48 2009 +0200
@@ -1,1106 +1,6 @@
-"""Base class for entity objects manipulated in clients
-
-:organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-from logilab.common import interface
-from logilab.common.compat import all
-from logilab.common.decorators import cached
-from logilab.mtconverter import TransformData, TransformError
-from rql.utils import rqlvar_maker
-
-from cubicweb import Unauthorized
-from cubicweb.vregistry import autoselectors
-from cubicweb.rset import ResultSet
-from cubicweb.common.appobject import AppRsetObject
-from cubicweb.common.registerers import id_registerer
-from cubicweb.common.selectors import yes
-from cubicweb.common.uilib import printable_value, html_escape, soup2xhtml
-from cubicweb.common.mixins import MI_REL_TRIGGERS
-from cubicweb.common.mttransforms import ENGINE
-from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint, bw_normalize_etype
-
-_marker = object()
-
-def greater_card(rschema, subjtypes, objtypes, index):
-    for subjtype in subjtypes:
-        for objtype in objtypes:
-            card = rschema.rproperty(subjtype, objtype, 'cardinality')[index]
-            if card in '+*':
-                return card
-    return '1'
-
-
-class RelationTags(object):
-    
-    MODE_TAGS = frozenset(('link', 'create'))
-    CATEGORY_TAGS = frozenset(('primary', 'secondary', 'generic', 'generated',
-                               'inlineview'))
-
-    def __init__(self, eclass, tagdefs):
-        # XXX if a rtag is redefined in a subclass,
-        # the rtag of the base class overwrite the rtag of the subclass
-        self.eclass = eclass
-        self._tagdefs = {}
-        for relation, tags in tagdefs.iteritems():
-            # tags must become a set
-            if isinstance(tags, basestring):
-                tags = set((tags,))
-            elif not isinstance(tags, set):
-                tags = set(tags)
-            # relation must become a 3-uple (rtype, targettype, role)
-            if isinstance(relation, basestring):
-                self._tagdefs[(relation, '*', 'subject')] = tags
-                self._tagdefs[(relation, '*', 'object')] = tags
-            elif len(relation) == 1: # useful ?
-                self._tagdefs[(relation[0], '*', 'subject')] = tags
-                self._tagdefs[(relation[0], '*', 'object')] = tags
-            elif len(relation) == 2:
-                rtype, ttype = relation
-                ttype = bw_normalize_etype(ttype) # XXX bw compat
-                self._tagdefs[rtype, ttype, 'subject'] = tags
-                self._tagdefs[rtype, ttype, 'object'] = tags
-            elif len(relation) == 3:
-                relation = list(relation)  # XXX bw compat
-                relation[1] = bw_normalize_etype(relation[1])
-                self._tagdefs[tuple(relation)] = tags
-            else:
-                raise ValueError('bad rtag definition (%r)' % (relation,))
-        
-
-    def __initialize__(self):
-        # eclass.[*]schema are only set when registering
-        self.schema = self.eclass.schema
-        eschema = self.eschema = self.eclass.e_schema
-        rtags = self._tagdefs
-        # expand wildcards in rtags and add automatic tags
-        for rschema, tschemas, role in sorted(eschema.relation_definitions(True)):
-            rtype = rschema.type
-            star_tags = rtags.pop((rtype, '*', role), set())
-            for tschema in tschemas:
-                tags = rtags.setdefault((rtype, tschema.type, role), set(star_tags))
-                if role == 'subject':
-                    X, Y = eschema, tschema
-                    card = rschema.rproperty(X, Y, 'cardinality')[0]
-                    composed = rschema.rproperty(X, Y, 'composite') == 'object'
-                else:
-                    X, Y = tschema, eschema
-                    card = rschema.rproperty(X, Y, 'cardinality')[1]
-                    composed = rschema.rproperty(X, Y, 'composite') == 'subject'
-                # set default category tags if needed
-                if not tags & self.CATEGORY_TAGS:
-                    if card in '1+':
-                        if not rschema.is_final() and composed:
-                            category = 'generated'
-                        elif rschema.is_final() and (
-                            rschema.type.endswith('_format')
-                            or rschema.type.endswith('_encoding')):
-                            category = 'generated'
-                        else:
-                            category = 'primary'
-                    elif rschema.is_final():
-                        if (rschema.type.endswith('_format')
-                            or rschema.type.endswith('_encoding')):
-                            category = 'generated'
-                        else:
-                            category = 'secondary'
-                    else: 
-                        category = 'generic'
-                    tags.add(category)
-                if not tags & self.MODE_TAGS:
-                    if card in '?1':
-                        # by default, suppose link mode if cardinality doesn't allow
-                        # more than one relation
-                        mode = 'link'
-                    elif rschema.rproperty(X, Y, 'composite') == role:
-                        # if self is composed of the target type, create mode
-                        mode = 'create'
-                    else:
-                        # link mode by default
-                        mode = 'link'
-                    tags.add(mode)
-
-    def _default_target(self, rschema, role='subject'):
-        eschema = self.eschema
-        if role == 'subject':
-            return eschema.subject_relation(rschema).objects(eschema)[0]
-        else:
-            return eschema.object_relation(rschema).subjects(eschema)[0]
-
-    # dict compat
-    def __getitem__(self, key):
-        if isinstance(key, basestring):
-            key = (key,)
-        return self.get_tags(*key)
-
-    __contains__ = __getitem__
-    
-    def get_tags(self, rtype, targettype=None, role='subject'):
-        rschema = self.schema.rschema(rtype)
-        if targettype is None:
-            tschema = self._default_target(rschema, role)
-        else:
-            tschema = self.schema.eschema(targettype)
-        return self._tagdefs[(rtype, tschema.type, role)]
-
-    __call__ = get_tags
-    
-    def get_mode(self, rtype, targettype=None, role='subject'):
-        # XXX: should we make an assertion on rtype not being final ?
-        # assert not rschema.is_final()
-        tags = self.get_tags(rtype, targettype, role)
-        # do not change the intersection order !
-        modes = tags & self.MODE_TAGS
-        assert len(modes) == 1
-        return modes.pop()
-
-    def get_category(self, rtype, targettype=None, role='subject'):
-        tags = self.get_tags(rtype, targettype, role)
-        categories = tags & self.CATEGORY_TAGS
-        assert len(categories) == 1
-        return categories.pop()
-
-    def is_inlined(self, rtype, targettype=None, role='subject'):
-        # return set(('primary', 'secondary')) & self.get_tags(rtype, targettype)
-        return 'inlineview' in self.get_tags(rtype, targettype, role)
-
-
-class metaentity(autoselectors):
-    """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
-        tagdefs = {}
-        widgets = {}
-        for base in bases:
-            tagdefs.update(getattr(base, '__rtags__', {}))
-            widgets.update(getattr(base, 'widgets', {}))
-        # update with the class' own rtgas
-        tagdefs.update(classdict.get('__rtags__', {}))
-        widgets.update(classdict.get('widgets', {}))
-        # XXX decide whether or not it's a good idea to replace __rtags__
-        #     good point: transparent support for inheritance levels >= 2
-        #     bad point: we loose the information of which tags are specific
-        #                to this entity class
-        classdict['__rtags__'] = tagdefs
-        classdict['widgets'] = widgets
-        eclass = super(metaentity, mcs).__new__(mcs, name, bases, classdict)
-        # adds the "rtags" attribute
-        eclass.rtags = RelationTags(eclass, tagdefs)
-        return eclass
-
-
-class Entity(AppRsetObject, dict):
-    """an entity instance has e_schema automagically set on
-    the class and instances has access to their issuing cursor.
-    
-    A property is set for each attribute and relation on each entity's type
-    class. Becare that among attributes, 'eid' is *NEITHER* stored in the
-    dict containment (which acts as a cache for other attributes dynamically
-    fetched)
-
-    :type e_schema: `cubicweb.schema.EntitySchema`
-    :ivar e_schema: the entity's schema
-
-    :type rest_var: str
-    :cvar rest_var: indicates which attribute should be used to build REST urls
-                    If None is specified, the first non-meta attribute will
-                    be used
-                    
-    :type skip_copy_for: list
-    :cvar skip_copy_for: a list of relations that should be skipped when copying
-                         this kind of entity. Note that some relations such
-                         as composite relations or relations that have '?1' as object
-                         cardinality
-    """
-    __metaclass__ = metaentity
-    __registry__ = 'etypes'
-    __registerer__ = id_registerer
-    __selectors__ = (yes,)
-    widgets = {}
-    id = None
-    e_schema = None
-    eid = None
-    rest_attr = None
-    skip_copy_for = ()
-
-    @classmethod
-    def registered(cls, registry):
-        """build class using descriptor at registration time"""
-        assert cls.id is not None
-        super(Entity, cls).registered(registry)
-        if cls.id != 'Any':
-            cls.__initialize__()
-        return cls
-                
-    MODE_TAGS = set(('link', 'create'))
-    CATEGORY_TAGS = set(('primary', 'secondary', 'generic', 'generated')) # , 'metadata'))
-    @classmethod
-    def __initialize__(cls):
-        """initialize a specific entity class by adding descriptors to access
-        entity type's attributes and relations
-        """
-        etype = cls.id
-        assert etype != 'Any', etype
-        cls.e_schema = eschema = cls.schema.eschema(etype)
-        for rschema, _ in eschema.attribute_definitions():
-            if rschema.type == 'eid':
-                continue
-            setattr(cls, rschema.type, Attribute(rschema.type))
-        mixins = []
-        for rschema, _, x in eschema.relation_definitions():
-            if (rschema, x) in MI_REL_TRIGGERS:
-                mixin = MI_REL_TRIGGERS[(rschema, x)]
-                if not (issubclass(cls, mixin) or mixin in mixins): # already mixed ?
-                    mixins.append(mixin)
-                for iface in getattr(mixin, '__implements__', ()):
-                    if not interface.implements(cls, iface):
-                        interface.extend(cls, iface)
-            if x == 'subject':
-                setattr(cls, rschema.type, SubjectRelation(rschema))
-            else:
-                attr = 'reverse_%s' % rschema.type
-                setattr(cls, attr, ObjectRelation(rschema))
-        if mixins:
-            cls.__bases__ = tuple(mixins + [p for p in cls.__bases__ if not p is object])
-            cls.debug('plugged %s mixins on %s', mixins, etype)
-        cls.rtags.__initialize__()
-    
-    @classmethod
-    def fetch_rql(cls, user, restriction=None, fetchattrs=None, mainvar='X',
-                  settype=True, ordermethod='fetch_order'):
-        """return a rql to fetch all entities of the class type"""
-        restrictions = restriction or []
-        if settype:
-            restrictions.append('%s is %s' % (mainvar, cls.id))
-        if fetchattrs is None:
-            fetchattrs = cls.fetch_attrs
-        selection = [mainvar]
-        orderby = []
-        # start from 26 to avoid possible conflicts with X
-        varmaker = rqlvar_maker(index=26)
-        cls._fetch_restrictions(mainvar, varmaker, fetchattrs, selection,
-                                orderby, restrictions, user, ordermethod)
-        rql = 'Any %s' % ','.join(selection)
-        if orderby:
-            rql +=  ' ORDERBY %s' % ','.join(orderby)
-        rql += ' WHERE %s' % ', '.join(restrictions)
-        return rql
-    
-    @classmethod
-    def _fetch_restrictions(cls, mainvar, varmaker, fetchattrs,
-                            selection, orderby, restrictions, user,
-                            ordermethod='fetch_order', visited=None):
-        eschema = cls.e_schema
-        if visited is None:
-            visited = set((eschema.type,))
-        elif eschema.type in visited:
-            # avoid infinite recursion
-            return
-        else:
-            visited.add(eschema.type)
-        _fetchattrs = []
-        for attr in fetchattrs:
-            try:
-                rschema = eschema.subject_relation(attr)
-            except KeyError:
-                cls.warning('skipping fetch_attr %s defined in %s (not found in schema)',
-                            attr, cls.id)
-                continue
-            if not user.matching_groups(rschema.get_groups('read')):
-                continue
-            var = varmaker.next()
-            selection.append(var)
-            restriction = '%s %s %s' % (mainvar, attr, var)
-            restrictions.append(restriction)
-            if not rschema.is_final():
-                # XXX this does not handle several destination types
-                desttype = rschema.objects(eschema.type)[0]
-                card = rschema.rproperty(eschema, desttype, 'cardinality')[0]
-                if card not in '?1':
-                    selection.pop()
-                    restrictions.pop()
-                    continue
-                if card == '?':
-                    restrictions[-1] += '?' # left outer join if not mandatory
-                destcls = cls.vreg.etype_class(desttype)
-                destcls._fetch_restrictions(var, varmaker, destcls.fetch_attrs,
-                                            selection, orderby, restrictions,
-                                            user, ordermethod, visited=visited)
-            orderterm = getattr(cls, ordermethod)(attr, var)
-            if orderterm:
-                orderby.append(orderterm)
-        return selection, orderby, restrictions
-
-    def __init__(self, req, rset, row=None, col=0):
-        AppRsetObject.__init__(self, req, rset)
-        dict.__init__(self)
-        self.row, self.col = row, col
-        self._related_cache = {}
-        if rset is not None:
-            self.eid = rset[row][col]
-        else:
-            self.eid = None
-        self._is_saved = True
-        
-    def __repr__(self):
-        return '<Entity %s %s %s at %s>' % (
-            self.e_schema, self.eid, self.keys(), id(self))
-
-    def __nonzero__(self):
-        return True
-
-    def __hash__(self):
-        return id(self)
-
-    def pre_add_hook(self):
-        """hook called by the repository before doing anything to add the entity
-        (before_add entity hooks have not been called yet). This give the
-        occasion to do weird stuff such as autocast (File -> Image for instance).
-        
-        This method must return the actual entity to be added.
-        """
-        return self
-    
-    def set_eid(self, eid):
-        self.eid = self['eid'] = eid
-
-    def has_eid(self):
-        """return True if the entity has an attributed eid (False
-        meaning that the entity has to be created
-        """
-        try:
-            int(self.eid)
-            return True
-        except (ValueError, TypeError):
-            return False
-
-    def is_saved(self):
-        """during entity creation, there is some time during which the entity
-        has an eid attributed though it's not saved (eg during before_add_entity
-        hooks). You can use this method to ensure the entity has an eid *and* is
-        saved in its source.
-        """
-        return self.has_eid() and self._is_saved
-    
-    @cached
-    def metainformation(self):
-        res = dict(zip(('type', 'source', 'extid'), self.req.describe(self.eid)))
-        res['source'] = self.req.source_defs()[res['source']]
-        return res
-
-    def clear_local_perm_cache(self, action):
-        for rqlexpr in self.e_schema.get_rqlexprs(action):
-            self.req.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None)
-
-    def check_perm(self, action):
-        self.e_schema.check_perm(self.req, action, self.eid)
-
-    def has_perm(self, action):
-        return self.e_schema.has_perm(self.req, action, self.eid)
-        
-    def view(self, vid, __registry='views', **kwargs):
-        """shortcut to apply a view on this entity"""
-        return self.vreg.render(__registry, vid, self.req, rset=self.rset,
-                                row=self.row, col=self.col, **kwargs)
-
-    def absolute_url(self, method=None, **kwargs):
-        """return an absolute url to view this entity"""
-        # in linksearch mode, we don't want external urls else selecting
-        # the object for use in the relation is tricky
-        # XXX search_state is web specific
-        if getattr(self.req, 'search_state', ('normal',))[0] == 'normal':
-            kwargs['base_url'] = self.metainformation()['source'].get('base-url')
-        if method is None or method == 'view':
-            kwargs['_restpath'] = self.rest_path()
-        else:
-            kwargs['rql'] = 'Any X WHERE X eid %s' % self.eid
-        return self.build_url(method, **kwargs)
-
-    def rest_path(self):
-        """returns a REST-like (relative) path for this entity"""
-        mainattr, needcheck = self._rest_attr_info()
-        etype = str(self.e_schema)
-        if mainattr == 'eid':
-            value = self.eid
-        else:
-            value = getattr(self, mainattr)
-            if value is None:
-                return '%s/eid/%s' % (etype.lower(), self.eid)
-        if needcheck:
-            # make sure url is not ambiguous
-            rql = 'Any COUNT(X) WHERE X is %s, X %s %%(value)s' % (etype, mainattr)
-            if value is not None:
-                nbresults = self.req.execute(rql, {'value' : value})[0][0]
-                # may an assertion that nbresults is not 0 would be a good idea
-                if nbresults != 1: # no ambiguity
-                    return '%s/eid/%s' % (etype.lower(), self.eid)
-        return '%s/%s' % (etype.lower(), self.req.url_quote(value))
-
-    @classmethod
-    def _rest_attr_info(cls):
-        mainattr, needcheck = 'eid', True
-        if cls.rest_attr:
-            mainattr = cls.rest_attr
-            needcheck = not cls.e_schema.has_unique_values(mainattr)
-        else:
-            for rschema in cls.e_schema.subject_relations():
-                if rschema.is_final() and rschema != 'eid' and cls.e_schema.has_unique_values(rschema):
-                    mainattr = str(rschema)
-                    needcheck = False
-                    break
-        if mainattr == 'eid':
-            needcheck = False
-        return mainattr, needcheck
-
-    @cached
-    def formatted_attrs(self):
-        """returns the list of attributes which have some format information
-        (i.e. rich text strings)
-        """
-        attrs = []
-        for rschema, attrschema in self.e_schema.attribute_definitions():
-            if attrschema.type == 'String' and self.has_format(rschema):
-                attrs.append(rschema.type)
-        return attrs
-        
-    def format(self, attr):
-        """return the mime type format for an attribute (if specified)"""
-        return getattr(self, '%s_format' % attr, None)
-    
-    def text_encoding(self, attr):
-        """return the text encoding for an attribute, default to site encoding
-        """
-        encoding = getattr(self, '%s_encoding' % attr, None)
-        return encoding or self.vreg.property_value('ui.encoding')
-
-    def has_format(self, attr):
-        """return true if this entity's schema has a format field for the given
-        attribute
-        """
-        return self.e_schema.has_subject_relation('%s_format' % attr)
-    
-    def has_text_encoding(self, attr):
-        """return true if this entity's schema has ab encoding field for the
-        given attribute
-        """
-        return self.e_schema.has_subject_relation('%s_encoding' % attr)
-
-    def printable_value(self, attr, value=_marker, attrtype=None,
-                        format='text/html', displaytime=True):
-        """return a displayable value (i.e. unicode string) which may contains
-        html tags
-        """
-        attr = str(attr)
-        if value is _marker:
-            value = getattr(self, attr)
-        if isinstance(value, basestring):
-            value = value.strip()
-        if value is None or value == '': # don't use "not", 0 is an acceptable value
-            return u''
-        if attrtype is None:
-            attrtype = self.e_schema.destination(attr)
-        props = self.e_schema.rproperties(attr)
-        if attrtype == 'String':
-            # internalinalized *and* formatted string such as schema
-            # description...
-            if props.get('internationalizable'):
-                value = self.req._(value)
-            attrformat = self.format(attr)
-            if attrformat:
-                return self.mtc_transform(value, attrformat, format,
-                                          self.req.encoding)
-        elif attrtype == 'Bytes':
-            attrformat = self.format(attr)
-            if attrformat:
-                try:
-                    encoding = getattr(self, '%s_encoding' % attr)
-                except AttributeError:
-                    encoding = self.req.encoding
-                return self.mtc_transform(value.getvalue(), attrformat, format,
-                                          encoding)
-            return u''
-        value = printable_value(self.req, attrtype, value, props, displaytime)
-        if format == 'text/html':
-            value = html_escape(value)
-        return value
-
-    def mtc_transform(self, data, format, target_format, encoding,
-                      _engine=ENGINE):
-        trdata = TransformData(data, format, encoding, appobject=self)
-        data = _engine.convert(trdata, target_format).decode()
-        if format == 'text/html':
-            data = soup2xhtml(data, self.req.encoding)                
-        return data
-    
-    # entity cloning ##########################################################
-
-    def copy_relations(self, ceid):
-        """copy relations of the object with the given eid on this object
-
-        By default meta and composite relations are skipped.
-        Overrides this if you want another behaviour
-        """
-        assert self.has_eid()
-        execute = self.req.execute
-        for rschema in self.e_schema.subject_relations():
-            if rschema.meta or rschema.is_final():
-                continue
-            # skip already defined relations
-            if getattr(self, rschema.type):
-                continue
-            if rschema.type in self.skip_copy_for:
-                continue
-            if rschema.type == 'in_state':
-                # if the workflow is defining an initial state (XXX AND we are
-                # not in the managers group? not done to be more consistent)
-                # don't try to copy in_state
-                if execute('Any S WHERE S state_of ET, ET initial_state S,'
-                           'ET name %(etype)s', {'etype': str(self.e_schema)}):
-                    continue
-            # skip composite relation
-            if self.e_schema.subjrproperty(rschema, 'composite'):
-                continue
-            # skip relation with card in ?1 else we either change the copied
-            # object (inlined relation) or inserting some inconsistency
-            if self.e_schema.subjrproperty(rschema, 'cardinality')[1] in '?1':
-                continue
-            rql = 'SET X %s V WHERE X eid %%(x)s, Y eid %%(y)s, Y %s V' % (
-                rschema.type, rschema.type)
-            execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
-            self.clear_related_cache(rschema.type, 'subject')
-        for rschema in self.e_schema.object_relations():
-            if rschema.meta:
-                continue
-            # skip already defined relations
-            if getattr(self, 'reverse_%s' % rschema.type):
-                continue
-            # skip composite relation
-            if self.e_schema.objrproperty(rschema, 'composite'):
-                continue
-            # skip relation with card in ?1 else we either change the copied
-            # object (inlined relation) or inserting some inconsistency
-            if self.e_schema.objrproperty(rschema, 'cardinality')[0] in '?1':
-                continue
-            rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % (
-                rschema.type, rschema.type)
-            execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
-            self.clear_related_cache(rschema.type, 'object')
-
-    # data fetching methods ###################################################
-
-    @cached
-    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,)])
-        return self.req.decorate_rset(rset)
-                       
-    def to_complete_relations(self):
-        """by default complete final relations to when calling .complete()"""
-        for rschema in self.e_schema.subject_relations():
-            if rschema.is_final():
-                continue
-            if len(rschema.objects(self.e_schema)) > 1:
-                # ambigous relations, the querier doesn't handle
-                # outer join correctly in this case
-                continue
-            if rschema.inlined:
-                matching_groups = self.req.user.matching_groups
-                if matching_groups(rschema.get_groups('read')) and \
-                   all(matching_groups(es.get_groups('read'))
-                       for es in rschema.objects(self.e_schema)):
-                    yield rschema, 'subject'
-                    
-    def to_complete_attributes(self, skip_bytes=True):
-        for rschema, attrschema in self.e_schema.attribute_definitions():
-            # skip binary data by default
-            if skip_bytes and attrschema.type == 'Bytes':
-                continue
-            attr = rschema.type
-            if attr == 'eid':
-                continue
-            # password retreival is blocked at the repository server level
-            if not self.req.user.matching_groups(rschema.get_groups('read')) \
-                   or attrschema.type == 'Password':
-                self[attr] = None
-                continue
-            yield attr
-            
-    def complete(self, attributes=None, skip_bytes=True):
-        """complete this entity by adding missing attributes (i.e. query the
-        repository to fill the entity)
-
-        :type skip_bytes: bool
-        :param skip_bytes:
-          if true, attribute of type Bytes won't be considered
-        """
-        assert self.has_eid()
-        varmaker = rqlvar_maker()
-        V = varmaker.next()
-        rql = ['WHERE %s eid %%(x)s' % V]
-        selected = []
-        for attr in (attributes or self.to_complete_attributes(skip_bytes)):
-            # if attribute already in entity, nothing to do
-            if self.has_key(attr):
-                continue
-            # case where attribute must be completed, but is not yet in entity
-            var = varmaker.next()
-            rql.append('%s %s %s' % (V, attr, var))
-            selected.append((attr, var))
-        # +1 since this doen't include the main variable
-        lastattr = len(selected) + 1
-        if attributes is None:
-            # fetch additional relations (restricted to 0..1 relations)
-            for rschema, role in self.to_complete_relations():
-                rtype = rschema.type
-                if self.relation_cached(rtype, role):
-                    continue
-                var = varmaker.next()
-                if role == 'subject':
-                    targettype = rschema.objects(self.e_schema)[0]
-                    card = rschema.rproperty(self.e_schema, targettype,
-                                             'cardinality')[0]
-                    if card == '1':
-                        rql.append('%s %s %s' % (V, rtype, var))
-                    else: # '?"
-                        rql.append('%s %s %s?' % (V, rtype, var))
-                else:
-                    targettype = rschema.subjects(self.e_schema)[1]
-                    card = rschema.rproperty(self.e_schema, targettype,
-                                             'cardinality')[1]
-                    if card == '1':
-                        rql.append('%s %s %s' % (var, rtype, V))
-                    else: # '?"
-                        rql.append('%s? %s %s' % (var, rtype, V))
-                assert card in '1?', '%s %s %s %s' % (self.e_schema, rtype,
-                                                      role, card)
-                selected.append(((rtype, role), var))
-        if selected:
-            # select V, we need it as the left most selected variable
-            # if some outer join are included to fetch inlined relations
-            rql = 'Any %s,%s %s' % (V, ','.join(var for attr, var in selected),
-                                    ','.join(rql))
-            execute = getattr(self.req, 'unsafe_execute', self.req.execute)
-            rset = execute(rql, {'x': self.eid}, 'x', build_descr=False)[0]
-            # handle attributes
-            for i in xrange(1, lastattr):
-                self[str(selected[i-1][0])] = rset[i]
-            # handle relations
-            for i in xrange(lastattr, len(rset)):
-                rtype, x = selected[i-1][0]
-                value = rset[i]
-                if value is None:
-                    rrset = ResultSet([], rql, {'x': self.eid})
-                    self.req.decorate_rset(rrset)
-                else:
-                    rrset = self.req.eid_rset(value)
-                self.set_related_cache(rtype, x, rrset)
-                
-    def get_value(self, name):
-        """get value for the attribute relation <name>, query the repository
-        to get the value if necessary.
-
-        :type name: str
-        :param name: name of the attribute to get
-        """
-        try:
-            value = self[name]
-        except KeyError:
-            if not self.is_saved():
-                return None
-            rql = "Any A WHERE X eid %%(x)s, X %s A" % name
-            # XXX should we really use unsafe_execute here??
-            execute = getattr(self.req, 'unsafe_execute', self.req.execute)
-            try:
-                rset = execute(rql, {'x': self.eid}, 'x')
-            except Unauthorized:
-                self[name] = value = None
-            else:
-                assert rset.rowcount <= 1, (self, rql, rset.rowcount)
-                try:
-                    self[name] = value = rset.rows[0][0]
-                except IndexError:
-                    # probably a multisource error
-                    self.critical("can't get value for attribute %s of entity with eid %s",
-                                  name, self.eid)
-                    if self.e_schema.destination(name) == 'String':
-                        self[name] = value = self.req._('unaccessible')
-                    else:
-                        self[name] = value = None
-        return value
-
-    def related(self, rtype, role='subject', limit=None, entities=False):
-        """returns a resultset of related entities
-        
-        :param role: is the role played by 'self' in the relation ('subject' or 'object')
-        :param limit: resultset's maximum size
-        :param entities: if True, the entites are returned; if False, a result set is returned
-        """
-        try:
-            return self.related_cache(rtype, role, entities, limit)
-        except KeyError:
-            pass
-        assert self.has_eid()
-        rql = self.related_rql(rtype, role)
-        rset = self.req.execute(rql, {'x': self.eid}, 'x')
-        self.set_related_cache(rtype, role, rset)
-        return self.related(rtype, role, limit, entities)
-
-    def related_rql(self, rtype, role='subject'):
-        rschema = self.schema[rtype]
-        if role == 'subject':
-            targettypes = rschema.objects(self.e_schema)
-            restriction = 'E eid %%(x)s, E %s X' % rtype
-            card = greater_card(rschema, (self.e_schema,), targettypes, 0)
-        else:
-            targettypes = rschema.subjects(self.e_schema)
-            restriction = 'E eid %%(x)s, X %s E' % rtype
-            card = greater_card(rschema, targettypes, (self.e_schema,), 1)
-        if len(targettypes) > 1:
-            fetchattrs_list = []
-            for ttype in targettypes:
-                etypecls = self.vreg.etype_class(ttype)
-                fetchattrs_list.append(set(etypecls.fetch_attrs))
-            fetchattrs = reduce(set.intersection, fetchattrs_list)
-            rql = etypecls.fetch_rql(self.req.user, [restriction], fetchattrs,
-                                     settype=False)
-        else:
-            etypecls = self.vreg.etype_class(targettypes[0])
-            rql = etypecls.fetch_rql(self.req.user, [restriction], settype=False)
-        # optimisation: remove ORDERBY if cardinality is 1 or ? (though
-        # greater_card return 1 for those both cases)
-        if card == '1':
-            if ' ORDERBY ' in rql:
-                rql = '%s WHERE %s' % (rql.split(' ORDERBY ', 1)[0],
-                                       rql.split(' WHERE ', 1)[1])
-        elif not ' ORDERBY ' in rql:
-            args = tuple(rql.split(' WHERE ', 1))
-            rql = '%s ORDERBY Z DESC WHERE X modification_date Z, %s' % args
-        return rql
-    
-    # generic vocabulary methods ##############################################
-
-    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
-        """
-        try:
-            vocabfunc = getattr(self, '%s_%s_vocabulary' % (role, rtype))
-        except AttributeError:
-            vocabfunc = getattr(self, '%s_relation_vocabulary' % role)
-        # NOTE: it is the responsibility of `vocabfunc` to sort the result
-        #       (direclty through RQL or via a python sort). This is also
-        #       important because `vocabfunc` might return a list with
-        #       couples (label, None) which act as separators. In these
-        #       cases, it doesn't make sense to sort results afterwards.
-        return vocabfunc(rtype, limit)
-            
-    def subject_relation_vocabulary(self, rtype, limit=None):
-        """defaut vocabulary method for the given relation, looking for
-        relation's object entities (i.e. self is the subject)
-        """
-        if isinstance(rtype, basestring):
-            rtype = self.schema.rschema(rtype)
-        done = None
-        assert not rtype.is_final(), rtype
-        if self.has_eid():
-            done = set(e.eid for e in getattr(self, str(rtype)))
-        result = []
-        rsetsize = None
-        for objtype in rtype.objects(self.e_schema):
-            if limit is not None:
-                rsetsize = limit - len(result)
-            result += self.relation_vocabulary(rtype, objtype, 'subject',
-                                               rsetsize, done)
-            if limit is not None and len(result) >= limit:
-                break
-        return result
-
-    def object_relation_vocabulary(self, rtype, limit=None):
-        """defaut vocabulary method for the given relation, looking for
-        relation's subject entities (i.e. self is the object)
-        """
-        if isinstance(rtype, basestring):
-            rtype = self.schema.rschema(rtype)
-        done = None
-        if self.has_eid():
-            done = set(e.eid for e in getattr(self, 'reverse_%s' % rtype))
-        result = []
-        rsetsize = None
-        for subjtype in rtype.subjects(self.e_schema):
-            if limit is not None:
-                rsetsize = limit - len(result)
-            result += self.relation_vocabulary(rtype, subjtype, 'object',
-                                               rsetsize, done)
-            if limit is not None and len(result) >= limit:
-                break
-        return result
-
-    def relation_vocabulary(self, rtype, targettype, role,
-                            limit=None, done=None):
-        if done is None:
-            done = set()
-        req = self.req
-        rset = self.unrelated(rtype, targettype, role, limit)
-        res = []
-        for entity in rset.entities():
-            if entity.eid in done:
-                continue
-            done.add(entity.eid)
-            res.append((entity.view('combobox'), entity.eid))
-        return res
-
-    def unrelated_rql(self, rtype, targettype, role, ordermethod=None,
-                      vocabconstraints=True):
-        """build a rql to fetch `targettype` entities unrelated to this entity
-        using (rtype, role) relation
-        """
-        ordermethod = ordermethod or 'fetch_unrelated_order'
-        if isinstance(rtype, basestring):
-            rtype = self.schema.rschema(rtype)
-        if role == 'subject':
-            evar, searchedvar = 'S', 'O'
-            subjtype, objtype = self.e_schema, targettype
-        else:
-            searchedvar, evar = 'S', 'O'
-            objtype, subjtype = self.e_schema, targettype
-        if self.has_eid():
-            restriction = ['NOT S %s O' % rtype, '%s eid %%(x)s' % evar]
-        else:
-            restriction = []
-        constraints = rtype.rproperty(subjtype, objtype, 'constraints')
-        if vocabconstraints:
-            # RQLConstraint is a subclass for RQLVocabularyConstraint, so they
-            # will be included as well
-            restriction += [cstr.restriction for cstr in constraints
-                            if isinstance(cstr, RQLVocabularyConstraint)]
-        else:
-            restriction += [cstr.restriction for cstr in constraints
-                            if isinstance(cstr, RQLConstraint)]
-        etypecls = self.vreg.etype_class(targettype)
-        rql = etypecls.fetch_rql(self.req.user, restriction,
-                                 mainvar=searchedvar, ordermethod=ordermethod)
-        # ensure we have an order defined
-        if not ' ORDERBY ' in rql:
-            before, after = rql.split(' WHERE ', 1)
-            rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after)
-        return rql
-    
-    def unrelated(self, rtype, targettype, role='subject', limit=None,
-                  ordermethod=None):
-        """return a result set of target type objects that may be related
-        by a given relation, with self as subject or object
-        """
-        rql = self.unrelated_rql(rtype, targettype, role, ordermethod)
-        if limit is not None:
-            before, after = rql.split(' WHERE ', 1)
-            rql = '%s LIMIT %s WHERE %s' % (before, limit, after)
-        if self.has_eid():
-            return self.req.execute(rql, {'x': self.eid})
-        return self.req.execute(rql)
-        
-    # relations cache handling ################################################
-    
-    def relation_cached(self, rtype, role):
-        """return true if the given relation is already cached on the instance
-        """
-        return '%s_%s' % (rtype, role) in self._related_cache
-    
-    def related_cache(self, rtype, role, entities=True, limit=None):
-        """return values for the given relation if it's cached on the instance,
-        else raise `KeyError`
-        """
-        res = self._related_cache['%s_%s' % (rtype, role)][entities]
-        if limit:
-            if entities:
-                res = res[:limit]
-            else:
-                res = res.limit(limit)
-        return res
-    
-    def set_related_cache(self, rtype, role, rset, col=0):
-        """set cached values for the given relation"""
-        if rset:
-            related = list(rset.entities(col))
-            rschema = self.schema.rschema(rtype)
-            if role == 'subject':
-                rcard = rschema.rproperty(self.e_schema, related[0].e_schema,
-                                          'cardinality')[1]
-                target = 'object'
-            else:
-                rcard = rschema.rproperty(related[0].e_schema, self.e_schema,
-                                          'cardinality')[0]
-                target = 'subject'
-            if rcard in '?1':
-                for rentity in related:
-                    rentity._related_cache['%s_%s' % (rtype, target)] = (self.as_rset(), [self])
-        else:
-            related = []
-        self._related_cache['%s_%s' % (rtype, role)] = (rset, related)
-        
-    def clear_related_cache(self, rtype=None, role=None):
-        """clear cached values for the given relation or the entire cache if
-        no relation is given
-        """
-        if rtype is None:
-            self._related_cache = {}
-        else:
-            assert role
-            self._related_cache.pop('%s_%s' % (rtype, role), None)
-        
-    # raw edition utilities ###################################################
-    
-    def set_attributes(self, **kwargs):
-        assert kwargs
-        relations = []
-        for key in kwargs:
-            relations.append('X %s %%(%s)s' % (key, key))
-        # update current local object
-        self.update(kwargs)
-        # and now update the database
-        kwargs['x'] = self.eid
-        self.req.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations),
-                         kwargs, 'x')
-            
-    def delete(self):
-        assert self.has_eid(), self.eid
-        self.req.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema,
-                         {'x': self.eid})
-    
-    # server side utilities ###################################################
-        
-    def set_defaults(self):
-        """set default values according to the schema"""
-        self._default_set = set()
-        for attr, value in self.e_schema.defaults():
-            if not self.has_key(attr):
-                self[str(attr)] = value
-                self._default_set.add(attr)
-
-    def check(self, creation=False):
-        """check this entity against its schema. Only final relation
-        are checked here, constraint on actual relations are checked in hooks
-        """
-        # necessary since eid is handled specifically and yams require it to be
-        # in the dictionary
-        if self.req is None:
-            _ = unicode
-        else:
-            _ = self.req._
-        self.e_schema.check(self, creation=creation, _=_)
-
-    def fti_containers(self, _done=None):
-        if _done is None:
-            _done = set()
-        _done.add(self.eid)
-        containers = tuple(self.e_schema.fulltext_containers())
-        if containers:
-            yielded = False
-            for rschema, target in containers:
-                if target == 'object':
-                    targets = getattr(self, rschema.type)
-                else:
-                    targets = getattr(self, 'reverse_%s' % rschema)
-                for entity in targets:
-                    if entity.eid in _done:
-                        continue
-                    for container in entity.fti_containers(_done):
-                        yield container
-                        yielded = True
-            if not yielded:
-                yield self
-        else:
-            yield self
-                    
-    def get_words(self):
-        """used by the full text indexer to get words to index
-
-        this method should only be used on the repository side since it depends
-        on the indexer package
-        
-        :rtype: list
-        :return: the list of indexable word of this entity
-        """
-        from indexer.query_objects import tokenize
-        words = []
-        for rschema in self.e_schema.indexable_attributes():
-            try:
-                value = self.printable_value(rschema, format='text/plain')
-            except TransformError, ex:
-                continue
-            except:
-                self.exception("can't add value of %s to text index for entity %s",
-                               rschema, self.eid)
-                continue
-            if value:
-                words += tokenize(value)
-        
-        for rschema, role in self.e_schema.fulltext_relations():
-            if role == 'subject':
-                for entity in getattr(self, rschema.type):
-                    words += entity.get_words()
-            else: # if role == 'object':
-                for entity in getattr(self, 'reverse_%s' % rschema.type):
-                    words += entity.get_words()
-        return words
-
-
-# attribute and relation descriptors ##########################################
-
-class Attribute(object):
-    """descriptor that controls schema attribute access"""
-
-    def __init__(self, attrname):
-        assert attrname != 'eid'
-        self._attrname = attrname
-
-    def __get__(self, eobj, eclass):
-        if eobj is None:
-            return self
-        return eobj.get_value(self._attrname)
-
-    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[self._attrname] = value
-
-
-class Relation(object):
-    """descriptor that controls schema relation access"""
-    _role = None # for pylint
-
-    def __init__(self, rschema):
-        self._rschema = rschema
-        self._rtype = rschema.type
-
-    def __get__(self, eobj, eclass):
-        if eobj is None:
-            raise AttributeError('%s cannot be only be accessed from instances'
-                                 % self._rtype)
-        return eobj.related(self._rtype, self._role, entities=True)
-    
-    def __set__(self, eobj, value):
-        raise NotImplementedError
-
-
-class SubjectRelation(Relation):
-    """descriptor that controls schema relation access"""
-    _role = 'subject'
-    
-class ObjectRelation(Relation):
-    """descriptor that controls schema relation access"""
-    _role = 'object'
-
-from logging import getLogger
-from cubicweb import set_log_methods
-set_log_methods(Entity, getLogger('cubicweb.entity'))
+"""pre 3.2 bw compat"""
+# pylint: disable-msg=W0614,W0401
+from warnings import warn
+warn('moved to cubicweb.entity', DeprecationWarning, stacklevel=2)
+from cubicweb.entity import *
+from cubicweb.entity import _marker
--- a/common/html4zope.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-# Author: David Goodger
-# Contact: goodger@users.sourceforge.net
-# Revision: $Revision: 1.2 $
-# Date: $Date: 2005-07-04 16:36:50 $
-# Copyright: This module has been placed in the public domain.
-
-"""
-Simple HyperText Markup Language document tree Writer.
-
-The output conforms to the HTML 4.01 Transitional DTD and to the Extensible
-HTML version 1.0 Transitional DTD (*almost* strict).  The output contains a
-minimum of formatting information.  A cascading style sheet ("default.css" by
-default) is required for proper viewing with a modern graphical browser.
-
-http://cvs.zope.org/Zope/lib/python/docutils/writers/Attic/html4zope.py?rev=1.1.2.2&only_with_tag=ajung-restructuredtext-integration-branch&content-type=text/vnd.viewcvs-markup
-"""
-
-__docformat__ = 'reStructuredText'
-
-from logilab.mtconverter import html_escape
-
-from docutils import nodes
-from docutils.writers.html4css1 import Writer as CSS1Writer
-from docutils.writers.html4css1 import HTMLTranslator as CSS1HTMLTranslator
-import os
-
-default_level = int(os.environ.get('STX_DEFAULT_LEVEL', 3))
-
-class Writer(CSS1Writer):
-    """css writer using our html translator"""
-    def __init__(self, base_url):
-        CSS1Writer.__init__(self)
-        self.translator_class = URLBinder(base_url, HTMLTranslator)
-
-    def apply_template(self):
-        """overriding this is necessary with docutils >= 0.5"""
-        return self.visitor.astext()
-
-class URLBinder:
-    def __init__(self, url, klass):
-        self.base_url = url
-        self.translator_class = HTMLTranslator
-        
-    def __call__(self, document):
-        translator = self.translator_class(document)
-        translator.base_url = self.base_url
-        return translator
-    
-class HTMLTranslator(CSS1HTMLTranslator):
-    """ReST tree to html translator"""
-
-    def astext(self):
-        """return the extracted html"""
-        return ''.join(self.body)
-    
-    def visit_title(self, node):
-        """Only 6 section levels are supported by HTML."""
-        if isinstance(node.parent, nodes.topic):
-            self.body.append(
-                  self.starttag(node, 'p', '', CLASS='topic-title'))
-            if node.parent.hasattr('id'):
-                self.body.append(
-                    self.starttag({}, 'a', '', name=node.parent['id']))
-                self.context.append('</a></p>\n')
-            else:
-                self.context.append('</p>\n')
-        elif self.section_level == 0:
-            # document title
-            self.head.append('<title>%s</title>\n'
-                             % self.encode(node.astext()))
-            self.body.append(self.starttag(node, 'h%d' % default_level, '',
-                                           CLASS='title'))
-            self.context.append('</h%d>\n' % default_level)
-        else:
-            self.body.append(
-                  self.starttag(node, 'h%s' % (
-                default_level+self.section_level-1), ''))
-            atts = {}
-            if node.hasattr('refid'):
-                atts['class'] = 'toc-backref'
-                atts['href'] = '%s#%s' % (self.base_url, node['refid'])
-            self.body.append(self.starttag({}, 'a', '', **atts))
-            self.context.append('</a></h%s>\n' % (
-                default_level+self.section_level-1))
-
-    def visit_subtitle(self, node):
-        """format a subtitle"""
-        if isinstance(node.parent, nodes.sidebar):
-            self.body.append(self.starttag(node, 'p', '',
-                                           CLASS='sidebar-subtitle'))
-            self.context.append('</p>\n')
-        else:
-            self.body.append(
-                  self.starttag(node, 'h%s' % (default_level+1), '',
-                                CLASS='subtitle'))
-            self.context.append('</h%s>\n' % (default_level+1))
-
-    def visit_document(self, node):
-        """syt: i don't want the enclosing <div class="document">"""
-    def depart_document(self, node):
-        """syt: i don't want the enclosing <div class="document">"""
-
-    def visit_reference(self, node):
-        """syt: i want absolute urls"""
-        if node.has_key('refuri'):
-            href = node['refuri']
-            if ( self.settings.cloak_email_addresses
-                 and href.startswith('mailto:')):
-                href = self.cloak_mailto(href)
-                self.in_mailto = 1
-        else:
-            assert node.has_key('refid'), \
-                   'References must have "refuri" or "refid" attribute.'
-            href = '%s#%s' % (self.base_url, node['refid'])
-        atts = {'href': href, 'class': 'reference'}
-        if not isinstance(node.parent, nodes.TextElement):
-            assert len(node) == 1 and isinstance(node[0], nodes.image)
-            atts['class'] += ' image-reference'
-        self.body.append(self.starttag(node, 'a', '', **atts))
-
-    ## override error messages to avoid XHTML problems ########################
-    def visit_problematic(self, node):
-        pass
-
-    def depart_problematic(self, node):
-        pass
-    
-    def visit_system_message(self, node):
-        backref_text = ''
-        if len(node['backrefs']):
-            backrefs = node['backrefs']
-            if len(backrefs) == 1:
-                backref_text = '; <em>backlink</em>'
-            else:
-                i = 1
-                backlinks = []
-                for backref in backrefs:
-                    backlinks.append(str(i))
-                    i += 1
-                backref_text = ('; <em>backlinks: %s</em>'
-                                % ', '.join(backlinks))
-        if node.hasattr('line'):
-            line = ', line %s' % node['line']
-        else:
-            line = ''
-        a_start = a_end = ''
-        error = u'System Message: %s%s/%s%s (%s %s)%s</p>\n' % (
-            a_start, node['type'], node['level'], a_end,
-            self.encode(node['source']), line, backref_text)
-        self.body.append(u'<div class="system-message"><b>ReST / HTML errors:</b>%s</div>' % html_escape(error))
-
-    def depart_system_message(self, node):
-        pass
--- a/common/i18n.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/i18n.py	Mon May 04 13:09:48 2009 +0200
@@ -1,14 +1,14 @@
 """Some i18n/gettext utilities.
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 import re
 import os
-from os.path import join, abspath, basename, splitext, exists
+from os.path import join, basename, splitext, exists
 from glob import glob
 
 from cubicweb.toolsutils import create_dir
--- a/common/mixins.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/mixins.py	Mon May 04 13:09:48 2009 +0200
@@ -7,9 +7,11 @@
 """
 __docformat__ = "restructuredtext en"
 
+from logilab.common.deprecation import obsolete
 from logilab.common.decorators import cached
 
-from cubicweb.common.selectors import implement_interface
+from cubicweb import typed_eid
+from cubicweb.selectors import implements
 from cubicweb.interfaces import IWorkflowable, IEmailable, ITree
 
 
@@ -24,10 +26,10 @@
     # XXX misnamed
     parent_target = 'subject'
     children_target = 'object'
-    
+
     def different_type_children(self, entities=True):
         """return children entities of different type as this entity.
-        
+
         according to the `entities` parameter, return entity objects or the
         equivalent result set
         """
@@ -39,7 +41,7 @@
 
     def same_type_children(self, entities=True):
         """return children entities of the same type as this entity.
-        
+
         according to the `entities` parameter, return entity objects or the
         equivalent result set
         """
@@ -48,7 +50,7 @@
         if entities:
             return [e for e in res if e.e_schema == self.e_schema]
         return res.filtered_rset(lambda x: x.e_schema == self.e_schema, self.col)
-    
+
     def iterchildren(self, _done=None):
         if _done is None:
             _done = set()
@@ -72,7 +74,7 @@
                     yield entity
             except AttributeError:
                 pass
-    
+
     @cached
     def path(self):
         """returns the list of eids from the root object to this object"""
@@ -94,7 +96,7 @@
 
         path.reverse()
         return path
-    
+
     def iterparents(self):
         def _uptoroot(self):
             curr = self
@@ -108,7 +110,7 @@
     def notification_references(self, view):
         """used to control References field of email send on notification
         for this entity. `view` is the notification view.
-        
+
         Should return a list of eids which can be used to generate message ids
         of previously sent email
         """
@@ -140,7 +142,7 @@
 
     def children_rql(self):
         return self.related_rql(self.tree_attribute, self.children_target)
-    
+
     def __iter__(self):
         return self.iterchildren()
 
@@ -161,7 +163,7 @@
     relation (which implies supporting 'wf_info_for' as well)
     """
     __implements__ = (IWorkflowable,)
-    
+
     @property
     def state(self):
         try:
@@ -169,7 +171,7 @@
         except IndexError:
             self.warning('entity %s has no state', self)
             return None
-    
+
     @property
     def displayable_state(self):
         return self.req._(self.state)
@@ -180,14 +182,14 @@
         if rset:
             return rset.get_entity(0, 0)
         return None
-    
+
     def wf_transition(self, trname):
         rset = self.req.execute('Any T, TN WHERE T name TN, T name %(n)s, T transition_of E, E name %(e)s',
                                 {'n': trname, 'e': str(self.e_schema)})
         if rset:
             return rset.get_entity(0, 0)
         return None
-    
+
     def change_state(self, stateeid, trcomment=None, trcommentformat=None):
         """change the entity's state according to a state defined in given
         parameters
@@ -199,7 +201,7 @@
             self.req.set_shared_data('trcommentformat', trcommentformat)
         self.req.execute('SET X in_state S WHERE X eid %(x)s, S eid %(s)s',
                          {'x': self.eid, 's': stateeid}, 'x')
-    
+
     def can_pass_transition(self, trname):
         """return the Transition instance if the current user can pass the
         transition with the given name, else None
@@ -213,42 +215,30 @@
         for tr in rset.entities():
             if tr.may_be_passed(self.eid, stateeid):
                 return tr
-    
+
     def latest_trinfo(self):
         """return the latest transition information for this entity"""
         return self.reverse_wf_info_for[-1]
-            
-    # specific vocabulary methods #############################################
 
-    def subject_in_state_vocabulary(self, rschema, limit=None):
-        """vocabulary method for the in_state relation, looking for
-        relation's object entities (i.e. self is the subject) according
-        to initial_state, state_of and next_state relation
-        """
-        if not self.has_eid() or not self.in_state:
-            # get the initial state
-            rql = 'Any S where S state_of ET, ET name %(etype)s, ET initial_state S'
-            rset = self.req.execute(rql, {'etype': str(self.e_schema)})
-            if rset:
-                return [(rset.get_entity(0, 0).view('combobox'), rset[0][0])]
-            return []
-        results = []
-        for tr in self.in_state[0].transitions(self):
-            state = tr.destination_state[0]
-            results.append((state.view('combobox'), state.eid))
-        return sorted(results)
-            
     # __method methods ########################################################
-    
+
     def set_state(self, params=None):
         """change the entity's state according to a state defined in given
         parameters, used to be called using __method controler facility
         """
         params = params or self.req.form
-        self.change_state(int(params.pop('state')), params.get('trcomment'),
+        self.change_state(typed_eid(params.pop('state')),
+                          params.get('trcomment'),
                           params.get('trcommentformat'))
         self.req.set_message(self.req._('__msg state changed'))
 
+    # specific vocabulary methods #############################################
+
+    @obsolete('use EntityFieldsForm.subject_in_state_vocabulary')
+    def subject_in_state_vocabulary(self, rschema, limit=None):
+        from cubicweb.web.form import EntityFieldsForm
+        return EntityFieldsForm(self.req, None, entity=self).subject_in_state_vocabulary(rschema, limit)
+
 
 
 class EmailableMixIn(object):
@@ -259,7 +249,7 @@
     primary_email / use_email scheme
     """
     __implements__ = (IEmailable,)
-    
+
     def get_email(self):
         if getattr(self, 'primary_email', None):
             return self.primary_email[0].address
@@ -281,14 +271,14 @@
     def as_email_context(self):
         """returns the dictionary as used by the sendmail controller to
         build email bodies.
-        
+
         NOTE: the dictionary keys should match the list returned by the
         `allowed_massmail_keys` method.
         """
         return dict( (attr, getattr(self, attr)) for attr in self.allowed_massmail_keys() )
 
 
-    
+
 MI_REL_TRIGGERS = {
     ('in_state',    'subject'): WorkflowableMixIn,
     ('primary_email',   'subject'): EmailableMixIn,
@@ -316,14 +306,13 @@
     """a recursive tree view"""
     id = 'tree'
     item_vid = 'treeitem'
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (ITree,)
+    __select__ = implements(ITree)
 
     def call(self, done=None, **kwargs):
         if done is None:
             done = set()
         super(TreeViewMixIn, self).call(done=done, **kwargs)
-            
+
     def cell_call(self, row, col=0, vid=None, done=None, **kwargs):
         done, entity = _done_init(done, self, row, col)
         if done is None:
@@ -352,7 +341,7 @@
         self.w(u'<div class="pathbar">')
         super(TreePathMixIn, self).call(**kwargs)
         self.w(u'</div>')
-        
+
     def cell_call(self, row, col=0, vid=None, done=None, **kwargs):
         done, entity = _done_init(done, self, row, col)
         if done is None:
@@ -394,7 +383,7 @@
 
     def in_progress(self):
         raise NotImplementedError()
-    
+
     def progress(self):
         try:
             return 100. * self.done / self.revised_cost
--- a/common/mttransforms.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/mttransforms.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """mime type transformation engine for cubicweb, based on mtconverter
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -11,10 +11,10 @@
 from logilab.mtconverter.engine import TransformEngine
 from logilab.mtconverter.transform import Transform
 from logilab.mtconverter import (register_base_transforms,
-                                 register_pil_transforms, 
+                                 register_pil_transforms,
                                  register_pygments_transforms)
 
-from cubicweb.common.uilib import rest_publish, html_publish, remove_html_tags
+from cubicweb.common.uilib import rest_publish, html_publish
 
 HTML_MIMETYPES = ('text/html', 'text/xhtml', 'application/xhtml+xml')
 
@@ -32,15 +32,6 @@
     def _convert(self, trdata):
         return html_publish(trdata.appobject, trdata.data)
 
-class ept_to_html(Transform):
-    inputs = ('text/cubicweb-page-template',)
-    output = 'text/html'
-    output_encoding = 'utf-8'
-    def _convert(self, trdata):
-        from cubicweb.common.tal import compile_template
-        value = trdata.encode(self.output_encoding)
-        return trdata.appobject.tal_render(compile_template(value), {})
-
 
 # Instantiate and configure the transformation engine
 
@@ -49,13 +40,32 @@
 ENGINE = TransformEngine()
 ENGINE.add_transform(rest_to_html())
 ENGINE.add_transform(html_to_html())
-ENGINE.add_transform(ept_to_html())
+
+try:
+    from cubicweb.ext.tal import compile_template
+except ImportError:
+    HAS_TAL = False
+    from cubicweb.schema import FormatConstraint
+    FormatConstraint.need_perm_formats.remove('text/cubicweb-page-template')
+
+else:
+    HAS_TAL = True
+
+    class ept_to_html(Transform):
+        inputs = ('text/cubicweb-page-template',)
+        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), {})
+
+    ENGINE.add_transform(ept_to_html())
 
 if register_pil_transforms(ENGINE, verb=False):
     HAS_PIL_TRANSFORMS = True
 else:
     HAS_PIL_TRANSFORMS = False
-    
+
 try:
     from logilab.mtconverter.transforms import pygmentstransforms
     for mt in ('text/plain',) + HTML_MIMETYPES:
@@ -74,9 +84,9 @@
             return origconvert(self, trdata)
         cls._convert = _convert
     patch_convert(pygmentstransforms.PygmentsHTMLTransform)
-    
+
     HAS_PYGMENTS_TRANSFORMS = True
 except ImportError:
     HAS_PYGMENTS_TRANSFORMS = False
-    
+
 register_base_transforms(ENGINE, verb=False)
--- a/common/registerers.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,207 +0,0 @@
-"""This file contains some basic registerers required by application objects
-registry to handle registration at startup time.
-
-A registerer is responsible to tell if an object should be registered according
-to the application's schema or to already registered object
-
-:organization: Logilab
-:copyright: 2006-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-from cubicweb.vregistry import registerer
-
-
-def _accepts_interfaces(obj):
-    return sorted(getattr(obj, 'accepts_interfaces', ()))
-
-
-class yes_registerer(registerer):
-    """register without any other action"""
-    def do_it_yourself(self, registered):
-        return self.vobject
-
-class priority_registerer(registerer):
-    """systematically kick previous registered class and register the
-    wrapped class (based on the fact that directory containing vobjects
-    are loaded from the most generic to the most specific).
-
-    This is usually for templates or startup views where we want to
-    keep only the latest in the load path
-    """
-    def do_it_yourself(self, registered):
-        if registered:
-            if len(registered) > 1:
-                self.warning('priority_registerer found more than one registered objects '
-                             '(registerer monkey patch ?)')
-            for regobj in registered[:]:
-                self.kick(registered, regobj, 'more than one object')
-        return self.vobject
-    
-    def remove_equivalents(self, registered):
-        for _obj in registered[:]:
-            if self.equivalent(_obj):
-                self.kick(registered, _obj, 'equivalent to %s'%_obj)
-                break
-            
-    def remove_all_equivalents(self, registered):
-        for _obj in registered[:]:
-            if _obj is self.vobject:
-                continue
-            if self.equivalent(_obj):
-                self.kick(registered, _obj, 'all are equivalent')
-
-    def equivalent(self, other):
-        raise NotImplementedError(self, self.vobject)
-
-
-class kick_registerer(registerer):
-    """systematically kick previous registered class and don't register the
-    wrapped class. This is temporarily used to discard library object registrable
-    but that we don't want to use
-    """
-    def do_it_yourself(self, registered):
-        if registered:
-            self.kick(registered, registered[-1], 'diy')
-        return 
-    
-
-class accepts_registerer(priority_registerer):
-    """register according to the .accepts attribute of the wrapped
-    class, which should be a tuple refering some entity's types
-
-    * if no type is defined the application'schema, skip the wrapped
-      class
-    * if the class defines a requires attribute, each entity type defined
-      in the requires list must be in the schema
-    * if an object previously registered has equivalent .accepts
-      attribute, kick it out
-    * register
-    """
-    def do_it_yourself(self, registered):
-        # if object is accepting interface, we have register it now and
-        # remove it latter if no object is implementing accepted interfaces
-        if _accepts_interfaces(self.vobject):
-            return self.vobject
-        if not 'Any' in self.vobject.accepts:
-            for ertype in self.vobject.accepts:
-                if ertype in self.schema:
-                    break
-            else:
-                self.skip()
-                return None
-        for required in getattr(self.vobject, 'requires', ()):
-            if required not in self.schema:
-                self.skip()
-                return
-        self.remove_equivalents(registered)
-        return self.vobject
-    
-    def equivalent(self, other):
-        if _accepts_interfaces(self.vobject) != _accepts_interfaces(other):
-            return False
-        if getattr(self.vobject, 'require_groups', ()) != getattr(other, 'require_groups', ()):
-            return False
-        try:
-            newaccepts = list(other.accepts)
-            for etype in self.vobject.accepts:
-                try:
-                    newaccepts.remove(etype)
-                except ValueError:
-                    continue
-            if newaccepts:
-                other.accepts = tuple(newaccepts)
-                return False
-            return True
-        except AttributeError:
-            return False
-
-
-class id_registerer(priority_registerer):
-    """register according to the "id" attribute of the wrapped class,
-    refering to an entity type.
-    
-    * if the type is not Any and is not defined the application'schema,
-      skip the wrapped class
-    * if an object previously registered has the same .id attribute,
-      kick it out
-    * register
-    """
-    def do_it_yourself(self, registered):
-        etype = self.vobject.id
-        if etype != 'Any' and not self.schema.has_entity(etype):
-            self.skip()
-            return
-        self.remove_equivalents(registered)
-        return self.vobject
-    
-    def equivalent(self, other):
-        return other.id == self.vobject.id
-
-
-class etype_rtype_registerer(registerer):
-    """registerer handling optional .etype and .rtype attributes.:
-    
-    * if .etype is set and is not an entity type defined in the
-      application schema, skip the wrapped class
-    * if .rtype or .relname is set and is not a relation type defined in
-      the application schema, skip the wrapped class
-    * register
-    """
-    def do_it_yourself(self, registered):
-        cls = self.vobject
-        if hasattr(cls, 'etype'):
-            if not self.schema.has_entity(cls.etype):
-                return
-        rtype = getattr(cls, 'rtype', None)
-        if rtype and not self.schema.has_relation(rtype):
-            return
-        return cls
-
-class etype_rtype_priority_registerer(etype_rtype_registerer):
-    """add priority behaviour to the etype_rtype_registerer
-    """
-    def do_it_yourself(self, registered):
-        cls = super(etype_rtype_priority_registerer, self).do_it_yourself(registered)
-        if cls:
-            registerer = priority_registerer(self.registry, cls)
-            cls = registerer.do_it_yourself(registered)
-        return cls
-
-class action_registerer(etype_rtype_registerer):
-    """'all in one' actions registerer, handling optional .accepts,
-    .etype and .rtype attributes:
-    
-    * if .etype is set and is not an entity type defined in the
-      application schema, skip the wrapped class
-    * if .rtype or .relname is set and is not a relation type defined in
-      the application schema, skip the wrapped class
-    * if .accepts is set, delegate to the accepts_registerer
-    * register
-    """
-    def do_it_yourself(self, registered):
-        cls = super(action_registerer, self).do_it_yourself(registered)
-        if hasattr(cls, 'accepts'):
-            registerer = accepts_registerer(self.registry, cls)
-            cls = registerer.do_it_yourself(registered)
-        return cls
-
-
-class extresources_registerer(priority_registerer):
-    """'registerer according to a .need_resources attributes which
-    should list necessary resource identifiers for the wrapped object.
-    If one of its resources is missing, don't register
-    """
-    def do_it_yourself(self, registered):
-        if not hasattr(self.config, 'has_resource'):
-            return
-        for resourceid in self.vobject.need_resources:
-            if not self.config.has_resource(resourceid):
-                return
-        return super(extresources_registerer, self).do_it_yourself(registered)
-    
-
-__all__ = [cls.__name__ for cls in globals().values()
-           if isinstance(cls, type) and issubclass(cls, registerer)
-           and not cls is registerer]
--- a/common/rest.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,263 +0,0 @@
-"""rest publishing functions
-
-contains some functions and setup of docutils for cubicweb. Provides the
-following ReST directives:
-
-* `eid`, create link to entity in the repository by their eid
-
-* `card`, create link to card entity in the repository by their wikiid
-  (proposing to create it when the refered card doesn't exist yet)
-
-* `winclude`, reference to a web documentation file (in wdoc/ directories)
-
-* `sourcecode` (if pygments is installed), source code colorization
-
-:organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-from cStringIO import StringIO
-from itertools import chain
-from logging import getLogger
-from os.path import join
-
-from docutils import statemachine, nodes, utils, io
-from docutils.core import publish_string
-from docutils.parsers.rst import Parser, states, directives
-from docutils.parsers.rst.roles import register_canonical_role, set_classes
-
-from logilab.mtconverter import html_escape
-
-from cubicweb.common.html4zope import Writer
-
-# We provide our own parser as an attempt to get rid of
-# state machine reinstanciation
-
-import re
-# compile states.Body patterns
-for k, v in states.Body.patterns.items():
-    if isinstance(v, str):
-        states.Body.patterns[k] = re.compile(v)
-
-# register ReStructured Text mimetype / extensions
-import mimetypes
-mimetypes.add_type('text/rest', '.rest')
-mimetypes.add_type('text/rest', '.rst')
-
-
-LOGGER = getLogger('cubicweb.rest')
-
-def eid_reference_role(role, rawtext, text, lineno, inliner,
-                       options={}, content=[]):
-    try:
-        try:
-            eid_num, rest = text.split(u':', 1)
-        except:
-            eid_num, rest = text, '#'+text
-        eid_num = int(eid_num)
-        if eid_num < 0:
-            raise ValueError
-    except ValueError:
-        msg = inliner.reporter.error(
-            'EID number must be a positive number; "%s" is invalid.'
-            % text, line=lineno)
-        prb = inliner.problematic(rawtext, rawtext, msg)
-        return [prb], [msg]
-    # Base URL mainly used by inliner.pep_reference; so this is correct:
-    context = inliner.document.settings.context
-    refedentity = context.req.eid_rset(eid_num).get_entity(0, 0)
-    ref = refedentity.absolute_url()
-    set_classes(options)
-    return [nodes.reference(rawtext, utils.unescape(rest), refuri=ref,
-                            **options)], []
-
-register_canonical_role('eid', eid_reference_role)
-
-
-def card_reference_role(role, rawtext, text, lineno, inliner,
-                       options={}, content=[]):
-    text = text.strip()
-    try:
-        wikiid, rest = text.split(u':', 1)
-    except:
-        wikiid, rest = text, text
-    context = inliner.document.settings.context
-    cardrset = context.req.execute('Card X WHERE X wikiid %(id)s',
-                                   {'id': wikiid})
-    if cardrset:
-        ref = cardrset.get_entity(0, 0).absolute_url()
-    else:
-        schema = context.schema
-        if schema.eschema('Card').has_perm(context.req, 'add'):
-            ref = context.req.build_url('view', vid='creation', etype='Card', wikiid=wikiid)
-        else:
-            ref = '#'
-    set_classes(options)
-    return [nodes.reference(rawtext, utils.unescape(rest), refuri=ref,
-                            **options)], []
-
-register_canonical_role('card', card_reference_role)
-
-
-def winclude_directive(name, arguments, options, content, lineno,
-                       content_offset, block_text, state, state_machine):
-    """Include a reST file as part of the content of this reST file.
-
-    same as standard include directive but using config.locate_doc_resource to
-    get actual file to include.
-
-    Most part of this implementation is copied from `include` directive defined
-    in `docutils.parsers.rst.directives.misc`
-    """
-    context = state.document.settings.context
-    source = state_machine.input_lines.source(
-        lineno - state_machine.input_offset - 1)
-    #source_dir = os.path.dirname(os.path.abspath(source))
-    fid = arguments[0]
-    for lang in chain((context.req.lang, context.vreg.property_value('ui.language')),
-                      context.config.available_languages()):
-        rid = '%s_%s.rst' % (fid, lang)
-        resourcedir = context.config.locate_doc_file(rid)
-        if resourcedir:
-            break
-    else:
-        severe = state_machine.reporter.severe(
-              'Problems with "%s" directive path:\nno resource matching %s.'
-              % (name, fid),
-              nodes.literal_block(block_text, block_text), line=lineno)
-        return [severe]
-    path = join(resourcedir, rid)
-    encoding = options.get('encoding', state.document.settings.input_encoding)
-    try:
-        state.document.settings.record_dependencies.add(path)
-        include_file = io.FileInput(
-            source_path=path, encoding=encoding,
-            error_handler=state.document.settings.input_encoding_error_handler,
-            handle_io_errors=None)
-    except IOError, error:
-        severe = state_machine.reporter.severe(
-              'Problems with "%s" directive path:\n%s: %s.'
-              % (name, error.__class__.__name__, error),
-              nodes.literal_block(block_text, block_text), line=lineno)
-        return [severe]
-    try:
-        include_text = include_file.read()
-    except UnicodeError, error:
-        severe = state_machine.reporter.severe(
-              'Problem with "%s" directive:\n%s: %s'
-              % (name, error.__class__.__name__, error),
-              nodes.literal_block(block_text, block_text), line=lineno)
-        return [severe]
-    if options.has_key('literal'):
-        literal_block = nodes.literal_block(include_text, include_text,
-                                            source=path)
-        literal_block.line = 1
-        return literal_block
-    else:
-        include_lines = statemachine.string2lines(include_text,
-                                                  convert_whitespace=1)
-        state_machine.insert_input(include_lines, path)
-        return []
-
-winclude_directive.arguments = (1, 0, 1)
-winclude_directive.options = {'literal': directives.flag,
-                              'encoding': directives.encoding}
-directives.register_directive('winclude', winclude_directive)
-
-try:
-    from pygments import highlight
-    from pygments.lexers import get_lexer_by_name, LEXERS
-    from pygments.formatters import HtmlFormatter
-except ImportError:
-    pass
-else:
-    _PYGMENTS_FORMATTER = HtmlFormatter()
-
-    def pygments_directive(name, arguments, options, content, lineno,
-                           content_offset, block_text, state, state_machine):
-        try:
-            lexer = get_lexer_by_name(arguments[0])
-        except ValueError:
-            import traceback
-            traceback.print_exc()
-            print sorted(aliases for module_name, name, aliases, _, _  in LEXERS.itervalues())
-            # no lexer found
-            lexer = get_lexer_by_name('text')
-        print 'LEXER', lexer
-        parsed = highlight(u'\n'.join(content), lexer, _PYGMENTS_FORMATTER)
-        context = state.document.settings.context
-        context.req.add_css('pygments.css')
-        return [nodes.raw('', parsed, format='html')]
-     
-    pygments_directive.arguments = (1, 0, 1)
-    pygments_directive.content = 1
-    directives.register_directive('sourcecode', pygments_directive)
-
-
-class CubicWebReSTParser(Parser):
-    """The (customized) reStructuredText parser."""
-
-    def __init__(self):
-        self.initial_state = 'Body'
-        self.state_classes = states.state_classes
-        self.inliner = states.Inliner()
-        self.statemachine = states.RSTStateMachine(
-              state_classes=self.state_classes,
-              initial_state=self.initial_state,
-              debug=0)
-
-    def parse(self, inputstring, document):
-        """Parse `inputstring` and populate `document`, a document tree."""
-        self.setup_parse(inputstring, document)
-        inputlines = statemachine.string2lines(inputstring,
-                                               convert_whitespace=1)
-        self.statemachine.run(inputlines, document, inliner=self.inliner)
-        self.finish_parse()
-
-
-_REST_PARSER = CubicWebReSTParser()
-
-def rest_publish(context, data):
-    """publish a string formatted as ReStructured Text to HTML
-    
-    :type context: a cubicweb application object
-
-    :type data: str
-    :param data: some ReST text
-
-    :rtype: unicode
-    :return:
-      the data formatted as HTML or the original data if an error occured
-    """
-    req = context.req
-    if isinstance(data, unicode):
-        encoding = 'unicode'
-    else:
-        encoding = req.encoding
-    settings = {'input_encoding': encoding, 'output_encoding': 'unicode',
-                'warning_stream': StringIO(), 'context': context,
-                # dunno what's the max, severe is 4, and we never want a crash
-                # (though try/except may be a better option...)
-                'halt_level': 10, 
-                }
-    if context:
-        if hasattr(req, 'url'):
-            base_url = req.url()
-        elif hasattr(context, 'absolute_url'):
-            base_url = context.absolute_url()
-        else:
-            base_url = req.base_url()
-    else:
-        base_url = None
-    try:
-        return publish_string(writer=Writer(base_url=base_url),
-                              parser=_REST_PARSER, source=data,
-                              settings_overrides=settings)
-    except Exception:
-        LOGGER.exception('error while publishing ReST text')
-        if not isinstance(data, unicode):
-            data = unicode(data, encoding, 'replace')
-        return html_escape(req._('error while publishing ReST text')
-                           + '\n\n' + data)
--- a/common/schema.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/schema.py	Mon May 04 13:09:48 2009 +0200
@@ -1,3 +1,5 @@
+"""pre 3.0 bw compat"""
+# pylint: disable-msg=W0614,W0401
 from warnings import warn
 warn('moved to cubicweb.schema', DeprecationWarning, stacklevel=2)
 from cubicweb.schema import *
--- a/common/selectors.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/selectors.py	Mon May 04 13:09:48 2009 +0200
@@ -1,571 +1,6 @@
-"""This file contains some basic selectors required by application objects.
-
-A selector is responsible to score how well an object may be used with a
-given result set (publishing time selection)
-
-If you have trouble with selectors, especially if the objet (typically
-a view or a component) you want to use is not selected and you want to
-know which one(s) of its selectors fail (e.g. returns 0), you can use
-`traced_selection` or even direclty `TRACED_OIDS`.
-
-`TRACED_OIDS` is a tuple of traced object ids. The special value
-'all' may be used to log selectors for all objects.
-
-For instance, say that the following code yields a `NoSelectableObject`
-exception::
-
-    self.view('calendar', myrset)
-
-You can log the selectors involved for *calendar* by replacing the line
-above by::
-
-    # in Python2.5
-    from cubicweb.common.selectors import traced_selection
-    with traced_selection():
-        self.view('calendar', myrset)
-
-    # in Python2.4
-    from cubicweb.common import selectors
-    selectors.TRACED_OIDS = ('calendar',)
-    self.view('calendar', myrset)
-    selectors.TRACED_OIDS = ()
- 
-
-
-:organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-
-__docformat__ = "restructuredtext en"
-
-import logging
-
-from logilab.common.compat import all
-from logilab.common.deprecation import deprecated_function
-
-from cubicweb import Unauthorized, NoSelectableObject, role
-from cubicweb.cwvreg import DummyCursorError
-from cubicweb.vregistry import chainall, chainfirst, NoSelectableObject
-from cubicweb.cwconfig import CubicWebConfiguration
-from cubicweb.schema import split_expression
-
-# helpers for debugging selectors
-SELECTOR_LOGGER = logging.getLogger('cubicweb.selectors')
-TRACED_OIDS = ()
-
-def lltrace(selector):
-    # don't wrap selectors if not in development mode
-    if CubicWebConfiguration.mode == 'installed':
-        return selector
-    def traced(cls, *args, **kwargs):
-        ret = selector(cls, *args, **kwargs)
-        if TRACED_OIDS == 'all' or cls.id in TRACED_OIDS:
-            SELECTOR_LOGGER.critical('selector %s returned %s for %s', selector.__name__, ret, cls)
-        return ret
-    traced.__name__ = selector.__name__
-    return traced
-
-class traced_selection(object):
-    """selector debugging helper.
-
-    Typical usage is :
-
-    >>> with traced_selection():
-    ...     # some code in which you want to debug selectors
-    ...     # for all objects
-
-    or
-
-    >>> with traced_selection( ('oid1', 'oid2') ):
-    ...     # some code in which you want to debug selectors
-    ...     # for objects with id 'oid1' and 'oid2'
-    
-    """
-    def __init__(self, traced='all'):
-        self.traced = traced
-        
-    def __enter__(self):
-        global TRACED_OIDS
-        TRACED_OIDS = self.traced
-
-    def __exit__(self, exctype, exc, traceback):
-        global TRACED_OIDS
-        TRACED_OIDS = ()
-        return traceback is None
-
-# very basic selectors ########################################################
-
-def yes(cls, *args, **kwargs):
-    """accept everything"""
-    return 1
-yes_selector = deprecated_function(yes)
-
-@lltrace
-def none_rset(cls, req, rset, *args, **kwargs):
-    """accept no result set"""
-    if rset is None:
-        return 1
-    return 0
-norset_selector = deprecated_function(none_rset)
-
-@lltrace
-def any_rset(cls, req, rset, *args, **kwargs):
-    """accept result set, whatever the number of result"""
-    if rset is not None:
-        return 1
-    return 0
-rset_selector = deprecated_function(any_rset)
-
-@lltrace
-def nonempty_rset(cls, req, rset, *args, **kwargs):
-    """accept any non empty result set"""
-    if rset is not None and rset.rowcount:
-        return 1
-    return 0
-anyrset_selector = deprecated_function(nonempty_rset)
-    
-@lltrace
-def empty_rset(cls, req, rset, *args, **kwargs):
-    """accept empty result set"""
-    if rset is not None and rset.rowcount == 0:
-        return 1
-    return 0
-emptyrset_selector = deprecated_function(empty_rset)
-
-@lltrace
-def one_line_rset(cls, req, rset, row=None, *args, **kwargs):
-    """accept result set with a single line of result"""
-    if rset is not None and (row is not None or rset.rowcount == 1):
-        return 1
-    return 0
-onelinerset_selector = deprecated_function(one_line_rset)
-
-@lltrace
-def two_lines_rset(cls, req, rset, *args, **kwargs):
-    """accept result set with *at least* two lines of result"""
-    if rset is not None and rset.rowcount > 1:
-        return 1
-    return 0
-twolinerset_selector = deprecated_function(two_lines_rset)
-
-@lltrace
-def two_cols_rset(cls, req, rset, *args, **kwargs):
-    """accept result set with at least one line and two columns of result"""
-    if rset is not None and rset.rowcount > 0 and len(rset.rows[0]) > 1:
-        return 1
-    return 0
-twocolrset_selector = deprecated_function(two_cols_rset)
-
-@lltrace
-def paginated_rset(cls, req, rset, *args, **kwargs):
-    """accept result sets with more rows than the page size
-    """
-    page_size = kwargs.get('page_size')
-    if page_size is None:
-        page_size = req.form.get('page_size')
-        if page_size is None:
-            page_size = req.property_value('navigation.page-size')
-        else:
-            page_size = int(page_size)
-    if rset is None or len(rset) <= page_size:
-        return 0
-    return 1
-largerset_selector = deprecated_function(paginated_rset)
-
-@lltrace
-def sorted_rset(cls, req, rset, row=None, col=None, **kwargs):
-    """accept sorted result set"""
-    rqlst = rset.syntax_tree()
-    if len(rqlst.children) > 1 or not rqlst.children[0].orderby:
-        return 0
-    return 2
-sortedrset_selector = deprecated_function(sorted_rset)
-
-@lltrace
-def one_etype_rset(cls, req, rset, *args, **kwargs):
-    """accept result set where entities in the first columns are all of the
-    same type
-    """
-    if len(rset.column_types(0)) != 1:
-        return 0
-    return 1
-oneetyperset_selector = deprecated_function(one_etype_rset)
-
-@lltrace
-def two_etypes_rset(cls, req, rset, **kwargs):
-    """accepts resultsets containing several entity types"""
-    if rset:
-        etypes = rset.column_types(0)
-        if len(etypes) > 1:
-            return 1
-    return 0
-multitype_selector = deprecated_function(two_etypes_rset)
-
-@lltrace
-def match_search_state(cls, req, rset, row=None, col=None, **kwargs):
-    """checks if the current search state is in a .search_states attribute of
-    the wrapped class
-
-    search state should be either 'normal' or 'linksearch' (eg searching for an
-    object to create a relation with another)
-    """
-    try:
-        if not req.search_state[0] in cls.search_states:
-            return 0
-    except AttributeError:
-        return 1 # class doesn't care about search state, accept it
-    return 1
-searchstate_selector = deprecated_function(match_search_state)
-
-@lltrace
-def anonymous_user(cls, req, *args, **kwargs):
-    """accept if user is anonymous"""
-    if req.cnx.anonymous_connection:
-        return 1
-    return 0
-anonymous_selector = deprecated_function(anonymous_user)
-
-@lltrace
-def authenticated_user(cls, req, *args, **kwargs):
-    """accept if user is authenticated"""
-    return not anonymous_user(cls, req, *args, **kwargs)
-not_anonymous_selector = deprecated_function(authenticated_user)
-
-@lltrace
-def match_form_params(cls, req, *args, **kwargs):
-    """check if parameters specified by the form_params attribute on
-    the wrapped class are specified in request form parameters
-    """
-    score = 0
-    for param in cls.form_params:
-        val = req.form.get(param)
-        if not val:
-            return 0
-        score += 1
-    return score + 1
-req_form_params_selector = deprecated_function(match_form_params)
-
-@lltrace
-def match_kwargs(cls, req, *args, **kwargs):
-    """check if arguments specified by the expected_kwargs attribute on
-    the wrapped class are specified in given named parameters
-    """
-    values = []
-    for arg in cls.expected_kwargs:
-        if not arg in kwargs:
-            return 0
-    return 1
-kwargs_selector = deprecated_function(match_kwargs)
-
-
-# not so basic selectors ######################################################
-
-@lltrace
-def accept_etype(cls, req, *args, **kwargs):
-    """check etype presence in request form *and* accepts conformance"""
-    if 'etype' not in req.form and 'etype' not in kwargs:
-        return 0
-    try:
-        etype = req.form['etype']
-    except KeyError:
-        etype = kwargs['etype']
-    # value is a list or a tuple if web request form received several
-    # values for etype parameter
-    assert isinstance(etype, basestring), "got multiple etype parameters in req.form"
-    if 'Any' in cls.accepts:
-        return 1
-    # no Any found, we *need* exact match
-    if etype not in cls.accepts:
-        return 0
-    # exact match must return a greater value than 'Any'-match
-    return 2
-etype_form_selector = deprecated_function(accept_etype)
-
-@lltrace
-def _non_final_entity(cls, req, rset, row=None, col=None, **kwargs):
-    """accept non final entities
-    if row is not specified, use the first one
-    if col is not specified, use the first one
-    """
-    etype = rset.description[row or 0][col or 0]
-    if etype is None: # outer join
-        return 0
-    if cls.schema.eschema(etype).is_final():
-        return 0
-    return 1
-_nfentity_selector = deprecated_function(_non_final_entity)
-
-@lltrace
-def _rql_condition(cls, req, rset, row=None, col=None, **kwargs):
-    """accept single entity result set if the entity match an rql condition
-    """
-    if cls.condition:
-        eid = rset[row or 0][col or 0]
-        if 'U' in frozenset(split_expression(cls.condition)):
-            rql = 'Any X WHERE X eid %%(x)s, U eid %%(u)s, %s' % cls.condition
-        else:
-            rql = 'Any X WHERE X eid %%(x)s, %s' % cls.condition
-        try:
-            return len(req.execute(rql, {'x': eid, 'u': req.user.eid}, 'x'))
-        except Unauthorized:
-            return 0
-        
-    return 1
-_rqlcondition_selector = deprecated_function(_rql_condition)
-
-@lltrace
-def _implement_interface(cls, req, rset, row=None, col=None, **kwargs):
-    """accept uniform result sets, and apply the following rules:
-
-    * wrapped class must have a accepts_interfaces attribute listing the
-      accepted ORed interfaces
-    * if row is None, return the sum of values returned by the method
-      for each entity's class in the result set. If any score is 0,
-      return 0.
-    * if row is specified, return the value returned by the method with
-      the entity's class of this row
-    """
-    # XXX this selector can be refactored : extract the code testing
-    #     for entity schema / interface compliance
-    score = 0
-    # check 'accepts' to give priority to more specific classes
-    if row is None:
-        for etype in rset.column_types(col or 0):
-            eclass = cls.vreg.etype_class(etype)
-            escore = 0
-            for iface in cls.accepts_interfaces:
-                escore += iface.is_implemented_by(eclass)
-            if not escore:
-                return 0
-            score += escore
-            accepts = set(getattr(cls, 'accepts', ()))
-            # if accepts is defined on the vobject, eclass must match
-            if accepts:
-                eschema = eclass.e_schema
-                etypes = set([eschema] + eschema.ancestors())
-                if accepts & etypes:
-                    score += 2
-                elif 'Any' not in accepts:
-                    return 0
-        return score + 1
-    etype = rset.description[row][col or 0]
-    if etype is None: # outer join
-        return 0
-    eclass = cls.vreg.etype_class(etype)
-    for iface in cls.accepts_interfaces:
-        score += iface.is_implemented_by(eclass)
-    if score:
-        accepts = set(getattr(cls, 'accepts', ()))
-        # if accepts is defined on the vobject, eclass must match
-        if accepts:
-            eschema = eclass.e_schema
-            etypes = set([eschema] + eschema.ancestors())
-            if accepts & etypes:
-                score += 1
-            elif 'Any' not in accepts:
-                return 0
-        score += 1
-    return score
-_interface_selector = deprecated_function(_implement_interface)
-
-@lltrace
-def score_entity_selector(cls, req, rset, row=None, col=None, **kwargs):
-    if row is None:
-        rows = xrange(rset.rowcount)
-    else:
-        rows = (row,)
-    for row in rows:
-        try:
-            score = cls.score_entity(rset.get_entity(row, col or 0))
-        except DummyCursorError:
-            # get a dummy cursor error, that means we are currently
-            # using a dummy rset to list possible views for an entity
-            # type, not for an actual result set. In that case, we
-            # don't care of the value, consider the object as selectable
-            return 1
-        if not score:
-            return 0
-    return 1
-
-@lltrace
-def accept_rset(cls, req, rset, row=None, col=None, **kwargs):
-    """simply delegate to cls.accept_rset method"""
-    return cls.accept_rset(req, rset, row=row, col=col)
-accept_rset_selector = deprecated_function(accept_rset)
-
-@lltrace
-def but_etype(cls, req, rset, row=None, col=None, **kwargs):
-    """restrict the searchstate_accept_one_selector to exclude entity's type
-    refered by the .etype attribute
-    """
-    if rset.description[row or 0][col or 0] == cls.etype:
-        return 0
-    return 1
-but_etype_selector = deprecated_function(but_etype)
-
-@lltrace
-def etype_rtype_selector(cls, req, rset, row=None, col=None, **kwargs):
-    """only check if the user has read access on the entity's type refered
-    by the .etype attribute and on the relations's type refered by the
-    .rtype attribute if set.
-    """
-    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
-
-@lltrace
-def has_relation(cls, req, rset, row=None, col=None, **kwargs):
-    """check if the user has read access on the relations's type refered by the
-    .rtype attribute of the class, and if all entities types in the
-    result set has this relation.
-    """
-    if hasattr(cls, 'rtype'):
-        rschema = cls.schema.rschema(cls.rtype)
-        perm = getattr(cls, 'require_permission', 'read')
-        if not (rschema.has_perm(req, perm) or rschema.has_local_role(perm)):
-            return 0
-        if row is None:
-            for etype in rset.column_types(col or 0):
-                if not cls.relation_possible(etype):
-                    return 0
-        elif not cls.relation_possible(rset.description[row][col or 0]):
-            return 0
-    return 1
-accept_rtype_selector = deprecated_function(has_relation)
-
-@lltrace
-def one_has_relation(cls, req, rset, row=None, col=None, **kwargs):
-    """check if the user has read access on the relations's type refered by the
-    .rtype attribute of the class, and if at least one entity type in the
-    result set has this relation.
-    """
-    rschema = cls.schema.rschema(cls.rtype)
-    perm = getattr(cls, 'require_permission', 'read')
-    if not (rschema.has_perm(req, perm) or rschema.has_local_role(perm)):
-        return 0
-    if row is None:
-        for etype in rset.column_types(col or 0):
-            if cls.relation_possible(etype):
-                return 1
-    elif cls.relation_possible(rset.description[row][col or 0]):
-        return 1
-    return 0
-one_has_relation_selector = deprecated_function(one_has_relation)
-
-@lltrace
-def has_related_entities(cls, req, rset, row=None, col=None, **kwargs):
-    return bool(rset.get_entity(row or 0, col or 0).related(cls.rtype, role(cls)))
-
-
-@lltrace
-def match_user_group(cls, req, rset=None, row=None, col=None, **kwargs):
-    """select according to user's groups"""
-    if not cls.require_groups:
-        return 1
-    user = req.user
-    if user is None:
-        return int('guests' in cls.require_groups)
-    score = 0
-    if 'owners' in cls.require_groups and rset:
-        if row is not None:
-            eid = rset[row][col or 0]
-            if user.owns(eid):
-                score = 1
-        else:
-            score = all(user.owns(r[col or 0]) for r in rset)
-    score += user.matching_groups(cls.require_groups)
-    if score:
-        # add 1 so that an object with one matching group take priority
-        # on an object without require_groups
-        return score + 1 
-    return 0
-in_group_selector = deprecated_function(match_user_group)
-
-@lltrace
-def user_can_add_etype(cls, req, rset, row=None, col=None, **kwargs):
-    """only check if the user has add access on the entity's type refered
-    by the .etype attribute.
-    """
-    if not cls.schema.eschema(cls.etype).has_perm(req, 'add'):
-        return 0
-    return 1
-add_etype_selector = deprecated_function(user_can_add_etype)
-
-@lltrace
-def match_context_prop(cls, req, rset, row=None, col=None, context=None,
-                       **kwargs):
-    propval = req.property_value('%s.%s.context' % (cls.__registry__, cls.id))
-    if not propval:
-        propval = cls.context
-    if context is not None and propval and context != propval:
-        return 0
-    return 1
-contextprop_selector = deprecated_function(match_context_prop)
-
-@lltrace
-def primary_view(cls, req, rset, row=None, col=None, view=None,
-                          **kwargs):
-    if view is not None and not view.is_primary():
-        return 0
-    return 1
-primaryview_selector = deprecated_function(primary_view)
-
-def appobject_selectable(registry, oid):
-    """return a selector that will have a positive score if an object for the
-    given registry and object id is selectable for the input context
-    """
-    @lltrace
-    def selector(cls, req, rset, *args, **kwargs):
-        try:
-            cls.vreg.select_object(registry, oid, req, rset, *args, **kwargs)
-            return 1
-        except NoSelectableObject:
-            return 0
-    return selector
-
-
-# compound selectors ##########################################################
-
-non_final_entity = chainall(nonempty_rset, _non_final_entity)
-non_final_entity.__name__ = 'non_final_entity'
-nfentity_selector = deprecated_function(non_final_entity)
-
-implement_interface = chainall(non_final_entity, _implement_interface)
-implement_interface.__name__ = 'implement_interface'
-interface_selector = deprecated_function(implement_interface)
-
-accept = chainall(non_final_entity, accept_rset)
-accept.__name__ = 'accept'
-accept_selector = deprecated_function(accept)
-
-accept_one = chainall(one_line_rset, accept)
-accept_one.__name__ = 'accept_one'
-accept_one_selector = deprecated_function(accept_one)
-
-rql_condition = chainall(non_final_entity, one_line_rset, _rql_condition)
-rql_condition.__name__ = 'rql_condition'
-rqlcondition_selector = deprecated_function(rql_condition)
-
-
-searchstate_accept = chainall(nonempty_rset, match_search_state, accept)
-searchstate_accept.__name__ = 'searchstate_accept'
-searchstate_accept_selector = deprecated_function(searchstate_accept)
-
-searchstate_accept_one = chainall(one_line_rset, match_search_state,
-                                  accept, _rql_condition)
-searchstate_accept_one.__name__ = 'searchstate_accept_one'
-searchstate_accept_one_selector = deprecated_function(searchstate_accept_one)
-
-searchstate_accept_one_but_etype = chainall(searchstate_accept_one, but_etype)
-searchstate_accept_one_but_etype.__name__ = 'searchstate_accept_one_but_etype'
-searchstate_accept_one_but_etype_selector = deprecated_function(
-    searchstate_accept_one_but_etype)
+"""pre 3.2 bw compat"""
+# pylint: disable-msg=W0614,W0401
+from warnings import warn
+warn('moved to cubicweb.selectors', DeprecationWarning, stacklevel=2)
+from cubicweb.selectors import *
+from cubicweb.selectors import _rql_condition
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common/tags.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,44 @@
+"""helper classes to generate simple (X)HTML tags
+
+:organization: Logilab
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from cubicweb.common.uilib import simple_sgml_tag
+
+class tag(object):
+    def __init__(self, name, escapecontent=True):
+        self.name = name
+        self.escapecontent = escapecontent
+
+    def __call__(self, __content=None, **attrs):
+        attrs.setdefault('escapecontent', self.escapecontent)
+        return simple_sgml_tag(self.name, __content, **attrs)
+
+input = tag('input')
+textarea = tag('textarea')
+a = tag('a')
+span = tag('span')
+div = tag('div', False)
+img = tag('img')
+label = tag('label')
+option = tag('option')
+h1 = tag('h1')
+h2 = tag('h2')
+h3 = tag('h3')
+h4 = tag('h4')
+h5 = tag('h5')
+
+def select(name, id=None, multiple=False, options=[], **attrs):
+    if multiple:
+        attrs['multiple'] = 'multiple'
+    if id:
+        attrs['id'] = id
+    html = [u'<select name="%s" %s>' % (
+        name, ' '.join('%s="%s"' % kv for kv in attrs.items()))]
+    html += options
+    html.append(u'</select>')
+    return u'\n'.join(html)
+
--- a/common/tal.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,256 +0,0 @@
-"""provides simpleTAL extensions for CubicWeb
-
-:organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-
-__docformat__ = "restructuredtext en"
-
-import sys
-import re
-from os.path import exists, isdir, join
-from logging import getLogger
-from StringIO import StringIO
-        
-from simpletal import simpleTAL, simpleTALES
-
-from logilab.common.decorators import cached
-
-LOGGER = getLogger('cubicweb.tal')
-
-
-class LoggerAdapter(object):
-    def __init__(self, tal_logger):
-        self.tal_logger = tal_logger
-        
-    def debug(self, msg):
-        LOGGER.debug(msg)
-
-    def warn(self, msg):
-        LOGGER.warning(msg)
-
-    def __getattr__(self, attrname):
-        return getattr(self.tal_logger, attrname)
-
-
-class CubicWebContext(simpleTALES.Context):
-    """add facilities to access entity / resultset"""
-
-    def __init__(self, options=None, allowPythonPath=1):
-        simpleTALES.Context.__init__(self, options, allowPythonPath)
-        self.log = LoggerAdapter(self.log)
-
-    def update(self, context):
-        for varname, value in context.items():
-            self.addGlobal(varname, value)
-
-    def addRepeat(self, name, var, initialValue):
-        simpleTALES.Context.addRepeat(self, name, var, initialValue)
-
-# XXX FIXME need to find a clean to define OPCODE values for extensions
-I18N_CONTENT = 18  
-I18N_REPLACE = 19
-RQL_EXECUTE  = 20
-# simpleTAL uses the OPCODE values to define priority over commands.
-# TAL_ITER should have the same priority than TAL_REPEAT (i.e. 3), but
-# we can't use the same OPCODE for two different commands without changing
-# the simpleTAL implementation. Another solution would be to totally override
-# the REPEAT implementation with the ITER one, but some specific operations
-# (involving len() for instance) are not implemented for ITER, so we prefer
-# to keep both implementations for now, and to fool simpleTAL by using a float
-# number between 3 and 4
-TAL_ITER     = 3.1
-
-
-# FIX simpleTAL HTML 4.01 stupidity
-# (simpleTAL never closes tags like INPUT, IMG, HR ...)
-simpleTAL.HTML_FORBIDDEN_ENDTAG.clear()
-
-class CubicWebTemplateCompiler(simpleTAL.HTMLTemplateCompiler):
-    """extends default compiler by adding i18n:content commands"""
-
-    def __init__(self):
-        simpleTAL.HTMLTemplateCompiler.__init__(self)
-        self.commandHandler[I18N_CONTENT] = self.compile_cmd_i18n_content
-        self.commandHandler[I18N_REPLACE] = self.compile_cmd_i18n_replace
-        self.commandHandler[RQL_EXECUTE] = self.compile_cmd_rql
-        self.commandHandler[TAL_ITER] = self.compile_cmd_tal_iter
-
-    def setTALPrefix(self, prefix):
-        simpleTAL.TemplateCompiler.setTALPrefix(self, prefix)
-        self.tal_attribute_map['i18n:content'] = I18N_CONTENT
-        self.tal_attribute_map['i18n:replace'] = I18N_REPLACE
-        self.tal_attribute_map['rql:execute'] = RQL_EXECUTE
-        self.tal_attribute_map['tal:iter'] = TAL_ITER
-
-    def compile_cmd_i18n_content(self, argument):
-        # XXX tal:content structure=, text= should we support this ?
-        structure_flag = 0
-        return (I18N_CONTENT, (argument, False, structure_flag, self.endTagSymbol))
-
-    def compile_cmd_i18n_replace(self, argument):
-        # XXX tal:content structure=, text= should we support this ?
-        structure_flag = 0
-        return (I18N_CONTENT, (argument, True, structure_flag, self.endTagSymbol))
-
-    def compile_cmd_rql(self, argument):
-        return (RQL_EXECUTE, (argument, self.endTagSymbol))
-
-    def compile_cmd_tal_iter(self, argument):
-        original_id, (var_name, expression, end_tag_symbol) = \
-                     simpleTAL.HTMLTemplateCompiler.compileCmdRepeat(self, argument)
-        return (TAL_ITER, (var_name, expression, self.endTagSymbol))
-
-    def getTemplate(self):
-        return CubicWebTemplate(self.commandList, self.macroMap, self.symbolLocationTable)
-
-    def compileCmdAttributes (self, argument):
-        """XXX modified to support single attribute
-        definition ending by a ';'
-
-        backport this to simpleTAL
-        """
-        # Compile tal:attributes into attribute command
-        # Argument: [(attributeName, expression)]
-        
-        # Break up the list of attribute settings first
-        commandArgs = []
-        # We only want to match semi-colons that are not escaped
-        argumentSplitter =  re.compile(r'(?<!;);(?!;)')
-        for attributeStmt in argumentSplitter.split(argument):
-            if not attributeStmt.strip():
-                continue
-            #  remove any leading space and un-escape any semi-colons
-            attributeStmt = attributeStmt.lstrip().replace(';;', ';')
-            # Break each attributeStmt into name and expression
-            stmtBits = attributeStmt.split(' ')
-            if (len (stmtBits) < 2):
-                # Error, badly formed attributes command
-                msg = "Badly formed attributes command '%s'.  Attributes commands must be of the form: 'name expression[;name expression]'" % argument
-                self.log.error(msg)
-                raise simpleTAL.TemplateParseException(self.tagAsText(self.currentStartTag), msg)
-            attName = stmtBits[0]
-            attExpr = " ".join(stmtBits[1:])
-            commandArgs.append((attName, attExpr))
-        return (simpleTAL.TAL_ATTRIBUTES, commandArgs)
-
-
-class CubicWebTemplateInterpreter(simpleTAL.TemplateInterpreter):
-    """provides implementation for interpreting cubicweb extensions"""
-    def __init__(self):
-        simpleTAL.TemplateInterpreter.__init__(self)
-        self.commandHandler[I18N_CONTENT] = self.cmd_i18n
-        self.commandHandler[TAL_ITER] = self.cmdRepeat
-        # self.commandHandler[RQL_EXECUTE] = self.cmd_rql
-
-    def cmd_i18n(self, command, args):
-        """i18n:content and i18n:replace implementation"""
-        string, replace_flag, structure_flag, end_symbol = args
-        if replace_flag:
-            self.outputTag = 0
-        result = self.context.globals['_'](string)
-        self.tagContent = (0, result)
-        self.movePCForward = self.symbolTable[end_symbol]
-        self.programCounter += 1
-
-
-class CubicWebTemplate(simpleTAL.HTMLTemplate):
-    """overrides HTMLTemplate.expand() to systematically use CubicWebInterpreter
-    """
-    def expand(self, context, outputFile):
-        interpreter = CubicWebTemplateInterpreter()
-        interpreter.initialise(context, outputFile)
-        simpleTAL.HTMLTemplate.expand(self, context, outputFile,# outputEncoding='unicode',
-                                      interpreter=interpreter)
-
-    def expandInline(self, context, outputFile, interpreter):
-        """ Internally used when expanding a template that is part of a context."""
-        try:
-            interpreter.execute(self)
-        except UnicodeError, unierror:
-            LOGGER.exception(str(unierror))
-            raise simpleTALES.ContextContentException("found non-unicode %r string in Context!" % unierror.args[1]), None, sys.exc_info()[-1]
-
-
-def compile_template(template):
-    """compiles a TAL template string
-    :type template: unicode
-    :param template: a TAL-compliant template string
-    """
-    string_buffer = StringIO(template)
-    compiler = CubicWebTemplateCompiler()
-    compiler.parseTemplate(string_buffer) # , inputEncoding='unicode')
-    return compiler.getTemplate()
-
-
-def compile_template_file(filepath):
-    """compiles a TAL template file
-    :type filepath: str
-    :param template: path of the file to compile 
-    """
-    fp = file(filepath)
-    file_content = unicode(fp.read()) # template file should be pure ASCII
-    fp.close()
-    return compile_template(file_content)
-
-
-def evaluatePython (self, expr):
-    if not self.allowPythonPath:
-        return self.false
-    globals = {}
-    for name, value in self.globals.items():
-        if isinstance (value, simpleTALES.ContextVariable):
-            value = value.rawValue()
-        globals[name] = value
-    globals['path'] = self.pythonPathFuncs.path
-    globals['string'] = self.pythonPathFuncs.string
-    globals['exists'] = self.pythonPathFuncs.exists
-    globals['nocall'] = self.pythonPathFuncs.nocall
-    globals['test'] = self.pythonPathFuncs.test
-    locals = {}
-    for name, value in self.locals.items():
-        if (isinstance (value, simpleTALES.ContextVariable)):
-            value = value.rawValue()
-        locals[name] = value
-    # XXX precompile expr will avoid late syntax error
-    try:
-        result = eval(expr, globals, locals)
-    except Exception, ex:
-        ex = ex.__class__('in %r: %s' % (expr, ex))
-        raise ex, None, sys.exc_info()[-1]
-    if (isinstance (result, simpleTALES.ContextVariable)):
-        return result.value()
-    return result
-
-simpleTALES.Context.evaluatePython = evaluatePython
-
-
-class talbased(object):
-    def __init__(self, filename, write=True):
-##         if not osp.isfile(filepath):
-##             # print "[tal.py] just for tests..."
-##             # get parent frame
-##             directory = osp.abspath(osp.dirname(sys._getframe(1).f_globals['__file__']))
-##             filepath = osp.join(directory, filepath)
-        self.filename = filename
-        self.write = write
-
-    def __call__(self, viewfunc):
-        def wrapped(instance, *args, **kwargs):
-            variables = viewfunc(instance, *args, **kwargs)
-            html = instance.tal_render(self._compiled_template(instance), variables)
-            if self.write:
-                instance.w(html)
-            else:
-                return html
-        return wrapped
-
-    def _compiled_template(self, instance):
-        for fileordirectory in instance.config.vregistry_path():
-            filepath = join(fileordirectory, self.filename)
-            if isdir(fileordirectory) and exists(filepath):
-                return compile_template_file(filepath)
-        raise Exception('no such template %s' % self.filename)
-    _compiled_template = cached(_compiled_template, 0)
-    
--- a/common/test/data/bootstrap_cubes	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/test/data/bootstrap_cubes	Mon May 04 13:09:48 2009 +0200
@@ -1,1 +1,1 @@
-file, tag
+
--- a/common/test/data/entities.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,15 +0,0 @@
-from cubicweb.entities import AnyEntity, fetch_config
-
-class Personne(AnyEntity):
-    """customized class forne Person entities"""
-    id = 'Personne'
-    fetch_attrs, fetch_order = fetch_config(['nom', 'prenom'])
-    rest_attr = 'nom'
-
-
-class Societe(AnyEntity):
-    id = 'Societe'
-    fetch_attrs = ('nom',)
-    
-class AnotherNote(AnyEntity):
-    id = 'AnotherNote'
--- a/common/test/data/schema/Affaire.sql	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-sujet varchar(128)
-ref   varchar(12)
--- a/common/test/data/schema/Note.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-from cubicweb.schema import format_constraint
-
-class AnotherNote(EntityType):
-    descr_format = String(meta=True, internationalizable=True,
-                                default='text/rest', constraints=[format_constraint])
-    descr = String(fulltextindexed=True,
-                   description=_('more detailed description'))
-    descr2_format = String(meta=True, internationalizable=True,
-                                default='text/rest', constraints=[format_constraint])
-    descr2 = String(fulltextindexed=True,
-                    description=_('more detailed description'))
-    
-
-class SubNote(AnotherNote):
-    __specializes_schema__ = True
-    descr3 = String()
--- a/common/test/data/schema/Note.sql	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-date varchar(10)
-type char(1)
-para varchar(512)
--- a/common/test/data/schema/Personne.sql	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-nom    ivarchar(64) NOT NULL
-prenom ivarchar(64)
-sexe   char(1) DEFAULT 'M' 
-promo  choice('bon','pasbon')
-titre  ivarchar(128)
-adel   varchar(128)
-ass    varchar(128)
-web    varchar(128)
-tel    integer
-fax    integer
-datenaiss datetime
-test   boolean 
-description text
-salary float
--- a/common/test/data/schema/Societe.sql	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-nom  ivarchar(64)
-web varchar(128)
-tel  integer
-fax  integer
-rncs varchar(32)
-ad1  varchar(128)
-ad2  varchar(128)
-ad3  varchar(128)
-cp   varchar(12)
-ville varchar(32)
--- a/common/test/data/schema/relations.rel	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-Personne travaille Societe
-Personne evaluee Note
-EUser evaluee Note
-Societe evaluee Note
-Personne concerne Affaire
-Affaire concerne Societe
-Personne evaluee Personne
-
-Note ecrit_par Personne inline CONSTRAINT E concerns P, X version_of P
-Personne connait Personne symetric
-
-Tag tags Note
-Tag tags Personne
-
-Affaire liee_a Societe
-Affaire liee_a Personne
--- a/common/test/unittest_entity.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,491 +0,0 @@
-# -*- coding: utf-8 -*-
-"""unit tests for cubicweb.web.views.entities module"""
-
-from cubicweb.devtools.apptest import EnvBasedTC
-
-from mx.DateTime import DateTimeType, now
-from cubicweb import Binary
-
-class EntityTC(EnvBasedTC):
-
-##     def setup_database(self):
-##         self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
-##         self.add_entity('Task', title=u'fait ca !', description=u'et plus vite', start=now())
-##         self.add_entity('Tag', name=u'x')
-##         self.add_entity('Link', title=u'perdu', url=u'http://www.perdu.com',
-##                         embed=False)
-    
-    def test_boolean_value(self):
-        e = self.etype_instance('Tag')
-        self.failUnless(e)
-
-    def test_yams_inheritance(self):
-        from entities import AnotherNote
-        e = self.etype_instance('SubNote')
-        self.assertIsInstance(e, AnotherNote)
-        e2 = self.etype_instance('SubNote')
-        self.assertIs(e.__class__, e2.__class__)
-
-    def test_has_eid(self):
-        e = self.etype_instance('Tag')
-        self.assertEquals(e.eid, None)
-        self.assertEquals(e.has_eid(), False)
-        e.eid = 'X'
-        self.assertEquals(e.has_eid(), False)
-        e.eid = 0
-        self.assertEquals(e.has_eid(), True)
-        e.eid = 2
-        self.assertEquals(e.has_eid(), True)
-        
-    def test_copy(self):
-        self.add_entity('Tag', name=u'x')
-        p = self.add_entity('Personne', nom=u'toto')
-        oe = self.add_entity('Note', type=u'x')
-        self.execute('SET T ecrit_par U WHERE T eid %(t)s, U eid %(u)s',
-                     {'t': oe.eid, 'u': p.eid}, ('t','u'))
-        self.execute('SET TAG tags X WHERE X eid %(x)s', {'x': oe.eid}, 'x')
-        e = self.add_entity('Note', type=u'z')
-        e.copy_relations(oe.eid)
-        self.assertEquals(len(e.ecrit_par), 1)
-        self.assertEquals(e.ecrit_par[0].eid, p.eid)
-        self.assertEquals(len(e.reverse_tags), 0)
-        
-    def test_copy_with_nonmeta_composite_inlined(self):
-        p = self.add_entity('Personne', nom=u'toto')
-        oe = self.add_entity('Note', type=u'x')
-        self.schema['ecrit_par'].set_rproperty('Note', 'Personne', 'composite', 'subject')
-        self.execute('SET T ecrit_par U WHERE T eid %(t)s, U eid %(u)s',
-                     {'t': oe.eid, 'u': p.eid}, ('t','u'))
-        e = self.add_entity('Note', type=u'z')
-        e.copy_relations(oe.eid)
-        self.failIf(e.ecrit_par)
-        self.failUnless(oe.ecrit_par)
-            
-    def test_copy_with_composite(self):
-        user = self.user()
-        adeleid = self.execute('INSERT EmailAddress X: X address "toto@logilab.org", U use_email X WHERE U login "admin"')[0][0]
-        e = self.entity('Any X WHERE X eid %(x)s', {'x':user.eid}, 'x')
-        self.assertEquals(e.use_email[0].address, "toto@logilab.org")
-        self.assertEquals(e.use_email[0].eid, adeleid)
-        usereid = self.execute('INSERT EUser X: X login "toto", X upassword "toto", X in_group G, X in_state S '
-                               'WHERE G name "users", S name "activated"')[0][0]
-        e = self.entity('Any X WHERE X eid %(x)s', {'x':usereid}, 'x')
-        e.copy_relations(user.eid)
-        self.failIf(e.use_email)
-        self.failIf(e.primary_email)
-        
-    def test_copy_with_non_initial_state(self):
-        user = self.user()
-        eid = self.execute('INSERT EUser X: X login "toto", X upassword %(pwd)s, X in_group G WHERE G name "users"',
-                           {'pwd': 'toto'})[0][0]
-        self.commit()
-        self.execute('SET X in_state S WHERE X eid %(x)s, S name "deactivated"', {'x': eid}, 'x')
-        self.commit()
-        eid2 = self.execute('INSERT EUser X: X login "tutu", X upassword %(pwd)s', {'pwd': 'toto'})[0][0]
-        e = self.entity('Any X WHERE X eid %(x)s', {'x': eid2}, 'x')
-        e.copy_relations(eid)
-        self.commit()
-        e.clear_related_cache('in_state', 'subject')
-        self.assertEquals(e.state, 'activated')
-
-    def test_related_cache_both(self):
-        user = self.entity('Any X WHERE X eid %(x)s', {'x':self.user().eid}, 'x')
-        adeleid = self.execute('INSERT EmailAddress X: X address "toto@logilab.org", U use_email X WHERE U login "admin"')[0][0]
-        self.commit()
-        self.assertEquals(user._related_cache.keys(), [])
-        email = user.primary_email[0]
-        self.assertEquals(sorted(user._related_cache), ['primary_email_subject'])
-        self.assertEquals(email._related_cache.keys(), ['primary_email_object'])
-        groups = user.in_group
-        self.assertEquals(sorted(user._related_cache), ['in_group_subject', 'primary_email_subject'])
-        for group in groups:
-            self.failIf('in_group_subject' in group._related_cache, group._related_cache.keys())
-
-    def test_related_limit(self):
-        p = self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
-        for tag in u'abcd':
-            self.add_entity('Tag', name=tag)
-        self.execute('SET X tags Y WHERE X is Tag, Y is Personne')
-        self.assertEquals(len(p.related('tags', 'object', limit=2)), 2)
-        self.assertEquals(len(p.related('tags', 'object')), 4)
-
-        
-    def test_fetch_rql(self):
-        user = self.user()
-        Personne = self.vreg.etype_class('Personne')
-        Societe = self.vreg.etype_class('Societe')
-        Note = self.vreg.etype_class('Note')
-        peschema = Personne.e_schema
-        seschema = Societe.e_schema
-        peschema.subject_relation('travaille').set_rproperty(peschema, seschema, 'cardinality', '1*')
-        peschema.subject_relation('connait').set_rproperty(peschema, peschema, 'cardinality', '11')
-        peschema.subject_relation('evaluee').set_rproperty(peschema, Note.e_schema, 'cardinality', '1*')
-        seschema.subject_relation('evaluee').set_rproperty(seschema, Note.e_schema, 'cardinality', '1*')
-        # testing basic fetch_attrs attribute
-        self.assertEquals(Personne.fetch_rql(user),
-                          'Any X,AA,AB,AC ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB, X modification_date AC')
-        pfetch_attrs = Personne.fetch_attrs
-        sfetch_attrs = Societe.fetch_attrs
-        try:
-            # testing unknown attributes
-            Personne.fetch_attrs = ('bloug', 'beep')
-            self.assertEquals(Personne.fetch_rql(user), 'Any X WHERE X is Personne')            
-            # testing one non final relation
-            Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
-            self.assertEquals(Personne.fetch_rql(user),
-                              'Any X,AA,AB,AC,AD ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB, X travaille AC, AC nom AD')
-            # testing two non final relations
-            Personne.fetch_attrs = ('nom', 'prenom', 'travaille', 'evaluee')
-            self.assertEquals(Personne.fetch_rql(user),
-                              'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA ASC,AF DESC WHERE X is Personne, X nom AA, '
-                              'X prenom AB, X travaille AC, AC nom AD, X evaluee AE, AE modification_date AF')
-            # testing one non final relation with recursion
-            Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
-            Societe.fetch_attrs = ('nom', 'evaluee')
-            self.assertEquals(Personne.fetch_rql(user),
-                              'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA ASC,AF DESC WHERE X is Personne, X nom AA, X prenom AB, '
-                              'X travaille AC, AC nom AD, AC evaluee AE, AE modification_date AF'
-                              )
-            # testing symetric relation
-            Personne.fetch_attrs = ('nom', 'connait')
-            self.assertEquals(Personne.fetch_rql(user), 'Any X,AA,AB ORDERBY AA ASC WHERE X is Personne, X nom AA, X connait AB')
-            # testing optional relation
-            peschema.subject_relation('travaille').set_rproperty(peschema, seschema, 'cardinality', '?*')
-            Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
-            Societe.fetch_attrs = ('nom',)
-            self.assertEquals(Personne.fetch_rql(user),
-                              'Any X,AA,AB,AC,AD ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD')
-            # testing relation with cardinality > 1
-            peschema.subject_relation('travaille').set_rproperty(peschema, seschema, 'cardinality', '**')
-            self.assertEquals(Personne.fetch_rql(user),
-                              'Any X,AA,AB ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB')
-            # XXX test unauthorized attribute
-        finally:
-            Personne.fetch_attrs = pfetch_attrs
-            Societe.fetch_attrs = sfetch_attrs
-
-    def test_related_rql(self):
-        from cubicweb.entities import fetch_config
-        Personne = self.vreg.etype_class('Personne')
-        Societe = self.vreg.etype_class('Societe')
-        Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', 'prenom', 'sexe'))
-        Societe.fetch_attrs, Societe.fetch_order = fetch_config(('nom', 'web'))
-        aff = self.add_entity('Affaire', sujet=u'my subject', ref=u'the ref')
-        self.assertEquals(aff.related_rql('liee_a'),
-                          'Any X,AA,AB ORDERBY AA ASC WHERE E eid %(x)s, E liee_a X, '
-                          'X nom AA, X modification_date AB')
-        Societe.fetch_attrs = ('web',)
-        self.assertEquals(aff.related_rql('liee_a'),
-                          'Any X ORDERBY Z DESC WHERE X modification_date Z, E eid %(x)s, E liee_a X')
-    
-    def test_entity_unrelated(self):
-        p = self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
-        e = self.add_entity('Tag', name=u'x')
-        rschema = e.e_schema.subject_relation('tags')
-        related = [r.eid for r in e.tags]
-        self.failUnlessEqual(related, [])
-        unrelated = [reid for rview, reid in e.vocabulary(rschema, 'subject')]
-        self.failUnless(p.eid in unrelated)
-        self.execute('SET X tags Y WHERE X is Tag, Y is Personne')
-        e = self.entity('Any X WHERE X is Tag')
-        unrelated = [reid for rview, reid in e.vocabulary(rschema, 'subject')]
-        self.failIf(p.eid in unrelated)
-
-    def test_entity_unrelated_limit(self):
-        e = self.add_entity('Tag', name=u'x')
-        self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
-        self.add_entity('Personne', nom=u'di mascio', prenom=u'gwen')
-        rschema = e.e_schema.subject_relation('tags')
-        self.assertEquals(len(e.vocabulary(rschema, 'subject', limit=1)),
-                          1)
-        
-    def test_new_entity_unrelated(self):
-        e = self.etype_instance('EUser')
-        rschema = e.e_schema.subject_relation('in_group')
-        unrelated = [reid for rview, reid in e.vocabulary(rschema, 'subject')]
-        # should be default groups but owners, i.e. managers, users, guests
-        self.assertEquals(len(unrelated), 3)
-
-
-    def test_rtags_expansion(self):
-        from cubicweb.entities import AnyEntity
-        class Personne(AnyEntity):
-            id = 'Personne'
-            __rtags__ = {
-                ('travaille', 'Societe', 'subject') : set(('primary',)),
-                ('evaluee', '*', 'subject') : set(('secondary',)),
-                'ecrit_par' : set(('inlineview',)),
-                }
-        self.vreg.register_vobject_class(Personne)
-        rtags = Personne.rtags
-        self.assertEquals(rtags.get_tags('evaluee', 'Note', 'subject'), set(('secondary', 'link')))
-        self.assertEquals(rtags.is_inlined('evaluee', 'Note', 'subject'), False)
-        self.assertEquals(rtags.get_tags('evaluee', 'Personne', 'subject'), set(('secondary', 'link')))
-        self.assertEquals(rtags.is_inlined('evaluee', 'Personne', 'subject'), False)
-        self.assertEquals(rtags.get_tags('ecrit_par', 'Note', 'object'), set(('inlineview', 'link')))
-        self.assertEquals(rtags.is_inlined('ecrit_par', 'Note', 'object'), True)
-        class Personne2(Personne):
-            id = 'Personne'
-            __rtags__ = {
-                ('evaluee', 'Note', 'subject') : set(('inlineview',)),
-                }
-        self.vreg.register_vobject_class(Personne2)
-        rtags = Personne2.rtags
-        self.assertEquals(rtags.get_tags('evaluee', 'Note', 'subject'), set(('inlineview', 'link')))
-        self.assertEquals(rtags.is_inlined('evaluee', 'Note', 'subject'), True)
-        self.assertEquals(rtags.get_tags('evaluee', 'Personne', 'subject'), set(('secondary', 'link')))
-        self.assertEquals(rtags.is_inlined('evaluee', 'Personne', 'subject'), False)
-    
-    def test_relations_by_category(self):
-        e = self.etype_instance('EUser')
-        def rbc(iterable):
-            return [(rschema.type, x) for rschema, tschemas, x in iterable]
-        self.assertEquals(rbc(e.relations_by_category('primary')),
-                          [('login', 'subject'), ('upassword', 'subject'),
-                           ('in_group', 'subject'), ('in_state', 'subject'),
-                           ('eid', 'subject'),])
-        # firstname and surname are put in secondary category in views.entities.EUserEntity
-        self.assertListEquals(rbc(e.relations_by_category('secondary')),
-                              [('firstname', 'subject'), ('surname', 'subject')])
-        self.assertListEquals(rbc(e.relations_by_category('generic')),
-                              [('primary_email', 'subject'),
-                               ('evaluee', 'subject'),
-                               ('for_user', 'object')])
-        # owned_by is defined both as subject and object relations on EUser
-        self.assertListEquals(rbc(e.relations_by_category('generated')),
-                              [('last_login_time', 'subject'),
-                               ('created_by', 'subject'),
-                               ('creation_date', 'subject'),
-                               ('is', 'subject'),
-                               ('is_instance_of', 'subject'),
-                               ('modification_date', 'subject'),
-                               ('owned_by', 'subject'),
-                               ('created_by', 'object'),
-                               ('wf_info_for', 'object'),
-                               ('owned_by', 'object'),
-                               ('bookmarked_by', 'object')])
-        e = self.etype_instance('Personne')
-        self.assertListEquals(rbc(e.relations_by_category('primary')),
-                              [('nom', 'subject'), ('eid', 'subject')])
-        self.assertListEquals(rbc(e.relations_by_category('secondary')),
-                              [('prenom', 'subject'),
-                               ('sexe', 'subject'),
-                               ('promo', 'subject'),
-                               ('titre', 'subject'),
-                               ('adel', 'subject'),
-                               ('ass', 'subject'),
-                               ('web', 'subject'),
-                               ('tel', 'subject'),
-                               ('fax', 'subject'),
-                               ('datenaiss', 'subject'),
-                               ('test', 'subject'),
-                               ('description', 'subject'),
-                               ('salary', 'subject')])
-        self.assertListEquals(rbc(e.relations_by_category('generic')),
-                              [('concerne', 'subject'),
-                               ('connait', 'subject'),
-                               ('evaluee', 'subject'),
-                               ('travaille', 'subject'),
-                               ('ecrit_par', 'object'),
-                               ('evaluee', 'object'),
-                               ('liee_a', 'object'),
-                               ('tags', 'object')])
-        self.assertListEquals(rbc(e.relations_by_category('generated')),
-                              [('created_by', 'subject'),
-                               ('creation_date', 'subject'),
-                               ('is', 'subject'),
-                               ('is_instance_of', 'subject'),
-                               ('modification_date', 'subject'),
-                               ('owned_by', 'subject')])
-        
-
-    def test_printable_value_string(self):
-        e = self.add_entity('Card', title=u'rest test', content=u'du :eid:`1:*ReST*`',
-                            content_format=u'text/rest')
-        self.assertEquals(e.printable_value('content'),
-                          '<p>du <a class="reference" href="http://testing.fr/cubicweb/egroup/managers">*ReST*</a></p>\n')
-        e['content'] = 'du <em>html</em> <ref rql="EUser X">users</ref>'
-        e['content_format'] = 'text/html'
-        self.assertEquals(e.printable_value('content'),
-                          'du <em>html</em> <a href="http://testing.fr/cubicweb/view?rql=EUser%20X">users</a>')
-        e['content'] = 'du *texte*'
-        e['content_format'] = 'text/plain'
-        self.assertEquals(e.printable_value('content'),
-                          '<p>\ndu *texte*\n</p>')
-        e['title'] = 'zou'
-        e['content'] = '<h1 tal:content="self/title">titre</h1>'
-        e['content_format'] = 'text/cubicweb-page-template'
-        self.assertEquals(e.printable_value('content'),
-                          '<h1>zou</h1>')
-        
-        #e = self.etype_instance('Task')
-        e['content'] = '''\
-a title
-=======
-du :eid:`1:*ReST*`'''
-        e['content_format'] = 'text/rest'
-        self.assertEquals(e.printable_value('content', format='text/plain'),
-                          e['content'])
-
-        e['content'] = u'<b>yo (zou éà ;)</b>'
-        e['content_format'] = 'text/html'
-        self.assertEquals(e.printable_value('content', format='text/plain').strip(),
-                          u'**yo (zou éà ;)**')
-
-    def test_printable_value_bytes(self):
-        e = self.add_entity('File', data=Binary('lambda x: 1'), data_format=u'text/x-python',
-                            data_encoding=u'ascii', name=u'toto.py')
-        from cubicweb.common import mttransforms
-        if mttransforms.HAS_PYGMENTS_TRANSFORMS:
-            self.assertEquals(e.printable_value('data'),
-                              '''<div class="highlight"><pre><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="mf">1</span>
-</pre></div>
-''')
-        else:
-            self.assertEquals(e.printable_value('data'),
-                              '''<pre class="python">
-<span style="color: #C00000;">lambda</span> <span style="color: #000000;">x</span><span style="color: #0000C0;">:</span> <span style="color: #0080C0;">1</span>
-</pre>
-''')
-        
-        e = self.add_entity('File', data=Binary('*héhéhé*'), data_format=u'text/rest',
-                            data_encoding=u'utf-8', name=u'toto.txt')
-        self.assertEquals(e.printable_value('data'),
-                          u'<p><em>héhéhé</em></p>\n')
-
-    def test_printable_value_bad_html(self):
-        """make sure we don't crash if we try to render invalid XHTML strings"""
-        e = self.add_entity('Card', title=u'bad html', content=u'<div>R&D<br>',
-                            content_format=u'text/html')
-        tidy = lambda x: x.replace('\n', '')
-        self.assertEquals(tidy(e.printable_value('content')),
-                          '<div>R&amp;D<br/></div>')
-        e['content'] = u'yo !! R&D <div> pas fermé'
-        self.assertEquals(tidy(e.printable_value('content')),
-                          u'yo !! R&amp;D <div> pas fermé</div>')
-        e['content'] = u'R&D'
-        self.assertEquals(tidy(e.printable_value('content')), u'R&amp;D')
-        e['content'] = u'R&D;'
-        self.assertEquals(tidy(e.printable_value('content')), u'R&amp;D;')
-        e['content'] = u'yo !! R&amp;D <div> pas fermé'
-        self.assertEquals(tidy(e.printable_value('content')),
-                          u'yo !! R&amp;D <div> pas fermé</div>')
-        e['content'] = u'été <div> été'
-        self.assertEquals(tidy(e.printable_value('content')),
-                          u'été <div> été</div>')
-        e['content'] = u'C&apos;est un exemple s&eacute;rieux'
-        self.assertEquals(tidy(e.printable_value('content')),
-                          u"C'est un exemple sérieux")
-        # make sure valid xhtml is left untouched
-        e['content'] = u'<div>R&amp;D<br/></div>'
-        self.assertEquals(e.printable_value('content'), e['content'])
-        e['content'] = u'<div>été</div>'
-        self.assertEquals(e.printable_value('content'), e['content'])
-        e['content'] = u'été'
-        self.assertEquals(e.printable_value('content'), e['content'])
-        
-
-    def test_entity_formatted_attrs(self):
-        e = self.etype_instance('Note')
-        self.assertEquals(e.formatted_attrs(), [])
-        e = self.etype_instance('File')
-        self.assertEquals(e.formatted_attrs(), ['description'])
-        e = self.etype_instance('AnotherNote')
-        self.assertEquals(e.formatted_attrs(), ['descr', 'descr2'])
-        
-        
-    def test_fulltextindex(self):
-        e = self.etype_instance('File')
-        e['name'] = 'an html file'
-        e['description'] = 'du <em>html</em>'
-        e['description_format'] = 'text/html'
-        e['data'] = Binary('some <em>data</em>')
-        e['data_format'] = 'text/html'
-        e['data_encoding'] = 'ascii'
-        self.assertEquals(set(e.get_words()), 
-                          set(['an', 'html', 'file', 'du', 'html', 'some', 'data']))
-
-        
-    def test_nonregr_relation_cache(self):
-        p1 = self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
-        p2 = self.add_entity('Personne', nom=u'toto')
-        self.execute('SET X evaluee Y WHERE X nom "di mascio", Y nom "toto"')
-        self.assertEquals(p1.evaluee[0].nom, "toto")
-        self.failUnless(not p1.reverse_evaluee)
-        
-    def test_complete_relation(self):
-        self.execute('SET RT add_permission G WHERE RT name "wf_info_for", G name "managers"')
-        self.commit()
-        try:
-            eid = self.execute('INSERT TrInfo X: X comment "zou", X wf_info_for U,'
-                               'X from_state S1, X to_state S2 WHERE '
-                               'U login "admin", S1 name "activated", S2 name "deactivated"')[0][0]
-            trinfo = self.entity('Any X WHERE X eid %(x)s', {'x': eid}, 'x')
-            trinfo.complete()
-            self.failUnless(trinfo.relation_cached('from_state', 'subject'))
-            self.failUnless(trinfo.relation_cached('to_state', 'subject'))
-            self.failUnless(trinfo.relation_cached('wf_info_for', 'subject'))
-            # check with a missing relation
-            eid = self.execute('INSERT TrInfo X: X comment "zou", X wf_info_for U,'
-                               'X to_state S2 WHERE '
-                               'U login "admin", S2 name "activated"')[0][0]
-            trinfo = self.entity('Any X WHERE X eid %(x)s', {'x': eid}, 'x')
-            trinfo.complete()
-            self.failUnless(isinstance(trinfo.creation_date, DateTimeType))
-            self.failUnless(trinfo.relation_cached('from_state', 'subject'))
-            self.failUnless(trinfo.relation_cached('to_state', 'subject'))
-            self.failUnless(trinfo.relation_cached('wf_info_for', 'subject'))
-            self.assertEquals(trinfo.from_state, [])
-        finally:
-            self.rollback()
-            self.execute('DELETE RT add_permission G WHERE RT name "wf_info_for", G name "managers"')
-            self.commit()
-
-    def test_request_cache(self):
-        req = self.request()
-        user = self.entity('EUser X WHERE X login "admin"', req=req)
-        state = user.in_state[0]
-        samestate = self.entity('State X WHERE X name "activated"', req=req)
-        self.failUnless(state is samestate)
-
-    def test_rest_path(self):
-        note = self.add_entity('Note', type=u'z')
-        self.assertEquals(note.rest_path(), 'note/%s' % note.eid)
-        # unique attr
-        tag = self.add_entity('Tag', name=u'x')
-        self.assertEquals(tag.rest_path(), 'tag/x')
-        # test explicit rest_attr
-        person = self.add_entity('Personne', prenom=u'john', nom=u'doe')
-        self.assertEquals(person.rest_path(), 'personne/doe')
-        # ambiguity test
-        person2 = self.add_entity('Personne', prenom=u'remi', nom=u'doe')
-        self.assertEquals(person.rest_path(), 'personne/eid/%s' % person.eid)
-        self.assertEquals(person2.rest_path(), 'personne/eid/%s' % person2.eid)
-        # unique attr with None value (wikiid in this case)
-        card1 = self.add_entity('Card', title=u'hop')
-        self.assertEquals(card1.rest_path(), 'card/eid/%s' % card1.eid)
-        card2 = self.add_entity('Card', title=u'pod', wikiid=u'zob/i')
-        self.assertEquals(card2.rest_path(), 'card/zob%2Fi')
-
-    def test_set_attributes(self):
-        person = self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
-        self.assertEquals(person.prenom, u'adrien')
-        self.assertEquals(person.nom, u'di mascio')
-        person.set_attributes(prenom=u'sylvain', nom=u'thénault')
-        person = self.entity('Personne P') # XXX retreival needed ?
-        self.assertEquals(person.prenom, u'sylvain')
-        self.assertEquals(person.nom, u'thénault')
-
-    def test_metainformation(self):
-        note = self.add_entity('Note', type=u'z')
-        metainf = note.metainformation()
-        self.assertEquals(metainf, {'source': {'adapter': 'native', 'uri': 'system'}, 'type': u'Note', 'extid': None})
-        self.assertEquals(note.absolute_url(), 'http://testing.fr/cubicweb/note/%s' % note.eid)
-        metainf['source'] = metainf['source'].copy()
-        metainf['source']['base-url']  = 'http://cubicweb2.com/'
-        self.assertEquals(note.absolute_url(), 'http://cubicweb2.com/note/%s' % note.eid)
-        
-if __name__ == '__main__':
-    from logilab.common.testlib import unittest_main
-    unittest_main()
-
--- a/common/test/unittest_migration.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/test/unittest_migration.py	Mon May 04 13:09:48 2009 +0200
@@ -30,6 +30,8 @@
         self.config = MigrTestConfig('data')
         from yams.schema import Schema
         self.config.load_schema = lambda expand_cubes=False: Schema('test')
+        self.config.__class__.cubicweb_vobject_path = frozenset()
+        self.config.__class__.cube_vobject_path = frozenset()
         
     def test_migration_files_base(self):
         self.assertListEquals(migration_files(self.config, [('cubicweb', (2,3,0), (2,4,0)),
--- a/common/test/unittest_mixins.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/test/unittest_mixins.py	Mon May 04 13:09:48 2009 +0200
@@ -7,14 +7,14 @@
         self.execute('SET X state_of ET WHERE ET name "Bookmark", X eid %(x)s',
                      {'x': s.eid})
         es = self.user().wf_state('activated')
-        self.assertEquals(es.state_of[0].name, 'EUser')
+        self.assertEquals(es.state_of[0].name, 'CWUser')
         
     def test_wf_transition(self):
         t = self.add_entity('Transition', name=u'deactivate')
         self.execute('SET X transition_of ET WHERE ET name "Bookmark", X eid %(x)s',
                      {'x': t.eid})
         et = self.user().wf_transition('deactivate')
-        self.assertEquals(et.transition_of[0].name, 'EUser')
+        self.assertEquals(et.transition_of[0].name, 'CWUser')
 
     def test_change_state(self):
         user = self.user()
--- a/common/test/unittest_rest.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/test/unittest_rest.py	Mon May 04 13:09:48 2009 +0200
@@ -5,7 +5,7 @@
         
 class RestTC(EnvBasedTC):
     def context(self):
-        return self.execute('EUser X WHERE X login "admin"').get_entity(0, 0)
+        return self.execute('CWUser X WHERE X login "admin"').get_entity(0, 0)
     
     def test_eid_role(self):
         context = self.context()
--- a/common/test/unittest_utils.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +0,0 @@
-"""unit tests for module cubicweb.common.utils"""
-
-from logilab.common.testlib import TestCase, unittest_main
-
-from cubicweb.common.utils import make_uid, UStringIO, SizeConstrainedList
-
-
-class MakeUidTC(TestCase):
-    def test_1(self):
-        self.assertNotEquals(make_uid('xyz'), make_uid('abcd'))
-        self.assertNotEquals(make_uid('xyz'), make_uid('xyz'))
-        
-    def test_2(self):
-        d = {}
-        while len(d)<10000:
-            uid = make_uid('xyz')
-            if d.has_key(uid):
-                self.fail(len(d))
-            d[uid] = 1
-
-        
-class UStringIOTC(TestCase):
-    def test_boolean_value(self):
-        self.assert_(UStringIO())
-
-
-class SizeConstrainedListTC(TestCase):
-
-    def test_append(self):
-        l = SizeConstrainedList(10)
-        for i in xrange(12):
-            l.append(i)
-        self.assertEquals(l, range(2, 12))
-    
-    def test_extend(self):
-        testdata = [(range(5), range(5)),
-                    (range(10), range(10)),
-                    (range(12), range(2, 12)),
-                    ]
-        for extension, expected in testdata:
-            l = SizeConstrainedList(10)
-            l.extend(extension)
-            yield self.assertEquals, l, expected
-
-   
-if __name__ == '__main__':
-    unittest_main()
--- a/common/uilib.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/uilib.py	Mon May 04 13:09:48 2009 +0200
@@ -11,29 +11,14 @@
 
 import csv
 import decimal
-import locale
 import re
+from datetime import datetime, date, timedelta
 from urllib import quote as urlquote
-from cStringIO import StringIO
-from copy import deepcopy
+from StringIO import StringIO
 
-import simplejson
-
-from mx.DateTime import DateTimeType, DateTimeDeltaType
-
-from logilab.common.textutils import unormalize
 from logilab.mtconverter import html_escape, html_unescape
 
-def ustrftime(date, fmt='%Y-%m-%d'):
-    """like strftime, but returns a unicode string instead of an encoded
-    string which may be problematic with localized date.
-    
-    encoding is guessed by locale.getpreferredencoding()
-    """
-    # date format may depend on the locale
-    encoding = locale.getpreferredencoding(do_setlocale=False) or 'UTF-8'
-    return unicode(date.strftime(fmt), encoding)
-
+from cubicweb.utils import ustrftime
 
 def rql_for_eid(eid):
     """return the rql query necessary to fetch entity with the given eid.  This
@@ -56,7 +41,7 @@
         # don't translate empty value if you don't want strange results
         if props is not None and value and props.get('internationalizable'):
             return req._(value)
-        
+
         return value
     if attrtype == 'Date':
         return ustrftime(value, req.property_value('ui.date-format'))
@@ -78,12 +63,12 @@
 # text publishing #############################################################
 
 try:
-    from cubicweb.common.rest import rest_publish # pylint: disable-msg=W0611
+    from cubicweb.ext.rest import rest_publish # pylint: disable-msg=W0611
 except ImportError:
     def rest_publish(entity, data):
         """default behaviour if docutils was not found"""
-        return data
-    
+        return html_escape(data)
+
 TAG_PROG = re.compile(r'</?.*?>', re.U)
 def remove_html_tags(text):
     """Removes HTML tags from text
@@ -216,20 +201,27 @@
     return text[:length] + u'...'
 
 
-    
+
 # HTML generation helper functions ############################################
 
-def simple_sgml_tag(tag, content=None, **attrs):
+def simple_sgml_tag(tag, content=None, escapecontent=True, **attrs):
     """generation of a simple sgml tag (eg without children tags) easier
 
     content and attributes will be escaped
     """
     value = u'<%s' % tag
     if attrs:
+        try:
+            attrs['class'] = attrs.pop('klass')
+        except KeyError:
+            pass
         value += u' ' + u' '.join(u'%s="%s"' % (attr, html_escape(unicode(value)))
-                                  for attr, value in attrs.items())
+                                  for attr, value in attrs.items()
+                                  if value is not None)
     if content:
-        value += u'>%s</%s>' % (html_escape(unicode(content)), tag)
+        if escapecontent:
+            content = html_escape(unicode(content))
+        value += u'>%s</%s>' % (content, tag)
     else:
         value += u'/>'
     return value
@@ -247,30 +239,6 @@
     """builds a HTML link that uses the js toggleVisibility function"""
     return u'<a href="%s">%s</a>' % (toggle_action(nodeid), label)
 
-def ajax_replace_url(nodeid, rql, vid=None, swap=False, **extraparams):
-    """builds a replacePageChunk-like url
-    >>> ajax_replace_url('foo', 'Person P')
-    "javascript: replacePageChunk('foo', 'Person%20P');"
-    >>> ajax_replace_url('foo', 'Person P', 'oneline')
-    "javascript: replacePageChunk('foo', 'Person%20P', 'oneline');"
-    >>> ajax_replace_url('foo', 'Person P', 'oneline', name='bar', age=12)
-    "javascript: replacePageChunk('foo', 'Person%20P', 'oneline', {'age':12, 'name':'bar'});"
-    >>> ajax_replace_url('foo', 'Person P', name='bar', age=12)
-    "javascript: replacePageChunk('foo', 'Person%20P', 'null', {'age':12, 'name':'bar'});"    
-    """
-    params = [repr(nodeid), repr(urlquote(rql))]
-    if extraparams and not vid:
-        params.append("'null'")
-    elif vid:
-        params.append(repr(vid))
-    if extraparams:
-        params.append(simplejson.dumps(extraparams))
-    if swap:
-        params.append('true')
-    return "javascript: replacePageChunk(%s);" % ', '.join(params)
-
-
-from StringIO import StringIO
 
 def ureport_as_html(layout):
     from logilab.common.ureports import HTMLWriter
@@ -324,7 +292,7 @@
         else:
             for child in path[-1].children:
                 build_matrix(path[:] + [child], matrix)
-        
+
     matrix = []
     build_matrix([tree], matrix)
 
@@ -353,12 +321,12 @@
             cell_12 = line[j+1] is not None
             cell_21 = line[j+1] is not None and line[j+1].next_sibling() is not None
             link_type = link_types.get((cell_11, cell_12, cell_21), 0)
-            if link_type == 0 and i > 0 and links[i-1][j] in (1,2,3):
+            if link_type == 0 and i > 0 and links[i-1][j] in (1, 2, 3):
                 link_type = 2
             links[-1].append(link_type)
-    
+
 
-    # We can now generate the HTML code for the <table> 
+    # We can now generate the HTML code for the <table>
     s = u'<table class="tree">\n'
     if caption:
         s += '<caption>%s</caption>\n' % caption
@@ -378,7 +346,7 @@
                 s += '<td rowspan="2">&nbsp;</td>'
             s += '<td class="tree_cell_%d_1">&nbsp;</td>' % link_cell
             s += '<td class="tree_cell_%d_2">&nbsp;</td>' % link_cell
-                
+
         cell = line[-1]
         if cell:
             if cell.id == selected_node:
@@ -468,7 +436,7 @@
                            (boxid, ''.join(html_info)))
             tcbk = tcbk.tb_next
         except Exception:
-            pass # doesn't really matter if we have no context info    
+            pass # doesn't really matter if we have no context info
     strings.append(u'</div>')
     return '\n'.join(strings)
 
@@ -476,7 +444,7 @@
 
 class UnicodeCSVWriter:
     """proxies calls to csv.writer.writerow to be able to deal with unicode"""
-    
+
     def __init__(self, wfunc, encoding, **kwargs):
         self.writer = csv.writer(self, **kwargs)
         self.wfunc = wfunc
@@ -514,23 +482,6 @@
         return newfunc
 
 
-def jsonize(function):
-    import simplejson
-    def newfunc(*args, **kwargs):
-        ret = function(*args, **kwargs)
-        if isinstance(ret, decimal.Decimal):
-            ret = float(ret)
-        elif isinstance(ret, DateTimeType):
-            ret = ret.strftime('%Y-%m-%d %H:%M')
-        elif isinstance(ret, DateTimeDeltaType):
-            ret = ret.seconds
-        try:
-            return simplejson.dumps(ret)
-        except TypeError:
-            return simplejson.dumps(repr(ret))
-    return newfunc
-
-
 def htmlescape(function):
     def newfunc(*args, **kwargs):
         ret = function(*args, **kwargs)
--- a/common/utils.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/utils.py	Mon May 04 13:09:48 2009 +0200
@@ -1,263 +1,5 @@
-"""Some utilities for CubicWeb server/clients.
-
-:organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-from md5 import md5
-from time import time
-from random import randint, seed
-
-# initialize random seed from current time
-seed()
-
-def make_uid(key):
-    """forge a unique identifier"""
-    msg = str(key) + "%.10f"%time() + str(randint(0, 1000000))
-    return md5(msg).hexdigest()
-
-def working_hours(mxdate):
-    """
-    Predicate returning True is the date's hour is in working hours (8h->20h)
-    """
-    if mxdate.hour > 7 and mxdate.hour < 21:
-        return True
-    return False
-    
-def date_range(begin, end, incr=1, include=None):
-    """yields each date between begin and end
-    :param begin: the start date
-    :param end: the end date
-    :param incr: the step to use to iterate over dates. Default is
-                 one day.                 
-    :param include: None (means no exclusion) or a function taking a
-                    date as parameter, and returning True if the date
-                    should be included.
-    """
-    date = begin
-    while date <= end:
-        if include is None or include(date): 
-            yield date
-        date += incr
-
-
-def dump_class(cls, clsname):
-    """create copy of a class by creating an empty class inheriting
-    from the given cls.
-
-    Those class will be used as place holder for attribute and relation
-    description
-    """
-    # type doesn't accept unicode name
-    # return type.__new__(type, str(clsname), (cls,), {})
-    # __autogenerated__ attribute is just a marker
-    return type(str(clsname), (cls,), {'__autogenerated__': True})
-
-
-def merge_dicts(dict1, dict2):
-    """update a copy of `dict1` with `dict2`"""
-    dict1 = dict(dict1)
-    dict1.update(dict2)
-    return dict1
-                
-
-class SizeConstrainedList(list):
-    """simple list that makes sure the list does not get bigger
-    than a given size.
-
-    when the list is full and a new element is added, the first
-    element of the list is removed before appending the new one
-
-    >>> l = SizeConstrainedList(2)
-    >>> l.append(1)
-    >>> l.append(2)
-    >>> l
-    [1, 2]
-    >>> l.append(3)
-    [2, 3]
-    """
-    def __init__(self, maxsize):
-        self.maxsize = maxsize
-
-    def append(self, element):
-        if len(self) == self.maxsize:
-            del self[0]
-        super(SizeConstrainedList, self).append(element)
-
-    def extend(self, sequence):
-        super(SizeConstrainedList, self).extend(sequence)
-        keepafter = len(self) - self.maxsize
-        if keepafter > 0:
-            del self[:keepafter]
-
-    __iadd__ = extend
-
-
-class UStringIO(list):
-    """a file wrapper which automatically encode unicode string to an encoding
-    specifed in the constructor
-    """
-
-    def __nonzero__(self):
-        return True
-    
-    def write(self, value):
-        assert isinstance(value, unicode), u"unicode required not %s : %s"\
-                                     % (type(value).__name__, repr(value))
-        self.append(value)
-        
-    def getvalue(self):
-        return u''.join(self)
-
-    def __repr__(self):
-        return '<%s at %#x>' % (self.__class__.__name__, id(self))
-
-
-class HTMLHead(UStringIO):
-    """wraps HTML header's stream
-
-    Request objects use a HTMLHead instance to ease adding of
-    javascripts and stylesheets
-    """
-    js_unload_code = u'jQuery(window).unload(unloadPageData);'
-
-    def __init__(self):
-        super(HTMLHead, self).__init__()
-        self.jsvars = []
-        self.jsfiles = []
-        self.cssfiles = []
-        self.ie_cssfiles = []
-        self.post_inlined_scripts = []
-        self.pagedata_unload = False
-
-
-    def add_raw(self, rawheader):
-        self.write(rawheader)
-
-    def define_var(self, var, value):
-        self.jsvars.append( (var, value) )
-
-    def add_post_inline_script(self, content):
-        self.post_inlined_scripts.append(content)
-
-    def add_onload(self, jscode):
-        self.add_post_inline_script(u"""jQuery(document).ready(function () {
- %s
- });""" % jscode)
-        
-    
-    def add_js(self, jsfile):
-        """adds `jsfile` to the list of javascripts used in the webpage
-
-        This function checks if the file has already been added
-        :param jsfile: the script's URL
-        """
-        if jsfile not in self.jsfiles:
-            self.jsfiles.append(jsfile)
-
-    def add_css(self, cssfile, media):
-        """adds `cssfile` to the list of javascripts used in the webpage
-
-        This function checks if the file has already been added
-        :param cssfile: the stylesheet's URL
-        """
-        if (cssfile, media) not in self.cssfiles:
-            self.cssfiles.append( (cssfile, media) )
-
-    def add_ie_css(self, cssfile, media='all'):
-        """registers some IE specific CSS"""
-        if (cssfile, media) not in self.ie_cssfiles:
-            self.ie_cssfiles.append( (cssfile, media) )
-
-    def add_unload_pagedata(self):
-        """registers onunload callback to clean page data on server"""
-        if not self.pagedata_unload:
-            self.post_inlined_scripts.append(self.js_unload_code)
-            self.pagedata_unload = True
-
-    def getvalue(self):
-        """reimplement getvalue to provide a consistent (and somewhat browser
-        optimzed cf. http://stevesouders.com/cuzillion) order in external
-        resources declaration
-        """
-        w = self.write
-        # 1/ variable declaration if any
-        if self.jsvars:
-            from simplejson import dumps
-            w(u'<script type="text/javascript">\n')
-            for var, value in self.jsvars:
-                w(u'%s = %s;\n' % (var, dumps(value)))
-            w(u'</script>\n')
-        # 2/ css files
-        for cssfile, media in self.cssfiles:
-            w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
-              (media, cssfile))
-        # 3/ ie css if necessary
-        if self.ie_cssfiles:
-            w(u'<!--[if lt IE 8]>\n')
-            for cssfile, media in self.ie_cssfiles:
-                w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
-                  (media, cssfile))
-            w(u'<![endif]--> \n')
-        # 4/ js files
-        for jsfile in self.jsfiles:
-            w(u'<script type="text/javascript" src="%s"></script>\n' % jsfile)
-        # 5/ post inlined scripts (i.e. scripts depending on other JS files)
-        if self.post_inlined_scripts:
-            w(u'<script type="text/javascript">\n')
-            w(u'\n\n'.join(self.post_inlined_scripts))
-            w(u'\n</script>\n')
-        return u'<head>\n%s</head>\n' % super(HTMLHead, self).getvalue()
-        
-
-class HTMLStream(object):
-    """represents a HTML page.
-
-    This is used my main templates so that HTML headers can be added
-    at any time during the page generation.
-    
-    HTMLStream uses the (U)StringIO interface to be compliant with
-    existing code.
-    """
-    
-    def __init__(self, req):
-        # stream for <head>
-        self.head = req.html_headers
-        # main stream
-        self.body = UStringIO()
-        self.doctype = u''
-        # xmldecl and html opening tag
-        self.xmldecl = u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding
-        self.htmltag = u'<html xmlns="http://www.w3.org/1999/xhtml" ' \
-                       'xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" ' \
-                       'xml:lang="%s" lang="%s">' % (req.lang, req.lang)
-
-
-    def write(self, data):
-        """StringIO interface: this method will be assigned to self.w
-        """
-        self.body.write(data)
-
-    def getvalue(self):
-        """writes HTML headers, closes </head> tag and writes HTML body"""
-        return u'%s\n%s\n%s\n%s\n%s\n</html>' % (self.xmldecl, self.doctype,
-                                                 self.htmltag,
-                                                 self.head.getvalue(),
-                                                 self.body.getvalue())
-
-
-class AcceptMixIn(object):
-    """Mixin class for vobjects defining the 'accepts' attribute describing
-    a set of supported entity type (Any by default).
-    """
-    # XXX deprecated, no more necessary
-
-
-from logilab.common.deprecation import moved, class_moved
-rql_for_eid = moved('cubicweb.common.uilib', 'rql_for_eid')
-ajax_replace_url = moved('cubicweb.common.uilib', 'ajax_replace_url')
-
-import cubicweb
-Binary = class_moved(cubicweb.Binary)
+"""pre 3.2 bw compat"""
+# pylint: disable-msg=W0614,W0401
+from warnings import warn
+warn('moved to cubicweb.utils', DeprecationWarning, stacklevel=2)
+from cubicweb.utils import *
--- a/common/view.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/common/view.py	Mon May 04 13:09:48 2009 +0200
@@ -1,481 +1,5 @@
-"""abstract views and templates classes for CubicWeb web client
-
-
-:organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-from cStringIO import StringIO
-
-from logilab.mtconverter import html_escape
-
-from cubicweb import NotAnEntity, NoSelectableObject
-from cubicweb.common.registerers import accepts_registerer, priority_registerer
-from cubicweb.common.selectors import (chainfirst, match_user_group, accept,
-                                       nonempty_rset, empty_rset, none_rset)
-from cubicweb.common.appobject import AppRsetObject, ComponentMixIn
-from cubicweb.common.utils import UStringIO, HTMLStream
-
-_ = unicode
-
-# robots control
-NOINDEX = u'<meta name="ROBOTS" content="NOINDEX" />'
-NOFOLLOW = u'<meta name="ROBOTS" content="NOFOLLOW" />'
-
-CW_XHTML_EXTENSIONS = '''[
-  <!ATTLIST html xmlns:cubicweb CDATA  #FIXED \'http://www.logilab.org/2008/cubicweb\'  >
-
-<!ENTITY % coreattrs
- "id          ID            #IMPLIED
-  class       CDATA         #IMPLIED
-  style       CDATA         #IMPLIED
-  title       CDATA         #IMPLIED
-
- cubicweb:sortvalue         CDATA   #IMPLIED
- cubicweb:target            CDATA   #IMPLIED
- cubicweb:limit             CDATA   #IMPLIED
- cubicweb:type              CDATA   #IMPLIED
- cubicweb:loadtype          CDATA   #IMPLIED
- cubicweb:wdgtype           CDATA   #IMPLIED
- cubicweb:initfunc          CDATA   #IMPLIED
- cubicweb:inputid           CDATA   #IMPLIED
- cubicweb:tindex            CDATA   #IMPLIED
- cubicweb:inputname         CDATA   #IMPLIED
- cubicweb:value             CDATA   #IMPLIED
- cubicweb:required          CDATA   #IMPLIED
- cubicweb:accesskey         CDATA   #IMPLIED
- cubicweb:maxlength         CDATA   #IMPLIED
- cubicweb:variables         CDATA   #IMPLIED
- cubicweb:displayactions    CDATA   #IMPLIED
- cubicweb:fallbackvid       CDATA   #IMPLIED
- cubicweb:vid               CDATA   #IMPLIED
- cubicweb:rql               CDATA   #IMPLIED
- cubicweb:actualrql         CDATA   #IMPLIED
- cubicweb:rooteid           CDATA   #IMPLIED
- cubicweb:dataurl           CDATA   #IMPLIED
- cubicweb:size              CDATA   #IMPLIED
- cubicweb:tlunit            CDATA   #IMPLIED
- cubicweb:loadurl           CDATA   #IMPLIED
- cubicweb:uselabel          CDATA   #IMPLIED
- cubicweb:facetargs         CDATA   #IMPLIED
- cubicweb:facetName         CDATA   #IMPLIED
-  "> ] '''
-
-TRANSITIONAL_DOCTYPE = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" %s>\n'
-
-STRICT_DOCTYPE = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" %s>\n'
-
-class View(AppRsetObject):
-    """abstract view class, used as base for every renderable object such
-    as views, templates, some components...web
-
-    A view is instantiated to render a [part of a] result set. View
-    subclasses may be parametred using the following class attributes:
-
-    * `templatable` indicates if the view may be embeded in a main
-      template or if it has to be rendered standalone (i.e. XML for
-      instance)
-    * if the view is not templatable, it should set the `content_type` class
-      attribute to the correct MIME type (text/xhtml by default)
-    * the `category` attribute may be used in the interface to regroup related
-      objects together
-
-    At instantiation time, the standard `req`, `rset`, and `cursor`
-    attributes are added and the `w` attribute will be set at rendering
-    time to a write function to use.
-    """
-    __registry__ = 'views'
-
-    templatable = True
-    need_navigation = True
-    # content_type = 'application/xhtml+xml' # text/xhtml'
-    binary = False
-    add_to_breadcrumbs = True
-    category = 'view'
-
-    def __init__(self, req, rset):
-        super(View, self).__init__(req, rset)
-        self.w = None
-
-    @property
-    def content_type(self):
-        if self.req.xhtml_browser():
-            return 'application/xhtml+xml'
-        return 'text/html'
-
-    def set_stream(self, w=None):
-        if self.w is not None:
-            return
-        if w is None:
-            if self.binary:
-                self._stream = stream = StringIO()
-            else:
-                self._stream = stream = UStringIO()
-            w = stream.write
-        else:
-            stream = None
-        self.w = w
-        return stream
-
-    # main view interface #####################################################
-
-    def dispatch(self, w=None, **context):
-        """called to render a view object for a result set.
-
-        This method is a dispatched to an actual method selected
-        according to optional row and col parameters, which are locating
-        a particular row or cell in the result set:
-
-        * if row [and col] are specified, `cell_call` is called
-        * if none of them is supplied, the view is considered to apply on
-          the whole result set (which may be None in this case), `call` is
-          called
-        """
-        row, col = context.get('row'), context.get('col')
-        if row is not None:
-            context.setdefault('col', 0)
-            view_func = self.cell_call
-        else:
-            view_func = self.call
-        stream = self.set_stream(w)
-        # stream = self.set_stream(context)
-        view_func(**context)
-        # return stream content if we have created it
-        if stream is not None:
-            return self._stream.getvalue()
-
-    # should default .call() method add a <div classs="section"> around each
-    # rset item
-    add_div_section = True
-
-    def call(self, **kwargs):
-        """the view is called for an entire result set, by default loop
-        other rows of the result set and call the same view on the
-        particular row
-
-        Views applicable on None result sets have to override this method
-        """
-        rset = self.rset
-        if rset is None:
-            raise NotImplementedError, self
-        wrap = self.templatable and len(rset) > 1 and self.add_div_section
-        for i in xrange(len(rset)):
-            if wrap:
-                self.w(u'<div class="section">')
-            self.wview(self.id, rset, row=i, **kwargs)
-            if wrap:
-                self.w(u"</div>")
-
-    def cell_call(self, row, col, **kwargs):
-        """the view is called for a particular result set cell"""
-        raise NotImplementedError, self
-
-    def linkable(self):
-        """return True if the view may be linked in a menu
-
-        by default views without title are not meant to be displayed
-        """
-        if not getattr(self, 'title', None):
-            return False
-        return True
-
-    def is_primary(self):
-        return self.id == 'primary'
-
-    def url(self):
-        """return the url associated with this view. Should not be
-        necessary for non linkable views, but a default implementation
-        is provided anyway.
-        """
-        try:
-            return self.build_url(vid=self.id, rql=self.req.form['rql'])
-        except KeyError:
-            return self.build_url(vid=self.id)
-
-    def set_request_content_type(self):
-        """set the content type returned by this view"""
-        self.req.set_content_type(self.content_type)
-
-    # view utilities ##########################################################
-
-    def view(self, __vid, rset, __fallback_vid=None, **kwargs):
-        """shortcut to self.vreg.render method avoiding to pass self.req"""
-        try:
-            view = self.vreg.select_view(__vid, self.req, rset, **kwargs)
-        except NoSelectableObject:
-            if __fallback_vid is None:
-                raise
-            view = self.vreg.select_view(__fallback_vid, self.req, rset, **kwargs)
-        return view.dispatch(**kwargs)
-
-    def wview(self, __vid, rset, __fallback_vid=None, **kwargs):
-        """shortcut to self.view method automatically passing self.w as argument
-        """
-        self.view(__vid, rset, __fallback_vid, w=self.w, **kwargs)
-
-    def whead(self, data):
-        self.req.html_headers.write(data)
-
-    def wdata(self, data):
-        """simple helper that escapes `data` and writes into `self.w`"""
-        self.w(html_escape(data))
-
-    def action(self, actionid, row=0):
-        """shortcut to get action object with id `actionid`"""
-        return self.vreg.select_action(actionid, self.req, self.rset,
-                                       row=row)
-
-    def action_url(self, actionid, label=None, row=0):
-        """simple method to be able to display `actionid` as a link anywhere
-        """
-        action = self.vreg.select_action(actionid, self.req, self.rset,
-                                         row=row)
-        if action:
-            label = label or self.req._(action.title)
-            return u'<a href="%s">%s</a>' % (html_escape(action.url()), label)
-        return u''
-
-    def html_headers(self):
-        """return a list of html headers (eg something to be inserted between
-        <head> and </head> of the returned page
-
-        by default return a meta tag to disable robot indexation of the page
-        """
-        return [NOINDEX]
-
-    def page_title(self):
-        """returns a title according to the result set - used for the
-        title in the HTML header
-        """
-        vtitle = self.req.form.get('vtitle')
-        if vtitle:
-            return self.req._(vtitle)
-        # class defined title will only be used if the resulting title doesn't
-        # seem clear enough
-        vtitle = getattr(self, 'title', None) or u''
-        if vtitle:
-            vtitle = self.req._(vtitle)
-        rset = self.rset
-        if rset and rset.rowcount:
-            if rset.rowcount == 1:
-                try:
-                    entity = self.complete_entity(0)
-                    # use long_title to get context information if any
-                    clabel = entity.dc_long_title()
-                except NotAnEntity:
-                    clabel = display_name(self.req, rset.description[0][0])
-                    clabel = u'%s (%s)' % (clabel, vtitle)
-            else :
-                etypes = rset.column_types(0)
-                if len(etypes) == 1:
-                    etype = iter(etypes).next()
-                    clabel = display_name(self.req, etype, 'plural')
-                else :
-                    clabel = u'#[*] (%s)' % vtitle
-        else:
-            clabel = vtitle
-        return u'%s (%s)' % (clabel, self.req.property_value('ui.site-title'))
-
-    def output_url_builder( self, name, url, args ):
-        self.w(u'<script language="JavaScript"><!--\n' \
-               u'function %s( %s ) {\n' % (name, ','.join(args) ) )
-        url_parts = url.split("%s")
-        self.w(u' url="%s"' % url_parts[0] )
-        for arg, part in zip(args, url_parts[1:]):
-            self.w(u'+str(%s)' % arg )
-            if part:
-                self.w(u'+"%s"' % part)
-        self.w('\n document.window.href=url;\n')
-        self.w('}\n-->\n</script>\n')
-
-    def create_url(self, etype, **kwargs):
-        """ return the url of the entity creation form for a given entity type"""
-        return self.req.build_url('add/%s'%etype, **kwargs)
-
-
-# concrete views base classes #################################################
-
-class EntityView(View):
-    """base class for views applying on an entity (i.e. uniform result set)
-    """
-    __registerer__ = accepts_registerer
-    __selectors__ = (accept,)
-    accepts = ('Any',)
-    category = 'entityview'
-
-    def field(self, label, value, row=True, show_label=True, w=None, tr=True):
-        """ read-only field """
-        if w is None:
-            w = self.w
-        if row:
-            w(u'<div class="row">')
-        if show_label:
-            if tr:
-                label = display_name(self.req, label)
-            w(u'<span class="label">%s</span>' % label)
-        w(u'<div class="field">%s</div>' % value)
-        if row:
-            w(u'</div>')
-
-
-class StartupView(View):
-    """base class for views which doesn't need a particular result set
-    to be displayed (so they can always be displayed !)
-    """
-    __registerer__ = priority_registerer
-    __selectors__ = (match_user_group, none_rset)
-    require_groups = ()
-    category = 'startupview'
-
-    def url(self):
-        """return the url associated with this view. We can omit rql here"""
-        return self.build_url('view', vid=self.id)
-
-    def html_headers(self):
-        """return a list of html headers (eg something to be inserted between
-        <head> and </head> of the returned page
-
-        by default startup views are indexed
-        """
-        return []
-
-
-class EntityStartupView(EntityView):
-    """base class for entity views which may also be applied to None
-    result set (usually a default rql is provided by the view class)
-    """
-    __registerer__ = accepts_registerer
-    __selectors__ = (chainfirst(none_rset, accept),)
-
-    default_rql = None
-
-    def __init__(self, req, rset):
-        super(EntityStartupView, self).__init__(req, rset)
-        if rset is None:
-            # this instance is not in the "entityview" category
-            self.category = 'startupview'
-
-    def startup_rql(self):
-        """return some rql to be executedif the result set is None"""
-        return self.default_rql
-
-    def call(self, **kwargs):
-        """override call to execute rql returned by the .startup_rql
-        method if necessary
-        """
-        if self.rset is None:
-            self.rset = self.req.execute(self.startup_rql())
-        rset = self.rset
-        for i in xrange(len(rset)):
-            self.wview(self.id, rset, row=i, **kwargs)
-
-    def url(self):
-        """return the url associated with this view. We can omit rql if we
-        are on a result set on which we do not apply.
-        """
-        if not self.__select__(self.req, self.rset):
-            return self.build_url(vid=self.id)
-        return super(EntityStartupView, self).url()
-
-
-class AnyRsetView(View):
-    """base class for views applying on any non empty result sets"""
-    __registerer__ = priority_registerer
-    __selectors__ = (nonempty_rset,)
-
-    category = 'anyrsetview'
-
-    def columns_labels(self, tr=True):
-        if tr:
-            translate = display_name
-        else:
-            translate = lambda req, val: val
-        rqlstdescr = self.rset.syntax_tree().get_description()[0] # XXX missing Union support
-        labels = []
-        for colindex, attr in enumerate(rqlstdescr):
-            # compute column header
-            if colindex == 0 or attr == 'Any': # find a better label
-                label = ','.join(translate(self.req, et)
-                                 for et in self.rset.column_types(colindex))
-            else:
-                label = translate(self.req, attr)
-            labels.append(label)
-        return labels
-
-
-class EmptyRsetView(View):
-    """base class for views applying on any empty result sets"""
-    __registerer__ = priority_registerer
-    __selectors__ = (empty_rset,)
-
-
-# concrete template base classes ##############################################
-
-class Template(View):
-    """a template is almost like a view, except that by default a template
-    is only used globally (i.e. no result set adaptation)
-    """
-    __registry__ = 'templates'
-    __registerer__ = priority_registerer
-    __selectors__ = (match_user_group,)
-
-    require_groups = ()
-
-    def template(self, oid, **kwargs):
-        """shortcut to self.registry.render method on the templates registry"""
-        w = kwargs.pop('w', self.w)
-        self.vreg.render('templates', oid, self.req, w=w, **kwargs)
-
-
-class MainTemplate(Template):
-    """main template are primary access point to render a full HTML page.
-    There is usually at least a regular main template and a simple fallback
-    one to display error if the first one failed
-    """
-
-    base_doctype = STRICT_DOCTYPE
-
-    @property
-    def doctype(self):
-        if self.req.xhtml_browser():
-            return self.base_doctype % CW_XHTML_EXTENSIONS
-        return self.base_doctype % ''
-
-    def set_stream(self, w=None, templatable=True):
-        if templatable and self.w is not None:
-            return
-
-        if w is None:
-            if self.binary:
-                self._stream = stream = StringIO()
-            elif not templatable:
-                # not templatable means we're using a non-html view, we don't
-                # want the HTMLStream stuff to interfere during data generation
-                self._stream = stream = UStringIO()
-            else:
-                self._stream = stream = HTMLStream(self.req)
-            w = stream.write
-        else:
-            stream = None
-        self.w = w
-        return stream
-
-    def write_doctype(self, xmldecl=True):
-        assert isinstance(self._stream, HTMLStream)
-        self._stream.doctype = self.doctype
-        if not xmldecl:
-            self._stream.xmldecl = u''
-
-# viewable components base classes ############################################
-
-class VComponent(ComponentMixIn, View):
-    """base class for displayable components"""
-    property_defs = {
-        'visible':  dict(type='Boolean', default=True,
-                         help=_('display the component or not')),}
-
-class SingletonVComponent(VComponent):
-    """base class for displayable unique components"""
-    __registerer__ = priority_registerer
+"""pre 3.2 bw compat"""
+# pylint: disable-msg=W0614,W0401
+from warnings import warn
+warn('moved to cubicweb.view', DeprecationWarning, stacklevel=2)
+from cubicweb.view import *
--- a/cwconfig.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/cwconfig.py	Mon May 04 13:09:48 2009 +0200
@@ -4,6 +4,11 @@
 :organization: Logilab
 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+
+.. envvar:: CW_CUBES_PATH
+
+   Augments the default search path for cubes
+   
 """
 __docformat__ = "restructuredtext en"
 
@@ -18,7 +23,7 @@
                                           ConfigurationMixIn, merge_options)
 
 from cubicweb import CW_SOFTWARE_ROOT, CW_MIGRATION_MAP, ConfigurationError
-from cubicweb.toolsutils import env_path, read_config, create_dir
+from cubicweb.toolsutils import env_path, create_dir
 
 CONFIGURATIONS = []
 
@@ -56,17 +61,6 @@
                                  % (directory, modes))
     return modes[0]
 
-# XXX generate this according to the configuration (repository/all-in-one/web)
-VREGOPTIONS = []
-for registry in ('etypes', 'hooks', 'controllers', 'actions', 'components',
-                 'views', 'templates', 'boxes', 'contentnavigation', 'urlrewriting',
-                 'facets'):
-    VREGOPTIONS.append(('disable-%s'%registry,
-                        {'type' : 'csv', 'default': (),
-                         'help': 'list of identifier of application objects from the %s registry to disable'%registry,
-                         'group': 'appobjects', 'inputlevel': 2,
-                         }))
-VREGOPTIONS = tuple(VREGOPTIONS)
 
 # persistent options definition
 PERSISTENT_OPTIONS = (
@@ -75,44 +69,44 @@
       'default': 'UTF-8',
       'help': _('user interface encoding'),
       'group': 'ui', 'sitewide': True,
-      }),    
+      }),
     ('language',
      {'type' : 'string',
       'default': 'en',
       'vocabulary': Method('available_languages'),
       'help': _('language of the user interface'),
-      'group': 'ui', 
+      'group': 'ui',
       }),
     ('date-format',
      {'type' : 'string',
       'default': '%Y/%m/%d',
       'help': _('how to format date in the ui ("man strftime" for format description)'),
-      'group': 'ui', 
+      'group': 'ui',
       }),
     ('datetime-format',
      {'type' : 'string',
       'default': '%Y/%m/%d %H:%M',
       'help': _('how to format date and time in the ui ("man strftime" for format description)'),
-      'group': 'ui', 
+      'group': 'ui',
       }),
     ('time-format',
      {'type' : 'string',
       'default': '%H:%M',
       'help': _('how to format time in the ui ("man strftime" for format description)'),
-      'group': 'ui', 
+      'group': 'ui',
       }),
     ('float-format',
      {'type' : 'string',
       'default': '%.3f',
       'help': _('how to format float numbers in the ui'),
-      'group': 'ui', 
+      'group': 'ui',
       }),
     ('default-text-format',
      {'type' : 'choice',
       'choices': ('text/plain', 'text/rest', 'text/html'),
       'default': 'text/html', # use fckeditor in the web ui
       'help': _('default text format for rich text fields.'),
-      'group': 'ui', 
+      'group': 'ui',
       }),
     ('short-line-size',
      {'type' : 'int',
@@ -125,7 +119,7 @@
 def register_persistent_options(options):
     global PERSISTENT_OPTIONS
     PERSISTENT_OPTIONS = merge_options(PERSISTENT_OPTIONS + options)
-                
+
 CFGTYPE2ETYPE_MAP = {
     'string': 'String',
     'choice': 'String',
@@ -133,7 +127,7 @@
     'int':    'Int',
     'float' : 'Float',
     }
-    
+
 class CubicWebNoAppConfiguration(ConfigurationMixIn):
     """base class for cubicweb configuration without a specific instance directory
     """
@@ -157,7 +151,7 @@
         mode = 'installed'
         CUBES_DIR = '/usr/share/cubicweb/cubes/'
 
-    options = VREGOPTIONS + (
+    options = (
        ('log-threshold',
          {'type' : 'string', # XXX use a dedicated type?
           'default': 'ERROR',
@@ -195,6 +189,14 @@
           'help': 'web server root url',
           'group': 'main', 'inputlevel': 1,
           }),
+        ('use-request-subdomain',
+         {'type' : 'yn',
+          'default': None,
+          'help': ('if set, base-url subdomain is replaced by the request\'s '
+                   'host, to help managing sites with several subdomains in a '
+                   'single cubicweb instance'),
+          'group': 'main', 'inputlevel': 1,
+          }),
         ('mangle-emails',
          {'type' : 'yn',
           'default': False,
@@ -202,9 +204,14 @@
 this option is set to yes",
           'group': 'email', 'inputlevel': 2,
           }),
+        ('disable-appobjects',
+         {'type' : 'csv', 'default': (),
+          'help': 'comma separated list of identifiers of application objects (<registry>.<oid>) to disable',
+          'group': 'appobjects', 'inputlevel': 2,
+          }),
         )
     # static and class methods used to get application independant resources ##
-        
+
     @staticmethod
     def cubicweb_version():
         """return installed cubicweb version"""
@@ -213,7 +220,7 @@
         version = __pkginfo__.numversion
         assert len(version) == 3, version
         return Version(version)
-    
+
     @staticmethod
     def persistent_options_configuration():
         return Configuration(options=PERSISTENT_OPTIONS)
@@ -226,7 +233,7 @@
         if cls.mode in ('dev', 'test') and not os.environ.get('APYCOT_ROOT'):
             return join(CW_SOFTWARE_ROOT, 'web')
         return cls.cube_dir('shared')
-        
+
     @classmethod
     def i18n_lib_dir(cls):
         """return application's i18n directory"""
@@ -242,7 +249,7 @@
                 if isdir(join(directory, cube)) and not cube in ('CVS', '.svn', 'shared', '.hg'):
                     cubes.add(cube)
         return sorted(cubes)
-    
+
     @classmethod
     def cubes_search_path(cls):
         """return the path of directories where cubes should be searched"""
@@ -257,7 +264,7 @@
         if not cls.CUBES_DIR in path:
             path.append(cls.CUBES_DIR)
         return path
-    
+
     @classmethod
     def cube_dir(cls, cube):
         """return the cube directory for the given cube id,
@@ -273,7 +280,7 @@
     def cube_migration_scripts_dir(cls, cube):
         """cube migration scripts directory"""
         return join(cls.cube_dir(cube), 'migration')
-    
+
     @classmethod
     def cube_pkginfo(cls, cube):
         """return the information module for the given cube"""
@@ -286,7 +293,7 @@
 
     @classmethod
     def cube_version(cls, cube):
-        """return the version of the cube located in the given directory        
+        """return the version of the cube located in the given directory
         """
         from logilab.common.changelog import Version
         version = cls.cube_pkginfo(cube).numversion
@@ -349,7 +356,7 @@
                         except KeyError:
                             continue
         return tuple(reversed(cubes))
-    
+
     @classmethod
     def cls_adjust_sys_path(cls):
         """update python path if necessary"""
@@ -387,7 +394,7 @@
                 except:
                     cls.exception('while loading cube %s', cube)
             else:
-                cls.warning('no __init__ file in cube %s', cube) 
+                cls.warning('no __init__ file in cube %s', cube)
 
     @classmethod
     def init_available_cubes(cls):
@@ -399,7 +406,7 @@
                 __import__('cubes.%s' % cube)
             except Exception, ex:
                 cls.warning("can't init cube %s: %s", cube, ex)
-        
+
     cubicweb_vobject_path = set(['entities'])
     cube_vobject_path = set(['entities'])
 
@@ -447,17 +454,17 @@
                 elif exists(path + '.py'):
                     vregpath.append(path + '.py')
         return vregpath
-        
+
     def __init__(self):
         ConfigurationMixIn.__init__(self)
         self.adjust_sys_path()
         self.load_defaults()
-        self.translations = {} 
+        self.translations = {}
 
     def adjust_sys_path(self):
         self.cls_adjust_sys_path()
-        
-    def init_log(self, logthreshold=None, debug=False, 
+
+    def init_log(self, logthreshold=None, debug=False,
                  logfile=None, syslog=False):
         """init the log service"""
         if logthreshold is None:
@@ -474,7 +481,7 @@
         for application objects. By default return nothing in NoApp config.
         """
         return []
-    
+
     def eproperty_definitions(self):
         cfg = self.persistent_options_configuration()
         for section, options in cfg.options_by_section():
@@ -487,7 +494,7 @@
                         'help': optdict['help'],
                         'sitewide': optdict.get('sitewide', False)}
                 yield key, pdef
-                
+
     def map_option(self, optdict):
         try:
             vocab = optdict['choices']
@@ -497,10 +504,10 @@
                 vocab = getattr(self, vocab.method, ())
         return CFGTYPE2ETYPE_MAP[optdict['type']], vocab
 
-    
+
 class CubicWebConfiguration(CubicWebNoAppConfiguration):
     """base class for cubicweb server and web configurations"""
-    
+
     INSTANCE_DATA_DIR = None
     if CubicWebNoAppConfiguration.mode == 'test':
         root = os.environ['APYCOT_ROOT']
@@ -523,7 +530,7 @@
     set_language = True
     # set this to true to avoid false error message while creating an application
     creating = False
-    
+
     options = CubicWebNoAppConfiguration.options + (
         ('log-file',
          {'type' : 'string',
@@ -546,7 +553,7 @@
           }),
         ('sender-name',
          {'type' : 'string',
-          'default': Method('default_application_id'), 
+          'default': Method('default_application_id'),
           'help': 'name used as HELO name for outgoing emails from the \
 repository.',
           'group': 'email', 'inputlevel': 2,
@@ -564,7 +571,7 @@
     def runtime_dir(cls):
         """run time directory for pid file..."""
         return env_path('CW_RUNTIME', cls.RUNTIME_DIR, 'run time')
-    
+
     @classmethod
     def registry_dir(cls):
         """return the control directory"""
@@ -576,7 +583,7 @@
         return env_path('CW_INSTANCE_DATA',
                         cls.INSTANCE_DATA_DIR or cls.REGISTRY_DIR,
                         'additional data')
-        
+
     @classmethod
     def migration_scripts_dir(cls):
         """cubicweb migration scripts directory"""
@@ -589,7 +596,7 @@
         config = config or guess_configuration(cls.application_home(appid))
         configcls = configuration_cls(config)
         return configcls(appid)
-    
+
     @classmethod
     def possible_configurations(cls, appid):
         """return the name of possible configurations for the given
@@ -597,7 +604,7 @@
         """
         home = cls.application_home(appid)
         return possible_configurations(home)
-    
+
     @classmethod
     def application_home(cls, appid):
         """return the home directory of the application with the given
@@ -616,9 +623,9 @@
     def accept_mode(cls, mode):
         #assert mode in cls.MODES, mode
         return mode in cls.MCOMPAT[cls.name]
-            
+
     # default configuration methods ###########################################
-    
+
     def default_application_id(self):
         """return the application identifier, useful for option which need this
         as default value
@@ -640,13 +647,13 @@
                     i += 1
             return path
         return '/var/log/cubicweb/%s-%s.log' % (self.appid, self.name)
-    
+
     def default_pid_file(self):
         """return default path to the pid file of the application'server"""
         return join(self.runtime_dir(), '%s-%s.pid' % (self.appid, self.name))
-    
+
     # instance methods used to get application specific resources #############
-    
+
     def __init__(self, appid):
         self.appid = appid
         CubicWebNoAppConfiguration.__init__(self)
@@ -664,11 +671,11 @@
     @property
     def apphome(self):
         return join(self.registry_dir(), self.appid)
-    
+
     @property
     def appdatahome(self):
         return join(self.instance_data_dir(), self.appid)
-        
+
     def init_cubes(self, cubes):
         assert self._cubes is None
         self._cubes = self.reorder_cubes(cubes)
@@ -681,7 +688,7 @@
         self.load_file_configuration(self.main_config_file())
         # configuration initialization hook
         self.load_configuration()
-        
+
     def cubes(self):
         """return the list of cubes used by this instance
 
@@ -690,7 +697,7 @@
         """
         assert self._cubes is not None
         return self._cubes
-        
+
     def cubes_path(self):
         """return the list of path to cubes used by this instance, from outer
         most to inner most cubes
@@ -702,11 +709,11 @@
         if not isinstance(cubes, list):
             cubes = list(cubes)
         self._cubes = self.reorder_cubes(list(self._cubes) + cubes)
-        
+
     def main_config_file(self):
         """return application's control configuration file"""
         return join(self.apphome, '%s.conf' % self.name)
-            
+
     def save(self):
         """write down current configuration"""
         self.generate_config(open(self.main_config_file(), 'w'))
@@ -719,7 +726,7 @@
             version = self.cube_version(pkg)
             infos.append('%s-%s' % (pkg, version))
         return md5.new(';'.join(infos)).hexdigest()
-                
+
     def load_site_cubicweb(self):
         """load (web?) application's specific site_cubicweb file"""
         for path in reversed([self.apphome] + self.cubes_path()):
@@ -733,7 +740,7 @@
                     self._load_site_cubicweb(sitefile)
                     self._site_loaded.add(sitefile)
                     self.warning('site_erudi.py is deprecated, should be renamed to site_cubicweb.py')
-                
+
     def _load_site_cubicweb(self, sitefile):
         context = {}
         execfile(sitefile, context, context)
@@ -742,14 +749,14 @@
         if context.get('options'):
             self.register_options(context['options'])
             self.load_defaults()
-                
+
     def load_configuration(self):
         """load application's configuration files"""
         super(CubicWebConfiguration, self).load_configuration()
         if self.apphome and self.set_language:
             # init gettext
             self._set_language()
-            
+
     def init_log(self, logthreshold=None, debug=False, force=False):
         """init the log service"""
         if not force and hasattr(self, '_logging_initialized'):
@@ -775,7 +782,7 @@
             lang = path.split(os.sep)[-3]
             if lang != 'en':
                 yield lang
-        
+
     def _set_language(self):
         """set language for gettext"""
         from gettext import translation
@@ -787,8 +794,8 @@
                 self.translations[language] = tr.ugettext
             except (ImportError, AttributeError, IOError):
                 self.exception('localisation support error for language %s',
-                               language)            
-    
+                               language)
+
     def vregistry_path(self):
         """return a list of files or directories where the registry will look
         for application objects
@@ -802,7 +809,7 @@
         if not 'all' in sources:
             print 'warning: ignoring specified sources, requires a repository '\
                   'configuration'
-        
+
     def migration_handler(self):
         """return a migration handler instance"""
         from cubicweb.common.migration import MigrationHelper
@@ -820,7 +827,7 @@
         return i18n.compile_i18n_catalogs(sourcedirs, i18ndir, langs)
 
 set_log_methods(CubicWebConfiguration, logging.getLogger('cubicweb.configuration'))
-        
+
 # alias to get a configuration instance from an application id
-application_configuration = CubicWebConfiguration.config_for        
+application_configuration = CubicWebConfiguration.config_for
 
--- a/cwctl.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/cwctl.py	Mon May 04 13:09:48 2009 +0200
@@ -1,17 +1,18 @@
 """%%prog %s [options] %s
 
-CubicWeb main applications controller. 
+CubicWeb main applications controller.
 %s"""
 
 import sys
-from os import remove, listdir, system, kill, getpgid
+from os import remove, listdir, system, kill, getpgid, pathsep
 from os.path import exists, join, isfile, isdir
 
+from logilab.common.clcommands import register_commands, pop_arg
+
 from cubicweb import ConfigurationError, ExecutionError, BadCommandUsage
-from cubicweb.cwconfig import CubicWebConfiguration, CONFIGURATIONS
-from cubicweb.toolsutils import (Command, register_commands, main_run, 
-                                 rm, create_dir, pop_arg, confirm)
-    
+from cubicweb.cwconfig import CubicWebConfiguration as cwcfg, CONFIGURATIONS
+from cubicweb.toolsutils import Command, main_run,  rm, create_dir, confirm
+
 def wait_process_end(pid, maxtry=10, waittime=1):
     """wait for a process to actually die"""
     import signal
@@ -41,13 +42,13 @@
             modes.append('web ui')
             break
     return modes
-    
-    
+
+
 class ApplicationCommand(Command):
     """base class for command taking 0 to n application id as arguments
     (0 meaning all registered applications)
     """
-    arguments = '[<application>...]'    
+    arguments = '[<application>...]'
     options = (
         ("force",
          {'short': 'f', 'action' : 'store_true',
@@ -57,13 +58,13 @@
          ),
         )
     actionverb = None
-    
+
     def ordered_instances(self):
         """return instances in the order in which they should be started,
         considering $REGISTRY_DIR/startorder file if it exists (useful when
         some instances depends on another as external source
         """
-        regdir = CubicWebConfiguration.registry_dir()
+        regdir = cwcfg.registry_dir()
         _allinstances = list_instances(regdir)
         if isfile(join(regdir, 'startorder')):
             allinstances = []
@@ -74,12 +75,13 @@
                         _allinstances.remove(line)
                         allinstances.append(line)
                     except ValueError:
-                        print 'ERROR: startorder file contains unexistant instance %s' % line
+                        print ('ERROR: startorder file contains unexistant '
+                               'instance %s' % line)
             allinstances += _allinstances
         else:
             allinstances = _allinstances
         return allinstances
-    
+
     def run(self, args):
         """run the <command>_method on each argument (a list of application
         identifiers)
@@ -94,7 +96,7 @@
         else:
             askconfirm = False
         self.run_args(args, askconfirm)
-        
+
     def run_args(self, args, askconfirm):
         for appid in args:
             if askconfirm:
@@ -102,7 +104,7 @@
                 if not confirm('%s application %r ?' % (self.name, appid)):
                     continue
             self.run_arg(appid)
-            
+
     def run_arg(self, appid):
         cmdmeth = getattr(self, '%s_application' % self.name)
         try:
@@ -141,7 +143,7 @@
                     sys.exit(status)
             else:
                 self.run_arg(appid)
-    
+
 # base commands ###############################################################
 
 class ListCommand(Command):
@@ -153,16 +155,16 @@
     name = 'list'
     options = (
         ('verbose',
-         {'short': 'v', 'action' : 'store_true', 
-          'help': "display more information."}),        
+         {'short': 'v', 'action' : 'store_true',
+          'help': "display more information."}),
         )
-    
+
     def run(self, args):
         """run the command with its specific arguments"""
         if args:
             raise BadCommandUsage('Too much arguments')
-        print 'CubicWeb version:', CubicWebConfiguration.cubicweb_version()
-        print 'Detected mode:', CubicWebConfiguration.mode
+        print 'CubicWeb version:', cwcfg.cubicweb_version()
+        print 'Detected mode:', cwcfg.mode
         print
         print 'Available configurations:'
         for config in CONFIGURATIONS:
@@ -172,21 +174,21 @@
                 if not line:
                     continue
                 print '   ', line
-        print 
-        cubesdirs = ', '.join(CubicWebConfiguration.cubes_search_path())
+        print
         try:
-            namesize = max(len(x) for x in CubicWebConfiguration.available_cubes())
+            cubesdir = pathsep.join(cwcfg.cubes_search_path())
+            namesize = max(len(x) for x in cwcfg.available_cubes())
         except ConfigurationError, ex:
             print 'No cubes available:', ex
         except ValueError:
-            print 'No cubes available in %s' % cubesdirs
+            print 'No cubes available in %s' % cubesdir
         else:
-            print 'Available cubes (%s):' % cubesdirs
-            for cube in CubicWebConfiguration.available_cubes():
+            print 'Available cubes (%s):' % cubesdir
+            for cube in cwcfg.available_cubes():
                 if cube in ('CVS', '.svn', 'shared', '.hg'):
                     continue
                 try:
-                    tinfo = CubicWebConfiguration.cube_pkginfo(cube)
+                    tinfo = cwcfg.cube_pkginfo(cube)
                     tversion = tinfo.version
                 except ConfigurationError:
                     tinfo = None
@@ -201,7 +203,7 @@
                     print '    available modes: %s' % ', '.join(modes)
         print
         try:
-            regdir = CubicWebConfiguration.registry_dir()
+            regdir = cwcfg.registry_dir()
         except ConfigurationError, ex:
             print 'No application available:', ex
             print
@@ -210,14 +212,14 @@
         if instances:
             print 'Available applications (%s):' % regdir
             for appid in instances:
-                modes = CubicWebConfiguration.possible_configurations(appid)
+                modes = cwcfg.possible_configurations(appid)
                 if not modes:
                     print '* %s (BROKEN application, no configuration found)' % appid
                     continue
                 print '* %s (%s)' % (appid, ', '.join(modes))
                 try:
-                    config = CubicWebConfiguration.config_for(appid, modes[0])
-                except Exception, exc: 
+                    config = cwcfg.config_for(appid, modes[0])
+                except Exception, exc:
                     print '    (BROKEN application, %s)' % exc
                     continue
         else:
@@ -259,7 +261,7 @@
           }
          ),
         )
-    
+
     def run(self, args):
         """run the command with its specific arguments"""
         from logilab.common.textutils import get_csv
@@ -267,19 +269,19 @@
         cubes = get_csv(pop_arg(args, 1))
         appid = pop_arg(args)
         # get the configuration and helper
-        CubicWebConfiguration.creating = True
-        config = CubicWebConfiguration.config_for(appid, configname)
+        cwcfg.creating = True
+        config = cwcfg.config_for(appid, configname)
         config.set_language = False
         config.init_cubes(config.expand_cubes(cubes))
         helper = self.config_helper(config)
         # check the cube exists
         try:
-            templdirs = [CubicWebConfiguration.cube_dir(cube)
+            templdirs = [cwcfg.cube_dir(cube)
                          for cube in cubes]
         except ConfigurationError, ex:
             print ex
             print '\navailable cubes:',
-            print ', '.join(CubicWebConfiguration.available_cubes())
+            print ', '.join(cwcfg.available_cubes())
             return
         # create the registry directory for this application
         create_dir(config.apphome)
@@ -295,7 +297,6 @@
         # write down configuration
         config.save()
         # handle i18n files structure
-        # XXX currently available languages are guessed from translations found
         # in the first cube given
         from cubicweb.common import i18n
         langs = [lang for lang, _ in i18n.available_catalogs(join(templdirs[0], 'i18n'))]
@@ -322,21 +323,21 @@
         print
         helper.postcreate()
 
-    
+
 class DeleteApplicationCommand(Command):
     """Delete an application. Will remove application's files and
     unregister it.
     """
     name = 'delete'
     arguments = '<application>'
-    
+
     options = ()
 
     def run(self, args):
         """run the command with its specific arguments"""
         appid = pop_arg(args, msg="No application specified !")
-        configs = [CubicWebConfiguration.config_for(appid, configname)
-                   for configname in CubicWebConfiguration.possible_configurations(appid)]
+        configs = [cwcfg.config_for(appid, configname)
+                   for configname in cwcfg.possible_configurations(appid)]
         if not configs:
             raise ExecutionError('unable to guess configuration for %s' % appid)
         for config in configs:
@@ -360,7 +361,7 @@
 
 class StartApplicationCommand(ApplicationCommand):
     """Start the given applications. If no application is given, start them all.
-    
+
     <application>...
       identifiers of the applications to start. If no application is
       given, start them all.
@@ -389,7 +390,7 @@
         # without all options defined
         debug = self.get('debug')
         force = self.get('force')
-        config = CubicWebConfiguration.config_for(appid)
+        config = cwcfg.config_for(appid)
         if self.get('profile'):
             config.global_set_option('profile', self.config.profile)
         helper = self.config_helper(config, cmdname='start')
@@ -413,22 +414,22 @@
 
 class StopApplicationCommand(ApplicationCommand):
     """Stop the given applications.
-    
+
     <application>...
       identifiers of the applications to stop. If no application is
       given, stop them all.
     """
     name = 'stop'
     actionverb = 'stopped'
-    
+
     def ordered_instances(self):
         instances = super(StopApplicationCommand, self).ordered_instances()
         instances.reverse()
         return instances
-    
+
     def stop_application(self, appid):
         """stop the application's server"""
-        config = CubicWebConfiguration.config_for(appid)
+        config = cwcfg.config_for(appid)
         helper = self.config_helper(config, cmdname='stop')
         helper.poststop() # do this anyway
         pidf = config['pid-file']
@@ -459,12 +460,12 @@
             # already removed by twistd
             pass
         print 'application %s stopped' % appid
-    
+
 
 class RestartApplicationCommand(StartApplicationCommand,
                                 StopApplicationCommand):
     """Restart the given applications.
-    
+
     <application>...
       identifiers of the applications to restart. If no application is
       given, restart them all.
@@ -473,7 +474,7 @@
     actionverb = 'restarted'
 
     def run_args(self, args, askconfirm):
-        regdir = CubicWebConfiguration.registry_dir()
+        regdir = cwcfg.registry_dir()
         if not isfile(join(regdir, 'startorder')) or len(args) <= 1:
             # no specific startorder
             super(RestartApplicationCommand, self).run_args(args, askconfirm)
@@ -496,30 +497,30 @@
             status = system('%s %s' % (forkcmd, appid))
             if status:
                 sys.exit(status)
-    
+
     def restart_application(self, appid):
         self.stop_application(appid)
         if self.start_application(appid):
             print 'application %s %s' % (appid, self.actionverb)
 
-        
+
 class ReloadConfigurationCommand(RestartApplicationCommand):
     """Reload the given applications. This command is equivalent to a
     restart for now.
-    
+
     <application>...
       identifiers of the applications to reload. If no application is
       given, reload them all.
     """
     name = 'reload'
-    
+
     def reload_application(self, appid):
         self.restart_application(appid)
-    
+
 
 class StatusCommand(ApplicationCommand):
     """Display status information about the given applications.
-    
+
     <application>...
       identifiers of the applications to status. If no application is
       given, get status information about all registered applications.
@@ -527,10 +528,11 @@
     name = 'status'
     options = ()
 
-    def status_application(self, appid):
+    @staticmethod
+    def status_application(appid):
         """print running status information for an application"""
-        for mode in CubicWebConfiguration.possible_configurations(appid):
-            config = CubicWebConfiguration.config_for(appid, mode)
+        for mode in cwcfg.possible_configurations(appid):
+            config = cwcfg.config_for(appid, mode)
             print '[%s-%s]' % (appid, mode),
             try:
                 pidf = config['pid-file']
@@ -574,7 +576,7 @@
          {'short': 'e', 'type' : 'string', 'metavar': 'X.Y.Z',
           'default': None,
           'help': 'force migration from the indicated cubicweb version.'}),
-        
+
         ('fs-only',
          {'short': 's', 'action' : 'store_true',
           'default': False,
@@ -584,13 +586,13 @@
          {'short': 'n', 'action' : 'store_true',
           'default': False,
           'help': 'don\'t try to stop application before migration and to restart it after.'}),
-        
+
         ('verbosity',
          {'short': 'v', 'type' : 'int', 'metavar': '<0..2>',
           'default': 1,
           'help': "0: no confirmation, 1: only main commands confirmed, 2 ask \
 for everything."}),
-        
+
         ('backup-db',
          {'short': 'b', 'type' : 'yn', 'metavar': '<y or n>',
           'default': None,
@@ -611,10 +613,10 @@
     def ordered_instances(self):
         # need this since mro return StopApplicationCommand implementation
         return ApplicationCommand.ordered_instances(self)
-    
+
     def upgrade_application(self, appid):
         from logilab.common.changelog import Version
-        config = CubicWebConfiguration.config_for(appid)
+        config = cwcfg.config_for(appid)
         config.creating = True # notice we're not starting the server
         config.verbosity = self.config.verbosity
         try:
@@ -644,7 +646,7 @@
                 continue
             if installedversion > applversion:
                 toupgrade.append( (cube, applversion, installedversion) )
-        cubicwebversion = config.cubicweb_version()           
+        cubicwebversion = config.cubicweb_version()
         if self.config.force_cubicweb_version:
             applcubicwebversion = Version(self.config.force_cubicweb_version)
             vcconf['cubicweb'] = applcubicwebversion
@@ -658,7 +660,7 @@
         for cube, fromversion, toversion in toupgrade:
             print '**** %s migration %s -> %s' % (cube, fromversion, toversion)
         # only stop once we're sure we have something to do
-        if not (CubicWebConfiguration.mode == 'dev' or self.config.nostartstop):
+        if not (cwcfg.mode == 'dev' or self.config.nostartstop):
             self.stop_application(appid)
         # run cubicweb/componants migration scripts
         mih.migrate(vcconf, reversed(toupgrade), self.config)
@@ -667,10 +669,9 @@
         # handle i18n upgrade:
         # * install new languages
         # * recompile catalogs
-        # XXX currently available languages are guessed from translations found
         # in the first componant given
         from cubicweb.common import i18n
-        templdir = CubicWebConfiguration.cube_dir(config.cubes()[0])
+        templdir = cwcfg.cube_dir(config.cubes()[0])
         langs = [lang for lang, _ in i18n.available_catalogs(join(templdir, 'i18n'))]
         errors = config.i18ncompile(langs)
         if errors:
@@ -683,7 +684,7 @@
         mih.shutdown()
         print
         print 'application migrated'
-        if not (CubicWebConfiguration.mode == 'dev' or self.config.nostartstop):
+        if not (cwcfg.mode == 'dev' or self.config.nostartstop):
             self.start_application(appid)
         print
 
@@ -706,7 +707,7 @@
           'help': 'only connect to the system source when the instance is '
           'using multiple sources. You can\'t use this option and the '
           '--ext-sources option at the same time.'}),
-        
+
         ('ext-sources',
          {'short': 'E', 'type' : 'csv', 'metavar': '<sources>',
           'default': None,
@@ -715,11 +716,11 @@
 will connect to all defined sources. If 'migration' is given, appropriate \
 sources for migration will be automatically selected.",
           }),
-        
+
         )
     def run(self, args):
         appid = pop_arg(args, 99, msg="No application specified !")
-        config = CubicWebConfiguration.config_for(appid)
+        config = cwcfg.config_for(appid)
         if self.config.ext_sources:
             assert not self.config.system_only
             sources = self.config.ext_sources
@@ -733,21 +734,22 @@
             mih.scripts_session(args)
         else:
             mih.interactive_shell()
-        mih.shutdown() 
+        mih.shutdown()
 
 
 class RecompileApplicationCatalogsCommand(ApplicationCommand):
     """Recompile i18n catalogs for applications.
-    
+
     <application>...
       identifiers of the applications to consider. If no application is
       given, recompile for all registered applications.
     """
     name = 'i18ncompile'
-    
-    def i18ncompile_application(self, appid):
+
+    @staticmethod
+    def i18ncompile_application(appid):
         """recompile application's messages catalogs"""
-        config = CubicWebConfiguration.config_for(appid)
+        config = cwcfg.config_for(appid)
         try:
             config.bootstrap_cubes()
         except IOError, ex:
@@ -771,10 +773,10 @@
     """list available instances, useful for bash completion."""
     name = 'listinstances'
     hidden = True
-    
+
     def run(self, args):
         """run the command with its specific arguments"""
-        regdir = CubicWebConfiguration.registry_dir()
+        regdir = cwcfg.registry_dir()
         for appid in sorted(listdir(regdir)):
             print appid
 
@@ -783,10 +785,10 @@
     """list available componants, useful for bash completion."""
     name = 'listcubes'
     hidden = True
-    
+
     def run(self, args):
         """run the command with its specific arguments"""
-        for cube in CubicWebConfiguration.available_cubes():
+        for cube in cwcfg.available_cubes():
             print cube
 
 register_commands((ListCommand,
@@ -803,10 +805,10 @@
                    ListInstancesCommand, ListCubesCommand,
                    ))
 
-                
+
 def run(args):
     """command line tool"""
-    CubicWebConfiguration.load_cwctl_plugins()
+    cwcfg.load_cwctl_plugins()
     main_run(args, __doc__)
 
 if __name__ == '__main__':
--- a/cwvreg.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/cwvreg.py	Mon May 04 13:09:48 2009 +0200
@@ -6,8 +6,6 @@
 """
 __docformat__ = "restructuredtext en"
 
-from warnings import warn
-
 from logilab.common.decorators import cached, clear_cache
 
 from rql import RQLHelper
@@ -17,16 +15,30 @@
 
 _ = unicode
 
-class DummyCursorError(Exception): pass
-class RaiseCursor:
-    @classmethod
-    def execute(cls, rql, args=None, eid_key=None):
-        raise DummyCursorError()
+def use_interfaces(obj):
+    """return interfaces used by the given object by searchinf for implements
+    selectors, with a bw compat fallback to accepts_interfaces attribute
+    """
+    from cubicweb.selectors import implements
+    try:
+        # XXX deprecated
+        return sorted(obj.accepts_interfaces)
+    except AttributeError:
+        try:
+            impl = obj.__select__.search_selector(implements)
+            if impl:
+                return sorted(impl.expected_ifaces)
+        except AttributeError:
+            pass # old-style vobject classes with no accepts_interfaces
+        except:
+            print 'bad selector %s on %s' % (obj.__select__, obj)
+            raise
+        return ()
 
 
 class CubicWebRegistry(VRegistry):
     """extend the generic VRegistry with some cubicweb specific stuff"""
-    
+
     def __init__(self, config, debug=None, initlog=True):
         if initlog:
             # first init log service
@@ -35,32 +47,34 @@
         self.schema = None
         self.reset()
         self.initialized = False
-        
+
     def items(self):
         return [item for item in self._registries.items()
                 if not item[0] in ('propertydefs', 'propertyvalues')]
 
     def values(self):
-        return [value for key,value in self._registries.items()
+        return [value for key, value in self._registries.items()
                 if not key in ('propertydefs', 'propertyvalues')]
-    
+
     def reset(self):
         self._registries = {}
         self._lastmodifs = {}
-        # two special registries, propertydefs which care all the property definitions, and
-        # propertyvals which contains values for those properties
+        self._needs_iface = {}
+        # two special registries, propertydefs which care all the property
+        # definitions, and propertyvals which contains values for those
+        # properties
         self._registries['propertydefs'] = {}
         self._registries['propertyvalues'] = self.eprop_values = {}
         for key, propdef in self.config.eproperty_definitions():
             self.register_property(key, **propdef)
-            
+
     def set_schema(self, schema):
         """set application'schema and load application objects"""
         self.schema = schema
         clear_cache(self, 'rqlhelper')
         # now we can load application's web objects
         self.register_objects(self.config.vregistry_path())
-        
+
     def update_schema(self, schema):
         """update .schema attribute on registered objects, necessary for some
         tests
@@ -72,56 +86,70 @@
             for objects in regcontent.values():
                 for obj in objects:
                     obj.schema = schema
-        
-    def register_objects(self, path, force_reload=None):
-        """overriden to handle type class cache issue"""
-        if  super(CubicWebRegistry, self).register_objects(path, force_reload):
-            # clear etype cache if you don't want to run into deep weirdness
-            clear_cache(self, 'etype_class')
-            # remove vobjects that don't support any available interface
-            interfaces = set()
-            for classes in self.get('etypes', {}).values():
-                for cls in classes:
-                    interfaces.update(cls.__implements__)
-            if not self.config.cleanup_interface_sobjects:
+
+    def register_if_interface_found(self, obj, ifaces, **kwargs):
+        """register an object but remove it if no entity class implements one of
+        the given interfaces
+        """
+        self.register(obj, **kwargs)
+        if not isinstance(ifaces,  (tuple, list)):
+            self._needs_iface[obj] = (ifaces,)
+        else:
+            self._needs_iface[obj] = ifaces
+
+    def register(self, obj, **kwargs):
+        if kwargs.get('registryname', obj.__registry__) == 'etypes':
+            if obj.id != 'Any' and not obj.id in self.schema:
+                self.error('don\'t register %s, %s type not defined in the '
+                           'schema', obj, obj.id)
                 return
-            for registry, regcontent in self._registries.items():
-                if registry in ('propertydefs', 'propertyvalues', 'etypes'):
-                    continue
-                for oid, objects in regcontent.items():
-                    for obj in reversed(objects[:]):
-                        if not obj in objects:
-                            continue # obj was kicked by a previous one
-                        accepted = set(getattr(obj, 'accepts_interfaces', ()))
-                        if accepted:
-                            for accepted_iface in accepted:
-                                for found_iface in interfaces:
-                                    if issubclass(found_iface, accepted_iface):
-                                        # consider priority if necessary
-                                        if hasattr(obj.__registerer__, 'remove_all_equivalents'):
-                                            registerer = obj.__registerer__(self, obj)
-                                            registerer.remove_all_equivalents(objects)
-                                        break
-                                else:
-                                    self.debug('kicking vobject %s because interface is not supported', obj)
-                                    objects.remove(obj)
-                    # if objects is empty, remove oid from registry
-                    if not objects:
-                        del regcontent[oid]
+            kwargs['clear'] = True
+        super(CubicWebRegistry, self).register(obj, **kwargs)
+        # XXX bw compat
+        ifaces = use_interfaces(obj)
+        if ifaces:
+            self._needs_iface[obj] = ifaces
+
+    def register_objects(self, path, force_reload=None):
+        """overriden to remove objects requiring a missing interface"""
+        if super(CubicWebRegistry, self).register_objects(path, force_reload):
+            self.initialization_completed()
+            # print registry content
+            for registry, objects in self.items():
+                self.debug('available in registry %s: %s', registry,
+                           sorted(objects))
+                for appobjects in objects.itervalues():
+                    for appobject in appobjects:
+                        appobject.vreg_initialization_completed()
 
-    def eid_rset(self, cursor, 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)
-        """
-        msg = '.eid_rset is deprecated, use req.eid_rset'
-        warn(msg, DeprecationWarning, stacklevel=2)
-        try:
-            return cursor.req.eid_rset(eid, etype)
-        except AttributeError:
-            # cursor is a session
-            return cursor.eid_rset(eid, etype)
-    
+    def initialization_completed(self):
+        # clear etype cache if you don't want to run into deep weirdness
+        clear_cache(self, 'etype_class')
+        # we may want to keep interface dependent objects (e.g.for i18n
+        # catalog generation)
+        if self.config.cleanup_interface_sobjects:
+            # remove vobjects that don't support any available interface
+            implemented_interfaces = set()
+            if 'Any' in self.get('etypes', ()):
+                for etype in self.schema.entities():
+                    cls = self.etype_class(etype)
+                    for iface in cls.__implements__:
+                        implemented_interfaces.update(iface.__mro__)
+                    implemented_interfaces.update(cls.__mro__)
+            for obj, ifaces in self._needs_iface.items():
+                ifaces = frozenset(isinstance(iface, basestring)
+                                   and iface in self.schema
+                                   and self.etype_class(iface)
+                                   or iface
+                                   for iface in ifaces)
+                if not ('Any' in ifaces or ifaces & implemented_interfaces):
+                    self.debug('kicking vobject %s (no implemented '
+                               'interface among %s)', obj, ifaces)
+                    self.unregister(obj)
+        # clear needs_iface so we don't try to remove some not-anymore-in
+        # objects on automatic reloading
+        self._needs_iface.clear()
+
     @cached
     def etype_class(self, etype):
         """return an entity class for the given entity type.
@@ -129,6 +157,8 @@
         default to a dump of the class registered for 'Any'
         """
         etype = str(etype)
+        if etype == 'Any':
+            return self.select(self.registry_objects('etypes', 'Any'), 'Any')
         eschema = self.schema.eschema(etype)
         baseschemas = [eschema] + eschema.ancestors()
         # browse ancestors from most specific to most generic and
@@ -136,18 +166,22 @@
         for baseschema in baseschemas:
             btype = str(baseschema)
             try:
-                return self.select(self.registry_objects('etypes', btype), etype)
+                cls = self.select(self.registry_objects('etypes', btype), etype)
+                break
             except ObjectNotFound:
                 pass
-        # no entity class for any of the ancestors, fallback to the default one
-        return self.select(self.registry_objects('etypes', 'Any'), etype)
+        else:
+            # no entity class for any of the ancestors, fallback to the default
+            # one
+            cls = self.select(self.registry_objects('etypes', 'Any'), etype)
+        return cls
 
     def render(self, registry, oid, req, **context):
         """select an object in a given registry and render it
 
         - registry: the registry's name
         - oid : the view to call
-        - req : the HTTP request         
+        - req : the HTTP request
         """
         objclss = self.registry_objects(registry, oid)
         try:
@@ -156,13 +190,13 @@
             rset = None
         selected = self.select(objclss, req, rset, **context)
         return selected.dispatch(**context)
-        
-    def main_template(self, req, oid='main', **context):
+
+    def main_template(self, req, oid='main-template', **context):
         """display query by calling the given template (default to main),
         and returning the output as a string instead of requiring the [w]rite
         method as argument
         """
-        res = self.render('templates', oid, req, **context)
+        res = self.render('views', oid, req, **context)
         if isinstance(res, unicode):
             return res.encode(req.encoding)
         assert isinstance(res, str)
@@ -176,7 +210,7 @@
         return [x for x in sorted(self.possible_objects(registry, *args, **kwargs),
                                   key=lambda x: x.propval('order'))
                 if x.propval('visible')]
-        
+
     def possible_actions(self, req, rset, **kwargs):
         if rset is None:
             actions = self.possible_vobjects('actions', req, rset, **kwargs)
@@ -186,7 +220,7 @@
         for action in actions:
             result.setdefault(action.category, []).append(action)
         return result
-        
+
     def possible_views(self, req, rset, **kwargs):
         """return an iterator on possible views for this result set
 
@@ -204,7 +238,7 @@
             except Exception:
                 self.exception('error while trying to list possible %s views for %s',
                                vid, rset)
-                
+
     def select_box(self, oid, *args, **kwargs):
         """return the most specific view according to the result set"""
         try:
@@ -218,7 +252,7 @@
             return self.select_object('actions', oid, *args, **kwargs)
         except NoSelectableObject:
             return
-    
+
     def select_component(self, cid, *args, **kwargs):
         """return the most specific component according to the result set"""
         try:
@@ -231,7 +265,7 @@
         views = self.registry_objects('views', __vid)
         return self.select(views, req, rset, **kwargs)
 
-    
+
     # properties handling #####################################################
 
     def user_property_keys(self, withsitewide=False):
@@ -245,7 +279,7 @@
         """register a given property"""
         properties = self._registries['propertydefs']
         assert type in YAMS_TO_PY
-        properties[key] = {'type': type, 'vocabulary': vocabulary, 
+        properties[key] = {'type': type, 'vocabulary': vocabulary,
                            'default': default, 'help': help,
                            'sitewide': sitewide}
 
@@ -263,7 +297,7 @@
                         'default': None, 'vocabulary': None,
                         'help': _('%s software version of the database') % soft}
             raise UnknownProperty('unregistered property %r' % key)
-            
+
     def property_value(self, key):
         try:
             return self._registries['propertyvalues'][key]
@@ -286,7 +320,7 @@
             if not value in vocab:
                 raise ValueError(_('unauthorized value'))
         return value
-    
+
     def init_properties(self, propvalues):
         """init the property values registry using the given set of couple (key, value)
         """
@@ -302,37 +336,6 @@
                 self.warning('%s (you should probably delete that property '
                              'from the database)', ex)
 
-
-    def property_value_widget(self, propkey, req=None, **attrs):
-        """return widget according to key's type / vocab"""
-        from cubicweb.web.widgets import StaticComboBoxWidget, widget_factory
-        if req is None:
-            tr = unicode
-        else:
-            tr = req._
-        try:
-            pdef = self.property_info(propkey)
-        except UnknownProperty, ex:
-            self.warning('%s (you should probably delete that property '
-                         'from the database)', ex)
-            return widget_factory(self, 'EProperty', self.schema['value'], 'String',
-                                  description=u'', **attrs)
-        req.form['value'] = pdef['default'] # XXX hack to pass the default value
-        vocab = pdef['vocabulary']
-        if vocab is not None:
-            if callable(vocab):
-                # list() just in case its a generator function
-                vocabfunc = lambda **kwargs: list(vocab(propkey, req))
-            else:
-                vocabfunc = lambda **kwargs: vocab
-            w = StaticComboBoxWidget(self, 'EProperty', self.schema['value'], 'String',
-                                     vocabfunc=vocabfunc, description=tr(pdef['help']),
-                                     **attrs)
-        else:
-            w = widget_factory(self, 'EProperty', self.schema['value'], pdef['type'],
-                               description=tr(pdef['help']), **attrs)
-        return w
-
     def parse(self, session, rql, args=None):
         rqlst = self.rqlhelper.parse(rql)
         def type_from_eid(eid, session=session):
@@ -377,8 +380,8 @@
             vobject.schema = self.schema
             vobject.config = self.config
         return super(MulCnxCubicWebRegistry, self).select(vobjects, *args, **kwargs)
-    
-from mx.DateTime import DateTime, Time, DateTimeDelta
+
+from datetime import datetime, date, time, timedelta
 
 YAMS_TO_PY = {
     'Boolean':  bool,
@@ -387,9 +390,9 @@
     'Bytes':    Binary,
     'Int':      int,
     'Float':    float,
-    'Date':     DateTime,
-    'Datetime': DateTime,
-    'Time':     Time,
-    'Interval': DateTimeDelta,
+    'Date':     date,
+    'Datetime': datetime,
+    'Time':     time,
+    'Interval': timedelta,
     }
 
--- a/dbapi.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/dbapi.py	Mon May 04 13:09:48 2009 +0200
@@ -5,18 +5,18 @@
 (most parts of this document are reported here in docstrings)
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from logging import getLogger, StreamHandler
+from logging import getLogger
 from time import time, clock
 
 from cubicweb import ConnectionError, RequestSessionMixIn, set_log_methods
 from cubicweb.cwvreg import CubicWebRegistry, MulCnxCubicWebRegistry
 from cubicweb.cwconfig import CubicWebNoAppConfiguration
-        
+
 _MARKER = object()
 
 class ConnectionProperties(object):
@@ -29,7 +29,7 @@
 
 def get_repository(method, database=None, config=None, vreg=None):
     """get a proxy object to the CubicWeb repository, using a specific RPC method.
-     
+
     Only 'in-memory' and 'pyro' are supported for now. Either vreg or config
     argument should be given
     """
@@ -42,7 +42,7 @@
         from cubicweb.server.repository import Repository
         return Repository(config, vreg=vreg)
     else: # method == 'pyro'
-        from Pyro import core, naming, config as pyroconfig
+        from Pyro import core, naming
         from Pyro.errors import NamingError, ProtocolError
         core.initClient(banner=0)
         nsid = ':%s.%s' % (config['pyro-ns-group'], database)
@@ -54,16 +54,16 @@
         except ProtocolError:
             raise ConnectionError('Could not connect to the Pyro name server '
                                   '(host: %s:%i)' % (nshost, nsport))
-        except NamingError, ex:
+        except NamingError:
             raise ConnectionError('Could not get repository for %s '
                                   '(not registered in Pyro), '
                                   'you may have to restart your server-side '
                                   'application' % nsid)
         return core.getProxyForURI(uri)
-        
+
 def repo_connect(repo, user, password, cnxprops=None):
     """Constructor to create a new connection to the CubicWeb repository.
-    
+
     Returns a Connection instance.
     """
     cnxprops = cnxprops or ConnectionProperties('inmemory')
@@ -72,7 +72,7 @@
     if cnxprops.cnxtype == 'inmemory':
         cnx.vreg = repo.vreg
     return cnx
-    
+
 def connect(database=None, user=None, password=None, host=None,
             group=None, cnxprops=None, port=None, setvreg=True, mulcnx=True,
             initlog=True):
@@ -110,7 +110,7 @@
 
 def in_memory_cnx(config, user, password):
     """usefull method for testing and scripting to get a dbapi.Connection
-    object connected to an in-memory repository instance 
+    object connected to an in-memory repository instance
     """
     if isinstance(config, CubicWebRegistry):
         vreg = config
@@ -126,7 +126,7 @@
 
 
 class DBAPIRequest(RequestSessionMixIn):
-    
+
     def __init__(self, vreg, cnx=None):
         super(DBAPIRequest, self).__init__(vreg)
         try:
@@ -146,10 +146,10 @@
 
     def base_url(self):
         return self.vreg.config['base-url']
-    
+
     def from_controller(self):
         return 'view'
-    
+
     def set_connection(self, cnx, user=None):
         """method called by the session handler when the user is authenticated
         or an anonymous connection is open
@@ -157,7 +157,7 @@
         self.cnx = cnx
         self.cursor = cnx.cursor(self)
         self.set_user(user)
-    
+
     def set_default_language(self, vreg):
         try:
             self.lang = vreg.property_value('ui.language')
@@ -175,26 +175,26 @@
         rset.vreg = self.vreg
         rset.req = self
         return rset
-    
+
     def describe(self, eid):
         """return a tuple (type, sourceuri, extid) for the entity with id <eid>"""
         return self.cnx.describe(eid)
-    
+
     def source_defs(self):
         """return the definition of sources used by the repository."""
         return self.cnx.source_defs()
-            
+
     # entities cache management ###############################################
-    
+
     def entity_cache(self, eid):
         return self._eid_cache[eid]
-    
+
     def set_entity_cache(self, entity):
         self._eid_cache[entity.eid] = entity
 
     def cached_entities(self):
         return self._eid_cache.values()
-    
+
     def drop_entity_cache(self, eid=None):
         if eid is None:
             self._eid_cache = {}
@@ -210,11 +210,11 @@
     def get_session_data(self, key, default=None, pop=False):
         """return value associated to `key` in session data"""
         return self.cnx.get_session_data(key, default, pop)
-        
+
     def set_session_data(self, key, value):
         """set value associated to `key` in session data"""
         return self.cnx.set_session_data(key, value)
-        
+
     def del_session_data(self, key):
         """remove value associated to `key` in session data"""
         return self.cnx.del_session_data(key)
@@ -222,7 +222,7 @@
     def get_shared_data(self, key, default=None, pop=False):
         """return value associated to `key` in shared data"""
         return self.cnx.get_shared_data(key, default, pop)
-        
+
     def set_shared_data(self, key, value, querydata=False):
         """set value associated to `key` in shared data
 
@@ -245,14 +245,14 @@
         self._user = user
         if user:
             self.set_entity_cache(user)
-        
+
     def execute(self, *args, **kwargs):
         """Session interface compatibility"""
         return self.cursor.execute(*args, **kwargs)
 
 set_log_methods(DBAPIRequest, getLogger('cubicweb.dbapi'))
-        
-        
+
+
 # exceptions ##################################################################
 
 class ProgrammingError(Exception): #DatabaseError):
@@ -288,15 +288,15 @@
 """String constant stating the type of parameter marker formatting expected by
 the interface. Possible values are :
 
-                'qmark'         Question mark style, 
+                'qmark'         Question mark style,
                                 e.g. '...WHERE name=?'
-                'numeric'       Numeric, positional style, 
+                'numeric'       Numeric, positional style,
                                 e.g. '...WHERE name=:1'
-                'named'         Named style, 
+                'named'         Named style,
                                 e.g. '...WHERE name=:name'
-                'format'        ANSI C printf format codes, 
+                'format'        ANSI C printf format codes,
                                 e.g. '...WHERE name=%s'
-                'pyformat'      Python extended format codes, 
+                'pyformat'      Python extended format codes,
                                 e.g. '...WHERE name=%(name)s'
 """
 paramstyle = 'pyformat'
@@ -333,41 +333,37 @@
 
     def request(self):
         return DBAPIRequest(self.vreg, self)
-    
+
     def session_data(self):
         """return a dictionnary containing session data"""
         return self.data
-        
+
     def get_session_data(self, key, default=None, pop=False):
         """return value associated to `key` in session data"""
         if pop:
             return self.data.pop(key, default)
         else:
             return self.data.get(key, default)
-        
+
     def set_session_data(self, key, value):
         """set value associated to `key` in session data"""
         self.data[key] = value
-        
+
     def del_session_data(self, key):
         """remove value associated to `key` in session data"""
         try:
             del self.data[key]
         except KeyError:
-            pass    
+            pass
 
     def check(self):
         """raise `BadSessionId` if the connection is no more valid"""
-        try:
-            self._repo.check_session(self.sessionid)
-        except AttributeError:
-            # XXX backward compat for repository running cubicweb < 2.48.3
-            self._repo.session_data(self.sessionid)
+        self._repo.check_session(self.sessionid)
 
     def get_shared_data(self, key, default=None, pop=False):
         """return value associated to `key` in shared data"""
         return self._repo.get_shared_data(self.sessionid, key, default, pop)
-        
+
     def set_shared_data(self, key, value, querydata=False):
         """set value associated to `key` in shared data
 
@@ -377,10 +373,10 @@
         repository side.
         """
         return self._repo.set_shared_data(self.sessionid, key, value, querydata)
-        
+
     def get_schema(self):
         """Return the schema currently used by the repository.
-        
+
         This is NOT part of the DB-API.
         """
         if self._closed is not None:
@@ -418,10 +414,10 @@
             # application specific hooks
             if self._repo.config.application_hooks:
                 hm.register_hooks(config.load_hooks(self.vreg))
-            
+
     def source_defs(self):
         """Return the definition of sources used by the repository.
-        
+
         This is NOT part of the DB-API.
         """
         if self._closed is not None:
@@ -434,9 +430,9 @@
         eid, login, groups, properties = self._repo.user_info(self.sessionid, props)
         if req is None:
             req = self.request()
-        rset = req.eid_rset(eid, 'EUser')
-        user = self.vreg.etype_class('EUser')(req, rset, row=0, groups=groups,
-                                              properties=properties)
+        rset = req.eid_rset(eid, 'CWUser')
+        user = self.vreg.etype_class('CWUser')(req, rset, row=0, groups=groups,
+                                               properties=properties)
         user['login'] = login # cache login
         return user
 
@@ -447,13 +443,13 @@
                 self.close()
             except:
                 pass
-    
+
     def describe(self, eid):
         return self._repo.describe(self.sessionid, eid)
-            
+
     def close(self):
         """Close the connection now (rather than whenever __del__ is called).
-        
+
         The connection will be unusable from this point forward; an Error (or
         subclass) exception will be raised if any operation is attempted with
         the connection. The same applies to all cursor objects trying to use the
@@ -469,7 +465,7 @@
         """Commit any pending transaction to the database. Note that if the
         database supports an auto-commit feature, this must be initially off. An
         interface method may be provided to turn it back on.
-            
+
         Database modules that do not support transactions should implement this
         method with void functionality.
         """
@@ -480,7 +476,7 @@
     def rollback(self):
         """This method is optional since not all databases provide transaction
         support.
-            
+
         In case a database does provide transactions this method causes the the
         database to roll back to the start of any pending transaction.  Closing
         a connection without committing the changes first will cause an implicit
@@ -514,7 +510,7 @@
     support is implemented (see also the connection's rollback() and commit()
     methods.)
     """
-    
+
     def __init__(self, connection, repo, req=None):
         """This read-only attribute return a reference to the Connection
         object on which the cursor was created.
@@ -526,7 +522,7 @@
         """This read/write attribute specifies the number of rows to fetch at a
         time with fetchmany(). It defaults to 1 meaning to fetch a single row
         at a time.
-        
+
         Implementations must observe this value with respect to the fetchmany()
         method, but are free to interact with the database a single row at a
         time. It may also be used in the implementation of executemany().
@@ -539,7 +535,7 @@
         self._closed = None
         self._index = 0
 
-        
+
     def close(self):
         """Close the cursor now (rather than whenever __del__ is called).  The
         cursor will be unusable from this point forward; an Error (or subclass)
@@ -547,30 +543,30 @@
         """
         self._closed = True
 
-            
+
     def execute(self, operation, parameters=None, eid_key=None, build_descr=True):
         """Prepare and execute a database operation (query or command).
         Parameters may be provided as sequence or mapping and will be bound to
         variables in the operation.  Variables are specified in a
         database-specific notation (see the module's paramstyle attribute for
         details).
-        
+
         A reference to the operation will be retained by the cursor.  If the
         same operation object is passed in again, then the cursor can optimize
         its behavior.  This is most effective for algorithms where the same
         operation is used, but different parameters are bound to it (many
         times).
-        
+
         For maximum efficiency when reusing an operation, it is best to use the
         setinputsizes() method to specify the parameter types and sizes ahead
         of time.  It is legal for a parameter to not match the predefined
         information; the implementation should compensate, possibly with a loss
         of efficiency.
-        
+
         The parameters may also be specified as list of tuples to e.g. insert
         multiple rows in a single operation, but this kind of usage is
         depreciated: executemany() should be used instead.
-        
+
         Return values are not defined by the DB-API, but this here it returns a
         ResultSet object.
         """
@@ -579,25 +575,25 @@
         self.req.decorate_rset(res)
         self._index = 0
         return res
-        
+
 
     def executemany(self, operation, seq_of_parameters):
         """Prepare a database operation (query or command) and then execute it
         against all parameter sequences or mappings found in the sequence
         seq_of_parameters.
-        
+
         Modules are free to implement this method using multiple calls to the
         execute() method or by using array operations to have the database
         process the sequence as a whole in one call.
-        
+
         Use of this method for an operation which produces one or more result
         sets constitutes undefined behavior, and the implementation is
         permitted (but not required) to raise an exception when it detects that
         a result set has been created by an invocation of the operation.
-        
+
         The same comments as for execute() also apply accordingly to this
         method.
-        
+
         Return values are not defined.
         """
         for parameters in seq_of_parameters:
@@ -610,7 +606,7 @@
     def fetchone(self):
         """Fetch the next row of a query result set, returning a single
         sequence, or None when no more data is available.
-        
+
         An Error (or subclass) exception is raised if the previous call to
         execute*() did not produce any result set or no call was issued yet.
         """
@@ -620,21 +616,21 @@
         self._index += 1
         return row
 
-        
+
     def fetchmany(self, size=None):
         """Fetch the next set of rows of a query result, returning a sequence
         of sequences (e.g. a list of tuples). An empty sequence is returned
         when no more rows are available.
-        
+
         The number of rows to fetch per call is specified by the parameter.  If
         it is not given, the cursor's arraysize determines the number of rows
         to be fetched. The method should try to fetch as many rows as indicated
         by the size parameter. If this is not possible due to the specified
         number of rows not being available, fewer rows may be returned.
-        
+
         An Error (or subclass) exception is raised if the previous call to
         execute*() did not produce any result set or no call was issued yet.
-        
+
         Note there are performance considerations involved with the size
         parameter.  For optimal performance, it is usually best to use the
         arraysize attribute.  If the size parameter is used, then it is best
@@ -648,12 +644,12 @@
         self._index += size
         return rows
 
-        
+
     def fetchall(self):
         """Fetch all (remaining) rows of a query result, returning them as a
         sequence of sequences (e.g. a list of tuples).  Note that the cursor's
         arraysize attribute can affect the performance of this operation.
-        
+
         An Error (or subclass) exception is raised if the previous call to
         execute*() did not produce any result set or no call was issued yet.
         """
@@ -669,39 +665,39 @@
     def setinputsizes(self, sizes):
         """This can be used before a call to execute*() to predefine memory
         areas for the operation's parameters.
-        
+
         sizes is specified as a sequence -- one item for each input parameter.
         The item should be a Type Object that corresponds to the input that
         will be used, or it should be an integer specifying the maximum length
         of a string parameter.  If the item is None, then no predefined memory
         area will be reserved for that column (this is useful to avoid
         predefined areas for large inputs).
-        
+
         This method would be used before the execute*() method is invoked.
-        
+
         Implementations are free to have this method do nothing and users are
         free to not use it.
         """
         pass
 
-        
+
     def setoutputsize(self, size, column=None):
         """Set a column buffer size for fetches of large columns (e.g. LONGs,
         BLOBs, etc.).  The column is specified as an index into the result
         sequence.  Not specifying the column will set the default size for all
         large columns in the cursor.
-        
+
         This method would be used before the execute*() method is invoked.
-        
+
         Implementations are free to have this method do nothing and users are
         free to not use it.
-        """    
+        """
         pass
 
-    
+
 class LogCursor(Cursor):
     """override the standard cursor to log executed queries"""
-    
+
     def execute(self, operation, parameters=None, eid_key=None, build_descr=True):
         """override the standard cursor to log executed queries"""
         tstart, cstart = time(), clock()
--- a/debian/control	Thu Apr 30 16:16:39 2009 +0200
+++ b/debian/control	Mon May 04 13:09:48 2009 +0200
@@ -10,7 +10,6 @@
 Homepage: http://www.cubicweb.org
 XS-Python-Version: >= 2.4, << 2.6
 
-
 Package: cubicweb
 Architecture: all
 XB-Python-Version: ${python:Versions}
@@ -76,8 +75,8 @@
 Package: cubicweb-common
 Architecture: all
 XB-Python-Version: ${python:Versions}
-Depends: ${python:Depends}, python-logilab-mtconverter (>= 0.6.0), python-simpletal (>= 4.0), graphviz, gettext, python-lxml, python-logilab-common (>= 0.38.1), python-yams (>= 0.20.2), python-rql (>= 0.20.2), python-simplejson (>= 1.3)
-Recommends: python-psyco
+Depends: ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.6.0), python-logilab-common (>= 0.39.0), python-yams (>= 0.22.0), python-rql (>= 0.22.0)
+Recommends: python-simpletal (>= 4.0), python-simplejson (>= 1.3), python-lxml
 Conflicts: cubicweb-core
 Replaces: cubicweb-core
 Description: common library for the CubicWeb framework
--- a/debian/cubicweb-ctl.postinst	Thu Apr 30 16:16:39 2009 +0200
+++ b/debian/cubicweb-ctl.postinst	Mon May 04 13:09:48 2009 +0200
@@ -13,25 +13,29 @@
 if [ "$1" = configure ]; then
     # XXX bw compat: erudi -> cubicweb migration
     if [ -e "/etc/erudi.d/" ]; then
-      mv /etc/erudi.d/* /etc/cubicweb.d/
-      echo 'moved /etc/erudi.d/* to /etc/cubicweb.d/'
-      sed -i s/ginco/cubicweb/g /etc/*/*.py
-      sed -i s/erudi/cubicweb/ */*.conf
+      mv /etc/erudi.d/* /etc/cubicweb.d/ && (
+	  echo 'moved /etc/erudi.d/* to /etc/cubicweb.d/'
+	  sed -i s/ginco/cubicweb/g /etc/*/*.py
+	  sed -i s/erudi/cubicweb/ */*.conf
+	  ) || true # empty dir
     fi
     if [ -e "/var/log/erudi/" ]; then
-      mv /var/log/erudi/* /var/log/cubicweb/
-      echo 'moved /var/log/erudi/* to /var/log/cubicweb/'
+      mv /var/log/erudi/* /var/log/cubicweb/ && (
+	  echo 'moved /var/log/erudi/* to /var/log/cubicweb/'
+	  ) || true # empty dir
     fi
     if [ -e "/var/lib/erudi/backup" ]; then
-      mv /var/lib/erudi/backup/* /var/lib/cubicweb/backup/
-      echo 'moved /var/lib/erudi/backup/* to /var/lib/cubicweb/backup/'
+      mv /var/lib/erudi/backup/* /var/lib/cubicweb/backup/ && (
+	  echo 'moved /var/lib/erudi/backup/* to /var/lib/cubicweb/backup/'
+	  ) || true # empty dir
     fi
     if [ -e "/var/lib/erudi/instances" ]; then
-      mv /var/lib/erudi/instances/* /var/lib/cubicweb/instances/
-      echo 'moved /var/lib/erudi/instances/* to /var/lib/cubicweb/instances/'
+      mv /var/lib/erudi/instances/* /var/lib/cubicweb/instances/ && (
+	  echo 'moved /var/lib/erudi/instances/* to /var/lib/cubicweb/instances/'
+	  ) || true # empty dir
     fi
 fi
-  
+
 #DEBHELPER#
- 
+
 exit 0
--- a/debian/cubicweb-web.postinst	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-#! /bin/sh -e
-
-ln -sf /usr/share/fckeditor/fckeditor.js /usr/share/cubicweb/cubes/shared/data
- 
-#DEBHELPER#
- 
-exit 0
--- a/devtools/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -1,19 +1,19 @@
 """Test tools for cubicweb
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 import os
 import logging
+from datetime import timedelta
 from os.path import (abspath, join, exists, basename, dirname, normpath, split,
                      isfile, isabs)
 
-from mx.DateTime import strptime, DateTimeDelta
-
 from cubicweb import CW_SOFTWARE_ROOT, ConfigurationError
+from cubicweb.utils import strptime
 from cubicweb.toolsutils import read_config
 from cubicweb.cwconfig import CubicWebConfiguration, merge_options
 from cubicweb.server.serverconfig import ServerConfiguration
@@ -271,6 +271,7 @@
                     for cellindex, (value, vtype) in enumerate(zip(row, rowdesc)):
                         if vtype in ('Date', 'Datetime') and type(value) is unicode:
                             found_date = True
+                            value = value.rsplit('.', 1)[0]
                             try:
                                 row[cellindex] = strptime(value, '%Y-%m-%d %H:%M:%S')
                             except:
@@ -284,7 +285,7 @@
                                 row[cellindex] = strptime(value, '%Y-%m-%d %H:%M:%S')
                         if vtype == 'Interval' and type(value) is int:
                             found_date = True
-                            row[cellindex] = DateTimeDelta(0, 0, 0, value)
+                            row[cellindex] = timedelta(0, value, 0) # XXX value is in number of seconds?
                     if not found_date:
                         break
             return rset
--- a/devtools/_apptest.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/_apptest.py	Mon May 04 13:09:48 2009 +0200
@@ -21,10 +21,10 @@
 from cubicweb.devtools import ApptestConfiguration, init_test_database
 from cubicweb.devtools.fake import FakeRequest
     
-SYSTEM_ENTITIES = ('EGroup', 'EUser',
-                   'EFRDef', 'ENFRDef',
-                   'EConstraint', 'EConstraintType', 'EProperty',
-                   'EEType', 'ERType',
+SYSTEM_ENTITIES = ('CWGroup', 'CWUser',
+                   'CWAttribute', 'CWRelation',
+                   'CWConstraint', 'CWConstraintType', 'CWProperty',
+                   'CWEType', 'CWRType',
                    'State', 'Transition', 'TrInfo',
                    'RQLExpression',
                    )
@@ -50,7 +50,7 @@
                     )
 
 def unprotected_entities(app_schema, strict=False):
-    """returned a Set of each non final entity type, excluding EGroup, and EUser...
+    """returned a Set of each non final entity type, excluding CWGroup, and CWUser...
     """
     if strict:
         protected_entities = yams.schema.BASE_TYPES
@@ -61,6 +61,7 @@
     
 
 def ignore_relations(*relations):
+    global SYSTEM_RELATIONS
     SYSTEM_RELATIONS += relations
 
 class TestEnvironment(object):
@@ -83,14 +84,13 @@
         self.restore_database()
         if verbose:
             print "init done"
-        login = source['db-user']
         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('EUser', 'EmailAddress', 'composite', False)
+        schema.rschema('primary_email').set_rproperty('CWUser', 'EmailAddress', 'composite', False)
         self.deletable_entities = unprotected_entities(schema)
 
     def restore_database(self):
@@ -119,7 +119,7 @@
     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 EUser X: X login %(login)s, X upassword %(passwd)s,'
+        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)
@@ -234,15 +234,14 @@
             print "init test database ..."
         source = config.sources()['system']
         self.vreg = CubicWebRegistry(config)
-        repo, self.cnx = init_test_database(driver=source['db-driver'],
-                                            vreg=self.vreg)
+        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
-        login = source['db-user']
         
     def setup(self, config=None):
         """config is passed by TestSuite but is ignored in this environment"""
--- a/devtools/apptest.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/apptest.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """This module provides misc utilities to test applications
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -32,11 +32,11 @@
     @property
     def message(self):
         return message_from_string(self.msg)
-    
+
     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
@@ -100,7 +100,7 @@
     env = None
     configcls = ApptestConfiguration
     requestcls = FakeRequest
-    
+
     # user / session management ###############################################
 
     def user(self, req=None):
@@ -118,13 +118,13 @@
 
     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)
@@ -132,19 +132,19 @@
     @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
@@ -152,7 +152,7 @@
     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)
@@ -160,16 +160,16 @@
     @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.etype_class(etype)(req, None, None)
         e.eid = None
         return e
-    
+
     def add_entity(self, etype, **kwargs):
         rql = ['INSERT %s X' % etype]
 
@@ -185,15 +185,15 @@
             sub_rql = []
             for key, value in kwargs.iteritems():
                 # entities
-                if hasattr(value, 'eid'): 
+                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: 
+                else:
                     sub_rql.append('X %s %%(%s)s' % (key, key))
                     rql_args[key] = value
             rql.append(', '.join(sub_rql))
@@ -215,8 +215,8 @@
         self.vreg.config.global_set_option(optname, value)
 
     def pviews(self, req, rset):
-        return sorted((a.id, a.__class__) for a in self.vreg.possible_views(req, rset)) 
-        
+        return sorted((a.id, a.__class__) for a in self.vreg.possible_views(req, rset))
+
     def pactions(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')):
         return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset)
                 if a.category not in skipcategories]
@@ -224,7 +224,7 @@
     def pactions_by_cats(self, req, rset, categories=('addrelated',)):
         return [(a.id, a.__class__) for a in self.vreg.possible_vobjects('actions', req, rset)
                 if a.category in categories]
-    
+
     paddrelactions = deprecated_function(pactions_by_cats)
 
     def pactionsdict(self, req, rset, skipcategories=('addrelated', 'siteactions', 'useractions')):
@@ -234,7 +234,7 @@
                 res.setdefault(a.category, []).append(a.__class__)
         return res
 
-    
+
     def remote_call(self, fname, *args):
         """remote call simulation"""
         dump = simplejson.dumps
@@ -244,7 +244,7 @@
         return ctrl.publish(), req
 
     # default test setup and teardown #########################################
-        
+
     def setup_database(self):
         pass
 
@@ -264,7 +264,7 @@
         self.setup_database()
         self.commit()
         MAILBOX[:] = [] # reset mailbox
-        
+
     @nocoverage
     def tearDown(self):
         self.rollback()
@@ -355,13 +355,13 @@
     """
     __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 EUser X: X login %(x)s, X upassword %(p)s,'
+        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)
@@ -369,9 +369,9 @@
                       {'x': eid})
         if commit:
             self.commit()
-        self.session.reset_pool()        
+        self.session.reset_pool()
         return eid
-    
+
     def login(self, login, password=None):
         cnx = repo_connect(self.repo, unicode(login), password or login,
                            ConnectionProperties('inmemory'))
@@ -380,7 +380,7 @@
 
     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()
@@ -388,7 +388,7 @@
             cnx.close()
         except Exception, ex:
             print "exception occured while closing connection", ex
-        
+
     # db api ##################################################################
 
     def execute(self, rql, args=None, eid_key=None):
@@ -400,27 +400,27 @@
         # application entities for convenience
         self.session.set_pool()
         return rset
-    
+
     def commit(self):
         self.__commit(self.cnxid)
-        self.session.set_pool()        
-    
+        self.session.set_pool()
+
     def rollback(self):
         self.__rollback(self.cnxid)
-        self.session.set_pool()        
-    
+        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
@@ -434,7 +434,7 @@
         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:
@@ -446,10 +446,10 @@
 
     pactions = EnvBasedTC.pactions.im_func
     pactionsdict = EnvBasedTC.pactionsdict.im_func
-    
+
     # default test setup and teardown #########################################
     copy_schema = False
-    
+
     def _prepare(self):
         MAILBOX[:] = [] # reset mailbox
         if hasattr(self, 'cnxid'):
@@ -494,18 +494,15 @@
     @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]
-        #self.maxeid = self.execute('Any MAX(X)')
-        
-    def tearDown(self, close=True):
+
+    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()
-        #if close:
-        #    self.close()
-    
+
--- a/devtools/devctl.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/devctl.py	Mon May 04 13:09:48 2009 +0200
@@ -2,24 +2,25 @@
 cubes development
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 import sys
-from os import walk, mkdir, chdir, listdir, getcwd
+from datetime import datetime
+from os import mkdir, chdir
 from os.path import join, exists, abspath, basename, normpath, split, isdir
 
 
 from logilab.common import STD_BLACKLIST
 from logilab.common.modutils import get_module_files
 from logilab.common.textutils import get_csv
+from logilab.common.clcommands import register_commands
 
-from cubicweb import CW_SOFTWARE_ROOT as BASEDIR
+from cubicweb import CW_SOFTWARE_ROOT as BASEDIR, BadCommandUsage
 from cubicweb.__pkginfo__ import version as cubicwebversion
-from cubicweb import BadCommandUsage
-from cubicweb.toolsutils import Command, register_commands, confirm, copy_skeleton
+from cubicweb.toolsutils import Command, confirm, copy_skeleton
 from cubicweb.web.webconfig import WebConfiguration
 from cubicweb.server.serverconfig import ServerConfiguration
 
@@ -39,7 +40,7 @@
 
     def my_cubes(self, cube):
         return (cube,) + self.cube_dependencies(cube) + self.cube_recommends(cube)
-    
+
     @property
     def apphome(self):
         return None
@@ -76,7 +77,7 @@
             if mod.__file__.startswith(path):
                 del sys.modules[name]
                 break
-    
+
 def generate_schema_pot(w, cubedir=None):
     """generate a pot file with schema specific i18n messages
 
@@ -85,30 +86,31 @@
     """
     from cubicweb.cwvreg import CubicWebRegistry
     cube = cubedir and split(cubedir)[-1]
-    config = DevDepConfiguration(cube)
-    cleanup_sys_modules(config)
+    libconfig = DevDepConfiguration(cube)
+    libconfig.cleanup_interface_sobjects = False
+    cleanup_sys_modules(libconfig)
     if cubedir:
-        libschema = config.load_schema()
         config = DevCubeConfiguration(cube)
         schema = config.load_schema()
     else:
+        config = libconfig
         schema = config.load_schema()
-        libschema = None
-        config.cleanup_interface_sobjects = False
+        libconfig = None
     vreg = CubicWebRegistry(config)
     # set_schema triggers objects registrations
     vreg.set_schema(schema)
     w(DEFAULT_POT_HEAD)
-    _generate_schema_pot(w, vreg, schema, libschema=libschema, cube=cube)
-                
-def _generate_schema_pot(w, vreg, schema, libschema=None, cube=None):
-    from mx.DateTime import now
+    _generate_schema_pot(w, vreg, schema, libconfig=libconfig, cube=cube)
+
+
+def _generate_schema_pot(w, vreg, schema, libconfig=None, cube=None):
     from cubicweb.common.i18n import add_msg
-    w('# schema pot file, generated on %s\n' % now().strftime('%Y-%m-%d %H:%M:%S'))
+    w('# schema pot file, generated on %s\n' % datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
     w('# \n')
     w('# singular and plural forms for each entity type\n')
     w('\n')
-    if libschema is not None:
+    if libconfig is not None:
+        libschema = libconfig.load_schema()
         entities = [e for e in schema.entities() if not e in libschema]
     else:
         entities = schema.entities()
@@ -128,7 +130,7 @@
     w('# subject and object forms for each relation type\n')
     w('# (no object form for final relation types)\n')
     w('\n')
-    if libschema is not None:
+    if libconfig is not None:
         relations = [r for r in schema.relations() if not r in libschema]
     else:
         relations = schema.relations()
@@ -143,46 +145,67 @@
             add_msg(w, rschema.description)
     w('# add related box generated message\n')
     w('\n')
+    actionbox = vreg['boxes']['edit_box'][0]
     for eschema in schema.entities():
         if eschema.is_final():
             continue
-        entity = vreg.etype_class(eschema)(None, None)
-        for x, rschemas in (('subject', eschema.subject_relations()),
+        for role, rschemas in (('subject', eschema.subject_relations()),
                             ('object', eschema.object_relations())):
             for rschema in rschemas:
                 if rschema.is_final():
                     continue
-                for teschema in rschema.targets(eschema, x):
-                    if defined_in_library(libschema, eschema, rschema, teschema, x):
-                        continue
-                    if entity.relation_mode(rschema.type, teschema.type, x) == 'create':
-                        if x == 'subject':
-                            label = 'add %s %s %s %s' % (eschema, rschema, teschema, x)
-                            label2 = "creating %s (%s %%(linkto)s %s %s)" % (teschema, eschema, rschema, teschema)
+                for teschema in rschema.targets(eschema, role):
+                    if libconfig is not None:
+                        if role == 'subject':
+                            subjtype, objtype = etype, tetype
                         else:
-                            label = 'add %s %s %s %s' % (teschema, rschema, eschema, x)
-                            label2 = "creating %s (%s %s %s %%(linkto)s)" % (teschema, teschema, rschema, eschema)
+                            subjtype, objtype = tetype, etype
+                        if libschema.rschema(rtype).has_rdef(subjtype, objtype):
+                            continue
+                    if actionbox.relation_mode(rschema, eschema, teschema, role) == 'create':
+                        if role == 'subject':
+                            label = 'add %s %s %s %s' % (eschema, rschema,
+                                                         teschema, role)
+                            label2 = "creating %s (%s %%(linkto)s %s %s)" % (
+                                teschema, eschema, rschema, teschema)
+                        else:
+                            label = 'add %s %s %s %s' % (teschema, rschema,
+                                                         eschema, role)
+                            label2 = "creating %s (%s %s %s %%(linkto)s)" % (
+                                teschema, teschema, rschema, eschema)
                         add_msg(w, label)
                         add_msg(w, label2)
-    cube = (cube and 'cubes.%s.' % cube or 'cubicweb.')
+    #cube = (cube and 'cubes.%s.' % cube or 'cubicweb.')
     done = set()
+    if libconfig is not None:
+        from cubicweb.cwvreg import CubicWebRegistry
+        libvreg = CubicWebRegistry(libconfig)
+        libvreg.set_schema(libschema) # trigger objects registration
+        # prefill done set
+        list(_iter_vreg_objids(libvreg, done))
+    for objid in _iter_vreg_objids(vreg, done):
+        add_msg(w, '%s_description' % objid)
+        add_msg(w, objid)
+
+def _iter_vreg_objids(vreg, done, prefix=None):
     for reg, objdict in vreg.items():
         for objects in objdict.values():
             for obj in objects:
                 objid = '%s_%s' % (reg, obj.id)
                 if objid in done:
-                    continue
-                if obj.__module__.startswith(cube) and obj.property_defs:
-                    add_msg(w, '%s_description' % objid)
-                    add_msg(w, objid)
+                    break
+                if obj.property_defs:
+                    yield objid
                     done.add(objid)
+                    break
 
-                    
-def defined_in_library(libschema, etype, rtype, tetype, x):
-    """return true if the given relation definition exists in cubicweb's library"""
+
+def defined_in_library(etype, rtype, tetype, role):
+    """return true if the given relation definition exists in cubicweb's library
+    """
     if libschema is None:
         return False
-    if x == 'subject':
+    if role == 'subject':
         subjtype, objtype = etype, tetype
     else:
         subjtype, objtype = tetype, etype
@@ -211,7 +234,7 @@
 
 class UpdateCubicWebCatalogCommand(Command):
     """Update i18n catalogs for cubicweb library.
-    
+
     It will regenerate cubicweb/i18n/xx.po files. You'll have then to edit those
     files to add translations of newly added messages.
     """
@@ -251,8 +274,12 @@
             cmd = 'xgettext --no-location --omit-header -k_ -o %s %s'
             if lang is not None:
                 cmd += ' -L %s' % lang
-            potfiles.append(join(tempdir, '%s.pot' % id))
-            execute(cmd % (potfiles[-1], ' '.join(files)))
+            potfile = join(tempdir, '%s.pot' % id)
+            execute(cmd % (potfile, ' '.join(files)))
+            if exists(potfile):
+                potfiles.append(potfile)
+            else:
+                print 'WARNING: %s file not generated' % potfile
         print '******** merging .pot files'
         cubicwebpot = join(tempdir, 'cubicweb.pot')
         execute('msgcat %s > %s' % (' '.join(potfiles), cubicwebpot))
@@ -281,7 +308,7 @@
     """
     name = 'i18nupdate'
     arguments = '[<cube>...]'
-    
+
     def run(self, args):
         """run the command with its specific arguments"""
         if args:
@@ -328,7 +355,7 @@
             execute('xgettext --no-location --omit-header -k_ -L java --from-code=utf-8 -o %s %s'
                     % (tmppotfile, ' '.join(jsfiles)))
             # no pot file created if there are no string to translate
-            if exists(tmppotfile): 
+            if exists(tmppotfile):
                 potfiles.append(tmppotfile)
         print '******** create cube specific catalog'
         tmppotfile = join(tempdir, 'generated.pot')
@@ -367,7 +394,7 @@
     name = 'live-server'
     arguments = ''
     options = ()
-    
+
     def run(self, args):
         """run the command with its specific arguments"""
         from cubicweb.devtools.livetest import runserver
@@ -415,7 +442,7 @@
          ),
         )
 
-    
+
     def run(self, args):
         if len(args) != 1:
             raise BadCommandUsage("exactly one argument (cube name) is expected")
@@ -449,7 +476,7 @@
                     distname = 'cubicweb-' + distname
         else:
             distname = 'cubicweb-%s' % cubename.lower()
-        
+
         longdesc = shortdesc = raw_input('Enter a short description for your cube: ')
         if verbose:
             longdesc = raw_input('Enter a long description (or nothing if you want to reuse the short one): ')
@@ -461,14 +488,13 @@
                 dependancies = ', '.join(repr(cube) for cube in includes)
         else:
             dependancies = ''
-        from mx.DateTime import now
         context = {'cubename' : cubename,
                    'distname' : distname,
                    'shortdesc' : shortdesc,
                    'longdesc' : longdesc or shortdesc,
                    'dependancies' : dependancies,
                    'version'  : cubicwebversion,
-                   'year'  : str(now().year),
+                   'year'  : str(datetime.now().year),
                    'author': self['author'],
                    'author-email': self['author-email'],
                    'author-web-site': self['author-web-site'],
@@ -488,7 +514,7 @@
             elif ans == 's':
                 break
         return includes
-    
+
 
 class ExamineLogCommand(Command):
     """Examine a rql log file.
@@ -507,7 +533,7 @@
     name = 'exlog'
     options = (
         )
-    
+
     def run(self, args):
         if args:
             raise BadCommandUsage("no argument expected")
@@ -541,7 +567,7 @@
         print 'Percentage;Cumulative Time;Occurences;Query'
         for time, occ, rql in stat:
             print '%.2f;%.2f;%s;%s' % (time/total_time, time, occ, rql)
-        
+
 register_commands((UpdateCubicWebCatalogCommand,
                    UpdateTemplateCatalogCommand,
                    LiveServerCommand,
--- a/devtools/fake.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/fake.py	Mon May 04 13:09:48 2009 +0200
@@ -24,13 +24,13 @@
         self.apphome = apphome
         self._cubes = cubes
         self['auth-mode'] = 'cookie'
-        self['uid'] = None 
+        self['uid'] = None
         self['base-url'] = BASE_URL
         self['rql-cache-size'] = 100
-       
+
     def cubes(self, expand=False):
         return self._cubes
-    
+
     def sources(self):
         return {}
 
@@ -41,7 +41,7 @@
         self.properties = {'ui.encoding': 'UTF8',
                            'ui.language': 'en',
                            }
-        
+
     def property_value(self, key):
         return self.properties[key]
 
@@ -51,10 +51,10 @@
         'views' : [Mock(id='primary'), Mock(id='secondary'),
                          Mock(id='oneline'), Mock(id='list')],
         }
-    
+
     def registry_objects(self, name, oid=None):
         return self._registries[name]
-    
+
     def etype_class(self, etype):
         class Entity(dict):
             e_schema = self.schema[etype]
@@ -112,15 +112,15 @@
     def set_header(self, header, value):
         """set an output HTTP header"""
         pass
-    
+
     def add_header(self, header, value):
         """set an output HTTP header"""
         pass
-    
+
     def remove_header(self, header):
         """remove an output HTTP header"""
         pass
-    
+
     def get_header(self, header, default=None):
         """return the value associated with the given input header,
         raise KeyError if the header is not set
@@ -169,7 +169,7 @@
         self.is_internal_session = False
         self.is_super_session = self.user.eid == -1
         self._query_data = {}
-        
+
     def execute(self, *args):
         pass
     def commit(self, *args):
@@ -186,7 +186,7 @@
 
     def set_entity_cache(self, entity):
         pass
-    
+
 class FakeRepo(object):
     querier = None
     def __init__(self, schema, vreg=None, config=None):
@@ -199,8 +199,9 @@
 
     def internal_session(self):
         return FakeSession(self)
-    
-    def extid2eid(self, source, extid, etype, session, insert=True):
+
+    def extid2eid(self, source, extid, etype, session, insert=True,
+                  recreate=False):
         try:
             return self.extids[extid]
         except KeyError:
@@ -213,7 +214,7 @@
             self.eids[eid] = extid
             source.after_entity_insertion(session, extid, entity)
             return eid
-        
+
     def eid2extid(self, source, eid, session=None):
         return self.eids[eid]
 
@@ -228,7 +229,7 @@
     def __init__(self, uri):
         self.uri = uri
 
-        
+
 class FakePool(object):
     def source(self, uri):
         return FakeSource(uri)
--- a/devtools/fill.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/fill.py	Mon May 04 13:09:48 2009 +0200
@@ -2,16 +2,16 @@
 """This modules defines func / methods for creating test repositories
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 from random import randint, choice
 from copy import deepcopy
+from datetime import datetime, date, timedelta
+from decimal import Decimal
 
-from mx.DateTime import DateTime, DateTimeDelta
-from decimal import Decimal
 from yams.constraints import (SizeConstraint, StaticVocabularyConstraint,
                               IntervalBoundConstraint)
 from rql.utils import decompose_b26 as base_decompose_b26
@@ -158,15 +158,15 @@
     
     def generate_date(self, attrname, index):
         """generates a random date (format is 'yyyy-mm-dd')"""
-        return DateTime(randint(2000, 2004), randint(1, 12), randint(1, 28))
+        return date(randint(2000, 2004), randint(1, 12), randint(1, 28))
 
     def generate_time(self, attrname, index):
         """generates a random time (format is ' HH:MM')"""
-        return DateTimeDelta(0, 11, index%60) #'11:%02d' % (index % 60)
+        return timedelta(0, 11, index%60) #'11:%02d' % (index % 60)
     
     def generate_datetime(self, attrname, index):
         """generates a random date (format is 'yyyy-mm-dd HH:MM')"""
-        return DateTime(randint(2000, 2004), randint(1, 12), randint(1, 28), 11, index%60)
+        return datetime(randint(2000, 2004), randint(1, 12), randint(1, 28), 11, index%60)
         
 
     def generate_bytes(self, attrname, index, format=None):
@@ -236,7 +236,7 @@
                         returns acceptable values for this attribute
     """
     # XXX HACK, remove or fix asap
-    if etype in (('String', 'Int', 'Float', 'Boolean', 'Date', 'EGroup', 'EUser')):
+    if etype in (('String', 'Int', 'Float', 'Boolean', 'Date', 'CWGroup', 'CWUser')):
         return []
     queries = []
     for index in xrange(entity_num):
--- a/devtools/htmlparser.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/htmlparser.py	Mon May 04 13:09:48 2009 +0200
@@ -1,20 +1,17 @@
 """defines a validating HTML parser used in web application tests"""
 
 import re
-from StringIO import StringIO
 
 from lxml import etree
-from lxml.builder import E
 
-from cubicweb.common.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE, CW_XHTML_EXTENSIONS
-
-STRICT_DOCTYPE = str(STRICT_DOCTYPE % CW_XHTML_EXTENSIONS).strip()
-TRANSITIONAL_DOCTYPE = str(TRANSITIONAL_DOCTYPE % CW_XHTML_EXTENSIONS).strip()
+from cubicweb.view import STRICT_DOCTYPE, TRANSITIONAL_DOCTYPE
+STRICT_DOCTYPE = str(STRICT_DOCTYPE)
+TRANSITIONAL_DOCTYPE = str(TRANSITIONAL_DOCTYPE)
 
 ERR_COUNT = 0
 
 class Validator(object):
-    
+
     def parse_string(self, data, sysid=None):
         try:
             data = self.preprocess_data(data)
@@ -55,24 +52,11 @@
         for blockquote in blockquotes:
             parent = blockquote.getparent()
             parent.remove(blockquote)
-##         # for each blockquote, wrap unauthorized child in a div
-##         for blockquote in blockquotes:
-##             if len(blockquote):
-##                 needs_wrap = [(index, child) for index, child in enumerate(blockquote)
-##                               if child.tag not in expected]
-##                 for index, child in needs_wrap:
-##                     # the child is automatically popped from blockquote when
-##                     # its parent is changed
-##                     div = E.div(child)
-##                     blockquote.insert(index, div)
-##             elif blockquote.text:
-##                 div = E.div(blockquote.text)
-##                 blockquote.text = None
-##                 blockquote.append(div)
         data = etree.tostring(tree)
-        return '<?xml version="1.0" encoding="UTF-8"?>%s\n%s' % (STRICT_DOCTYPE, data)
+        return '<?xml version="1.0" encoding="UTF-8"?>%s\n%s' % (
+            STRICT_DOCTYPE, data)
 
-   
+
 class SaxOnlyValidator(Validator):
 
     def __init__(self):
@@ -85,7 +69,7 @@
         Validator.__init__(self)
         self.parser = etree.HTMLParser()
 
-    
+
 
 class PageInfo(object):
     """holds various informations on the view's output"""
@@ -103,7 +87,7 @@
         self.h4_tags = self.find_tag('h4')
         self.input_tags = self.find_tag('input')
         self.title_tags = [self.h1_tags, self.h2_tags, self.h3_tags, self.h4_tags]
-        
+
     def find_tag(self, tag):
         """return a list which contains text of all "tag" elements """
         if self.default_ns is None:
@@ -113,14 +97,14 @@
         if tag in ('a', 'input'):
             return [(elt.text, elt.attrib) for elt in self.etree.iterfind(iterstr)]
         return [u''.join(elt.xpath('.//text()')) for elt in self.etree.iterfind(iterstr)]
-         
+
     def appears(self, text):
         """returns True if <text> appears in the page"""
         return text in self.raw_text
 
     def __contains__(self, text):
         return text in self.source
-    
+
     def has_title(self, text, level=None):
         """returns True if <h?>text</h?>
 
@@ -150,7 +134,7 @@
                 if sre.match(title):
                     return True
             return False
-    
+
     def has_link(self, text, url=None):
         """returns True if <a href=url>text</a> was found in the page"""
         for link_text, attrs in self.a_tags:
@@ -164,7 +148,7 @@
                 except KeyError:
                     continue
         return False
-    
+
     def has_link_regexp(self, pattern, url=None):
         """returns True if <a href=url>pattern</a> was found in the page"""
         sre = re.compile(pattern)
--- a/devtools/livetest.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/livetest.py	Mon May 04 13:09:48 2009 +0200
@@ -36,12 +36,9 @@
         """Indicate which resource to use to process down the URL's path"""
         if len(segments) and segments[0] == 'data':
             # Anything in data/ is treated as static files
-            dirlist = [self.data_dir, join(dirname(cubicweb.web.__file__), 'data')]
-            for alternative in dirlist:
-                filepath = join(alternative, *segments[1:]) 
-                if exists(filepath):
-                    self.info('publish static file: %s', '/'.join(segments))
-                    return static.File(filepath), ()
+            datadir = self.config.locate_resource(segments[1])
+            if datadir:
+                return static.File(str(datadir), segments[1:])
         # Otherwise we use this single resource
         return self, ()
     
--- a/devtools/repotest.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/repotest.py	Mon May 04 13:09:48 2009 +0200
@@ -228,10 +228,10 @@
     variantes = _orig_build_variantes(self, newsolutions)
     sortedvariantes = []
     for variante in variantes:
-        orderedkeys = sorted((k[1], k[2], v) for k,v in variante.iteritems())
+        orderedkeys = sorted((k[1], k[2], v) for k, v in variante.iteritems())
         variante = DumbOrderedDict(sorted(variante.iteritems(),
-                                          lambda a,b: cmp((a[0][1],a[0][2],a[1]),
-                                                          (b[0][1],b[0][2],b[1]))))
+                                          lambda a, b: cmp((a[0][1],a[0][2],a[1]),
+                                                           (b[0][1],b[0][2],b[1]))))
         sortedvariantes.append( (orderedkeys, variante) )
     return [v for ok, v in sorted(sortedvariantes)]
 
@@ -241,7 +241,7 @@
 
 def _check_permissions(*args, **kwargs):
     res, restricted = _orig_check_permissions(*args, **kwargs)
-    res = DumbOrderedDict(sorted(res.iteritems(), lambda a,b: cmp(a[1], b[1])))
+    res = DumbOrderedDict(sorted(res.iteritems(), lambda a, b: cmp(a[1], b[1])))
     return res, restricted
 
 def _dummy_check_permissions(self, rqlst):
@@ -267,7 +267,7 @@
     from cubicweb.server.msplanner import PartPlanInformation
 except ImportError:
     class PartPlanInformation(object):
-        def merge_input_maps(*args):
+        def merge_input_maps(self, *args):
             pass
         def _choose_term(self, sourceterms):
             pass    
--- a/devtools/test/data/schema/relations.rel	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/test/data/schema/relations.rel	Mon May 04 13:09:48 2009 +0200
@@ -23,11 +23,11 @@
 Project uses Project
 
 Version version_of Project inline
-Version todo_by EUser
+Version todo_by CWUser
 
 Comment about Bug inline
 Comment about Story inline
 Comment about Comment inline
 
-EUser interested_in Project
+CWUser interested_in Project
 
--- a/devtools/test/data/views/bug.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/test/data/views/bug.py	Mon May 04 13:09:48 2009 +0200
@@ -1,6 +1,7 @@
 """only for unit tests !"""
 
-from cubicweb.common.view import EntityView
+from cubicweb.view import EntityView
+from cubicweb.selectors import implements
 
 HTML_PAGE = u"""<html>
   <body>
@@ -11,7 +12,7 @@
 
 class SimpleView(EntityView):
     id = 'simple'
-    accepts = ('Bug',)
+    __select__ = implements('Bug',)
 
     def call(self, **kwargs):
         self.cell_call(0, 0)
@@ -21,7 +22,7 @@
 
 class RaisingView(EntityView):
     id = 'raising'
-    accepts = ('Bug',)
+    __select__ = implements('Bug',)
 
     def cell_call(self, row, col):
         raise ValueError()
--- a/devtools/test/runtests.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-from logilab.common.testlib import main
-
-if __name__ == '__main__':
-    import sys, os
-    main(os.path.dirname(sys.argv[0]) or '.')
--- a/devtools/test/unittest_testlib.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/test/unittest_testlib.py	Mon May 04 13:09:48 2009 +0200
@@ -24,7 +24,7 @@
                 self.view('raising', self.execute('Bug B'), template=None)
             
             def test_correct_view(self):
-                self.view('primary', self.execute('EUser U'), template=None)
+                self.view('primary', self.execute('CWUser U'), template=None)
             
         tests = [MyWebTest('test_error_view'), MyWebTest('test_correct_view')]
         result = self.runner.run(TestSuite(tests))
--- a/devtools/testlib.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/devtools/testlib.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """this module contains base classes for web tests
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -13,8 +13,6 @@
 from logilab.common.testlib import InnerTest
 from logilab.common.pytest import nocoverage
 
-from rql import parse
-
 from cubicweb.devtools import VIEW_VALIDATORS
 from cubicweb.devtools.apptest import EnvBasedTC
 from cubicweb.devtools._apptest import unprotected_entities, SYSTEM_RELATIONS
@@ -24,8 +22,6 @@
 from cubicweb.sobjects.notification import NotificationView
 
 from cubicweb.vregistry import NoSelectableObject
-from cubicweb.web.action import Action
-from cubicweb.web.views.basetemplates import TheMainTemplate
 
 
 ## TODO ###############
@@ -114,16 +110,16 @@
     # maps vid : validator name (override content_type_validators)
     vid_validators = dict((vid, VALMAP[valkey])
                           for vid, valkey in VIEW_VALIDATORS.iteritems())
-    
+
     no_auto_populate = ()
-    ignored_relations = ()    
-        
+    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
@@ -149,7 +145,7 @@
             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)
+            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:
@@ -158,7 +154,7 @@
         self.commit()
 
     @nocoverage
-    def _check_html(self, output, view, template='main'):
+    def _check_html(self, output, view, template='main-template'):
         """raises an exception if the HTML is invalid"""
         try:
             validatorclass = self.vid_validators[view.id]
@@ -175,7 +171,7 @@
         return validator.parse_string(output.strip())
 
 
-    def view(self, vid, rset, req=None, template='main', **kwargs):
+    def view(self, vid, rset, req=None, template='main-template', **kwargs):
         """This method tests the view `vid` on `rset` using `template`
 
         If no error occured while rendering the view, the HTML is analyzed
@@ -184,12 +180,12 @@
         :returns: an instance of `cubicweb.devtools.htmlparser.PageInfo`
                   encapsulation the generated HTML
         """
-        req = req or rset.req
+        req = req or rset and rset.req or self.request()
         # print "testing ", vid,
         # if rset:
         #     print rset, len(rset), id(rset)
         # else:
-        #     print 
+        #     print
         req.form['vid'] = vid
         view = self.vreg.select_view(vid, req, rset, **kwargs)
         # set explicit test description
@@ -197,24 +193,16 @@
             self.set_description("testing %s, mod=%s (%s)" % (vid, view.__module__, rset.printable_rql()))
         else:
             self.set_description("testing %s, mod=%s (no rset)" % (vid, view.__module__))
-        viewfunc = lambda **k: self.vreg.main_template(req, template, **kwargs)
         if template is None: # raw view testing, no template
             viewfunc = view.dispatch
-        elif template == 'main':
-            _select_view_and_rset = TheMainTemplate._select_view_and_rset
-            # patch TheMainTemplate.process_rql to avoid recomputing resultset
-            def __select_view_and_rset(self, view=view, rset=rset):
-                self.rset = rset
-                return view, rset
-            TheMainTemplate._select_view_and_rset = __select_view_and_rset
-        try:
-            return self._test_view(viewfunc, view, template, **kwargs)
-        finally:
-            if template == 'main':
-                TheMainTemplate._select_view_and_rset = _select_view_and_rset
+        else:
+            templateview = self.vreg.select_view(template, req, rset, view=view, **kwargs)
+            kwargs['view'] = view
+            viewfunc = lambda **k: self.vreg.main_template(req, template, **kwargs)
+        return self._test_view(viewfunc, view, template, kwargs)
 
 
-    def _test_view(self, viewfunc, view, template='main', **kwargs):
+    def _test_view(self, viewfunc, view, template='main-template', kwargs={}):
         """this method does the actual call to the view
 
         If no error occured while rendering the view, the HTML is analyzed
@@ -249,13 +237,13 @@
                     output = '\n'.join(line_template % (idx + 1, line)
                                 for idx, line in enumerate(output)
                                 if line_context_filter(idx+1, position))
-                    msg+= '\nfor output:\n%s' % output
+                    msg += '\nfor output:\n%s' % output
             raise AssertionError, msg, tcbk
 
 
     def to_test_etypes(self):
         return unprotected_entities(self.schema, strict=True)
-    
+
     def iter_automatic_rsets(self, limit=10):
         """generates basic resultsets for each entity type"""
         etypes = self.to_test_etypes()
@@ -272,7 +260,7 @@
         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
@@ -309,7 +297,7 @@
         req = rset.req
         for box in self.vreg.possible_objects('boxes', req, rset):
             yield box
-            
+
     def list_startup_views(self):
         """returns the list of startup views"""
         req = self.request()
@@ -318,7 +306,7 @@
                 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)
@@ -332,15 +320,11 @@
             backup_rset = rset._prepare_copy(rset.rows, rset.description)
             yield InnerTest(self._testname(rset, view.id, 'view'),
                             self.view, view.id, rset,
-                            rset.req.reset_headers(), 'main')
+                            rset.req.reset_headers(), 'main-template')
             # We have to do this because some views modify the
             # resultset's syntax tree
             rset = backup_rset
         for action in self.list_actions_for(rset):
-            # XXX this seems a bit dummy
-            #yield InnerTest(self._testname(rset, action.id, 'action'),
-            #                self.failUnless,
-            #                isinstance(action, Action))
             yield InnerTest(self._testname(rset, action.id, 'action'), action.url)
         for box in self.list_boxes_for(rset):
             yield InnerTest(self._testname(rset, box.id, 'box'), box.dispatch)
@@ -348,7 +332,7 @@
     @staticmethod
     def _testname(rset, objid, objtype):
         return '%s_%s_%s' % ('_'.join(rset.column_types(0)), objid, objtype)
-            
+
 
 class AutomaticWebTest(WebTest):
     """import this if you wan automatic tests to be ran"""
@@ -365,7 +349,7 @@
         for rset in self.iter_automatic_rsets(limit=10):
             for testargs in self._test_everything_for(rset):
                 yield testargs
-                
+
     ## startup views
     def test_startup_views(self):
         for vid in self.list_startup_views():
@@ -390,7 +374,7 @@
         vreg._selected[vobject.__class__] -= 1
     except (KeyError, AttributeError):
         pass
-        
+
 def vreg_instrumentize(testclass):
     from cubicweb.devtools.apptest import TestEnvironment
     env = testclass._env = TestEnvironment('data', configcls=testclass.configcls,
--- a/doc/book/en/A03a-concepts.en.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/en/A03a-concepts.en.txt	Mon May 04 13:09:48 2009 +0200
@@ -267,7 +267,7 @@
   * `set_header(header, value)`, adds an arbitrary header in the response
   * `cursor()` returns a RQL cursor on the session
   * `execute(*args, **kwargs)`, shortcut to ``.cursor().execute()``
-  * `property_value(key)`, properties management (`EProperty`)
+  * `property_value(key)`, properties management (`CWProperty`)
   * dictionary `data` to store data to share informations between components
     *while a request is executed*
 
--- a/doc/book/en/B0011-schema-stdlib.en.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/en/B0011-schema-stdlib.en.txt	Mon May 04 13:09:48 2009 +0200
@@ -12,10 +12,10 @@
 ``````````````
 The available system entities are:
 
-* `EUser`, system users
-* `EGroup`, users groups
-* `EEType`, entity type
-* `ERType`, relation type
+* `CWUser`, system users
+* `CWGroup`, users groups
+* `CWEType`, entity type
+* `CWRType`, relation type
 
 * `State`, workflow state
 * `Transition`, workflow transition
@@ -24,8 +24,8 @@
 * `EmailAddress`, email address, used by the system to send notifications
   to the users and also used by others optionnals schemas
 
-* `EProperty`, used to configure the application
-* `EPermission`, used to configure the security of the application
+* `CWProperty`, used to configure the application
+* `CWPermission`, used to configure the security of the application
 
 * `Card`, generic documenting card
 * `Bookmark`, an entity type used to allow a user to customize his links within
--- a/doc/book/en/B0012-schema-definition.en.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/en/B0012-schema-definition.en.txt	Mon May 04 13:09:48 2009 +0200
@@ -35,18 +35,18 @@
 `````````````````````````````
 
 All `CubicWeb` built-in types are available : `String`, `Int`, `Float`,
-`Decimal`, `Boolean`, `Date`, `Datetime`, `Time`, `Interval`, `Byte` 
+`Decimal`, `Boolean`, `Date`, `Datetime`, `Time`, `Interval`, `Byte`
 and `Password`.
 They are implicitely imported (as well as the special the function "_"
 for translation :ref:`internationalization`).
 
 An attribute is defined in the schema as follows::
-    
+
     attr_name = attr_type(properties*)
 
 where `attr_type` is one of the type listed above and `properties` is
 a list of  the attribute needs to statisfy (see :ref:`properties`
-for more details). 
+for more details).
 
 
 Meta-data
@@ -55,22 +55,22 @@
 Each entity type has at least the following meta-relations :
 
   - `eid` (`Int`)
-  
+
   - `creation_date` (`Datetime`)
-  
+
   - `modification_date` (`Datetime`)
-  
-  - `created_by` (`EUser`) (which user created the entity)
-  
-  - `owned_by` (`EUser`) (to whom the entity belongs; by default the 
+
+  - `created_by` (`CWUser`) (which user created the entity)
+
+  - `owned_by` (`CWUser`) (to whom the entity belongs; by default the
      creator but not necessary, and it could have multiple owners)
-     
-  - `is` (`EEType`) (of which type the entity is)
+
+  - `is` (`CWEType`) (of which type the entity is)
 
 
 * relations can be defined by using `ObjectRelation` or `SubjectRelation`.
   The first argument of `SubjectRelation` or `ObjectRelation` gives respectively
-  the object/subject entity type of the relation. This could be :  
+  the object/subject entity type of the relation. This could be :
 
   * a string corresponding to an entity type
 
@@ -79,7 +79,7 @@
   * special string such as follows :
 
     - "**" : all types of entities
-    - "*" : all types of non-meta entities 
+    - "*" : all types of non-meta entities
     - "@" : all types of meta entities but not system entities (e.g. used for
       the basic schema description)
 
@@ -91,7 +91,7 @@
 .. _properties:
 
 
-* optional properties for attributes and relations : 
+* optional properties for attributes and relations :
 
   - `description` : a string describing an attribute or a relation. By default
     this string will be used in the editing form of the entity, which means
@@ -103,7 +103,7 @@
 
   - `cardinality` : a two character string which specify the cardinality of the
     relation. The first character defines the cardinality of the relation on
-    the subject, and the second on the object. When a relation can have 
+    the subject, and the second on the object. When a relation can have
     multiple subjects or objects, the cardinality applies to all,
     not on a one-to-one basis (so it must be consistent...). The possible
     values are inspired from regular expression syntax :
@@ -116,24 +116,24 @@
   - `meta` : boolean indicating that the relation is a meta-relation (false by
     default)
 
-* optional properties for attributes : 
+* optional properties for attributes :
 
   - `required` : boolean indicating if the attribute is required (false by default)
 
   - `unique` : boolean indicating if the value of the attribute has to be unique
     or not within all entities of the same type (false by default)
 
-  - `indexed` : boolean indicating if an index needs to be created for this 
+  - `indexed` : boolean indicating if an index needs to be created for this
     attribute in the database (false by default). This is useful only if
     you know that you will have to run numerous searches on the value of this
     attribute.
 
   - `default` : default value of the attribute. In case of date types, the values
     which could be used correspond to the RQL keywords `TODAY` and `NOW`.
-  
+
   - `vocabulary` : specify static possible values of an attribute
 
-* optional properties of type `String` : 
+* optional properties of type `String` :
 
   - `fulltextindexed` : boolean indicating if the attribute is part of
     the full text index (false by default) (*applicable on the type `Byte`
@@ -144,11 +144,11 @@
 
   - `maxsize` : integer providing the maximum size of the string (no limit by default)
 
-* optional properties for relations : 
+* optional properties for relations :
 
   - `composite` : string indicating that the subject (composite == 'subject')
     is composed of the objects of the relations. For the opposite case (when
-    the object is composed of the subjects of the relation), we just set 
+    the object is composed of the subjects of the relation), we just set
     'object' as value. The composition implies that when the relation
     is deleted (so when the composite is deleted), the composed are also deleted.
 
@@ -159,7 +159,7 @@
 * `SizeConstraint` : allows to specify a minimum and/or maximum size on
   string (generic case of `maxsize`)
 
-* `BoundConstraint` : allows to specify a minimum and/or maximum value on 
+* `BoundConstraint` : allows to specify a minimum and/or maximum value on
   numeric types
 
 * `UniqueConstraint` : identical to "unique=True"
@@ -168,7 +168,7 @@
 
 * `RQLConstraint` : allows to specify a RQL query that has to be satisfied
   by the subject and/or the object of the relation. In this query the variables
-  `S` and `O` are reserved for the entities subject and object of the 
+  `S` and `O` are reserved for the entities subject and object of the
   relation.
 
 * `RQLVocabularyConstraint` : similar to the previous type of constraint except
@@ -184,9 +184,9 @@
 
 A relation is defined by a Python class heriting `RelationType`. The name
 of the class corresponds to the name of the type. The class then contains
-a description of the properties of this type of relation, and could as well 
+a description of the properties of this type of relation, and could as well
 contain a string for the subject and a string for the object. This allows to create
-new definition of associated relations, (so that the class can have the 
+new definition of associated relations, (so that the class can have the
 definition properties from the relation) for example ::
 
   class locked_by(RelationType):
@@ -194,7 +194,7 @@
     inlined = True
     cardinality = '?*'
     subject = '*'
-    object = 'EUser'
+    object = 'CWUser'
 
 In addition to the permissions, the properties of the relation types
 (shared also by all definition of relation of this type) are :
@@ -217,13 +217,12 @@
 `ObjectRelation`) is all we need.
 
 
-
 Updating your application with your new schema
 ----------------------------------------------
 
-If you modified your schema, the update is not automatic; indeed, this is 
+If you modified your schema, the update is not automatic; indeed, this is
 in general not a good idea.
-Instead, you call a shell on your application, which is a 
+Instead, you call a shell on your application, which is a
 an interactive python shell, with an appropriate
 cubicweb environment ::
 
--- a/doc/book/en/B0020-define-workflows.en.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/en/B0020-define-workflows.en.txt	Mon May 04 13:09:48 2009 +0200
@@ -69,7 +69,7 @@
   
   _ = unicode
 
-  moderators = add_entity('EGroup', name=u"moderators")
+  moderators = add_entity('CWGroup', name=u"moderators")
 
 This adds the `moderators` user group.
 
--- a/doc/book/en/B1070-ui-components.en.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/en/B1070-ui-components.en.txt	Mon May 04 13:09:48 2009 +0200
@@ -9,6 +9,6 @@
 ---------------------
 XXXFILLME
 
-EProperty
+CWProperty
 ---------
 XXXFILLME
--- a/doc/book/en/C040-instance-config.en.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/en/C040-instance-config.en.txt	Mon May 04 13:09:48 2009 +0200
@@ -30,7 +30,7 @@
 
 :`main.anonymous-user`, `main.anonymous-password`:
     login and password to use to connect to the RQL server with
-    HTTP anonymous connection. EUser account should exist.
+    HTTP anonymous connection. CWUser account should exist.
 
 :`main.base-url`:
     url base site to be used to generate the urls of web pages
@@ -146,7 +146,7 @@
 
 Configuring Eproperties
 -----------------------
-Other configuration settings are in the form of entities `EProperty`
+Other configuration settings are in the form of entities `CWProperty`
 in the database. It must be edited via the web interface or by
 RQL queries.
 
--- a/doc/book/en/D020-cookbook.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/en/D020-cookbook.txt	Mon May 04 13:09:48 2009 +0200
@@ -42,7 +42,7 @@
         update = "UPDATE entities SET source='system' WHERE eid=%(eid)s;"
         cursor.execute("SELECT eid,type,source,extid,mtime FROM entities WHERE source!='system'")
         for eid, type, source, extid, mtime in cursor.fetchall():
-            if type != 'EUser':
+            if type != 'CWUser':
                 print "don't know what to do with entity type", type
                 continue
             if source != 'ldapuser':
--- a/doc/book/en/MERGE_ME-tut-create-app.en.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/en/MERGE_ME-tut-create-app.en.txt	Mon May 04 13:09:48 2009 +0200
@@ -323,7 +323,7 @@
   
   _ = unicode
 
-  moderators      = add_entity('EGroup', name=u"moderators")
+  moderators      = add_entity('CWGroup', name=u"moderators")
 
   submitted = add_state(_('submitted'), 'BlogEntry', initial=True)
   published = add_state(_('published'), 'BlogEntry')
--- a/doc/book/fr/02-foundation.fr.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/fr/02-foundation.fr.txt	Mon May 04 13:09:48 2009 +0200
@@ -258,7 +258,7 @@
     réponse 
   * `cursor()` retourne un curseur RQL sur la session
   * `execute(*args, **kwargs)`, raccourci vers .cursor().execute()
-  * `property_value(key)`, gestion des propriétés (`EProperty`)
+  * `property_value(key)`, gestion des propriétés (`CWProperty`)
   * le dictionaire `data` pour stocker des données pour partager de
     l'information entre les composants *durant l'éxécution de la requête*.
 
--- a/doc/book/fr/04-01-schema-stdlib.fr.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/fr/04-01-schema-stdlib.fr.txt	Mon May 04 13:09:48 2009 +0200
@@ -11,10 +11,10 @@
 Schémas "systèmes"
 ``````````````````
 
-* `EUser`, utilisateurs du système
-* `EGroup`, groupes d'utilisateurs
-* `EEType`, types d'entité
-* `ERType`, types de relation
+* `CWUser`, utilisateurs du système
+* `CWGroup`, groupes d'utilisateurs
+* `CWEType`, types d'entité
+* `CWRType`, types de relation
 
 * `State`, état d'un workflow
 * `Transition`, transition d'un workflow
@@ -23,8 +23,8 @@
 * `EmailAddress`, adresse électronique, utilisé par le système de notification
   pour les utilisateurs et par d'autres schéma optionnels
 
-* `EProperty`, utilisé pour configurer l'application
-* `EPermission`, utilisé pour configurer la sécurité de l'application
+* `CWProperty`, utilisé pour configurer l'application
+* `CWPermission`, utilisé pour configurer la sécurité de l'application
 
 * `Card`, fiche documentaire générique
 * `Bookmark`, un type d'entité utilisé pour permetter à un utilisateur de
--- a/doc/book/fr/04-02-schema-definition.fr.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/fr/04-02-schema-definition.fr.txt	Mon May 04 13:09:48 2009 +0200
@@ -32,12 +32,12 @@
   
   - `modification_date` (`Datetime`)
   
-  - `created_by` (`EUser`) (quel utilisateur a créé l'entité)
+  - `created_by` (`CWUser`) (quel utilisateur a créé l'entité)
   
-  - `owned_by` (`EUser`) (à qui appartient l'entité, par défaut le
+  - `owned_by` (`CWUser`) (à qui appartient l'entité, par défaut le
      créateur mais pas forcément et il peut exister plusieurs propriétaires)
      
-  - `is` (`EEType`)
+  - `is` (`CWEType`)
 
   
 * il est également possible de définir des relations dont le type d'entité est
@@ -167,7 +167,7 @@
     inlined = True
     cardinality = '?*'
     subject = '*'
-    object = 'EUser'
+    object = 'CWUser'
 
 En plus des permissions, les propriétés propres aux types de relation (et donc
 partagés par toutes les définitions de relation de ce type) sont :
@@ -263,16 +263,16 @@
     'add'/'read' son pris en considération
 
 
-En plus de cela, le type d'entité `EPermission` de la librairie standard permet
+En plus de cela, le type d'entité `CWPermission` de la librairie standard permet
 de construire des modèles de sécurités très complexes et dynamiques. Le schéma
 de ce type d'entité est le suivant : ::
 
 
-    class EPermission(MetaEntityType):
+    class CWPermission(MetaEntityType):
 	"""entity type that may be used to construct some advanced security configuration
 	"""
 	name = String(required=True, indexed=True, internationalizable=True, maxsize=100)
-	require_group = SubjectRelation('EGroup', cardinality='+*',
+	require_group = SubjectRelation('CWGroup', cardinality='+*',
 					description=_('groups to which the permission is granted'))
 	require_state = SubjectRelation('State',
 				    description=_("entity'state in which the permission is applyable"))
@@ -311,7 +311,7 @@
 		       }
 	inlined = True
 
-Cette configuration suppose indique qu'une entité `EPermission` de nom
+Cette configuration suppose indique qu'une entité `CWPermission` de nom
 "add_version" peut-être associée à un projet et donner le droit de créer des
 versions sur ce projet à des groupes spécifiques. Il est important de noter les
 points suivants :
@@ -319,7 +319,7 @@
 * dans ce cas il faut protéger à la fois le type d'entité "Version" et la
   relation liant une version à un projet ("version_of")
 
-* du fait de la généricité du type d'entité `EPermission`, il faut effectuer
+* du fait de la généricité du type d'entité `CWPermission`, il faut effectuer
   l'unification avec les groupes et / ou les états le cas échéant dans
   l'expression ("U in_group G, P require_group G" dans l'exemple ci-dessus)
 
--- a/doc/book/fr/06-define-workflows.fr.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/fr/06-define-workflows.fr.txt	Mon May 04 13:09:48 2009 +0200
@@ -117,7 +117,7 @@
   
   _ = unicode
 
-  moderators      = add_entity('EGroup', name=u"moderators")
+  moderators      = add_entity('CWGroup', name=u"moderators")
 
   submitted = add_state(_('submitted'), 'BlogEntry', initial=True)
   published = add_state(_('published'), 'BlogEntry')
--- a/doc/book/fr/09-instance-config.fr.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/fr/09-instance-config.fr.txt	Mon May 04 13:09:48 2009 +0200
@@ -29,7 +29,7 @@
 
 :`main.anonymous-user`, `main.anonymous-password`:
    login et mot de passe à utiliser pour se connecter au serveur RQL lors des
-   connexions HTTP anonymes. Il faut que le compte EUser associé existe.
+   connexions HTTP anonymes. Il faut que le compte CWUser associé existe.
 
 :`main.base-url`:
    url de base du site, à utiliser pour générer les urls des pages web
@@ -145,7 +145,7 @@
 
 Configuration Eproperties
 -------------------------
-D'autres paramètres de configuration sont sous la forme d'entités `EProperty`
+D'autres paramètres de configuration sont sous la forme d'entités `CWProperty`
 dans la base de données. Il faut donc les éditer via l'interface web ou par des
 requêtes rql.
 
--- a/doc/book/fr/12-ui-components.fr.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/fr/12-ui-components.fr.txt	Mon May 04 13:09:48 2009 +0200
@@ -9,6 +9,6 @@
 ---------------------
 XXXFILLME
 
-EProperty
+CWProperty
 ---------
 XXXFILLME
--- a/doc/book/fr/MERGE_ME-tut-create-app.fr.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/doc/book/fr/MERGE_ME-tut-create-app.fr.txt	Mon May 04 13:09:48 2009 +0200
@@ -331,7 +331,7 @@
   
   _ = unicode
 
-  moderators      = add_entity('EGroup', name=u"moderators")
+  moderators      = add_entity('CWGroup', name=u"moderators")
 
   submitted = add_state(_('submitted'), 'BlogEntry', initial=True)
   published = add_state(_('published'), 'BlogEntry')
--- a/embedded/mx/DateTime/ARPA.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,227 +0,0 @@
-""" This module provides a set of constructors and routines to convert
-    between DateTime[Delta] instances and ARPA representations of date
-    and time. The format is specified by RFC822 + RFC1123.
-
-    Note: Timezones are only interpreted by ParseDateTimeGMT(). All
-    other constructors silently ignore the time zone information.
-
-    Copyright (c) 1998-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
-    Copyright (c) 2000-2007, eGenix.com Software GmbH; mailto:info@egenix.com
-    See the documentation for further information on copyrights,
-    or contact the author. All Rights Reserved.
-
-"""
-import DateTime,Timezone
-import re,string
-
-# Grammar: RFC822 + RFC1123 + depreciated RFC850
-_litday = '(?P<litday>Mon|Tue|Wed|Thu|Fri|Sat|Sun)[a-z]*'
-_litmonth = '(?P<litmonth>Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)'\
-            '[a-z]*'
-_date = ('(?:(?P<day>\d?\d)(?: +' + _litmonth + 
-         ' +|-(?P<month>\d?\d)-)(?P<year>(?:\d\d)?\d\d))')
-_zone = Timezone.zone
-_time = ('(?:(?P<hour>\d\d):(?P<minute>\d\d)'
-         '(?::(?P<second>\d\d))?(?: +'+_zone+')?)')
-#       Timezone information is made optional because some mail apps
-#       forget to add it (most of these seem to be spamming engines, btw).
-#       It defaults to UTC.
-
-_arpadate = '(?:'+ _litday + ',? )? *' + _date
-_arpadatetime = '(?:'+ _litday + ',? )? *' + _date + ' +' + _time
-
-#       We are not strict about the extra characters: some applications
-#       add extra information to the date header field. Additional spaces
-#       between the fields and extra characters in the literal day
-#       and month fields are also silently ignored.
-
-arpadateRE = re.compile(_arpadate)
-arpadatetimeRE = re.compile(_arpadatetime)
-
-# Translation tables
-litdaytable = {'mon':0, 'tue':1, 'wed':2, 'thu':3, 'fri':4, 'sat':5, 'sun':6 }
-litmonthtable = {'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6,
-                 'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12 }
-_days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
-_months = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
-                 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ]
-
-def ParseDate(arpastring,parse_arpadate=arpadateRE.match,
-
-              strip=string.strip,atoi=string.atoi,atof=string.atof,
-              lower=string.lower):
-
-    """ParseDate(arpastring)
-
-       Returns a DateTime instance reflecting the given ARPA
-       date. Only the date part is parsed, any time part will be
-       ignored. The instance's time is set to 0:00:00.
-
-    """
-    s = strip(arpastring)
-    date = parse_arpadate(s)
-    if not date:
-        raise ValueError,'wrong format'
-    litday,day,litmonth,month,year = date.groups()
-    if len(year) == 2:
-        year = DateTime.add_century(atoi(year))
-    else:
-        year = atoi(year)
-    if litmonth:
-        litmonth = lower(litmonth)
-        try:
-            month = litmonthtable[litmonth]
-        except KeyError:
-            raise ValueError,'wrong month format'
-    else:
-        month = atoi(month)
-    day = atoi(day)
-    # litday and timezone are ignored
-    return DateTime.DateTime(year,month,day)
-
-def ParseDateTime(arpastring,parse_arpadatetime=arpadatetimeRE.match,
-
-                  strip=string.strip,atoi=string.atoi,atof=string.atof,
-                  lower=string.lower):
-
-    """ParseDateTime(arpastring)
-
-       Returns a DateTime instance reflecting the given ARPA date assuming
-       it is local time (timezones are silently ignored).
-    """
-    s = strip(arpastring)
-    date = parse_arpadatetime(s)
-    if not date:
-        raise ValueError,'wrong format or unknown time zone'
-    litday,day,litmonth,month,year,hour,minute,second,zone = date.groups()
-    if len(year) == 2:
-        year = DateTime.add_century(atoi(year))
-    else:
-        year = atoi(year)
-    if litmonth:
-        litmonth = lower(litmonth)
-        try:
-            month = litmonthtable[litmonth]
-        except KeyError:
-            raise ValueError,'wrong month format'
-    else:
-        month = atoi(month)
-    day = atoi(day)
-    hour = atoi(hour)
-    minute = atoi(minute)
-    if second is None:
-        second = 0.0
-    else:
-        second = atof(second)
-    # litday and timezone are ignored
-    return DateTime.DateTime(year,month,day,hour,minute,second)
-
-def ParseDateTimeGMT(arpastring,parse_arpadatetime=arpadatetimeRE.match,
-
-                     strip=string.strip,atoi=string.atoi,atof=string.atof,
-                     lower=string.lower):
-
-    """ParseDateTimeGMT(arpastring)
-
-       Returns a DateTime instance reflecting the given ARPA date converting
-       it to UTC (timezones are honored).
-    """
-    s = strip(arpastring)
-    date = parse_arpadatetime(s)
-    if not date:
-        raise ValueError,'wrong format or unknown time zone'
-    litday,day,litmonth,month,year,hour,minute,second,zone = date.groups()
-    if len(year) == 2:
-        year = DateTime.add_century(atoi(year))
-    else:
-        year = atoi(year)
-    if litmonth:
-        litmonth = lower(litmonth)
-        try:
-            month = litmonthtable[litmonth]
-        except KeyError:
-            raise ValueError,'wrong month format'
-    else:
-        month = atoi(month)
-    day = atoi(day)
-    hour = atoi(hour)
-    minute = atoi(minute)
-    if second is None:
-        second = 0.0
-    else:
-        second = atof(second)
-    offset = Timezone.utc_offset(zone)
-    # litday is ignored
-    return DateTime.DateTime(year,month,day,hour,minute,second) - offset
-
-# Alias
-ParseDateTimeUTC = ParseDateTimeGMT
-
-def str(datetime,tz=None):
-
-    """str(datetime,tz=DateTime.tz_offset(datetime))
-
-    Returns the datetime instance as ARPA date string. tz can be given
-    as DateTimeDelta instance providing the time zone difference from
-    datetime's zone to UTC. It defaults to
-    DateTime.tz_offset(datetime) which assumes local time. """
-
-    if tz is None:
-        tz = datetime.gmtoffset()
-    return '%s, %02i %s %04i %02i:%02i:%02i %+03i%02i' % (
-        _days[datetime.day_of_week], datetime.day, 
-        _months[datetime.month], datetime.year,
-        datetime.hour, datetime.minute, datetime.second,
-        tz.hour,tz.minute)
-
-def strGMT(datetime):
-
-    """ strGMT(datetime)
-
-    Returns the datetime instance as ARPA date string assuming it
-    is given in GMT. """
-
-    return '%s, %02i %s %04i %02i:%02i:%02i GMT' % (
-        _days[datetime.day_of_week], datetime.day, 
-        _months[datetime.month], datetime.year,
-        datetime.hour, datetime.minute, datetime.second)
-
-def strUTC(datetime):
-
-    """ strUTC(datetime)
-
-    Returns the datetime instance as ARPA date string assuming it
-    is given in UTC. """
-
-    return '%s, %02i %s %04i %02i:%02i:%02i UTC' % (
-        _days[datetime.day_of_week], datetime.day, 
-        _months[datetime.month], datetime.year,
-        datetime.hour, datetime.minute, datetime.second)
-
-def _test():
-    import sys, os, rfc822
-    file = os.path.join(os.environ['HOME'], 'nsmail/Inbox')
-    f = open(file, 'r')
-    while 1:
-        m = rfc822.Message(f)
-        if not m:
-            break
-        print 'From:', m.getaddr('from')
-        print 'To:', m.getaddrlist('to')
-        print 'Subject:', m.getheader('subject')
-        raw = m.getheader('date')
-        try:
-            date = ParseDateTimeUTC(raw)
-            print 'Date:',strUTC(date)
-        except ValueError,why:
-            print 'PROBLEMS:',repr(raw),'-->',why
-            raw_input('...hit return to continue')
-        print
-        # Netscape mail file
-        while 1:
-            line = f.readline()
-            if line[:6] == 'From -':
-                break
-
-if __name__ == '__main__':
-    _test()
--- a/embedded/mx/DateTime/DateTime.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1054 +0,0 @@
-""" Python part of the low-level DateTime[Delta] type implementation.
-
-    Copyright (c) 1998-2001, Marc-Andre Lemburg; mailto:mal@lemburg.com
-    Copyright (c) 2000-2007, eGenix.com Software GmbH; mailto:info@egenix.com
-    See the documentation for further information on copyrights,
-    or contact the author. All Rights Reserved.
-"""
-# Import the python implementation module
-from mxDateTime_python import *
-from mxDateTime_python import __version__
-
-# Singletons
-oneSecond = DateTimeDelta(0,0,0,1)
-oneMinute = DateTimeDelta(0,0,1)
-oneHour = DateTimeDelta(0,1)
-oneDay = DateTimeDelta(1)
-oneWeek = DateTimeDelta(7)
-Epoch = DateTimeFromAbsDateTime(1,0)
-
-# Shortcuts for pickle; for backward compatibility only (they are now
-# defined in __init__.py to further reduce the pickles length)
-def _DT(absdate,abstime):
-    return DateTimeFromAbsDateTime(absdate,abstime)
-def _DTD(seconds):
-    return DateTimeDeltaFromSeconds(seconds)
-
-# Module init
-class modinit:
-
-    global _time,_string,_math,_types
-    import time,string,math,types
-    _time = time
-    _string = string
-    _math = math
-    _types = types
-
-del modinit
-
-### Helpers
-
-def _isstring(arg,
-
-              isinstance=isinstance, types=_types):
-    
-    if isinstance(arg, types.StringType):
-        return 1
-    try:
-        if isinstance(arg, types.UnicodeType):
-            return 1
-    except AttributeError:
-        pass
-    return 0
-
-### Compatibility APIs
-
-# Aliases and functions to make 'from mx.DateTime import *' work much
-# like 'from time import *'
-
-def localtime(ticks=None,
-              # Locals:
-              time=_time.time,float=float,localtime=_time.localtime,
-              round=round,int=int,DateTime=DateTime,floor=_math.floor):
-
-    """localtime(ticks=None)
-
-       Construct a DateTime instance using local time from ticks.  If
-       ticks are not given, it defaults to the current time.  The
-       result is similar to time.localtime(). Fractions of a second
-       are rounded to the nearest micro-second.
-
-    """
-    if ticks is None:
-        ticks = time()
-    else:
-        ticks = float(ticks)
-    ticks = round(ticks, 6)
-    fticks = floor(ticks)
-    Y,M,D,h,m,s = localtime(fticks)[:6]
-    s = s + (ticks - fticks)
-    return DateTime(Y,M,D,h,m,s)
-
-def gmtime(ticks=None,
-           # Locals:
-           time=_time.time,float=float,gmtime=_time.gmtime,
-           round=round,int=int,DateTime=DateTime,floor=_math.floor):
-
-    """gmtime(ticks=None)
-
-       Construct a DateTime instance using UTC time from ticks.  If
-       ticks are not given, it defaults to the current time.  The
-       result is similar to time.gmtime(). Fractions of a second are
-       rounded to the nearest micro-second.
-
-    """
-    if ticks is None:
-        ticks = time()
-    else:
-        ticks = float(ticks)
-    ticks = round(ticks, 6)
-    fticks = floor(ticks)
-    Y,M,D,h,m,s = gmtime(ticks)[:6]
-    s = s + (ticks - fticks)
-    return DateTime(Y,M,D,h,m,s)
-
-def mktime((year,month,day,hour,minute,second,dow,doy,dst),
-           # Locals:
-           DateTime=DateTime):
-
-    """mktime((year,month,day,hour,minute,second,dow,doy,dst))
-
-       Same as the DateTime() constructor accept that the interface
-       used is compatible to the similar time.mktime() API.
-
-       Note that the tuple elements dow, doy and dst are not used in
-       any way.
-      
-    """
-    return DateTime(year,month,day,hour,minute,second)
-
-def ctime(datetime):
-
-    """ctime(datetime)
-
-       Returns a string representation of the given DateTime instance
-       using the current locale's default settings.
-
-    """
-    return datetime.strftime('%c')
-
-def today(hour=0,minute=0,second=0.0,
-          # Locals:
-          localtime=_time.localtime,time=_time.time,DateTime=DateTime):
-
-    """today(hour=0,minute=0,second=0.0)
-
-       Returns a DateTime instance for today (in local time) at the
-       given time (defaults to midnight).
-
-    """
-    Y,M,D = localtime(time())[:3]
-    return DateTime(Y,M,D,hour,minute,second)
-
-def TimeDelta(hours=0.0,minutes=0.0,seconds=0.0,
-              # Locals:
-              DateTimeDelta=DateTimeDelta):
-
-    """TimeDelta(hours=0.0,minutes=0.0,seconds=0.0)
-
-       Returns a DateTimeDelta-object reflecting the given time
-       delta. Seconds can be given as float to indicate fractions.
-
-    """
-    return DateTimeDelta(0,hours,minutes,seconds)
-
-def gm2local(datetime):
-
-    """ gm2local(datetime)
-
-        Convert a DateTime instance holding UTC time to a DateTime
-        instance using local time.
-
-    """
-    return localtime(datetime.gmticks())
-
-def local2gm(datetime):
-
-    """ local2gm(datetime)
-
-        Convert a DateTime instance holding local time to a DateTime
-        instance using UTC time.
-
-    """
-    return gmtime(datetime.ticks())
-
-# Alias
-gmt = utc
-
-# Default value for DateTimeFromTJD's tjd_myriad parameter
-current_myriad = localtime().tjd_myriad
-
-def DateTimeFromTJD(tjd,tjd_myriad=current_myriad):
-
-    """ DateTimeFromTJD(tjd[,myriad])
-
-        Return a DateTime instance for the given Truncated Julian Day.
-        myriad defaults to the TJD myriad current at package import
-        time.
-
-        Note that this version of Truncated Julian Day number does
-        real truncation of important information. It's use is
-        discouraged and unsupported.
-
-    """
-    return DateTimeFromAbsDays(tjd + tjd_myriad * 10000.0 - 1721425.0)
-
-def DateTimeFromJDN(jdn):
-
-    """ DateTimeFromJDN(jdn)
-
-        Return a DateTime instance for the given Julian Day Number.
-
-        References:
-        -----------
-        Gregorian 2000-01-01 12:00:00 corresponds to JDN 2451545.0.
-        Gregorian 1858-11-17 00:00:00.00 corresponds to JDN 2400000.5; MJD 0.0.
-        Julian -4712-01-01 12:00:00.00 corresponds to JDN 0.0.
-        Gregorian -4713-11-24 12:00:00.00 corresponds to JDN 0.0.
-
-    """
-    return DateTimeFromAbsDays(jdn - 1721425.5)
-
-def DateTimeFromMJD(mjd):
-
-    """ DateTimeFromMJD(mjd)
-
-        Return a DateTime instance for the given Modified Julian Day
-        (MJD). The MJD is calculated the same way as the JDN except
-        that 1858-11-17 00:00:00.00 is taken as origin of the scale.
-
-    """
-    return DateTimeFromAbsDays(mjd + 678575.0)
-
-def DateTimeFrom(*args, **kws):
-
-    """ DateTimeFrom(*args, **kws)
-
-        Generic DateTime instance constructor. Can handle parsing
-        strings, numbers and keywords.
-
-        XXX Add support for Unicode.
-
-    """
-    if len(args) == 1:
-        # Single argument
-        arg = args[0]
-        argtype = type(arg)
-        if _isstring(arg):
-            import Parser
-            return apply(Parser.DateTimeFromString, args, kws)
-        elif argtype is DateTimeType:
-            return arg
-        elif argtype is DateTimeDeltaType:
-            raise TypeError,'cannot convert DateTimeDelta to DateTime'
-        else:
-            try:
-                value = float(arg)
-            except (TypeError, ValueError):
-                value = int(arg)
-            assert not kws
-            return DateTimeFromTicks(value)
-
-    elif len(args) > 1:
-        # More than one argument
-        if len(args) == 2 and _isstring(args[0]) and _isstring(args[1]):
-            # interpret as date and time string
-            import Parser
-            return apply(Parser.DateTimeFromString,
-                         (args[0] + ' ' + args[1],),
-                         kws)
-
-        # Assume the arguments are the same as for DateTime()
-        return apply(DateTime, args, kws)
-
-    elif len(kws) > 0:
-        # Keyword arguments; add defaults... today at 0:00:00
-        hour = kws.get('hour',0)
-        minute = kws.get('minute',0)
-        second = kws.get('second',0)
-        today = now()
-        day = kws.get('day',today.day)
-        month = kws.get('month',today.month)
-        year = kws.get('year',today.year)
-        return DateTime(year,month,day,hour,minute,second)
-
-    else:
-        raise TypeError,'cannot convert arguments to DateTime'
-
-def DateTimeDeltaFrom(*args, **kws):
-
-    """ DateTimeDeltaFrom(*args, **kws)
-
-        Generic DateTimeDelta instance constructor. Can handle parsing
-        strings, numbers and keywords.
-
-        XXX Add support for Unicode.
-
-    """
-    if len(args) == 1:
-        # Single argument
-        arg = args[0]
-        if _isstring(arg):
-            import Parser
-            return apply(Parser.DateTimeDeltaFromString, args, kws)
-        elif type(arg) is DateTimeDeltaType:
-            return arg
-        elif type(arg) is DateTimeType:
-            raise TypeError,'cannot convert DateTime to DateTimeDelta'
-        else:
-            try:
-                value = float(arg)
-            except TypeError:
-                value = int(arg)
-            assert not kws
-            return DateTimeDeltaFromSeconds(value)
-
-    elif len(args) > 1:
-        # Assume the arguments are the same as for DateTimeDelta()
-        return apply(DateTimeDelta, args, kws)
-
-    elif len(kws) > 0:
-        # Keyword arguments; default: 00:00:00:00.00
-        hours = kws.get('hours',0)
-        minutes = kws.get('minutes',0)
-        seconds = kws.get('seconds',0.0)
-        days = kws.get('days',0)
-        return DateTimeDelta(days,hours,minutes,seconds)
-
-    else:
-        raise TypeError,'cannot convert arguments to DateTimeDelta'
-
-def TimeDeltaFrom(*args, **kws):
-
-    """ TimeDeltaFrom(*args, **kws)
-
-        Generic TimeDelta instance constructor. Can handle parsing
-        strings, numbers and keywords.
-
-        XXX Add support for Unicode.
-
-    """
-    if len(args) > 1:
-        # Assume the arguments are the same as for TimeDelta(): without
-        # days part !
-        return apply(DateTimeDelta, (0,)+args, kws)
-    else:
-        # Otherwise treat the arguments just like for DateTimeDelta
-        # instances.
-        return apply(DateTimeDeltaFrom, args, kws)
-
-def DateFromTicks(ticks,
-                  # Locals:
-                  DateTime=DateTime,localtime=_time.localtime):
-
-    """ DateFromTicks(ticks)
-
-        Constructs a DateTime instance pointing to the local time date
-        at 00:00:00.00 (midnight) indicated by the given ticks value.
-        The time part is ignored.
-
-    """
-    return apply(DateTime, localtime(ticks)[:3])
-
-def TimestampFromTicks(ticks,
-                       # Locals:
-                       DateTime=DateTime,localtime=_time.localtime):
-
-    """ TimestampFromTicks(ticks)
-
-        Constructs a DateTime instance pointing to the local date and
-        time indicated by the given ticks value.
-
-    """
-    return apply(DateTime, localtime(ticks)[:6])
-
-def TimeFromTicks(ticks,
-                  # Locals:
-                  DateTimeDelta=DateTimeDelta,localtime=_time.localtime):
-
-    """ TimeFromTicks(ticks)
-
-        Constructs a DateTimeDelta instance pointing to the local time
-        indicated by the given ticks value. The date part is ignored.
-
-    """
-    return apply(DateTimeDelta, (0,) + localtime(ticks)[3:6])
-
-# Aliases
-utctime = gmtime
-utc2local = gm2local
-local2utc = local2gm
-DateTimeFromTicks = localtime
-Date = DateTime
-Time = TimeDelta
-Timestamp = DateTime
-DateFrom = DateTimeFrom # XXX should only parse the date part !
-TimeFrom = TimeDeltaFrom
-TimestampFrom = DateTimeFrom
-GregorianDateTime = DateTime
-GregorianDate = Date
-JulianDate = JulianDateTime
-
-
-### For backward compatibility (these are depreciated):
-
-def gmticks(datetime):
-
-    """gmticks(datetime)
-
-       [DEPRECIATED: use the .gmticks() method]
-    
-       Returns a ticks value based on the values stored in
-       datetime under the assumption that they are given in UTC,
-       rather than local time.
-
-    """
-    return datetime.gmticks()
-
-# Alias
-utcticks = gmticks
-
-def tz_offset(datetime,
-              # Locals:
-              oneSecond=oneSecond):
-
-    """tz_offset(datetime)
-
-       [DEPRECIATED: use the .gmtoffset() method]
-    
-       Returns a DateTimeDelta instance representing the UTC
-       offset for datetime assuming that the stored values refer
-       to local time. If you subtract this value from datetime,
-       you'll get UTC time.
-
-    """
-    return datetime.gmtoffset()
-
-### Constants (only English; see Locale.py for other languages)
-
-# Weekdays
-Monday =        0
-Tuesday =       1
-Wednesday =     2
-Thursday =      3
-Friday =        4
-Saturday =      5
-Sunday =        6
-# as mapping
-Weekday = {'Saturday': 5, 6: 'Sunday', 'Sunday': 6, 'Thursday': 3,
-           'Wednesday': 2, 'Friday': 4, 'Tuesday': 1, 'Monday': 0,
-           5: 'Saturday', 4: 'Friday', 3: 'Thursday', 2: 'Wednesday',
-           1: 'Tuesday', 0: 'Monday'}
-
-# Months
-January =       1
-February =      2
-March =         3
-April =         4
-May =           5
-June =          6
-July =          7
-August =        8 
-September =     9
-October =       10
-November =      11
-December =      12
-# as mapping
-Month = {2: 'February', 3: 'March', None: 0, 'July': 7, 11: 'November',
-    'December': 12, 'June': 6, 'January': 1, 'September': 9, 'August':
-    8, 'March': 3, 'November': 11, 'April': 4, 12: 'December', 'May':
-    5, 10: 'October', 9: 'September', 8: 'August', 7: 'July', 6:
-    'June', 5: 'May', 4: 'April', 'October': 10, 'February': 2, 1:
-    'January', 0: None}
-
-# Limits (see also the range checks in mxDateTime.c)
-MaxDateTime = DateTime(5867440,12,31) 
-MinDateTime = DateTime(-5851455,1,1)
-MaxDateTimeDelta = DateTimeDeltaFromSeconds(2147483647 * 86400.0)
-MinDateTimeDelta = -MaxDateTimeDelta
-
-###
-
-class RelativeDateTime:
-
-    """RelativeDateTime(years=0,months=0,days=0,
-                  hours=0,minutes=0,seconds=0,
-                  year=0,month=0,day=0,
-                  hour=None,minute=None,second=None,
-                  weekday=None,weeks=None)
-
-       Returns a RelativeDateTime instance for the specified relative
-       time. The constructor handles keywords, so you'll only have to
-       give those parameters which should be changed when you add the
-       relative to an absolute DateTime instance.
-
-       Adding RelativeDateTime instances is supported with the
-       following rules: deltas will be added together, right side
-       absolute values override left side ones.
-
-       Adding RelativeDateTime instances to DateTime instances will
-       return DateTime instances with the appropriate calculations
-       applied, e.g. to get a DateTime instance for the first of next
-       month, you'd call now() + RelativeDateTime(months=+1,day=1).
-
-    """
-    years = 0
-    months = 0
-    days = 0
-    year = None
-    month = 0
-    day = 0
-    hours = 0
-    minutes = 0
-    seconds = 0
-    hour = None
-    minute = None
-    second = None
-    weekday = None
-
-    # cached hash value
-    _hash = None
-
-    # For Zope security:
-    __roles__ = None
-    __allow_access_to_unprotected_subobjects__ = 1
-
-    def __init__(self,
-                 years=0,months=0,days=0,
-                 hours=0,minutes=0,seconds=0,
-                 year=None,month=None,day=None,
-                 hour=None,minute=None,second=None,
-                 weekday=None,weeks=0):
-        
-        self.years = years
-        self.months = months
-        self.days = days + weeks*7
-        self.year = year
-        self.month = month
-        self.day = day
-        self.hours = hours
-        self.minutes = minutes
-        self.seconds = seconds
-        self.hour = hour
-        self.minute = minute
-        self.second = second
-        if weekday is not None:
-            #  Make sure we've got a 2-tuple
-            assert len(weekday) == 2
-            self.weekday = weekday
-
-    def __add__(self,other,
-                # Locals:
-                isinstance=isinstance):
-
-        if isinstance(other,RelativeDateTime):
-            # RelativeDateTime (self) + RelativeDateTime (other)
-
-            r = RelativeDateTime()
-            # date deltas
-            r.years = self.years + other.years
-            r.months = self.months + other.months
-            r.days = self.days + other.days
-            # absolute entries of other override those in self, if given
-            r.year = other.year or self.year
-            r.month = other.month or self.month
-            r.day = other.day or self.day
-            r.weekday = other.weekday or self.weekday
-            # time deltas
-            r.hours = self.hours + other.hours
-            r.minutes = self.minutes + other.minutes
-            r.seconds = self.seconds + other.seconds
-            # absolute entries of other override those in self, if given
-            r.hour = other.hour or self.hour
-            r.minute = other.minute or self.minute
-            r.second = other.second or self.second
-            return r
-
-        else:
-            raise TypeError,"can't add the two types"
-
-    def __radd__(self,other,
-                 # Locals:
-                 isinstance=isinstance,DateTimeType=DateTimeType,
-                 DateTime=DateTime,DateTimeDelta=DateTimeDelta):
-
-        if isinstance(other,DateTimeType):
-            # DateTime (other) + RelativeDateTime (self)
-
-            # date
-            if self.year is None:
-                year = other.year + self.years
-            else:
-                year = self.year + self.years
-            if self.month is None:
-                month = other.month + self.months
-            else:
-                month = self.month + self.months
-            if self.day is None:
-                day = other.day
-            else:
-                day = self.day
-            if day < 0:
-                # fix negative day values
-                month = month + 1
-                day = day + 1
-            day = day + self.days
-            # time
-            if self.hour is None:
-                hour = other.hour + self.hours
-            else:
-                hour = self.hour + self.hours
-            if self.minute is None:
-                minute = other.minute + self.minutes
-            else:
-                minute = self.minute + self.minutes
-            if self.second is None:
-                second = other.second + self.seconds
-            else:
-                second = self.second + self.seconds
-
-            # Refit into proper ranges:
-            if month < 1 or month > 12:
-                month = month - 1
-                yeardelta, monthdelta = divmod(month, 12)
-                year = year + yeardelta
-                month = monthdelta + 1
-
-            # Make sure we have integers
-            year = int(year)
-            month = int(month)
-            day = int(day)
-
-            if self.weekday is None:
-                return DateTime(year, month, 1) + \
-                       DateTimeDelta(day-1,hour,minute,second)
-            
-            # Adjust to the correct weekday
-            day_of_week,index = self.weekday
-            d = DateTime(year, month, 1) + \
-                DateTimeDelta(day-1,hour,minute,second)
-            if index == 0:
-                # 0 index: next weekday if no match
-                return d + (day_of_week - d.day_of_week)
-            elif index > 0:
-                # positive index (1 == first weekday of month)
-                first = d - (d.day - 1)
-                diff = day_of_week - first.day_of_week
-                if diff >= 0:
-                    return first + (diff + (index-1) * 7)
-                else:
-                    return first + (diff + index * 7)
-            else:
-                # negative index (-1 == last weekday of month)
-                last = d + (d.days_in_month - d.day)
-                diff = day_of_week - last.day_of_week
-                if diff <= 0:
-                    return last + (diff + (index+1) * 7)
-                else:
-                    return last + (diff + index * 7)
-            
-        else:
-            raise TypeError,"can't add the two types"
-
-    def __sub__(self,other):
-
-        if isinstance(other,RelativeDateTime):
-            # RelativeDateTime (self) - RelativeDateTime (other)
-
-            r = RelativeDateTime()
-            # date deltas
-            r.years = self.years - other.years
-            r.months = self.months - other.months
-            r.days = self.days - other.days
-            # absolute entries of other override those in self, if given
-            r.year = other.year or self.year
-            r.month = other.month or self.month
-            r.day = other.day or self.day
-            r.weekday = other.weekday or self.weekday
-            # time deltas
-            r.hours = self.hours - other.hours
-            r.minutes = self.minutes - other.minutes
-            r.seconds = self.seconds - other.seconds
-            # absolute entries of other override those in self, if given
-            r.hour = other.hour or self.hour
-            r.minute = other.minute or self.minute
-            r.second = other.second or self.second
-
-            return r
-
-        else:
-            raise TypeError,"can't subtract the two types"
-
-    def __rsub__(self,other,
-                 # Locals:
-                 isinstance=isinstance,DateTimeType=DateTimeType):
-
-        if isinstance(other,DateTimeType):
-            # DateTime (other) - RelativeDateTime (self)
-            return other + self.__neg__()
-
-        else:
-            raise TypeError,"can't subtract the two types"
-
-    def __neg__(self):
-
-        # - RelativeDateTime(self)
-
-        r = RelativeDateTime()
-        # negate date deltas
-        r.years = - self.years
-        r.months = - self.months
-        r.days = - self.days
-        # absolute entries don't change
-        r.year = self.year
-        r.month = self.month
-        r.day = self.day
-        r.weekday = self.weekday
-        # negate time deltas
-        r.hours = - self.hours
-        r.minutes = - self.minutes
-        r.seconds = - self.seconds
-        # absolute entries don't change
-        r.hour = self.hour
-        r.minute = self.minute
-        r.second = self.second
-
-        return r
-
-    def __nonzero__(self):
-
-        # RelativeDateTime instances are considered false in case
-        # they do not define any alterations
-        if (self.year is None and
-            self.years == 0 and
-            self.month is None and
-            self.months == 0 and
-            self.day is None and
-            self.weekday is None and
-            self.days == 0 and
-            self.hour is None and
-            self.hours == 0 and
-            self.minute is None and
-            self.minutes == 0 and
-            self.second is None and
-            self.seconds == 0):
-            return 0
-        else:
-            return 1
-
-    def __mul__(self,other):
-
-        # RelativeDateTime (self) * Number (other)
-        factor = float(other)
-
-        r = RelativeDateTime()
-        # date deltas
-        r.years = factor * self.years
-        r.months = factor * self.months
-        r.days = factor * self.days
-        # time deltas
-        r.hours = factor * self.hours
-        r.minutes = factor * self.minutes
-        r.seconds = factor * self.seconds
-        return r
-
-    __rmul__ = __mul__
-
-    def __div__(self,other):
-
-        # RelativeDateTime (self) / Number (other)
-        return self.__mul__(1/float(other))
-
-    def __eq__(self, other):
-
-        if isinstance(self, RelativeDateTime) and \
-           isinstance(other, RelativeDateTime):
-            # RelativeDateTime (self) == RelativeDateTime (other)
-            if (self.years == other.years and
-                self.months == other.months and
-                self.days == other.days and
-                self.year == other.year and
-                self.day == other.day and
-                self.hours == other.hours and
-                self.minutes == other.minutes and
-                self.seconds == other.seconds and
-                self.hour == other.hour and
-                self.minute == other.minute and
-                self.second == other.second and
-                self.weekday == other.weekday):
-                return 1
-            else:
-                return 0
-        else:
-            raise TypeError,"can't compare the two types"
-
-    def __hash__(self):
-
-        if self._hash is not None:
-            return self._hash
-        x = 1234
-        for value in (self.years, self.months, self.days,
-                      self.year, self.day,
-                      self.hours, self.minutes, self.seconds,
-                      self.hour, self.minute, self.second,
-                      self.weekday):
-            if value is None:
-                x = 135051820 ^ x
-            else:
-                x = hash(value) ^ x
-        self._hash = x
-        return x
-
-    def __str__(self,
-
-                join=_string.join):
-
-        l = []
-        append = l.append
-
-        # Format date part
-        if self.year is not None:
-            append('%04i-' % self.year)
-        elif self.years:
-            append('(%0+5i)-' % self.years)
-        else:
-            append('YYYY-')
-        if self.month is not None:
-            append('%02i-' % self.month)
-        elif self.months:
-            append('(%0+3i)-' % self.months)
-        else:
-            append('MM-')
-        if self.day is not None:
-            append('%02i' % self.day)
-        elif self.days:
-            append('(%0+3i)' % self.days)
-        else:
-            append('DD')
-        if self.weekday:
-            append(' %s:%i' % (Weekday[self.weekday[0]][:3],self.weekday[1]))
-        append(' ')
-        
-        # Normalize relative time values to avoid fractions
-        hours = self.hours
-        minutes = self.minutes
-        seconds = self.seconds
-        hours_fraction = hours - int(hours)
-        minutes = minutes + hours_fraction * 60.0
-        minutes_fraction = minutes - int(minutes)
-        seconds = seconds + minutes_fraction * 6.0
-        seconds_fraction = seconds - int(seconds)
-
-        if 0:
-            # Normalize to standard time ranges
-            if seconds > 60.0:
-                extra_minutes, seconds = divmod(seconds, 60.0)
-                minutes = minutes + extra_minutes
-            elif seconds < -60.0:
-                extra_minutes, seconds = divmod(seconds, -60.0)
-                minutes = minutes - extra_minutes
-            if minutes >= 60.0:
-                extra_hours, minutes = divmod(minutes, 60.0)
-                hours = hours + extra_hours
-            elif minutes <= -60.0:
-                extra_hours, minutes = divmod(minutes, -60.0)
-                hours = hours - extra_hours
-
-        # Format time part
-        if self.hour is not None:
-            append('%02i:' % self.hour)
-        elif hours:
-            append('(%0+3i):' % hours)
-        else:
-            append('HH:')
-        if self.minute is not None:
-            append('%02i:' % self.minute)
-        elif minutes:
-            append('(%0+3i):' % minutes)
-        else:
-            append('MM:')
-        if self.second is not None:
-            append('%02i' % self.second)
-        elif seconds:
-            append('(%0+3i)' % seconds)
-        else:
-            append('SS')
-            
-        return join(l,'')
-
-    def __repr__(self):
-
-        return "<%s instance for '%s' at 0x%x>" % ( 
-            self.__class__.__name__, 
-            self.__str__(), 
-            id(self))
-
-# Alias
-RelativeDate = RelativeDateTime
-
-def RelativeDateTimeFrom(*args, **kws):
-
-    """ RelativeDateTimeFrom(*args, **kws)
-
-        Generic RelativeDateTime instance constructor. Can handle
-        parsing strings and keywords.
-
-    """
-    if len(args) == 1:
-        # Single argument
-        arg = args[0]
-        if _isstring(arg):
-            import Parser
-            return apply(Parser.RelativeDateTimeFromString, args, kws)
-        elif isinstance(arg, RelativeDateTime):
-            return arg
-        else:
-            raise TypeError,\
-                  'cannot convert argument to RelativeDateTime'
-
-    else:
-        return apply(RelativeDateTime,args,kws)
-
-def RelativeDateTimeDiff(date1,date2,
-
-                         floor=_math.floor,int=int,divmod=divmod,
-                         RelativeDateTime=RelativeDateTime):
-
-    """ RelativeDateTimeDiff(date1,date2)
-
-        Returns a RelativeDateTime instance representing the difference
-        between date1 and date2 in relative terms.
-
-        The following should hold: 
-        
-        date2 + RelativeDateDiff(date1,date2) == date1 
-
-        for all dates date1 and date2.
-
-        Note that due to the algorithm used by this function, not the
-        whole range of DateTime instances is supported; there could
-        also be a loss of precision.
-
-        XXX There are still some problems left (thanks to Carel
-        Fellinger for pointing these out):
-
-        29 1 1901 ->  1 3 1901 = 1 month
-        29 1 1901 ->  1 3 1900 = -10 month and -28 days, but
-        29 1 1901 -> 28 2 1900 = -11 month and -1 day
-
-        and even worse:
-
-        >>> print RelativeDateDiff(Date(1900,3,1),Date(1901,2,1))
-        YYYY-(-11)-DD HH:MM:SS
-
-        with:
-
-        >>> print Date(1901,1,29) + RelativeDateTime(months=-11)
-        1900-03-01 00:00:00.00
-        >>> print Date(1901,2,1) + RelativeDateTime(months=-11)
-        1900-03-01 00:00:00.00
-
-    """
-    diff = date1 - date2
-    if diff.days == 0:
-        return RelativeDateTime()
-    date1months = date1.year * 12 + (date1.month - 1)
-    date2months = date2.year * 12 + (date2.month - 1)
-    #print 'months',date1months,date2months
-
-    # Calculate the months difference
-    diffmonths = date1months - date2months
-    #print 'diffmonths',diffmonths
-    if diff.days > 0:
-        years,months = divmod(diffmonths,12)
-    else:
-        years,months = divmod(diffmonths,-12)
-        years = -years
-    date3 = date2 + RelativeDateTime(years=years,months=months)
-    diff3 = date1 - date3
-    days = date1.absdays - date3.absdays
-    #print 'date3',date3,'diff3',diff3,'days',days
-
-    # Correction to ensure that all relative parts have the same sign
-    while days * diff.days < 0:
-        if diff.days > 0:
-            diffmonths = diffmonths - 1
-            years,months = divmod(diffmonths,12)
-        else:
-            diffmonths = diffmonths + 1
-            years,months = divmod(diffmonths,-12)
-            years = -years
-        #print 'diffmonths',diffmonths
-        date3 = date2 + RelativeDateTime(years=years,months=months)
-        diff3 = date1 - date3
-        days = date1.absdays - date3.absdays
-        #print 'date3',date3,'diff3',diff3,'days',days
-
-    # Drop the fraction part of days
-    if days > 0:
-        days = int(floor(days))
-    else:
-        days = int(-floor(-days))
-
-    return RelativeDateTime(years=years,
-                            months=months,
-                            days=days,
-                            hours=diff3.hour,
-                            minutes=diff3.minute,
-                            seconds=diff3.second)
-
-# Aliases
-RelativeDateDiff = RelativeDateTimeDiff
-Age = RelativeDateTimeDiff
-
-###
-
-_current_year = now().year
-_current_century, _current_year_in_century = divmod(_current_year, 100)
-_current_century = _current_century * 100
-
-def add_century(year,
-
-                current_year=_current_year,
-                current_century=_current_century):
-    
-    """ Sliding window approach to the Y2K problem: adds a suitable
-        century to the given year and returns it as integer.
-
-        The window used depends on the current year (at import time).
-        If adding the current century to the given year gives a year
-        within the range current_year-70...current_year+30 [both
-        inclusive], then the current century is added. Otherwise the
-        century (current + 1 or - 1) producing the least difference is
-        chosen.
-
-    """
-    if year > 99:
-        # Take it as-is
-        return year
-    year = year + current_century
-    diff = year - current_year
-    if diff >= -70 and diff <= 30:
-        return year
-    elif diff < -70:
-        return year + 100
-    else:
-        return year - 100
-
-# Reference formulas for JDN taken from the Calendar FAQ:
-
-def gregorian_jdn(year,month,day):
-
-    # XXX These require proper integer division.
-    a = (14-month)/12
-    y = year+4800-a
-    m = month + 12*a - 3
-    return day + (306*m+5)/10 + y*365 + y/4 - y/100 + y/400 - 32045
-
-def julian_jdn(year,month,day):
-
-    # XXX These require proper integer division.
-    a = (14-month)/12
-    y = year+4800-a
-    m = month + 12*a - 3
-    return day + (306*m+5)/10 + y*365 + y/4 - 32083
--- a/embedded/mx/DateTime/ISO.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,366 +0,0 @@
-""" This module provides a set of constructors and routines to convert
-    between DateTime[Delta] instances and ISO representations of date
-    and time.
-
-    Note: Timezones are only interpreted by ParseDateTimeGMT(). All
-    other constructors silently ignore the time zone information.
-
-    Copyright (c) 1998-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
-    Copyright (c) 2000-2007, eGenix.com Software GmbH; mailto:info@egenix.com
-    See the documentation for further information on copyrights,
-    or contact the author.
-
-"""
-import DateTime,Timezone
-import re,string
-
-# Grammar: ISO 8601 (not all, but what we need from it)
-_year = '(?P<year>\d?\d\d\d)'
-_month = '(?P<month>\d?\d)'
-_day = '(?P<day>\d?\d)'
-_hour = '(?P<hour>\d?\d)'
-_minute = '(?P<minute>\d?\d)'
-_second = '(?P<second>\d?\d(?:\.\d+)?)'
-_sign = '(?P<sign>[-+])'
-_week = 'W(?P<week>\d?\d)'
-_zone = Timezone.isozone
-
-_weekdate = _year + '-?(?:' + _week + '-?' + _day + '?)?'
-_date = _year + '-?' + '(?:' + _month + '-?' + _day + '?)?'
-_time = _hour + ':?' + _minute + ':?' + _second + '?(?:' + _zone + ')?'
-
-isodatetimeRE = re.compile(_date + '(?:[ T]' + _time + ')?$')
-isodateRE = re.compile(_date + '$')
-isotimeRE = re.compile(_time + '$')
-isodeltaRE = re.compile(_sign + '?' + _time + '$')
-isoweekRE = re.compile(_weekdate + '$')
-isoweektimeRE = re.compile(_weekdate + '(?:[ T]' + _time + ')?$')
-
-def WeekTime(year,isoweek=1,isoday=1,hour=0,minute=0,second=0.0):
-
-    """Week(year,isoweek=1,isoday=1,hour=0,minute=0,second=0.0)
-
-       Returns a DateTime instance pointing to the given ISO week and
-       day.  isoday defaults to 1, which corresponds to Monday in the
-       ISO numbering. The time part is set as given.
-
-    """
-    d = DateTime.DateTime(year,1,1,hour,minute,second)
-    if d.iso_week[0] == year:
-        # 1.1. belongs to year (backup to Monday)
-        return d + (-d.day_of_week + 7 * (isoweek-1) + isoday-1)
-    else:
-        # 1.1. belongs to year-1 (advance to next Monday)
-        return d + (7-d.day_of_week + 7 * (isoweek-1) + isoday-1)
-
-# Alias
-Week = WeekTime
-
-# Aliases for the other constructors (they all happen to already use
-# ISO format)
-Date = DateTime.Date
-Time = DateTime.Time
-TimeDelta = DateTime.TimeDelta
-
-def ParseDateTime(isostring,parse_isodatetime=isodatetimeRE.match,
-
-                  strip=string.strip,atoi=string.atoi,atof=string.atof):
-
-    """ParseDateTime(isostring)
-
-       Returns a DateTime instance reflecting the given ISO date. A
-       time part is optional and must be delimited from the date by a
-       space or 'T'.
-
-       Time zone information is parsed, but not evaluated.
-
-    """
-    s = strip(isostring)
-    date = parse_isodatetime(s)
-    if not date:
-        raise ValueError,'wrong format, use YYYY-MM-DD HH:MM:SS'
-    year,month,day,hour,minute,second,zone = date.groups()
-    year = atoi(year)
-    if month is None:
-        month = 1
-    else:
-        month = atoi(month)
-    if day is None:
-        day = 1
-    else:
-        day = atoi(day)
-    if hour is None:
-        hour = 0
-    else:
-        hour = atoi(hour)
-    if minute is None:
-        minute = 0
-    else:
-        minute = atoi(minute)
-    if second is None:
-        second = 0.0
-    else:
-        second = atof(second)
-    return DateTime.DateTime(year,month,day,hour,minute,second)
-
-def ParseDateTimeGMT(isostring,parse_isodatetime=isodatetimeRE.match,
-
-                     strip=string.strip,atoi=string.atoi,atof=string.atof):
-
-    """ParseDateTimeGMT(isostring)
-
-       Returns a DateTime instance in UTC reflecting the given ISO
-       date. A time part is optional and must be delimited from the
-       date by a space or 'T'. Timezones are honored.
-
-    """
-    s = strip(isostring)
-    date = parse_isodatetime(s)
-    if not date:
-        raise ValueError,'wrong format, use YYYY-MM-DD HH:MM:SS'
-    year,month,day,hour,minute,second,zone = date.groups()
-    year = atoi(year)
-    if month is None:
-        month = 1
-    else:
-        month = atoi(month)
-    if day is None:
-        day = 1
-    else:
-        day = atoi(day)
-    if hour is None:
-        hour = 0
-    else:
-        hour = atoi(hour)
-    if minute is None:
-        minute = 0
-    else:
-        minute = atoi(minute)
-    if second is None:
-        second = 0.0
-    else:
-        second = atof(second)
-    offset = Timezone.utc_offset(zone)
-    return DateTime.DateTime(year,month,day,hour,minute,second) - offset
-
-# Alias
-ParseDateTimeUTC = ParseDateTimeGMT
-
-def ParseDate(isostring,parse_isodate=isodateRE.match,
-
-              strip=string.strip,atoi=string.atoi,atof=string.atof):
-
-    """ParseDate(isostring)
-
-       Returns a DateTime instance reflecting the given ISO date. A
-       time part may not be included.
-
-    """
-    s = strip(isostring)
-    date = parse_isodate(s)
-    if not date:
-        raise ValueError,'wrong format, use YYYY-MM-DD'
-    year,month,day = date.groups()
-    year = atoi(year)
-    if month is None:
-        month = 1
-    else:
-        month = atoi(month)
-    if day is None:
-        day = 1
-    else:
-        day = atoi(day)
-    return DateTime.DateTime(year,month,day)
-
-def ParseWeek(isostring,parse_isoweek=isoweekRE.match,
-
-              strip=string.strip,atoi=string.atoi,atof=string.atof):
-
-    """ParseWeek(isostring)
-
-       Returns a DateTime instance reflecting the given ISO date. A
-       time part may not be included.
-
-    """
-    s = strip(isostring)
-    date = parse_isoweek(s)
-    if not date:
-        raise ValueError,'wrong format, use yyyy-Www-d, e.g. 1998-W01-1'
-    year,week,day = date.groups()
-    year = atoi(year)
-    if week is None:
-        week = 1
-    else:
-        week = atoi(week)
-    if day is None:
-        day = 1
-    else:
-        day = atoi(day)
-    return Week(year,week,day)
-
-def ParseWeekTime(isostring,parse_isoweektime=isoweektimeRE.match,
-
-                  strip=string.strip,atoi=string.atoi,atof=string.atof):
-
-    """ParseWeekTime(isostring)
-
-       Returns a DateTime instance reflecting the given ISO date. A
-       time part is optional and must be delimited from the date by a
-       space or 'T'.
-
-    """
-    s = strip(isostring)
-    date = parse_isoweektime(s)
-    if not date:
-        raise ValueError,'wrong format, use e.g. "1998-W01-1 12:00:30"'
-    year,week,day,hour,minute,second,zone = date.groups()
-    year = atoi(year)
-    if week is None:
-        week = 1
-    else:
-        week = atoi(week)
-    if day is None:
-        day = 1
-    else:
-        day = atoi(day)
-    if hour is None:
-        hour = 0
-    else:
-        hour = atoi(hour)
-    if minute is None:
-        minute = 0
-    else:
-        minute = atoi(minute)
-    if second is None:
-        second = 0.0
-    else:
-        second = atof(second)
-    return WeekTime(year,week,day,hour,minute,second)
-
-def ParseTime(isostring,parse_isotime=isotimeRE.match,
-
-              strip=string.strip,atoi=string.atoi,atof=string.atof):
-
-    """ParseTime(isostring)
-
-       Returns a DateTimeDelta instance reflecting the given ISO time.
-       Hours and minutes must be given, seconds are
-       optional. Fractions of a second may also be used,
-       e.g. 12:23:12.34.
-
-    """
-    s = strip(isostring)
-    time = parse_isotime(s)
-    if not time:
-        raise ValueError,'wrong format, use HH:MM:SS'
-    hour,minute,second,zone = time.groups()
-    hour = atoi(hour)
-    minute = atoi(minute)
-    if second is not None:
-        second = atof(second)
-    else:
-        second = 0.0
-    return DateTime.TimeDelta(hour,minute,second)
-
-def ParseTimeDelta(isostring,parse_isodelta=isodeltaRE.match,
-
-                   strip=string.strip,atoi=string.atoi,atof=string.atof):
-
-    """ParseTimeDelta(isostring)
-
-       Returns a DateTimeDelta instance reflecting the given ISO time
-       as delta. Hours and minutes must be given, seconds are
-       optional. Fractions of a second may also be used,
-       e.g. 12:23:12.34. In addition to the ISO standard a sign may be
-       prepended to the time, e.g. -12:34.
-
-    """
-    s = strip(isostring)
-    time = parse_isodelta(s)
-    if not time:
-        raise ValueError,'wrong format, use [-]HH:MM:SS'
-    sign,hour,minute,second,zone = time.groups()
-    hour = atoi(hour)
-    minute = atoi(minute)
-    if second is not None:
-        second = atof(second)
-    else:
-        second = 0.0
-    if sign and sign == '-':
-        return -DateTime.TimeDelta(hour,minute,second)
-    else:
-        return DateTime.TimeDelta(hour,minute,second)
-
-def ParseAny(isostring):
-
-    """ParseAny(isostring)
-
-       Parses the given string and tries to convert it to a
-       DateTime[Delta] instance.
-
-    """
-    try:
-        return ParseDateTime(isostring)
-    except ValueError:
-        pass
-    try:
-        return ParseWeekTime(isostring)
-    except ValueError:
-        pass
-    try:
-        return ParseTimeDelta(isostring)
-    except ValueError:
-        raise ValueError,'unsupported format: "%s"' % isostring
-
-def str(datetime,tz=None):
-
-    """str(datetime,tz=DateTime.tz_offset(datetime))
-
-       Returns the datetime instance as ISO date string. tz can be
-       given as DateTimeDelta instance providing the time zone
-       difference from datetime's zone to UTC. It defaults to
-       DateTime.tz_offset(datetime) which assumes local time.
-
-    """
-    if tz is None:
-        tz = datetime.gmtoffset()
-    return '%04i-%02i-%02i %02i:%02i:%02i%+03i%02i' % (
-        datetime.year, datetime.month, datetime.day, 
-        datetime.hour, datetime.minute, datetime.second,
-        tz.hour,tz.minute)
-
-def strGMT(datetime):
-
-    """strGMT(datetime)
-
-       Returns the datetime instance as ISO date string assuming it is
-       given in GMT.
-
-    """
-    return '%04i-%02i-%02i %02i:%02i:%02i+0000' % (
-        datetime.year, datetime.month, datetime.day, 
-        datetime.hour, datetime.minute, datetime.second)
-
-def strUTC(datetime):
-
-    """strUTC(datetime)
-
-       Returns the datetime instance as ISO date string assuming it is
-       given in UTC.
-
-    """
-    return '%04i-%02i-%02i %02i:%02i:%02i+0000' % (
-        datetime.year, datetime.month, datetime.day, 
-        datetime.hour, datetime.minute, datetime.second)
-
-# Testing
-if __name__ == '__main__':
-    e = DateTime.Date(1900,1,1)
-    for i in range(100000):
-        d = e + i
-        year,week,day = d.iso_week
-        c = WeekTime(year,week,day)
-        if d != c:
-            print ' Check %s (given; %i) != %s (parsed)' % (d,d.day_of_week,c)
-        elif i % 1000 == 0:
-            print d,'ok'
--- a/embedded/mx/DateTime/Parser.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1225 +0,0 @@
-# -*- coding: latin-1 -*-
-
-""" Date/Time string parsing module.
-
-    Note about the Y2K problems:
-
-       The parser can only handle years with at least 2 digits. 2
-       digit year values get expanded by adding the century using
-       DateTime.add_century(), while 3 digit year get converted
-       literally. To have 2 digit years also be interpreted literally,
-       add leading zeros, e.g. year 99 must be written as 099 or 0099.
-
-    Copyright (c) 1998-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
-    Copyright (c) 2000-2007, eGenix.com Software GmbH; mailto:info@egenix.com
-    See the documentation for further information on copyrights,
-    or contact the author. All Rights Reserved.
-
-"""
-import types,re,string
-import DateTime,ISO,ARPA,Timezone
-
-# Enable to produce debugging output
-_debug = 0
-
-# REs for matching date and time parts in a string; These REs
-# parse a superset of ARPA, ISO, American and European style dates.
-# Timezones are supported via the Timezone submodule.
-
-_year = '(?P<year>-?\d+\d(?!:))'
-_fullyear = '(?P<year>-?\d+\d\d(?!:))'
-_year_epoch = '(?:' + _year + '(?P<epoch> *[ABCDE\.]+)?)'
-_fullyear_epoch = '(?:' + _fullyear + '(?P<epoch> *[ABCDE\.]+)?)'
-_relyear = '(?:\((?P<relyear>[-+]?\d+)\))'
-
-_month = '(?P<month>\d?\d(?!:))'
-_fullmonth = '(?P<month>\d\d(?!:))'
-_litmonth = ('(?P<litmonth>'
-             'jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec|'
-             'mär|mae|mrz|mai|okt|dez|'
-             'fev|avr|juin|juil|aou|aoû|déc|'
-             'ene|abr|ago|dic|'
-             'out'
-             ')[a-z,\.;]*')
-litmonthtable = {
-    # English
-    'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6,
-    'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12,
-    # German
-    'mär':3, 'mae':3, 'mrz':3, 'mai':5, 'okt':10, 'dez':12,
-    # French
-    'fev':2, 'avr':4, 'juin':6, 'juil':7, 'aou':8, 'aoû':8,
-    'déc':12,
-    # Spanish
-    'ene':1, 'abr':4, 'ago':8, 'dic':12,
-    # Portuguese
-    'out':10,
-    }
-_relmonth = '(?:\((?P<relmonth>[-+]?\d+)\))'
-
-_day = '(?P<day>\d?\d(?!:))'
-_usday = '(?P<day>\d?\d(?!:))(?:st|nd|rd|th|[,\.;])?'
-_fullday = '(?P<day>\d\d(?!:))'
-_litday = ('(?P<litday>'
-           'mon|tue|wed|thu|fri|sat|sun|'
-           'die|mit|don|fre|sam|son|'
-           'lun|mar|mer|jeu|ven|sam|dim|'
-           'mie|jue|vie|sab|dom|'
-           'pri|seg|ter|cua|qui'
-           ')[a-z]*')
-litdaytable = {
-    # English
-    'mon':0, 'tue':1, 'wed':2, 'thu':3, 'fri':4, 'sat':5, 'sun':6,
-    # German
-    'die':1, 'mit':2, 'don':3, 'fre':4, 'sam':5, 'son':6,
-    # French
-    'lun':0, 'mar':1, 'mer':2, 'jeu':3, 'ven':4, 'sam':5, 'dim':6,
-    # Spanish
-    'mie':2, 'jue':3, 'vie':4, 'sab':5, 'dom':6,
-    # Portuguese
-    'pri':0, 'seg':1, 'ter':2, 'cua':3, 'qui':4,
-    }
-_relday = '(?:\((?P<relday>[-+]?\d+)\))'
-
-_hour = '(?P<hour>[012]?\d)'
-_minute = '(?P<minute>[0-6]\d)'
-_second = '(?P<second>[0-6]\d(?:[.,]\d+)?)'
-
-_days = '(?P<days>\d*\d(?:[.,]\d+)?)'
-_hours = '(?P<hours>\d*\d(?:[.,]\d+)?)'
-_minutes = '(?P<minutes>\d*\d(?:[.,]\d+)?)'
-_seconds = '(?P<seconds>\d*\d(?:[.,]\d+)?)'
-
-_reldays = '(?:\((?P<reldays>[-+]?\d+(?:[.,]\d+)?)\))'
-_relhours = '(?:\((?P<relhours>[-+]?\d+(?:[.,]\d+)?)\))'
-_relminutes = '(?:\((?P<relminutes>[-+]?\d+(?:[.,]\d+)?)\))'
-_relseconds = '(?:\((?P<relseconds>[-+]?\d+(?:[.,]\d+)?)\))'
-
-_sign = '(?:(?P<sign>[-+]) *)'
-_week = 'W(?P<week>\d?\d)'
-_zone = Timezone.zone
-_ampm = '(?P<ampm>[ap][m.]+)'
-
-_time = (_hour + ':' + _minute + '(?::' + _second + '|[^:]|$) *'
-         + _ampm + '? *' + _zone + '?')
-_isotime = _hour + ':?' + _minute + ':?' + _second + '? *' + _zone + '?'
-
-_weekdate = _year + '-?(?:' + _week + '-?' + _day + '?)?'
-_eurodate = _day + '\.' + _month + '\.' + _year_epoch + '?'
-_usdate = _month + '/' + _day + '(?:/' + _year_epoch + '|[^/]|$)'
-_altusdate = _month + '-' + _day + '-' + _fullyear_epoch
-_isodate = _year + '-' + _month + '-?' + _day + '?(?!:)'
-_altisodate = _year + _fullmonth + _fullday + '(?!:)'
-_usisodate = _fullyear + '/' + _fullmonth + '/' + _fullday
-_litdate = ('(?:'+ _litday + ',? )? *' + 
-            _usday + ' *' + 
-            '[- ] *(?:' + _litmonth + '|'+ _month +') *[- ] *' +
-            _year_epoch + '?')
-_altlitdate = ('(?:'+ _litday + ',? )? *' + 
-               _litmonth + '[ ,.a-z]+' +
-               _usday + 
-               '(?:[ a-z]+' + _year_epoch + ')?')
-_eurlitdate = ('(?:'+ _litday + ',?[ a-z]+)? *' + 
-               '(?:'+ _usday + '[ a-z]+)? *' +
-               _litmonth + 
-               '(?:[ ,.a-z]+' + _year_epoch + ')?')
-
-_relany = '[*%?a-zA-Z]+'
-
-_relisodate = ('(?:(?:' + _relany + '|' + _year + '|' + _relyear + ')-' +
-               '(?:' + _relany + '|' + _month + '|' + _relmonth + ')-' +
-               '(?:' + _relany + '|' + _day + '|' + _relday + '))')
-
-_asctime = ('(?:'+ _litday + ',? )? *' + 
-                _usday + ' *' + 
-                '[- ] *(?:' + _litmonth + '|'+ _month +') *[- ]' +
-                '(?:[0-9: ]+)' + 
-                _year_epoch + '?')
-
-_relisotime = ('(?:(?:' + _relany + '|' + _hour + '|' + _relhours + '):' +
-               '(?:' + _relany + '|' + _minute + '|' + _relminutes + ')' +
-               '(?::(?:' + _relany + '|' + _second + '|' + _relseconds + '))?)')
-
-_isodelta1 = (_sign + '?' +
-              _days + ':' + _hours + ':' + _minutes + ':' + _seconds)
-_isodelta2 = (_sign + '?' + 
-              _hours + ':' + _minutes + ':' + _seconds)
-_isodelta3 = (_sign + '?' + 
-              _hours + ':' + _minutes)
-_litdelta = (_sign + '?' +
-             '(?:' + _days + ' *d[a-z]*[,; ]*)?' + 
-             '(?:' + _hours + ' *h[a-z]*[,; ]*)?' + 
-             '(?:' + _minutes + ' *m[a-z]*[,; ]*)?' +
-             '(?:' + _seconds + ' *s[a-z]*[,; ]*)?')
-_litdelta2 = (_sign + '?' +
-             '(?:' + _days + ' *d[a-z]*[,; ]*)?' + 
-              _hours + ':' + _minutes + '(?::' + _seconds + ')?')
-
-_timeRE = re.compile(_time, re.I)
-_isotimeRE = re.compile(_isotime, re.I)
-_isodateRE = re.compile(_isodate, re.I)
-_altisodateRE = re.compile(_altisodate, re.I)
-_usisodateRE = re.compile(_usisodate, re.I)
-_eurodateRE = re.compile(_eurodate, re.I)
-_usdateRE = re.compile(_usdate, re.I)
-_altusdateRE = re.compile(_altusdate, re.I)
-_litdateRE = re.compile(_litdate, re.I)
-_altlitdateRE = re.compile(_altlitdate, re.I)
-_eurlitdateRE = re.compile(_eurlitdate, re.I)
-_relisodateRE = re.compile(_relisodate, re.I)
-_asctimeRE = re.compile(_asctime, re.I)
-_isodelta1RE = re.compile(_isodelta1)
-_isodelta2RE = re.compile(_isodelta2)
-_isodelta3RE = re.compile(_isodelta3)
-_litdeltaRE = re.compile(_litdelta)
-_litdelta2RE = re.compile(_litdelta2)
-_relisotimeRE = re.compile(_relisotime, re.I)
-
-# Available date parsers
-_date_formats = ('euro',
-                 'usiso', 'us', 'altus',
-                 'iso', 'altiso', 
-                 'lit', 'altlit', 'eurlit',
-                 'unknown')
-
-# Available time parsers
-_time_formats = ('standard',
-                 'iso',
-                 'unknown')
-
-def _parse_date(text, formats=_date_formats, defaultdate=None,
-
-                int=int,float=float,lower=string.lower,
-                add_century=DateTime.add_century,
-                now=DateTime.now,us_formats=('us', 'altus'),
-                iso_formats=('iso', 'altiso', 'usiso')):
-
-    """ Parses the date part given in text and returns a tuple
-        (text,day,month,year,style) with the following
-        meanings:
-
-        * text gives the original text without the date part
-
-        * day,month,year give the parsed date
-
-        * style gives information about which parser was successful:
-          'euro' - the European date parser
-          'us' - the US date parser
-          'altus' - the alternative US date parser (with '-' instead of '/')
-          'iso' - the ISO date parser
-          'altiso' - the alternative ISO date parser (without '-')
-          'usiso' - US style ISO date parser (yyyy/mm/dd)
-          'lit' - the US literal date parser
-          'altlit' - the alternative US literal date parser
-          'eurlit' - the Eurpean literal date parser
-          'unknown' - no date part was found, defaultdate was used
-
-        formats may be set to a tuple of style strings specifying
-        which of the above parsers to use and in which order to try
-        them. Default is to try all of them in the above order.
-
-        defaultdate provides the defaults to use in case no date part
-        is found. Most other parsers default to the current year
-        January 1 if some of these date parts are missing.
-
-        If 'unknown' is not given in formats and the date cannot be
-        parsed, a ValueError is raised.
-
-    """
-    match = None
-    style = ''
-    
-    # Apply parsers in the order given in formats
-    for format in formats:
-
-        if format == 'euro':
-            # European style date
-            match = _eurodateRE.search(text)
-            if match is not None:
-                day,month,year,epoch = match.groups()
-                if year:
-                    if len(year) == 2:
-                        # Y2K problem:
-                        year = add_century(int(year))
-                    else:
-                        year = int(year)
-                else:
-                    if defaultdate is None:
-                        defaultdate = now()
-                    year = defaultdate.year
-                if epoch and 'B' in epoch:
-                    year = -year + 1
-                month = int(month)
-                day = int(day)
-                # Could have mistaken euro format for us style date
-                # which uses month, day order
-                if month > 12 or month == 0:
-                    match = None
-                    continue
-                break
-
-        elif format in iso_formats:
-            # ISO style date
-            if format == 'iso':
-                match = _isodateRE.search(text)
-            elif format == 'altiso':
-                match = _altisodateRE.search(text)
-                # Avoid mistaking ISO time parts ('Thhmmss') for dates
-                if match is not None:
-                    left, right = match.span()
-                    if left > 0 and \
-                       text[left - 1:left] == 'T':
-                        match = None
-                        continue
-            else:
-                match = _usisodateRE.search(text)
-            if match is not None:
-                year,month,day = match.groups()
-                if len(year) == 2:
-                    # Y2K problem:
-                    year = add_century(int(year))
-                else:
-                    year = int(year)
-                # Default to January 1st
-                if not month:
-                    month = 1
-                else:
-                    month = int(month)
-                if not day:
-                    day = 1
-                else:
-                    day = int(day)
-                break
-
-        elif format in us_formats:
-            # US style date
-            if format == 'us':
-                match = _usdateRE.search(text)
-            else:
-                match = _altusdateRE.search(text)
-            if match is not None:
-                month,day,year,epoch = match.groups()
-                if year:
-                    if len(year) == 2:
-                        # Y2K problem:
-                        year = add_century(int(year))
-                    else:
-                        year = int(year)
-                else:
-                    if defaultdate is None:
-                        defaultdate = now()
-                    year = defaultdate.year
-                if epoch and 'B' in epoch:
-                    year = -year + 1
-                # Default to 1 if no day is given
-                if day:
-                    day = int(day)
-                else:
-                    day = 1
-                month = int(month)
-                # Could have mistaken us format for euro style date
-                # which uses day, month order
-                if month > 12 or month == 0:
-                    match = None
-                    continue
-                break
-
-        elif format == 'lit':
-            # US style literal date
-            match = _litdateRE.search(text)
-            if match is not None:
-                litday,day,litmonth,month,year,epoch = match.groups()
-                break
-
-        elif format == 'altlit':
-            # Alternative US style literal date
-            match = _altlitdateRE.search(text)
-            if match is not None: 
-                litday,litmonth,day,year,epoch = match.groups()
-                month = '<missing>'
-                break
-
-        elif format == 'eurlit':
-            # European style literal date
-            match = _eurlitdateRE.search(text)
-            if match is not None: 
-                litday,day,litmonth,year,epoch = match.groups()
-                month = '<missing>'
-                break
-
-        elif format == 'unknown':
-            # No date part: use defaultdate
-            if defaultdate is None:
-                defaultdate = now()
-            year = defaultdate.year
-            month = defaultdate.month
-            day = defaultdate.day
-            style = format
-            break
-
-    # Check success
-    if match is not None:
-        # Remove date from text
-        left, right = match.span()
-        if 0 and _debug:
-            print 'parsed date:',repr(text[left:right]),\
-                  'giving:',year,month,day
-        text = text[:left] + text[right:]
-        style = format
-        
-    elif not style:
-        # Not recognized: raise an error
-        raise ValueError, 'unknown date format: "%s"' % text
-
-    # Literal date post-processing
-    if style in ('lit', 'altlit', 'eurlit'):
-        if 0 and _debug: print match.groups()
-        # Default to current year, January 1st
-        if not year:
-            if defaultdate is None:
-                defaultdate = now()
-            year = defaultdate.year
-        else:
-            if len(year) == 2:
-                # Y2K problem:
-                year = add_century(int(year))
-            else:
-                year = int(year)
-        if epoch and 'B' in epoch:
-            year = -year + 1
-        if litmonth:
-            litmonth = lower(litmonth)
-            try:
-                month = litmonthtable[litmonth]
-            except KeyError:
-                raise ValueError,\
-                      'wrong month name: "%s"' % litmonth
-        elif month:
-            month = int(month)
-        else:
-            month = 1
-        if day:
-            day = int(day)
-        else:
-            day = 1
-
-    #print '_parse_date:',text,day,month,year,style
-    return text,day,month,year,style
-
-def _parse_time(text, formats=_time_formats,
-
-                int=int,float=float,replace=string.replace):
-
-    """ Parses a time part given in text and returns a tuple
-        (text,hour,minute,second,offset,style) with the following
-        meanings:
-
-        * text gives the original text without the time part
-        * hour,minute,second give the parsed time
-        * offset gives the time zone UTC offset
-        * style gives information about which parser was successful:
-          'standard' - the standard parser
-          'iso' - the ISO time format parser
-          'unknown' - no time part was found
-
-        formats may be set to a tuple specifying the parsers to use:
-          'standard' - standard time format with ':' delimiter
-          'iso' - ISO time format (superset of 'standard')
-          'unknown' - default to 0:00:00, 0 zone offset
-
-        If 'unknown' is not given in formats and the time cannot be
-        parsed, a ValueError is raised.
-
-    """
-    match = None
-    style = ''
-
-    # Apply parsers in the order given in formats
-    for format in formats:
-
-        # Standard format
-        if format == 'standard':
-            match = _timeRE.search(text)
-            if match is not None:
-                hour,minute,second,ampm,zone = match.groups()
-                style = 'standard'
-                break
-
-        # ISO format
-        if format == 'iso':
-            match =  _isotimeRE.search(text)
-            if match is not None:
-                hour,minute,second,zone = match.groups()
-                ampm = None
-                style = 'iso'
-                break
-
-        # Default handling
-        elif format == 'unknown':
-            hour,minute,second,offset = 0,0,0.0,0
-            style = 'unknown'
-            break
-
-    if not style:
-        # If no default handling should be applied, raise an error
-        raise ValueError, 'unknown time format: "%s"' % text
-
-    # Post-processing
-    if match is not None:
-        if zone:
-            # Convert to UTC offset
-            offset = Timezone.utc_offset(zone)
-        else:
-            offset = 0
-        hour = int(hour)
-        if ampm:
-            if ampm[0] in ('p', 'P'):
-                # 12pm = midday
-                if hour < 12:
-                    hour = hour + 12
-            else:
-                # 12am = midnight 
-                if hour >= 12:
-                    hour = hour - 12
-        if minute:
-            minute = int(minute)
-        else:
-            minute = 0
-        if not second:
-            second = 0.0
-        else:
-            if ',' in second:
-                second = replace(second, ',', '.')
-            second = float(second)
-
-        # Remove time from text
-        left,right = match.span()
-        if 0 and _debug: 
-            print 'parsed time:',repr(text[left:right]),\
-                  'giving:',hour,minute,second,offset
-        text = text[:left] + text[right:]
-
-    #print '_parse_time:',text,hour,minute,second,offset,style
-    return text,hour,minute,second,offset,style
-
-###
-
-def DateTimeFromString(text, formats=_date_formats, defaultdate=None,
-                       time_formats=_time_formats,
-
-                       DateTime=DateTime):
-
-    """ DateTimeFromString(text, [formats, defaultdate])
-    
-        Returns a DateTime instance reflecting the date and time given
-        in text. In case a timezone is given, the returned instance
-        will point to the corresponding UTC time value. Otherwise, the
-        value is set as given in the string.
-
-        formats may be set to a tuple of strings specifying which of
-        the following parsers to use and in which order to try
-        them. Default is to try all of them in the order given below:
-
-          'euro' - the European date parser
-          'us' - the US date parser
-          'altus' - the alternative US date parser (with '-' instead of '/')
-          'iso' - the ISO date parser
-          'altiso' - the alternative ISO date parser (without '-')
-          'usiso' - US style ISO date parser (yyyy/mm/dd)
-          'lit' - the US literal date parser
-          'altlit' - the alternative US literal date parser
-          'eurlit' - the Eurpean literal date parser
-          'unknown' - if no date part is found, use defaultdate
-
-        defaultdate provides the defaults to use in case no date part
-        is found. Most of the parsers default to the current year
-        January 1 if some of these date parts are missing.
-
-        If 'unknown' is not given in formats and the date cannot
-        be parsed, a ValueError is raised.
-
-        time_formats may be set to a tuple of strings specifying which
-        of the following parsers to use and in which order to try
-        them. Default is to try all of them in the order given below:
-
-          'standard' - standard time format HH:MM:SS (with ':' delimiter)
-          'iso' - ISO time format (superset of 'standard')
-          'unknown' - default to 00:00:00 in case the time format
-                      cannot be parsed
-
-        Defaults to 00:00:00.00 for time parts that are not included
-        in the textual representation.
-
-        If 'unknown' is not given in time_formats and the time cannot
-        be parsed, a ValueError is raised.
-
-    """
-    origtext = text
-    formats = tuple(formats)
-
-    if formats is _date_formats or \
-       'iso' in formats or \
-       'altiso' in formats:
-
-        # First try standard order (parse time, then date)
-        if formats[0] not in ('iso', 'altiso'):
-            text,hour,minute,second,offset,timestyle = _parse_time(
-                origtext,
-                time_formats)
-            text,day,month,year,datestyle = _parse_date(
-                text,
-                formats + ('unknown',),
-                defaultdate)
-            if 0 and _debug:
-                print 'tried time/date on %s, date=%s, time=%s' % (origtext,
-                                                                   datestyle,
-                                                                   timestyle)
-        else:
-            timestyle = 'iso'
-            
-        # If this fails, try the ISO order (date, then time)
-        if timestyle in ('iso', 'unknown'):
-            text,day,month,year,datestyle = _parse_date(
-                origtext,
-                formats,
-                defaultdate)
-            text,hour,minute,second,offset,timestyle = _parse_time(
-                text,
-                time_formats)
-            if 0 and _debug:
-                print 'tried ISO on %s, date=%s, time=%s' % (origtext,
-                                                             datestyle,
-                                                             timestyle)
-    else:
-        # Standard order: time part, then date part
-        text,hour,minute,second,offset,timestyle = _parse_time(
-            origtext,
-            time_formats)
-        text,day,month,year,datestyle = _parse_date(
-            text,
-            formats,
-            defaultdate)
-
-    if (datestyle == 'unknown' and 'unknown' not in formats) or \
-       (timestyle == 'unknown' and 'unknown' not in time_formats):
-        raise ValueError,\
-              'Failed to parse "%s": found "%s" date, "%s" time' % \
-              (origtext, datestyle, timestyle)
-    
-    try:
-        return DateTime.DateTime(year,month,day,hour,minute,second) - offset
-    except DateTime.RangeError, why:
-        raise DateTime.RangeError,\
-              'Failed to parse "%s": %s' % (origtext, why)
-
-def DateFromString(text, formats=_date_formats, defaultdate=None,
-
-                   DateTime=DateTime):
-
-    """ DateFromString(text, [formats, defaultdate])
-    
-        Returns a DateTime instance reflecting the date given in
-        text. A possibly included time part is ignored.
-
-        formats and defaultdate work just like for
-        DateTimeFromString().
-
-    """
-    _text,day,month,year,datestyle = _parse_date(text, formats, defaultdate)
-
-    if datestyle == 'unknown' and \
-       'unknown' not in formats:
-        raise ValueError,\
-              'Failed to parse "%s": found "%s" date' % \
-              (origtext, datestyle)
-
-    try:
-        return DateTime.DateTime(year,month,day)
-    except DateTime.RangeError, why:
-        raise DateTime.RangeError,\
-              'Failed to parse "%s": %s' % (text, why)
-
-def validateDateTimeString(text, formats=_date_formats):
-
-    """ validateDateTimeString(text, [formats, defaultdate])
-
-        Validates the given text and returns 1/0 depending on whether
-        text includes parseable date and time values or not.
-
-        formats works just like for DateTimeFromString() and defines
-        the order of date/time parsers to apply. It defaults to the
-        same list of parsers as for DateTimeFromString().
-
-        XXX Undocumented !
-    
-    """
-    formats = list(formats)
-    if 'unknown' in formats:
-        formats.remove('unknown')
-    try:
-        DateTimeFromString(text, formats)
-    except (DateTime.RangeError, ValueError), why:
-        return 0
-    return 1
-
-def validateDateString(text, formats=_date_formats):
-
-    """ validateDateString(text, [formats, defaultdate])
-
-        Validates the given text and returns 1/0 depending on whether
-        text includes a parseable date value or not.
-
-        formats works just like for DateTimeFromString() and defines
-        the order of date/time parsers to apply. It defaults to the
-        same list of parsers as for DateTimeFromString().
-    
-        XXX Undocumented !
-    
-    """
-    formats = list(formats)
-    if 'unknown' in formats:
-        formats.remove('unknown')
-    try:
-        DateFromString(text, formats)
-    except (DateTime.RangeError, ValueError), why:
-        return 0
-    return 1
-
-def TimeFromString(text, formats=_time_formats,
-
-                   DateTime=DateTime):
-
-    """ TimeFromString(text, [formats])
-    
-        Returns a DateTimeDelta instance reflecting the time given in
-        text. A possibly included date part is ignored.
-
-        formats may be set to a tuple of strings specifying which of
-        the following parsers to use and in which order to try
-        them. Default is to try all of them in the order given below:
-
-          'standard' - standard time format with ':' delimiter
-          'iso' - ISO time format (superset of 'standard')
-          'unknown' - default to 00:00:00 in case the time format
-                      cannot be parsed
-
-        Defaults to 00:00:00.00 for parts that are not included in the
-        textual representation.
-        
-    """
-    _text,hour,minute,second,offset,timestyle = _parse_time(
-        text,
-        formats)
-
-    if timestyle == 'unknown' and \
-       'unknown' not in formats:
-        raise ValueError,\
-              'Failed to parse "%s": found "%s" time' % \
-              (text, timestyle)
-
-    try:
-        dtd = DateTime.DateTimeDelta(0.0, hour, minute, second)
-    except DateTime.RangeError, why:
-        raise DateTime.RangeError,\
-              'Failed to parse "%s": %s' % (text, why)
-    else:
-        # XXX What to do with offset ?
-        return dtd
-
-#
-# XXX Still missing: validateTimeString(), validateDateTimeDeltaString()
-#                    and validateTimeDeltaString()
-#
-
-def DateTimeDeltaFromString(text,
-
-                            float=float,DateTime=DateTime):
-
-    """ DateTimeDeltaFromString(text)
-    
-        Returns a DateTimeDelta instance reflecting the delta given in
-        text. Defaults to 0:00:00:00.00 for parts that are not
-        included in the textual representation or cannot be parsed.
-
-    """
-    match = _isodelta1RE.search(text)
-    if match is not None:
-        sign, days, hours, minutes, seconds = match.groups()
-    else:
-        match = _litdelta2RE.search(text)
-        if match is not None:
-            sign, days, hours, minutes, seconds = match.groups()
-        else:
-            match = _isodelta2RE.search(text)
-            if match is not None:
-                sign, hours, minutes, seconds = match.groups()
-                days = None
-            else:
-                match = _isodelta3RE.search(text)
-                if match is not None:
-                    sign, hours, minutes = match.groups()
-                    days = None
-                    seconds = None
-                else:
-                    match = _litdeltaRE.search(text)
-                    if match is not None:
-                        sign, days, hours, minutes, seconds = match.groups()
-
-                    else:
-                        # Not matched:
-                        return DateTime.DateTimeDelta(0.0)
-
-    # Conversions
-    if days:
-        days = float(days)
-    else:
-        days = 0.0
-    if hours:
-        hours = float(hours)
-    else:
-        hours = 0.0
-    if minutes:
-        minutes = float(minutes)
-    else:
-        minutes = 0.0
-    if seconds:
-        seconds = float(seconds)
-    else:
-        seconds = 0.0
-    if sign != '-':
-        sign = 1
-    else:
-        sign = -1
-
-    try:
-        dtd = DateTime.DateTimeDelta(days,hours,minutes,seconds)
-    except DateTime.RangeError, why:
-        raise DateTime.RangeError,\
-              'Failed to parse "%s": %s' % (text, why)
-    else:
-        if sign < 0:
-            return -dtd
-        else:
-            return dtd
-
-# Aliases
-TimeDeltaFromString = DateTimeDeltaFromString
-
-###
-
-def _parse_reldate(text,
-
-                   int=int,float=float):
-
-    match = _relisodateRE.search(text)
-    if match is not None:
-        groups = match.groups()
-        if 0 and _debug: print groups
-        year,years,month,months,day,days = groups
-        if year:
-            year = int(year)
-        if years:
-            years = float(years)
-        else:
-            years = 0
-        if month:
-            month = int(month)
-        if months:
-            months = float(months)
-        else:
-            months = 0
-        if day:
-            day = int(day)
-        if days:
-            days = float(days)
-        else:
-            days = 0
-        return year,years,month,months,day,days
-    else:
-        return None,0,None,0,None,0
-
-def _parse_reltime(text,
-
-                   int=int,float=float):
-
-    match = _relisotimeRE.search(text)
-    if match is not None:
-        groups = match.groups()
-        if 0 and _debug: print groups
-        hour,hours,minute,minutes,second,seconds = groups
-        if hour:
-            hour = int(hour)
-        if hours:
-            hours = float(hours)
-        else:
-            hours = 0
-        if minute:
-            minute = int(minute)
-        if minutes:
-            minutes = float(minutes)
-        else:
-            minutes = 0
-        if second:
-            second = int(second)
-        if seconds:
-            seconds = float(seconds)
-        else:
-            seconds = 0
-        return hour,hours,minute,minutes,second,seconds
-    else:
-        return None,0,None,0,None,0
-
-def RelativeDateTimeFromString(text,
-
-                               RelativeDateTime=DateTime.RelativeDateTime):
-
-    """ RelativeDateTimeFromString(text)
-    
-        Returns a RelativeDateTime instance reflecting the relative
-        date and time given in text.
-
-        Defaults to wildcards for parts or values which are not
-        included in the textual representation or cannot be parsed.
-
-        The format used in text must adhere to the following syntax:
-
-                        [YYYY-MM-DD] [HH:MM[:SS]]
-
-        with the usual meanings. Values which should not be altered
-        may be replaced with '*', '%', '?' or any combination of
-        letters, e.g. 'YYYY'. Relative settings must be enclosed in
-        parenthesis if given and should include a sign, e.g. '(+0001)'
-        for the year part. All other settings are interpreted as
-        absolute values.
-
-        Date and time parts are both optional as a whole. Seconds in
-        the time part are optional too. Everything else (including the
-        hyphens and colons) is mandatory.
-
-    """
-    year,years,month,months,day,days = _parse_reldate(text)
-    hour,hours,minute,minutes,second,seconds = _parse_reltime(text)
-    return RelativeDateTime(year=year,years=years,
-                            month=month,months=months,
-                            day=day,days=days,
-                            hour=hour,hours=hours,
-                            minute=minute,minutes=minutes,
-                            second=second,seconds=seconds)
-
-def RelativeDateFromString(text,
-
-                           RelativeDateTime=DateTime.RelativeDateTime):
-
-    """ RelativeDateFromString(text)
-    
-        Same as RelativeDateTimeFromString(text) except that only the
-        date part of text is taken into account.
-
-    """
-    year,years,month,months,day,days = _parse_reldate(text)
-    return RelativeDateTime(year=year,years=years,
-                            month=month,months=months,
-                            day=day,days=days)
-
-def RelativeTimeFromString(text,
-
-                           RelativeDateTime=DateTime.RelativeDateTime):
-
-    """ RelativeTimeFromString(text)
-    
-        Same as RelativeDateTimeFromString(text) except that only the
-        time part of text is taken into account.
-
-    """
-    hour,hours,minute,minutes,second,seconds = _parse_reltime(text)
-    return RelativeDateTime(hour=hour,hours=hours,
-                            minute=minute,minutes=minutes,
-                            second=second,seconds=seconds)
-
-### Tests
-
-def _test():
-
-    import sys
-
-    t = DateTime.now()
-
-    print 'Testing DateTime Parser...'
-
-    l = [
-
-        # Literal formats
-        ('Sun Nov  6 08:49:37 1994', '1994-11-06 08:49:37.00'),
-        ('sun nov  6 08:49:37 1994', '1994-11-06 08:49:37.00'),
-        ('sUN NOV  6 08:49:37 1994', '1994-11-06 08:49:37.00'),
-        ('Sunday, 06-Nov-94 08:49:37 GMT', '1994-11-06 08:49:37.00'),
-        ('Sun, 06 Nov 1994 08:49:37 GMT', '1994-11-06 08:49:37.00'),
-        ('06-Nov-94 08:49:37', '1994-11-06 08:49:37.00'),
-        ('06-Nov-94', '1994-11-06 00:00:00.00'),
-        ('06-NOV-94', '1994-11-06 00:00:00.00'),
-        ('November 19 08:49:37', '%s-11-19 08:49:37.00' % t.year),
-        ('Nov. 9', '%s-11-09 00:00:00.00' % t.year),
-        ('Sonntag, der 6. November 1994, 08:49:37 GMT', '1994-11-06 08:49:37.00'),
-        ('6. November 2001, 08:49:37', '2001-11-06 08:49:37.00'),
-        ('sep 6', '%s-09-06 00:00:00.00' % t.year),
-        ('sep 6 2000', '2000-09-06 00:00:00.00'),
-        ('September 29', '%s-09-29 00:00:00.00' % t.year),
-        ('Sep. 29', '%s-09-29 00:00:00.00' % t.year),
-        ('6 sep', '%s-09-06 00:00:00.00' % t.year),
-        ('29 September', '%s-09-29 00:00:00.00' % t.year),
-        ('29 Sep.', '%s-09-29 00:00:00.00' % t.year),
-        ('sep 6 2001', '2001-09-06 00:00:00.00'),
-        ('Sep 6, 2001', '2001-09-06 00:00:00.00'),
-        ('September 6, 2001', '2001-09-06 00:00:00.00'),
-        ('sep 6 01', '2001-09-06 00:00:00.00'),
-        ('Sep 6, 01', '2001-09-06 00:00:00.00'),
-        ('September 6, 01', '2001-09-06 00:00:00.00'),
-        ('30 Apr 2006 20:19:00', '2006-04-30 20:19:00.00'),
-        
-        # ISO formats
-        ('1994-11-06 08:49:37', '1994-11-06 08:49:37.00'),
-        ('010203', '2001-02-03 00:00:00.00'),
-        ('2001-02-03 00:00:00.00', '2001-02-03 00:00:00.00'),
-        ('2001-02 00:00:00.00', '2001-02-01 00:00:00.00'),
-        ('2001-02-03', '2001-02-03 00:00:00.00'),
-        ('2001-02', '2001-02-01 00:00:00.00'),
-        ('20000824/2300', '2000-08-24 23:00:00.00'),
-        ('20000824/0102', '2000-08-24 01:02:00.00'),
-        ('20000824', '2000-08-24 00:00:00.00'),
-        ('20000824/020301', '2000-08-24 02:03:01.00'),
-        ('20000824 020301', '2000-08-24 02:03:01.00'),
-        ('-20000824 020301', '-2000-08-24 02:03:01.00'),
-        ('20000824T020301', '2000-08-24 02:03:01.00'),
-        ('20000824 020301', '2000-08-24 02:03:01.00'),
-        ('2000-08-24 02:03:01.00', '2000-08-24 02:03:01.00'),
-        ('T020311', '%s 02:03:11.00' % t.date),
-        ('2003-12-9', '2003-12-09 00:00:00.00'),
-        ('03-12-9', '2003-12-09 00:00:00.00'),
-        ('003-12-9', '0003-12-09 00:00:00.00'),
-        ('0003-12-9', '0003-12-09 00:00:00.00'),
-        ('2003-1-9', '2003-01-09 00:00:00.00'),
-        ('03-1-9', '2003-01-09 00:00:00.00'),
-        ('003-1-9', '0003-01-09 00:00:00.00'),
-        ('0003-1-9', '0003-01-09 00:00:00.00'),
-
-        # US formats
-        ('06/11/94 08:49:37', '1994-06-11 08:49:37.00'),
-        ('11/06/94 08:49:37', '1994-11-06 08:49:37.00'),
-        ('9/23/2001', '2001-09-23 00:00:00.00'),
-        ('9-23-2001', '2001-09-23 00:00:00.00'),
-        ('9/6', '%s-09-06 00:00:00.00' % t.year),
-        ('09/6', '%s-09-06 00:00:00.00' % t.year),
-        ('9/06', '%s-09-06 00:00:00.00' % t.year),
-        ('09/06', '%s-09-06 00:00:00.00' % t.year),
-        ('9/6/2001', '2001-09-06 00:00:00.00'),
-        ('09/6/2001', '2001-09-06 00:00:00.00'),
-        ('9/06/2001', '2001-09-06 00:00:00.00'),
-        ('09/06/2001', '2001-09-06 00:00:00.00'),
-        ('9-6-2001', '2001-09-06 00:00:00.00'),
-        ('09-6-2001', '2001-09-06 00:00:00.00'),
-        ('9-06-2001', '2001-09-06 00:00:00.00'),
-        ('09-06-2001', '2001-09-06 00:00:00.00'),
-        ('2002/05/28 13:10:56.1147 GMT+2', '2002-05-28 13:10:56.11'),
-        ('1970/01/01', '1970-01-01 00:00:00.00'),
-        ('20021025 12:00 PM', '2002-10-25 12:00:00.00'),
-        ('20021025 12:30 PM', '2002-10-25 12:30:00.00'),
-        ('20021025 12:00 AM', '2002-10-25 00:00:00.00'),
-        ('20021025 12:30 AM', '2002-10-25 00:30:00.00'),
-        ('20021025 1:00 PM', '2002-10-25 13:00:00.00'),
-        ('20021025 2:00 AM', '2002-10-25 02:00:00.00'),
-        ('Thursday, February 06, 2003 12:40 PM', '2003-02-06 12:40:00.00'),
-        ('Mon, 18 Sep 2006 23:03:00', '2006-09-18 23:03:00.00'),
-
-        # European formats
-        ('6.11.2001, 08:49:37', '2001-11-06 08:49:37.00'),
-        ('06.11.2001, 08:49:37', '2001-11-06 08:49:37.00'),
-        ('06.11. 08:49:37', '%s-11-06 08:49:37.00' % t.year),
-        #('21/12/2002', '2002-12-21 00:00:00.00'),
-        #('21/08/2002', '2002-08-21 00:00:00.00'),
-        #('21-08-2002', '2002-08-21 00:00:00.00'),
-        #('13/01/03', '2003-01-13 00:00:00.00'),
-        #('13/1/03', '2003-01-13 00:00:00.00'),
-        #('13/1/3', '2003-01-13 00:00:00.00'),
-        #('13/01/3', '2003-01-13 00:00:00.00'),
-
-        # Time only formats
-        ('01:03', '%s 01:03:00.00' % t.date),
-        ('01:03:11', '%s 01:03:11.00' % t.date),
-        ('01:03:11.50', '%s 01:03:11.50' % t.date),
-        ('01:03:11.50 AM', '%s 01:03:11.50' % t.date),
-        ('01:03:11.50 PM', '%s 13:03:11.50' % t.date),
-        ('01:03:11.50 a.m.', '%s 01:03:11.50' % t.date),
-        ('01:03:11.50 p.m.', '%s 13:03:11.50' % t.date),
-
-        # Invalid formats
-        ('6..2001, 08:49:37', '%s 08:49:37.00' % t.date),
-        ('9//2001', 'ignore'),
-        ('06--94 08:49:37', 'ignore'),
-        ('20000824020301', 'ignore'),
-        ('20-03 00:00:00.00', 'ignore'),
-        ('9/2001', 'ignore'),
-        ('9-6', 'ignore'),
-        ('09-6', 'ignore'),
-        ('9-06', 'ignore'),
-        ('09-06', 'ignore'),
-        ('20000824/23', 'ignore'),
-        ('November 1994 08:49:37', 'ignore'),
-        ('Nov. 94', 'ignore'),
-        ('Mon, 18 Sep 2006 23:03:00 +1234567890', 'ignore'),
-
-        ]
-
-    # Add Unicode versions
-    try:
-        unicode
-    except NameError:
-        pass
-    else:
-        k = []
-        for text, result in l:
-            k.append((unicode(text), result))
-        l.extend(k)
-
-    for text, reference in l:
-        try:
-            value = DateTimeFromString(text)
-        except:
-            if reference is None:
-                continue
-            else:
-                value = str(sys.exc_info()[1])
-        valid_datetime = validateDateTimeString(text)
-        valid_date = validateDateString(text)
-        if str(value) != reference and \
-           not reference == 'ignore':
-            print 'Failed to parse "%s"' % text
-            print '  expected: %s' % (reference or '<exception>')
-            print '  parsed:   %s' % value
-        elif _debug:
-            print 'Parsed "%s" successfully' % text
-        if _debug:
-            if not valid_datetime:
-                print '  "%s" failed date/time validation' % text
-            if not valid_date:
-                print '  "%s" failed date validation' % text
-
-    et = DateTime.now()
-    print 'done. (after %f seconds)' % ((et-t).seconds)
-
-    ###
-
-    print 'Testing DateTimeDelta Parser...'
-
-    t = DateTime.now()
-    l = [
-
-        # Literal formats
-        ('Sun Nov  6 08:49:37 1994', '08:49:37.00'),
-        ('1 day, 8 hours, 49 minutes, 37 seconds', '1:08:49:37.00'),
-        ('10 days, 8 hours, 49 minutes, 37 seconds', '10:08:49:37.00'),
-        ('8 hours, 49 minutes, 37 seconds', '08:49:37.00'),
-        ('49 minutes, 37 seconds', '00:49:37.00'),
-        ('37 seconds', '00:00:37.00'),
-        ('37.5 seconds', '00:00:37.50'),
-        ('8 hours later', '08:00:00.00'),
-        ('2 days', '2:00:00:00.00'),
-        ('2 days 23h', '2:23:00:00.00'),
-        ('2 days 23:57', '2:23:57:00.00'),
-        ('2 days 23:57:13', '2:23:57:13.00'),
-        ('', '00:00:00.00'),
-        
-        # ISO formats
-        ('1994-11-06 08:49:37', '08:49:37.00'),
-        ('10:08:49:37', '10:08:49:37.00'),
-        ('08:49:37', '08:49:37.00'),
-        ('08:49', '08:49:00.00'),
-        ('-10:08:49:37', '-10:08:49:37.00'),
-        ('-08:49:37', '-08:49:37.00'),
-        ('-08:49', '-08:49:00.00'),
-        ('- 10:08:49:37', '-10:08:49:37.00'),
-        ('- 08:49:37', '-08:49:37.00'),
-        ('- 08:49', '-08:49:00.00'),
-        ('10:08:49:37.5', '10:08:49:37.50'),
-        ('08:49:37.5', '08:49:37.50'),
-        ('10:8:49:37', '10:08:49:37.00'),
-        ('8:9:37', '08:09:37.00'),
-        ('8:9', '08:09:00.00'),
-        ('8', '00:00:00.00'),
-
-        # Invalid formats
-        #('', None),
-        #('8', None),
-
-        ]
-
-    for text, reference in l:
-        try:
-            value = DateTimeDeltaFromString(text)
-        except:
-            if reference is None:
-                continue
-            else:
-                value = str(sys.exc_info()[1])
-        if str(value) != reference and \
-           not reference == 'ignore':
-            print 'Failed to parse "%s"' % text
-            print '  expected: %s' % (reference or '<exception>')
-            print '  parsed:   %s' % value
-        elif _debug:
-            print 'Parsed "%s" successfully' % text
-
-    et = DateTime.now()
-    print 'done. (after %f seconds)' % ((et-t).seconds)
-
-    ###
-
-    print 'Testing Time Parser...'
-
-    t = DateTime.now()
-    l = [
-
-        # Standard formats
-        ('08:49:37 AM', '08:49:37.00'),
-        ('08:49:37 PM', '20:49:37.00'),
-        ('12:00:00 AM', '00:00:00.00'),
-        ('12:00:00 PM', '12:00:00.00'),
-        ('8:09:37', '08:09:37.00'),
-        ('8:09', '08:09:00.00'),
-        
-        # ISO formats
-        ('08:49:37', '08:49:37.00'),
-        ('08:49', '08:49:00.00'),
-        ('08:49:37.5', '08:49:37.50'),
-        ('08:49:37,5', '08:49:37.50'),
-        ('08:09', '08:09:00.00'),
-
-        # Invalid formats
-        ('', None),
-        ('8:9:37', 'XXX Should give an exception'),
-        ('08:9:37', 'XXX Should give an exception'),
-        ('8:9', None),
-        ('8', None),
-
-        ]
-
-    for text, reference in l:
-        try:
-            value = TimeFromString(text, formats=('standard', 'iso'))
-        except:
-            if reference is None:
-                continue
-            else:
-                value = str(sys.exc_info()[1])
-        if str(value) != reference and \
-           not reference == 'ignore':
-            print 'Failed to parse "%s"' % text
-            print '  expected: %s' % (reference or '<exception>')
-            print '  parsed:   %s' % value
-        elif _debug:
-            print 'Parsed "%s" successfully' % text
-
-    et = DateTime.now()
-    print 'done. (after %f seconds)' % ((et-t).seconds)
-
-if __name__ == '__main__':
-    _test()
--- a/embedded/mx/DateTime/Timezone.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,169 +0,0 @@
-# -*- coding: latin-1 -*-
-
-""" Timezone information.
-
-    XXX This module still has prototype status and is undocumented.
-
-    XXX Double check the offsets given in the zonetable below.
-
-    XXX Add TZ environment variable parsing functions. The REs are already
-        there.
-
-    Copyright (c) 1998-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
-    Copyright (c) 2000-2007, eGenix.com Software GmbH; mailto:info@egenix.com
-    See the documentation for further information on copyrights,
-    or contact the author. All Rights Reserved.
-
-"""
-import DateTime
-import re,string
-
-### REs
-
-# time zone parsing
-isozone = ('(?P<zone>[+-]\d\d:?(?:\d\d)?|Z)')
-zone = ('(?P<zone>[A-Z]+|[+-]\d\d?:?(?:\d\d)?)')
-zoneoffset = ('(?:'
-              '(?P<zonesign>[+-])?'
-              '(?P<hours>\d\d?)'
-              ':?'
-              '(?P<minutes>\d\d)?'
-              '(?P<extra>\d+)?'
-              ')'
-              )
-
-# TZ environment variable parsing
-dstswitchtime = ('(?P<hour>\d\d?):?'
-                 '(?P<minute>\d\d)?:?'
-                 '(?P<second>\d\d)?')
-dstswitch = ('(?:'
-              '(?P<doy>\d+)|'
-              '(?:J(?P<jdoy>\d+))|'
-              '(?:M(?P<month>\d+).(?P<week>\d+).(?P<day>\d+))'
-             ')'
-             '(?:/' + dstswitchtime + ')?'
-             )
-
-# XXX Doesn't work since re doesn't like multiple occurrences of
-#     group names.
-#tz = ('(?::(?P<filename>.+))|'
-#      '(?P<std>[A-Z]+)' + zoneoffset + 
-#      '(?:'
-#       '(?P<dst>[A-Z]+)' + zoneoffset + '?'+
-#       '(?:[;,]' + dstswitch + '[;,]' + dstswitch + ')'
-#      ')?'
-#      )
-
-# Compiled RE objects
-isozoneRE = re.compile(zone)
-zoneRE = re.compile(zone)
-zoneoffsetRE = re.compile(zoneoffset)
-#tzRE= re.compile(tz)
-
-### Time zone offset table
-#
-# The offset given here represent the difference between UTC and the
-# given time zone.
-#
-# Additions and corrections are always welcome :-)
-#
-# Note that some zone names are ambiguous, e.g. IST can refer to Irish
-# Summer Time, Indian Standard Time, Israel Standard Time. We've
-# usualy chosen meaning with the most wide-spread use.
-#
-zonetable = {
-              # Timezone abbreviations
-              # Std     Summer
-
-              # Standards
-              'UT':0,
-              'UTC':0,
-              'GMT':0,
-
-              # A few common timezone abbreviations
-              'CET':1,  'CEST':2, 'CETDST':2, # Central European
-              'MET':1,  'MEST':2, 'METDST':2, # Mean European
-              'MEZ':1,  'MESZ':2,             # Mitteleuropäische Zeit
-              'EET':2,  'EEST':3, 'EETDST':3, # Eastern Europe
-              'WET':0,  'WEST':1, 'WETDST':1, # Western Europe
-              'MSK':3,  'MSD':4,  # Moscow
-              'IST':5.5,          # India
-              'JST':9,            # Japan
-              'KST':9,            # Korea
-              'HKT':8,            # Hong Kong
-
-              # US time zones
-              'AST':-4, 'ADT':-3, # Atlantic
-              'EST':-5, 'EDT':-4, # Eastern
-              'CST':-6, 'CDT':-5, # Central
-              'MST':-7, 'MDT':-6, # Midwestern
-              'PST':-8, 'PDT':-7, # Pacific
-
-              # Australian time zones
-              'CAST':9.5, 'CADT':10.5, # Central
-              'EAST':10,  'EADT':11,   # Eastern
-              'WAST':8,   'WADT':9,    # Western
-              'SAST':9.5, 'SADT':10.5, # Southern
-
-              # US military time zones
-              'Z': 0,
-              'A': 1,
-              'B': 2,
-              'C': 3,
-              'D': 4,
-              'E': 5,
-              'F': 6,
-              'G': 7,
-              'H': 8,
-              'I': 9,
-              'K': 10,
-              'L': 11,
-              'M': 12,
-              'N':-1,
-              'O':-2,
-              'P':-3,
-              'Q':-4,
-              'R':-5,
-              'S':-6,
-              'T':-7,
-              'U':-8,
-              'V':-9,
-              'W':-10,
-              'X':-11,
-              'Y':-12
-              }    
-
-def utc_offset(zone,
-
-               atoi=string.atoi,zoneoffset=zoneoffsetRE,
-               zonetable=zonetable,zerooffset=DateTime.DateTimeDelta(0),
-               oneMinute=DateTime.oneMinute,upper=string.upper):
-
-    """ utc_offset(zonestring)
-
-        Return the UTC time zone offset as DateTimeDelta instance.
-
-        zone must be string and can either be given as +-HH:MM,
-        +-HHMM, +-HH numeric offset or as time zone
-        abbreviation. Daylight saving time must be encoded into the
-        zone offset.
-
-        Timezone abbreviations are treated case-insensitive.
-
-    """
-    if not zone:
-        return zerooffset
-    uzone = upper(zone)
-    if zonetable.has_key(uzone):
-        return zonetable[uzone]*DateTime.oneHour
-    offset = zoneoffset.match(zone)
-    if not offset:
-        raise ValueError,'wrong format or unknown time zone: "%s"' % zone
-    zonesign,hours,minutes,extra = offset.groups()
-    if extra:
-        raise ValueError,'illegal time zone offset: "%s"' % zone
-    offset = int(hours or 0) * 60 + int(minutes or 0)
-    if zonesign == '-':
-        offset = -offset
-    return offset*oneMinute
-
--- a/embedded/mx/DateTime/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-""" mxDateTime - Date and time handling routines and types
-
-    Copyright (c) 1998-2000, Marc-Andre Lemburg; mailto:mal@lemburg.com
-    Copyright (c) 2000-2007, eGenix.com Software GmbH; mailto:info@egenix.com
-    See the documentation for further information on copyrights,
-    or contact the author. All Rights Reserved.
-"""
-from DateTime import *
-from DateTime import __version__
-
-## mock strptime implementation
-from datetime import datetime
-
-def strptime(datestr, formatstr, datetime=datetime):
-    """mocked strptime implementation"""
-    date = datetime.strptime(datestr, formatstr)
-    return DateTime(date.year, date.month, date.day,
-                    date.hour, date.minute, date.second)
-
-# don't expose datetime directly
-del datetime
--- a/embedded/mx/DateTime/mxDateTime_python.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,614 +0,0 @@
-"""
-    Python implementation courtesy of Drew Csillag (StarMedia Network, Inc.)
-
-    This version has been somewhat modified by MAL. It is still fairly
-    rough though and not necessarily high performance... 
-
-    XXX Still needs testing and checkup !!!
-
-    WARNING: Using this file is only recommended if you really must
-    use it for some reason. It is not being actively maintained !
-
-"""
-
-__version__ = '1.2.0 [Python]'
-
-import time,types,exceptions,math
-
-### Errors
-
-class Error(exceptions.StandardError):
-    pass
-
-class RangeError(Error):
-    pass
-
-### Constants (internal use only)
-
-month_offset=(
-    (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365),
-    (0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366),
-    )
-
-days_in_month=(
-    (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
-    (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),
-    )
-
-### Helpers
-
-def _IS_LEAPYEAR(d):
-    return ((d.year % 4 == 0)
-            and (
-                (d.year % 100 != 0)
-                or (d.year % 400 == 0)
-                )
-            )
-
-def _YEAROFFSET(d):
-    return (
-        (d.year - 1) * 365
-        + (d.year - 1) / 4
-        - (d.year - 1) / 100
-        + (d.year - 1) / 400
-        )
-
-class _EmptyClass:
-    pass
-
-def createEmptyObject(Class,
-                      _EmptyClass=_EmptyClass):
-
-    o = _EmptyClass()
-    o.__class__ = Class
-    return o
-
-### DateTime class
-
-class DateTime:
-
-    def __init__(self, year, month=1, day=1, hour=0, minute=0, second=0.0):
-
-        second=1.0 * second
-        if month <= 0:
-            raise RangeError, "year out of range (>0)"
-
-        #calculate absolute date
-        leap = (year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))
-
-        #Negative values indicate days relative to the years end
-        if month < 0:
-            month = month + 13 
-
-        if not (month >= 1 and month <= 12):
-            raise RangeError, "month out of range (1-12)"
-
-        #Negative values indicate days relative to the months end
-        if (day < 0):
-            day = day + days_in_month[leap][month - 1] + 1;
-
-        if not (day >= 1 and day <= days_in_month[leap][month - 1]):
-            raise RangeError, "day out of range"
-
-        year = year - 1
-        yearoffset = year * 365 + year / 4 - year / 100 + year / 400
-        year = year + 1
-        absdate = day + month_offset[leap][month - 1] + yearoffset;
-
-        self.absdate = absdate
-        self.year = year
-        self.month = month
-        self.day = day
-        self.day_of_week = (absdate - 1) % 7
-        self.day_of_year = absdate - yearoffset
-        self.days_in_month = days_in_month[leap][month - 1]
-        comdate = absdate - 693594
-
-        if not (hour >=0 and hour <= 23):
-            raise RangeError, "hour out of range (0-23)"
-        if not (minute >= 0 and minute <= 59):
-            raise RangeError, "minute out of range (0-59)"
-        if not (second >= 0.0 and
-                (second < 60.0 or 
-                 (hour == 23 and minute == 59 and second < 61.0))):
-            raise RangeError, "second out of range (0.0 - <60.0; <61.0 for 23:59)"
-
-        self.abstime = (hour * 3600 + minute * 60) + second
-        self.hour = hour
-        self.minute = minute
-        self.second = second
-        self.dst = -1
-        self.tz = "???"
-        self.is_leapyear = leap
-        self.yearoffset = yearoffset
-        self.iso_week = (self.year, self.day, self.day_of_week)
-
-        if comdate < 0.0:
-            comdate = comdate - self.abstime / 86400.0
-        else:
-            comdate = comdate + self.abstime / 86400.0
-
-        self.comdate = comdate
-
-    def COMDate(self):
-        return self.comdate
-    
-    def __str__(self):
-        return "%04d-%02d-%02d %02d:%02d:%05.2f" % (
-            self.year, self.month, self.day, self.hour, self.minute,
-            self.second)
-    
-    def __getattr__(self, attr):
-        if attr == 'mjd':
-            return (self - mjd0).days
-        elif attr == 'jdn':
-            return (self - jdn0).days
-        elif attr == 'tjd':
-            return (self - jdn0).days % 10000
-        elif attr == 'tjd_myriad':
-            return int((self - jdn0).days) / 10000 + 240
-        elif attr == 'absdays':
-            return self.absdate - 1 + self.abstime / 86400.0
-        else:
-            try:
-                return self.__dict__[attr]
-            except:
-                raise AttributeError, attr
-
-    def __mul__(self, other):
-        raise TypeError, "bad operand type(s) for *"
-
-    def __div__(self, other):
-        raise TypeError, "bad operand type(s) for /"
-    
-    def strftime(self, format_string="%c"):
-        "localtime([seconds]) -> (tm_year,tm_mon,tm_day,tm_hour,tm_min,tm_sec,tm_wday,tm_yday,tm_isdst)"
-        # The map prevents a deprecation warning on Python 2.5.1 (Mac)
-        # DeprecationWarning: integer argument expected, got float
-        items = [int(item) for item in self.tuple()]
-        return time.strftime(format_string, items)
-
-    # Alias
-    Format = strftime
-    
-    def tuple(self):
-        return (self.year, self.month, self.day, self.hour, self.minute,
-                self.second, self.day_of_week, self.day_of_year, -1)
-        #return time.localtime(self.ticks())
-
-    def absvalues(self):
-        return self.absdate, self.abstime
-    
-    def __float__(self):
-        return self.ticks()
-
-    def __int__(self):
-        return int(self.ticks)
-    
-    def ticks(self, offset=0.0, dst=-1):
-        tticks=time.mktime(self.year, self.month, self.day, self.hour,
-                           self.minute, self.second, self.day_of_week, 0, dst)
-        if tticks == -1:
-            raise OverflowError, "cannot convert value to a time value"
-        ticks = (1.0*tticks) + (self.abstime - int(self.abstime)) - offset
-        return ticks
-
-    def gmticks(self, offset=0.0):
-        from mx.DateTime import tz_offset
-        return (self-tz_offset(self)).ticks()
-
-    def gmtoffset(self):
-        gmtime = DateTime(*time.gmtime()[:6])
-        return - (now() - gmtime)
-    
-    def __repr__(self):
-        return "<DateTime object for '%d-%02d-%02d %02d:%02d:%05.2f' at %x>"% (
-            self.year, self.month, self.day, self.hour, self.minute,
-            self.second, id(self))
-
-    def __cmp__(self, other,
-                cmp=cmp):
-
-        if isinstance(other,DateTime):
-            cmpdate = cmp(self.absdate,other.absdate)
-            if cmpdate == 0:
-                return cmp(self.abstime,other.abstime)
-            else:
-                return cmpdate
-        elif type(other) == types.NoneType:
-            return -1
-        elif type(other) == types.StringType:
-            return -1
-        elif type(other) in (types.FloatType, types.LongType, types.IntType):
-            return 1
-        return -1
-
-    def __hash__(self):
-        return hash(self.tuple())
-    
-    def __add__(self, other):
-        abstime=self.abstime
-        absdate=self.absdate
-
-        didadd=0
-        
-        if type(other) == types.InstanceType:
-            if other.__class__ == DateTimeDelta:
-                abstime = abstime + other.seconds
-                didadd=1
-            elif other.__class__ == DateTime:
-                raise TypeError, "DateTime + DateTime is not supported"
-            else:
-                return other.__class__.__radd__(other, self)
-            
-        elif type(other) == types.IntType or type(other) == types.FloatType:
-            abstime = abstime + other * 86400.0
-            didadd=1
-
-        if not didadd:
-            raise TypeError, "cannot add these two types"
-
-        if abstime >= 86400.0:
-            days = abstime / 86400.0
-            absdate = absdate + days
-            abstime = abstime - (86400.0 * int(days))
-            #print "absdate, abstime = ", absdate, abstime
-        elif abstime < 0.0:
-            days = int(((-abstime - 1) / 86400.0)) + 1
-            #days = int(-abstime / 86400.0)
-            absdate = absdate - days
-            abstime = abstime + 86400.0 * int(days)
-
-        if absdate < 1:
-            raise RangeError, "underflow while adding"
-
-        return DateTimeFromAbsDateTime(absdate, abstime)
-
-    def __radd__(self, other):
-        return DateTime.__add__(other, self)
-    
-    def __sub__(self, other):
-        abstime=self.abstime
-        absdate=self.absdate
-
-        didsub=0
-        if type(other) == types.InstanceType:
-            if other.__class__ == DateTimeDelta:
-                abstime = abstime - other.seconds
-                didsub = 1
-            elif other.__class__ == DateTime:
-                absdate = absdate - other.absdate
-                abstime = abstime - other.abstime
-                return DateTimeDelta(absdate,0.0,0.0,abstime)
-            else:
-                return other.__rsub__(self)
-
-        elif type(other) == types.IntType or type(other) == types.FloatType:
-            abstime = abstime - other * 86400.0;
-            didsub=1
-
-        if not didsub:
-            raise TypeError, "cannot subtract these two types"
-
-        if abstime >= 86400.0:
-            days = abstime / 86400.0
-            absdate = absdate + days
-            abstime = abstime - (86400.0 * days)
-            #print "absdate, abstime = ", absdate, abstime
-        elif abstime < 0.0:
-            #print "abstime < 0"
-            days = int( ((-abstime - 1) / 86400.0) + 1)
-            #days = -abstime / 86400.0
-            absdate = absdate - int(days)
-            abstime = (1.0*abstime) + (86400.0 * days)
-            #print "absdate, abstime", absdate, abstime
-        if absdate < 1:
-            raise RangeError, "underflow while adding"
-
-        return DateTimeFromAbsDateTime(absdate, abstime)
-
-# Constants
-mjd0 = DateTime(1858, 11, 17)
-jdn0 = DateTime(-4713, 1, 1, 12, 0, 0.0)
-
-# Other DateTime constructors
-
-def DateTimeFromCOMDate(comdate):
-
-    absdate = int(comdate)
-    abstime = (comdate - float(absdate)) * 86400.0
-    if abstime < 0.0:
-        abstime = -abstime
-    absdate = absdate + 693594;
-    dt = DateTimeFromAbsDateTime(absdate, abstime)
-    dt.comdate = comdate
-    return dt
-    
-def DateTimeFromAbsDateTime(absdate, abstime):
-
-    # Create the object without calling its default constructor
-    dt = createEmptyObject(DateTime)
-
-    # Init. the object
-    abstime=1.0 * abstime
-    if abstime < 0 and abstime > -0.001: abstime = 0.0
-    if not (absdate > 0):
-        raise RangeError, "absdate out of range (>0)"
-    if not (abstime >= 0.0 and abstime <= 86400.0):
-        raise RangeError, "abstime out of range (0.0 - 86400.0) <%s>" % abstime
-
-    dt.absdate=absdate
-    dt.abstime=abstime
-
-    #calculate com date
-    comdate = 1.0 * (dt.absdate - 693594)
-    if comdate < 0.0:
-        comdate = comdate - dt.abstime / 86400.0
-    else:
-        comdate = comdate + dt.abstime / 86400.0
-    dt.comdate = comdate
-
-    #calculate the date
-    #print "absdate=", absdate
-    year = int((1.0 * absdate) / 365.2425)
-
-    #newApproximation:
-    while 1:
-        #print "year=", year
-        yearoffset = year * 365 + year / 4 - year / 100 + year / 400
-        #print "yearoffset=", yearoffset
-        #print "absdate=", absdate
-        if yearoffset >= absdate:
-            year = year - 1
-            #print "year = ", year
-            continue #goto newApproximation
-
-        year = year + 1
-        leap = (year % 4 == 0) and ((year % 100 != 0) or (year % 400 == 0))
-        dayoffset = absdate - yearoffset
-        #print "dayoffset=", dayoffset
-        if dayoffset > 365 and leap == 0:
-            #print "dayoffset=", dayoffset
-            continue #goto newApproximation
-
-        monthoffset = month_offset[leap]
-        for month in range(1, 13):
-            if monthoffset[month] >= dayoffset:
-                break
-        dt.year = year
-        dt.month = month
-        dt.day = dayoffset - month_offset[leap][month-1]
-        dt.day_of_week = (dt.absdate - 1) % 7
-        dt.day_of_year = dayoffset
-        break
-    
-    #calculate the time
-    inttime = int(abstime)
-    hour = inttime / 3600
-    minute = (inttime % 3600) / 60
-    second = abstime - 1.0 * (hour*3600 + minute*60)
-    dt.hour = hour;
-    dt.minute = minute;
-    dt.second = second;
-    dt.days_in_month = days_in_month[leap][month - 1]
-    dt.dst = -1
-    dt.tz = "???"
-    dt.is_leapyear = leap
-    dt.yearoffset = yearoffset
-    return dt
-
-def now(
-        time=time.time,float=float,localtime=time.localtime,
-        round=round,int=int,DateTime=DateTime,floor=math.floor):
-    ticks = time()
-    Y,M,D,h,m,s = localtime(ticks)[:6]
-    s = s + (ticks - floor(ticks))
-    return DateTime(Y,M,D,h,m,s)
-
-def utc(
-        time=time.time,float=float,gmtime=time.gmtime,
-        round=round,int=int,DateTime=DateTime,floor=math.floor):
-
-    ticks = time()
-    Y,M,D,h,m,s = gmtime(ticks)[:6]
-    s = s + (ticks - floor(ticks))
-    return DateTime(Y,M,D,h,m,s)
-
-# Aliases
-Date = Timestamp = DateTime
-
-# XXX Calendars are not supported:
-def notSupported(*args,**kws):
-    raise Error,'calendars are not supported by the Python version of mxDateTime'
-JulianDateTime = notSupported
-
-### DateTimeDelta class
-               
-class DateTimeDelta:
-
-    def __init__(self, days=0, hours=0, minutes=0, seconds=0):
-
-        seconds = seconds + (days * 86400.0 + hours * 3600.0 + minutes * 60.0)
-        self.seconds = seconds
-        if seconds < 0.0:
-            seconds = -seconds
-        day = long(seconds / 86400.0)
-        seconds = seconds - (86400.0 * day)
-        wholeseconds = int(seconds)
-        hour = wholeseconds / 3600
-        minute = (wholeseconds % 3600) / 60
-        second = seconds - (hour * 3600.0 + minute * 60.0)
-        self.day = day
-        self.hour = hour
-        self.minute = minute
-        self.second = second
-        seconds=self.seconds
-        self.minutes = seconds / 60.0
-        self.hours = seconds / 3600.0
-        self.days = seconds / 86400.0
-
-    def __str__(self):
-        if self.day != 0:
-            if self.seconds >= 0.0:
-                r="%s:%02d:%02d:%05.2f" % (
-                    self.day, self.hour, self.minute, self.second)
-            else:
-                r="-%s:%02d:%02d:%05.2f" % (
-                    self.day, self.hour, self.minute, self.second)
-        else:
-            if self.seconds >= 0.0:
-                r="%02d:%02d:%05.2f" % (self.hour, self.minute, self.second)
-            else:
-                r="-%02d:%02d:%05.2f" % (self.hour, self.minute, self.second)
-        return r
-            
-    def absvalues(self):
-        days=self.seconds / 86400
-        seconds=self.seconds - (days * 86400.0)
-        return days, seconds
-
-    def tuple(self):
-        return (self.day, self.hour, self.minute, self.second)
-
-    def strftime(self, format_string):
-        raise NotImplementedError
-    
-    def __int__(self):
-        return int(self.seconds)
-
-    def __float__(self):
-        return self.seconds
-    
-    def __cmp__(self, other, accuracy=0.0):
-        if (type(other) == types.InstanceType
-            and other.__class__ == DateTimeDelta):
-
-            diff=self.seconds - other.seconds
-            if abs(diff) > accuracy:
-                if diff > 0: return 1
-                return -1
-            
-        elif type(other) == types.FloatType:
-            diff=self.seconds - other
-            if abs(diff) > accuracy:
-                if diff > 0: return 1
-                return -1
-            
-        elif type(other) == types.IntType:
-            diff=self.seconds - other
-            if abs(diff) > accuracy:
-                if diff > 0: return 1
-                return -1
-            
-        return 0
-    
-    def __getattr__(self, attr):
-        seconds=self.__dict__['seconds']
-        if attr in ('hour', 'minute', 'second', 'day'):
-            if seconds >= 0.0:
-                return self.__dict__[attr]
-            else:
-                return -self.__dict__[attr]
-        else:
-            try:
-                return self.__dict__[attr]
-            except:
-                raise AttributeError, attr
-
-    def __div__(self, other):
-        if type(other) in (types.IntType, types.FloatType):
-            return DateTimeDelta(0.0,0.0,0.0,self.seconds / other)
-        elif (type(other) == types.InstanceType
-              and isinstance(other,DateTimeDelta)):
-            return DateTimeDelta(0.0,0.0,0.0,self.seconds / other.seconds)
-        raise TypeError, "bad operand types for /"
-    
-    def __mul__(self, other):
-        if type(other) == types.IntType or type(other) == types.FloatType:
-            return DateTimeDelta(0.0,0.0,0.0,self.seconds * other)
-        else:
-            #print "type", type(other)
-            raise TypeError, "cannot multiply these two types"
-
-    def __rmul__(self, other):
-        return self.__mul__(other)
-    
-    def __neg__(self):
-        return DateTimeDelta(0.0,0.0,0.0,-self.seconds)
-        
-    def __repr__(self):
-        if self.day != 0:
-            if self.seconds >= 0.0:
-                strval="%s:%02d:%02d:%05.2f" % (self.day, self.hour,
-                                                 self.minute, self.second)
-            else:
-                strval="-%s:%02d:%02d:%05.2f" % (self.day, self.hour,
-                                                  self.minute, self.second)
-        else:
-            if self.seconds >= 0.0:
-                strval="%02d:%02d:%05.2f" % (self.hour, self.minute,
-                                            self.second)
-            else:
-                strval="-%02d:%02d:%05.2f" % (self.hour, self.minute,
-                                             self.second)
-        return "<DateTimeDelta object for '%s' at %x>" % (strval, id(self))
-    
-    def __abs__(self):
-        if self.seconds < 0:
-            return -self
-        return self
-
-    def __nonzero__(self):
-        return self.seconds != 0.0
-    
-    def __add__(self, other):
-        if type(other) == types.InstanceType:
-            if isinstance(other,DateTime):
-                return other + self
-            elif isinstance(other,DateTimeDelta):
-                return DateTimeDelta(0.0,0.0,0.0,self.seconds + other.seconds)
-
-    # What about __radd__ ?
-        
-# Other DateTimeDelta constructors
-
-def TimeDelta(hour=0.0, minute=0.0, second=0.0):
-    return DateTimeDelta(0.0, hours, minutes, seconds)
-
-Time=TimeDelta
-
-def DateTimeDeltaFromSeconds(seconds):
-    return DateTimeDelta(0.0,0.0,0.0,seconds)
-
-def DateTimeDeltaFromDays(days):
-    return DateTimeDelta(days)
-
-### Types
-
-DateTimeType = DateTime
-DateTimeDeltaType = DateTimeDelta
-
-### Functions
-
-def cmp(a,b,acc):
-
-    if isinstance(a,DateTime) and isinstance(b,DateTime):
-        diff = a.absdays - b.absdays
-        if (diff >= 0 and diff <= acc) or (diff < 0 and -diff <= acc):
-            return 0
-        elif diff < 0:
-            return 1
-        else:
-            return -1
-
-    elif isinstance(a,DateTimeDelta) and isinstance(b,DateTimeDelta):
-        diff = a.days - b.days
-        if (diff >= 0 and diff <= acc) or (diff < 0 and -diff <= acc):
-            return 0
-        elif diff < 0:
-            return 1
-        else:
-            return -1
-
-    else:
-        raise TypeError,"objects must be DateTime[Delta] instances"
--- a/entities/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/entities/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -8,12 +8,12 @@
 
 from warnings import warn
 
-from logilab.common.deprecation import deprecated_function
+from logilab.common.deprecation import deprecated_function, obsolete
 from logilab.common.decorators import cached
 
 from cubicweb import Unauthorized, typed_eid
-from cubicweb.common.utils import dump_class
-from cubicweb.common.entity import Entity
+from cubicweb.entity import Entity
+from cubicweb.utils import dump_class
 from cubicweb.schema import FormatConstraint
 
 from cubicweb.interfaces import IBreadCrumbs, IFeed
@@ -23,38 +23,9 @@
     """an entity instance has e_schema automagically set on the class and
     instances have access to their issuing cursor
     """
-    id = 'Any'   
-    __rtags__ = {
-        'is' : ('generated', 'link'),
-        'is_instance_of' : ('generated', 'link'),
-        'identity' : ('generated', 'link'),
-        
-        # use primary and not generated for eid since it has to be an hidden
-        # field in edition
-        ('eid',                '*', 'subject'): 'primary',
-        ('creation_date',      '*', 'subject'): 'generated',
-        ('modification_date',  '*', 'subject'): 'generated',
-        ('has_text',           '*', 'subject'): 'generated',
-        
-        ('require_permission', '*', 'subject') : ('generated', 'link'),
-        ('owned_by',           '*', 'subject') : ('generated', 'link'),
-        ('created_by',         '*', 'subject') : ('generated', 'link'),
-        
-        ('wf_info_for',        '*', 'subject') : ('generated', 'link'),
-        ('wf_info_for',        '*', 'object')  : ('generated', 'link'),
-                 
-        ('description',        '*', 'subject'): 'secondary',
+    id = 'Any'
+    __implements__ = (IBreadCrumbs, IFeed)
 
-        # XXX should be moved in their respective cubes
-        ('filed_under',        '*', 'subject') : ('generic', 'link'),
-        ('filed_under',        '*', 'object')  : ('generic', 'create'),
-        # generated since there is a componant to handle comments
-        ('comments',           '*', 'subject') : ('generated', 'link'),
-        ('comments',           '*', 'object')  : ('generated', 'link'),
-        }
-
-    __implements__ = (IBreadCrumbs, IFeed)
-    
     @classmethod
     def selected(cls, etype):
         """the special Any entity is used as the default factory, so
@@ -67,7 +38,7 @@
         usercls.id = etype
         usercls.__initialize__()
         return usercls
-    
+
     fetch_attrs = ('modification_date',)
     @classmethod
     def fetch_order(cls, attr, var):
@@ -75,7 +46,7 @@
         this type are fetched
         """
         return cls.fetch_unrelated_order(attr, var)
-    
+
     @classmethod
     def fetch_unrelated_order(cls, attr, var):
         """class method used to control sort order when multiple entities of
@@ -87,30 +58,15 @@
         return None
 
     @classmethod
-    def __initialize__(cls): 
+    def __initialize__(cls):
         super(ANYENTITY, cls).__initialize__() # XXX
-        eschema = cls.e_schema
-        eschema.format_fields = {}
         # set a default_ATTR method for rich text format fields
-        for attr, formatattr in eschema.rich_text_fields():
-            if not hasattr(cls, 'default_%s' % formatattr):
-                setattr(cls, 'default_%s' % formatattr, cls._default_format)
-            eschema.format_fields[formatattr] = attr
-            
-    def _default_format(self):
-        return self.req.property_value('ui.default-text-format')
+        # XXX move this away once the old widgets have been dropped!
+        eschema = cls.e_schema
+        for metaattr, (metadata, attr) in eschema.meta_attributes().iteritems():
+            if metadata == 'format' and not hasattr(cls, 'default_%s' % metaattr):
+                setattr(cls, 'default_%s' % metaattr, cls._default_format)
 
-    def use_fckeditor(self, attr):
-        """return True if fckeditor should be used to edit entity's attribute named
-        `attr`, according to user preferences
-        """
-        req = self.req
-        if req.property_value('ui.fckeditor') and self.has_format(attr):
-            if self.has_eid() or '%s_format' % attr in self:
-                return self.format(attr) == 'text/html'
-            return req.property_value('ui.default-text-format') == 'text/html'
-        return False
-    
     # meta data api ###########################################################
 
     def dc_title(self):
@@ -128,7 +84,7 @@
     def dc_long_title(self):
         """return a more detailled title for this entity"""
         return self.dc_title()
-    
+
     def dc_description(self, format='text/plain'):
         """return a suitable description for this entity"""
         if self.e_schema.has_subject_relation('description'):
@@ -155,7 +111,6 @@
     def dc_type(self, form=''):
         """return the display name for the type of this entity (translated)"""
         return self.e_schema.display_name(self.req, form)
-    display_name = deprecated_function(dc_type) # require agueol > 0.8.1, asteretud > 0.10.0 for removal
 
     def dc_language(self):
         """return language used by this entity (translated)"""
@@ -166,10 +121,10 @@
                                  'internationalizable'):
                 return self.req._(self.req.user.property_value('ui.language'))
         return self.req._(self.vreg.property_value('ui.language'))
-        
+
     @property
     def creator(self):
-        """return the EUser entity which has created this entity, or None if
+        """return the CWUser entity which has created this entity, or None if
         unknown or if the curent user doesn't has access to this euser
         """
         try:
@@ -202,9 +157,79 @@
 
     def rss_feed_url(self):
         return self.absolute_url(vid='rss')
-    
+
     # abstractions making the whole things (well, some at least) working ######
-    
+
+    def sortvalue(self, rtype=None):
+        """return a value which can be used to sort this entity or given
+        entity's attribute
+        """
+        if rtype is None:
+            return self.dc_title().lower()
+        value = self.get_value(rtype)
+        # do not restrict to `unicode` because Bytes will return a `str` value
+        if isinstance(value, basestring):
+            return self.printable_value(rtype, format='text/plain').lower()
+        return value
+
+    # edition helper functions ################################################
+
+    def linked_to(self, rtype, target, remove=True):
+        """if entity should be linked to another using __linkto form param for
+        the given relation/target, return eids of related entities
+
+        This method is consuming matching link-to information from form params
+        if `remove` is True (by default).
+        """
+        try:
+            return self.__linkto[(rtype, target)]
+        except AttributeError:
+            self.__linkto = {}
+        except KeyError:
+            pass
+        linktos = list(self.req.list_form_param('__linkto'))
+        linkedto = []
+        for linkto in linktos[:]:
+            ltrtype, eid, lttarget = linkto.split(':')
+            if rtype == ltrtype and target == lttarget:
+                # delete __linkto from form param to avoid it being added as
+                # hidden input
+                if remove:
+                    linktos.remove(linkto)
+                    self.req.form['__linkto'] = linktos
+                linkedto.append(typed_eid(eid))
+        self.__linkto[(rtype, target)] = linkedto
+        return linkedto
+
+    # edit controller callbacks ###############################################
+
+    def after_deletion_path(self):
+        """return (path, parameters) which should be used as redirect
+        information when this entity is being deleted
+        """
+        return str(self.e_schema).lower(), {}
+
+    def pre_web_edit(self):
+        """callback called by the web editcontroller when an entity will be
+        created/modified, to let a chance to do some entity specific stuff.
+
+        Do nothing by default.
+        """
+        pass
+
+    # server side helpers #####################################################
+
+    def notification_references(self, view):
+        """used to control References field of email send on notification
+        for this entity. `view` is the notification view.
+
+        Should return a list of eids which can be used to generate message ids
+        of previously sent email
+        """
+        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
@@ -223,127 +248,30 @@
             tschema = rschema.subjects(cls.e_schema)[0]
             wdg = widget(cls.vreg, tschema, rschema, cls, 'object')
         return wdg
-        
-    def sortvalue(self, rtype=None):
-        """return a value which can be used to sort this entity or given
-        entity's attribute
-        """
-        if rtype is None:
-            return self.dc_title().lower()
-        value = self.get_value(rtype)
-        # do not restrict to `unicode` because Bytes will return a `str` value
-        if isinstance(value, basestring):
-            return self.printable_value(rtype, format='text/plain').lower()
-        return value
 
-    def after_deletion_path(self):
-        """return (path, parameters) which should be used as redirect
-        information when this entity is being deleted
-        """
-        return str(self.e_schema).lower(), {}
-
-    def add_related_schemas(self):
-        """this is actually used ui method to generate 'addrelated' actions from
-        the schema.
+    @obsolete('use EntityFieldsForm.subject_relation_vocabulary')
+    def subject_relation_vocabulary(self, rtype, limit):
+        from cubicweb.web.form import EntityFieldsForm
+        return EntityFieldsForm(self.req, None, entity=self).subject_relation_vocabulary(rtype, limit)
 
-        If you're using explicit 'addrelated' actions for an entity types, you
-        should probably overrides this method to return an empty list else you
-        may get some unexpected actions.
-        """
-        req = self.req
-        eschema = self.e_schema
-        for role, rschemas in (('subject', eschema.subject_relations()),
-                               ('object', eschema.object_relations())):
-            for rschema in rschemas:
-                if rschema.is_final():
-                    continue
-                # check the relation can be added as well
-                if role == 'subject'and not rschema.has_perm(req, 'add', fromeid=self.eid):
-                    continue
-                if role == 'object'and not rschema.has_perm(req, 'add', toeid=self.eid):
-                    continue
-                # check the target types can be added as well
-                for teschema in rschema.targets(eschema, role):
-                    if not self.relation_mode(rschema, teschema, role) == 'create':
-                        continue
-                    if teschema.has_local_role('add') or teschema.has_perm(req, 'add'):
-                        yield rschema, teschema, role
+    @obsolete('use EntityFieldsForm.object_relation_vocabulary')
+    def object_relation_vocabulary(self, rtype, limit):
+        from cubicweb.web.form import EntityFieldsForm
+        return EntityFieldsForm(self.req, None, entity=self).object_relation_vocabulary(rtype, limit)
 
-    def relation_mode(self, rtype, targettype, role='subject'):
-        """return a string telling if the given relation is usually created
-        to a new entity ('create' mode) or to an existant entity ('link' mode)
-        """
-        return self.rtags.get_mode(rtype, targettype, role)
-
-    # edition helper functions ################################################
-    
+    @obsolete('use AutomaticEntityForm.[e]relations_by_category')
     def relations_by_category(self, categories=None, permission=None):
-        if categories is not None:
-            if not isinstance(categories, (list, tuple, set, frozenset)):
-                categories = (categories,)
-            if not isinstance(categories, (set, frozenset)):
-                categories = frozenset(categories)
-        eschema, rtags  = self.e_schema, self.rtags
-        if self.has_eid():
-            eid = self.eid
-        else:
-            eid = None
-        for rschema, targetschemas, role in eschema.relation_definitions(True):
-            if rschema in ('identity', 'has_text'):
-                continue
-            # check category first, potentially lower cost than checking
-            # permission which may imply rql queries
-            if categories is not None:
-                targetschemas = [tschema for tschema in targetschemas
-                                 if rtags.get_tags(rschema.type, tschema.type, role).intersection(categories)]
-                if not targetschemas:
-                    continue
-            tags = rtags.get_tags(rschema.type, role=role)
-            if permission is not None:
-                # tag allowing to hijack the permission machinery when
-                # permission is not verifiable until the entity is actually
-                # created...
-                if eid is None and ('%s_on_new' % permission) in tags:
-                    yield (rschema, targetschemas, role)
-                    continue
-                if rschema.is_final():
-                    if not rschema.has_perm(self.req, permission, eid):
-                        continue
-                elif role == 'subject':
-                    if not ((eid is None and rschema.has_local_role(permission)) or
-                            rschema.has_perm(self.req, permission, fromeid=eid)):
-                        continue
-                    # on relation with cardinality 1 or ?, we need delete perm as well
-                    # if the relation is already set
-                    if (permission == 'add'
-                        and rschema.cardinality(eschema, targetschemas[0], role) in '1?'
-                        and self.has_eid() and self.related(rschema.type, role)
-                        and not rschema.has_perm(self.req, 'delete', fromeid=eid,
-                                                 toeid=self.related(rschema.type, role)[0][0])):
-                        continue
-                elif role == 'object':
-                    if not ((eid is None and rschema.has_local_role(permission)) or
-                            rschema.has_perm(self.req, permission, toeid=eid)):
-                        continue
-                    # on relation with cardinality 1 or ?, we need delete perm as well
-                    # if the relation is already set
-                    if (permission == 'add'
-                        and rschema.cardinality(targetschemas[0], eschema, role) in '1?'
-                        and self.has_eid() and self.related(rschema.type, role)
-                        and not rschema.has_perm(self.req, 'delete', toeid=eid,
-                                                 fromeid=self.related(rschema.type, role)[0][0])):
-                        continue
-            yield (rschema, targetschemas, role)
+        from cubicweb.web.views.autoform import AutomaticEntityForm
+        return AutomaticEntityForm.erelations_by_category(self, categories, permission)
 
+    @obsolete('use AutomaticEntityForm.[e]srelations_by_category')
     def srelations_by_category(self, categories=None, permission=None):
-        result = []
-        for rschema, ttypes, target in self.relations_by_category(categories,
-                                                                  permission):
-            if rschema.is_final():
-                continue
-            result.append( (rschema.display_name(self.req, target), rschema, target) )
-        return sorted(result)
-                
+        from cubicweb.web.views.autoform import AutomaticEntityForm
+        return AutomaticEntityForm.esrelations_by_category(self, categories, permission)
+
+    def _default_format(self):
+        return self.req.property_value('ui.default-text-format')
+
     def attribute_values(self, attrname):
         if self.has_eid() or attrname in self:
             try:
@@ -372,51 +300,15 @@
             values = (values,)
         return values
 
-    def linked_to(self, rtype, target, remove=True):
-        """if entity should be linked to another using __linkto form param for
-        the given relation/target, return eids of related entities
-
-        This method is consuming matching link-to information from form params
-        if `remove` is True (by default).
+    def use_fckeditor(self, attr):
+        """return True if fckeditor should be used to edit entity's attribute named
+        `attr`, according to user preferences
         """
-        try:
-            return self.__linkto[(rtype, target)]
-        except AttributeError:
-            self.__linkto = {}
-        except KeyError:
-            pass
-        linktos = list(self.req.list_form_param('__linkto'))
-        linkedto = []
-        for linkto in linktos[:]:
-            ltrtype, eid, lttarget = linkto.split(':')
-            if rtype == ltrtype and target == lttarget:
-                # delete __linkto from form param to avoid it being added as
-                # hidden input
-                if remove:
-                    linktos.remove(linkto)
-                    self.req.form['__linkto'] = linktos
-                linkedto.append(typed_eid(eid))
-        self.__linkto[(rtype, target)] = linkedto
-        return linkedto
-
-    def pre_web_edit(self):
-        """callback called by the web editcontroller when an entity will be
-        created/modified, to let a chance to do some entity specific stuff.
-
-        Do nothing by default.
-        """
-        pass
-    
-    # server side helpers #####################################################
-    
-    def notification_references(self, view):
-        """used to control References field of email send on notification
-        for this entity. `view` is the notification view.
-        
-        Should return a list of eids which can be used to generate message ids
-        of previously sent email
-        """
-        return ()
+        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
--- a/entities/authobjs.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/entities/authobjs.py	Mon May 04 13:09:48 2009 +0200
@@ -5,37 +5,26 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
+
 from logilab.common.decorators import cached
 
 from cubicweb import Unauthorized
 from cubicweb.entities import AnyEntity, fetch_config
 
-class EGroup(AnyEntity):
-    id = 'EGroup'
+class CWGroup(AnyEntity):
+    id = 'CWGroup'
     fetch_attrs, fetch_order = fetch_config(['name'])
-    __rtags__ = dict(in_group='create')
+    fetch_unrelated_order = fetch_order
 
     def db_key_name(self):
         """XXX goa specific"""
         return self.get('name')
 
-    
-class EUser(AnyEntity):
-    id = 'EUser'
+class CWUser(AnyEntity):
+    id = 'CWUser'
     fetch_attrs, fetch_order = fetch_config(['login', 'firstname', 'surname'])
-    
-    __rtags__ = { 'firstname'  : 'secondary',
-                  'surname'    : 'secondary',
-                  'last_login_time' : 'generated',
-                  'todo_by'    : 'create',
-                  'use_email'  : 'inlineview', # 'primary',
-                  'in_state'   : 'primary', 
-                  'in_group'   : 'primary', 
-                  ('owned_by', '*', 'object') : ('generated', 'link'),
-                  ('created_by','*','object') : ('generated', 'link'),
-                  ('bookmarked_by', '*', 'object'): ('generated', 'create'),
-                  }
-    
+    fetch_unrelated_order = fetch_order
+
     # used by repository to check if  the user can log in or not
     AUTHENTICABLE_STATES = ('activated',)
 
@@ -43,12 +32,12 @@
     def __init__(self, *args, **kwargs):
         groups = kwargs.pop('groups', None)
         properties = kwargs.pop('properties', None)
-        super(EUser, self).__init__(*args, **kwargs)
+        super(CWUser, self).__init__(*args, **kwargs)
         if groups is not None:
             self._groups = groups
         if properties is not None:
             self._properties = properties
-            
+
     @property
     def groups(self):
         try:
@@ -56,7 +45,7 @@
         except AttributeError:
             self._groups = set(g.name for g in self.in_group)
             return self._groups
-        
+
     @property
     def properties(self):
         try:
@@ -75,7 +64,7 @@
         except ValueError:
             self.warning('incorrect value for eproperty %s of user %s', key, self.login)
         return self.vreg.property_value(key)
-    
+
     def matching_groups(self, groups):
         """return the number of the given group(s) in which the user is
 
@@ -97,13 +86,13 @@
         """ checks if user is an anonymous user"""
         #FIXME on the web-side anonymous user is detected according
         # to config['anonymous-user'], we don't have this info on
-        # the server side. 
+        # the server side.
         return self.groups == frozenset(('guests', ))
 
     def owns(self, eid):
         if hasattr(self.req, 'unsafe_execute'):
             # use unsafe_execute on the repository side, in case
-            # session's user doesn't have access to EUser
+            # session's user doesn't have access to CWUser
             execute = self.req.unsafe_execute
         else:
             execute = self.req.execute
@@ -115,7 +104,7 @@
     owns = cached(owns, keyarg=1)
 
     def has_permission(self, pname, contexteid=None):
-        rql = 'Any P WHERE P is EPermission, U eid %(u)s, U in_group G, '\
+        rql = 'Any P WHERE P is CWPermission, U eid %(u)s, U in_group G, '\
               'P name %(pname)s, P require_group G'
         kwargs = {'pname': pname, 'u': self.eid}
         cachekey = None
@@ -127,12 +116,12 @@
             return self.req.execute(rql, kwargs, cachekey)
         except Unauthorized:
             return False
-    
+
     # presentation utilities ##################################################
-    
+
     def name(self):
         """construct a name using firstname / surname or login if not defined"""
-        
+
         if self.firstname and self.surname:
             return self.req._('%(firstname)s %(surname)s') % {
                 'firstname': self.firstname, 'surname' : self.surname}
@@ -150,5 +139,5 @@
         return self.get('login')
 
 from logilab.common.deprecation import class_renamed
-Euser = class_renamed('Euser', EUser)
-Euser.id = 'Euser'
+EUser = class_renamed('EUser', CWUser)
+EGroup = class_renamed('EGroup', CWGroup)
--- a/entities/lib.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/entities/lib.py	Mon May 04 13:09:48 2009 +0200
@@ -7,11 +7,12 @@
 __docformat__ = "restructuredtext en"
 
 from urlparse import urlsplit, urlunsplit
-from mx.DateTime import now
+from datetime import datetime
 
 from logilab.common.decorators import cached
 
-from cubicweb.common.entity import _marker
+from cubicweb import UnknownProperty
+from cubicweb.entity import _marker
 from cubicweb.entities import AnyEntity, fetch_config
 
 def mangle_email(address):
@@ -25,19 +26,15 @@
     id = 'EmailAddress'
     fetch_attrs, fetch_order = fetch_config(['address', 'alias', 'canonical'])
 
-    widgets = {
-        'address' : "EmailWidget",
-        }
-
     def dc_title(self):
         if self.alias:
             return '%s <%s>' % (self.alias, self.display_address())
         return self.display_address()
-    
+
     @property
     def email_of(self):
         return self.reverse_use_email and self.reverse_use_email[0]
-    
+
     @cached
     def canonical_form(self):
         if self.canonical:
@@ -91,39 +88,32 @@
 Emailaddress.id = 'Emailaddress'
 
 
-class EProperty(AnyEntity):
-    id = 'EProperty'
+class CWProperty(AnyEntity):
+    id = 'CWProperty'
 
     fetch_attrs, fetch_order = fetch_config(['pkey', 'value'])
-
-    widgets = {
-        'pkey' : "PropertyKeyWidget",
-        'value' : "PropertyValueWidget",
-        }
-    
     rest_attr = 'pkey'
 
     def typed_value(self):
         return self.vreg.typed_value(self.pkey, self.value)
-        
+
     def dc_description(self):
-        return self.req._(self.vreg.property_info(self.pkey)['help'])
+        try:
+            return self.req._(self.vreg.property_info(self.pkey)['help'])
+        except UnknownProperty:
+            return u''
 
     def after_deletion_path(self):
         """return (path, parameters) which should be used as redirect
         information when this entity is being deleted
         """
         return 'view', {}
-        
+
 
 class Bookmark(AnyEntity):
     """customized class for Bookmark entities"""
     id = 'Bookmark'
     fetch_attrs, fetch_order = fetch_config(['title', 'path'])
-    widgets = {
-        'path' : "StringWidget",
-        }
-    __rtags__ = {'path': 'primary'}
 
     def actual_url(self):
         url = self.req.build_url(self.path)
@@ -139,14 +129,15 @@
     def action_url(self):
         return self.absolute_url() + '/follow'
 
-class ECache(AnyEntity):
+
+class CWCache(AnyEntity):
     """Cache"""
-    id = 'ECache'
-    
+    id = 'CWCache'
     fetch_attrs, fetch_order = fetch_config(['name'])
 
     def touch(self):
-        self.req.execute('SET X timestamp %(t)s WHERE X eid %(x)s', {'t': now(), 'x': self.eid}, 'x')
+        self.req.execute('SET X timestamp %(t)s WHERE X eid %(x)s',
+                         {'t': datetime.now(), 'x': self.eid}, 'x')
 
     def valid(self, date):
         return date < self.timestamp
--- a/entities/schemaobjs.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/entities/schemaobjs.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """schema definition related entities
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -14,17 +14,10 @@
 from cubicweb.entities import AnyEntity, fetch_config
 
 
-class EEType(AnyEntity):
-    id = 'EEType'
+class CWEType(AnyEntity):
+    id = 'CWEType'
     fetch_attrs, fetch_order = fetch_config(['name'])
-    __rtags__ = {
-        ('final',         '*', 'subject'): 'generated',
-        
-        ('state_of',      '*', 'object'): 'create',
-        ('transition_of', '*', 'object'): 'create',
-        ('from_entity',   '*', 'object'): 'link',
-        ('to_entity',     '*', 'object'): 'link',
-        }
+
     def dc_title(self):
         return self.req._(self.name)
     
@@ -44,14 +37,9 @@
         return self.get('name')
 
 
-class ERType(AnyEntity):
-    id = 'ERType'
+class CWRType(AnyEntity):
+    id = 'CWRType'
     fetch_attrs, fetch_order = fetch_config(['name'])
-    __rtags__ = {
-        ('final',         '*', 'subject'): 'generated',
-        
-        ('relation_type', '*', 'object') : 'create',
-        }
     
     def dc_title(self):
         return self.req._(self.name)
@@ -97,14 +85,9 @@
         return self.get('name')
 
 
-class ENFRDef(AnyEntity):
-    id = 'ENFRDef'
+class CWRelation(AnyEntity):
+    id = 'CWRelation'
     fetch_attrs = fetch_config(['cardinality'])[0]
-    __rtags__ = {
-        ('relation_type', 'ERType', 'subject') : 'inlineview',
-        ('from_entity', 'EEType', 'subject') : 'inlineview',
-        ('to_entity', 'EEType', 'subject') : 'inlineview',
-        }
     
     def dc_title(self):
         return u'%s %s %s' % (
@@ -130,11 +113,11 @@
         """
         if self.relation_type:
             return self.relation_type[0].rest_path(), {}
-        return super(ENFRDef, self).after_deletion_path()
+        return super(CWRelation, self).after_deletion_path()
 
 
-class EFRDef(ENFRDef):
-    id = 'EFRDef'
+class CWAttribute(CWRelation):
+    id = 'CWAttribute'
     
     def dc_long_title(self):
         card = self.cardinality
@@ -147,8 +130,8 @@
             self.to_entity[0].name)
 
 
-class EConstraint(AnyEntity):
-    id = 'EConstraint'
+class CWConstraint(AnyEntity):
+    id = 'CWConstraint'
     fetch_attrs, fetch_order = fetch_config(['value'])
 
     def dc_title(self):
@@ -160,7 +143,7 @@
         """
         if self.reverse_constrained_by:
             return self.reverse_constrained_by[0].rest_path(), {}
-        return super(EConstraint, self).after_deletion_path()
+        return super(CWConstraint, self).after_deletion_path()
 
     @property
     def type(self):
@@ -171,10 +154,6 @@
     id = 'RQLExpression'
     fetch_attrs, fetch_order = fetch_config(['exprtype', 'mainvars', 'expression'])
 
-    widgets = {
-        'expression' : "StringWidget",
-        }
-
     def dc_title(self):
         return '%s(%s)' % (self.exprtype, self.expression or u'')
 
@@ -205,15 +184,10 @@
         return super(RQLExpression, self).after_deletion_path()
 
 
-class EPermission(AnyEntity):
-    id = 'EPermission'
+class CWPermission(AnyEntity):
+    id = 'CWPermission'
     fetch_attrs, fetch_order = fetch_config(['name', 'label'])
 
-
-    __rtags__ = {
-        'require_group' : 'primary',
-        }
-
     def dc_title(self):
         if self.label:
             return '%s (%s)' % (self.req._(self.name), self.label)
@@ -226,4 +200,4 @@
         permissionof = getattr(self, 'reverse_require_permission', ())
         if len(permissionof) == 1:
             return permissionof[0].rest_path(), {}
-        return super(EPermission, self).after_deletion_path()
+        return super(CWPermission, self).after_deletion_path()
--- a/entities/test/unittest_base.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/entities/test/unittest_base.py	Mon May 04 13:09:48 2009 +0200
@@ -1,8 +1,6 @@
 # -*- coding: utf-8 -*-
 """unit tests for cubicweb.entities.base module"""
 
-from mx.DateTime import now
-
 from logilab.common.testlib import unittest_main
 from logilab.common.decorators import clear_cache
 from logilab.common.interface import implements
@@ -12,7 +10,7 @@
 from cubicweb import ValidationError
 from cubicweb.interfaces import IMileStone, IWorkflowable
 from cubicweb.entities import AnyEntity
-from cubicweb.entities.authobjs import EUser
+from cubicweb.entities.authobjs import CWUser
 from cubicweb.web.widgets import AutoCompletionWidget
 
 
@@ -34,37 +32,24 @@
 
     def test_type(self):
         self.assertEquals(self.member.dc_type(), 'euser')
+        
 
-    def test_custom_widget(self):
-        class EUser2(EUser):
-            widgets = {
-                'login' : 'AutoCompletionWidget',
-                }
-        clear_cache(self.vreg, 'etype_class')
-        self.vreg.register_vobject_class(EUser2)
-        p = self.entity('EUser U WHERE U login "member"')
-        self.failUnless(isinstance(p, EUser2))
-        w = p.get_widget('login')
-        self.failUnless(isinstance(w, AutoCompletionWidget))
-
-    def test_format_vocabulary(self):
-        card = self.add_entity('Card', title=u"hello")
-        self.assertEquals(card.default_content_format(), 'text/html')
-        self.execute('INSERT EProperty X: X pkey "ui.default-text-format", X value "text/rest", X for_user U WHERE U login "admin"')
-        self.commit()
-        self.assertEquals(card.default_content_format(), 'text/rest')
+    def test_entity_meta_attributes(self):
+        # XXX move to yams
+        self.assertEquals(self.schema['CWUser'].meta_attributes(), {})
+        self.assertEquals(dict((str(k), v) for k, v in self.schema['Card'].meta_attributes().iteritems()),
+                          {'content_format': ('format', 'content')})
         
 
-
-class EUserTC(BaseEntityTC):
+class CWUserTC(BaseEntityTC):
     def test_dc_title_and_name(self):
-        e = self.entity('EUser U WHERE U login "member"')
+        e = self.entity('CWUser U WHERE U login "member"')
         self.assertEquals(e.dc_title(), 'member')
         self.assertEquals(e.name(), 'member')
-        self.execute(u'SET X firstname "bouah" WHERE X is EUser, X login "member"')
+        self.execute(u'SET X firstname "bouah" WHERE X is CWUser, X login "member"')
         self.assertEquals(e.dc_title(), 'member')
         self.assertEquals(e.name(), u'bouah')
-        self.execute(u'SET X surname "lôt" WHERE X is EUser, X login "member"')
+        self.execute(u'SET X surname "lôt" WHERE X is CWUser, X login "member"')
         self.assertEquals(e.dc_title(), 'member')
         self.assertEquals(e.name(), u'bouah lôt')
 
@@ -72,7 +57,7 @@
 class StateAndTransitionsTC(BaseEntityTC):
         
     def test_transitions(self):
-        user = self.entity('EUser X')
+        user = self.entity('CWUser X')
         e = self.entity('State S WHERE S name "activated"')
         trs = list(e.transitions(user))
         self.assertEquals(len(trs), 1)
@@ -86,12 +71,12 @@
         e = self.entity('State S WHERE S name "activated"')
         trs = list(e.transitions(user))
         self.assertEquals(len(trs), 0)
-        user = self.entity('EUser X')
+        user = self.entity('CWUser X')
         self.assert_(not user.can_pass_transition('deactivate'))
         self.assert_(not user.can_pass_transition('activate'))
         
     def test_transitions_with_dest_specfied(self):
-        user = self.entity('EUser X')
+        user = self.entity('CWUser X')
         e = self.entity('State S WHERE S name "activated"')
         e2 = self.entity('State S WHERE S name "deactivated"')
         trs = list(e.transitions(user, e2.eid))
@@ -147,9 +132,9 @@
         state3 = self.add_entity('State', name=u'state3')
         tr1 = self.add_entity('Transition', name=u'tr1')
         tr2 = self.add_entity('Transition', name=u'tr2')
-        self.execute('SET X state_of Y WHERE X eid in (%s, %s), Y is EEType, Y name "Card"' %
+        self.execute('SET X state_of Y WHERE X eid in (%s, %s), Y is CWEType, Y name "Card"' %
                       (state1.eid, state2.eid))
-        self.execute('SET X state_of Y WHERE X eid in (%s, %s), Y is EEType, Y name "Bookmark"' %
+        self.execute('SET X state_of Y WHERE X eid in (%s, %s), Y is CWEType, Y name "Bookmark"' %
                       (state1.eid, state3.eid))
         self.execute('SET X transition_of Y WHERE X eid %s, Y name "Card"' % tr1.eid)
         self.execute('SET X transition_of Y WHERE X eid %s, Y name "Bookmark"' % tr2.eid)
@@ -186,9 +171,9 @@
         state2 = self.add_entity('State', name=u'state2')
         tr1 = self.add_entity('Transition', name=u'tr1')
         tr2 = self.add_entity('Transition', name=u'tr2')
-        self.execute('SET X state_of Y WHERE X eid in (%s, %s), Y is EEType, Y name "Card"' %
+        self.execute('SET X state_of Y WHERE X eid in (%s, %s), Y is CWEType, Y name "Card"' %
                       (state1.eid, state2.eid))
-        self.execute('SET X state_of Y WHERE X eid in (%s, %s), Y is EEType, Y name "Bookmark"' %
+        self.execute('SET X state_of Y WHERE X eid in (%s, %s), Y is CWEType, Y name "Bookmark"' %
                       (state1.eid, state2.eid))
         self.execute('SET X transition_of Y WHERE X eid %s, Y name "Card"' % tr1.eid)
         self.execute('SET X transition_of Y WHERE X eid %s, Y name "Bookmark"' % tr2.eid)
@@ -240,34 +225,20 @@
         self.assertEquals(email.printable_value('address'), 'syt')
 
 
-class EUserTC(BaseEntityTC):
+class CWUserTC(BaseEntityTC):
     
     def test_complete(self):
-        e = self.entity('EUser X WHERE X login "admin"')
+        e = self.entity('CWUser X WHERE X login "admin"')
         e.complete()
 
         
     def test_matching_groups(self):
-        e = self.entity('EUser X WHERE X login "admin"')
+        e = self.entity('CWUser X WHERE X login "admin"')
         self.failUnless(e.matching_groups('managers'))
         self.failIf(e.matching_groups('xyz'))
         self.failUnless(e.matching_groups(('xyz', 'managers')))
         self.failIf(e.matching_groups(('xyz', 'abcd')))
 
-    def test_subject_in_state_vocabulary(self):
-        # on a new entity
-        e = self.etype_instance('EUser')
-        rschema = e.e_schema.subject_relation('in_state')
-        states = list(e.subject_in_state_vocabulary(rschema))
-        self.assertEquals(len(states), 1)
-        self.assertEquals(states[0][0], u'activated') # list of (combobox view, state eid)
-        # on an existant entity
-        e = self.entity('Any X WHERE X is EUser')
-        self.assertEquals(e.in_state[0].name, 'activated')
-        states = list(e.subject_in_state_vocabulary(rschema))
-        self.assertEquals(len(states), 1)
-        self.assertEquals(states[0][0], u'deactivated') # list of (combobox view, state eid)
-
     def test_workflow_base(self):
         e = self.create_user('toto')
         self.assertEquals(e.state, 'activated')
@@ -280,7 +251,7 @@
         e.change_state(deactivatedeid, u'deactivate 2')
         self.commit()
         # get a fresh user to avoid potential cache issues
-        e = self.entity('EUser X WHERE X eid %s' % e.eid)
+        e = self.entity('CWUser X WHERE X eid %s' % e.eid)
         self.assertEquals([tr.comment for tr in e.reverse_wf_info_for],
                           [None, 'deactivate 1', 'activate 1', 'deactivate 2'])
         self.assertEquals(e.latest_trinfo().comment, 'deactivate 2')
@@ -289,10 +260,11 @@
 class InterfaceTC(EnvBasedTC):
 
     def test_nonregr_subclasses_and_mixins_interfaces(self):
-        class MyUser(EUser):
+        class MyUser(CWUser):
             __implements__ = (IMileStone,)
+        self.vreg._loadedmods[__name__] = {}
         self.vreg.register_vobject_class(MyUser)
-        self.failUnless(implements(EUser, IWorkflowable))
+        self.failUnless(implements(CWUser, IWorkflowable))
         self.failUnless(implements(MyUser, IMileStone))
         self.failUnless(implements(MyUser, IWorkflowable))
 
@@ -311,6 +283,7 @@
         #self.assertEquals(eclass.__bases__, (AnyEntity,))
         # build class from most generic to most specific and make
         # sure the most specific is always selected
+        self.vreg._loadedmods[__name__] = {}
         for etype in ('Company', 'Division', 'SubDivision'):
             class Foo(AnyEntity):
                 id = etype
--- a/entities/wfobjs.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/entities/wfobjs.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """workflow definition and history related entities
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -17,10 +17,7 @@
     """
     id = 'Transition'
     fetch_attrs, fetch_order = fetch_config(['name'])
-    __rtags__ = {('destination_state',  '*', 'subject'):  'create',
-                 ('allowed_transition', '*', 'object') :  'create',
-                  }
-                 
+
     def may_be_passed(self, eid, stateeid):
         """return true if the logged user may pass this transition
 
@@ -47,7 +44,7 @@
 
     def destination(self):
         return self.destination_state[0]
-    
+
     def after_deletion_path(self):
         """return (path, parameters) which should be used as redirect
         information when this entity is being deleted
@@ -56,7 +53,7 @@
             return self.transition_of[0].rest_path(), {'vid': 'workflow'}
         return super(Transition, self).after_deletion_path()
 
-    
+
 class State(AnyEntity):
     """customized class for State entities
 
@@ -66,11 +63,7 @@
     id = 'State'
     fetch_attrs, fetch_order = fetch_config(['name'])
     rest_attr = 'eid'
-    
-    __rtags__ = {'destination_state' : 'create',
-                 'allowed_transition' : 'create'
-                 }
-    
+
     def transitions(self, entity, desteid=None):
         rql = ('Any T,N,DS where S allowed_transition T, S eid %(x)s, '
                'T name N, T destination_state DS, '
@@ -82,7 +75,7 @@
         for tr in rset.entities():
             if tr.may_be_passed(entity.eid, self.eid):
                 yield tr
-                
+
     def after_deletion_path(self):
         """return (path, parameters) which should be used as redirect
         information when this entity is being deleted
@@ -91,7 +84,7 @@
             return self.state_of[0].rest_path(), {'vid': 'workflow'}
         return super(State, self).after_deletion_path()
 
-    
+
 class TrInfo(AnyEntity):
     """customized class for Transition information entities
     """
@@ -104,7 +97,7 @@
     @property
     def previous_state(self):
         return self.from_state and self.from_state[0]
-    
+
     @property
     def new_state(self):
         return self.to_state[0]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/entity.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,963 @@
+"""Base class for entity objects manipulated in clients
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from warnings import warn
+
+from logilab.common import interface
+from logilab.common.compat import all
+from logilab.common.decorators import cached
+from logilab.common.deprecation import obsolete
+from logilab.mtconverter import TransformData, TransformError, html_escape
+
+from rql.utils import rqlvar_maker
+
+from cubicweb import Unauthorized
+from cubicweb.rset import ResultSet
+from cubicweb.selectors import yes
+from cubicweb.appobject import AppRsetObject
+from cubicweb.schema import RQLVocabularyConstraint, RQLConstraint, bw_normalize_etype
+
+try:
+    from cubicweb.common.uilib import printable_value, soup2xhtml
+    from cubicweb.common.mixins import MI_REL_TRIGGERS
+    from cubicweb.common.mttransforms import ENGINE
+except ImportError:
+    # missing -common
+    MI_REL_TRIGGERS = {}
+
+_marker = object()
+
+def greater_card(rschema, subjtypes, objtypes, index):
+    for subjtype in subjtypes:
+        for objtype in objtypes:
+            card = rschema.rproperty(subjtype, objtype, 'cardinality')[index]
+            if card in '+*':
+                return card
+    return '1'
+
+
+_MODE_TAGS = set(('link', 'create'))
+_CATEGORY_TAGS = set(('primary', 'secondary', 'generic', 'generated')) # , 'metadata'))
+
+try:
+    from cubicweb.web import formwidgets, uicfg
+    from cubicweb.web.views.editforms import AutomaticEntityForm
+
+    def _dispatch_rtags(tags, rtype, role, stype, otype):
+        for tag in tags:
+            if tag in _MODE_TAGS:
+                uicfg.rmode.tag_relation(tag, (stype, rtype, otype), role)
+            elif tag in _CATEGORY_TAGS:
+                uicfg.rcategories.tag_relation(tag, (stype, rtype, otype), role)
+            elif tag == 'inlineview':
+                uicfg.rinlined.tag_relation(True, (stype, rtype, otype), role)
+            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'):
+                        warn('%s widget is deprecated' % wdgname, DeprecationWarning)
+                        continue
+                    if wdgname == 'StringWidget':
+                        wdgname = 'TextInput'
+                    widget = getattr(formwidgets, wdgname)
+                    assert hasattr(widget, 'render')
+                    AutomaticEntityForm.rwidgets.tag_relation(
+                        widget, (etype, rtype, '*'), 'subject')
+        return super(_metaentity, mcs).__new__(mcs, name, bases, classdict)
+
+
+class Entity(AppRsetObject, dict):
+    """an entity instance has e_schema automagically set on
+    the class and instances has access to their issuing cursor.
+
+    A property is set for each attribute and relation on each entity's type
+    class. Becare that among attributes, 'eid' is *NEITHER* stored in the
+    dict containment (which acts as a cache for other attributes dynamically
+    fetched)
+
+    :type e_schema: `cubicweb.schema.EntitySchema`
+    :ivar e_schema: the entity's schema
+
+    :type rest_var: str
+    :cvar rest_var: indicates which attribute should be used to build REST urls
+                    If None is specified, the first non-meta attribute will
+                    be used
+
+    :type skip_copy_for: list
+    :cvar skip_copy_for: a list of relations that should be skipped when copying
+                         this kind of entity. Note that some relations such
+                         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
+
+    @classmethod
+    def registered(cls, registry):
+        """build class using descriptor at registration time"""
+        assert cls.id is not None
+        super(Entity, cls).registered(registry)
+        if cls.id != 'Any':
+            cls.__initialize__()
+        return cls
+
+    MODE_TAGS = set(('link', 'create'))
+    CATEGORY_TAGS = set(('primary', 'secondary', 'generic', 'generated')) # , 'metadata'))
+    @classmethod
+    def __initialize__(cls):
+        """initialize a specific entity class by adding descriptors to access
+        entity type's attributes and relations
+        """
+        etype = cls.id
+        assert etype != 'Any', etype
+        cls.e_schema = eschema = cls.schema.eschema(etype)
+        for rschema, _ in eschema.attribute_definitions():
+            if rschema.type == 'eid':
+                continue
+            setattr(cls, rschema.type, Attribute(rschema.type))
+        mixins = []
+        for rschema, _, x in eschema.relation_definitions():
+            if (rschema, x) in MI_REL_TRIGGERS:
+                mixin = MI_REL_TRIGGERS[(rschema, x)]
+                if not (issubclass(cls, mixin) or mixin in mixins): # already mixed ?
+                    mixins.append(mixin)
+                for iface in getattr(mixin, '__implements__', ()):
+                    if not interface.implements(cls, iface):
+                        interface.extend(cls, iface)
+            if x == 'subject':
+                setattr(cls, rschema.type, SubjectRelation(rschema))
+            else:
+                attr = 'reverse_%s' % rschema.type
+                setattr(cls, attr, ObjectRelation(rschema))
+        if mixins:
+            cls.__bases__ = tuple(mixins + [p for p in cls.__bases__ if not p is object])
+            cls.debug('plugged %s mixins on %s', mixins, etype)
+
+    @classmethod
+    def fetch_rql(cls, user, restriction=None, fetchattrs=None, mainvar='X',
+                  settype=True, ordermethod='fetch_order'):
+        """return a rql to fetch all entities of the class type"""
+        restrictions = restriction or []
+        if settype:
+            restrictions.append('%s is %s' % (mainvar, cls.id))
+        if fetchattrs is None:
+            fetchattrs = cls.fetch_attrs
+        selection = [mainvar]
+        orderby = []
+        # start from 26 to avoid possible conflicts with X
+        varmaker = rqlvar_maker(index=26)
+        cls._fetch_restrictions(mainvar, varmaker, fetchattrs, selection,
+                                orderby, restrictions, user, ordermethod)
+        rql = 'Any %s' % ','.join(selection)
+        if orderby:
+            rql +=  ' ORDERBY %s' % ','.join(orderby)
+        rql += ' WHERE %s' % ', '.join(restrictions)
+        return rql
+
+    @classmethod
+    def _fetch_restrictions(cls, mainvar, varmaker, fetchattrs,
+                            selection, orderby, restrictions, user,
+                            ordermethod='fetch_order', visited=None):
+        eschema = cls.e_schema
+        if visited is None:
+            visited = set((eschema.type,))
+        elif eschema.type in visited:
+            # avoid infinite recursion
+            return
+        else:
+            visited.add(eschema.type)
+        _fetchattrs = []
+        for attr in fetchattrs:
+            try:
+                rschema = eschema.subject_relation(attr)
+            except KeyError:
+                cls.warning('skipping fetch_attr %s defined in %s (not found in schema)',
+                            attr, cls.id)
+                continue
+            if not user.matching_groups(rschema.get_groups('read')):
+                continue
+            var = varmaker.next()
+            selection.append(var)
+            restriction = '%s %s %s' % (mainvar, attr, var)
+            restrictions.append(restriction)
+            if not rschema.is_final():
+                # XXX this does not handle several destination types
+                desttype = rschema.objects(eschema.type)[0]
+                card = rschema.rproperty(eschema, desttype, 'cardinality')[0]
+                if card not in '?1':
+                    selection.pop()
+                    restrictions.pop()
+                    continue
+                if card == '?':
+                    restrictions[-1] += '?' # left outer join if not mandatory
+                destcls = cls.vreg.etype_class(desttype)
+                destcls._fetch_restrictions(var, varmaker, destcls.fetch_attrs,
+                                            selection, orderby, restrictions,
+                                            user, ordermethod, visited=visited)
+            orderterm = getattr(cls, ordermethod)(attr, var)
+            if orderterm:
+                orderby.append(orderterm)
+        return selection, orderby, restrictions
+
+    @classmethod
+    @cached
+    def parent_classes(cls):
+        parents = [cls.vreg.etype_class(e.type) for e in cls.e_schema.ancestors()]
+        parents.append(cls.vreg.etype_class('Any'))
+        return parents
+
+    def __init__(self, req, rset=None, row=None, col=0):
+        AppRsetObject.__init__(self, req, rset, row, col)
+        dict.__init__(self)
+        self._related_cache = {}
+        if rset is not None:
+            self.eid = rset[row][col]
+        else:
+            self.eid = None
+        self._is_saved = True
+
+    def __repr__(self):
+        return '<Entity %s %s %s at %s>' % (
+            self.e_schema, self.eid, self.keys(), id(self))
+
+    def __nonzero__(self):
+        return True
+
+    def __hash__(self):
+        return id(self)
+
+    def pre_add_hook(self):
+        """hook called by the repository before doing anything to add the entity
+        (before_add entity hooks have not been called yet). This give the
+        occasion to do weird stuff such as autocast (File -> Image for instance).
+
+        This method must return the actual entity to be added.
+        """
+        return self
+
+    def set_eid(self, eid):
+        self.eid = self['eid'] = eid
+
+    def has_eid(self):
+        """return True if the entity has an attributed eid (False
+        meaning that the entity has to be created
+        """
+        try:
+            int(self.eid)
+            return True
+        except (ValueError, TypeError):
+            return False
+
+    def is_saved(self):
+        """during entity creation, there is some time during which the entity
+        has an eid attributed though it's not saved (eg during before_add_entity
+        hooks). You can use this method to ensure the entity has an eid *and* is
+        saved in its source.
+        """
+        return self.has_eid() and self._is_saved
+
+    @cached
+    def metainformation(self):
+        res = dict(zip(('type', 'source', 'extid'), self.req.describe(self.eid)))
+        res['source'] = self.req.source_defs()[res['source']]
+        return res
+
+    def clear_local_perm_cache(self, action):
+        for rqlexpr in self.e_schema.get_rqlexprs(action):
+            self.req.local_perm_cache.pop((rqlexpr.eid, (('x', self.eid),)), None)
+
+    def check_perm(self, action):
+        self.e_schema.check_perm(self.req, action, self.eid)
+
+    def has_perm(self, action):
+        return self.e_schema.has_perm(self.req, action, self.eid)
+
+    def view(self, vid, __registry='views', **kwargs):
+        """shortcut to apply a view on this entity"""
+        return self.vreg.render(__registry, vid, self.req, rset=self.rset,
+                                row=self.row, col=self.col, **kwargs)
+
+    def absolute_url(self, method=None, **kwargs):
+        """return an absolute url to view this entity"""
+        # in linksearch mode, we don't want external urls else selecting
+        # the object for use in the relation is tricky
+        # XXX search_state is web specific
+        if getattr(self.req, 'search_state', ('normal',))[0] == 'normal':
+            kwargs['base_url'] = self.metainformation()['source'].get('base-url')
+        if method is None or method == 'view':
+            kwargs['_restpath'] = self.rest_path()
+        else:
+            kwargs['rql'] = 'Any X WHERE X eid %s' % self.eid
+        return self.build_url(method, **kwargs)
+
+    def rest_path(self):
+        """returns a REST-like (relative) path for this entity"""
+        mainattr, needcheck = self._rest_attr_info()
+        etype = str(self.e_schema)
+        if mainattr == 'eid':
+            value = self.eid
+        else:
+            value = getattr(self, mainattr)
+            if value is None:
+                return '%s/eid/%s' % (etype.lower(), self.eid)
+        if needcheck:
+            # make sure url is not ambiguous
+            rql = 'Any COUNT(X) WHERE X is %s, X %s %%(value)s' % (etype, mainattr)
+            if value is not None:
+                nbresults = self.req.execute(rql, {'value' : value})[0][0]
+                # may an assertion that nbresults is not 0 would be a good idea
+                if nbresults != 1: # no ambiguity
+                    return '%s/eid/%s' % (etype.lower(), self.eid)
+        return '%s/%s' % (etype.lower(), self.req.url_quote(value))
+
+    @classmethod
+    def _rest_attr_info(cls):
+        mainattr, needcheck = 'eid', True
+        if cls.rest_attr:
+            mainattr = cls.rest_attr
+            needcheck = not cls.e_schema.has_unique_values(mainattr)
+        else:
+            for rschema in cls.e_schema.subject_relations():
+                if rschema.is_final() and rschema != 'eid' and cls.e_schema.has_unique_values(rschema):
+                    mainattr = str(rschema)
+                    needcheck = False
+                    break
+        if mainattr == 'eid':
+            needcheck = False
+        return mainattr, needcheck
+
+    def attr_metadata(self, attr, metadata):
+        """return a metadata for an attribute (None if unspecified)"""
+        value = getattr(self, '%s_%s' % (attr, metadata), None)
+        if value is None and metadata == 'encoding':
+            value = self.vreg.property_value('ui.encoding')
+        return value
+
+    def printable_value(self, attr, value=_marker, attrtype=None,
+                        format='text/html', displaytime=True):
+        """return a displayable value (i.e. unicode string) which may contains
+        html tags
+        """
+        attr = str(attr)
+        if value is _marker:
+            value = getattr(self, attr)
+        if isinstance(value, basestring):
+            value = value.strip()
+        if value is None or value == '': # don't use "not", 0 is an acceptable value
+            return u''
+        if attrtype is None:
+            attrtype = self.e_schema.destination(attr)
+        props = self.e_schema.rproperties(attr)
+        if attrtype == 'String':
+            # internalinalized *and* formatted string such as schema
+            # description...
+            if props.get('internationalizable'):
+                value = self.req._(value)
+            attrformat = self.attr_metadata(attr, 'format')
+            if attrformat:
+                return self.mtc_transform(value, attrformat, format,
+                                          self.req.encoding)
+        elif attrtype == 'Bytes':
+            attrformat = self.attr_metadata(attr, 'format')
+            if attrformat:
+                encoding = self.attr_metadata(attr, 'encoding')
+                return self.mtc_transform(value.getvalue(), attrformat, format,
+                                          encoding)
+            return u''
+        value = printable_value(self.req, attrtype, value, props, displaytime)
+        if format == 'text/html':
+            value = html_escape(value)
+        return value
+
+    def mtc_transform(self, data, format, target_format, encoding,
+                      _engine=ENGINE):
+        trdata = TransformData(data, format, encoding, appobject=self)
+        data = _engine.convert(trdata, target_format).decode()
+        if format == 'text/html':
+            data = soup2xhtml(data, self.req.encoding)
+        return data
+
+    # entity cloning ##########################################################
+
+    def copy_relations(self, ceid):
+        """copy relations of the object with the given eid on this object
+
+        By default meta and composite relations are skipped.
+        Overrides this if you want another behaviour
+        """
+        assert self.has_eid()
+        execute = self.req.execute
+        for rschema in self.e_schema.subject_relations():
+            if rschema.meta or rschema.is_final():
+                continue
+            # skip already defined relations
+            if getattr(self, rschema.type):
+                continue
+            if rschema.type in self.skip_copy_for:
+                continue
+            if rschema.type == 'in_state':
+                # if the workflow is defining an initial state (XXX AND we are
+                # not in the managers group? not done to be more consistent)
+                # don't try to copy in_state
+                if execute('Any S WHERE S state_of ET, ET initial_state S,'
+                           'ET name %(etype)s', {'etype': str(self.e_schema)}):
+                    continue
+            # skip composite relation
+            if self.e_schema.subjrproperty(rschema, 'composite'):
+                continue
+            # skip relation with card in ?1 else we either change the copied
+            # object (inlined relation) or inserting some inconsistency
+            if self.e_schema.subjrproperty(rschema, 'cardinality')[1] in '?1':
+                continue
+            rql = 'SET X %s V WHERE X eid %%(x)s, Y eid %%(y)s, Y %s V' % (
+                rschema.type, rschema.type)
+            execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
+            self.clear_related_cache(rschema.type, 'subject')
+        for rschema in self.e_schema.object_relations():
+            if rschema.meta:
+                continue
+            # skip already defined relations
+            if getattr(self, 'reverse_%s' % rschema.type):
+                continue
+            # skip composite relation
+            if self.e_schema.objrproperty(rschema, 'composite'):
+                continue
+            # skip relation with card in ?1 else we either change the copied
+            # object (inlined relation) or inserting some inconsistency
+            if self.e_schema.objrproperty(rschema, 'cardinality')[0] in '?1':
+                continue
+            rql = 'SET V %s X WHERE X eid %%(x)s, Y eid %%(y)s, V %s Y' % (
+                rschema.type, rschema.type)
+            execute(rql, {'x': self.eid, 'y': ceid}, ('x', 'y'))
+            self.clear_related_cache(rschema.type, 'object')
+
+    # data fetching methods ###################################################
+
+    @cached
+    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,)])
+        return self.req.decorate_rset(rset)
+
+    def to_complete_relations(self):
+        """by default complete final relations to when calling .complete()"""
+        for rschema in self.e_schema.subject_relations():
+            if rschema.is_final():
+                continue
+            if len(rschema.objects(self.e_schema)) > 1:
+                # ambigous relations, the querier doesn't handle
+                # outer join correctly in this case
+                continue
+            if rschema.inlined:
+                matching_groups = self.req.user.matching_groups
+                if matching_groups(rschema.get_groups('read')) and \
+                   all(matching_groups(es.get_groups('read'))
+                       for es in rschema.objects(self.e_schema)):
+                    yield rschema, 'subject'
+
+    def to_complete_attributes(self, skip_bytes=True):
+        for rschema, attrschema in self.e_schema.attribute_definitions():
+            # skip binary data by default
+            if skip_bytes and attrschema.type == 'Bytes':
+                continue
+            attr = rschema.type
+            if attr == 'eid':
+                continue
+            # password retreival is blocked at the repository server level
+            if not self.req.user.matching_groups(rschema.get_groups('read')) \
+                   or attrschema.type == 'Password':
+                self[attr] = None
+                continue
+            yield attr
+
+    def complete(self, attributes=None, skip_bytes=True):
+        """complete this entity by adding missing attributes (i.e. query the
+        repository to fill the entity)
+
+        :type skip_bytes: bool
+        :param skip_bytes:
+          if true, attribute of type Bytes won't be considered
+        """
+        assert self.has_eid()
+        varmaker = rqlvar_maker()
+        V = varmaker.next()
+        rql = ['WHERE %s eid %%(x)s' % V]
+        selected = []
+        for attr in (attributes or self.to_complete_attributes(skip_bytes)):
+            # if attribute already in entity, nothing to do
+            if self.has_key(attr):
+                continue
+            # case where attribute must be completed, but is not yet in entity
+            var = varmaker.next()
+            rql.append('%s %s %s' % (V, attr, var))
+            selected.append((attr, var))
+        # +1 since this doen't include the main variable
+        lastattr = len(selected) + 1
+        if attributes is None:
+            # fetch additional relations (restricted to 0..1 relations)
+            for rschema, role in self.to_complete_relations():
+                rtype = rschema.type
+                if self.relation_cached(rtype, role):
+                    continue
+                var = varmaker.next()
+                if role == 'subject':
+                    targettype = rschema.objects(self.e_schema)[0]
+                    card = rschema.rproperty(self.e_schema, targettype,
+                                             'cardinality')[0]
+                    if card == '1':
+                        rql.append('%s %s %s' % (V, rtype, var))
+                    else: # '?"
+                        rql.append('%s %s %s?' % (V, rtype, var))
+                else:
+                    targettype = rschema.subjects(self.e_schema)[1]
+                    card = rschema.rproperty(self.e_schema, targettype,
+                                             'cardinality')[1]
+                    if card == '1':
+                        rql.append('%s %s %s' % (var, rtype, V))
+                    else: # '?"
+                        rql.append('%s? %s %s' % (var, rtype, V))
+                assert card in '1?', '%s %s %s %s' % (self.e_schema, rtype,
+                                                      role, card)
+                selected.append(((rtype, role), var))
+        if selected:
+            # select V, we need it as the left most selected variable
+            # if some outer join are included to fetch inlined relations
+            rql = 'Any %s,%s %s' % (V, ','.join(var for attr, var in selected),
+                                    ','.join(rql))
+            execute = getattr(self.req, 'unsafe_execute', self.req.execute)
+            rset = execute(rql, {'x': self.eid}, 'x', build_descr=False)[0]
+            # handle attributes
+            for i in xrange(1, lastattr):
+                self[str(selected[i-1][0])] = rset[i]
+            # handle relations
+            for i in xrange(lastattr, len(rset)):
+                rtype, x = selected[i-1][0]
+                value = rset[i]
+                if value is None:
+                    rrset = ResultSet([], rql, {'x': self.eid})
+                    self.req.decorate_rset(rrset)
+                else:
+                    rrset = self.req.eid_rset(value)
+                self.set_related_cache(rtype, x, rrset)
+
+    def get_value(self, name):
+        """get value for the attribute relation <name>, query the repository
+        to get the value if necessary.
+
+        :type name: str
+        :param name: name of the attribute to get
+        """
+        try:
+            value = self[name]
+        except KeyError:
+            if not self.is_saved():
+                return None
+            rql = "Any A WHERE X eid %%(x)s, X %s A" % name
+            # XXX should we really use unsafe_execute here??
+            execute = getattr(self.req, 'unsafe_execute', self.req.execute)
+            try:
+                rset = execute(rql, {'x': self.eid}, 'x')
+            except Unauthorized:
+                self[name] = value = None
+            else:
+                assert rset.rowcount <= 1, (self, rql, rset.rowcount)
+                try:
+                    self[name] = value = rset.rows[0][0]
+                except IndexError:
+                    # probably a multisource error
+                    self.critical("can't get value for attribute %s of entity with eid %s",
+                                  name, self.eid)
+                    if self.e_schema.destination(name) == 'String':
+                        self[name] = value = self.req._('unaccessible')
+                    else:
+                        self[name] = value = None
+        return value
+
+    def related(self, rtype, role='subject', limit=None, entities=False):
+        """returns a resultset of related entities
+
+        :param role: is the role played by 'self' in the relation ('subject' or 'object')
+        :param limit: resultset's maximum size
+        :param entities: if True, the entites are returned; if False, a result set is returned
+        """
+        try:
+            return self.related_cache(rtype, role, entities, limit)
+        except KeyError:
+            pass
+        assert self.has_eid()
+        rql = self.related_rql(rtype, role)
+        rset = self.req.execute(rql, {'x': self.eid}, 'x')
+        self.set_related_cache(rtype, role, rset)
+        return self.related(rtype, role, limit, entities)
+
+    def related_rql(self, rtype, role='subject'):
+        rschema = self.schema[rtype]
+        if role == 'subject':
+            targettypes = rschema.objects(self.e_schema)
+            restriction = 'E eid %%(x)s, E %s X' % rtype
+            card = greater_card(rschema, (self.e_schema,), targettypes, 0)
+        else:
+            targettypes = rschema.subjects(self.e_schema)
+            restriction = 'E eid %%(x)s, X %s E' % rtype
+            card = greater_card(rschema, targettypes, (self.e_schema,), 1)
+        if len(targettypes) > 1:
+            fetchattrs_list = []
+            for ttype in targettypes:
+                etypecls = self.vreg.etype_class(ttype)
+                fetchattrs_list.append(set(etypecls.fetch_attrs))
+            fetchattrs = reduce(set.intersection, fetchattrs_list)
+            rql = etypecls.fetch_rql(self.req.user, [restriction], fetchattrs,
+                                     settype=False)
+        else:
+            etypecls = self.vreg.etype_class(targettypes[0])
+            rql = etypecls.fetch_rql(self.req.user, [restriction], settype=False)
+        # optimisation: remove ORDERBY if cardinality is 1 or ? (though
+        # greater_card return 1 for those both cases)
+        if card == '1':
+            if ' ORDERBY ' in rql:
+                rql = '%s WHERE %s' % (rql.split(' ORDERBY ', 1)[0],
+                                       rql.split(' WHERE ', 1)[1])
+        elif not ' ORDERBY ' in rql:
+            args = tuple(rql.split(' WHERE ', 1))
+            rql = '%s ORDERBY Z DESC WHERE X modification_date Z, %s' % args
+        return rql
+
+    # generic vocabulary methods ##############################################
+
+    @obsolete('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 cubicweb.web.form import EntityFieldsForm
+        from logilab.common.testlib import mock_object
+        form = EntityFieldsForm(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
+        using (rtype, role) relation
+        """
+        ordermethod = ordermethod or 'fetch_unrelated_order'
+        if isinstance(rtype, basestring):
+            rtype = self.schema.rschema(rtype)
+        if role == 'subject':
+            evar, searchedvar = 'S', 'O'
+            subjtype, objtype = self.e_schema, targettype
+        else:
+            searchedvar, evar = 'S', 'O'
+            objtype, subjtype = self.e_schema, targettype
+        if self.has_eid():
+            restriction = ['NOT S %s O' % rtype, '%s eid %%(x)s' % evar]
+        else:
+            restriction = []
+        constraints = rtype.rproperty(subjtype, objtype, 'constraints')
+        if vocabconstraints:
+            # RQLConstraint is a subclass for RQLVocabularyConstraint, so they
+            # will be included as well
+            restriction += [cstr.restriction for cstr in constraints
+                            if isinstance(cstr, RQLVocabularyConstraint)]
+        else:
+            restriction += [cstr.restriction for cstr in constraints
+                            if isinstance(cstr, RQLConstraint)]
+        etypecls = self.vreg.etype_class(targettype)
+        rql = etypecls.fetch_rql(self.req.user, restriction,
+                                 mainvar=searchedvar, ordermethod=ordermethod)
+        # ensure we have an order defined
+        if not ' ORDERBY ' in rql:
+            before, after = rql.split(' WHERE ', 1)
+            rql = '%s ORDERBY %s WHERE %s' % (before, searchedvar, after)
+        return rql
+
+    def unrelated(self, rtype, targettype, role='subject', limit=None,
+                  ordermethod=None):
+        """return a result set of target type objects that may be related
+        by a given relation, with self as subject or object
+        """
+        rql = self.unrelated_rql(rtype, targettype, role, ordermethod)
+        if limit is not None:
+            before, after = rql.split(' WHERE ', 1)
+            rql = '%s LIMIT %s WHERE %s' % (before, limit, after)
+        if self.has_eid():
+            return self.req.execute(rql, {'x': self.eid})
+        return self.req.execute(rql)
+
+    # relations cache handling ################################################
+
+    def relation_cached(self, rtype, role):
+        """return true if the given relation is already cached on the instance
+        """
+        return '%s_%s' % (rtype, role) in self._related_cache
+
+    def related_cache(self, rtype, role, entities=True, limit=None):
+        """return values for the given relation if it's cached on the instance,
+        else raise `KeyError`
+        """
+        res = self._related_cache['%s_%s' % (rtype, role)][entities]
+        if limit:
+            if entities:
+                res = res[:limit]
+            else:
+                res = res.limit(limit)
+        return res
+
+    def set_related_cache(self, rtype, role, rset, col=0):
+        """set cached values for the given relation"""
+        if rset:
+            related = list(rset.entities(col))
+            rschema = self.schema.rschema(rtype)
+            if role == 'subject':
+                rcard = rschema.rproperty(self.e_schema, related[0].e_schema,
+                                          'cardinality')[1]
+                target = 'object'
+            else:
+                rcard = rschema.rproperty(related[0].e_schema, self.e_schema,
+                                          'cardinality')[0]
+                target = 'subject'
+            if rcard in '?1':
+                for rentity in related:
+                    rentity._related_cache['%s_%s' % (rtype, target)] = (self.as_rset(), [self])
+        else:
+            related = []
+        self._related_cache['%s_%s' % (rtype, role)] = (rset, related)
+
+    def clear_related_cache(self, rtype=None, role=None):
+        """clear cached values for the given relation or the entire cache if
+        no relation is given
+        """
+        if rtype is None:
+            self._related_cache = {}
+        else:
+            assert role
+            self._related_cache.pop('%s_%s' % (rtype, role), None)
+
+    # raw edition utilities ###################################################
+
+    def set_attributes(self, **kwargs):
+        assert kwargs
+        relations = []
+        for key in kwargs:
+            relations.append('X %s %%(%s)s' % (key, key))
+        # update current local object
+        self.update(kwargs)
+        # and now update the database
+        kwargs['x'] = self.eid
+        self.req.execute('SET %s WHERE X eid %%(x)s' % ','.join(relations),
+                         kwargs, 'x')
+
+    def delete(self):
+        assert self.has_eid(), self.eid
+        self.req.execute('DELETE %s X WHERE X eid %%(x)s' % self.e_schema,
+                         {'x': self.eid})
+
+    # server side utilities ###################################################
+
+    def set_defaults(self):
+        """set default values according to the schema"""
+        self._default_set = set()
+        for attr, value in self.e_schema.defaults():
+            if not self.has_key(attr):
+                self[str(attr)] = value
+                self._default_set.add(attr)
+
+    def check(self, creation=False):
+        """check this entity against its schema. Only final relation
+        are checked here, constraint on actual relations are checked in hooks
+        """
+        # necessary since eid is handled specifically and yams require it to be
+        # in the dictionary
+        if self.req is None:
+            _ = unicode
+        else:
+            _ = self.req._
+        self.e_schema.check(self, creation=creation, _=_)
+
+    def fti_containers(self, _done=None):
+        if _done is None:
+            _done = set()
+        _done.add(self.eid)
+        containers = tuple(self.e_schema.fulltext_containers())
+        if containers:
+            yielded = False
+            for rschema, target in containers:
+                if target == 'object':
+                    targets = getattr(self, rschema.type)
+                else:
+                    targets = getattr(self, 'reverse_%s' % rschema)
+                for entity in targets:
+                    if entity.eid in _done:
+                        continue
+                    for container in entity.fti_containers(_done):
+                        yield container
+                        yielded = True
+            if not yielded:
+                yield self
+        else:
+            yield self
+
+    def get_words(self):
+        """used by the full text indexer to get words to index
+
+        this method should only be used on the repository side since it depends
+        on the indexer package
+
+        :rtype: list
+        :return: the list of indexable word of this entity
+        """
+        from indexer.query_objects import tokenize
+        words = []
+        for rschema in self.e_schema.indexable_attributes():
+            try:
+                value = self.printable_value(rschema, format='text/plain')
+            except TransformError:
+                continue
+            except:
+                self.exception("can't add value of %s to text index for entity %s",
+                               rschema, self.eid)
+                continue
+            if value:
+                words += tokenize(value)
+
+        for rschema, role in self.e_schema.fulltext_relations():
+            if role == 'subject':
+                for entity in getattr(self, rschema.type):
+                    words += entity.get_words()
+            else: # if role == 'object':
+                for entity in getattr(self, 'reverse_%s' % rschema.type):
+                    words += entity.get_words()
+        return words
+
+
+# attribute and relation descriptors ##########################################
+
+class Attribute(object):
+    """descriptor that controls schema attribute access"""
+
+    def __init__(self, attrname):
+        assert attrname != 'eid'
+        self._attrname = attrname
+
+    def __get__(self, eobj, eclass):
+        if eobj is None:
+            return self
+        return eobj.get_value(self._attrname)
+
+    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[self._attrname] = value
+
+
+class Relation(object):
+    """descriptor that controls schema relation access"""
+    _role = None # for pylint
+
+    def __init__(self, rschema):
+        self._rschema = rschema
+        self._rtype = rschema.type
+
+    def __get__(self, eobj, eclass):
+        if eobj is None:
+            raise AttributeError('%s cannot be only be accessed from instances'
+                                 % self._rtype)
+        return eobj.related(self._rtype, self._role, entities=True)
+
+    def __set__(self, eobj, value):
+        raise NotImplementedError
+
+
+class SubjectRelation(Relation):
+    """descriptor that controls schema relation access"""
+    _role = 'subject'
+
+class ObjectRelation(Relation):
+    """descriptor that controls schema relation access"""
+    _role = 'object'
+
+from logging import getLogger
+from cubicweb import set_log_methods
+set_log_methods(Entity, getLogger('cubicweb.entity'))
--- a/etwist/request.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/etwist/request.py	Mon May 04 13:09:48 2009 +0200
@@ -1,14 +1,14 @@
 """Twisted request handler for CubicWeb
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from twisted.web2 import http, http_headers
+from datetime import datetime
 
-from mx.DateTime import DateTimeFromTicks
+from twisted.web2 import http, http_headers
 
 from cubicweb.web import DirectResponse
 from cubicweb.web.request import CubicWebRequestBase
@@ -117,7 +117,7 @@
         mtime = self.get_header('If-modified-since', raw=False)
         if mtime:
             # :/ twisted is returned a localized time stamp
-            return DateTimeFromTicks(mtime) + GMTOFFSET
+            return datetime.fromtimestamp(mtime) + GMTOFFSET
         return None
 
 
--- a/etwist/server.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/etwist/server.py	Mon May 04 13:09:48 2009 +0200
@@ -1,15 +1,16 @@
 """twisted server for CubicWeb web applications
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 import sys
 import select
-
-from mx.DateTime import today, RelativeDate
+from time import mktime
+from datetime import date, timedelta
+from urlparse import urlsplit, urlunsplit
 
 from twisted.application import service, strports
 from twisted.internet import reactor, task, threads
@@ -18,7 +19,7 @@
 from twisted.web2 import static, resource, responsecode
 
 from cubicweb import ObjectNotFound
-from cubicweb.web import (AuthenticationError, NotFound, Redirect, 
+from cubicweb.web import (AuthenticationError, NotFound, Redirect,
                        RemoteCallFailed, DirectResponse, StatusResponse,
                        ExplicitLogin)
 from cubicweb.web.application import CubicWebPublisher
@@ -42,7 +43,15 @@
         start_task(interval, catch_error_func)
     # ensure no tasks will be further added
     repo._looping_tasks = ()
-    
+
+def host_prefixed_baseurl(baseurl, host):
+    scheme, netloc, url, query, fragment = urlsplit(baseurl)
+    netloc_domain = '.' + '.'.join(netloc.split('.')[-2:])
+    if host.endswith(netloc_domain):
+        netloc = host
+    baseurl = urlunsplit((scheme, netloc, url, query, fragment))
+    return baseurl
+
 
 class LongTimeExpiringFile(static.File):
     """overrides static.File and sets a far futre ``Expires`` date
@@ -62,8 +71,8 @@
             # Don't provide additional resource information to error responses
             if response.code < 400:
                 # the HTTP RFC recommands not going further than 1 year ahead
-                expires = today() + RelativeDate(months=6)
-                response.headers.setHeader('Expires', int(expires.ticks()))
+                expires = date.today() + timedelta(days=6*30)
+                response.headers.setHeader('Expires', mktime(expires.timetuple()))
             return response
         d = maybeDeferred(super(LongTimeExpiringFile, self).renderHTTP, request)
         return d.addCallback(setExpireHeader)
@@ -71,7 +80,7 @@
 
 class CubicWebRootResource(resource.PostableResource):
     addSlash = False
-    
+
     def __init__(self, config, debug=None):
         self.appli = CubicWebPublisher(config, debug=debug)
         self.debugmode = debug
@@ -103,7 +112,7 @@
         interval = min(config['cleanup-session-time'] or 120,
                        config['cleanup-anonymous-session-time'] or 720) / 2.
         start_task(interval, self.appli.session_handler.clean_sessions)
-        
+
     def shutdown_event(self):
         """callback fired when the server is shutting down to properly
         clean opened sessions
@@ -116,7 +125,7 @@
             self.pyro_daemon.handleRequests(self.pyro_listen_timeout)
         except select.error:
             return
-        
+
     def locateChild(self, request, segments):
         """Indicate which resource to use to process down the URL's path"""
         if segments:
@@ -143,7 +152,7 @@
                     return static.File(fckeditordir), segments[1:]
         # Otherwise we use this single resource
         return self, ()
-    
+
     def render(self, request):
         """Render a page from the root resource"""
         # reload modified files (only in development or debug mode)
@@ -153,7 +162,7 @@
             return self.render_request(request)
         else:
             return threads.deferToThread(self.render_request, request)
-            
+
     def render_request(self, request):
         origpath = request.path
         host = request.host
@@ -163,10 +172,13 @@
             origpath = origpath[6:]
             request.uri = request.uri[6:]
             https = True
-            baseurl = self.https_url or self.base_url 
+            baseurl = self.https_url or self.base_url
         else:
             https = False
             baseurl = self.base_url
+        if self.config['use-request-subdomain']:
+            baseurl = host_prefixed_baseurl(baseurl, host)
+            self.warning('used baseurl is %s for this request', baseurl)
         req = CubicWebTwistedRequestAdapter(request, self.appli.vreg, https, baseurl)
         if req.authmode == 'http':
             # activate realm-based auth
@@ -180,7 +192,7 @@
             return self.redirect(req, ex.location)
         if https and req.cnx.anonymous_connection:
             # don't allow anonymous on https connection
-            return self.request_auth(req)            
+            return self.request_auth(req)
         if self.url_rewriter is not None:
             # XXX should occur before authentication?
             try:
@@ -226,11 +238,6 @@
                 return self.request_auth(req, loggedout=True)
         except Redirect, ex:
             return self.redirect(req, ex.location)
-        if not result:
-            # no result, something went wrong...
-            self.error('no data (%s)', req)
-            # 500 Internal server error
-            return self.redirect(req, req.build_url('error'))
         # request may be referenced by "onetime callback", so clear its entity
         # cache to avoid memory usage
         req.drop_entity_cache()
@@ -242,11 +249,11 @@
         self.debug('redirecting to %s', location)
         # 303 See other
         return http.Response(code=303, headers=req.headers_out)
-        
+
     def request_auth(self, req, loggedout=False):
         if self.https_url and req.base_url() != self.https_url:
             req.headers_out.setHeader('location', self.https_url + 'login')
-            return http.Response(code=303, headers=req.headers_out)            
+            return http.Response(code=303, headers=req.headers_out)
         if self.config['auth-mode'] == 'http':
             code = responsecode.UNAUTHORIZED
         else:
@@ -260,7 +267,7 @@
             content = self.appli.need_login_content(req)
         return http.Response(code, req.headers_out, content)
 
-    
+
 # This part gets run when you run this file via: "twistd -noy demo.py"
 def main(appid, cfgname):
     """Starts an cubicweb  twisted server for an application
@@ -295,12 +302,12 @@
 
 # XXX set max file size to 100Mo: put max upload size in the configuration
 # line below for twisted >= 8.0, default param value for earlier version
-resource.PostableResource.maxSize = 100*1024*1024 
+resource.PostableResource.maxSize = 100*1024*1024
 def parsePOSTData(request, maxMem=100*1024, maxFields=1024,
                   maxSize=100*1024*1024):
     if request.stream.length == 0:
         return defer.succeed(None)
-    
+
     ctype = request.headers.getHeader('content-type')
 
     if ctype is None:
@@ -318,7 +325,7 @@
     def error(f):
         f.trap(fileupload.MimeFormatError)
         raise http.HTTPError(responsecode.BAD_REQUEST)
-    
+
     if ctype.mediaType == 'application' and ctype.mediaSubtype == 'x-www-form-urlencoded':
         d = fileupload.parse_urlencoded(request.stream, keep_blank_values=True)
         d.addCallbacks(updateArgs, error)
@@ -360,7 +367,7 @@
             acount += 1
         else:
             try:
-                ocount[obj.__class__]+= 1
+                ocount[obj.__class__] += 1
             except KeyError:
                 ocount[obj.__class__] = 1
             except AttributeError:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/etwist/test/unittest_server.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,35 @@
+from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.etwist.server import host_prefixed_baseurl
+
+
+class HostPrefixedBaseURLTC(EnvBasedTC):
+
+    def _check(self, baseurl, host, waited):
+        self.assertEquals(host_prefixed_baseurl(baseurl, host), waited,
+                          'baseurl %s called through host %s should be considered as %s'
+                          % (baseurl, host, waited))
+
+    def test1(self):
+        self._check('http://www.cubicweb.org/hg/', 'code.cubicweb.org',
+                    'http://code.cubicweb.org/hg/')
+
+    def test2(self):
+        self._check('http://www.cubicweb.org/hg/', 'cubicweb.org',
+                    'http://www.cubicweb.org/hg/')
+
+    def test3(self):
+        self._check('http://cubicweb.org/hg/', 'code.cubicweb.org',
+                    'http://code.cubicweb.org/hg/')
+
+    def test4(self):
+        self._check('http://www.cubicweb.org/hg/', 'localhost',
+                    'http://www.cubicweb.org/hg/')
+
+    def test5(self):
+        self._check('http://www.cubicweb.org/cubes/', 'hg.code.cubicweb.org',
+                    'http://hg.code.cubicweb.org/cubes/')
+
+    def test6(self):
+        self._check('http://localhost:8080/hg/', 'code.cubicweb.org',
+                    'http://localhost:8080/hg/')
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ext/html4zope.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,153 @@
+# Author: David Goodger
+# Contact: goodger@users.sourceforge.net
+# Revision: $Revision: 1.2 $
+# Date: $Date: 2005-07-04 16:36:50 $
+# Copyright: This module has been placed in the public domain.
+
+"""
+Simple HyperText Markup Language document tree Writer.
+
+The output conforms to the HTML 4.01 Transitional DTD and to the Extensible
+HTML version 1.0 Transitional DTD (*almost* strict).  The output contains a
+minimum of formatting information.  A cascading style sheet ("default.css" by
+default) is required for proper viewing with a modern graphical browser.
+
+http://cvs.zope.org/Zope/lib/python/docutils/writers/Attic/html4zope.py?rev=1.1.2.2&only_with_tag=ajung-restructuredtext-integration-branch&content-type=text/vnd.viewcvs-markup
+"""
+
+__docformat__ = 'reStructuredText'
+
+from logilab.mtconverter import html_escape
+
+from docutils import nodes
+from docutils.writers.html4css1 import Writer as CSS1Writer
+from docutils.writers.html4css1 import HTMLTranslator as CSS1HTMLTranslator
+import os
+
+default_level = int(os.environ.get('STX_DEFAULT_LEVEL', 3))
+
+class Writer(CSS1Writer):
+    """css writer using our html translator"""
+    def __init__(self, base_url):
+        CSS1Writer.__init__(self)
+        self.translator_class = URLBinder(base_url, HTMLTranslator)
+
+    def apply_template(self):
+        """overriding this is necessary with docutils >= 0.5"""
+        return self.visitor.astext()
+
+class URLBinder:
+    def __init__(self, url, klass):
+        self.base_url = url
+        self.translator_class = HTMLTranslator
+        
+    def __call__(self, document):
+        translator = self.translator_class(document)
+        translator.base_url = self.base_url
+        return translator
+    
+class HTMLTranslator(CSS1HTMLTranslator):
+    """ReST tree to html translator"""
+
+    def astext(self):
+        """return the extracted html"""
+        return ''.join(self.body)
+    
+    def visit_title(self, node):
+        """Only 6 section levels are supported by HTML."""
+        if isinstance(node.parent, nodes.topic):
+            self.body.append(
+                  self.starttag(node, 'p', '', CLASS='topic-title'))
+            if node.parent.hasattr('id'):
+                self.body.append(
+                    self.starttag({}, 'a', '', name=node.parent['id']))
+                self.context.append('</a></p>\n')
+            else:
+                self.context.append('</p>\n')
+        elif self.section_level == 0:
+            # document title
+            self.head.append('<title>%s</title>\n'
+                             % self.encode(node.astext()))
+            self.body.append(self.starttag(node, 'h%d' % default_level, '',
+                                           CLASS='title'))
+            self.context.append('</h%d>\n' % default_level)
+        else:
+            self.body.append(
+                  self.starttag(node, 'h%s' % (
+                default_level+self.section_level-1), ''))
+            atts = {}
+            if node.hasattr('refid'):
+                atts['class'] = 'toc-backref'
+                atts['href'] = '%s#%s' % (self.base_url, node['refid'])
+            self.body.append(self.starttag({}, 'a', '', **atts))
+            self.context.append('</a></h%s>\n' % (
+                default_level+self.section_level-1))
+
+    def visit_subtitle(self, node):
+        """format a subtitle"""
+        if isinstance(node.parent, nodes.sidebar):
+            self.body.append(self.starttag(node, 'p', '',
+                                           CLASS='sidebar-subtitle'))
+            self.context.append('</p>\n')
+        else:
+            self.body.append(
+                  self.starttag(node, 'h%s' % (default_level+1), '',
+                                CLASS='subtitle'))
+            self.context.append('</h%s>\n' % (default_level+1))
+
+    def visit_document(self, node):
+        """syt: i don't want the enclosing <div class="document">"""
+    def depart_document(self, node):
+        """syt: i don't want the enclosing <div class="document">"""
+
+    def visit_reference(self, node):
+        """syt: i want absolute urls"""
+        if node.has_key('refuri'):
+            href = node['refuri']
+            if ( self.settings.cloak_email_addresses
+                 and href.startswith('mailto:')):
+                href = self.cloak_mailto(href)
+                self.in_mailto = 1
+        else:
+            assert node.has_key('refid'), \
+                   'References must have "refuri" or "refid" attribute.'
+            href = '%s#%s' % (self.base_url, node['refid'])
+        atts = {'href': href, 'class': 'reference'}
+        if not isinstance(node.parent, nodes.TextElement):
+            assert len(node) == 1 and isinstance(node[0], nodes.image)
+            atts['class'] += ' image-reference'
+        self.body.append(self.starttag(node, 'a', '', **atts))
+
+    ## override error messages to avoid XHTML problems ########################
+    def visit_problematic(self, node):
+        pass
+
+    def depart_problematic(self, node):
+        pass
+    
+    def visit_system_message(self, node):
+        backref_text = ''
+        if len(node['backrefs']):
+            backrefs = node['backrefs']
+            if len(backrefs) == 1:
+                backref_text = '; <em>backlink</em>'
+            else:
+                i = 1
+                backlinks = []
+                for backref in backrefs:
+                    backlinks.append(str(i))
+                    i += 1
+                backref_text = ('; <em>backlinks: %s</em>'
+                                % ', '.join(backlinks))
+        if node.hasattr('line'):
+            line = ', line %s' % node['line']
+        else:
+            line = ''
+        a_start = a_end = ''
+        error = u'System Message: %s%s/%s%s (%s %s)%s</p>\n' % (
+            a_start, node['type'], node['level'], a_end,
+            self.encode(node['source']), line, backref_text)
+        self.body.append(u'<div class="system-message"><b>ReST / HTML errors:</b>%s</div>' % html_escape(error))
+
+    def depart_system_message(self, node):
+        pass
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ext/rest.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,260 @@
+"""rest publishing functions
+
+contains some functions and setup of docutils for cubicweb. Provides the
+following ReST directives:
+
+* `eid`, create link to entity in the repository by their eid
+
+* `card`, create link to card entity in the repository by their wikiid
+  (proposing to create it when the refered card doesn't exist yet)
+
+* `winclude`, reference to a web documentation file (in wdoc/ directories)
+
+* `sourcecode` (if pygments is installed), source code colorization
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from cStringIO import StringIO
+from itertools import chain
+from logging import getLogger
+from os.path import join
+
+from docutils import statemachine, nodes, utils, io
+from docutils.core import publish_string
+from docutils.parsers.rst import Parser, states, directives
+from docutils.parsers.rst.roles import register_canonical_role, set_classes
+
+from logilab.mtconverter import html_escape
+
+from cubicweb.ext.html4zope import Writer
+
+# We provide our own parser as an attempt to get rid of
+# state machine reinstanciation
+
+import re
+# compile states.Body patterns
+for k, v in states.Body.patterns.items():
+    if isinstance(v, str):
+        states.Body.patterns[k] = re.compile(v)
+
+# register ReStructured Text mimetype / extensions
+import mimetypes
+mimetypes.add_type('text/rest', '.rest')
+mimetypes.add_type('text/rest', '.rst')
+
+
+LOGGER = getLogger('cubicweb.rest')
+
+def eid_reference_role(role, rawtext, text, lineno, inliner,
+                       options={}, content=[]):
+    try:
+        try:
+            eid_num, rest = text.split(u':', 1)
+        except:
+            eid_num, rest = text, '#'+text
+        eid_num = int(eid_num)
+        if eid_num < 0:
+            raise ValueError
+    except ValueError:
+        msg = inliner.reporter.error(
+            'EID number must be a positive number; "%s" is invalid.'
+            % text, line=lineno)
+        prb = inliner.problematic(rawtext, rawtext, msg)
+        return [prb], [msg]
+    # Base URL mainly used by inliner.pep_reference; so this is correct:
+    context = inliner.document.settings.context
+    refedentity = context.req.eid_rset(eid_num).get_entity(0, 0)
+    ref = refedentity.absolute_url()
+    set_classes(options)
+    return [nodes.reference(rawtext, utils.unescape(rest), refuri=ref,
+                            **options)], []
+
+register_canonical_role('eid', eid_reference_role)
+
+
+def card_reference_role(role, rawtext, text, lineno, inliner,
+                       options={}, content=[]):
+    text = text.strip()
+    try:
+        wikiid, rest = text.split(u':', 1)
+    except:
+        wikiid, rest = text, text
+    context = inliner.document.settings.context
+    cardrset = context.req.execute('Card X WHERE X wikiid %(id)s',
+                                   {'id': wikiid})
+    if cardrset:
+        ref = cardrset.get_entity(0, 0).absolute_url()
+    else:
+        schema = context.schema
+        if schema.eschema('Card').has_perm(context.req, 'add'):
+            ref = context.req.build_url('view', vid='creation', etype='Card', wikiid=wikiid)
+        else:
+            ref = '#'
+    set_classes(options)
+    return [nodes.reference(rawtext, utils.unescape(rest), refuri=ref,
+                            **options)], []
+
+register_canonical_role('card', card_reference_role)
+
+
+def winclude_directive(name, arguments, options, content, lineno,
+                       content_offset, block_text, state, state_machine):
+    """Include a reST file as part of the content of this reST file.
+
+    same as standard include directive but using config.locate_doc_resource to
+    get actual file to include.
+
+    Most part of this implementation is copied from `include` directive defined
+    in `docutils.parsers.rst.directives.misc`
+    """
+    context = state.document.settings.context
+    source = state_machine.input_lines.source(
+        lineno - state_machine.input_offset - 1)
+    #source_dir = os.path.dirname(os.path.abspath(source))
+    fid = arguments[0]
+    for lang in chain((context.req.lang, context.vreg.property_value('ui.language')),
+                      context.config.available_languages()):
+        rid = '%s_%s.rst' % (fid, lang)
+        resourcedir = context.config.locate_doc_file(rid)
+        if resourcedir:
+            break
+    else:
+        severe = state_machine.reporter.severe(
+              'Problems with "%s" directive path:\nno resource matching %s.'
+              % (name, fid),
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [severe]
+    path = join(resourcedir, rid)
+    encoding = options.get('encoding', state.document.settings.input_encoding)
+    try:
+        state.document.settings.record_dependencies.add(path)
+        include_file = io.FileInput(
+            source_path=path, encoding=encoding,
+            error_handler=state.document.settings.input_encoding_error_handler,
+            handle_io_errors=None)
+    except IOError, error:
+        severe = state_machine.reporter.severe(
+              'Problems with "%s" directive path:\n%s: %s.'
+              % (name, error.__class__.__name__, error),
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [severe]
+    try:
+        include_text = include_file.read()
+    except UnicodeError, error:
+        severe = state_machine.reporter.severe(
+              'Problem with "%s" directive:\n%s: %s'
+              % (name, error.__class__.__name__, error),
+              nodes.literal_block(block_text, block_text), line=lineno)
+        return [severe]
+    if options.has_key('literal'):
+        literal_block = nodes.literal_block(include_text, include_text,
+                                            source=path)
+        literal_block.line = 1
+        return literal_block
+    else:
+        include_lines = statemachine.string2lines(include_text,
+                                                  convert_whitespace=1)
+        state_machine.insert_input(include_lines, path)
+        return []
+
+winclude_directive.arguments = (1, 0, 1)
+winclude_directive.options = {'literal': directives.flag,
+                              'encoding': directives.encoding}
+directives.register_directive('winclude', winclude_directive)
+
+try:
+    from pygments import highlight
+    from pygments.lexers import get_lexer_by_name, LEXERS
+    from pygments.formatters import HtmlFormatter
+except ImportError:
+    pass
+else:
+    _PYGMENTS_FORMATTER = HtmlFormatter()
+
+    def pygments_directive(name, arguments, options, content, lineno,
+                           content_offset, block_text, state, state_machine):
+        try:
+            lexer = get_lexer_by_name(arguments[0])
+        except ValueError:
+            import traceback
+            traceback.print_exc()
+            print sorted(aliases for module_name, name, aliases, _, _  in LEXERS.itervalues())
+            # no lexer found
+            lexer = get_lexer_by_name('text')
+        parsed = highlight(u'\n'.join(content), lexer, _PYGMENTS_FORMATTER)
+        context = state.document.settings.context
+        context.req.add_css('pygments.css')
+        return [nodes.raw('', parsed, format='html')]
+
+    pygments_directive.arguments = (1, 0, 1)
+    pygments_directive.content = 1
+    directives.register_directive('sourcecode', pygments_directive)
+
+
+class CubicWebReSTParser(Parser):
+    """The (customized) reStructuredText parser."""
+
+    def __init__(self):
+        self.initial_state = 'Body'
+        self.state_classes = states.state_classes
+        self.inliner = states.Inliner()
+        self.statemachine = states.RSTStateMachine(
+              state_classes=self.state_classes,
+              initial_state=self.initial_state,
+              debug=0)
+
+    def parse(self, inputstring, document):
+        """Parse `inputstring` and populate `document`, a document tree."""
+        self.setup_parse(inputstring, document)
+        inputlines = statemachine.string2lines(inputstring,
+                                               convert_whitespace=1)
+        self.statemachine.run(inputlines, document, inliner=self.inliner)
+        self.finish_parse()
+
+
+def rest_publish(context, data):
+    """publish a string formatted as ReStructured Text to HTML
+
+    :type context: a cubicweb application object
+
+    :type data: str
+    :param data: some ReST text
+
+    :rtype: unicode
+    :return:
+      the data formatted as HTML or the original data if an error occured
+    """
+    req = context.req
+    if isinstance(data, unicode):
+        encoding = 'unicode'
+    else:
+        encoding = req.encoding
+    settings = {'input_encoding': encoding, 'output_encoding': 'unicode',
+                'warning_stream': StringIO(), 'context': context,
+                # dunno what's the max, severe is 4, and we never want a crash
+                # (though try/except may be a better option...)
+                'halt_level': 10,
+                }
+    if context:
+        if hasattr(req, 'url'):
+            base_url = req.url()
+        elif hasattr(context, 'absolute_url'):
+            base_url = context.absolute_url()
+        else:
+            base_url = req.base_url()
+    else:
+        base_url = None
+    try:
+        return publish_string(writer=Writer(base_url=base_url),
+                              parser=CubicWebReSTParser(), source=data,
+                              settings_overrides=settings)
+    except Exception:
+        LOGGER.exception('error while publishing ReST text')
+        if not isinstance(data, unicode):
+            data = unicode(data, encoding, 'replace')
+        return html_escape(req._('error while publishing ReST text')
+                           + '\n\n' + data)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ext/tal.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,256 @@
+"""provides simpleTAL extensions for CubicWeb
+
+:organization: Logilab
+:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+
+__docformat__ = "restructuredtext en"
+
+import sys
+import re
+from os.path import exists, isdir, join
+from logging import getLogger
+from StringIO import StringIO
+        
+from simpletal import simpleTAL, simpleTALES
+
+from logilab.common.decorators import cached
+
+LOGGER = getLogger('cubicweb.tal')
+
+
+class LoggerAdapter(object):
+    def __init__(self, tal_logger):
+        self.tal_logger = tal_logger
+        
+    def debug(self, msg):
+        LOGGER.debug(msg)
+
+    def warn(self, msg):
+        LOGGER.warning(msg)
+
+    def __getattr__(self, attrname):
+        return getattr(self.tal_logger, attrname)
+
+
+class CubicWebContext(simpleTALES.Context):
+    """add facilities to access entity / resultset"""
+
+    def __init__(self, options=None, allowPythonPath=1):
+        simpleTALES.Context.__init__(self, options, allowPythonPath)
+        self.log = LoggerAdapter(self.log)
+
+    def update(self, context):
+        for varname, value in context.items():
+            self.addGlobal(varname, value)
+
+    def addRepeat(self, name, var, initialValue):
+        simpleTALES.Context.addRepeat(self, name, var, initialValue)
+
+# XXX FIXME need to find a clean to define OPCODE values for extensions
+I18N_CONTENT = 18  
+I18N_REPLACE = 19
+RQL_EXECUTE  = 20
+# simpleTAL uses the OPCODE values to define priority over commands.
+# TAL_ITER should have the same priority than TAL_REPEAT (i.e. 3), but
+# we can't use the same OPCODE for two different commands without changing
+# the simpleTAL implementation. Another solution would be to totally override
+# the REPEAT implementation with the ITER one, but some specific operations
+# (involving len() for instance) are not implemented for ITER, so we prefer
+# to keep both implementations for now, and to fool simpleTAL by using a float
+# number between 3 and 4
+TAL_ITER     = 3.1
+
+
+# FIX simpleTAL HTML 4.01 stupidity
+# (simpleTAL never closes tags like INPUT, IMG, HR ...)
+simpleTAL.HTML_FORBIDDEN_ENDTAG.clear()
+
+class CubicWebTemplateCompiler(simpleTAL.HTMLTemplateCompiler):
+    """extends default compiler by adding i18n:content commands"""
+
+    def __init__(self):
+        simpleTAL.HTMLTemplateCompiler.__init__(self)
+        self.commandHandler[I18N_CONTENT] = self.compile_cmd_i18n_content
+        self.commandHandler[I18N_REPLACE] = self.compile_cmd_i18n_replace
+        self.commandHandler[RQL_EXECUTE] = self.compile_cmd_rql
+        self.commandHandler[TAL_ITER] = self.compile_cmd_tal_iter
+
+    def setTALPrefix(self, prefix):
+        simpleTAL.TemplateCompiler.setTALPrefix(self, prefix)
+        self.tal_attribute_map['i18n:content'] = I18N_CONTENT
+        self.tal_attribute_map['i18n:replace'] = I18N_REPLACE
+        self.tal_attribute_map['rql:execute'] = RQL_EXECUTE
+        self.tal_attribute_map['tal:iter'] = TAL_ITER
+
+    def compile_cmd_i18n_content(self, argument):
+        # XXX tal:content structure=, text= should we support this ?
+        structure_flag = 0
+        return (I18N_CONTENT, (argument, False, structure_flag, self.endTagSymbol))
+
+    def compile_cmd_i18n_replace(self, argument):
+        # XXX tal:content structure=, text= should we support this ?
+        structure_flag = 0
+        return (I18N_CONTENT, (argument, True, structure_flag, self.endTagSymbol))
+
+    def compile_cmd_rql(self, argument):
+        return (RQL_EXECUTE, (argument, self.endTagSymbol))
+
+    def compile_cmd_tal_iter(self, argument):
+        original_id, (var_name, expression, end_tag_symbol) = \
+                     simpleTAL.HTMLTemplateCompiler.compileCmdRepeat(self, argument)
+        return (TAL_ITER, (var_name, expression, self.endTagSymbol))
+
+    def getTemplate(self):
+        return CubicWebTemplate(self.commandList, self.macroMap, self.symbolLocationTable)
+
+    def compileCmdAttributes (self, argument):
+        """XXX modified to support single attribute
+        definition ending by a ';'
+
+        backport this to simpleTAL
+        """
+        # Compile tal:attributes into attribute command
+        # Argument: [(attributeName, expression)]
+        
+        # Break up the list of attribute settings first
+        commandArgs = []
+        # We only want to match semi-colons that are not escaped
+        argumentSplitter =  re.compile(r'(?<!;);(?!;)')
+        for attributeStmt in argumentSplitter.split(argument):
+            if not attributeStmt.strip():
+                continue
+            #  remove any leading space and un-escape any semi-colons
+            attributeStmt = attributeStmt.lstrip().replace(';;', ';')
+            # Break each attributeStmt into name and expression
+            stmtBits = attributeStmt.split(' ')
+            if (len (stmtBits) < 2):
+                # Error, badly formed attributes command
+                msg = "Badly formed attributes command '%s'.  Attributes commands must be of the form: 'name expression[;name expression]'" % argument
+                self.log.error(msg)
+                raise simpleTAL.TemplateParseException(self.tagAsText(self.currentStartTag), msg)
+            attName = stmtBits[0]
+            attExpr = " ".join(stmtBits[1:])
+            commandArgs.append((attName, attExpr))
+        return (simpleTAL.TAL_ATTRIBUTES, commandArgs)
+
+
+class CubicWebTemplateInterpreter(simpleTAL.TemplateInterpreter):
+    """provides implementation for interpreting cubicweb extensions"""
+    def __init__(self):
+        simpleTAL.TemplateInterpreter.__init__(self)
+        self.commandHandler[I18N_CONTENT] = self.cmd_i18n
+        self.commandHandler[TAL_ITER] = self.cmdRepeat
+        # self.commandHandler[RQL_EXECUTE] = self.cmd_rql
+
+    def cmd_i18n(self, command, args):
+        """i18n:content and i18n:replace implementation"""
+        string, replace_flag, structure_flag, end_symbol = args
+        if replace_flag:
+            self.outputTag = 0
+        result = self.context.globals['_'](string)
+        self.tagContent = (0, result)
+        self.movePCForward = self.symbolTable[end_symbol]
+        self.programCounter += 1
+
+
+class CubicWebTemplate(simpleTAL.HTMLTemplate):
+    """overrides HTMLTemplate.expand() to systematically use CubicWebInterpreter
+    """
+    def expand(self, context, outputFile):
+        interpreter = CubicWebTemplateInterpreter()
+        interpreter.initialise(context, outputFile)
+        simpleTAL.HTMLTemplate.expand(self, context, outputFile,# outputEncoding='unicode',
+                                      interpreter=interpreter)
+
+    def expandInline(self, context, outputFile, interpreter):
+        """ Internally used when expanding a template that is part of a context."""
+        try:
+            interpreter.execute(self)
+        except UnicodeError, unierror:
+            LOGGER.exception(str(unierror))
+            raise simpleTALES.ContextContentException("found non-unicode %r string in Context!" % unierror.args[1]), None, sys.exc_info()[-1]
+
+
+def compile_template(template):
+    """compiles a TAL template string
+    :type template: unicode
+    :param template: a TAL-compliant template string
+    """
+    string_buffer = StringIO(template)
+    compiler = CubicWebTemplateCompiler()
+    compiler.parseTemplate(string_buffer) # , inputEncoding='unicode')
+    return compiler.getTemplate()
+
+
+def compile_template_file(filepath):
+    """compiles a TAL template file
+    :type filepath: str
+    :param template: path of the file to compile 
+    """
+    fp = file(filepath)
+    file_content = unicode(fp.read()) # template file should be pure ASCII
+    fp.close()
+    return compile_template(file_content)
+
+
+def evaluatePython (self, expr):
+    if not self.allowPythonPath:
+        return self.false
+    globals = {}
+    for name, value in self.globals.items():
+        if isinstance (value, simpleTALES.ContextVariable):
+            value = value.rawValue()
+        globals[name] = value
+    globals['path'] = self.pythonPathFuncs.path
+    globals['string'] = self.pythonPathFuncs.string
+    globals['exists'] = self.pythonPathFuncs.exists
+    globals['nocall'] = self.pythonPathFuncs.nocall
+    globals['test'] = self.pythonPathFuncs.test
+    locals = {}
+    for name, value in self.locals.items():
+        if (isinstance (value, simpleTALES.ContextVariable)):
+            value = value.rawValue()
+        locals[name] = value
+    # XXX precompile expr will avoid late syntax error
+    try:
+        result = eval(expr, globals, locals)
+    except Exception, ex:
+        ex = ex.__class__('in %r: %s' % (expr, ex))
+        raise ex, None, sys.exc_info()[-1]
+    if (isinstance (result, simpleTALES.ContextVariable)):
+        return result.value()
+    return result
+
+simpleTALES.Context.evaluatePython = evaluatePython
+
+
+class talbased(object):
+    def __init__(self, filename, write=True):
+##         if not osp.isfile(filepath):
+##             # print "[tal.py] just for tests..."
+##             # get parent frame
+##             directory = osp.abspath(osp.dirname(sys._getframe(1).f_globals['__file__']))
+##             filepath = osp.join(directory, filepath)
+        self.filename = filename
+        self.write = write
+
+    def __call__(self, viewfunc):
+        def wrapped(instance, *args, **kwargs):
+            variables = viewfunc(instance, *args, **kwargs)
+            html = instance.tal_render(self._compiled_template(instance), variables)
+            if self.write:
+                instance.w(html)
+            else:
+                return html
+        return wrapped
+
+    def _compiled_template(self, instance):
+        for fileordirectory in instance.config.vregistry_path():
+            filepath = join(fileordirectory, self.filename)
+            if isdir(fileordirectory) and exists(filepath):
+                return compile_template_file(filepath)
+        raise Exception('no such template %s' % self.filename)
+    _compiled_template = cached(_compiled_template, 0)
+    
--- a/gettext.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/gettext.py	Mon May 04 13:09:48 2009 +0200
@@ -442,7 +442,6 @@
 
 
 def bindtextdomain(domain, localedir=None):
-    global _localedirs
     if localedir is not None:
         _localedirs[domain] = localedir
     return _localedirs.get(domain, _default_localedir)
--- a/goa/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -1,69 +1,12 @@
 """cubicweb on google appengine
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 
-from datetime import datetime, time, date
-from mx.DateTime import DateTime, Date, Time
-
-def mx2datetime(mxobj, yamstype):
-    """converts a mx date object (DateTime, Date or Time) into a
-    regular python datetime object
-    """
-    #if yamstype == 'Datetime':
-    # don't use date, db model doesn't actually support it, only datetime
-    return datetime(mxobj.year, mxobj.month, mxobj.day,
-                    mxobj.hour, mxobj.minute, int(mxobj.second))
-#     elif yamstype == 'Date':
-#         return date(mxobj.year, mxobj.month, mxobj.day)
-#     # XXX don't support time either, what should we do here ?
-#     return time(mxobj.hour, mxobj.minute, int(mxobj.second))
-
-def datetime2mx(datetimeobj, yamstype=None):
-    """converts a mx date object (DateTime, Date or Time) into a
-    regular python datetime object
-    """
-    if yamstype is None:
-        yamstype = guess_yamstype_for_date(datetimeobj)
-    assert yamstype is not None
-    if yamstype == 'Datetime':
-        # don't use date, db model doesn't actually support it, only datetime
-        return DateTime(datetimeobj.year, datetimeobj.month, datetimeobj.day,
-                        datetimeobj.hour, datetimeobj.minute, int(datetimeobj.second))
-    elif yamstype == 'Date':
-        return Date(datetimeobj.year, datetimeobj.month, datetimeobj.day)
-    # XXX don't support time either, what should we do here ?
-    return Time(datetimeobj.hour, datetimeobj.minute, int(datetimeobj.second))
-
-
-def guess_yamstype_for_date(datetimeobj):
-    """guesses yams correct type according to `datetimeobj`'s type"""
-    if isinstance(datetimeobj, datetime):
-        return 'Datetime'
-    elif isinstance(datetimeobj, date):
-        return 'Date'
-    elif isinstance(datetimeobj, time):
-        return 'Time'
-    return None
-
-
-def use_mx_for_dates(func):
-    """decorator to convert func's return value into mx objects
-    instead of datetime objects
-    """
-    def wrapper(*args, **kwargs):
-        value = func(*args, **kwargs)
-        yamstype = guess_yamstype_for_date(value)
-        if yamstype is None:
-            return value
-        return datetime2mx(value, yamstype)
-    return wrapper
-
-
 try:
     # WARNING: do not import the google's db module here since it will take
     #          precedence over our own db submodule
@@ -152,7 +95,7 @@
         repository.Repository.get_cubes = get_cubes
         
         from rql import RQLHelper
-        RQLHelper.simplify = lambda x,r: None
+        RQLHelper.simplify = lambda x, r: None
 
         # activate entity caching on the server side
 
--- a/goa/appobjects/components.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/appobjects/components.py	Mon May 04 13:09:48 2009 +0200
@@ -1,28 +1,21 @@
 """overrides some base views for cubicweb on google appengine
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from os.path import join
-
 from logilab.mtconverter import html_escape
-from logilab.common.decorators import cached
 
 from cubicweb import typed_eid
+from cubicweb.selectors import one_line_rset, match_search_state, accept
 from cubicweb.schema import display_name
 from cubicweb.common.view import StartupView, EntityView
-from cubicweb.common.selectors import (one_line_rset, match_search_state,
-                                    accept)
 from cubicweb.web import Redirect
 from cubicweb.web.views import vid_from_rset
-from cubicweb.goa.db import rset_from_objs
 
-from google.appengine.api import datastore, mail
-
-from main import APPLROOT
+from google.appengine.api import mail
 
 
 class SearchForAssociationView(EntityView):
@@ -31,9 +24,7 @@
     """
     id = 'search-associate'
     
-    __selectors__ = (one_line_rset, match_search_state, accept)
-    accepts = ('Any',)
-    search_states = ('linksearch',)
+    __select__ = one_line_rset() & match_search_state('linksearch') & accept
 
     def cell_call(self, row, col):
         entity = self.entity(0, 0)
--- a/goa/appobjects/dbmgmt.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/appobjects/dbmgmt.py	Mon May 04 13:09:48 2009 +0200
@@ -2,7 +2,7 @@
 restoration).
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -13,6 +13,7 @@
 from logilab.common.decorators import cached
 from logilab.mtconverter import html_escape
 
+from cubicweb.selectors import none_rset, match_user_groups
 from cubicweb.common.view import StartupView
 from cubicweb.web import Redirect
 from cubicweb.goa.dbinit import fix_entities, init_persistent_schema, insert_versions
@@ -39,7 +40,7 @@
     which are doing datastore administration requests
     """
     id = 'authinfo'
-    require_groups = ('managers',)
+    __select__ = none_rset() & match_user_groups('managers')
 
     def call(self):
         cookie = self.req.get_cookie()
@@ -61,7 +62,7 @@
     step by step to avoid depassing quotas
     """
     id = 'contentinit'
-    require_groups = ('managers',)
+    __select__ = none_rset() & match_user_groups('managers')
 
     def server_session(self):
         ssession = self.config.repo_session(self.req.cnx.sessionid)
@@ -166,8 +167,8 @@
         
 class ContentClear(StartupView):
     id = 'contentclear'
-    require_groups = ('managers',)
-    skip_etypes = ('EGroup', 'EUser')
+    __select__ = none_rset() & match_user_groups('managers')
+    skip_etypes = ('CWGroup', 'CWUser')
     
     def call(self):
         # XXX should use unsafe_execute with all hooks deactivated
--- a/goa/appobjects/gauthservice.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/appobjects/gauthservice.py	Mon May 04 13:09:48 2009 +0200
@@ -1,29 +1,18 @@
 """authentication using google authentication service
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb.common.registerers import priority_registerer
 from cubicweb.web.views.basecomponents import UserLink
 from cubicweb.web.views.actions import LogoutAction
 
 from google.appengine.api import users
 
 
-class use_google_auth_registerer(priority_registerer):
-    """register object if use-google-auth is true"""
-    
-    def do_it_yourself(self, registered):
-        if not hasattr(self.config, 'has_resource'):
-            return
-        return super(use_google_auth_registerer, self).do_it_yourself(registered)
-
-
-class GAEUserLink(UserLink):
-    __registerer__ = use_google_auth_registerer
+class GACWUserLink(UserLink):
 
     def anon_user_link(self):
         self.w(self.req._('anonymous'))
@@ -31,7 +20,11 @@
                % (users.create_login_url(self.req.url()), self.req._('login')))
 
 class GAELogoutAction(LogoutAction):
-    __registerer__ = use_google_auth_registerer
 
     def url(self):
         return users.create_logout_url(self.req.build_url('logout') )
+    
+def registration_callback(vreg):
+    if hasattr(vreg.config, 'has_resource'):
+        vreg.register(GACWUserLink, clear=True)
+        vreg.register(GAELogoutAction, clear=True)
--- a/goa/appobjects/sessions.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/appobjects/sessions.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """persistent sessions stored in big table
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 
 XXX TODO:
@@ -15,8 +15,9 @@
 
 from logilab.common.decorators import cached, clear_cache
 
-from cubicweb import UnknownEid, BadConnectionId
+from cubicweb import BadConnectionId
 from cubicweb.dbapi import Connection, ConnectionProperties, repo_connect
+from cubicweb.selectors import none_rset, match_user_groups
 from cubicweb.server.session import Session
 from cubicweb.web import InvalidSession
 from cubicweb.web.application import AbstractSessionManager
@@ -254,7 +255,7 @@
 
 class SessionsCleaner(StartupView):
     id = 'cleansessions'
-    require_groups = ('managers',)
+    __select__ = none_rset() & match_user_groups('managers')
     
     def call(self):
         # clean web session
@@ -268,4 +269,9 @@
         self.w(u'%s repository sessions closed<br/>\n' % nbclosed)
         self.w(u'%s remaining sessions<br/>\n' % remaining)
         self.w(u'</div>')
+
         
+def registration_callback(vreg):
+    vreg.register(SessionsCleaner)
+    vreg.register(GAEAuthenticationManager, clear=True)
+    vreg.register(GAEPersistentSessionManager, clear=True)
--- a/goa/db.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/db.py	Mon May 04 13:09:48 2009 +0200
@@ -25,21 +25,20 @@
 * XXX ListProperty
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from datetime import datetime
 from copy import deepcopy
 
 from logilab.common.decorators import cached, iclassmethod
 
 from cubicweb import RequestSessionMixIn, Binary, entities
 from cubicweb.rset import ResultSet
-from cubicweb.common.entity import metaentity
+from cubicweb.entity import metaentity
 from cubicweb.server.utils import crypt_password
-from cubicweb.goa import use_mx_for_dates, mx2datetime, MODE
+from cubicweb.goa import MODE
 from cubicweb.goa.dbinit import init_relations
 
 from google.appengine.api.datastore import Get, Put, Key, Entity, Query
@@ -174,15 +173,11 @@
         return '<ModelEntity %s %s %s at %s>' % (
             self.e_schema, self.eid, self.keys(), id(self))
 
-    __getattribute__ = use_mx_for_dates(entities.AnyEntity.__getattribute__)
-
     def _cubicweb_to_datastore(self, attr, value):
         attr = attr[2:] # remove 's_' / 'o_' prefix
         if attr in self._attributes:
             tschema = self.e_schema.destination(attr)
-            if tschema in ('Datetime', 'Date', 'Time'):
-                value = mx2datetime(value, tschema)
-            elif tschema == 'String':
+            if tschema == 'String':
                 if len(value) > 500:
                     value = Text(value)                
             elif tschema == 'Password':
@@ -288,7 +283,6 @@
             return 'eid', False
         return mainattr, needcheck
     
-    @use_mx_for_dates
     def get_value(self, name):
         try:
             value = self[name]
--- a/goa/dbinit.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/dbinit.py	Mon May 04 13:09:48 2009 +0200
@@ -15,7 +15,7 @@
     try:
         return _GROUP_CACHE[groupname]
     except KeyError:
-        key = Key.from_path('EGroup', 'key_' + groupname, parent=None)
+        key = Key.from_path('CWGroup', 'key_' + groupname, parent=None)
         try:
             group = Get(key)
         except datastore_errors.EntityNotFoundError:
@@ -29,7 +29,7 @@
 def create_user(login, password, groups):
     """create a cubicweb user"""
     from cubicweb.server.utils import crypt_password
-    user = Entity('EUser', name=login)
+    user = Entity('CWUser', name=login)
     user['s_login'] = unicode(login)
     user['s_upassword'] = crypt_password(password)
     set_user_groups(user, groups)
@@ -39,7 +39,7 @@
 def create_groups():
     """create initial cubicweb groups"""
     for groupname in ('managers', 'users', 'guests'):
-        group = Entity('EGroup', name='key_' + groupname)
+        group = Entity('CWGroup', name='key_' + groupname)
         group['s_name'] = unicode(groupname)
         Put(group)
         _GROUP_CACHE[groupname] = group
@@ -74,23 +74,23 @@
             gaeentity[dsrelation] = None
     
 def fix_entities(schema):
-    for etype in ('EUser', 'EGroup'):
+    for etype in ('CWUser', 'CWGroup'):
         eschema = schema.eschema(etype)
         for gaeentity in Query(etype).Run():
             init_relations(gaeentity, eschema)
-            # XXX o_is on EEType entity
-            gaeentity['s_is'] = Key.from_path('EEType', 'key_' + etype, parent=None)
+            # XXX o_is on CWEType entity
+            gaeentity['s_is'] = Key.from_path('CWEType', 'key_' + etype, parent=None)
             Put(gaeentity)
     
 def init_persistent_schema(ssession, schema):
     execute = ssession.unsafe_execute
-    rql = ('INSERT EEType X: X name %(name)s, X description %(descr)s,'
+    rql = ('INSERT CWEType X: X name %(name)s, X description %(descr)s,'
            'X final FALSE, X meta %(meta)s')
-    eschema = schema.eschema('EEType')
-    execute(rql, {'name': u'EEType', 'descr': unicode(eschema.description),
+    eschema = schema.eschema('CWEType')
+    execute(rql, {'name': u'CWEType', 'descr': unicode(eschema.description),
                   'meta': eschema.meta})
     for eschema in schema.entities():
-        if eschema.is_final() or eschema == 'EEType':
+        if eschema.is_final() or eschema == 'CWEType':
             continue
         execute(rql, {'name': unicode(eschema), 'meta': eschema.meta,
                       'descr': unicode(eschema.description)})
@@ -98,11 +98,11 @@
 def insert_versions(ssession, config):
     execute = ssession.unsafe_execute
     # insert versions
-    execute('INSERT EProperty X: X pkey %(pk)s, X value%(v)s',
+    execute('INSERT CWProperty X: X pkey %(pk)s, X value%(v)s',
             {'pk': u'system.version.cubicweb',
              'v': unicode(config.cubicweb_version())})
     for cube in config.cubes():
-        execute('INSERT EProperty X: X pkey %(pk)s, X value%(v)s',
+        execute('INSERT CWProperty X: X pkey %(pk)s, X value%(v)s',
                 {'pk': u'system.version.%s' % cube,
                  'v': unicode(config.cube_version(cube))})
     
--- a/goa/dbmyams.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/dbmyams.py	Mon May 04 13:09:48 2009 +0200
@@ -13,12 +13,8 @@
 from google.appengine.ext import db
 from google.appengine.api import datastore_types
 
-from yams.schema2sql import eschema_attrs
-from yams.constraints import SizeConstraint
-from yams.reader import PyFileReader
 from yams.buildobjs import (String, Int, Float, Boolean, Date, Time, Datetime,
-                            Interval, Password, Bytes, ObjectRelation,
-                            SubjectRelation, RestrictedEntityType)
+                            Bytes, SubjectRelation)
 from yams.buildobjs import metadefinition, EntityType
 
 from cubicweb.schema import CubicWebSchemaLoader
@@ -172,7 +168,7 @@
     # the loader is instantiated because this is where the dbmodels
     # are registered in the yams schema
     for compname in config['included-cubes']:
-        comp = __import__('%s.schema' % compname)
+        __import__('%s.schema' % compname)
     loader = GaeSchemaLoader(use_gauthservice=config['use-google-auth'], db=db)
     loader.lib_directory = SCHEMAS_LIB_DIRECTORY
     if schemaclasses is not None:
@@ -180,10 +176,10 @@
             loader.load_dbmodel(cls.__name__, goadb.extract_dbmodel(cls))
     elif config['schema-type'] == 'dbmodel':
         import schema as appschema
-        for objname, obj in vars(appschema).items():
+        for obj in vars(appschema).values():
             if isinstance(obj, type) and issubclass(obj, goadb.Model) and obj.__module__ == appschema.__name__:
                 loader.load_dbmodel(obj.__name__, goadb.extract_dbmodel(obj))
-    for erschema in ('EGroup', 'EEType', 'ERType', 'RQLExpression',
+    for erschema in ('CWGroup', 'CWEType', 'CWRType', 'RQLExpression',
                      'is_', 'is_instance_of',
                      'read_permission', 'add_permission',
                      'delete_permission', 'update_permission'):
@@ -198,18 +194,18 @@
     if extrahook is not None:
         extrahook(loader)
     if config['use-google-auth']:
-        loader.defined['EUser'].remove_relation('upassword')
-        loader.defined['EUser'].permissions['add'] = ()
-        loader.defined['EUser'].permissions['delete'] = ()
-    for etype in ('EGroup', 'RQLExpression'):
+        loader.defined['CWUser'].remove_relation('upassword')
+        loader.defined['CWUser'].permissions['add'] = ()
+        loader.defined['CWUser'].permissions['delete'] = ()
+    for etype in ('CWGroup', 'RQLExpression'):
         read_perm_rel = loader.defined[etype].get_relations('read_permission').next()
         read_perm_rel.cardinality = '**'
-    # XXX not yet ready for EUser workflow
-    loader.defined['EUser'].remove_relation('in_state')
-    loader.defined['EUser'].remove_relation('wf_info_for')
-    # remove RQLConstraint('NOT O name "owners"') on EUser in_group EGroup
+    # XXX not yet ready for CWUser workflow
+    loader.defined['CWUser'].remove_relation('in_state')
+    loader.defined['CWUser'].remove_relation('wf_info_for')
+    # remove RQLConstraint('NOT O name "owners"') on CWUser in_group CWGroup
     # since "owners" group is not persistent with gae
-    loader.defined['EUser'].get_relations('in_group').next().constraints = []
+    loader.defined['CWUser'].get_relations('in_group').next().constraints = []
     # return the full schema including the cubes' schema
     for ertype in loader.defined.values():
         if getattr(ertype, 'inlined', False):
--- a/goa/doc/devmanual_fr/chap_autres_composants_ui.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/doc/devmanual_fr/chap_autres_composants_ui.txt	Mon May 04 13:09:48 2009 +0200
@@ -9,6 +9,6 @@
 ---------------------
 XXXFILLME
 
-EProperty
+CWProperty
 ---------
 XXXFILLME
--- a/goa/doc/devmanual_fr/chap_bases_framework_erudi.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/doc/devmanual_fr/chap_bases_framework_erudi.txt	Mon May 04 13:09:48 2009 +0200
@@ -127,7 +127,7 @@
     réponse 
   * `cursor()` retourne un curseur RQL sur la session
   * `execute(*args, **kwargs)`, raccourci vers .cursor().execute()
-  * `property_value(key)`, gestion des propriétés (`EProperty`)
+  * `property_value(key)`, gestion des propriétés (`CWProperty`)
   * le dictionaire `data` pour stocker des données pour partager de
     l'information entre les composants *durant l'éxécution de la requête*.
 
--- a/goa/doc/devmanual_fr/chap_configuration_instance.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/doc/devmanual_fr/chap_configuration_instance.txt	Mon May 04 13:09:48 2009 +0200
@@ -27,7 +27,7 @@
 
 :`main.anonymous-user`, `main.anonymous-password`:
    login et mot de passe à utiliser pour se connecter au serveur RQL lors des
-   connexions HTTP anonymes. Il faut que le compte EUser associé existe.
+   connexions HTTP anonymes. Il faut que le compte CWUser associé existe.
 
 :`main.base-url`:
    url de base du site, à utiliser pour générer les urls des pages web
@@ -143,7 +143,7 @@
 
 Configuration Eproperties
 -------------------------
-D'autres paramètres de configuration sont sous la forme d'entités `EProperty`
+D'autres paramètres de configuration sont sous la forme d'entités `CWProperty`
 dans la base de données. Il faut donc les éditer via l'interface web ou par des
 requêtes rql.
 
--- a/goa/doc/devmanual_fr/sect_definition_schema.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/doc/devmanual_fr/sect_definition_schema.txt	Mon May 04 13:09:48 2009 +0200
@@ -27,8 +27,8 @@
   - `eid` (`Int`)
   - `creation_date` (`Datetime`)
   - `modification_date` (`Datetime`)
-  - `owned_by` (`EUser`)
-  - `is` (`EEType`)
+  - `owned_by` (`CWUser`)
+  - `is` (`CWEType`)
 
 * il est également possible de définir des relations dont le type d'entité est
   l'objet en utilisant `ObjectRelation` plutôt que `SubjectRelation`
@@ -158,7 +158,7 @@
     inlined = True
     cardinality = '?*'
     subject = '*'
-    object = 'EUser'
+    object = 'CWUser'
 
 En plus des permissions, les propriétés propres aux types de relation (et donc
 partagés par toutes les définitions de relation de ce type) sont :
@@ -254,16 +254,16 @@
     'add'/'read' son pris en considération
 
 
-En plus de cela, le type d'entité `EPermission` de la librairie standard permet
+En plus de cela, le type d'entité `CWPermission` de la librairie standard permet
 de construire des modèles de sécurités très complexes et dynamiques. Le schéma
 de ce type d'entité est le suivant : ::
 
 
-    class EPermission(MetaEntityType):
+    class CWPermission(MetaEntityType):
 	"""entity type that may be used to construct some advanced security configuration
 	"""
 	name = String(required=True, indexed=True, internationalizable=True, maxsize=100)
-	require_group = SubjectRelation('EGroup', cardinality='+*',
+	require_group = SubjectRelation('CWGroup', cardinality='+*',
 					description=_('groups to which the permission is granted'))
 	require_state = SubjectRelation('State',
 				    description=_("entity'state in which the permission is applyable"))
@@ -302,7 +302,7 @@
 		       }
 	inlined = True
 
-Cette configuration suppose indique qu'une entité `EPermission` de nom
+Cette configuration suppose indique qu'une entité `CWPermission` de nom
 "add_version" peut-être associée à un projet et donner le droit de créer des
 versions sur ce projet à des groupes spécifiques. Il est important de noter les
 points suivants :
@@ -310,7 +310,7 @@
 * dans ce cas il faut protéger à la fois le type d'entité "Version" et la
   relation liant une version à un projet ("version_of")
 
-* du fait de la généricité du type d'entité `EPermission`, il faut effectuer
+* du fait de la généricité du type d'entité `CWPermission`, il faut effectuer
   l'unification avec les groupes et / ou les états le cas échéant dans
   l'expression ("U in_group G, P require_group G" dans l'exemple ci-dessus)
 
--- a/goa/doc/devmanual_fr/sect_stdlib_schemas.txt	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/doc/devmanual_fr/sect_stdlib_schemas.txt	Mon May 04 13:09:48 2009 +0200
@@ -9,10 +9,10 @@
 Schémas "systèmes"
 ``````````````````
 
-* `EUser`, utilisateurs du système
-* `EGroup`, groupes d'utilisateurs
-* `EEType`, types d'entité
-* `ERType`, types de relation
+* `CWUser`, utilisateurs du système
+* `CWGroup`, groupes d'utilisateurs
+* `CWEType`, types d'entité
+* `CWRType`, types de relation
 
 * `State`, état d'un workflow
 * `Transition`, transition d'un workflow
@@ -21,8 +21,8 @@
 * `EmailAddress`, adresse électronique, utilisé par le système de notification
   pour les utilisateurs et par d'autres schéma optionnels
 
-* `EProperty`, utilisé pour configurer l'application
-* `EPermission`, utilisé pour configurer la sécurité de l'application
+* `CWProperty`, utilisé pour configurer l'application
+* `CWPermission`, utilisé pour configurer la sécurité de l'application
 
 * `Card`, fiche documentaire générique
 * `Bookmark`, un type d'entité utilisé pour permetter à un utilisateur de
--- a/goa/gaesource.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/gaesource.py	Mon May 04 13:09:48 2009 +0200
@@ -1,22 +1,19 @@
 """Adapter for google appengine source.
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from logilab.common.decorators import cached, clear_cache
-
-from cubicweb import AuthenticationError, UnknownEid, server
+from cubicweb import AuthenticationError, UnknownEid
 from cubicweb.server.sources import AbstractSource, ConnectionWrapper
 from cubicweb.server.pool import SingleOperation
 from cubicweb.server.utils import crypt_password
 from cubicweb.goa.dbinit import set_user_groups
 from cubicweb.goa.rqlinterpreter import RQLInterpreter
 
-from google.appengine.api.datastore import Key, Entity, Get, Put, Delete
-from google.appengine.api.datastore import Query
+from google.appengine.api.datastore import Key, Entity, Put, Delete
 from google.appengine.api import datastore_errors, users
     
 def _init_groups(guser, euser):
@@ -39,11 +36,11 @@
             pass
         else:
             entity.clear_related_cache(rtype, role)
-    if gaesubject.kind() == 'EUser':
+    if gaesubject.kind() == 'CWUser':
         for asession in session.repo._sessions.itervalues():
             if asession.user.eid == subject:
                 asession.user.clear_related_cache(rtype, 'subject')
-    if gaeobject.kind() == 'EUser':
+    if gaeobject.kind() == 'CWUser':
         for asession in session.repo._sessions.itervalues():
             if asession.user.eid == object:
                 asession.user.clear_related_cache(rtype, 'object')
@@ -117,9 +114,9 @@
 class GAESource(AbstractSource):
     """adapter for a system source on top of google appengine datastore"""
 
-    passwd_rql = "Any P WHERE X is EUser, X login %(login)s, X upassword P"
-    auth_rql = "Any X WHERE X is EUser, X login %(login)s, X upassword %(pwd)s"
-    _sols = ({'X': 'EUser', 'P': 'Password'},)
+    passwd_rql = "Any P WHERE X is CWUser, X login %(login)s, X upassword P"
+    auth_rql = "Any X WHERE X is CWUser, X login %(login)s, X upassword %(pwd)s"
+    _sols = ({'X': 'CWUser', 'P': 'Password'},)
     
     options = ()
     
@@ -160,7 +157,7 @@
         """set the application'schema"""
         self.interpreter = RQLInterpreter(schema)
         self.schema = schema
-        if 'EUser' in schema and not self.repo.config['use-google-auth']:
+        if 'CWUser' in schema and not self.repo.config['use-google-auth']:
             # rql syntax trees used to authenticate users
             self._passwd_rqlst = self.compile_rql(self.passwd_rql)
             self._auth_rqlst = self.compile_rql(self.auth_rql)
@@ -188,7 +185,7 @@
         # XXX http://code.google.com/appengine/docs/users/userobjects.html
         # use a reference property to automatically work with email address
         # changes after the propagation feature is implemented
-        key = Key.from_path('EUser', 'key_' + login, parent=None)
+        key = Key.from_path('CWUser', 'key_' + login, parent=None)
         try:
             euser = session.datastore_get(key)
             # XXX fix user. Required until we find a better way to fix broken records
@@ -198,14 +195,14 @@
             return str(key)
         except datastore_errors.EntityNotFoundError:
             # create a record for this user
-            euser = Entity('EUser', name='key_' + login)
+            euser = Entity('CWUser', name='key_' + login)
             euser['s_login'] = login
             _init_groups(guser, euser)
             Put(euser)
             return str(euser.key())
         
     def authenticate_local(self, session, login, password):
-        """return EUser eid for the given login/password if this account is
+        """return CWUser eid for the given login/password if this account is
         defined in this source, else raise `AuthenticationError`
 
         two queries are needed since passwords are stored crypted, so we have
@@ -252,7 +249,7 @@
         """replace an entity in the source"""
         gaeentity = entity.to_gae_model()
         _mark_modified(session, entity.to_gae_model())
-        if gaeentity.kind() == 'EUser':
+        if gaeentity.kind() == 'CWUser':
             for asession in self.repo._sessions.itervalues():
                 if asession.user.eid == entity.eid:
                     asession.user.update(dict(gaeentity))
--- a/goa/goaconfig.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/goaconfig.py	Mon May 04 13:09:48 2009 +0200
@@ -11,7 +11,7 @@
 
 from cubicweb import CW_SOFTWARE_ROOT
 from cubicweb.cwconfig import CubicWebConfiguration
-from cubicweb.web.webconfig import WebConfiguration, merge_options, Method
+from cubicweb.web.webconfig import WebConfiguration, merge_options
 from cubicweb.server.serverconfig import ServerConfiguration
 from cubicweb.goa.dbmyams import load_schema
 
--- a/goa/goactl.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/goactl.py	Mon May 04 13:09:48 2009 +0200
@@ -1,21 +1,21 @@
 """cubicweb on appengine plugins for cubicweb-ctl
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from os.path import exists, join, split, dirname, basename, normpath, abspath
+from os.path import exists, join, split, basename, normpath, abspath
+
+from logilab.common.clcommands import register_commands
 
-from cubicweb import BadCommandUsage
-from cubicweb import CW_SOFTWARE_ROOT
-from cubicweb.toolsutils import (Command, register_commands, copy_skeleton,
-                              create_dir, create_symlink, create_copy)
+from cubicweb import CW_SOFTWARE_ROOT, BadCommandUsage
+from cubicweb.toolsutils import (Command, copy_skeleton, create_symlink,
+                                 create_dir)
 from cubicweb.cwconfig import CubicWebConfiguration
 
 from logilab import common as lgc
-from logilab.common.textutils import get_csv
 from logilab import constraint as lgcstr
 from logilab import mtconverter as lgmtc
 import rql, yams, yapps, simplejson, dateutil, vobject, docutils, roman
@@ -47,28 +47,28 @@
     '__init__.py',
     '__pkginfo__.py',
     '_exceptions.py',
+    'appobject.py',
     'dbapi.py',
     'cwvreg.py',
     'cwconfig.py',
+    'entity.py',
     'interfaces.py',
     'rset.py',
     'schema.py',
     'schemaviewer.py',
+    'selectors.py',
+    'utils.py',
     'vregistry.py',
+    'view.py',
     
-    'common/appobject.py',
-    'common/entity.py',
-    'common/html4zope.py',
     'common/mail.py',
     'common/migration.py',
     'common/mixins.py',
     'common/mttransforms.py',
-    'common/registerers.py',
-    'common/rest.py',
-    'common/selectors.py',
-    'common/view.py',
     'common/uilib.py',
-    'common/utils.py',
+
+    'ext/html4zope.py',
+    'ext/rest.py',
 
     'server/hookhelper.py',
     'server/hooksmanager.py',
--- a/goa/rqlinterpreter.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/rqlinterpreter.py	Mon May 04 13:09:48 2009 +0200
@@ -1,20 +1,17 @@
 """provide a minimal RQL support for google appengine dbmodel
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from mx.DateTime import DateTimeType, DateTimeDeltaType
 from datetime import datetime
 
 from rql import RQLHelper, nodes
-from logilab.common.compat import any
 
 from cubicweb import Binary
 from cubicweb.rset import ResultSet
-from cubicweb.goa import mx2datetime, datetime2mx
 from cubicweb.server import SQL_CONNECT_HOOKS
 
 from google.appengine.api.datastore import Key, Get, Query, Entity
@@ -250,9 +247,6 @@
     def __init__(self, rel, kwargs):
         RelationRestriction.__init__(self, rel, None)
         value = self.rhs.eval(kwargs)
-        if isinstance(value, (DateTimeType, DateTimeDeltaType)):
-            #yamstype = self.schema.rschema(self.rtype).objects()[0]
-            value = mx2datetime(value, 'Datetime')
         self.value = value
         if self.operator == 'ILIKE':
             if value.startswith('%'):
@@ -294,11 +288,7 @@
         RelationRestriction.__init__(self, rel, None)
         values = []
         for c in self.rel.children[1].iget_nodes(nodes.Constant):
-            value = c.eval(kwargs)
-            if isinstance(value, (DateTimeType, DateTimeDeltaType)):
-                #yamstype = self.schema.rschema(self.rtype).objects()[0]
-                value = mx2datetime(value, 'Datetime')
-            values.append(value)
+            values.append(c.eval(kwargs))
         self.value = values
 
     @property
@@ -322,9 +312,7 @@
 
 def append_result(res, descr, i, j, value, etype):
     if value is not None:
-        if etype in ('Date', 'Datetime', 'Time'):
-            value = datetime2mx(value, etype)
-        elif isinstance(value, Text):
+        if isinstance(value, Text):
             value = unicode(value)
         elif isinstance(value, Blob):
             value = Binary(str(value))
--- a/goa/skel/views.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/skel/views.py	Mon May 04 13:09:48 2009 +0200
@@ -1,10 +1,8 @@
 # custom application views
-
-from mx.DateTime import DateTime
+from datetime import date
 
-from cubicweb.web.views import baseviews
-from cubicweb.web.views.boxes import BoxTemplate
-from cubicweb.web.views.calendar import MONTHNAMES
+from cubicweb.utils import last_day
+from cubicweb.web.views import baseviews, boxes, calendar
 from cubicweb.web.htmlwidgets import BoxLink, BoxWidget
 
 _ = unicode
@@ -12,15 +10,15 @@
 
 class BlogEntryPrimaryView(baseviews.PrimaryView):
     accepts = ('BlogEntry',)
-    
+
     def cell_call(self, row, col):
         entity = self.entity(row, col)
         self.w(u'<h1>%s</h1>' % entity.dc_title())
         entity.view('metadata', w=self.w)
         self.w(entity.printable_value('text'))
-        
+
 
-class BlogArchiveBox(BoxTemplate):
+class BlogArchiveBox(boxes.BoxTemplate):
     """side box usually displaying some related entities in a primary view"""
     id = 'blog_archives_box'
     title = _('blog archives')
@@ -37,12 +35,12 @@
                 blogmonths.append( (year, month) )
         box = BoxWidget(_('Blog archives'), id=self.id)
         for year, month in blogmonths:
-            firstday = DateTime(year, month, 1)
-            lastday = DateTime(year, month, firstday.days_in_month)
+            firstday = date(year, month, 1)
+            lastday = last_day(firstday)
             rql = ('Any B WHERE B is BlogEntry, B creation_date >= "%s", B creation_date <= "%s"'
                    % (firstday.strftime('%Y-%m-%d'), lastday.strftime('%Y-%m-%d')))
             url = self.build_url(rql=rql)
-            label = u'%s %s' % (_(MONTHNAMES[month-1]), year)
+            label = u'%s %s' % (_(calendar.MONTHNAMES[month-1]), year)
             box.append( BoxLink(url, label) )
         box.render(self.w)
 
--- a/goa/test/unittest_db.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/test/unittest_db.py	Mon May 04 13:09:48 2009 +0200
@@ -18,20 +18,20 @@
     MODEL_CLASSES = (Blog,)
 
     def test_set_none_relation(self):
-        eprop = self.add_entity('EProperty', pkey=u'ui.language', value=u'en')
+        eprop = self.add_entity('CWProperty', pkey=u'ui.language', value=u'en')
         self.failUnless('s_for_user' in eprop._dbmodel)
         self.assertEquals(eprop._dbmodel['s_for_user'], None)
 
     def test_euser_key(self):
-        euser = self.add_entity('EUser', login=u'toto', upassword='toto')
+        euser = self.add_entity('CWUser', login=u'toto', upassword='toto')
         self.assertEquals(euser.key().name(), 'key_toto')
         
     def test_egroup_key(self):
-        egroup = self.add_entity('EGroup', name=u'toto')
+        egroup = self.add_entity('CWGroup', name=u'toto')
         self.assertEquals(egroup.key().name(), 'key_toto')
 
     def test_password_encryption(self):
-        euser = self.add_entity('EUser', login=u'toto', upassword='toto')
+        euser = self.add_entity('CWUser', login=u'toto', upassword='toto')
         self.failUnless(euser.upassword != 'toto', euser.upassword)
         self.assertEquals(crypt_password('toto', euser.upassword[:2]), euser.upassword)
 
--- a/goa/test/unittest_editcontroller.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/test/unittest_editcontroller.py	Mon May 04 13:09:48 2009 +0200
@@ -8,7 +8,7 @@
 from cubicweb.web import INTERNAL_FIELD_VALUE, Redirect
 
 from cubicweb.goa.goaconfig import GAEConfiguration
-from cubicweb.entities.authobjs import EUser
+from cubicweb.entities.authobjs import CWUser
 
 
 class EditControllerTC(GAEBasedTC):
@@ -73,7 +73,7 @@
     def test_validation_unique(self):
         """test creation of two linked entities"""        
         user = self.user
-        self.req.form = {'eid': 'X', '__type:X': 'EUser',
+        self.req.form = {'eid': 'X', '__type:X': 'CWUser',
                          'login:X': self.user.login, 'edits-login:X': u'', 
                          'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'', 
                          }
@@ -84,13 +84,13 @@
         """checking that a manager user can edit itself"""
         self.skip('missing actual gae support, retry latter')
         user = self.user
-        basegroups = [str(eid) for eid, in self.req.execute('EGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
-        groupeids = [eid for eid, in self.req.execute('EGroup G WHERE G name in ("managers", "users")')]
+        basegroups = [str(eid) for eid, in self.req.execute('CWGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
+        groupeids = [eid for eid, in self.req.execute('CWGroup G WHERE G name in ("managers", "users")')]
         groups = [str(eid) for eid in groupeids]
         stateeid = [eid for eid, in self.req.execute('State S WHERE S name "activated"')][0]
         self.req.form = {
             'eid':       user.eid,
-            '__type:'+user.eid:    'EUser',
+            '__type:'+user.eid:    'CWUser',
             'login:'+user.eid:     unicode(user.login),
             'firstname:'+user.eid: u'Th\xe9nault',
             'surname:'+user.eid:   u'Sylvain',
@@ -115,10 +115,10 @@
         user = self.create_user('user')
         cnx = self.login('user')
         req = self.request()
-        #self.assertEquals(self.ctrl.schema['EUser']._groups['read'],
+        #self.assertEquals(self.ctrl.schema['CWUser']._groups['read'],
         #                  ('managers', 'users'))
         req.form = {
-            'eid': user.eid, '__type:'+user.eid: 'EUser',
+            'eid': user.eid, '__type:'+user.eid: 'CWUser',
             '__maineid' : str(user.eid),
             'upassword:'+user.eid: 'tournicoton',
             'upassword-confirm:'+user.eid: 'tournicoton',
@@ -134,10 +134,10 @@
         relations (meaning no changes)
         """
         user = self.user
-        groupeids = [eid for eid, in self.req.execute('EGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
+        groupeids = [eid for eid, in self.req.execute('CWGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
         self.req.form = {
             'eid':       user.eid,
-            '__type:'+user.eid:    'EUser',
+            '__type:'+user.eid:    'CWUser',
             'login:'+user.eid:     unicode(user.login),
             'firstname:'+user.eid: u'Th\xe9nault',
             'surname:'+user.eid:   u'Sylvain',
@@ -158,10 +158,10 @@
         
         
     def test_create_multiple_linked(self):
-        gueid = self.req.execute('EGroup G WHERE G name "users"')[0][0]
+        gueid = self.req.execute('CWGroup G WHERE G name "users"')[0][0]
         self.req.form = {'eid': ['X', 'Y'],
                          
-                         '__type:X': 'EUser',
+                         '__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'', 
@@ -184,7 +184,7 @@
     def test_edit_multiple_linked(self):
         peid = self.create_user('adim').eid
         self.req.form = {'eid': [peid, 'Y'],
-                         '__type:%s'%peid: 'EUser',
+                         '__type:%s'%peid: 'CWUser',
                          'surname:%s'%peid: u'Di Masci', 'edits-surname:%s'%peid: '',
                          
                          '__type:Y': 'EmailAddress',
@@ -203,7 +203,7 @@
         
         emaileid = email.eid
         self.req.form = {'eid': [peid, emaileid],
-                         '__type:%s'%peid: 'EUser',
+                         '__type:%s'%peid: 'CWUser',
                          'surname:%s'%peid: u'Di Masci', 'edits-surname:%s'%peid: 'Di Masci',
                          '__type:%s'%emaileid: 'EmailAddress',
                          'address:%s'%emaileid: u'adim@logilab.fr', 'edits-address:%s'%emaileid: 'dima@logilab.fr',
@@ -226,13 +226,13 @@
         """        
         user = self.user
         self.req.form = {'__cloned_eid:X': user.eid,
-                         'eid': 'X', '__type:X': 'EUser',
+                         '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': 'EUser',
+                         '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'', 
                          }
@@ -241,7 +241,7 @@
 
     def test_req_pending_insert(self):
         """make sure req's pending insertions are taken into account"""
-        tmpgroup = self.add_entity('EGroup', name=u"test")
+        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()
@@ -254,7 +254,7 @@
     def test_req_pending_delete(self):
         """make sure req's pending deletions are taken into account"""
         user = self.user
-        groupeid = self.req.execute('INSERT EGroup G: G name "test", U in_group G WHERE U eid %(x)s',
+        groupeid = self.req.execute('INSERT CWGroup G: G name "test", U in_group G WHERE U eid %(x)s',
                                     {'x': user.eid})[0][0]
         usergroups = [gname for gname, in
                       self.req.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
@@ -273,13 +273,13 @@
         def custom_login_edit(self, formparams, value, relations):
             formparams['login'] = value.upper()
             relations.append('X login %(login)s')
-        EUser.custom_login_edit = custom_login_edit
+        CWUser.custom_login_edit = custom_login_edit
         try:
             user = self.user
             eid = repr(user.eid)
             self.req.form = {
                 'eid': eid,
-                '__type:'+eid:  'EUser',
+                '__type:'+eid:  'CWUser',
                 'login:'+eid: u'foo',
                 'edits-login:'+eid:  unicode(user.login),
                 }
@@ -287,7 +287,7 @@
             rset = self.req.execute('Any L WHERE X eid %(x)s, X login L', {'x': user.eid}, 'x')
             self.assertEquals(rset[0][0], 'FOO')
         finally:
-            del EUser.custom_login_edit
+            del CWUser.custom_login_edit
         
     def test_redirect_apply_button(self):
         redirectrql = rql_for_eid(4012) # whatever
@@ -358,10 +358,10 @@
         
 
     def test_nonregr_multiple_empty_email_addr(self):
-        gueid = self.req.execute('EGroup G WHERE G name "users"')[0][0]
+        gueid = self.req.execute('CWGroup G WHERE G name "users"')[0][0]
         self.req.form = {'eid': ['X', 'Y'],
                          
-                         '__type:X': 'EUser',
+                         '__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, 
@@ -385,7 +385,7 @@
             self.req.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': 'EUser',
+                             'eid': 'X', '__type:X': 'CWUser',
                              'login': u'dodo', 'edits-login': u'dodo', 
                              'surname:X': u'Boom', 'edits-surname:X': u'',
                              '__errorurl' : "whatever but required",
@@ -400,7 +400,7 @@
                 self.req.form['rql'] = 'Any X WHERE X eid %s' % p.eid
                 self.req.form['vid'] = 'copy'
                 self.env.app.publish('view', self.req)
-            rset = self.req.execute('EUser P WHERE P surname "Boom"')
+            rset = self.req.execute('CWUser P WHERE P surname "Boom"')
             self.assertEquals(len(rset), 0)
         finally:
             p.__class__.skip_copy_for = old_skips
--- a/goa/test/unittest_metadata.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/test/unittest_metadata.py	Mon May 04 13:09:48 2009 +0200
@@ -26,7 +26,7 @@
         GAEBasedTC.setUp(self)
         self.req = self.request()
         self.a = self.add_entity('Article')
-        self.p = self.add_entity('EProperty', pkey=u'ui.language', value=u'en')
+        self.p = self.add_entity('CWProperty', pkey=u'ui.language', value=u'en')
         self.session.commit()
         
     def _test_timestamp(self, entity, attr, sleep=0.1):
@@ -96,9 +96,9 @@
         en = self.execute('Any EN WHERE E name EN, X is E, X eid %(x)s', {'x': self.a.eid}, 'x')[0][0]
         self.assertEquals(en, 'Article')
         en = self.execute('Any EN WHERE E name EN, X is E, X eid %(x)s', {'x': self.p.eid}, 'x')[0][0]
-        self.assertEquals(en, 'EProperty') 
+        self.assertEquals(en, 'CWProperty') 
         en = self.execute('Any EN WHERE E name EN, X is E, X eid %(x)s', {'x': self.req.user.eid}, 'x')[0][0]
-        self.assertEquals(en, 'EUser')
+        self.assertEquals(en, 'CWUser')
 
         
 if __name__ == '__main__':
--- a/goa/test/unittest_rql.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/test/unittest_rql.py	Mon May 04 13:09:48 2009 +0200
@@ -566,13 +566,13 @@
         self.assertEquals(rset.rows, [[article2.eid]])
         
     def test_8_not_relation_final_1(self):
-        rset = self.req.execute('Any G WHERE G is EGroup, NOT G name "guests"')
+        rset = self.req.execute('Any G WHERE G is CWGroup, NOT G name "guests"')
         self._check_rset_size(rset, 2, 1)
         self.assertUnorderedIterableEquals([g.name for g in rset.entities()],
                                            ['users', 'managers'])        
         
     def test_8_not_relation_final_2(self):
-        rset = self.req.execute('Any GN WHERE G is EGroup, NOT G name "guests", G name GN')
+        rset = self.req.execute('Any GN WHERE G is CWGroup, NOT G name "guests", G name GN')
         self._check_rset_size(rset, 2, 1)
         self.assertUnorderedIterableEquals([gn for gn, in rset.rows],
                                            ['users', 'managers'])
--- a/goa/test/unittest_schema.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/test/unittest_schema.py	Mon May 04 13:09:48 2009 +0200
@@ -21,8 +21,8 @@
                              set(('Boolean', 'Bytes', 'Date', 'Datetime', 'Float',
                               'Decimal',
                               'Int', 'Interval', 'Password', 'String', 'Time',
-                              'EEType', 'EGroup', 'EPermission', 'EProperty', 'ERType',
-                              'EUser', 'EmailAddress',
+                              'CWEType', 'CWGroup', 'CWPermission', 'CWProperty', 'CWRType',
+                              'CWUser', 'EmailAddress',
                               'RQLExpression', 'State', 'Transition', 'TrInfo',
                               'Article', 'Blog', 'YamsEntity')))
         self.assertSetEquals(set(str(e) for e in schema.relations()),
@@ -66,7 +66,7 @@
                              ('ambiguous_relation', 'talks_about', 'identity'))
 
     def test_yams_imported(self):
-        eschema = self.schema['EProperty']
+        eschema = self.schema['CWProperty']
         # only relations defined in the class are actually ordered
         orels = [str(e) for e in eschema.ordered_relations()]
         orels, others = orels[:3], orels[3:]
@@ -87,7 +87,7 @@
                              ('Blog', 'Article'))
 
     def test_euser(self):
-        eschema = self.schema['EUser']
+        eschema = self.schema['CWUser']
         # XXX pretend to have some relations it has not
         self.assertEquals([str(e) for e in eschema.ordered_relations()],
                           ['login', 'firstname', 'surname', 'last_login_time',
--- a/goa/testlib.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/testlib.py	Mon May 04 13:09:48 2009 +0200
@@ -1,4 +1,4 @@
-from logilab.common.testlib import TestCase, mock_object
+from logilab.common.testlib import TestCase
 
 import os, os.path as osp
 import time
@@ -15,11 +15,10 @@
     from google.appengine.api import datastore_file_stub
     from google.appengine.ext import db as gdb
     from cubicweb.goa import db, do_monkey_patch
-    from cubicweb.goa.dbmyams import load_schema
     import_appengine_failed = None
 except ImportError, exc:
-    raise
-    class db:
+    # XXX necessary ?
+    class db: 
         class Model:
             pass
         class DummyProperty:
@@ -34,8 +33,6 @@
     import_appengine_failed = 'cannot import appengine: %s' % exc
     
 
-from cubicweb import CW_SOFTWARE_ROOT
-from cubicweb.server.utils import crypt_password
 from cubicweb.devtools.fake import FakeRequest
 from cubicweb.goa.goavreg import GAERegistry
 from cubicweb.goa.goaconfig import GAEConfiguration
@@ -180,7 +177,7 @@
         
     def create_user(self, login, groups=('users',), req=None):
         assert not self.config['use-google-auth']
-        user = self.add_entity('EUser', upassword=str(login), login=unicode(login))
+        user = self.add_entity('CWUser', upassword=str(login), login=unicode(login))
         cu = self.session.cursor()
         cu.execute('SET X in_group G WHERE X eid %%(x)s, G name IN(%s)'
                     % ','.join(repr(g) for g in groups),
--- a/goa/tools/i18n.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,282 +0,0 @@
-#!/usr/bin/env python
-"""This script is just a thin wrapper around ``msgcat`` and ``msgfmt``
-to generate ``.mo`` files
-"""
-
-import sys
-import os
-import os.path as osp
-import shutil
-from tempfile import mktemp
-from glob import glob
-from mx.DateTime import now
-
-from logilab.common.fileutils import ensure_fs_mode
-from logilab.common.shellutils import find, rm
-
-from yams import BASE_TYPES
-
-from cubicweb import CW_SOFTWARE_ROOT
-# from cubicweb.__pkginfo__ import version as cubicwebversion
-cubicwebversion = '2.48.2'
-
-DEFAULT_POT_HEAD = r'''# LAX application po file
-
-msgid ""
-msgstr ""
-"Project-Id-Version: cubicweb %s\n"
-"PO-Revision-Date: 2008-03-28 18:14+0100\n"
-"Last-Translator: Logilab Team <contact@logilab.fr>\n"
-"Language-Team: fr <contact@logilab.fr>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Generated-By: cubicweb-devtools\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-''' % cubicwebversion
-
-
-STDLIB_ERTYPES = BASE_TYPES | set( ('EUser', 'EProperty', 'Card', 'identity', 'for_user') )
-
-def create_dir(directory):
-    """create a directory if it doesn't exist yet"""
-    try:
-        os.makedirs(directory)
-        print 'created directory', directory
-    except OSError, ex:
-        import errno
-        if ex.errno != errno.EEXIST:
-            raise
-        print 'directory %s already exists' % directory
-
-def execute(cmd):
-    """display the command, execute it and raise an Exception if returned
-    status != 0
-    """
-    print cmd.replace(os.getcwd() + os.sep, '')
-    status = os.system(cmd)
-    if status != 0:
-        raise Exception()
-
-def add_msg(w, msgid):
-    """write an empty pot msgid definition"""
-    if isinstance(msgid, unicode):
-        msgid = msgid.encode('utf-8')
-    msgid = msgid.replace('"', r'\"').splitlines()
-    if len(msgid) > 1:
-        w('msgid ""\n')
-        for line in msgid:
-            w('"%s"' % line.replace('"', r'\"'))
-    else:
-        w('msgid "%s"\n' % msgid[0])
-    w('msgstr ""\n\n')
-
-
-def generate_schema_pot(w, vreg, tmpldir):
-    """generate a pot file with schema specific i18n messages
-
-    notice that relation definitions description and static vocabulary
-    should be marked using '_' and extracted using xgettext
-    """
-    cube = tmpldir and osp.split(tmpldir)[-1]
-    config = vreg.config
-    vreg.register_objects(config.vregistry_path())
-    w(DEFAULT_POT_HEAD)
-    _generate_schema_pot(w, vreg, vreg.schema, libschema=None, # no libschema for now
-                         cube=cube)
-
-
-def _generate_schema_pot(w, vreg, schema, libschema=None, cube=None):
-    w('# schema pot file, generated on %s\n' % now().strftime('%Y-%m-%d %H:%M:%S'))
-    w('# \n')
-    w('# singular and plural forms for each entity type\n')
-    w('\n')
-    # XXX hard-coded list of stdlib's entity schemas
-    libschema = libschema or STDLIB_ERTYPES
-    entities = [e for e in schema.entities() if not e in libschema]
-    done = set()
-    for eschema in sorted(entities):
-        etype = eschema.type
-        add_msg(w, etype)
-        add_msg(w, '%s_plural' % etype)
-        if not eschema.is_final():
-            add_msg(w, 'This %s' % etype)
-            add_msg(w, 'New %s' % etype)
-            add_msg(w, 'add a %s' % etype)
-            add_msg(w, 'remove this %s' % etype)
-        if eschema.description and not eschema.description in done:
-            done.add(eschema.description)
-            add_msg(w, eschema.description)
-    w('# subject and object forms for each relation type\n')
-    w('# (no object form for final relation types)\n')
-    w('\n')
-    if libschema is not None:
-        relations = [r for r in schema.relations() if not r in libschema]
-    else:
-        relations = schema.relations()
-    for rschema in sorted(set(relations)):
-        rtype = rschema.type
-        add_msg(w, rtype)
-        if not (schema.rschema(rtype).is_final() or rschema.symetric):
-            add_msg(w, '%s_object' % rtype)
-        if rschema.description and rschema.description not in done:
-            done.add(rschema.description)
-            add_msg(w, rschema.description)
-    w('# add related box generated message\n')
-    w('\n')
-    for eschema in schema.entities():
-        if eschema.is_final():
-            continue
-        entity = vreg.etype_class(eschema)(None, None)
-        for x, rschemas in (('subject', eschema.subject_relations()),
-                            ('object', eschema.object_relations())):
-            for rschema in rschemas:
-                if rschema.is_final():
-                    continue
-                for teschema in rschema.targets(eschema, x):
-                    if defined_in_library(libschema, eschema, rschema, teschema, x):
-                        continue
-                    if entity.relation_mode(rschema.type, teschema.type, x) == 'create':
-                        if x == 'subject':
-                            label = 'add %s %s %s %s' % (eschema, rschema, teschema, x)
-                            label2 = "creating %s (%s %%(linkto)s %s %s)" % (teschema, eschema, rschema, teschema)
-                        else:
-                            label = 'add %s %s %s %s' % (teschema, rschema, eschema, x)
-                            label2 = "creating %s (%s %s %s %%(linkto)s)" % (teschema, teschema, rschema, eschema)
-                        add_msg(w, label)
-                        add_msg(w, label2)
-    cube = (cube or 'cubicweb') + '.'
-    done = set()
-    for reg, objdict in vreg.items():
-        for objects in objdict.values():
-            for obj in objects:
-                objid = '%s_%s' % (reg, obj.id)
-                if objid in done:
-                    continue
-                if obj.__module__.startswith(cube) and obj.property_defs:
-                    add_msg(w, '%s_description' % objid)
-                    add_msg(w, objid)
-                    done.add(objid)
-                    
-def defined_in_library(libschema, etype, rtype, tetype, x):
-    """return true if the given relation definition exists in cubicweb's library"""
-    if libschema is None:
-        return False
-    if x == 'subject':
-        subjtype, objtype = etype, tetype
-    else:
-        subjtype, objtype = tetype, etype
-    try:
-        return libschema.rschema(rtype).has_rdef(subjtype, objtype)
-    except (KeyError, AttributeError):
-        # if libschema is a simple list of entity types (lax specific)
-        # or if the relation could not be found
-        return False
-
-
-
-# XXX check if this is a pure duplication of the original
-# `cubicweb.common.i18n` function
-def compile_i18n_catalogs(sourcedirs, destdir, langs):
-    """generate .mo files for a set of languages into the `destdir` i18n directory
-    """
-    print 'compiling %s catalogs...' % destdir
-    errors = []
-    for lang in langs:
-        langdir = osp.join(destdir, lang, 'LC_MESSAGES')
-        if not osp.exists(langdir):
-            create_dir(langdir)
-        pofiles = [osp.join(path, '%s.po' % lang) for path in sourcedirs]
-        pofiles = [pof for pof in pofiles if osp.exists(pof)]
-        mergedpo = osp.join(destdir, '%s_merged.po' % lang)
-        try:
-            # merge application messages' catalog with the stdlib's one
-            execute('msgcat --use-first --sort-output --strict %s > %s'
-                    % (' '.join(pofiles), mergedpo))
-            # make sure the .mo file is writeable and compile with *msgfmt*
-            applmo = osp.join(destdir, lang, 'LC_MESSAGES', 'cubicweb.mo')
-            try:
-                ensure_fs_mode(applmo)
-            except OSError:
-                pass # suppose not osp.exists
-            execute('msgfmt %s -o %s' % (mergedpo, applmo))
-        except Exception, ex:
-            errors.append('while handling language %s: %s' % (lang, ex))
-        try:
-            # clean everything
-            os.unlink(mergedpo)
-        except Exception:
-            continue
-    return errors
-
-
-def update_cubes_catalog(vreg, appdirectory, langs):
-    toedit = []
-    tmpl = osp.basename(osp.normpath(appdirectory))
-    tempdir = mktemp()
-    os.mkdir(tempdir)
-    print '*' * 72
-    print 'updating %s cube...' % tmpl
-    os.chdir(appdirectory)
-    potfiles = []
-    if osp.exists(osp.join('i18n', 'entities.pot')):
-        potfiles = potfiles.append( osp.join('i18n', 'entities.pot') )
-    print '******** extract schema messages'
-    schemapot = osp.join(tempdir, 'schema.pot')
-    potfiles.append(schemapot)
-    # XXX
-    generate_schema_pot(open(schemapot, 'w').write, vreg, appdirectory)
-    print '******** extract Javascript messages'
-    jsfiles =  find('.', '.js')
-    if jsfiles:
-        tmppotfile = osp.join(tempdir, 'js.pot')
-        execute('xgettext --no-location --omit-header -k_ -L java --from-code=utf-8 -o %s %s'
-                % (tmppotfile, ' '.join(jsfiles)))
-        # no pot file created if there are no string to translate
-        if osp.exists(tmppotfile): 
-            potfiles.append(tmppotfile)
-    print '******** create cube specific catalog'
-    tmppotfile = osp.join(tempdir, 'generated.pot')
-    execute('xgettext --no-location --omit-header -k_ -o %s %s'
-            % (tmppotfile, ' '.join(glob('*.py'))))
-    if osp.exists(tmppotfile): # doesn't exists of no translation string found
-        potfiles.append(tmppotfile)
-    potfile = osp.join(tempdir, 'cube.pot')
-    print '******** merging .pot files'
-    execute('msgcat %s > %s' % (' '.join(potfiles), potfile))
-    print '******** merging main pot file with existing translations'
-    os.chdir('i18n')
-    for lang in langs:
-        print '****', lang
-        tmplpo = '%s.po' % lang
-        if not osp.exists(tmplpo):
-            shutil.copy(potfile, tmplpo)
-        else:
-            execute('msgmerge -N -s %s %s > %snew' % (tmplpo, potfile, tmplpo))
-            ensure_fs_mode(tmplpo)
-            shutil.move('%snew' % tmplpo, tmplpo)
-        toedit.append(osp.abspath(tmplpo))
-    # cleanup
-    rm(tempdir)
-    # instructions pour la suite
-    print '*' * 72
-    print 'you can now edit the following files:'
-    print '* ' + '\n* '.join(toedit)
-             
-
-def getlangs(i18ndir):
-    return [fname[:-3] for fname in os.listdir(i18ndir)
-            if fname.endswith('.po')]
-
-
-def get_i18n_directory(appdirectory):
-    if not osp.isdir(appdirectory):
-        print '%s is not an application directory' % appdirectory
-        sys.exit(2)
-    i18ndir = osp.join(appdirectory, 'i18n')
-    if not osp.isdir(i18ndir):
-        print '%s is not an application directory ' \
-            '(i18n subdirectory missing)' % appdirectory
-        sys.exit(2)
-    return i18ndir
--- a/goa/tools/laxctl.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/goa/tools/laxctl.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """provides all lax instances management commands into a single utility script
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -17,14 +17,9 @@
 
 from logilab.common.clcommands import Command, register_commands, main_run
 
-from cubicweb import CW_SOFTWARE_ROOT
 from cubicweb.common.uilib import remove_html_tags
-
 APPLROOT = osp.abspath(osp.join(osp.dirname(osp.abspath(__file__)), '..'))
 
-# XXX import custom?
-
-from tools import i18n
 
 def initialize_vregistry(applroot):
     # apply monkey patches first
@@ -51,33 +46,6 @@
     def run(self, args):
         self.vreg = initialize_vregistry(APPLROOT)
         self._run(args)
-                
-
-class I18nUpdateCommand(LaxCommand):
-    """updates i18n catalogs"""
-    name = 'i18nupdate'
-    
-    def _run(self, args):
-        assert not args, 'no argument expected'
-        i18ndir = i18n.get_i18n_directory(APPLROOT)
-        i18n.update_cubes_catalog(self.vreg, APPLROOT,
-                                      langs=i18n.getlangs(i18ndir))
-
-
-class I18nCompileCommand(LaxCommand):
-    """compiles i18n catalogs"""
-    name = 'i18ncompile'
-    min_args = max_args = 0
-    
-    def _run(self, args):
-        assert not args, 'no argument expected'
-        i18ndir = i18n.get_i18n_directory(APPLROOT)
-        langs = i18n.getlangs(i18ndir)
-        print 'generating .mo files for langs', ', '.join(langs)
-        cubicweb_i18ndir = osp.join(APPLROOT, 'cubes', 'shared')
-        paths = self.vreg.config.cubes_path() + [cubicweb_i18ndir]
-        sourcedirs = [i18ndir] + [osp.join(path, 'i18n') for path in paths]
-        i18n.compile_i18n_catalogs(sourcedirs, i18ndir, langs=langs)
         
 
 class GenerateSchemaCommand(LaxCommand):
@@ -274,9 +242,7 @@
         self.extract_message(data)
             
     
-register_commands([I18nUpdateCommand,
-                   I18nCompileCommand,
-                   GenerateSchemaCommand,
+register_commands([GenerateSchemaCommand,
                    PopulateDataDirCommand,
                    DSInitCommand,
                    CleanSessionsCommand,
--- a/hercule.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/hercule.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """RQL client for cubicweb, connecting to application using pyro
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -11,8 +11,8 @@
 
 from logilab.common import flatten
 from logilab.common.cli import CLIHelper
-from logilab.common.clcommands import BadCommandUsage, pop_arg
-from cubicweb.toolsutils import CONNECT_OPTIONS, Command, register_commands
+from logilab.common.clcommands import BadCommandUsage, pop_arg, register_commands
+from cubicweb.toolsutils import CONNECT_OPTIONS, Command
  
 # result formatter ############################################################
 
@@ -37,7 +37,7 @@
         format_results(writer, layout, stream)
     finally:
         stream.close()
-        status = os.waitpid(pid, 0)
+        os.waitpid(pid, 0)
 
 def izip2(list1, list2):
     for i in xrange(len(list1)):
@@ -132,11 +132,11 @@
     
     def do_description(self):
         """display the description of the latest result"""
-        if self.cursor.description is None:
+        if self.rset.description is None:
             print _('No query has been executed')
         else:
             print '\n'.join([', '.join(line_desc)
-                             for line_desc in self.cursor.description])
+                             for line_desc in self.rset.description])
 
     help_do_description = ('description', "description", _(do_description.__doc__))
     
@@ -200,7 +200,7 @@
         self._previous_lines = []
         # search results
         try:
-            self.cursor.execute(query)
+            self.rset = rset = self.cursor.execute(query)
         except:
             if self.autocommit:
                 self.cnx.rollback()
@@ -208,18 +208,18 @@
         else:
             if self.autocommit:
                 self.cnx.commit()
-        self.handle_result(self.cursor.fetchall(), self.cursor.description)
+        self.handle_result(rset)
 
-    def handle_result(self, result, description):
+    def handle_result(self, rset):
         """display query results if any"""
-        if not result:
+        if not rset:
             print _('No result matching query')
         else:
             from logilab.common.ureports import Table
-            children = flatten(izip2(description, result), to_string)
-            layout = Table(cols=2*len(result[0]), children=children, cheaders=1)
+            children = flatten(izip2(rset.description, rset.rows), to_string)
+            layout = Table(cols=2*len(rset.rows[0]), children=children, cheaders=1)
             self._format(self.writer, layout)
-            print _('%s results matching query') % len(result)
+            print _('%s results matching query') % rset.rowcount
 
     def display_schema(self, schema):
         """display a schema object"""
--- a/interfaces.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/interfaces.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """Specific views for entities implementing IDownloadable
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
@@ -11,7 +11,7 @@
 
 class IEmailable(Interface):
     """interface for emailable entities"""
-    
+
     def get_email(self):
         """return email address"""
 
@@ -28,7 +28,7 @@
     def as_email_context(self):
         """returns the dictionary as used by the sendmail controller to
         build email bodies.
-        
+
         NOTE: the dictionary keys should match the list returned by the
         `allowed_massmail_keys` method.
         """
@@ -45,12 +45,12 @@
         """change the entity's state according to a state defined in given
         parameters
         """
-    
+
     def can_pass_transition(self, trname):
         """return true if the current user can pass the transition with the
         given name
         """
-    
+
     def latest_trinfo(self):
         """return the latest transition information for this entity
         """
@@ -73,7 +73,7 @@
     @property
     def todo(self):
         """what remains to be done"""
-    
+
     def progress_info(self):
         """returns a dictionary describing progress/estimated cost of the
         version.
@@ -93,19 +93,19 @@
 
     def progress(self):
         """returns the % progress of the task item"""
-        
-    
+
+
 class IMileStone(IProgress):
     """represents an ITask's item"""
-    
+
     parent_type = None # specify main task's type
-    
+
     def get_main_task(self):
         """returns the main ITask entity"""
 
     def initial_prevision_date(self):
         """returns the initial expected end of the milestone"""
-        
+
     def eta_date(self):
         """returns expected date of completion based on what remains
         to be done
@@ -128,7 +128,7 @@
 
     def __iter__(self):
         """iterates over the item's children"""
-        
+
     def is_leaf(self):
         """returns true if this node as no child"""
 
@@ -146,7 +146,7 @@
     """interface for entities which can be linked to a previous and/or next
     entity
     """
-    
+
     def next_entity(self):
         """return the 'next' entity"""
     def previous_entity(self):
@@ -155,10 +155,10 @@
 
 class IBreadCrumbs(Interface):
     """interface for entities which can be "located" on some path"""
-    
+
     def breadcrumbs(self, view, recurs=False):
         """return a list containing some:
-        
+
         * tuple (url, label)
         * entity
         * simple label string
@@ -173,7 +173,7 @@
 
 class IDownloadable(Interface):
     """interface for downloadable entities"""
-    
+
     def download_url(self): # XXX not really part of this interface
         """return an url to download entity's content"""
     def download_content_type(self):
@@ -188,31 +188,31 @@
 
 class IEmbedable(Interface):
     """interface for embedable entities"""
-    
+
     def embeded_url(self):
         """embed action interface"""
-    
+
 class ICalendarable(Interface):
-    """interface for itms that do have a begin date 'start' and an end
-date 'stop'"""    
-    
+    """interface for items that do have a begin date 'start' and an end date 'stop'
+    """
+
 class ICalendarViews(Interface):
     """calendar views interface"""
     def matching_dates(self, begin, end):
         """
         :param begin: day considered as begin of the range (`DateTime`)
         :param end: day considered as end of the range (`DateTime`)
-        
+
         :return:
           a list of dates (`DateTime`) in the range [`begin`, `end`] on which
           this entity apply
         """
-        
+
 class ITimetableViews(Interface):
     """timetable views interface"""
     def timetable_date(self):
         """XXX explain
-        
+
         :return: date (`DateTime`)
         """
 
@@ -231,17 +231,18 @@
         """returns the icon that should be used as the marker
         (returns None for default)
         """
-        
+
 class IFeed(Interface):
     """interface for entities with rss flux"""
-    
+
     def rss_feed_url(self):
         """return an url which layout sub-entities item
         """
+
 class ISiocItem(Interface):
     """interface for entities (which are item
     in sioc specification) with sioc views"""
-    
+
     def isioc_content(self):
         """return content entity"""
 
@@ -252,11 +253,11 @@
         """return container type (post, BlogPost, MailMessage)"""
 
     def isioc_replies(self):
-        """return replies items"""       
+        """return replies items"""
 
     def isioc_topics(self):
         """return topics items"""
-            
+
 class ISiocContainer(Interface):
     """interface for entities (which are container
     in sioc specification) with sioc views"""
@@ -267,5 +268,5 @@
     def isioc_items(self):
         """return contained items"""
 
-   
-    
+
+
--- a/md5crypt.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/md5crypt.py	Mon May 04 13:09:48 2009 +0200
@@ -53,7 +53,7 @@
 
 
 def crypt(pw, salt, magic=None):
-    if magic==None:
+    if magic is None:
         magic = MAGIC
     # Take care of the magic string if present
     if salt[:len(magic)] == magic:
@@ -63,7 +63,7 @@
     salt = salt[:8]
     ctx = pw + magic + salt
     final = md5.md5(pw + salt + pw).digest()
-    for pl in range(len(pw),0,-16):
+    for pl in xrange(len(pw), 0, -16):
         if pl > 16:
             ctx = ctx + final[:16]
         else:
@@ -80,7 +80,7 @@
     # The following is supposed to make
     # things run slower. 
     # my question: WTF???
-    for i in range(1000):
+    for i in xrange(1000):
         ctx1 = ''
         if i & 1:
             ctx1 = ctx1 + pw
--- a/misc/migration/2.37.1_Any.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-if 'Keyword' in schema:
-    synchronize_schema('Keyword')
--- a/misc/migration/2.39.0_Any.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-try:
-    # missing on some old databases
-    sql('CREATE INDEX entities_extid_idx ON entities(extid)')
-except:
-    pass # already exists
-checkpoint() 
-sql('CREATE INDEX entities_type_idx ON entities(type)')
-checkpoint()
-
--- a/misc/migration/2.42.1_Any.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/misc/migration/2.42.1_Any.py	Mon May 04 13:09:48 2009 +0200
@@ -14,5 +14,5 @@
 
 if 'inline_view' in schema:
     # inline_view attribute should have been deleted for a while now....
-    drop_attribute('ENFRDef', 'inline_view')
+    drop_attribute('CWRelation', 'inline_view')
     
--- a/misc/migration/2.44.0_Any.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/misc/migration/2.44.0_Any.py	Mon May 04 13:09:48 2009 +0200
@@ -1,11 +1,11 @@
-change_relation_props('EFRDef', 'cardinality', 'String', internationalizable=True)
-change_relation_props('ENFRDef', 'cardinality', 'String', internationalizable=True)
+change_relation_props('CWAttribute', 'cardinality', 'String', internationalizable=True)
+change_relation_props('CWRelation', 'cardinality', 'String', internationalizable=True)
 
-drop_relation_definition('EPermission', 'require_state', 'State')
+drop_relation_definition('CWPermission', 'require_state', 'State')
 
 if confirm('cleanup require_permission relation'):
     try:
-        newrschema = newschema.rschema('require_permission')
+        newrschema = fsschema.rschema('require_permission')
     except KeyError:
         newrschema = None
     for rsubj, robj in schema.rschema('require_permission').rdefs():
--- a/misc/migration/2.48.8_Any.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/misc/migration/2.48.8_Any.py	Mon May 04 13:09:48 2009 +0200
@@ -1,2 +1,2 @@
-for etype in ('ERType', 'EFRDef', 'ENFRDef', 'EConstraint', 'EConstraintType'):
+for etype in ('CWRType', 'CWAttribute', 'CWRelation', 'CWConstraint', 'CWConstraintType'):
     synchronize_permissions(etype)
--- a/misc/migration/2.99.0_Any.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/misc/migration/2.99.0_Any.py	Mon May 04 13:09:48 2009 +0200
@@ -1,6 +1,6 @@
 from cubicweb import CW_MIGRATION_MAP
 
-for pk, in rql('Any K WHERE X is EProperty, X pkey IN (%s), X pkey K'
+for pk, in rql('Any K WHERE X is CWProperty, X pkey IN (%s), X pkey K'
                % ','.join("'system.version.%s'" % cube for cube in CW_MIGRATION_MAP),
                ask_confirm=False):
     cube = pk.split('.')[-1]
@@ -9,4 +9,4 @@
         {'oldk': pk, 'newk': newk}, ask_confirm=False)
     print 'renamed', pk, 'to', newk
 
-add_entity_type('ECache')
+add_entity_type('CWCache')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.2.0_Any.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,4 @@
+rql('SET X value "main-template" WHERE X is CWProperty, '
+    'X pkey "ui.main-template", X value "main"')
+checkpoint()
+
--- a/misc/migration/bootstrapmigration_repository.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/misc/migration/bootstrapmigration_repository.py	Mon May 04 13:09:48 2009 +0200
@@ -3,27 +3,30 @@
 it should only include low level schema changes
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
+if applcubicwebversion < (3, 2, 0) and cubicwebversion >= (3, 2, 0):
+   add_cube('card', update_database=False)
+
 if applcubicwebversion < (2, 47, 0) and cubicwebversion >= (2, 47, 0):
     from cubicweb.server import schemaserial
     schemaserial.HAS_FULLTEXT_CONTAINER = False
-    cnx.set_shared_data('do-not-insert-is_instance_of', True)
-    add_attribute('ERType', 'fulltext_container')
+    session.set_shared_data('do-not-insert-is_instance_of', True)
+    add_attribute('CWRType', 'fulltext_container')
     schemaserial.HAS_FULLTEXT_CONTAINER = True
 
 
- 
+
 if applcubicwebversion < (2, 50, 0) and cubicwebversion >= (2, 50, 0):
-    cnx.set_shared_data('do-not-insert-is_instance_of', True)
+    session.set_shared_data('do-not-insert-is_instance_of', True)
     add_relation_type('is_instance_of')
     # fill the relation using an efficient sql query instead of using rql
     sql('INSERT INTO is_instance_of_relation '
 	'  SELECT * from is_relation')
     checkpoint()
-    cnx.set_shared_data('do-not-insert-is_instance_of', False)
+    session.set_shared_data('do-not-insert-is_instance_of', False)
 
 if applcubicwebversion < (2, 42, 0) and cubicwebversion >= (2, 42, 0):
     sql('ALTER TABLE entities ADD COLUMN mtime TIMESTAMP')
@@ -40,4 +43,4 @@
     sql('CREATE INDEX deleted_entities_dtime_idx ON deleted_entities(dtime)')
     sql('CREATE INDEX deleted_entities_extid_idx ON deleted_entities(extid)')
     checkpoint()
-   
+
--- a/misc/migration/postcreate.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/misc/migration/postcreate.py	Mon May 04 13:09:48 2009 +0200
@@ -1,22 +1,22 @@
 """cubicweb post creation script, set user's workflow"""
 
-activatedeid = add_state(_('activated'), 'EUser', initial=True)
-deactivatedeid = add_state(_('deactivated'), 'EUser')
-add_transition(_('deactivate'), 'EUser',
+activatedeid = add_state(_('activated'), 'CWUser', initial=True)
+deactivatedeid = add_state(_('deactivated'), 'CWUser')
+add_transition(_('deactivate'), 'CWUser',
                (activatedeid,), deactivatedeid,
                requiredgroups=('managers',))
-add_transition(_('activate'), 'EUser',
+add_transition(_('activate'), 'CWUser',
                (deactivatedeid,), activatedeid,
                requiredgroups=('managers',))
 
 # need this since we already have at least one user in the database (the default admin)
-rql('SET X in_state S WHERE X is EUser, S eid %s' % activatedeid)
+rql('SET X in_state S WHERE X is CWUser, S eid %s' % activatedeid)
 
 # create anonymous user if all-in-one config and anonymous user has been specified
 if hasattr(config, 'anonymous_user'):
     anonlogin, anonpwd = config.anonymous_user()
     if anonlogin:
-        rql('INSERT EUser X: X login %(login)s, X upassword %(pwd)s,'
+        rql('INSERT CWUser X: X login %(login)s, X upassword %(pwd)s,'
             'X in_state S, X in_group G WHERE G name "guests", S name "activated"',
             {'login': unicode(anonlogin), 'pwd': anonpwd})
 
@@ -30,11 +30,11 @@
         default = cfg.option_default(optname, optdict)
         # only record values differing from default
         if value != default:
-            rql('INSERT EProperty X: X pkey %(k)s, X value %(v)s', {'k': key, 'v': value})
+            rql('INSERT CWProperty X: X pkey %(k)s, X value %(v)s', {'k': key, 'v': value})
 
 # add PERM_USE_TEMPLATE_FORMAT permission
 from cubicweb.schema import PERM_USE_TEMPLATE_FORMAT
-eid = add_entity('EPermission', name=PERM_USE_TEMPLATE_FORMAT,
+eid = add_entity('CWPermission', name=PERM_USE_TEMPLATE_FORMAT,
                  label=_('use template languages'))
 rql('SET X require_group G WHERE G name "managers", X eid %(x)s',
     {'x': eid}, 'x')    
--- a/rset.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/rset.py	Mon May 04 13:09:48 2009 +0200
@@ -191,8 +191,7 @@
         else:
             entities = sorted(enumerate(self),
                               key=lambda (i, e): keyfunc(e), reverse=reverse)
-
-        for index, entity in entities:
+        for index, _ in entities:
             rows.append(self.rows[index])
             descr.append(self.description[index])
         rset.rowcount = len(rows)
@@ -222,7 +221,7 @@
         for idx, line in enumerate(self):
             if col >= 0:
                 try:
-                    key = self.get_entity(idx,col)
+                    key = self.get_entity(idx, col)
                 except NotAnEntity:
                     key = line[col]
             else:
@@ -381,6 +380,9 @@
             pass
         # build entity instance
         etype = self.description[row][col]
+        if etype == 'EUser':
+            import traceback
+            traceback.printstack()
         entity = self.vreg.etype_class(etype)(req, self, row, col)
         entity.set_eid(eid)
         # cache entity
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rtags.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,89 @@
+"""relation tags store
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+
+class RelationTags(object):
+    """a tag store for full relation definitions :
+
+         (subject type, relation type, object type, tagged)
+
+    allowing to set tags using wildcard (eg '*') as subject type / object type
+
+    This class associates a single tag to each key.
+    """
+
+    def __init__(self):
+        self._tagdefs = {}
+
+    def __repr__(self):
+        return repr(self._tagdefs)
+
+    # dict compat
+    def __getitem__(self, key):
+        return self.get(*key)
+    __contains__ = __getitem__
+
+    def _get_keys(self, rtype, tagged, stype, otype):
+        assert tagged in ('subject', 'object'), tagged
+        keys = [(rtype, tagged, '*', '*'),
+                (rtype, tagged, '*', otype),
+                (rtype, tagged, stype, '*'),
+                (rtype, tagged, stype, otype)]
+        if stype == '*' or otype == '*':
+            keys.remove((rtype, tagged, '*', '*'))
+            if stype == '*':
+                keys.remove((rtype, tagged, '*', otype))
+            if otype == '*':
+                keys.remove((rtype, tagged, stype, '*'))
+        return keys
+
+    def tag_attribute(self, tag, stype, attr):
+        self._tagdefs[(str(attr), 'subject', str(stype), '*')] = tag
+
+    def tag_relation(self, tag, relation, tagged):
+        assert tagged in ('subject', 'object'), tagged
+        stype, rtype, otype = relation
+        self._tagdefs[(str(rtype), tagged, str(stype), str(otype))] = tag
+
+    def del_rtag(self, relation, tagged):
+        assert tagged in ('subject', 'object'), tagged
+        stype, rtype, otype = relation
+        del self._tagdefs[(str(rtype), tagged, str(stype), str(otype))]
+
+    def get(self, rtype, tagged, stype='*', otype='*'):
+        for key in reversed(self._get_keys(rtype, tagged, stype, otype)):
+            try:
+                return self._tagdefs[key]
+            except KeyError:
+                continue
+        return None
+
+    def etype_get(self, etype, rtype, tagged, ttype='*'):
+        if tagged == 'subject':
+            return self.get(rtype, tagged, etype, ttype)
+        return self.get(rtype, tagged, ttype, etype)
+
+
+
+class RelationTagsSet(RelationTags):
+    """This class associates a set of tags to each key."""
+
+    def tag_relation(self, tag, relation, tagged):
+        assert tagged in ('subject', 'object'), tagged
+        stype, rtype, otype = relation
+        rtags = self._tagdefs.setdefault((rtype, tagged, stype, otype), set())
+        rtags.add(tag)
+
+    def get(self, rtype, tagged, stype='*', otype='*'):
+        rtags = set()
+        for key in self._get_keys(rtype, tagged, stype, otype):
+            try:
+                rtags.update(self._tagdefs[key])
+            except KeyError:
+                continue
+        return rtags
--- a/schema.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/schema.py	Mon May 04 13:09:48 2009 +0200
@@ -6,11 +6,11 @@
 """
 __docformat__ = "restructuredtext en"
 
-import warnings
 import re
 from logging import getLogger
+from warnings import warn
 
-from logilab.common.decorators import cached, clear_cache
+from logilab.common.decorators import cached, clear_cache, monkeypatch
 from logilab.common.compat import any
 
 from yams import BadSchemaDefinition, buildobjs as ybo
@@ -22,6 +22,12 @@
 from rql import parse, nodes, RQLSyntaxError, TypeResolverException
 
 from cubicweb import ETYPE_NAME_MAP, ValidationError, Unauthorized
+from cubicweb import set_log_methods
+
+# XXX <3.2 bw compat
+from yams import schema
+schema.use_py_datetime()
+nodes.use_py_datetime()
 
 _ = unicode
 
@@ -36,9 +42,8 @@
 
 def bw_normalize_etype(etype):
     if etype in ETYPE_NAME_MAP:
-        from warnings import warn
         msg = '%s has been renamed to %s, please update your code' % (
-            etype, ETYPE_NAME_MAP[etype])            
+            etype, ETYPE_NAME_MAP[etype])
         warn(msg, DeprecationWarning, stacklevel=4)
         etype = ETYPE_NAME_MAP[etype]
     return etype
@@ -68,6 +73,40 @@
     return (etype,)
 ybo.RelationDefinition._actual_types = _actual_types
 
+
+## cubicweb provides a RichString class for convenience
+class RichString(ybo.String):
+    """Convenience RichString attribute type
+    The following declaration::
+
+      class Card(EntityType):
+          content = RichString(fulltextindexed=True, default_format='text/rest')
+
+    is equivalent to::
+
+      class Card(EntityType):
+          content_format = String(meta=True, internationalizable=True,
+                                 default='text/rest', constraints=[format_constraint])
+          content  = String(fulltextindexed=True)
+    """
+    def __init__(self, default_format='text/plain', format_constraints=None, **kwargs):
+        self.default_format = default_format
+        self.format_constraints = format_constraints or [format_constraint]
+        super(RichString, self).__init__(**kwargs)
+
+PyFileReader.context['RichString'] = RichString
+
+## need to monkeypatch yams' _add_relation function to handle RichString
+yams_add_relation = ybo._add_relation
+@monkeypatch(ybo)
+def _add_relation(relations, rdef, name=None, insertidx=None):
+    if isinstance(rdef, RichString):
+        format_attrdef = ybo.String(meta=True, internationalizable=True,
+                                    default=rdef.default_format, maxsize=50,
+                                    constraints=rdef.format_constraints)
+        yams_add_relation(relations, format_attrdef, name+'_format', insertidx)
+    yams_add_relation(relations, rdef, name, insertidx)
+
 def display_name(req, key, form=''):
     """return a internationalized string for the key (schema entity or relation
     name) in a given form
@@ -219,19 +258,19 @@
             eid = getattr(edef, 'eid', None)
         self.eid = eid
         # take care: no _groups attribute when deep-copying
-        if getattr(self, '_groups', None): 
+        if getattr(self, '_groups', None):
             for groups in self._groups.itervalues():
                 for group_or_rqlexpr in groups:
                     if isinstance(group_or_rqlexpr, RRQLExpression):
                         msg = "can't use RRQLExpression on an entity type, use an ERQLExpression (%s)"
                         raise BadSchemaDefinition(msg % self.type)
-            
+
     def attribute_definitions(self):
         """return an iterator on attribute definitions
-        
+
         attribute relations are a subset of subject relations where the
         object's type is a final entity
-        
+
         an attribute definition is a 2-uple :
         * name of the relation
         * schema of the destination entity type
@@ -241,7 +280,7 @@
             if rschema.type == 'has_text':
                 continue
             yield rschema, attrschema
-            
+
     def add_subject_relation(self, rschema):
         """register the relation schema as possible subject relation"""
         super(CubicWebEntitySchema, self).add_subject_relation(rschema)
@@ -250,7 +289,7 @@
     def del_subject_relation(self, rtype):
         super(CubicWebEntitySchema, self).del_subject_relation(rtype)
         self._update_has_text(False)
-        
+
     def _update_has_text(self, need_has_text=None):
         may_need_has_text, has_has_text = False, False
         for rschema in self.subject_relations():
@@ -278,23 +317,11 @@
             self.schema.add_relation_def(rdef)
         elif not need_has_text and has_has_text:
             self.schema.del_relation_def(self.type, 'has_text', 'String')
-            
+
     def schema_entity(self):
         """return True if this entity type is used to build the schema"""
         return self.type in self.schema.schema_entity_types()
 
-    def rich_text_fields(self):
-        """return an iterator on (attribute, format attribute) of rich text field
-
-        (the first tuple element containing the text and the second the text format)
-        """
-        for rschema, _ in self.attribute_definitions():
-            if rschema.type.endswith('_format'):
-                for constraint in self.constraints(rschema):
-                    if isinstance(constraint, FormatConstraint):
-                        yield self.subject_relation(rschema.type[:-7]), rschema
-                        break
-                    
     def check_perm(self, session, action, eid=None):
         # NB: session may be a server session or a request object
         user = session.user
@@ -310,17 +337,17 @@
         # else if there is some rql expressions, check them
         if any(rqlexpr.check(session, eid)
                for rqlexpr in self.get_rqlexprs(action)):
-            return        
+            return
         raise Unauthorized(action, str(self))
 
     def rql_expression(self, expression, mainvars=None, eid=None):
         """rql expression factory"""
         return ERQLExpression(expression, mainvars, eid)
-    
+
 class CubicWebRelationSchema(RelationSchema):
     RelationSchema._RPROPERTIES['eid'] = None
     _perms_checked = False
-    
+
     def __init__(self, schema=None, rdef=None, eid=None, **kwargs):
         if rdef is not None:
             # if this relation is inlined
@@ -329,8 +356,8 @@
         if eid is None and rdef is not None:
             eid = getattr(rdef, 'eid', None)
         self.eid = eid
-                    
-        
+
+
     def update(self, subjschema, objschema, rdef):
         super(CubicWebRelationSchema, self).update(subjschema, objschema, rdef)
         if not self._perms_checked and self._groups:
@@ -350,7 +377,7 @@
                             newrqlexprs.append(ERQLExpression(rqlexpr.expression,
                                                               rqlexpr.mainvars,
                                                               rqlexpr.eid))
-                            self.set_rqlexprs(action, newrqlexprs) 
+                            self.set_rqlexprs(action, newrqlexprs)
                         else:
                             msg = "can't use RRQLExpression on a final relation "\
                                   "type (eg attribute relation), use an ERQLExpression (%s)"
@@ -361,16 +388,16 @@
                               "a RRQLExpression (%s)"
                         raise BadSchemaDefinition(msg % self.type)
             self._perms_checked = True
-            
+
     def cardinality(self, subjtype, objtype, target):
         card = self.rproperty(subjtype, objtype, 'cardinality')
         return (target == 'subject' and card[0]) or \
                (target == 'object' and card[1])
-    
+
     def schema_relation(self):
         return self.type in ('relation_type', 'from_entity', 'to_entity',
                              'constrained_by', 'cstrtype')
-    
+
     def physical_mode(self):
         """return an appropriate mode for physical storage of this relation type:
         * 'subjectinline' if every possible subject cardinalities are 1 or ?
@@ -386,7 +413,7 @@
         # in an allowed group, if so that's enough internal sessions should
         # always stop there
         if session.user.matching_groups(self.get_groups(action)):
-            return 
+            return
         # else if there is some rql expressions, check them
         if any(rqlexpr.check(session, *args, **kwargs)
                for rqlexpr in self.get_rqlexprs(action)):
@@ -399,7 +426,7 @@
             return ERQLExpression(expression, mainvars, eid)
         return RRQLExpression(expression, mainvars, eid)
 
-    
+
 class CubicWebSchema(Schema):
     """set of entities and relations schema defining the possible data sets
     used in an application
@@ -407,11 +434,11 @@
 
     :type name: str
     :ivar name: name of the schema, usually the application identifier
-    
+
     :type base: str
     :ivar base: path of the directory where the schema is defined
     """
-    reading_from_database = False    
+    reading_from_database = False
     entity_class = CubicWebEntitySchema
     relation_class = CubicWebRelationSchema
 
@@ -428,15 +455,15 @@
         rschema = self.add_relation_type(ybo.RelationType('identity', meta=True))
         rschema.final = False
         rschema.set_default_groups()
-        
+
     def schema_entity_types(self):
         """return the list of entity types used to build the schema"""
-        return frozenset(('EEType', 'ERType', 'EFRDef', 'ENFRDef',
-                          'EConstraint', 'EConstraintType', 'RQLExpression',
+        return frozenset(('CWEType', 'CWRType', 'CWAttribute', 'CWRelation',
+                          'CWConstraint', 'CWConstraintType', 'RQLExpression',
                           # XXX those are not really "schema" entity types
                           #     but we usually don't want them as @* targets
-                          'EProperty', 'EPermission', 'State', 'Transition'))
-        
+                          'CWProperty', 'CWPermission', 'State', 'Transition'))
+
     def add_entity_type(self, edef):
         edef.name = edef.name.encode()
         edef.name = bw_normalize_etype(edef.name)
@@ -451,13 +478,13 @@
             self.add_relation_def(rdef)
         self._eid_index[eschema.eid] = eschema
         return eschema
-        
+
     def add_relation_type(self, rdef):
         rdef.name = rdef.name.lower().encode()
         rschema = super(CubicWebSchema, self).add_relation_type(rdef)
         self._eid_index[rschema.eid] = rschema
         return rschema
-    
+
     def add_relation_def(self, rdef):
         """build a part of a relation schema
         (i.e. add a relation between two specific entity's types)
@@ -477,25 +504,25 @@
         rdef.name = rdef.name.lower()
         rdef.subject = bw_normalize_etype(rdef.subject)
         rdef.object = bw_normalize_etype(rdef.object)
-        super(CubicWebSchema, self).add_relation_def(rdef)
-        try:
-            self._eid_index[rdef.eid] = (self.eschema(rdef.subject),
-                                         self.rschema(rdef.name),
-                                         self.eschema(rdef.object))
-        except AttributeError:
-            pass # not a serialized schema
-    
+        if super(CubicWebSchema, self).add_relation_def(rdef):
+            try:
+                self._eid_index[rdef.eid] = (self.eschema(rdef.subject),
+                                             self.rschema(rdef.name),
+                                             self.eschema(rdef.object))
+            except AttributeError:
+                pass # not a serialized schema
+
     def del_relation_type(self, rtype):
         rschema = self.rschema(rtype)
         self._eid_index.pop(rschema.eid, None)
         super(CubicWebSchema, self).del_relation_type(rtype)
-    
+
     def del_relation_def(self, subjtype, rtype, objtype):
         for k, v in self._eid_index.items():
             if v == (subjtype, rtype, objtype):
                 del self._eid_index[k]
         super(CubicWebSchema, self).del_relation_def(subjtype, rtype, objtype)
-        
+
     def del_entity_type(self, etype):
         eschema = self.eschema(etype)
         self._eid_index.pop(eschema.eid, None)
@@ -504,7 +531,7 @@
         if 'has_text' in eschema.subject_relations():
             self.del_relation_def(etype, 'has_text', 'String')
         super(CubicWebSchema, self).del_entity_type(etype)
-        
+
     def schema_by_eid(self, eid):
         return self._eid_index[eid]
 
@@ -516,22 +543,22 @@
 
     limit the proposed values to a set of entities returned by a rql query,
     but this is not enforced at the repository level
-    
+
      restriction is additional rql restriction that will be added to
      a predefined query, where the S and O variables respectivly represent
      the subject and the object of the relation
     """
-    
+
     def __init__(self, restriction):
         self.restriction = restriction
 
     def serialize(self):
         return self.restriction
-    
+
     def deserialize(cls, value):
         return cls(value)
     deserialize = classmethod(deserialize)
-    
+
     def check(self, entity, rtype, value):
         """return true if the value satisfy the constraint, else false"""
         # implemented as a hook in the repository
@@ -541,7 +568,7 @@
         """raise ValidationError if the relation doesn't satisfy the constraint
         """
         pass # this is a vocabulary constraint, not enforce
-    
+
     def __str__(self):
         return self.restriction
 
@@ -559,7 +586,7 @@
                                       ('s', 'o'), build_descr=False)
     def error(self, eid, rtype, msg):
         raise ValidationError(eid, {rtype: msg})
-        
+
     def repo_check(self, session, eidfrom, rtype, eidto):
         """raise ValidationError if the relation doesn't satisfy the constraint
         """
@@ -581,12 +608,12 @@
             #     eidfrom or eidto (from user interface point of view)
             self.error(eidfrom, rtype, 'unique constraint %s failed' % self)
 
-    
+
 def split_expression(rqlstring):
     for expr in rqlstring.split(','):
         for word in expr.split():
             yield word
-            
+
 def normalize_expression(rqlstring):
     """normalize an rql expression to ease schema synchronization (avoid
     suppressing and reinserting an expression if only a space has been added/removed
@@ -610,19 +637,19 @@
             if len(self.rqlst.defined_vars[mainvar].references()) <= 2:
                 LOGGER.warn('You did not use the %s variable in your RQL expression %s',
                             mainvar, self)
-    
+
     def __str__(self):
         return self.full_rql
     def __repr__(self):
         return '%s(%s)' % (self.__class__.__name__, self.full_rql)
-        
+
     def __deepcopy__(self, memo):
         return self.__class__(self.expression, self.mainvars)
     def __getstate__(self):
         return (self.expression, self.mainvars)
     def __setstate__(self, state):
         self.__init__(*state)
-        
+
     @cached
     def transform_has_permission(self):
         found = None
@@ -666,7 +693,7 @@
             rqlst.recover()
             return rql, found, keyarg
         return rqlst.as_string(), None, None
-        
+
     def _check(self, session, **kwargs):
         """return True if the rql expression is matching the given relation
         between fromeid and toeid
@@ -726,7 +753,7 @@
         if self.eid is not None:
             session.local_perm_cache[key] = False
         return False
-    
+
     @property
     def minimal_rql(self):
         return 'Any %s WHERE %s' % (self.mainvars, self.expression)
@@ -751,16 +778,16 @@
         if 'U' in defined:
             rql += ', U eid %(u)s'
         return rql
-    
+
     def check(self, session, eid=None):
         if 'X' in self.rqlst.defined_vars:
             if eid is None:
                 return False
             return self._check(session, x=eid)
         return self._check(session)
-    
+
 PyFileReader.context['ERQLExpression'] = ERQLExpression
-        
+
 class RRQLExpression(RQLExpression):
     def __init__(self, expression, mainvars=None, eid=None):
         if mainvars is None:
@@ -790,7 +817,7 @@
         if 'U' in defined:
             rql += ', U eid %(u)s'
         return rql
-    
+
     def check(self, session, fromeid=None, toeid=None):
         kwargs = {}
         if 'S' in self.rqlst.defined_vars:
@@ -802,17 +829,44 @@
                 return False
             kwargs['o'] = toeid
         return self._check(session, **kwargs)
-        
+
 PyFileReader.context['RRQLExpression'] = RRQLExpression
 
-        
+# workflow extensions #########################################################
+
+class workflowable_definition(ybo.metadefinition):
+    """extends default EntityType's metaclass to add workflow relations
+    (i.e. in_state and wf_info_for).
+    This is the default metaclass for WorkflowableEntityType
+    """
+    def __new__(mcs, name, bases, classdict):
+        abstract = classdict.pop('abstract', False)
+        defclass = super(workflowable_definition, mcs).__new__(mcs, name, bases, classdict)
+        if not abstract:
+            existing_rels = set(rdef.name for rdef in defclass.__relations__)
+            if 'in_state' not in existing_rels and 'wf_info_for' not in existing_rels:
+                in_state = ybo.SubjectRelation('State', cardinality='1*',
+                                               # XXX automatize this
+                                               constraints=[RQLConstraint('S is ET, O state_of ET')],
+                                               description=_('account state'))
+                yams_add_relation(defclass.__relations__, in_state, 'in_state')
+                wf_info_for = ybo.ObjectRelation('TrInfo', cardinality='1*', composite='object')
+                yams_add_relation(defclass.__relations__, wf_info_for, 'wf_info_for')
+        return defclass
+
+class WorkflowableEntityType(ybo.EntityType):
+    __metaclass__ = workflowable_definition
+    abstract = True
+
+PyFileReader.context['WorkflowableEntityType'] = WorkflowableEntityType
+
 # schema loading ##############################################################
 
 class CubicWebRelationFileReader(RelationFileReader):
     """cubicweb specific relation file reader, handling additional RQL
     constraints on a relation definition
     """
-    
+
     def handle_constraint(self, rdef, constraint_text):
         """arbitrary constraint is an rql expression for cubicweb"""
         if not rdef.constraints:
@@ -824,7 +878,7 @@
             rdef.inlined = True
         RelationFileReader.process_properties(self, rdef, relation_def)
 
-        
+
 CONSTRAINTS['RQLConstraint'] = RQLConstraint
 CONSTRAINTS['RQLUniqueConstraint'] = RQLUniqueConstraint
 CONSTRAINTS['RQLVocabularyConstraint'] = RQLVocabularyConstraint
@@ -839,20 +893,20 @@
     SchemaLoader.file_handlers.update({'.rel' : CubicWebRelationFileReader,
                                        })
 
-    def load(self, config, path=()):
+    def load(self, config, path=(), **kwargs):
         """return a Schema instance from the schema definition read
         from <directory>
         """
         self.lib_directory = config.schemas_lib_dir()
         return super(BootstrapSchemaLoader, self).load(
-            path, config.appid, register_base_types=False)
-    
+            path, config.appid, register_base_types=False, **kwargs)
+
     def _load_definition_files(self, cubes=None):
         # bootstraping, ignore cubes
         for filepath in self.include_schema_files('bootstrap'):
             self.info('loading %s', filepath)
             self.handle_file(filepath)
-        
+
     def unhandled_file(self, filepath):
         """called when a file without handler associated has been found"""
         self.warning('ignoring file %r', filepath)
@@ -863,7 +917,7 @@
     application's schema
     """
 
-    def load(self, config):
+    def load(self, config, **kwargs):
         """return a Schema instance from the schema definition read
         from <directory>
         """
@@ -872,13 +926,13 @@
             path = reversed([config.apphome] + config.cubes_path())
         else:
             path = reversed(config.cubes_path())
-        return super(CubicWebSchemaLoader, self).load(config, path=path)
+        return super(CubicWebSchemaLoader, self).load(config, path=path, **kwargs)
 
     def _load_definition_files(self, cubes):
         for filepath in (self.include_schema_files('bootstrap')
                          + self.include_schema_files('base')
+                         + self.include_schema_files('workflow')
                          + self.include_schema_files('Bookmark')):
-#                         + self.include_schema_files('Card')):
             self.info('loading %s', filepath)
             self.handle_file(filepath)
         for cube in cubes:
@@ -892,14 +946,15 @@
 PERM_USE_TEMPLATE_FORMAT = _('use_template_format')
 
 class FormatConstraint(StaticVocabularyConstraint):
-    need_perm_formats = (_('text/cubicweb-page-template'),
-                         )
+    need_perm_formats = [_('text/cubicweb-page-template')]
+
     regular_formats = (_('text/rest'),
                        _('text/html'),
                        _('text/plain'),
                        )
     def __init__(self):
         pass
+
     def serialize(self):
         """called to make persistent valuable data of a constraint"""
         return None
@@ -910,22 +965,22 @@
         a `cls` instance
         """
         return cls()
-    
-    def vocabulary(self, entity=None):
-        if entity and entity.req.user.has_permission(PERM_USE_TEMPLATE_FORMAT):
-            return self.regular_formats + self.need_perm_formats
+
+    def vocabulary(self, entity=None, req=None):
+        if req is None and entity is not None:
+            req = entity.req
+        if req is not None and req.user.has_permission(PERM_USE_TEMPLATE_FORMAT):
+            return self.regular_formats + tuple(self.need_perm_formats)
         return self.regular_formats
-    
+
     def __str__(self):
         return 'value in (%s)' % u', '.join(repr(unicode(word)) for word in self.vocabulary())
-    
-    
+
+
 format_constraint = FormatConstraint()
 CONSTRAINTS['FormatConstraint'] = FormatConstraint
 PyFileReader.context['format_constraint'] = format_constraint
 
-from logging import getLogger
-from cubicweb import set_log_methods
 set_log_methods(CubicWebSchemaLoader, getLogger('cubicweb.schemaloader'))
 set_log_methods(BootstrapSchemaLoader, getLogger('cubicweb.bootstrapschemaloader'))
 set_log_methods(RQLExpression, getLogger('cubicweb.schema'))
@@ -936,7 +991,7 @@
 def bw_import_erschema(self, ertype, schemamod=None, instantiate=True):
     return orig_import_erschema(self, bw_normalize_etype(ertype), schemamod, instantiate)
 PyFileReader.import_erschema = bw_import_erschema
-    
+
 # XXX itou for some Statement methods
 from rql import stmts
 orig_get_etype = stmts.ScopeNode.get_etype
--- a/schemas/Bookmark.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/schemas/Bookmark.py	Mon May 04 13:09:48 2009 +0200
@@ -5,7 +5,7 @@
     path  = String(maxsize=512, required=True,
                    description=_("relative url of the bookmarked page"))
     
-    bookmarked_by = SubjectRelation('EUser',
+    bookmarked_by = SubjectRelation('CWUser',
                                     description=_("users using this bookmark"))
     
 
--- a/schemas/base.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/schemas/base.py	Mon May 04 13:09:48 2009 +0200
@@ -1,16 +1,15 @@
 """core CubicWeb schema, but not necessary at bootstrap time
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb.schema import format_constraint
 
-
-class EUser(RestrictedEntityType):
+class CWUser(WorkflowableEntityType):
     """define a CubicWeb user"""
+    meta = True # XXX backported from old times, shouldn't be there anymore
     permissions = {
         'read':   ('managers', 'users', ERQLExpression('X identity U')),
         'add':    ('managers',),
@@ -25,19 +24,14 @@
     surname   = String(maxsize=64)
     last_login_time  = Datetime(description=_('last connection date'))
     # allowing an email to be the primary email of multiple entities is necessary for
-    # test at least :-/    
+    # test at least :-/
     primary_email = SubjectRelation('EmailAddress', cardinality='??',
                                     description=_('email address to use for notification'))
     use_email     = SubjectRelation('EmailAddress', cardinality='*?', composite='subject')
 
-    in_group = SubjectRelation('EGroup', cardinality='+*',
+    in_group = SubjectRelation('CWGroup', cardinality='+*',
                                constraints=[RQLConstraint('NOT O name "owners"')],
                                description=_('groups grant permissions to the user'))
-    in_state = SubjectRelation('State', cardinality='1*',
-                               # XXX automatize this
-                               constraints=[RQLConstraint('S is ET, O state_of ET')],
-                               description=_('account state'))
-    wf_info_for = ObjectRelation('TrInfo', cardinality='1*', composite='object')
 
 
 class EmailAddress(MetaEntityType):
@@ -48,9 +42,9 @@
         'delete': ('managers', 'owners', ERQLExpression('P use_email X, U has_update_permission P')),
         'update': ('managers', 'owners', ERQLExpression('P use_email X, U has_update_permission P')),
         }
-    
+
     alias   = String(fulltextindexed=True, maxsize=56)
-    address = String(required=True, fulltextindexed=True, 
+    address = String(required=True, fulltextindexed=True,
                      indexed=True, unique=True, maxsize=128)
     canonical = Boolean(default=False,
                         description=_('when multiple addresses are equivalent \
@@ -70,7 +64,7 @@
 class primary_email(RelationType):
     """the prefered email"""
     permissions = use_email.permissions
-    
+
 class identical_to(RelationType):
     """identical_to"""
     symetric = True
@@ -89,22 +83,22 @@
 class in_group(MetaRelationType):
     """core relation indicating a user's groups"""
     meta = False
-    
+
 class owned_by(MetaRelationType):
     """core relation indicating owners of an entity. This relation
     implicitly put the owner into the owners group for the entity
     """
     permissions = {
         'read':   ('managers', 'users', 'guests'),
-        'add':    ('managers', RRQLExpression('S owned_by U'),), 
+        'add':    ('managers', RRQLExpression('S owned_by U'),),
         'delete': ('managers', RRQLExpression('S owned_by U'),),
         }
     # 0..n cardinality for entities created by internal session (no attached user)
     # and to support later deletion of a user which has created some entities
     cardinality = '**'
     subject = '**'
-    object = 'EUser'
-    
+    object = 'CWUser'
+
 class created_by(MetaRelationType):
     """core relation indicating the original creator of an entity"""
     permissions = {
@@ -114,11 +108,11 @@
         }
     # 0..1 cardinality for entities created by internal session (no attached user)
     # and to support later deletion of a user which has created some entities
-    cardinality = '?*' 
+    cardinality = '?*'
     subject = '**'
-    object = 'EUser'
+    object = 'CWUser'
 
-    
+
 class creation_date(MetaAttributeRelationType):
     """creation time of an entity"""
     cardinality = '11'
@@ -132,115 +126,7 @@
     object = 'Datetime'
 
 
-class State(MetaEntityType):
-    """used to associate simple states to an entity type and/or to define
-    workflows
-    """
-    name = String(required=True, indexed=True, internationalizable=True,
-                  maxsize=256)
-    description_format = String(meta=True, internationalizable=True, maxsize=50,
-                                default='text/rest', constraints=[format_constraint])
-    description = String(fulltextindexed=True,
-                         description=_('semantic description of this state'))
-    
-    state_of = SubjectRelation('EEType', cardinality='+*',
-                    description=_('entity types which may use this state'),
-                    constraints=[RQLConstraint('O final FALSE')])
-    allowed_transition = SubjectRelation('Transition', cardinality='**',
-                                         constraints=[RQLConstraint('S state_of ET, O transition_of ET')],
-                                         description=_('allowed transitions from this state'))
-    
-    initial_state = ObjectRelation('EEType', cardinality='?*',
-                                   # S initial_state O, O state_of S
-                                   constraints=[RQLConstraint('O state_of S')],
-                                   description=_('initial state for entities of this type'))
-
-
-class Transition(MetaEntityType):
-    """use to define a transition from one or multiple states to a destination
-    states in workflow's definitions.
-    """
-    name = String(required=True, indexed=True, internationalizable=True,
-                  maxsize=256)
-    description_format = String(meta=True, internationalizable=True, maxsize=50,
-                                default='text/rest', constraints=[format_constraint])
-    description = String(fulltextindexed=True,
-                         description=_('semantic description of this transition'))
-    condition = SubjectRelation('RQLExpression', cardinality='*?', composite='subject',
-                                description=_('a RQL expression which should return some results, '
-                                              'else the transition won\'t be available. '
-                                              'This query may use X and U variables '
-                                              'that will respectivly represents '
-                                              'the current entity and the current user'))
-    
-    require_group = SubjectRelation('EGroup', cardinality='**',
-                                    description=_('group in which a user should be to be '
-                                                  'allowed to pass this transition'))
-    transition_of = SubjectRelation('EEType', cardinality='+*',
-                                    description=_('entity types which may use this transition'),
-                                    constraints=[RQLConstraint('O final FALSE')])
-    destination_state = SubjectRelation('State', cardinality='?*',
-                                        constraints=[RQLConstraint('S transition_of ET, O state_of ET')],
-                                        description=_('destination state for this transition'))
-
-
-class TrInfo(MetaEntityType):
-    from_state = SubjectRelation('State', cardinality='?*')
-    to_state = SubjectRelation('State', cardinality='1*')
-    comment_format = String(meta=True, internationalizable=True, maxsize=50,
-                            default='text/rest', constraints=[format_constraint])
-    comment = String(fulltextindexed=True)
-    # get actor and date time using owned_by and creation_date
-
-
-class from_state(MetaRelationType):
-    inlined = True
-class to_state(MetaRelationType):
-    inlined = True
-class wf_info_for(MetaRelationType):
-    """link a transition information to its object"""
-    permissions = {
-        'read':   ('managers', 'users', 'guests',),# RRQLExpression('U has_read_permission O')),
-        'add':    (), # handled automatically, no one should add one explicitly
-        'delete': ('managers',), # RRQLExpression('U has_delete_permission O')
-        }
-    inlined = True
-    composite = 'object'
-    fulltext_container = composite
-    
-class state_of(MetaRelationType):
-    """link a state to one or more entity type"""
-class transition_of(MetaRelationType):
-    """link a transition to one or more entity type"""
-class condition(MetaRelationType):
-    """link a transition to one or more rql expression allowing to go through
-    this transition
-    """
-    
-class initial_state(MetaRelationType):
-    """indicate which state should be used by default when an entity using
-    states is created
-    """
-    inlined = True
-
-class destination_state(MetaRelationType):
-    """destination state of a transition"""
-    inlined = True
-    
-class allowed_transition(MetaRelationType):
-    """allowed transition from this state"""
-
-class in_state(UserRelationType):
-    """indicate the current state of an entity"""
-    meta = True
-    # not inlined intentionnaly since when using ldap sources, user'state
-    # has to be stored outside the EUser table
-    
-    # add/delete perms given to managers/users, after what most of the job
-    # is done by workflow enforcment
-    
-
-class EProperty(EntityType):
+class CWProperty(EntityType):
     """used for cubicweb configuration. Once a property has been created you
     can't change the key.
     """
@@ -257,8 +143,8 @@
                                 'You must select this first to be able to set '
                                 'value'))
     value = String(internationalizable=True, maxsize=256)
-    
-    for_user = SubjectRelation('EUser', cardinality='?*', composite='object',
+
+    for_user = SubjectRelation('CWUser', cardinality='?*', composite='object',
                                description=_('user for which this property is '
                                              'applying. If this relation is not '
                                              'set, the property is considered as'
@@ -277,17 +163,17 @@
     inlined = True
 
 
-class EPermission(MetaEntityType):
+class CWPermission(MetaEntityType):
     """entity type that may be used to construct some advanced security configuration
     """
     name = String(required=True, indexed=True, internationalizable=True, maxsize=100,
                   description=_('name or identifier of the permission'))
     label = String(required=True, internationalizable=True, maxsize=100,
                    description=_('distinct label to distinguate between other permission entity of the same name'))
-    require_group = SubjectRelation('EGroup', 
+    require_group = SubjectRelation('CWGroup',
                                     description=_('groups to which the permission is granted'))
 
-# explicitly add X require_permission EPermission for each entity that should have
+# explicitly add X require_permission CWPermission for each entity that should have
 # configurable security
 class require_permission(RelationType):
     """link a permission to the entity. This permission should be used in the
@@ -298,7 +184,7 @@
         'add':    ('managers',),
         'delete': ('managers',),
         }
-    
+
 class require_group(MetaRelationType):
     """used to grant a permission to a group"""
     permissions = {
@@ -307,12 +193,13 @@
         'delete': ('managers',),
         }
 
-    
+
 class see_also(RelationType):
     """generic relation to link one entity to another"""
     symetric = True
 
-class ECache(MetaEntityType):
+
+class CWCache(MetaEntityType):
     """a simple cache entity characterized by a name and
     a validity date.
 
--- a/schemas/bootstrap.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/schemas/bootstrap.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """core CubicWeb schema necessary for bootstrapping the actual application's schema
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
@@ -10,20 +10,18 @@
 
 # not restricted since as "is" is handled as other relations, guests need
 # access to this
-class EEType(MetaEntityType):
+class CWEType(MetaEntityType):
     """define an entity type, used to build the application schema"""
     name = String(required=True, indexed=True, internationalizable=True,
                   unique=True, maxsize=64)
-    description_format = String(meta=True, internationalizable=True, maxsize=50,
-                                default='text/plain', constraints=[format_constraint])
-    description = String(internationalizable=True,
-                         description=_('semantic description of this entity type'))
+    description = RichString(internationalizable=True,
+                             description=_('semantic description of this entity type'))
     meta = Boolean(description=_('is it an application entity type or not ?'))
     # necessary to filter using RQL
     final = Boolean(description=_('automatic'))
 
 
-class ERType(MetaEntityType):
+class CWRType(MetaEntityType):
     """define a relation type, used to build the application schema"""
     name = String(required=True, indexed=True, internationalizable=True,
                   unique=True, maxsize=64)
@@ -41,61 +39,61 @@
     final = Boolean(description=_('automatic'))
 
 
-class EFRDef(MetaEntityType):
+class CWAttribute(MetaEntityType):
     """define a final relation: link a final relation type from a non final
-    entity to a final entity type. 
+    entity to a final entity type.
 
     used to build the application schema
     """
-    relation_type = SubjectRelation('ERType', cardinality='1*',
+    relation_type = SubjectRelation('CWRType', cardinality='1*',
                                     constraints=[RQLConstraint('O final TRUE')],
                                     composite='object')
-    from_entity = SubjectRelation('EEType', cardinality='1*',
+    from_entity = SubjectRelation('CWEType', cardinality='1*',
                                   constraints=[RQLConstraint('O final FALSE')],
                                   composite='object')
-    to_entity = SubjectRelation('EEType', cardinality='1*',
+    to_entity = SubjectRelation('CWEType', cardinality='1*',
                                 constraints=[RQLConstraint('O final TRUE')],
                                 composite='object')
-    constrained_by = SubjectRelation('EConstraint', cardinality='*1', composite='subject')
-    
+    constrained_by = SubjectRelation('CWConstraint', cardinality='*1', composite='subject')
+
     cardinality = String(maxsize=2, internationalizable=True,
-                         vocabulary=[_('?1'), _('11'), _('??'), _('1?')], 
+                         vocabulary=[_('?1'), _('11'), _('??'), _('1?')],
                          description=_('subject/object cardinality'))
     ordernum = Int(description=('control subject entity\'s relations order'), default=0)
-    
+
     indexed = Boolean(description=_('create an index for quick search on this attribute'))
     fulltextindexed = Boolean(description=_('index this attribute\'s value in the plain text index'))
     internationalizable = Boolean(description=_('is this attribute\'s value translatable'))
     defaultval = String(maxsize=256)
-    
+
     description_format = String(meta=True, internationalizable=True, maxsize=50,
                                 default='text/plain', constraints=[format_constraint])
     description = String(internationalizable=True,
                          description=_('semantic description of this attribute'))
-    
+
 
-CARDINALITY_VOCAB = [_('?*'), _('1*'), _('+*'), _('**'), 
-                     _('?+'), _('1+'), _('++'), _('*+'), 
+CARDINALITY_VOCAB = [_('?*'), _('1*'), _('+*'), _('**'),
+                     _('?+'), _('1+'), _('++'), _('*+'),
                      _('?1'), _('11'), _('+1'), _('*1'),
                      _('??'), _('1?'), _('+?'), _('*?')]
 
-class ENFRDef(MetaEntityType):
+class CWRelation(MetaEntityType):
     """define a non final relation: link a non final relation type from a non
-    final entity to a non final entity type. 
+    final entity to a non final entity type.
 
     used to build the application schema
     """
-    relation_type = SubjectRelation('ERType', cardinality='1*',
+    relation_type = SubjectRelation('CWRType', cardinality='1*',
                                     constraints=[RQLConstraint('O final FALSE')],
                                     composite='object')
-    from_entity = SubjectRelation('EEType', cardinality='1*',
+    from_entity = SubjectRelation('CWEType', cardinality='1*',
                                   constraints=[RQLConstraint('O final FALSE')],
                                   composite='object')
-    to_entity = SubjectRelation('EEType', cardinality='1*',
+    to_entity = SubjectRelation('CWEType', cardinality='1*',
                                 constraints=[RQLConstraint('O final FALSE')],
                                 composite='object')
-    constrained_by = SubjectRelation('EConstraint', cardinality='*1', composite='subject')
-    
+    constrained_by = SubjectRelation('CWConstraint', cardinality='*1', composite='subject')
+
     cardinality = String(maxsize=2, internationalizable=True,
                          vocabulary=CARDINALITY_VOCAB,
                          description=_('subject/object cardinality'))
@@ -107,12 +105,12 @@
                                      'deleted.'),
                        vocabulary=('', _('subject'), _('object')),
                        maxsize=8, default=None)
-    
+
     description_format = String(meta=True, internationalizable=True, maxsize=50,
                                 default='text/plain', constraints=[format_constraint])
     description = String(internationalizable=True,
                          description=_('semantic description of this relation'))
-    
+
 
 # not restricted since it has to be read when checking allowed transitions
 class RQLExpression(MetaEntityType):
@@ -122,7 +120,7 @@
                       description=_('name of the main variables which should be '
                                     'used in the selection if necessary (comma '
                                     'separated)'))
-    expression = String(required=True, 
+    expression = String(required=True,
                         description=_('restriction part of a rql query. '
                                       'For entity rql expression, X and U are '
                                       'predefined respectivly to the current object and to '
@@ -131,45 +129,45 @@
                                       'relation\'subject, object and to '
                                       'the request user. '))
 
-    read_permission = ObjectRelation(('EEType', 'ERType'), cardinality='+?', composite='subject',
+    read_permission = ObjectRelation(('CWEType', 'CWRType'), cardinality='+?', composite='subject',
                                       description=_('rql expression allowing to read entities/relations of this type'))
-    add_permission = ObjectRelation(('EEType', 'ERType'), cardinality='*?', composite='subject',
+    add_permission = ObjectRelation(('CWEType', 'CWRType'), cardinality='*?', composite='subject',
                                      description=_('rql expression allowing to add entities/relations of this type'))
-    delete_permission = ObjectRelation(('EEType', 'ERType'), cardinality='*?', composite='subject',
+    delete_permission = ObjectRelation(('CWEType', 'CWRType'), cardinality='*?', composite='subject',
                                         description=_('rql expression allowing to delete entities/relations of this type'))
-    update_permission = ObjectRelation('EEType', cardinality='*?', composite='subject',
+    update_permission = ObjectRelation('CWEType', cardinality='*?', composite='subject',
                                         description=_('rql expression allowing to update entities of this type'))
-    
+
 
-class EConstraint(MetaEntityType):
+class CWConstraint(MetaEntityType):
     """define a schema constraint"""
-    cstrtype = SubjectRelation('EConstraintType', cardinality='1*')
+    cstrtype = SubjectRelation('CWConstraintType', cardinality='1*')
     value = String(description=_('depends on the constraint type'))
 
 
-class EConstraintType(MetaEntityType):
+class CWConstraintType(MetaEntityType):
     """define a schema constraint type"""
     name = String(required=True, indexed=True, internationalizable=True,
                   unique=True, maxsize=64)
 
 
 # not restricted since it has to be read when checking allowed transitions
-class EGroup(MetaEntityType):
+class CWGroup(MetaEntityType):
     """define a CubicWeb users group"""
     name = String(required=True, indexed=True, internationalizable=True,
                   unique=True, maxsize=64)
 
-    read_permission = ObjectRelation(('EEType', 'ERType'), cardinality='+*',
+    read_permission = ObjectRelation(('CWEType', 'CWRType'), cardinality='+*',
                                       description=_('groups allowed to read entities/relations of this type'))
-    add_permission = ObjectRelation(('EEType', 'ERType'),
+    add_permission = ObjectRelation(('CWEType', 'CWRType'),
                                      description=_('groups allowed to add entities/relations of this type'))
-    delete_permission = ObjectRelation(('EEType', 'ERType'),
+    delete_permission = ObjectRelation(('CWEType', 'CWRType'),
                                         description=_('groups allowed to delete entities/relations of this type'))
-    update_permission = ObjectRelation('EEType',
+    update_permission = ObjectRelation('CWEType',
                                         description=_('groups allowed to update entities of this type'))
-    
-    
-    
+
+
+
 class relation_type(MetaRelationType):
     """link a relation definition to its relation type"""
     inlined = True
@@ -181,7 +179,7 @@
     inlined = True
 class constrained_by(MetaRelationType):
     """constraints applying on this relation"""
-    
+
 class cstrtype(MetaRelationType):
     """constraint factory"""
     inlined = True
@@ -216,7 +214,7 @@
         }
     cardinality = '1*'
     subject = '**'
-    object = 'EEType'
+    object = 'CWEType'
 
 class is_instance_of(MetaRelationType):
     """core relation indicating the types (including specialized types)
@@ -231,7 +229,7 @@
         }
     cardinality = '+*'
     subject = '**'
-    object = 'EEType'
+    object = 'CWEType'
 
 class specializes(MetaRelationType):
     name = 'specializes'
@@ -241,5 +239,5 @@
         'delete': ('managers',),
         }
     cardinality = '?*'
-    subject = 'EEType'
-    object = 'EEType'
+    subject = 'CWEType'
+    object = 'CWEType'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/schemas/workflow.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,108 @@
+"""workflow related schemas
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+
+class State(MetaEntityType):
+    """used to associate simple states to an entity type and/or to define
+    workflows
+    """
+    name = String(required=True, indexed=True, internationalizable=True,
+                  maxsize=256)
+    description = RichString(fulltextindexed=True, default_format='text/rest',
+                             description=_('semantic description of this state'))
+
+    state_of = SubjectRelation('CWEType', cardinality='+*',
+                    description=_('entity types which may use this state'),
+                    constraints=[RQLConstraint('O final FALSE')])
+    allowed_transition = SubjectRelation('Transition', cardinality='**',
+                                         constraints=[RQLConstraint('S state_of ET, O transition_of ET')],
+                                         description=_('allowed transitions from this state'))
+
+    initial_state = ObjectRelation('CWEType', cardinality='?*',
+                                   # S initial_state O, O state_of S
+                                   constraints=[RQLConstraint('O state_of S')],
+                                   description=_('initial state for entities of this type'))
+
+
+class Transition(MetaEntityType):
+    """use to define a transition from one or multiple states to a destination
+    states in workflow's definitions.
+    """
+    name = String(required=True, indexed=True, internationalizable=True,
+                  maxsize=256)
+    description_format = String(meta=True, internationalizable=True, maxsize=50,
+                                default='text/rest', constraints=[format_constraint])
+    description = String(fulltextindexed=True,
+                         description=_('semantic description of this transition'))
+    condition = SubjectRelation('RQLExpression', cardinality='*?', composite='subject',
+                                description=_('a RQL expression which should return some results, '
+                                              'else the transition won\'t be available. '
+                                              'This query may use X and U variables '
+                                              'that will respectivly represents '
+                                              'the current entity and the current user'))
+
+    require_group = SubjectRelation('CWGroup', cardinality='**',
+                                    description=_('group in which a user should be to be '
+                                                  'allowed to pass this transition'))
+    transition_of = SubjectRelation('CWEType', cardinality='+*',
+                                    description=_('entity types which may use this transition'),
+                                    constraints=[RQLConstraint('O final FALSE')])
+    destination_state = SubjectRelation('State', cardinality='?*',
+                                        constraints=[RQLConstraint('S transition_of ET, O state_of ET')],
+                                        description=_('destination state for this transition'))
+
+
+class TrInfo(MetaEntityType):
+    from_state = SubjectRelation('State', cardinality='?*')
+    to_state = SubjectRelation('State', cardinality='1*')
+    comment_format = String(meta=True, internationalizable=True, maxsize=50,
+                            default='text/rest', constraints=[format_constraint])
+    comment = String(fulltextindexed=True)
+    # get actor and date time using owned_by and creation_date
+
+
+class from_state(MetaRelationType):
+    inlined = True
+class to_state(MetaRelationType):
+    inlined = True
+class wf_info_for(MetaRelationType):
+    """link a transition information to its object"""
+    permissions = {
+        'read':   ('managers', 'users', 'guests',),# RRQLExpression('U has_read_permission O')),
+        'add':    (), # handled automatically, no one should add one explicitly
+        'delete': ('managers',), # RRQLExpression('U has_delete_permission O')
+        }
+    inlined = True
+    composite = 'object'
+    fulltext_container = composite
+
+class state_of(MetaRelationType):
+    """link a state to one or more entity type"""
+class transition_of(MetaRelationType):
+    """link a transition to one or more entity type"""
+
+class initial_state(MetaRelationType):
+    """indicate which state should be used by default when an entity using
+    states is created
+    """
+    inlined = True
+
+class destination_state(MetaRelationType):
+    """destination state of a transition"""
+    inlined = True
+
+class allowed_transition(MetaRelationType):
+    """allowed transition from this state"""
+
+class in_state(UserRelationType):
+    """indicate the current state of an entity"""
+    meta = True
+    # not inlined intentionnaly since when using ldap sources, user'state
+    # has to be stored outside the CWUser table
+
+    # add/delete perms given to managers/users, after what most of the job
+    # is done by workflow enforcment
+
--- a/schemaviewer.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/schemaviewer.py	Mon May 04 13:09:48 2009 +0200
@@ -46,13 +46,12 @@
         esection = Section(children=(Title(self.req._('Entities'),
                                            klass='titleUnderline'),))
         layout.append(esection)
-        entities = [eschema for eschema in schema.entities()
+        eschemas = [eschema for eschema in schema.entities()
                     if not eschema.is_final()]
         if skipmeta:
-            entities = [eschema for eschema in entities
+            eschemas = [eschema for eschema in eschemas
                         if not eschema.meta]
-        keys = [(eschema.type, eschema) for eschema in entities]
-        for key, eschema in sorted(keys):
+        for eschema in sorted(eschema):
             esection.append(self.visit_entityschema(eschema, skiprels))
         if display_relations:
             title = Title(self.req._('Relations'), klass='titleUnderline')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/selectors.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,1153 @@
+"""This file contains some basic selectors required by application objects.
+
+A selector is responsible to score how well an object may be used with a
+given context by returning a score.
+
+In CubicWeb Usually the context consists for a request object, a result set
+or None, a specific row/col in the result set, etc...
+
+
+If you have trouble with selectors, especially if the objet (typically
+a view or a component) you want to use is not selected and you want to
+know which one(s) of its selectors fail (e.g. returns 0), you can use
+`traced_selection` or even direclty `TRACED_OIDS`.
+
+`TRACED_OIDS` is a tuple of traced object ids. The special value
+'all' may be used to log selectors for all objects.
+
+For instance, say that the following code yields a `NoSelectableObject`
+exception::
+
+    self.view('calendar', myrset)
+
+You can log the selectors involved for *calendar* by replacing the line
+above by::
+
+    # in Python2.5
+    from cubicweb.selectors import traced_selection
+    with traced_selection():
+        self.view('calendar', myrset)
+
+    # in Python2.4
+    from cubicweb import selectors
+    selectors.TRACED_OIDS = ('calendar',)
+    self.view('calendar', myrset)
+    selectors.TRACED_OIDS = ()
+
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+
+__docformat__ = "restructuredtext en"
+
+import logging
+from warnings import warn
+
+from logilab.common.compat import all
+from logilab.common.deprecation import deprecated_function
+from logilab.common.interface import implements as implements_iface
+
+from yams import BASE_TYPES
+
+from cubicweb import (Unauthorized, NoSelectableObject, NotAnEntity,
+                      role, typed_eid)
+from cubicweb.vregistry import (NoSelectableObject, Selector,
+                                chainall, objectify_selector)
+from cubicweb.cwconfig import CubicWebConfiguration
+from cubicweb.schema import split_expression
+
+# helpers for debugging selectors
+SELECTOR_LOGGER = logging.getLogger('cubicweb.selectors')
+TRACED_OIDS = ()
+
+def lltrace(selector):
+    # don't wrap selectors if not in development mode
+    if CubicWebConfiguration.mode == 'installed':
+        return selector
+    def traced(cls, *args, **kwargs):
+        # /!\ lltrace decorates pure function or __call__ method, this
+        #     means argument order may be different
+        if isinstance(cls, Selector):
+            selname = str(cls)
+            vobj = args[0]
+        else:
+            selname = selector.__name__
+            vobj = cls
+        oid = vobj.id
+        ret = selector(cls, *args, **kwargs)
+        if TRACED_OIDS == 'all' or oid in TRACED_OIDS:
+            #SELECTOR_LOGGER.warning('selector %s returned %s for %s', selname, ret, cls)
+            print 'selector %s returned %s for %s' % (selname, ret, vobj)
+        return ret
+    traced.__name__ = selector.__name__
+    return traced
+
+class traced_selection(object):
+    """selector debugging helper.
+
+    Typical usage is :
+
+    >>> with traced_selection():
+    ...     # some code in which you want to debug selectors
+    ...     # for all objects
+
+    or
+
+    >>> with traced_selection( ('oid1', 'oid2') ):
+    ...     # some code in which you want to debug selectors
+    ...     # for objects with id 'oid1' and 'oid2'
+
+    """
+    def __init__(self, traced='all'):
+        self.traced = traced
+
+    def __enter__(self):
+        global TRACED_OIDS
+        TRACED_OIDS = self.traced
+
+    def __exit__(self, exctype, exc, traceback):
+        global TRACED_OIDS
+        TRACED_OIDS = ()
+        return traceback is None
+
+
+def score_interface(cls_or_inst, cls, iface):
+    """Return true 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()
+        if iface is cls:
+            return len(parents) + 4
+        if iface is parents[-1]: # Any
+            return 1
+        for index, etype in enumerate(reversed(parents[:-1])):
+            basecls = vreg.etype_class(etype)
+            if iface is basecls:
+                return index + 3
+        return 0
+    if implements_iface(cls_or_inst, iface):
+        # implenting an interface takes precedence other special Any interface
+        return 2
+    return 0
+
+
+# abstract selectors ##########################################################
+
+class PartialSelectorMixIn(object):
+    """convenience mix-in for selectors that will look into the containing
+    class to find missing information.
+
+    cf. `cubicweb.web.action.LinkToEntityAction` for instance
+    """
+    def __call__(self, cls, *args, **kwargs):
+        self.complete(cls)
+        return super(PartialSelectorMixIn, self).__call__(cls, *args, **kwargs)
+
+
+class ImplementsMixIn(object):
+    """mix-in class for selectors checking implemented interfaces of something
+    """
+    def __init__(self, *expected_ifaces):
+        super(ImplementsMixIn, self).__init__()
+        self.expected_ifaces = expected_ifaces
+
+    def __str__(self):
+        return '%s(%s)' % (self.__class__.__name__,
+                           ','.join(str(s) for s in self.expected_ifaces))
+
+    def score_interfaces(self, cls_or_inst, cls):
+        score = 0
+        vreg, eschema = cls_or_inst.vreg, cls_or_inst.e_schema
+        for iface in self.expected_ifaces:
+            if isinstance(iface, basestring):
+                # entity type
+                try:
+                    iface = vreg.etype_class(iface)
+                except KeyError:
+                    continue # entity type not in the schema
+            score += score_interface(cls_or_inst, cls, iface)
+        return score
+
+
+class EClassSelector(Selector):
+    """abstract class for selectors working on the entity classes of the result
+    set. Its __call__ method has the following behaviour:
+
+    * if row is specified, return the score returned by the score_class method
+      called with the entity class found in the specified cell
+    * else return the sum of score returned by the score_class method for each
+      entity type found in the specified column, unless:
+      - `once_is_enough` is True, in which case the first non-zero score is
+        returned
+      - `once_is_enough` is False, in which case if score_class return 0, 0 is
+        returned
+    """
+    def __init__(self, once_is_enough=False):
+        self.once_is_enough = once_is_enough
+
+    @lltrace
+    def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
+        if not rset:
+            return 0
+        score = 0
+        if row is None:
+            for etype in rset.column_types(col):
+                if etype is None: # outer join
+                    continue
+                escore = self.score(cls, req, etype)
+                if not escore and not self.once_is_enough:
+                    return 0
+                elif self.once_is_enough:
+                    return escore
+                score += escore
+        else:
+            etype = rset.description[row][col]
+            if etype is not None:
+                score = self.score(cls, req, etype)
+        return score
+
+    def score(self, cls, req, etype):
+        if etype in BASE_TYPES:
+            return 0
+        return self.score_class(cls.vreg.etype_class(etype), req)
+
+    def score_class(self, eclass, req):
+        raise NotImplementedError()
+
+
+class EntitySelector(EClassSelector):
+    """abstract class for selectors working on the entity instances of the
+    result set. Its __call__ method has the following behaviour:
+
+    * if 'entity' find in kwargs, return the score returned by the score_entity
+      method for this entity
+    * if row is specified, return the score returned by the score_entity method
+      called with the entity instance found in the specified cell
+    * else return the sum of score returned by the score_entity method for each
+      entity found in the specified column, unless:
+      - `once_is_enough` is True, in which case the first non-zero score is
+        returned
+      - `once_is_enough` is False, in which case if score_class return 0, 0 is
+        returned
+
+    note: None values (resulting from some outer join in the query) are not
+          considered.
+    """
+
+    @lltrace
+    def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
+        if not rset and not kwargs.get('entity'):
+            return 0
+        score = 0
+        if kwargs.get('entity'):
+            score = self.score_entity(kwargs['entity'])
+        elif row is None:
+            for row, rowvalue in enumerate(rset.rows):
+                if rowvalue[col] is None: # outer join
+                    continue
+                escore = self.score(req, rset, row, col)
+                if not escore and not self.once_is_enough:
+                    return 0
+                elif self.once_is_enough:
+                    return escore
+                score += escore
+        else:
+            etype = rset.description[row][col]
+            if etype is not None: # outer join
+                score = self.score(req, rset, row, col)
+        return score
+
+    def score(self, req, rset, row, col):
+        try:
+            return self.score_entity(rset.get_entity(row, col))
+        except NotAnEntity:
+            return 0
+
+    def score_entity(self, entity):
+        raise NotImplementedError()
+
+
+# very basic selectors ########################################################
+
+class yes(Selector):
+    """return arbitrary score"""
+    def __init__(self, score=1):
+        self.score = score
+    def __call__(self, *args, **kwargs):
+        return self.score
+
+@objectify_selector
+@lltrace
+def none_rset(cls, req, rset, *args, **kwargs):
+    """accept no result set (e.g. given rset is None)"""
+    if rset is None:
+        return 1
+    return 0
+
+@objectify_selector
+@lltrace
+def any_rset(cls, req, rset, *args, **kwargs):
+    """accept result set, whatever the number of result it contains"""
+    if rset is not None:
+        return 1
+    return 0
+
+@objectify_selector
+@lltrace
+def nonempty_rset(cls, req, rset, *args, **kwargs):
+    """accept any non empty result set"""
+    if rset is not None and rset.rowcount:
+        return 1
+    return 0
+
+@objectify_selector
+@lltrace
+def empty_rset(cls, req, rset, *args, **kwargs):
+    """accept empty result set"""
+    if rset is not None and rset.rowcount == 0:
+        return 1
+    return 0
+
+@objectify_selector
+@lltrace
+def one_line_rset(cls, req, rset, row=None, *args, **kwargs):
+    """if row is specified, accept result set with a single line of result,
+    else accepts anyway
+    """
+    if rset is not None and (row is not None or rset.rowcount == 1):
+        return 1
+    return 0
+
+@objectify_selector
+@lltrace
+def two_lines_rset(cls, req, rset, *args, **kwargs):
+    """accept result set with *at least* two lines of result"""
+    if rset is not None and rset.rowcount > 1:
+        return 1
+    return 0
+
+@objectify_selector
+@lltrace
+def two_cols_rset(cls, req, rset, *args, **kwargs):
+    """accept result set with at least one line and two columns of result"""
+    if rset is not None and rset.rowcount and len(rset.rows[0]) > 1:
+        return 1
+    return 0
+
+@objectify_selector
+@lltrace
+def paginated_rset(cls, req, rset, *args, **kwargs):
+    """accept result set with more lines than the page size.
+
+    Page size is searched in (respecting order):
+    * a page_size argument
+    * a page_size form parameters
+    * the navigation.page-size property
+    """
+    page_size = kwargs.get('page_size')
+    if page_size is None:
+        page_size = req.form.get('page_size')
+        if page_size is None:
+            page_size = req.property_value('navigation.page-size')
+        else:
+            page_size = int(page_size)
+    if rset is None or rset.rowcount <= page_size:
+        return 0
+    return 1
+
+@objectify_selector
+@lltrace
+def sorted_rset(cls, req, rset, row=None, col=0, **kwargs):
+    """accept sorted result set"""
+    rqlst = rset.syntax_tree()
+    if len(rqlst.children) > 1 or not rqlst.children[0].orderby:
+        return 0
+    return 2
+
+@objectify_selector
+@lltrace
+def one_etype_rset(cls, req, rset, row=None, col=0, *args, **kwargs):
+    """accept result set where entities in the specified column (or 0) are all
+    of the same type
+    """
+    if rset is None:
+        return 0
+    if len(rset.column_types(col)) != 1:
+        return 0
+    return 1
+
+@objectify_selector
+@lltrace
+def two_etypes_rset(cls, req, rset, row=None, col=0, **kwargs):
+    """accept result set where entities in the specified column (or 0) are not
+    of the same type
+    """
+    if rset:
+        etypes = rset.column_types(col)
+        if len(etypes) > 1:
+            return 1
+    return 0
+
+class non_final_entity(EClassSelector):
+    """accept if entity type found in the result set is non final.
+
+    See `EClassSelector` documentation for behaviour when row is not specified.
+    """
+    def score(self, cls, req, etype):
+        if etype in BASE_TYPES:
+            return 0
+        return 1
+
+@objectify_selector
+@lltrace
+def authenticated_user(cls, req, *args, **kwargs):
+    """accept if user is authenticated"""
+    if req.cnx.anonymous_connection:
+        return 0
+    return 1
+
+def anonymous_user():
+    return ~ authenticated_user()
+
+@objectify_selector
+@lltrace
+def primary_view(cls, req, rset, row=None, col=0, view=None, **kwargs):
+    """accept if view given as named argument is a primary view, or if no view
+    is given
+    """
+    if view is not None and not view.is_primary():
+        return 0
+    return 1
+
+@objectify_selector
+@lltrace
+def match_context_prop(cls, req, rset, row=None, col=0, context=None,
+                       **kwargs):
+    """accept if:
+    * no context given
+    * context (`basestring`) is matching the context property value for the
+      given cls
+    """
+    propval = req.property_value('%s.%s.context' % (cls.__registry__, cls.id))
+    if not propval:
+        propval = cls.context
+    if context is not None and propval and context != propval:
+        return 0
+    return 1
+
+
+class match_search_state(Selector):
+    """accept if the current request search state is in one of the expected
+    states given to the initializer
+
+    :param expected: either 'normal' or 'linksearch' (eg searching for an
+                     object to create a relation with another)
+    """
+    def __init__(self, *expected):
+        assert expected, self
+        self.expected = frozenset(expected)
+
+    def __str__(self):
+        return '%s(%s)' % (self.__class__.__name__,
+                           ','.join(sorted(str(s) for s in self.expected)))
+
+    @lltrace
+    def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
+        try:
+            if not req.search_state[0] in self.expected:
+                return 0
+        except AttributeError:
+            return 1 # class doesn't care about search state, accept it
+        return 1
+
+
+class match_form_params(match_search_state):
+    """accept if parameters specified as initializer arguments are specified
+    in request's form parameters
+
+    :param *expected: parameters (eg `basestring`) which are expected to be
+                      found in request's form parameters
+    """
+
+    @lltrace
+    def __call__(self, cls, req, *args, **kwargs):
+        score = 0
+        for param in self.expected:
+            val = req.form.get(param)
+            if not val:
+                return 0
+            score += 1
+        return len(self.expected)
+
+
+class match_kwargs(match_search_state):
+    """accept if parameters specified as initializer arguments are specified
+    in named arguments given to the selector
+
+    :param *expected: parameters (eg `basestring`) which are expected to be
+                      found in named arguments (kwargs)
+    """
+
+    @lltrace
+    def __call__(self, cls, req, *args, **kwargs):
+        for arg in self.expected:
+            if not arg in kwargs:
+                return 0
+        return len(self.expected)
+
+
+class match_user_groups(match_search_state):
+    """accept if logged users is in at least one of the given groups. Returned
+    score is the number of groups in which the user is.
+
+    If the special 'owners' group is given:
+    * if row is specified check the entity at the given row/col is owned by the
+      logged user
+    * if row is not specified check all entities in col are owned by the logged
+      user
+
+    :param *required_groups: name of groups (`basestring`) in which the logged
+                             user should be
+    """
+
+    @lltrace
+    def __call__(self, cls, req, rset=None, row=None, col=0, **kwargs):
+        user = req.user
+        if user is None:
+            return int('guests' in self.expected)
+        score = user.matching_groups(self.expected)
+        if not score and 'owners' in self.expected and rset:
+            if row is not None:
+                if not user.owns(rset[row][col]):
+                    return 0
+                score = 1
+            else:
+                score = all(user.owns(r[col]) for r in rset)
+        return score
+
+
+class match_transition(match_search_state):
+    @lltrace
+    def __call__(self, cls, req, rset=None, row=None, col=0, **kwargs):
+        try:
+            trname = req.execute('Any XN WHERE X is Transition, X eid %(x)s, X name XN',
+                                 {'x': typed_eid(req.form['treid'])})[0][0]
+        except (KeyError, IndexError):
+            return 0
+        # XXX check this is a transition that apply to the object?
+        if not trname in self.expected:
+            return 0
+        return 1
+
+
+class match_view(match_search_state):
+    """accept if the current view is in one of the expected vid given to the
+    initializer
+    """
+    @lltrace
+    def __call__(self, cls, req, rset, row=None, col=0, view=None, **kwargs):
+        if view is None or not view.id in self.expected:
+            return 0
+        return 1
+
+
+class appobject_selectable(Selector):
+    """accept with another appobject is selectable using selector's input
+    context.
+
+    :param registry: a registry name (`basestring`)
+    :param oid: an object identifier (`basestring`)
+    """
+    def __init__(self, registry, oid):
+        self.registry = registry
+        self.oid = oid
+
+    def __call__(self, cls, req, rset, *args, **kwargs):
+        try:
+            cls.vreg.select_object(self.registry, self.oid, req, rset, *args, **kwargs)
+            return 1
+        except NoSelectableObject:
+            return 0
+
+
+# not so basic selectors ######################################################
+
+class implements(ImplementsMixIn, EClassSelector):
+    """accept if entity classes found in the result set implements at least one
+    of the interfaces given as argument. Returned score is the number of
+    implemented interfaces.
+
+    See `EClassSelector` documentation for behaviour when row is not specified.
+
+    :param *expected_ifaces: expected interfaces. An interface may be a class
+                             or an entity type (e.g. `basestring`) in which case
+                             the associated class will be searched in the
+                             registry (at selection time)
+
+    note: when interface is an entity class, the score will reflect class
+          proximity so the most specific object'll be selected
+    """
+    def score_class(self, eclass, req):
+        return self.score_interfaces(eclass, eclass)
+
+
+class specified_etype_implements(implements):
+    """accept if entity class specified using an 'etype' parameters in name
+    argument or request form implements at least one of the interfaces given as
+    argument. Returned score is the number of implemented interfaces.
+
+    :param *expected_ifaces: expected interfaces. An interface may be a class
+                             or an entity type (e.g. `basestring`) in which case
+                             the associated class will be searched in the
+                             registry (at selection time)
+
+    note: when interface is an entity class, the score will reflect class
+          proximity so the most specific object'll be selected
+    """
+
+    @lltrace
+    def __call__(self, cls, req, *args, **kwargs):
+        try:
+            etype = req.form['etype']
+        except KeyError:
+            try:
+                etype = kwargs['etype']
+            except KeyError:
+                return 0
+        return self.score_class(cls.vreg.etype_class(etype), req)
+
+
+class entity_implements(ImplementsMixIn, EntitySelector):
+    """accept if entity instances found in the result set implements at least one
+    of the interfaces given as argument. Returned score is the number of
+    implemented interfaces.
+
+    See `EntitySelector` documentation for behaviour when row is not specified.
+
+    :param *expected_ifaces: expected interfaces. An interface may be a class
+                             or an entity type (e.g. `basestring`) in which case
+                             the associated class will be searched in the
+                             registry (at selection time)
+
+    note: when interface is an entity class, the score will reflect class
+          proximity so the most specific object'll be selected
+    """
+    def score_entity(self, entity):
+        return self.score_interfaces(entity, entity.__class__)
+
+
+class relation_possible(EClassSelector):
+    """accept if entity class found in the result set support the relation.
+
+    See `EClassSelector` documentation for behaviour when row is not specified.
+
+    :param rtype: a relation type (`basestring`)
+    :param role: the role of the result set entity in the relation. 'subject' or
+                 'object', default to 'subject'.
+    :param target_type: if specified, check the relation's end may be of this
+                        target type (`basestring`)
+    :param action: a relation schema action (one of 'read', 'add', 'delete')
+                   which must be granted to the logged user, else a 0 score will
+                   be returned
+    """
+    def __init__(self, rtype, role='subject', target_etype=None,
+                 action='read', once_is_enough=False):
+        super(relation_possible, self).__init__(once_is_enough)
+        self.rtype = rtype
+        self.role = role
+        self.target_etype = target_etype
+        self.action = action
+
+    @lltrace
+    def __call__(self, cls, req, *args, **kwargs):
+        rschema = cls.schema.rschema(self.rtype)
+        if not (rschema.has_perm(req, self.action)
+                or rschema.has_local_role(self.action)):
+            return 0
+        score = super(relation_possible, self).__call__(cls, req, *args, **kwargs)
+        return score
+
+    def score_class(self, eclass, req):
+        eschema = eclass.e_schema
+        try:
+            if self.role == 'object':
+                rschema = eschema.object_relation(self.rtype)
+            else:
+                rschema = eschema.subject_relation(self.rtype)
+        except KeyError:
+            return 0
+        if self.target_etype is not None:
+            try:
+                if self.role == 'subject':
+                    return int(self.target_etype in rschema.objects(eschema))
+                else:
+                    return int(self.target_etype in rschema.subjects(eschema))
+            except KeyError:
+                return 0
+        return 1
+
+
+class partial_relation_possible(PartialSelectorMixIn, relation_possible):
+    """partial version of the relation_possible selector
+
+    The selector will look for class attributes to find its missing
+    information. The list of attributes required on the class
+    for this selector are:
+
+    - `rtype`: same as `rtype` parameter of the `relation_possible` selector
+
+    - `role`: this attribute will be passed to the `cubicweb.role` function
+      to determine the role of class in the relation
+
+    - `etype` (optional): the entity type on the other side of the relation
+
+    :param action: a relation schema action (one of 'read', 'add', 'delete')
+                   which must be granted to the logged user, else a 0 score will
+                   be returned
+    """
+    def __init__(self, action='read', once_is_enough=False):
+        super(partial_relation_possible, self).__init__(None, None, None,
+                                                        action, once_is_enough)
+
+    def complete(self, cls):
+        self.rtype = cls.rtype
+        self.role = role(cls)
+        self.target_etype = getattr(cls, 'etype', None)
+
+
+class may_add_relation(EntitySelector):
+    """accept if the relation can be added to an entity found in the result set
+    by the logged user.
+
+    See `EntitySelector` documentation for behaviour when row is not specified.
+
+    :param rtype: a relation type (`basestring`)
+    :param role: the role of the result set entity in the relation. 'subject' or
+                 'object', default to 'subject'.
+    """
+
+    def __init__(self, rtype, role='subject', once_is_enough=False):
+        super(may_add_relation, self).__init__(once_is_enough)
+        self.rtype = rtype
+        self.role = role
+
+    def score_entity(self, entity):
+        rschema = entity.schema.rschema(self.rtype)
+        if self.role == 'subject':
+            if not rschema.has_perm(entity.req, 'add', fromeid=entity.eid):
+                return 0
+        elif not rschema.has_perm(entity.req, 'add', toeid=entity.eid):
+            return 0
+        return 1
+
+
+class partial_may_add_relation(PartialSelectorMixIn, may_add_relation):
+    """partial version of the may_add_relation selector
+
+    The selector will look for class attributes to find its missing
+    information. The list of attributes required on the class
+    for this selector are:
+
+    - `rtype`: same as `rtype` parameter of the `relation_possible` selector
+
+    - `role`: this attribute will be passed to the `cubicweb.role` function
+      to determine the role of class in the relation.
+
+    :param action: a relation schema action (one of 'read', 'add', 'delete')
+                   which must be granted to the logged user, else a 0 score will
+                   be returned
+    """
+    def __init__(self, once_is_enough=False):
+        super(partial_may_add_relation, self).__init__(None, None, once_is_enough)
+
+    def complete(self, cls):
+        self.rtype = cls.rtype
+        self.role = role(cls)
+
+
+class has_related_entities(EntitySelector):
+    """accept if entity found in the result set has some linked entities using
+    the specified relation (optionaly filtered according to the specified target
+    type). Checks first if the relation is possible.
+
+    See `EntitySelector` documentation for behaviour when row is not specified.
+
+    :param rtype: a relation type (`basestring`)
+    :param role: the role of the result set entity in the relation. 'subject' or
+                 'object', default to 'subject'.
+    :param target_type: if specified, check the relation's end may be of this
+                        target type (`basestring`)
+    """
+    def __init__(self, rtype, role='subject', target_etype=None,
+                 once_is_enough=False):
+        super(has_related_entities, self).__init__(once_is_enough)
+        self.rtype = rtype
+        self.role = role
+        self.target_etype = target_etype
+
+    def score_entity(self, entity):
+        relpossel = relation_possible(self.rtype, self.role, self.target_etype)
+        if not relpossel.score_class(entity.__class__, entity.req):
+            return 0
+        rset = entity.related(self.rtype, self.role)
+        if self.target_etype:
+            return any(r for r in rset.description if r[0] == self.target_etype)
+        return rset and 1 or 0
+
+
+class partial_has_related_entities(PartialSelectorMixIn, has_related_entities):
+    """partial version of the has_related_entities selector
+
+    The selector will look for class attributes to find its missing
+    information. The list of attributes required on the class
+    for this selector are:
+
+    - `rtype`: same as `rtype` parameter of the `relation_possible` selector
+
+    - `role`: this attribute will be passed to the `cubicweb.role` function
+      to determine the role of class in the relation.
+
+    - `etype` (optional): the entity type on the other side of the relation
+
+    :param action: a relation schema action (one of 'read', 'add', 'delete')
+                   which must be granted to the logged user, else a 0 score will
+                   be returned
+    """
+    def __init__(self, once_is_enough=False):
+        super(partial_has_related_entities, self).__init__(None, None,
+                                                           None, once_is_enough)
+    def complete(self, cls):
+        self.rtype = cls.rtype
+        self.role = role(cls)
+        self.target_etype = getattr(cls, 'etype', None)
+
+
+class has_permission(EntitySelector):
+    """accept if user has the permission to do the requested action on a result
+    set entity.
+
+    * if row is specified, return 1 if user has the permission on the entity
+      instance found in the specified cell
+    * else return a positive score if user has the permission for every entity
+      in the found in the specified column
+
+    note: None values (resulting from some outer join in the query) are not
+          considered.
+
+    :param action: an entity schema action (eg 'read'/'add'/'delete'/'update')
+    """
+    def __init__(self, action, once_is_enough=False):
+        super(has_permission, self).__init__(once_is_enough)
+        self.action = action
+
+    @lltrace
+    def __call__(self, cls, req, rset, row=None, col=0, **kwargs):
+        if rset is None:
+            return 0
+        user = req.user
+        action = self.action
+        if row is None:
+            score = 0
+            need_local_check = []
+            geteschema = cls.schema.eschema
+            for etype in rset.column_types(0):
+                if etype in BASE_TYPES:
+                    return 0
+                eschema = geteschema(etype)
+                if not user.matching_groups(eschema.get_groups(action)):
+                    if eschema.has_local_role(action):
+                        # have to ckeck local roles
+                        need_local_check.append(eschema)
+                        continue
+                    else:
+                        # even a local role won't be enough
+                        return 0
+                score += 1
+            if need_local_check:
+                # check local role for entities of necessary types
+                for i, row in enumerate(rset):
+                    if not rset.description[i][0] in need_local_check:
+                        continue
+                    if not self.score(req, rset, i, col):
+                        return 0
+                score += 1
+            return score
+        return self.score(req, rset, row, col)
+
+    def score_entity(self, entity):
+        if entity.has_perm(self.action):
+            return 1
+        return 0
+
+
+class has_add_permission(EClassSelector):
+    """accept if logged user has the add permission on entity class found in the
+    result set, and class is not a strict subobject.
+
+    See `EClassSelector` documentation for behaviour when row is not specified.
+    """
+    def score(self, cls, req, etype):
+        eschema = cls.schema.eschema(etype)
+        if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
+               and eschema.has_perm(req, 'add'):
+            return 1
+        return 0
+
+
+class rql_condition(EntitySelector):
+    """accept if an arbitrary rql return some results for an eid found in the
+    result set. Returned score is the number of items returned by the rql
+    condition.
+
+    See `EntitySelector` documentation for behaviour when row is not specified.
+
+    :param expression: basestring containing an rql expression, which should use
+                       X variable to represent the context entity and may use U
+                       to represent the logged user
+
+    return the sum of the number of items returned by the rql condition as score
+    or 0 at the first entity scoring to zero.
+    """
+    def __init__(self, expression, once_is_enough=False):
+        super(rql_condition, self).__init__(once_is_enough)
+        if 'U' in frozenset(split_expression(expression)):
+            rql = 'Any X WHERE X eid %%(x)s, U eid %%(u)s, %s' % expression
+        else:
+            rql = 'Any X WHERE X eid %%(x)s, %s' % expression
+        self.rql = rql
+
+    def score(self, req, rset, row, col):
+        try:
+            return len(req.execute(self.rql, {'x': rset[row][col],
+                                              'u': req.user.eid}, 'x'))
+        except Unauthorized:
+            return 0
+
+
+class but_etype(EntitySelector):
+    """accept if the given entity types are not found in the result set.
+
+    See `EntitySelector` documentation for behaviour when row is not specified.
+
+    :param *etypes: entity types (`basestring`) which should be refused
+    """
+    def __init__(self, *etypes):
+        super(but_etype, self).__init__()
+        self.but_etypes = etypes
+
+    def score(self, req, rset, row, col):
+        if rset.description[row][col] in self.but_etypes:
+            return 0
+        return 1
+
+
+class score_entity(EntitySelector):
+    """accept if some arbitrary function return a positive score for an entity
+    found in the result set.
+
+    See `EntitySelector` documentation for behaviour when row is not specified.
+
+    :param scorefunc: callable expected to take an entity as argument and to
+                      return a score >= 0
+    """
+    def __init__(self, scorefunc, once_is_enough=False):
+        super(score_entity, self).__init__(once_is_enough)
+        self.score_entity = scorefunc
+
+
+# XXX DEPRECATED ##############################################################
+
+yes_selector = deprecated_function(yes)
+norset_selector = deprecated_function(none_rset)
+rset_selector = deprecated_function(any_rset)
+anyrset_selector = deprecated_function(nonempty_rset)
+emptyrset_selector = deprecated_function(empty_rset)
+onelinerset_selector = deprecated_function(one_line_rset)
+twolinerset_selector = deprecated_function(two_lines_rset)
+twocolrset_selector = deprecated_function(two_cols_rset)
+largerset_selector = deprecated_function(paginated_rset)
+sortedrset_selector = deprecated_function(sorted_rset)
+oneetyperset_selector = deprecated_function(one_etype_rset)
+multitype_selector = deprecated_function(two_etypes_rset)
+anonymous_selector = deprecated_function(anonymous_user)
+not_anonymous_selector = deprecated_function(authenticated_user)
+primaryview_selector = deprecated_function(primary_view)
+contextprop_selector = deprecated_function(match_context_prop)
+
+def nfentity_selector(cls, req, rset, row=None, col=0, **kwargs):
+    return non_final_entity()(cls, req, rset, row, col)
+nfentity_selector = deprecated_function(nfentity_selector)
+
+def implement_interface(cls, req, rset, row=None, col=0, **kwargs):
+    return implements(*cls.accepts_interfaces)(cls, req, rset, row, col)
+_interface_selector = deprecated_function(implement_interface)
+interface_selector = deprecated_function(implement_interface)
+implement_interface = deprecated_function(implement_interface, 'use implements')
+
+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 = deprecated_function(accept_etype)
+accept_etype = deprecated_function(accept_etype, 'use specified_etype_implements')
+
+def searchstate_selector(cls, req, rset, row=None, col=0, **kwargs):
+    return match_search_state(cls.search_states)(cls, req, rset, row, col)
+searchstate_selector = deprecated_function(searchstate_selector)
+
+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 = deprecated_function(match_user_group)
+match_user_group = deprecated_function(match_user_group)
+
+def has_relation(cls, req, rset, 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)
+has_relation = deprecated_function(has_relation)
+
+def one_has_relation(cls, req, rset, 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)
+one_has_relation = deprecated_function(one_has_relation, 'use relation_possible selector')
+
+def accept_rset(cls, req, rset, 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 = deprecated_function(accept_rset)
+accept_rset = deprecated_function(accept_rset, 'use implements selector')
+
+accept = chainall(non_final_entity(), accept_rset, name='accept')
+accept_selector = deprecated_function(accept)
+accept = deprecated_function(accept, 'use implements selector')
+
+accept_one = deprecated_function(chainall(one_line_rset, accept,
+                                          name='accept_one'))
+accept_one_selector = deprecated_function(accept_one)
+
+
+def _rql_condition(cls, req, rset, row=None, col=0, **kwargs):
+    if cls.condition:
+        return rql_condition(cls.condition)(cls, req, rset, row, col)
+    return 1
+_rqlcondition_selector = deprecated_function(_rql_condition)
+
+rqlcondition_selector = deprecated_function(chainall(non_final_entity(), one_line_rset, _rql_condition,
+                         name='rql_condition'))
+
+def but_etype_selector(cls, req, rset, row=None, col=0, **kwargs):
+    return but_etype(cls.etype)(cls, req, rset, row, col)
+but_etype_selector = deprecated_function(but_etype_selector)
+
+@lltrace
+def etype_rtype_selector(cls, req, rset, 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_function(etype_rtype_selector)
+
+#req_form_params_selector = deprecated_function(match_form_params) # form_params
+#kwargs_selector = deprecated_function(match_kwargs) # expected_kwargs
+
+# compound selectors ##########################################################
+
+searchstate_accept = chainall(nonempty_rset(), accept,
+                              name='searchstate_accept')
+searchstate_accept_selector = deprecated_function(searchstate_accept)
+
+searchstate_accept_one = chainall(one_line_rset, accept, _rql_condition,
+                                  name='searchstate_accept_one')
+searchstate_accept_one_selector = deprecated_function(searchstate_accept_one)
+
+searchstate_accept = deprecated_function(searchstate_accept)
+searchstate_accept_one = deprecated_function(searchstate_accept_one)
+
+
+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',
+                 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/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -93,10 +93,10 @@
     print 'inserting default user and groups'
     needisfix = []
     for group in BASEGROUPS:
-        rset = session.execute('INSERT EGroup X: X name %(name)s',
+        rset = session.execute('INSERT CWGroup X: X name %(name)s',
                                {'name': unicode(group)})
         needisfix.append( (rset.rows[0][0], rset.description[0][0]) )
-    rset = session.execute('INSERT EUser X: X login %(login)s, X upassword %(pwd)s',
+    rset = session.execute('INSERT CWUser X: X login %(login)s, X upassword %(pwd)s',
                            {'login': login, 'pwd': pwd})
     needisfix.append( (rset.rows[0][0], rset.description[0][0]) )
     session.execute('SET U in_group G WHERE G name "managers"')
@@ -116,10 +116,10 @@
         handler.session.unsafe_execute('SET X is_instance_of E WHERE X eid %(x)s, E name %(name)s',
                                        {'x': eid, 'name': etype}, 'x')
     # insert versions
-    handler.cmd_add_entity('EProperty', pkey=u'system.version.cubicweb',
+    handler.cmd_add_entity('CWProperty', pkey=u'system.version.cubicweb',
                            value=unicode(config.cubicweb_version()))
     for cube in config.cubes():
-        handler.cmd_add_entity('EProperty', 
+        handler.cmd_add_entity('CWProperty', 
                                pkey=u'system.version.%s' % cube.lower(),
                                value=unicode(config.cube_version(cube)))
     # yoo !
--- a/server/checkintegrity.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/checkintegrity.py	Mon May 04 13:09:48 2009 +0200
@@ -8,8 +8,8 @@
 __docformat__ = "restructuredtext en"
 
 import sys
+from datetime import datetime
 
-from mx.DateTime import now
 from logilab.common.shellutils import ProgressBar
 
 from cubicweb.server.sqlutils import SQL_PREFIX
@@ -88,7 +88,7 @@
           ', '.join(sorted(str(e) for e in etypes))
     pb = ProgressBar(len(etypes) + 1)
     # first monkey patch Entity.check to disable validation
-    from cubicweb.common.entity import Entity
+    from cubicweb.entity import Entity
     _check = Entity.check
     Entity.check = lambda self, creation=False: True
     # clear fti table first
@@ -111,7 +111,7 @@
                           'VocabularyConstraint', 'RQLConstraint',
                           'RQLVocabularyConstraint')
     rql = ('Any COUNT(X),RN,EN,ECTN GROUPBY RN,EN,ECTN ORDERBY 1 '
-           'WHERE X is EConstraint, R constrained_by X, '
+           'WHERE X is CWConstraint, R constrained_by X, '
            'R relation_type RT, R from_entity ET, RT name RN, '
            'ET name EN, X cstrtype ECT, ECT name ECTN')
     for count, rn, en, cstrname in session.execute(rql):
@@ -237,8 +237,8 @@
     eidcolumn = SQL_PREFIX + 'eid'
     for etype, in cursor.fetchall():
         table = SQL_PREFIX + etype
-        for rel, default in ( ('creation_date', now()),
-                              ('modification_date', now()), ):
+        for rel, default in ( ('creation_date', datetime.now()),
+                              ('modification_date', datetime.now()), ):
             column = SQL_PREFIX + rel
             cursor = session.system_sql("SELECT %s FROM %s WHERE %s is NULL"
                                         % (eidcolumn, table, column))
@@ -252,7 +252,7 @@
                     print >> sys.stderr, ' [FIXED]'
                 else:
                     print >> sys.stderr
-    cursor = session.system_sql('SELECT MIN(%s) FROM %sEUser;' % (eidcolumn,
+    cursor = session.system_sql('SELECT MIN(%s) FROM %sCWUser;' % (eidcolumn,
                                                                   SQL_PREFIX))
     default_user_eid = cursor.fetchone()[0]
     assert default_user_eid is not None, 'no user defined !'
--- a/server/hookhelper.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/hookhelper.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """helper functions for application hooks
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -10,7 +10,7 @@
 from threading import Lock
 
 from cubicweb import RepositoryError
-from cubicweb.server.pool import Operation, SingleLastOperation
+from cubicweb.server.pool import SingleLastOperation
 
 
 def entity_name(session, eid):
--- a/server/hooks.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/hooks.py	Mon May 04 13:09:48 2009 +0200
@@ -7,12 +7,10 @@
 """
 __docformat__ = "restructuredtext en"
 
-from mx.DateTime import now
+from datetime import datetime
 
 from cubicweb import UnknownProperty, ValidationError, BadConnectionId
 
-from cubicweb.common.uilib import soup2xhtml
-
 from cubicweb.server.pool import Operation, LateOperation, PreCommitOperation
 from cubicweb.server.hookhelper import (check_internal_entity, previous_state,
                                      get_user_sessions, rproperty)
@@ -30,14 +28,14 @@
     this is a conveniency hook, you shouldn't have to disable it
     """
     if not 'creation_date' in entity:
-        entity['creation_date'] = now()
+        entity['creation_date'] = datetime.now()
     if not 'modification_date' in entity:
-        entity['modification_date'] = now()
+        entity['modification_date'] = datetime.now()
 
 def setmtime_before_update_entity(session, entity):
     """update an entity -> set modification date"""
     if not 'modification_date' in entity:
-        entity['modification_date'] = now()
+        entity['modification_date'] = datetime.now()
         
 class SetCreatorOp(PreCommitOperation):
         
@@ -88,7 +86,6 @@
         FTIndexEntityOp(session, entity=session.entity(eidto))
     elif ftcontainer == 'object':
         FTIndexEntityOp(session, entity=session.entity(eidfrom))
-
 def fti_update_after_delete_relation(session, eidfrom, rtype, eidto):
     """sync fulltext index when relevant relation is deleted. Reindexing both
     entities is necessary.
@@ -128,8 +125,8 @@
     hm.register_hook(fti_update_after_delete_relation, 'after_delete_relation', '')
     if 'is' in hm.schema:
         hm.register_hook(setis_after_add_entity, 'after_add_entity', '')
-    if 'EUser' in hm.schema:
-        hm.register_hook(setowner_after_add_user, 'after_add_entity', 'EUser')
+    if 'CWUser' in hm.schema:
+        hm.register_hook(setowner_after_add_user, 'after_add_entity', 'CWUser')
             
 # core hooks ##################################################################
     
@@ -210,30 +207,6 @@
 
 
 
-class tidy_html_fields(object):
-    """tidy HTML in rich text strings
-
-    FIXME: (adim) the whole idea of having a class is to store the
-    event type. There might be another way to get dynamically the
-    event inside the hook function.
-    """
-    # FIXME hooks manager use func_name to register
-    func_name = 'tidy_html_field'
-    
-    def __init__(self, event):
-        self.event = event
-
-    def __call__(self, session, entity):
-        for attr in entity.formatted_attrs():
-            value = entity.get(attr)
-            # text was not changed
-            if self.event == 'before_add_entity':
-                fmt = entity.get('%s_format' % attr)
-            else:
-                fmt = entity.get_value('%s_format' % attr)
-            if value and fmt == 'text/html':
-                entity[attr] = soup2xhtml(value, session.encoding)
-
 
 class CheckRequiredRelationOperation(LateOperation):
     """checking relation cardinality has to be done after commit in
@@ -309,7 +282,7 @@
 
 def _register_core_hooks(hm):
     hm.register_hook(handle_composite_before_del_relation, 'before_delete_relation', '')
-    hm.register_hook(before_del_group, 'before_delete_entity', 'EGroup')
+    hm.register_hook(before_del_group, 'before_delete_entity', 'CWGroup')
     
     #hm.register_hook(cstrcheck_before_update_entity, 'before_update_entity', '')
     hm.register_hook(cardinalitycheck_after_add_entity, 'after_add_entity', '')
@@ -317,8 +290,6 @@
     hm.register_hook(cstrcheck_after_add_relation, 'after_add_relation', '')
     hm.register_hook(uniquecstrcheck_before_modification, 'before_add_entity', '')
     hm.register_hook(uniquecstrcheck_before_modification, 'before_update_entity', '')
-    hm.register_hook(tidy_html_fields('before_add_entity'), 'before_add_entity', '')
-    hm.register_hook(tidy_html_fields('before_update_entity'), 'before_update_entity', '')
 
 
 # user/groups synchronisation #################################################
@@ -391,7 +362,7 @@
     
 def _register_usergroup_hooks(hm):
     """register user/group related hooks on the hooks manager"""
-    hm.register_hook(after_del_user, 'after_delete_entity', 'EUser')
+    hm.register_hook(after_del_user, 'after_delete_entity', 'CWUser')
     hm.register_hook(after_add_in_group, 'after_add_relation', 'in_group')
     hm.register_hook(after_del_in_group, 'after_delete_relation', 'in_group')
 
@@ -471,10 +442,10 @@
                                  str(eschema))
 
 
-# EProperty hooks #############################################################
+# CWProperty hooks #############################################################
 
 
-class DelEPropertyOp(Operation):
+class DelCWPropertyOp(Operation):
     """a user's custom properties has been deleted"""
     
     def commit_event(self):
@@ -484,14 +455,14 @@
         except KeyError:
             self.error('%s has no associated value', self.key)
 
-class ChangeEPropertyOp(Operation):
+class ChangeCWPropertyOp(Operation):
     """a user's custom properties has been added/changed"""
         
     def commit_event(self):
         """the observed connections pool has been commited"""
         self.epropdict[self.key] = self.value
 
-class AddEPropertyOp(Operation):
+class AddCWPropertyOp(Operation):
     """a user's custom properties has been added/changed"""
         
     def commit_event(self):
@@ -499,7 +470,7 @@
         eprop = self.eprop
         if not eprop.for_user:
             self.repo.vreg.eprop_values[eprop.pkey] = eprop.value
-        # if for_user is set, update is handled by a ChangeEPropertyOp operation
+        # if for_user is set, update is handled by a ChangeCWPropertyOp operation
 
 def after_add_eproperty(session, entity):
     key, value = entity.pkey, entity.value
@@ -513,7 +484,7 @@
         session.unsafe_execute('SET P for_user U WHERE P eid %(x)s,U eid %(u)s',
                                {'x': entity.eid, 'u': session.user.eid}, 'x')
     else:
-        AddEPropertyOp(session, eprop=entity)
+        AddCWPropertyOp(session, eprop=entity)
         
 def after_update_eproperty(session, entity):
     key, value = entity.pkey, entity.value
@@ -525,11 +496,11 @@
         raise ValidationError(entity.eid, {'value': session._(str(ex))})
     if entity.for_user:
         for session_ in get_user_sessions(session.repo, entity.for_user[0].eid):
-            ChangeEPropertyOp(session, epropdict=session_.user.properties,
+            ChangeCWPropertyOp(session, epropdict=session_.user.properties,
                               key=key, value=value)
     else:
         # site wide properties
-        ChangeEPropertyOp(session, epropdict=session.vreg.eprop_values,
+        ChangeCWPropertyOp(session, epropdict=session.vreg.eprop_values,
                           key=key, value=value)
         
 def before_del_eproperty(session, eid):
@@ -540,10 +511,10 @@
     else:
         key = session.execute('Any K WHERE P eid %(x)s, P pkey K',
                               {'x': eid}, 'x')[0][0]
-        DelEPropertyOp(session, epropdict=session.vreg.eprop_values, key=key)
+        DelCWPropertyOp(session, epropdict=session.vreg.eprop_values, key=key)
 
 def after_add_for_user(session, fromeid, rtype, toeid):
-    if not session.describe(fromeid)[0] == 'EProperty':
+    if not session.describe(fromeid)[0] == 'CWProperty':
         return
     key, value = session.execute('Any K,V WHERE P eid %(x)s,P pkey K,P value V',
                                  {'x': fromeid}, 'x')[0]
@@ -551,7 +522,7 @@
         raise ValidationError(fromeid,
                               {'for_user': session._("site-wide property can't be set for user")})
     for session_ in get_user_sessions(session.repo, toeid):
-        ChangeEPropertyOp(session, epropdict=session_.user.properties,
+        ChangeCWPropertyOp(session, epropdict=session_.user.properties,
                           key=key, value=value)
         
 def before_del_for_user(session, fromeid, rtype, toeid):
@@ -559,12 +530,12 @@
                           {'x': fromeid}, 'x')[0][0]
     relation_deleted(session, fromeid, rtype, toeid)
     for session_ in get_user_sessions(session.repo, toeid):
-        DelEPropertyOp(session, epropdict=session_.user.properties, key=key)
+        DelCWPropertyOp(session, epropdict=session_.user.properties, key=key)
 
 def _register_eproperty_hooks(hm):
     """register workflow related hooks on the hooks manager"""
-    hm.register_hook(after_add_eproperty, 'after_add_entity', 'EProperty')
-    hm.register_hook(after_update_eproperty, 'after_update_entity', 'EProperty')
-    hm.register_hook(before_del_eproperty, 'before_delete_entity', 'EProperty')
+    hm.register_hook(after_add_eproperty, 'after_add_entity', 'CWProperty')
+    hm.register_hook(after_update_eproperty, 'after_update_entity', 'CWProperty')
+    hm.register_hook(before_del_eproperty, 'before_delete_entity', 'CWProperty')
     hm.register_hook(after_add_for_user, 'after_add_relation', 'for_user')
     hm.register_hook(before_del_for_user, 'before_delete_relation', 'for_user')
--- a/server/hooksmanager.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/hooksmanager.py	Mon May 04 13:09:48 2009 +0200
@@ -23,7 +23,7 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -180,13 +180,12 @@
 #         self.register_hook(tidy_html_fields('before_add_entity'), 'before_add_entity', '')
 #         self.register_hook(tidy_html_fields('before_update_entity'), 'before_update_entity', '')
             
-from cubicweb.vregistry import autoselectors
-from cubicweb.common.appobject import AppObject
-from cubicweb.common.registerers import accepts_registerer, yes_registerer
-from cubicweb.common.selectors import yes
+from cubicweb.selectors import yes
+from cubicweb.appobject import AppObject
 
-class autoid(autoselectors):
+class autoid(type):
     """metaclass to create an unique 'id' attribute on the class using it"""
+    # XXX is this metaclass really necessary ?
     def __new__(mcs, name, bases, classdict):
         cls = super(autoid, mcs).__new__(mcs, name, bases, classdict)
         cls.id = str(id(cls))
@@ -195,8 +194,7 @@
 class Hook(AppObject):
     __metaclass__ = autoid
     __registry__ = 'hooks'
-    __registerer__ = accepts_registerer
-    __selectors__ = (yes,)
+    __select__ = yes()
     # set this in derivated classes
     events = None
     accepts = None
@@ -245,7 +243,6 @@
         raise NotImplementedError
     
 class SystemHook(Hook):
-    __registerer__ = yes_registerer
     accepts = ('',)
 
 from logging import getLogger
--- a/server/migractions.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/migractions.py	Mon May 04 13:09:48 2009 +0200
@@ -19,15 +19,17 @@
 import sys
 import os
 from os.path import join, exists
+from datetime import datetime
 
-from mx.DateTime import now
+from logilab.common.deprecation import deprecated_function, obsolete
 from logilab.common.decorators import cached
 from logilab.common.adbh import get_adv_func_helper
 
 from yams.constraints import SizeConstraint
 from yams.schema2sql import eschema2sql, rschema2sql
 
-from cubicweb import AuthenticationError
+from cubicweb import AuthenticationError, ETYPE_NAME_MAP
+from cubicweb.schema import CubicWebRelationSchema
 from cubicweb.dbapi import get_repository, repo_connect
 from cubicweb.common.migration import MigrationHelper, yes
 
@@ -38,42 +40,7 @@
 except ImportError: # LAX
     pass
 
-def set_sql_prefix(prefix):
-    """3.1.5 migration function: allow to unset/reset SQL_PREFIX"""
-    for module in ('checkintegrity', 'migractions', 'schemahooks',
-                   'sources.rql2sql', 'sources.native'):
-        try:
-            sys.modules['cubicweb.server.%s' % module].SQL_PREFIX = prefix
-            print 'changed SQL_PREFIX for %s' % module
-        except KeyError:
-            pass
-        
-def update_database(repo):
-    """3.1.3 migration function: update database schema by adding SQL_PREFIX to
-    entity type tables and columns
-    """
-    pool = repo._get_pool()
-    source = repo.system_source
-    sqlcu = pool['system']
-    for etype in repo.schema.entities():
-        if etype.is_final():
-            continue
-        try:
-            sqlcu.execute('ALTER TABLE %s RENAME TO cw_%s' % (etype, etype))
-            print 'renamed %s table' % etype
-        except:
-            pass
-        for rschema in etype.subject_relations():
-            if rschema == 'has_text':
-                continue
-            if rschema.is_final() or rschema.inlined:
-                sqlcu.execute('ALTER TABLE cw_%s RENAME %s TO cw_%s'
-                              % (etype, rschema, rschema))
-                print 'renamed %s.%s column' % (etype, rschema)
-    pool.commit()
-    repo._free_pool(pool)
 
-        
 class ServerMigrationHelper(MigrationHelper):
     """specific migration helper for server side  migration scripts,
     providind actions related to schema/data migration
@@ -98,23 +65,13 @@
 
     @cached
     def repo_connect(self):
-        try:
-            self.repo = get_repository(method='inmemory', config=self.config)
-        except:
-            import traceback
-            traceback.print_exc()
-            print '3.1.5 migration'
-            # XXX 3.1.5 migration
-            set_sql_prefix('')
-            self.repo = get_repository(method='inmemory', config=self.config)
-            update_database(self.repo)
-            set_sql_prefix('cw_')
+        self.repo = get_repository(method='inmemory', config=self.config)
         return self.repo
-    
+
     def shutdown(self):
         if self.repo is not None:
             self.repo.shutdown()
-        
+
     def rewrite_vcconfiguration(self):
         """write current installed versions (of cubicweb software
         and of each used cube) into the database
@@ -124,12 +81,12 @@
             pkgversion = self.config.cube_version(pkg)
             self.cmd_set_property('system.version.%s' % pkg.lower(), pkgversion)
         self.commit()
-        
+
     def backup_database(self, backupfile=None, askconfirm=True):
         config = self.config
         source = config.sources()['system']
         helper = get_adv_func_helper(source['db-driver'])
-        date = now().strftime('%Y-%m-%d_%H:%M:%S')
+        date = datetime.now().strftime('%Y-%m-%d_%H:%M:%S')
         app = config.appid
         backupfile = backupfile or join(config.backup_dir(),
                                         '%s-%s.dump' % (app, date))
@@ -156,7 +113,7 @@
                 print 'database backup:', backupfile
                 restrict_perms_to_user(backupfile, self.info)
                 break
-        
+
     def restore_database(self, backupfile, drop=True):
         config = self.config
         source = config.sources()['system']
@@ -182,7 +139,7 @@
                     else:
                         break
             print 'database restored'
-        
+
     def migrate(self, vcconf, toupgrade, options):
         if not options.fs_only:
             if options.backup_db is None:
@@ -190,7 +147,7 @@
             elif options.backup_db:
                 self.backup_database(askconfirm=False)
         super(ServerMigrationHelper, self).migrate(vcconf, toupgrade, options)
-    
+
     def process_script(self, migrscript, funcname=None, *args, **kwargs):
         """execute a migration script
         in interactive mode,  display the migration script path, ask for
@@ -202,7 +159,7 @@
         else:
             return super(ServerMigrationHelper, self).process_script(
                 migrscript, funcname, *args, **kwargs)
-        
+
     @property
     def cnx(self):
         """lazy connection"""
@@ -237,7 +194,7 @@
     @property
     def session(self):
         return self.repo._get_session(self.cnx.sessionid)
-    
+
     @property
     @cached
     def rqlcursor(self):
@@ -246,15 +203,15 @@
         # some query while no pool is set on the session (eg on entity attribute
         # access for instance)
         return self.cnx.cursor()
-    
+
     def commit(self):
         if hasattr(self, '_cnx'):
             self._cnx.commit()
-            
+
     def rollback(self):
         if hasattr(self, '_cnx'):
             self._cnx.rollback()
-                   
+
     def rqlexecall(self, rqliter, cachekey=None, ask_confirm=True):
         for rql, kwargs in rqliter:
             self.rqlexec(rql, kwargs, cachekey, ask_confirm)
@@ -268,12 +225,12 @@
                         'rql': self.rqlexec,
                         'rqliter': self.rqliter,
                         'schema': self.repo.schema,
-                        # XXX deprecate
-                        'newschema': self.fs_schema,
                         'fsschema': self.fs_schema,
-                        'cnx': self.cnx,
                         'session' : self.session,
                         'repo' : self.repo,
+                        'synchronize_schema': deprecated_function(self.sync_schema_props_perms),
+                        'synchronize_eschema': deprecated_function(self.sync_schema_props_perms),
+                        'synchronize_rschema': deprecated_function(self.sync_schema_props_perms),
                         })
         return context
 
@@ -281,9 +238,9 @@
     def group_mapping(self):
         """cached group mapping"""
         return ss.group_mapping(self.rqlcursor)
-        
+
     def exec_event_script(self, event, cubepath=None, funcname=None,
-                          *args, **kwargs):            
+                          *args, **kwargs):
         if cubepath:
             apc = join(cubepath, 'migration', '%s.py' % event)
         else:
@@ -308,312 +265,10 @@
                     self.repo.hm.register_hook(setowner_after_add_entity,
                                                'after_add_entity', '')
                     self.reactivate_verification_hooks()
-    
-    # base actions ############################################################
-
-    def checkpoint(self):
-        """checkpoint action"""
-        if self.confirm('commit now ?', shell=False):
-            self.commit()
-
-    def cmd_add_cube(self, cube, update_database=True):
-        self.cmd_add_cubes( (cube,), update_database)
-    
-    def cmd_add_cubes(self, cubes, update_database=True):
-        """update_database is telling if the database schema should be updated
-        or if only the relevant eproperty should be inserted (for the case where
-        a cube has been extracted from an existing application, so the
-        cube schema is already in there)
-        """
-        newcubes = super(ServerMigrationHelper, self).cmd_add_cubes(cubes)
-        if not newcubes:
-            return
-        for pack in newcubes:
-            self.cmd_set_property('system.version.'+pack,
-                                  self.config.cube_version(pack))
-        if not update_database:
-            self.commit()
-            return
-        newcubes_schema = self.config.load_schema()
-        new = set()
-        # execute pre-create files
-        for pack in reversed(newcubes):
-            self.exec_event_script('precreate', self.config.cube_dir(pack))
-        # add new entity and relation types
-        for rschema in newcubes_schema.relations():
-            if not rschema in self.repo.schema:
-                self.cmd_add_relation_type(rschema.type)
-                new.add(rschema.type)
-        for eschema in newcubes_schema.entities():
-            if not eschema in self.repo.schema:
-                self.cmd_add_entity_type(eschema.type)
-                new.add(eschema.type)
-        # check if attributes has been added to existing entities
-        for rschema in newcubes_schema.relations():
-            existingschema = self.repo.schema.rschema(rschema.type)
-            for (fromtype, totype) in rschema.iter_rdefs():
-                if existingschema.has_rdef(fromtype, totype):
-                    continue
-                # check we should actually add the relation definition
-                if not (fromtype in new or totype in new or rschema in new):
-                    continue
-                self.cmd_add_relation_definition(str(fromtype), rschema.type, 
-                                                 str(totype))
-        # execute post-create files
-        for pack in reversed(newcubes):
-            self.exec_event_script('postcreate', self.config.cube_dir(pack))
-            self.commit()        
-                
-    def cmd_remove_cube(self, cube):
-        removedcubes = super(ServerMigrationHelper, self).cmd_remove_cube(cube)
-        if not removedcubes:
-            return
-        fsschema = self.fs_schema
-        removedcubes_schema = self.config.load_schema()
-        reposchema = self.repo.schema
-        # execute pre-remove files
-        for pack in reversed(removedcubes):
-            self.exec_event_script('preremove', self.config.cube_dir(pack))
-        # remove cubes'entity and relation types
-        for rschema in fsschema.relations():
-            if not rschema in removedcubes_schema and rschema in reposchema:
-                self.cmd_drop_relation_type(rschema.type)
-        for eschema in fsschema.entities():
-            if not eschema in removedcubes_schema and eschema in reposchema:
-                self.cmd_drop_entity_type(eschema.type)
-        for rschema in fsschema.relations():
-            if rschema in removedcubes_schema and rschema in reposchema: 
-                # check if attributes/relations has been added to entities from 
-                # other cubes
-                for fromtype, totype in rschema.iter_rdefs():
-                    if not removedcubes_schema[rschema.type].has_rdef(fromtype, totype) and \
-                           reposchema[rschema.type].has_rdef(fromtype, totype):
-                        self.cmd_drop_relation_definition(
-                            str(fromtype), rschema.type, str(totype))
-        # execute post-remove files
-        for pack in reversed(removedcubes):
-            self.exec_event_script('postremove', self.config.cube_dir(pack))
-            self.rqlexec('DELETE EProperty X WHERE X pkey %(pk)s',
-                         {'pk': u'system.version.'+pack}, ask_confirm=False)
-            self.commit()
-            
-    # schema migration actions ################################################
-    
-    def cmd_add_attribute(self, etype, attrname, attrtype=None, commit=True):
-        """add a new attribute on the given entity type"""
-        if attrtype is None:
-            rschema = self.fs_schema.rschema(attrname)
-            attrtype = rschema.objects(etype)[0]
-        self.cmd_add_relation_definition(etype, attrname, attrtype, commit=commit)
-        
-    def cmd_drop_attribute(self, etype, attrname, commit=True):
-        """drop an existing attribute from the given entity type
-        
-        `attrname` is a string giving the name of the attribute to drop
-        """
-        rschema = self.repo.schema.rschema(attrname)
-        attrtype = rschema.objects(etype)[0]
-        self.cmd_drop_relation_definition(etype, attrname, attrtype, commit=commit)
 
-    def cmd_rename_attribute(self, etype, oldname, newname, commit=True):
-        """rename an existing attribute of the given entity type
-        
-        `oldname` is a string giving the name of the existing attribute
-        `newname` is a string giving the name of the renamed attribute
-        """
-        eschema = self.fs_schema.eschema(etype)
-        attrtype = eschema.destination(newname)
-        # have to commit this first step anyway to get the definition
-        # actually in the schema
-        self.cmd_add_attribute(etype, newname, attrtype, commit=True)
-        # skipp NULL values if the attribute is required
-        rql = 'SET X %s VAL WHERE X is %s, X %s VAL' % (newname, etype, oldname)
-        card = eschema.rproperty(newname, 'cardinality')[0]
-        if card == '1':
-            rql += ', NOT X %s NULL' % oldname
-        self.rqlexec(rql, ask_confirm=self.verbosity>=2)
-        self.cmd_drop_attribute(etype, oldname, commit=commit)
-            
-    def cmd_add_entity_type(self, etype, auto=True, commit=True):
-        """register a new entity type
-        
-        in auto mode, automatically register entity's relation where the
-        targeted type is known
-        """
-        applschema = self.repo.schema
-        if etype in applschema:
-            eschema = applschema[etype]
-            if eschema.is_final():
-                applschema.del_entity_type(etype)
-        else:
-            eschema = self.fs_schema.eschema(etype)
-        confirm = self.verbosity >= 2
-        # register the entity into EEType
-        self.rqlexecall(ss.eschema2rql(eschema), ask_confirm=confirm)
-        # add specializes relation if needed
-        self.rqlexecall(ss.eschemaspecialize2rql(eschema), ask_confirm=confirm)
-        # register groups / permissions for the entity
-        self.rqlexecall(ss.erperms2rql(eschema, self.group_mapping()),
-                        ask_confirm=confirm)
-        # register entity's attributes
-        for rschema, attrschema in eschema.attribute_definitions():
-            # ignore those meta relations, they will be automatically added
-            if rschema.type in ('eid', 'creation_date', 'modification_date'):
-                continue
-            if not rschema.type in applschema:
-                # need to add the relation type and to commit to get it
-                # actually in the schema
-                self.cmd_add_relation_type(rschema.type, False, commit=True)
-            # register relation definition
-            self.rqlexecall(ss.rdef2rql(rschema, etype, attrschema.type),
-                            ask_confirm=confirm)
-        if auto:
-            # we have commit here to get relation types actually in the schema
-            self.commit()
-            added = []
-            for rschema in eschema.subject_relations():
-                # attribute relation have already been processed and
-                # 'owned_by'/'created_by' will be automatically added
-                if rschema.final or rschema.type in ('owned_by', 'created_by', 'is', 'is_instance_of'): 
-                    continue
-                rtypeadded = rschema.type in applschema
-                for targetschema in rschema.objects(etype):
-                    # ignore relations where the targeted type is not in the
-                    # current application schema
-                    targettype = targetschema.type
-                    if not targettype in applschema and targettype != etype:
-                        continue
-                    if not rtypeadded:
-                        # need to add the relation type and to commit to get it
-                        # actually in the schema
-                        added.append(rschema.type)
-                        self.cmd_add_relation_type(rschema.type, False, commit=True)
-                        rtypeadded = True
-                    # register relation definition
-                    # remember this two avoid adding twice non symetric relation
-                    # such as "Emailthread forked_from Emailthread"
-                    added.append((etype, rschema.type, targettype))
-                    self.rqlexecall(ss.rdef2rql(rschema, etype, targettype),
-                                    ask_confirm=confirm)
-            for rschema in eschema.object_relations():
-                rtypeadded = rschema.type in applschema or rschema.type in added
-                for targetschema in rschema.subjects(etype):
-                    # ignore relations where the targeted type is not in the
-                    # current application schema
-                    targettype = targetschema.type
-                    # don't check targettype != etype since in this case the
-                    # relation has already been added as a subject relation
-                    if not targettype in applschema:
-                        continue
-                    if not rtypeadded:
-                        # need to add the relation type and to commit to get it
-                        # actually in the schema
-                        self.cmd_add_relation_type(rschema.type, False, commit=True)
-                        rtypeadded = True
-                    elif (targettype, rschema.type, etype) in added:
-                        continue
-                    # register relation definition
-                    self.rqlexecall(ss.rdef2rql(rschema, targettype, etype),
-                                    ask_confirm=confirm)
-        if commit:
-            self.commit()
-                
-    def cmd_drop_entity_type(self, etype, commit=True):
-        """unregister an existing entity type
-        
-        This will trigger deletion of necessary relation types and definitions
-        """
-        # XXX what if we delete an entity type which is specialized by other types
-        # unregister the entity from EEType
-        self.rqlexec('DELETE EEType X WHERE X name %(etype)s', {'etype': etype},
-                     ask_confirm=self.verbosity>=2)
-        if commit:
-            self.commit()
+    # schema synchronization internals ########################################
 
-    def cmd_rename_entity_type(self, oldname, newname, commit=True):
-        """rename an existing entity type in the persistent schema
-        
-        `oldname` is a string giving the name of the existing entity type
-        `newname` is a string giving the name of the renamed entity type
-        """
-        self.rqlexec('SET ET name %(newname)s WHERE ET is EEType, ET name %(oldname)s',
-                     {'newname' : unicode(newname), 'oldname' : oldname})
-        if commit:
-            self.commit()
-        
-    def cmd_add_relation_type(self, rtype, addrdef=True, commit=True):
-        """register a new relation type named `rtype`, as described in the
-        schema description file.
-
-        `addrdef` is a boolean value; when True, it will also add all relations
-        of the type just added found in the schema definition file. Note that it
-        implies an intermediate "commit" which commits the relation type
-        creation (but not the relation definitions themselves, for which
-        committing depends on the `commit` argument value).
-        
-        """
-        rschema = self.fs_schema.rschema(rtype)
-        # register the relation into ERType and insert necessary relation
-        # definitions
-        self.rqlexecall(ss.rschema2rql(rschema, addrdef=False),
-                        ask_confirm=self.verbosity>=2)
-        # register groups / permissions for the relation
-        self.rqlexecall(ss.erperms2rql(rschema, self.group_mapping()),
-                        ask_confirm=self.verbosity>=2)
-        if addrdef:
-            self.commit()
-            self.rqlexecall(ss.rdef2rql(rschema),
-                            ask_confirm=self.verbosity>=2)
-        if commit:
-            self.commit()
-        
-    def cmd_drop_relation_type(self, rtype, commit=True):
-        """unregister an existing relation type"""
-        # unregister the relation from ERType
-        self.rqlexec('DELETE ERType X WHERE X name %r' % rtype,
-                     ask_confirm=self.verbosity>=2)
-        if commit:
-            self.commit()
-        
-    def cmd_rename_relation(self, oldname, newname, commit=True):
-        """rename an existing relation
-        
-        `oldname` is a string giving the name of the existing relation
-        `newname` is a string giving the name of the renamed relation
-        """
-        self.cmd_add_relation_type(newname, commit=True)
-        self.rqlexec('SET X %s Y WHERE X %s Y' % (newname, oldname),
-                     ask_confirm=self.verbosity>=2)
-        self.cmd_drop_relation_type(oldname, commit=commit)
-
-    def cmd_add_relation_definition(self, subjtype, rtype, objtype, commit=True):
-        """register a new relation definition, from its definition found in the
-        schema definition file
-        """
-        rschema = self.fs_schema.rschema(rtype)
-        if not rtype in self.repo.schema:
-            self.cmd_add_relation_type(rtype, addrdef=False, commit=True)
-        self.rqlexecall(ss.rdef2rql(rschema, subjtype, objtype),
-                        ask_confirm=self.verbosity>=2)
-        if commit:
-            self.commit()
-        
-    def cmd_drop_relation_definition(self, subjtype, rtype, objtype, commit=True):
-        """unregister an existing relation definition"""
-        rschema = self.repo.schema.rschema(rtype)
-        # unregister the definition from EFRDef or ENFRDef
-        if rschema.is_final():
-            etype = 'EFRDef'
-        else:
-            etype = 'ENFRDef'
-        rql = ('DELETE %s X WHERE X from_entity FE, FE name "%s",'
-               'X relation_type RT, RT name "%s", X to_entity TE, TE name "%s"')
-        self.rqlexec(rql % (etype, subjtype, rtype, objtype),
-                     ask_confirm=self.verbosity>=2)
-        if commit:
-            self.commit()
-        
-    def cmd_synchronize_permissions(self, ertype, commit=True):
+    def _synchronize_permissions(self, ertype):
         """permission synchronization for an entity or relation type"""
         if ertype in ('eid', 'has_text', 'identity'):
             return
@@ -677,20 +332,17 @@
                                  {'expr': expr, 'exprtype': exprtype,
                                   'vars': expression.mainvars, 'x': teid}, 'x',
                                  ask_confirm=False)
-        if commit:
-            self.commit()
-        
-    def cmd_synchronize_rschema(self, rtype, syncrdefs=True, syncperms=True,
-                                commit=True):
+
+    def _synchronize_rschema(self, rtype, syncrdefs=True, syncperms=True):
         """synchronize properties of the persistent relation schema against its
         current definition:
-        
+
         * description
         * symetric, meta
         * inlined
         * relation definitions if `syncrdefs`
         * permissions if `syncperms`
-        
+
         physical schema changes should be handled by repository's schema hooks
         """
         rtype = str(rtype)
@@ -705,17 +357,14 @@
             for subj, obj in rschema.iter_rdefs():
                 if not reporschema.has_rdef(subj, obj):
                     continue
-                self.cmd_synchronize_rdef_schema(subj, rschema, obj,
-                                                 commit=False)
+                self._synchronize_rdef_schema(subj, rschema, obj)
         if syncperms:
-            self.cmd_synchronize_permissions(rtype, commit=False)
-        if commit:
-            self.commit()
-                
-    def cmd_synchronize_eschema(self, etype, syncperms=True, commit=True):
+            self._synchronize_permissions(rtype)
+
+    def _synchronize_eschema(self, etype, syncperms=True):
         """synchronize properties of the persistent entity schema against
         its current definition:
-        
+
         * description
         * internationalizable, fulltextindexed, indexed, meta
         * relations from/to this entity
@@ -733,11 +382,11 @@
         repospschema = repoeschema.specializes()
         espschema = eschema.specializes()
         if repospschema and not espschema:
-            self.rqlexec('DELETE X specializes Y WHERE X is EEType, X name %(x)s',
+            self.rqlexec('DELETE X specializes Y WHERE X is CWEType, X name %(x)s',
                          {'x': str(repoeschema)})
         elif not repospschema and espschema:
-            self.rqlexec('SET X specializes Y WHERE X is EEType, X name %(x)s, '
-                         'Y is EEType, Y name %(y)s',
+            self.rqlexec('SET X specializes Y WHERE X is CWEType, X name %(x)s, '
+                         'Y is CWEType, Y name %(y)s',
                          {'x': str(repoeschema), 'y': str(espschema)})
         self.rqlexecall(ss.updateeschema2rql(eschema),
                         ask_confirm=self.verbosity >= 2)
@@ -750,22 +399,18 @@
                 if not rschema in repoeschema.object_relations():
                     continue
                 subjtypes, objtypes = targettypes, [etype]
-            self.cmd_synchronize_rschema(rschema, syncperms=syncperms,
-                                         syncrdefs=False, commit=False)
+            self._synchronize_rschema(rschema, syncperms=syncperms,
+                                      syncrdefs=False)
             reporschema = self.repo.schema.rschema(rschema)
             for subj in subjtypes:
                 for obj in objtypes:
                     if not reporschema.has_rdef(subj, obj):
                         continue
-                    self.cmd_synchronize_rdef_schema(subj, rschema, obj,
-                                                     commit=False)
+                    self._synchronize_rdef_schema(subj, rschema, obj)
         if syncperms:
-            self.cmd_synchronize_permissions(etype, commit=False)
-        if commit:
-            self.commit()
+            self._synchronize_permissions(etype)
 
-    def cmd_synchronize_rdef_schema(self, subjtype, rtype, objtype,
-                                    commit=True):
+    def _synchronize_rdef_schema(self, subjtype, rtype, objtype):
         """synchronize properties of the persistent relation definition schema
         against its current definition:
         * order and other properties
@@ -799,7 +444,7 @@
                 self.rqlexec('DELETE X constrained_by C WHERE C eid %(x)s',
                              {'x': cstr.eid}, 'x',
                              ask_confirm=confirm)
-                self.rqlexec('DELETE EConstraint C WHERE C eid %(x)s',
+                self.rqlexec('DELETE CWConstraint C WHERE C eid %(x)s',
                              {'x': cstr.eid}, 'x',
                              ask_confirm=confirm)
             else:
@@ -813,24 +458,349 @@
             self.rqlexecall(ss.constraint2rql(rschema, subjtype, objtype,
                                               newcstr),
                             ask_confirm=confirm)
+
+    # base actions ############################################################
+
+    def checkpoint(self):
+        """checkpoint action"""
+        if self.confirm('commit now ?', shell=False):
+            self.commit()
+
+    def cmd_add_cube(self, cube, update_database=True):
+        self.cmd_add_cubes( (cube,), update_database)
+
+    def cmd_add_cubes(self, cubes, update_database=True):
+        """update_database is telling if the database schema should be updated
+        or if only the relevant eproperty should be inserted (for the case where
+        a cube has been extracted from an existing application, so the
+        cube schema is already in there)
+        """
+        newcubes = super(ServerMigrationHelper, self).cmd_add_cubes(cubes)
+        if not newcubes:
+            return
+        for pack in newcubes:
+            self.cmd_set_property('system.version.'+pack,
+                                  self.config.cube_version(pack))
+        if not update_database:
+            self.commit()
+            return
+        newcubes_schema = self.config.load_schema(construction_mode='non-strict')
+        new = set()
+        # execute pre-create files
+        for pack in reversed(newcubes):
+            self.exec_event_script('precreate', self.config.cube_dir(pack))
+        # add new entity and relation types
+        for rschema in newcubes_schema.relations():
+            if not rschema in self.repo.schema:
+                self.cmd_add_relation_type(rschema.type)
+                new.add(rschema.type)
+        for eschema in newcubes_schema.entities():
+            if not eschema in self.repo.schema:
+                self.cmd_add_entity_type(eschema.type)
+                new.add(eschema.type)
+        # check if attributes has been added to existing entities
+        for rschema in newcubes_schema.relations():
+            existingschema = self.repo.schema.rschema(rschema.type)
+            for (fromtype, totype) in rschema.iter_rdefs():
+                if existingschema.has_rdef(fromtype, totype):
+                    continue
+                # check we should actually add the relation definition
+                if not (fromtype in new or totype in new or rschema in new):
+                    continue
+                self.cmd_add_relation_definition(str(fromtype), rschema.type,
+                                                 str(totype))
+        # execute post-create files
+        for pack in reversed(newcubes):
+            self.exec_event_script('postcreate', self.config.cube_dir(pack))
+            self.commit()
+
+    def cmd_remove_cube(self, cube):
+        removedcubes = super(ServerMigrationHelper, self).cmd_remove_cube(cube)
+        if not removedcubes:
+            return
+        fsschema = self.fs_schema
+        removedcubes_schema = self.config.load_schema(construction_mode='non-strict')
+        reposchema = self.repo.schema
+        # execute pre-remove files
+        for pack in reversed(removedcubes):
+            self.exec_event_script('preremove', self.config.cube_dir(pack))
+        # remove cubes'entity and relation types
+        for rschema in fsschema.relations():
+            if not rschema in removedcubes_schema and rschema in reposchema:
+                self.cmd_drop_relation_type(rschema.type)
+        for eschema in fsschema.entities():
+            if not eschema in removedcubes_schema and eschema in reposchema:
+                self.cmd_drop_entity_type(eschema.type)
+        for rschema in fsschema.relations():
+            if rschema in removedcubes_schema and rschema in reposchema:
+                # check if attributes/relations has been added to entities from
+                # other cubes
+                for fromtype, totype in rschema.iter_rdefs():
+                    if not removedcubes_schema[rschema.type].has_rdef(fromtype, totype) and \
+                           reposchema[rschema.type].has_rdef(fromtype, totype):
+                        self.cmd_drop_relation_definition(
+                            str(fromtype), rschema.type, str(totype))
+        # execute post-remove files
+        for pack in reversed(removedcubes):
+            self.exec_event_script('postremove', self.config.cube_dir(pack))
+            self.rqlexec('DELETE CWProperty X WHERE X pkey %(pk)s',
+                         {'pk': u'system.version.'+pack}, ask_confirm=False)
+            self.commit()
+
+    # schema migration actions ################################################
+
+    def cmd_add_attribute(self, etype, attrname, attrtype=None, commit=True):
+        """add a new attribute on the given entity type"""
+        if attrtype is None:
+            rschema = self.fs_schema.rschema(attrname)
+            attrtype = rschema.objects(etype)[0]
+        self.cmd_add_relation_definition(etype, attrname, attrtype, commit=commit)
+
+    def cmd_drop_attribute(self, etype, attrname, commit=True):
+        """drop an existing attribute from the given entity type
+
+        `attrname` is a string giving the name of the attribute to drop
+        """
+        rschema = self.repo.schema.rschema(attrname)
+        attrtype = rschema.objects(etype)[0]
+        self.cmd_drop_relation_definition(etype, attrname, attrtype, commit=commit)
+
+    def cmd_rename_attribute(self, etype, oldname, newname, commit=True):
+        """rename an existing attribute of the given entity type
+
+        `oldname` is a string giving the name of the existing attribute
+        `newname` is a string giving the name of the renamed attribute
+        """
+        eschema = self.fs_schema.eschema(etype)
+        attrtype = eschema.destination(newname)
+        # have to commit this first step anyway to get the definition
+        # actually in the schema
+        self.cmd_add_attribute(etype, newname, attrtype, commit=True)
+        # skipp NULL values if the attribute is required
+        rql = 'SET X %s VAL WHERE X is %s, X %s VAL' % (newname, etype, oldname)
+        card = eschema.rproperty(newname, 'cardinality')[0]
+        if card == '1':
+            rql += ', NOT X %s NULL' % oldname
+        self.rqlexec(rql, ask_confirm=self.verbosity>=2)
+        self.cmd_drop_attribute(etype, oldname, commit=commit)
+
+    def cmd_add_entity_type(self, etype, auto=True, commit=True):
+        """register a new entity type
+
+        in auto mode, automatically register entity's relation where the
+        targeted type is known
+        """
+        applschema = self.repo.schema
+        if etype in applschema:
+            eschema = applschema[etype]
+            if eschema.is_final():
+                applschema.del_entity_type(etype)
+        else:
+            eschema = self.fs_schema.eschema(etype)
+        confirm = self.verbosity >= 2
+        # register the entity into CWEType
+        self.rqlexecall(ss.eschema2rql(eschema), ask_confirm=confirm)
+        # add specializes relation if needed
+        self.rqlexecall(ss.eschemaspecialize2rql(eschema), ask_confirm=confirm)
+        # register groups / permissions for the entity
+        self.rqlexecall(ss.erperms2rql(eschema, self.group_mapping()),
+                        ask_confirm=confirm)
+        # register entity's attributes
+        for rschema, attrschema in eschema.attribute_definitions():
+            # ignore those meta relations, they will be automatically added
+            if rschema.type in ('eid', 'creation_date', 'modification_date'):
+                continue
+            if not rschema.type in applschema:
+                # need to add the relation type and to commit to get it
+                # actually in the schema
+                self.cmd_add_relation_type(rschema.type, False, commit=True)
+            # register relation definition
+            self.rqlexecall(ss.rdef2rql(rschema, etype, attrschema.type),
+                            ask_confirm=confirm)
+        if auto:
+            # we have commit here to get relation types actually in the schema
+            self.commit()
+            added = []
+            for rschema in eschema.subject_relations():
+                # attribute relation have already been processed and
+                # 'owned_by'/'created_by' will be automatically added
+                if rschema.final or rschema.type in ('owned_by', 'created_by', 'is', 'is_instance_of'):
+                    continue
+                rtypeadded = rschema.type in applschema
+                for targetschema in rschema.objects(etype):
+                    # ignore relations where the targeted type is not in the
+                    # current application schema
+                    targettype = targetschema.type
+                    if not targettype in applschema and targettype != etype:
+                        continue
+                    if not rtypeadded:
+                        # need to add the relation type and to commit to get it
+                        # actually in the schema
+                        added.append(rschema.type)
+                        self.cmd_add_relation_type(rschema.type, False, commit=True)
+                        rtypeadded = True
+                    # register relation definition
+                    # remember this two avoid adding twice non symetric relation
+                    # such as "Emailthread forked_from Emailthread"
+                    added.append((etype, rschema.type, targettype))
+                    self.rqlexecall(ss.rdef2rql(rschema, etype, targettype),
+                                    ask_confirm=confirm)
+            for rschema in eschema.object_relations():
+                rtypeadded = rschema.type in applschema or rschema.type in added
+                for targetschema in rschema.subjects(etype):
+                    # ignore relations where the targeted type is not in the
+                    # current application schema
+                    targettype = targetschema.type
+                    # don't check targettype != etype since in this case the
+                    # relation has already been added as a subject relation
+                    if not targettype in applschema:
+                        continue
+                    if not rtypeadded:
+                        # need to add the relation type and to commit to get it
+                        # actually in the schema
+                        self.cmd_add_relation_type(rschema.type, False, commit=True)
+                        rtypeadded = True
+                    elif (targettype, rschema.type, etype) in added:
+                        continue
+                    # register relation definition
+                    self.rqlexecall(ss.rdef2rql(rschema, targettype, etype),
+                                    ask_confirm=confirm)
         if commit:
             self.commit()
-        
-    def cmd_synchronize_schema(self, syncperms=True, commit=True):
+
+    def cmd_drop_entity_type(self, etype, commit=True):
+        """unregister an existing entity type
+
+        This will trigger deletion of necessary relation types and definitions
+        """
+        # XXX what if we delete an entity type which is specialized by other types
+        # unregister the entity from CWEType
+        self.rqlexec('DELETE CWEType X WHERE X name %(etype)s', {'etype': etype},
+                     ask_confirm=self.verbosity>=2)
+        if commit:
+            self.commit()
+
+    def cmd_rename_entity_type(self, oldname, newname, commit=True):
+        """rename an existing entity type in the persistent schema
+
+        `oldname` is a string giving the name of the existing entity type
+        `newname` is a string giving the name of the renamed entity type
+        """
+        self.rqlexec('SET ET name %(newname)s WHERE ET is CWEType, ET name %(oldname)s',
+                     {'newname' : unicode(newname), 'oldname' : oldname})
+        if commit:
+            self.commit()
+
+    def cmd_add_relation_type(self, rtype, addrdef=True, commit=True):
+        """register a new relation type named `rtype`, as described in the
+        schema description file.
+
+        `addrdef` is a boolean value; when True, it will also add all relations
+        of the type just added found in the schema definition file. Note that it
+        implies an intermediate "commit" which commits the relation type
+        creation (but not the relation definitions themselves, for which
+        committing depends on the `commit` argument value).
+
+        """
+        rschema = self.fs_schema.rschema(rtype)
+        # register the relation into CWRType and insert necessary relation
+        # definitions
+        self.rqlexecall(ss.rschema2rql(rschema, addrdef=False),
+                        ask_confirm=self.verbosity>=2)
+        # register groups / permissions for the relation
+        self.rqlexecall(ss.erperms2rql(rschema, self.group_mapping()),
+                        ask_confirm=self.verbosity>=2)
+        if addrdef:
+            self.commit()
+            self.rqlexecall(ss.rdef2rql(rschema),
+                            ask_confirm=self.verbosity>=2)
+        if commit:
+            self.commit()
+
+    def cmd_drop_relation_type(self, rtype, commit=True):
+        """unregister an existing relation type"""
+        # unregister the relation from CWRType
+        self.rqlexec('DELETE CWRType X WHERE X name %r' % rtype,
+                     ask_confirm=self.verbosity>=2)
+        if commit:
+            self.commit()
+
+    def cmd_rename_relation(self, oldname, newname, commit=True):
+        """rename an existing relation
+
+        `oldname` is a string giving the name of the existing relation
+        `newname` is a string giving the name of the renamed relation
+        """
+        self.cmd_add_relation_type(newname, commit=True)
+        self.rqlexec('SET X %s Y WHERE X %s Y' % (newname, oldname),
+                     ask_confirm=self.verbosity>=2)
+        self.cmd_drop_relation_type(oldname, commit=commit)
+
+    def cmd_add_relation_definition(self, subjtype, rtype, objtype, commit=True):
+        """register a new relation definition, from its definition found in the
+        schema definition file
+        """
+        rschema = self.fs_schema.rschema(rtype)
+        if not rtype in self.repo.schema:
+            self.cmd_add_relation_type(rtype, addrdef=False, commit=True)
+        self.rqlexecall(ss.rdef2rql(rschema, subjtype, objtype),
+                        ask_confirm=self.verbosity>=2)
+        if commit:
+            self.commit()
+
+    def cmd_drop_relation_definition(self, subjtype, rtype, objtype, commit=True):
+        """unregister an existing relation definition"""
+        rschema = self.repo.schema.rschema(rtype)
+        # unregister the definition from CWAttribute or CWRelation
+        if rschema.is_final():
+            etype = 'CWAttribute'
+        else:
+            etype = 'CWRelation'
+        rql = ('DELETE %s X WHERE X from_entity FE, FE name "%s",'
+               'X relation_type RT, RT name "%s", X to_entity TE, TE name "%s"')
+        self.rqlexec(rql % (etype, subjtype, rtype, objtype),
+                     ask_confirm=self.verbosity>=2)
+        if commit:
+            self.commit()
+
+    def cmd_sync_schema_props_perms(self, ertype=None, syncperms=True,
+                                    syncprops=True, syncrdefs=True, commit=True):
         """synchronize the persistent schema against the current definition
         schema.
-        
+
         It will synch common stuff between the definition schema and the
         actual persistent schema, it won't add/remove any entity or relation.
         """
-        for etype in self.repo.schema.entities():
-            self.cmd_synchronize_eschema(etype, syncperms=syncperms, commit=False)
+        assert syncperms or syncprops, 'nothing to do'
+        if ertype is not None:
+            if isinstance(ertype, (tuple, list)):
+                assert len(ertype) == 3, 'not a relation definition'
+                assert syncprops, 'can\'t update permission for a relation definition'
+                self._synchronize_rdef_schema(*ertype)
+            elif syncprops:
+                erschema = self.repo.schema[ertype]
+                if isinstance(erschema, CubicWebRelationSchema):
+                    self._synchronize_rschema(erschema, syncperms=syncperms,
+                                              syncrdefs=syncrdefs)
+                else:
+                    self._synchronize_eschema(erschema, syncperms=syncperms)
+            else:
+                self._synchronize_permissions(ertype)
+        else:
+            for etype in self.repo.schema.entities():
+                if syncprops:
+                    self._synchronize_eschema(etype, syncperms=syncperms)
+                else:
+                    self._synchronize_permissions(etype)
         if commit:
             self.commit()
-                
+
     def cmd_change_relation_props(self, subjtype, rtype, objtype,
                                   commit=True, **kwargs):
-        """change some properties of a relation definition"""
+        """change some properties of a relation definition
+
+        you usually want to use sync_schema_props_perms instead.
+        """
         assert kwargs
         restriction = []
         if subjtype and subjtype != 'Any':
@@ -853,7 +823,9 @@
     def cmd_set_size_constraint(self, etype, rtype, size, commit=True):
         """set change size constraint of a string attribute
 
-        if size is None any size constraint will be removed
+        if size is None any size constraint will be removed.
+
+        you usually want to use sync_schema_props_perms instead.
         """
         oldvalue = None
         for constr in self.repo.schema.eschema(etype).constraints(rtype):
@@ -862,7 +834,7 @@
         if oldvalue == size:
             return
         if oldvalue is None and not size is None:
-            ceid = self.rqlexec('INSERT EConstraint C: C value %(v)s, C cstrtype CT '
+            ceid = self.rqlexec('INSERT CWConstraint C: C value %(v)s, C cstrtype CT '
                                 'WHERE CT name "SizeConstraint"',
                                 {'v': SizeConstraint(size).serialize()},
                                 ask_confirm=self.verbosity>=2)[0][0]
@@ -882,12 +854,16 @@
                              'S name "%s", R name "%s"' % (etype, rtype),
                              ask_confirm=self.verbosity>=2)
                 # cleanup unused constraints
-                self.rqlexec('DELETE EConstraint C WHERE NOT X constrained_by C')
+                self.rqlexec('DELETE CWConstraint C WHERE NOT X constrained_by C')
         if commit:
             self.commit()
-    
+
+    @obsolete('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)
+
     # Workflows handling ######################################################
-    
+
     def cmd_add_state(self, name, stateof, initial=False, commit=False, **kwargs):
         """method to ease workflow definition: add a state for one or more
         entity type(s)
@@ -905,7 +881,7 @@
         if commit:
             self.commit()
         return stateeid
-    
+
     def cmd_add_transition(self, name, transitionof, fromstates, tostate,
                            requiredgroups=(), conditions=(), commit=False, **kwargs):
         """method to ease workflow definition: add a transition for one or more
@@ -962,27 +938,27 @@
         entity.change_state(entity.wf_state(statename).eid)
         if commit:
             self.commit()
-        
-    # EProperty handling ######################################################
+
+    # CWProperty handling ######################################################
 
     def cmd_property_value(self, pkey):
-        rql = 'Any V WHERE X is EProperty, X pkey %(k)s, X value V'
+        rql = 'Any V WHERE X is CWProperty, X pkey %(k)s, X value V'
         rset = self.rqlexec(rql, {'k': pkey}, ask_confirm=False)
         return rset[0][0]
 
     def cmd_set_property(self, pkey, value):
         value = unicode(value)
         try:
-            prop = self.rqlexec('EProperty X WHERE X pkey %(k)s', {'k': pkey},
+            prop = self.rqlexec('CWProperty X WHERE X pkey %(k)s', {'k': pkey},
                                 ask_confirm=False).get_entity(0, 0)
         except:
-            self.cmd_add_entity('EProperty', pkey=unicode(pkey), value=value)
+            self.cmd_add_entity('CWProperty', pkey=unicode(pkey), value=value)
         else:
             self.rqlexec('SET X value %(v)s WHERE X pkey %(k)s',
                          {'k': pkey, 'v': value}, ask_confirm=False)
 
     # other data migration commands ###########################################
-        
+
     def cmd_add_entity(self, etype, *args, **kwargs):
         """add a new entity of the given type"""
         rql = 'INSERT %s X' % etype
@@ -1002,10 +978,10 @@
         if commit:
             self.commit()
         return eid
-    
+
     def sqlexec(self, sql, args=None, ask_confirm=True):
         """execute the given sql if confirmed
-        
+
         should only be used for low level stuff undoable with existing higher
         level actions
         """
@@ -1023,7 +999,7 @@
             except:
                 # no result to fetch
                 return
-    
+
     def rqlexec(self, rql, kwargs=None, cachekey=None, ask_confirm=True):
         """rql action"""
         if not isinstance(rql, (tuple, list)):
@@ -1050,7 +1026,7 @@
 
     def cmd_reactivate_verification_hooks(self):
         self.repo.hm.reactivate_verification_hooks()
-        
+
     # broken db commands ######################################################
 
     def cmd_change_attribute_type(self, etype, attr, newtype, commit=True):
@@ -1063,8 +1039,8 @@
         rschema = self.repo.schema.rschema(attr)
         oldtype = rschema.objects(etype)[0]
         rdefeid = rschema.rproperty(etype, oldtype, 'eid')
-        sql = ("UPDATE EFRDef "
-               "SET to_entity=(SELECT eid FROM EEType WHERE name='%s')"
+        sql = ("UPDATE CWAttribute "
+               "SET to_entity=(SELECT eid FROM CWEType WHERE name='%s')"
                "WHERE eid=%s") % (newtype, rdefeid)
         self.sqlexec(sql, ask_confirm=False)
         dbhelper = self.repo.system_source.dbhelper
@@ -1073,7 +1049,7 @@
         self.sqlexec(sql, ask_confirm=False)
         if commit:
             self.commit()
-        
+
     def cmd_add_entity_type_table(self, etype, commit=True):
         """low level method to create the sql table for an existing entity.
         This may be useful on accidental desync between the repository schema
@@ -1087,7 +1063,7 @@
                 self.sqlexec(sql)
         if commit:
             self.commit()
-            
+
     def cmd_add_relation_type_table(self, rtype, commit=True):
         """low level method to create the sql table for an existing relation.
         This may be useful on accidental desync between the repository schema
@@ -1100,7 +1076,7 @@
                 self.sqlexec(sql)
         if commit:
             self.commit()
-            
+
 
 class ForRqlIterator:
     """specific rql iterator to make the loop skipable"""
@@ -1110,10 +1086,10 @@
         self.kwargs = kwargs
         self.ask_confirm = ask_confirm
         self._rsetit = None
-        
+
     def __iter__(self):
         return self
-    
+
     def next(self):
         if self._rsetit is not None:
             return self._rsetit.next()
@@ -1126,7 +1102,6 @@
             if not self._h.confirm('execute rql: %s ?' % msg):
                 raise StopIteration
         try:
-            #print rql, kwargs
             rset = self._h.rqlcursor.execute(rql, kwargs)
         except Exception, ex:
             if self._h.confirm('error: %s\nabort?' % ex):
--- a/server/msplanner.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/msplanner.py	Mon May 04 13:09:48 2009 +0200
@@ -33,25 +33,25 @@
 
 Exemples of multi-sources query execution
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-For a system source and a ldap user source (only EUser and its attributes
+For a system source and a ldap user source (only CWUser and its attributes
 is supported, no group or such):
 
-:EUser X:
-1. fetch EUser X from both sources and return concatenation of results
+:CWUser X:
+1. fetch CWUser X from both sources and return concatenation of results
 
-:EUser X WHERE X in_group G, G name 'users':
+:CWUser X WHERE X in_group G, G name 'users':
 * catch 1
-  1. fetch EUser X from both sources, store concatenation of results into a
+  1. fetch CWUser X from both sources, store concatenation of results into a
      temporary table
   2. return the result of TMP X WHERE X in_group G, G name 'users' from the
      system source
 * catch 2
-  1. return the result of EUser X WHERE X in_group G, G name 'users' from system
+  1. return the result of CWUser X WHERE X in_group G, G name 'users' from system
      source, that's enough (optimization of the sql querier will avoid join on
-     EUser, so we will directly get local eids)
+     CWUser, so we will directly get local eids)
     
-:EUser X,L WHERE X in_group G, X login L, G name 'users':
-1. fetch Any X,L WHERE X is EUser, X login L from both sources, store
+:CWUser X,L WHERE X in_group G, X login L, G name 'users':
+1. fetch Any X,L WHERE X is CWUser, X login L from both sources, store
    concatenation of results into a temporary table
 2. return the result of Any X, L WHERE X is TMP, X login LX in_group G,
    G name 'users' from the system source
@@ -59,13 +59,13 @@
 
 :Any X WHERE X owned_by Y:
 * catch 1
-  1. fetch EUser X from both sources, store concatenation of results into a
+  1. fetch CWUser X from both sources, store concatenation of results into a
      temporary table
   2. return the result of Any X WHERE X owned_by Y, Y is TMP from the system
      source
 * catch 2
   1. return the result of Any X WHERE X owned_by Y from system source, that's
-     enough (optimization of the sql querier will avoid join on EUser, so we
+     enough (optimization of the sql querier will avoid join on CWUser, so we
      will directly get local eids)
 
 
@@ -75,17 +75,16 @@
 """
 __docformat__ = "restructuredtext en"
 
-from copy import deepcopy
 from itertools import imap, ifilterfalse
 
 from logilab.common.compat import any
 from logilab.common.decorators import cached
 
 from rql.stmts import Union, Select
-from rql.nodes import VariableRef, Comparison, Relation, Constant, Exists, Variable
+from rql.nodes import VariableRef, Comparison, Relation, Constant, Variable
 
 from cubicweb import server
-from cubicweb.common.utils import make_uid
+from cubicweb.utils import make_uid
 from cubicweb.server.utils import cleanup_solutions
 from cubicweb.server.ssplanner import (SSPlanner, OneFetchStep,
                                        add_types_restriction)
@@ -1389,7 +1388,7 @@
             return False
         if not same_scope(var):
             return False
-        if any(v for v,_ in var.stinfo['attrvars'] if not v.name in terms):
+        if any(v for v, _ in var.stinfo['attrvars'] if not v in terms):
             return False
         return True
         
--- a/server/pool.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/pool.py	Mon May 04 13:09:48 2009 +0200
@@ -11,7 +11,7 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
--- a/server/querier.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/querier.py	Mon May 04 13:09:48 2009 +0200
@@ -13,8 +13,7 @@
 from logilab.common.compat import any
 from rql import RQLHelper, RQLSyntaxError
 from rql.stmts import Union, Select
-from rql.nodes import (Relation, VariableRef, Constant, Exists, Variable,
-                       SubQuery)
+from rql.nodes import (Relation, VariableRef, Constant, SubQuery)
 
 from cubicweb import Unauthorized, QueryError, UnknownEid, typed_eid
 from cubicweb import server
--- a/server/repository.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/repository.py	Mon May 04 13:09:48 2009 +0200
@@ -19,10 +19,9 @@
 import sys
 import Queue
 from os.path import join, exists
+from datetime import datetime
 from time import time, localtime, strftime
 
-from mx.DateTime import now
-
 from logilab.common.decorators import cached
 
 from yams import BadSchemaDefinition
@@ -61,7 +60,7 @@
         remove inserted eid from repository type/source cache
         """
         self.repo.clear_caches(self.session.query_data('pendingeids', ()))
-        
+
     def rollback_event(self):
         """the observed connections pool has been rollbacked,
         remove inserted eid from repository type/source cache
@@ -85,7 +84,7 @@
         session.repo.system_source.fti_unindex_entity(session, entity.eid)
         for container in entity.fti_containers():
             session.repo.index_entity(session, container)
-            
+
     def commit_event(self):
         pass
 
@@ -113,20 +112,22 @@
     if card[0] in '1?':
         rschema = session.repo.schema.rschema(rtype)
         if not rschema.inlined:
-            session.unsafe_execute('DELETE X %s Y WHERE X eid %%(x)s, NOT Y eid %%(y)s' % rtype,
-                                   {'x': eidfrom, 'y': eidto}, 'x')
+            session.unsafe_execute(
+                'DELETE X %s Y WHERE X eid %%(x)s, NOT Y eid %%(y)s' % rtype,
+                {'x': eidfrom, 'y': eidto}, 'x')
     if card[1] in '1?':
-        session.unsafe_execute('DELETE X %s Y WHERE NOT X eid %%(x)s, Y eid %%(y)s' % rtype,
-                               {'x': eidfrom, 'y': eidto}, 'y')
+        session.unsafe_execute(
+            'DELETE X %s Y WHERE NOT X eid %%(x)s, Y eid %%(y)s' % rtype,
+            {'x': eidfrom, 'y': eidto}, 'y')
 
-    
+
 class Repository(object):
     """a repository provides access to a set of persistent storages for
     entities and relations
 
     XXX protect pyro access
     """
-    
+
     def __init__(self, config, vreg=None, debug=False):
         self.config = config
         if vreg is None:
@@ -154,7 +155,7 @@
         for uri, source_config in config.sources().items():
             if uri == 'admin':
                 # not an actual source
-                continue 
+                continue
             source = self.get_source(uri, source_config)
             self.sources_by_uri[uri] = source
             self.sources.append(source)
@@ -179,18 +180,21 @@
             self.warning("set fs application'schema as bootstrap schema")
             config.bootstrap_cubes()
             self.set_bootstrap_schema(self.config.load_schema())
-            # need to load the Any and EUser entity types
+            # need to load the Any and CWUser entity types
             self.vreg.schema = self.schema
             etdirectory = join(CW_SOFTWARE_ROOT, 'entities')
-            self.vreg.load_file(etdirectory, '__init__.py')
-            self.vreg.load_file(etdirectory, 'authobjs.py')
+            self.vreg.init_registration([etdirectory])
+            self.vreg.load_file(join(etdirectory, '__init__.py'),
+                                'cubicweb.entities.__init__')
+            self.vreg.load_file(join(etdirectory, 'authobjs.py'),
+                                'cubicweb.entities.authobjs')
         else:
             # test start: use the file system schema (quicker)
             self.warning("set fs application'schema")
             config.bootstrap_cubes()
             self.set_schema(self.config.load_schema())
         if not config.creating:
-            if 'EProperty' in self.schema:
+            if 'CWProperty' in self.schema:
                 self.vreg.init_properties(self.properties())
             # call source's init method to complete their initialisation if
             # needed (for instance looking for persistent configuration using an
@@ -210,16 +214,16 @@
                 source.init_creating()
         # close initialization pool and reopen fresh ones for proper
         # initialization now that we know cubes
-        self._get_pool().close(True) 
+        self._get_pool().close(True)
         for i in xrange(config['connections-pool-size']):
             self._available_pools.put_nowait(ConnectionsPool(self.sources))
-        
+
     # internals ###############################################################
 
     def get_source(self, uri, source_config):
         source_config['uri'] = uri
         return get_source(source_config, self.schema, self)
-        
+
     def set_schema(self, schema, resetvreg=True):
         schema.rebuild_infered_relations()
         self.info('set schema %s %#x', schema.name, id(schema))
@@ -253,11 +257,13 @@
             except BadSchemaDefinition:
                 raise
             except Exception, ex:
-                raise Exception('Is the database initialised ? (cause: %s)' % 
+                import traceback
+                traceback.print_exc()
+                raise Exception('Is the database initialised ? (cause: %s)' %
                                 (ex.args and ex.args[0].strip() or 'unknown')), \
                                 None, sys.exc_info()[-1]
             self.info('set the actual schema')
-            # XXX have to do this since EProperty isn't in the bootstrap schema
+            # XXX have to do this since CWProperty isn't in the bootstrap schema
             #     it'll be redone in set_schema
             self.set_bootstrap_schema(appschema)
             # 2.49 migration
@@ -265,13 +271,13 @@
                 session.set_pool()
                 if not 'template' in file(join(self.config.apphome, 'vc.conf')).read():
                     # remaning from cubicweb < 2.38...
-                    session.execute('DELETE EProperty X WHERE X pkey "system.version.template"')
+                    session.execute('DELETE CWProperty X WHERE X pkey "system.version.template"')
                     session.commit()
         finally:
             session.close()
         self.config.init_cubes(self.get_cubes())
         self.set_schema(appschema)
-        
+
     def set_bootstrap_schema(self, schema):
         """disable hooks when setting a bootstrap schema, but restore
         the configuration for the next time
@@ -289,7 +295,7 @@
         config.schema_hooks = True
         config.notification_hooks = True
         config.application_hooks = True
-            
+
     def start_looping_tasks(self):
         assert isinstance(self._looping_tasks, list), 'already started'
         for i, (interval, func) in enumerate(self._looping_tasks):
@@ -302,7 +308,7 @@
 
     def looping_task(self, interval, func):
         """register a function to be called every `interval` seconds.
-        
+
         looping tasks can only be registered during repository initialization,
         once done this method will fail.
         """
@@ -315,7 +321,7 @@
         """start function in a separated thread"""
         t = RepoThread(func, self._running_threads)
         t.start()
-        
+
     #@locked
     def _get_pool(self):
         try:
@@ -326,7 +332,7 @@
                             'connections) or to much load on the server (in '
                             'which case you can try to set a bigger '
                             'connections pools size)')
-        
+
     def _free_pool(self, pool):
         pool.rollback()
         self._available_pools.put_nowait(pool)
@@ -376,13 +382,13 @@
                       ((hits + misses) * 100) / (hits + misses + nocache))
         except ZeroDivisionError:
             pass
-        
+
     def authenticate_user(self, session, login, password):
         """validate login / password, raise AuthenticationError on failure
-        return associated EUser instance on success
+        return associated CWUser instance on success
         """
         for source in self.sources:
-            if source.support_entity('EUser'):
+            if source.support_entity('CWUser'):
                 try:
                     eid = source.authenticate(session, login, password)
                     break
@@ -397,19 +403,21 @@
         return euser
 
     def _build_user(self, session, eid):
-        cls = self.vreg.etype_class('EUser')
+        """return a CWUser entity for user with the given eid"""
+        cls = self.vreg.etype_class('CWUser')
         rql = cls.fetch_rql(session.user, ['X eid %(x)s'])
         rset = session.execute(rql, {'x': eid}, 'x')
         assert len(rset) == 1, rset
         euser = rset.get_entity(0, 0)
+        # pylint: disable-msg=W0104
         # prefetch / cache euser's groups and properties. This is especially
         # useful for internal sessions to avoid security insertions
         euser.groups
         euser.properties
         return euser
-        
+
     # public (dbapi) interface ################################################
-            
+
     def get_schema(self):
         """return the application schema. This is a public method, not
         requiring a session id
@@ -441,7 +449,7 @@
         session = self.internal_session()
         try:
             for pk, version in session.execute(
-                'Any K,V WHERE P is EProperty, P value V, P pkey K, '
+                'Any K,V WHERE P is CWProperty, P value V, P pkey K, '
                 'P pkey ~="system.version.%"', build_descr=False):
                 cube = pk.split('.')[-1]
                 # XXX cubicweb migration
@@ -461,7 +469,7 @@
         finally:
             session.close()
         return vcconf
-    
+
     @cached
     def source_defs(self):
         sources = self.config.sources().copy()
@@ -478,23 +486,27 @@
         """return a result set containing system wide properties"""
         session = self.internal_session()
         try:
-            return session.execute('Any K,V WHERE P is EProperty,'
+            return session.execute('Any K,V WHERE P is CWProperty,'
                                    'P pkey K, P value V, NOT P for_user U',
                                    build_descr=False)
         finally:
             session.close()
 
-    def register_user(self, login, password, **kwargs):
+    def register_user(self, login, password, email=None, **kwargs):
         """check a user with the given login exists, if not create it with the
         given password. This method is designed to be used for anonymous
         registration on public web site.
         """
         session = self.internal_session()
+        # for consistency, keep same error as unique check hook (although not required)
+        errmsg = session._('the value "%s" is already used, use another one')
         try:
-            if session.execute('EUser X WHERE X login %(login)s', {'login': login}):
-                return False
+            if (session.execute('CWUser X WHERE X login %(login)s', {'login': login})
+                or session.execute('CWUser X WHERE X use_email C, C address %(login)s',
+                                   {'login': login})):
+                raise ValidationError(None, {'login': errmsg % login})
             # we have to create the user
-            user = self.vreg.etype_class('EUser')(session, None)
+            user = self.vreg.etype_class('CWUser')(session, None)
             if isinstance(password, unicode):
                 # password should *always* be utf8 encoded
                 password = password.encode('UTF8')
@@ -504,17 +516,23 @@
             self.glob_add_entity(session, user)
             session.execute('SET X in_group G WHERE X eid %(x)s, G name "users"',
                             {'x': user.eid})
+            if email or '@' in login:
+                d = {'login': login, 'email': email or login}
+                if session.execute('EmailAddress X WHERE X address %(email)s', d):
+                    raise ValidationError(None, {'address': errmsg % d['email']})
+                session.execute('INSERT EmailAddress X: X address %(email)s, '
+                                'U primary_email X, U use_email X WHERE U login %(login)s', d)
             session.commit()
         finally:
             session.close()
         return True
-        
+
     def connect(self, login, password, cnxprops=None):
         """open a connection for a given user
 
         base_url may be needed to send mails
         cnxtype indicate if this is a pyro connection or a in-memory connection
-        
+
         raise `AuthenticationError` if the authentication failed
         raise `ConnectionError` if we can't open a connection
         """
@@ -566,7 +584,7 @@
                 raise
         finally:
             session.reset_pool()
-    
+
     def describe(self, sessionid, eid):
         """return a tuple (type, source, extid) for the entity with id <eid>"""
         session = self._get_session(sessionid, setpool=True)
@@ -600,12 +618,12 @@
         self.debug('begin commit for session %s', sessionid)
         try:
             self._get_session(sessionid, setpool=True).commit()
-        except (ValidationError, Unauthorized): 
+        except (ValidationError, Unauthorized):
             raise
         except:
             self.exception('unexpected error')
             raise
-        
+
     def rollback(self, sessionid):
         """commit transaction for the session with the given id"""
         self.debug('begin rollback for session %s', sessionid)
@@ -627,7 +645,7 @@
         session.close()
         del self._sessions[sessionid]
         self.info('closed session %s for user %s', sessionid, session.user.login)
-    
+
     def user_info(self, sessionid, props=None):
         """this method should be used by client to:
         * check session id validity
@@ -641,9 +659,9 @@
                 session.change_property(prop, value)
         user = session.user
         return user.eid, user.login, user.groups, user.properties
-            
+
     # public (inter-repository) interface #####################################
-    
+
     def entities_modified_since(self, etypes, mtime):
         """function designed to be called from an external repository which
         is using this one as a rql source for synchronization, and return a
@@ -656,7 +674,7 @@
           deleted since the given timestamp
         """
         session = self.internal_session()
-        updatetime = now()
+        updatetime = datetime.now()
         try:
             modentities, delentities = self.system_source.modified_entities(
                 session, etypes, mtime)
@@ -665,7 +683,7 @@
             session.close()
 
     # session handling ########################################################
-        
+
     def close_sessions(self):
         """close every opened sessions"""
         for sessionid in self._sessions.keys():
@@ -687,7 +705,7 @@
                 self.close(session.id)
                 nbclosed += 1
         return nbclosed
-    
+
     def internal_session(self, cnxprops=None):
         """return a dbapi like connection/cursor using internal user which
         have every rights on the repository. You'll *have to* commit/rollback
@@ -698,7 +716,7 @@
         session = InternalSession(self, cnxprops)
         session.set_pool()
         return session
-            
+
     def _get_session(self, sessionid, setpool=False):
         """return the user associated to the given session identifier"""
         try:
@@ -713,7 +731,7 @@
     # * correspondance between eid and (type, source)
     # * correspondance between eid and local id (i.e. specific to a given source)
     # * searchable text indexes
-    
+
     def type_and_source_from_eid(self, eid, session=None):
         """return a tuple (type, source, extid) for the entity with id <eid>"""
         try:
@@ -753,15 +771,15 @@
             rqlcache.pop('Any X WHERE X eid %s' % eid, None)
             for source in self.sources:
                 source.clear_eid_cache(eid, etype)
-                
+
     def type_from_eid(self, eid, session=None):
         """return the type of the entity with id <eid>"""
         return self.type_and_source_from_eid(eid, session)[0]
-    
+
     def source_from_eid(self, eid, session=None):
         """return the source for the given entity's eid"""
         return self.sources_by_uri[self.type_and_source_from_eid(eid, session)[1]]
-        
+
     def eid2extid(self, source, eid, session=None):
         """get local id from an eid"""
         etype, uri, extid = self.type_and_source_from_eid(eid, session)
@@ -830,7 +848,7 @@
         except:
             session.rollback(reset_pool)
             raise
-        
+
     def add_info(self, session, entity, source, extid=None, complete=True):
         """add type and source info for an eid into the system table,
         and index the entity with the full text index
@@ -844,11 +862,11 @@
         if self.do_fti:
             FTIndexEntityOp(session, entity=entity)
         CleanupEidTypeCacheOp(session)
-        
+
     def delete_info(self, session, eid):
         self._prepare_delete_info(session, eid)
         self._delete_info(session, eid)
-        
+
     def _prepare_delete_info(self, session, eid):
         """prepare the repository for deletion of an entity:
         * update the fti
@@ -859,7 +877,7 @@
         pending = session.query_data('pendingeids', set(), setdefault=True)
         pending.add(eid)
         CleanupEidTypeCacheOp(session)
-        
+
     def _delete_info(self, session, eid):
         """delete system information on deletion of an entity:
         * delete all relations on this entity
@@ -868,7 +886,7 @@
         etype, uri, extid = self.type_and_source_from_eid(eid, session)
         self._clear_eid_relations(session, etype, eid)
         self.system_source.delete_info(session, eid, etype, uri, extid)
-        
+
     def _clear_eid_relations(self, session, etype, eid):
         """when a entity is deleted, build and execute rql query to delete all
         its relations
@@ -899,7 +917,7 @@
             return
         alreadydone.add(entity.eid)
         self.system_source.fti_index_entity(session, entity)
-        
+
     def locate_relation_source(self, session, subject, rtype, object):
         subjsource = self.source_from_eid(subject, session)
         objsource = self.source_from_eid(object, session)
@@ -910,17 +928,17 @@
         else:
             source = subjsource
         return source
-    
+
     def locate_etype_source(self, etype):
         for source in self.sources:
             if source.support_entity(etype, 1):
                 return source
         else:
             raise ETypeNotSupportedBySources(etype)
-        
+
     def glob_add_entity(self, session, entity):
         """add an entity to the repository
-        
+
         the entity eid should originaly be None and a unique eid is assigned to
         the entity instance
         """
@@ -963,7 +981,7 @@
                 self.hm.call_hooks('after_add_relation', attr, session,
                                     entity.eid, attr, value)
         return entity.eid
-        
+
     def glob_update_entity(self, session, entity):
         """replace an entity in the repository
         the type and the eid of an entity must not be changed
@@ -1033,7 +1051,7 @@
         if source.should_call_hooks:
             self.hm.call_hooks('after_delete_entity', etype, session, eid)
         # don't clear cache here this is done in a hook on commit
-        
+
     def glob_add_relation(self, session, subject, rtype, object):
         """add a relation to the repository"""
         assert subject is not None
@@ -1071,7 +1089,7 @@
 
 
     # pyro handling ###########################################################
-    
+
     def pyro_register(self, host=''):
         """register the repository as a pyro object"""
         from Pyro import core
@@ -1090,7 +1108,7 @@
         self.info(msg, nsgroup, nsid)
         self.pyro_registered = True
         return daemon
-    
+
     def pyro_nameserver(self, host=None, group=None):
         """locate and bind the the name server to the daemon"""
         from Pyro import naming, errors
@@ -1105,25 +1123,25 @@
         return nameserver
 
     # multi-sources planner helpers ###########################################
-    
+
     @cached
     def rel_type_sources(self, rtype):
         return [source for source in self.sources
                 if source.support_relation(rtype)
                 or rtype in source.dont_cross_relations]
-    
+
     @cached
     def can_cross_relation(self, rtype):
         return [source for source in self.sources
                 if source.support_relation(rtype)
                 and rtype in source.cross_relations]
-    
+
     @cached
     def is_multi_sources_relation(self, rtype):
         return any(source for source in self.sources
                    if not source is self.system_source
                    and source.support_relation(rtype))
-    
+
 
 def pyro_unregister(config):
     """unregister the repository from the pyro name server"""
--- a/server/rqlannotation.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/rqlannotation.py	Mon May 04 13:09:48 2009 +0200
@@ -2,18 +2,16 @@
 code generation.
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 from logilab.common.compat import any
 
-from rql.nodes import Relation, Exists, VariableRef, Constant, Variable, Or
+from rql.nodes import Relation, VariableRef, Constant, Variable, Or
 from rql.utils import common_parent
 
-from cubicweb import server
-
 def _annotate_select(annotator, rqlst):
     for subquery in rqlst.with_:
         annotator._annotate_union(subquery.query)
--- a/server/rqlrewrite.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/rqlrewrite.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """RQL rewriting utilities, used for read security checking
 
 :organization: Logilab
-:copyright: 2007-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
@@ -25,7 +25,7 @@
                         except KeyError:
                             pass
                         break
-                except KeyError,ex:
+                except KeyError:
                     # variable has been rewritten
                     continue
             else:
@@ -315,7 +315,7 @@
         return cmp_
 
     def visit_mathexpression(self, mexpr):
-        cmp_ = nodes.MathExpression(cmp.operator)
+        cmp_ = nodes.MathExpression(mexpr.operator)
         for c in cmp.children:
             cmp_.append(c.accept(self))
         return cmp_
@@ -357,7 +357,7 @@
         else: # target == 'subject':
             cardindex = 1
             ttypes_func = rschema.subjects
-            rprop = lambda x,y,z: rschema.rproperty(y, x, z)
+            rprop = lambda x, y, z: rschema.rproperty(y, x, z)
         for etype in self.varstinfo['possibletypes']:
             for ttype in ttypes_func(etype):
                 if rprop(etype, ttype, 'cardinality')[cardindex] in '+*':
--- a/server/schemahooks.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/schemahooks.py	Mon May 04 13:09:48 2009 +0200
@@ -23,8 +23,8 @@
                                      check_internal_entity)
     
 # core entity and relation types which can't be removed
-CORE_ETYPES = list(BASE_TYPES) + ['EEType', 'ERType', 'EUser', 'EGroup',
-                                  'EConstraint', 'EFRDef', 'ENFRDef']
+CORE_ETYPES = list(BASE_TYPES) + ['CWEType', 'CWRType', 'CWUser', 'CWGroup',
+                                  'CWConstraint', 'CWAttribute', 'CWRelation']
 CORE_RTYPES = ['eid', 'creation_date', 'modification_date',
                'login', 'upassword', 'name',
                'is', 'instanceof', 'owned_by', 'created_by', 'in_group',
@@ -104,6 +104,7 @@
         
 class DropTableOp(PreCommitOperation):
     """actually remove a database from the application's schema"""
+    table = None # make pylint happy
     def precommit_event(self):
         dropped = self.session.query_data('droppedtables',
                                           default=set(), setdefault=True)
@@ -117,6 +118,7 @@
     """actually remove the attribut's column from entity table in the system
     database
     """
+    table = column = None # make pylint happy
     def precommit_event(self):
         session, table, column = self.session, self.table, self.column
         # drop index if any
@@ -132,7 +134,7 @@
 
 # deletion ####################################################################
 
-class DeleteEETypeOp(SchemaOperation):
+class DeleteCWETypeOp(SchemaOperation):
     """actually remove the entity type from the application's schema"""    
     def commit_event(self):
         try:
@@ -143,9 +145,9 @@
             pass
 
 def before_del_eetype(session, eid):
-    """before deleting a EEType entity:
+    """before deleting a CWEType entity:
     * check that we don't remove a core entity type
-    * cascade to delete related EFRDef and ENFRDef entities
+    * cascade to delete related CWAttribute and CWRelation entities
     * instantiate an operation to delete the entity type on commit
     """
     # final entities can't be deleted, don't care about that
@@ -153,7 +155,7 @@
     # delete every entities of this type
     session.unsafe_execute('DELETE %s X' % name)
     DropTableOp(session, table=SQL_PREFIX + name)
-    DeleteEETypeOp(session, name)
+    DeleteCWETypeOp(session, name)
 
 def after_del_eetype(session, eid):
     # workflow cleanup
@@ -161,7 +163,7 @@
     session.execute('DELETE Transition X WHERE NOT X transition_of Y')
 
         
-class DeleteERTypeOp(SchemaOperation):
+class DeleteCWRTypeOp(SchemaOperation):
     """actually remove the relation type from the application's schema"""    
     def commit_event(self):
         try:
@@ -171,18 +173,18 @@
             pass
 
 def before_del_ertype(session, eid):
-    """before deleting a ERType entity:
+    """before deleting a CWRType entity:
     * check that we don't remove a core relation type
-    * cascade to delete related EFRDef and ENFRDef entities
+    * cascade to delete related CWAttribute and CWRelation entities
     * instantiate an operation to delete the relation type on commit
     """
     name = check_internal_entity(session, eid, CORE_RTYPES)
     # delete relation definitions using this relation type
-    session.execute('DELETE EFRDef X WHERE X relation_type Y, Y eid %(x)s',
+    session.execute('DELETE CWAttribute X WHERE X relation_type Y, Y eid %(x)s',
                     {'x': eid})
-    session.execute('DELETE ENFRDef X WHERE X relation_type Y, Y eid %(x)s',
+    session.execute('DELETE CWRelation X WHERE X relation_type Y, Y eid %(x)s',
                     {'x': eid})
-    DeleteERTypeOp(session, name)
+    DeleteCWRTypeOp(session, name)
 
     
 class DelErdefOp(SchemaOperation):
@@ -196,7 +198,7 @@
             pass
         
 def after_del_relation_type(session, rdefeid, rtype, rteid):
-    """before deleting a EFRDef or ENFRDef entity:
+    """before deleting a CWAttribute or CWRelation entity:
     * if this is a final or inlined relation definition, instantiate an
       operation to drop necessary column, else if this is the last instance
       of a non final relation, instantiate an operation to drop necessary
@@ -208,9 +210,9 @@
     pendings = session.query_data('pendingeids', ())
     # first delete existing relation if necessary
     if rschema.is_final():
-        rdeftype = 'EFRDef'
+        rdeftype = 'CWAttribute'
     else:
-        rdeftype = 'ENFRDef'
+        rdeftype = 'CWRelation'
         if not (subjschema.eid in pendings or objschema.eid in pendings):
             session.execute('DELETE X %s Y WHERE X is %s, Y is %s'
                             % (rschema, subjschema, objschema))
@@ -233,20 +235,21 @@
         DropTableOp(session, table='%s_relation' % rschema.type)
     # if this is the last instance, drop associated relation type
     if lastrel and not rteid in pendings:
-        execute('DELETE ERType X WHERE X eid %(x)s', {'x': rteid}, 'x')
+        execute('DELETE CWRType X WHERE X eid %(x)s', {'x': rteid}, 'x')
     DelErdefOp(session, (subjschema, rschema, objschema))
 
         
 # addition ####################################################################
 
-class AddEETypeOp(EarlySchemaOperation):
+class AddCWETypeOp(EarlySchemaOperation):
     """actually add the entity type to the application's schema"""    
+    eid = None # make pylint happy
     def commit_event(self):
         eschema = self.schema.add_entity_type(self.kobj)
         eschema.eid = self.eid
         
 def before_add_eetype(session, entity):
-    """before adding a EEType entity:
+    """before adding a CWEType entity:
     * check that we are not using an existing entity type,
     """
     name = entity['name']
@@ -255,11 +258,11 @@
         raise RepositoryError('an entity type %s already exists' % name)
 
 def after_add_eetype(session, entity):
-    """after adding a EEType entity:
+    """after adding a CWEType entity:
     * create the necessary table
     * set creation_date and modification_date by creating the necessary
-      EFRDef entities
-    * add owned_by relation by creating the necessary ENFRDef entity
+      CWAttribute entities
+    * add owned_by relation by creating the necessary CWRelation entity
     * register an operation to add the entity type to the application's
       schema on commit
     """
@@ -294,21 +297,22 @@
     # register operation to modify the schema on commit
     # this have to be done before adding other relations definitions
     # or permission settings
-    AddEETypeOp(session, etype, eid=entity.eid)
+    AddCWETypeOp(session, etype, eid=entity.eid)
     # add meta creation_date, modification_date and owned_by relations
     for rql, kwargs in relrqls:
         session.execute(rql, kwargs)
 
 
-class AddERTypeOp(EarlySchemaOperation):
+class AddCWRTypeOp(EarlySchemaOperation):
     """actually add the relation type to the application's schema"""    
+    eid = None # make pylint happy
     def commit_event(self):
         rschema = self.schema.add_relation_type(self.kobj)
         rschema.set_default_groups()
         rschema.eid = self.eid
         
 def before_add_ertype(session, entity):
-    """before adding a ERType entity:
+    """before adding a CWRType entity:
     * check that we are not using an existing relation type,
     * register an operation to add the relation type to the application's
       schema on commit
@@ -320,12 +324,12 @@
         raise RepositoryError('a relation type %s already exists' % name)
     
 def after_add_ertype(session, entity):
-    """after a ERType entity has been added:
+    """after a CWRType entity has been added:
     * register an operation to add the relation type to the application's
       schema on commit
     We don't know yeat this point if a table is necessary
     """
-    AddERTypeOp(session, RelationType(name=entity['name'],
+    AddCWRTypeOp(session, RelationType(name=entity['name'],
                                       description=entity.get('description'),
                                       meta=entity.get('meta', False),
                                       inlined=entity.get('inlined', False),
@@ -352,8 +356,8 @@
     }
 
 
-class AddEFRDefPreCommitOp(PreCommitOperation):
-    """an attribute relation (EFRDef) has been added:
+class AddCWAttributePreCommitOp(PreCommitOperation):
+    """an attribute relation (CWAttribute) has been added:
     * add the necessary column
     * set default on this column if any and possible
     * register an operation to add the relation definition to the
@@ -361,6 +365,7 @@
       
     constraints are handled by specific hooks
     """
+    entity = None # make pylint happy
     def precommit_event(self):
         session = self.session
         entity = self.entity
@@ -434,10 +439,10 @@
         AddErdefOp(session, rdef)
 
 def after_add_efrdef(session, entity):
-    AddEFRDefPreCommitOp(session, entity=entity)
+    AddCWAttributePreCommitOp(session, entity=entity)
 
 
-class AddENFRDefPreCommitOp(PreCommitOperation):
+class AddCWRelationPreCommitOp(PreCommitOperation):
     """an actual relation has been added:
     * if this is an inlined relation, add the necessary column
       else if it's the first instance of this relation type, add the
@@ -447,6 +452,7 @@
 
     constraints are handled by specific hooks
     """
+    entity = None # make pylint happy
     def precommit_event(self):
         session = self.session
         entity = self.entity
@@ -504,7 +510,7 @@
                 session.add_query_data('createdtables', rtype)
                 
 def after_add_enfrdef(session, entity):
-    AddENFRDefPreCommitOp(session, entity=entity)
+    AddCWRelationPreCommitOp(session, entity=entity)
 
 
 # update ######################################################################
@@ -538,6 +544,7 @@
 
 class UpdateEntityTypeName(SchemaOperation):
     """this operation updates physical storage accordingly"""
+    oldname = newname = None # make pylint happy
 
     def precommit_event(self):
         # we need sql to operate physical changes on the system database
@@ -556,6 +563,7 @@
 
 class UpdateRdefOp(SchemaOperation):
     """actually update some properties of a relation definition"""
+    rschema = values = None # make pylint happy
 
     def precommit_event(self):
         if 'indexed' in self.values:
@@ -593,6 +601,8 @@
 
 class UpdateRtypeOp(SchemaOperation):
     """actually update some properties of a relation definition"""    
+    rschema = values = entity = None # make pylint happy
+
     def precommit_event(self):
         session = self.session
         rschema = self.rschema
@@ -673,6 +683,8 @@
 
 class ConstraintOp(SchemaOperation):
     """actually update constraint of a relation definition"""
+    entity = None # make pylint happy
+    
     def prepare_constraints(self, rtype, subjtype, objtype):
         constraints = rtype.rproperty(subjtype, objtype, 'constraints')
         self.constraints = list(constraints)
@@ -730,6 +742,7 @@
 
 class DelConstraintOp(ConstraintOp):
     """actually remove a constraint of a relation definition"""
+    rtype = subjtype = objtype = None # make pylint happy
     
     def precommit_event(self):
         self.prepare_constraints(self.rtype, self.subjtype, self.objtype)
@@ -833,7 +846,7 @@
 def after_add_permission(session, subject, rtype, object):
     """added entity/relation *_permission, need to update schema"""
     perm = rtype.split('_', 1)[0]
-    if session.describe(object)[0] == 'EGroup':
+    if session.describe(object)[0] == 'CWGroup':
         AddGroupPermissionOp(session, perm, subject, object)
     else: # RQLExpression
         expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR',
@@ -893,7 +906,7 @@
     if subject in session.query_data('pendingeids', ()):
         return
     perm = rtype.split('_', 1)[0]
-    if session.describe(object)[0] == 'EGroup':
+    if session.describe(object)[0] == 'CWGroup':
         DelGroupPermissionOp(session, perm, subject, object)
     else: # RQLExpression
         expr = session.execute('Any EXPR WHERE X eid %(x)s, X expression EXPR',
@@ -912,28 +925,28 @@
     """register schema related hooks on the hooks manager"""
     # schema synchronisation #####################
     # before/after add
-    hm.register_hook(before_add_eetype, 'before_add_entity', 'EEType')
-    hm.register_hook(before_add_ertype, 'before_add_entity', 'ERType')
-    hm.register_hook(after_add_eetype, 'after_add_entity', 'EEType')
-    hm.register_hook(after_add_ertype, 'after_add_entity', 'ERType')
-    hm.register_hook(after_add_efrdef, 'after_add_entity', 'EFRDef')
-    hm.register_hook(after_add_enfrdef, 'after_add_entity', 'ENFRDef')
+    hm.register_hook(before_add_eetype, 'before_add_entity', 'CWEType')
+    hm.register_hook(before_add_ertype, 'before_add_entity', 'CWRType')
+    hm.register_hook(after_add_eetype, 'after_add_entity', 'CWEType')
+    hm.register_hook(after_add_ertype, 'after_add_entity', 'CWRType')
+    hm.register_hook(after_add_efrdef, 'after_add_entity', 'CWAttribute')
+    hm.register_hook(after_add_enfrdef, 'after_add_entity', 'CWRelation')
     # before/after update
-    hm.register_hook(before_update_eetype, 'before_update_entity', 'EEType')
-    hm.register_hook(before_update_ertype, 'before_update_entity', 'ERType')
-    hm.register_hook(after_update_ertype, 'after_update_entity', 'ERType')
-    hm.register_hook(after_update_erdef, 'after_update_entity', 'EFRDef')
-    hm.register_hook(after_update_erdef, 'after_update_entity', 'ENFRDef')
+    hm.register_hook(before_update_eetype, 'before_update_entity', 'CWEType')
+    hm.register_hook(before_update_ertype, 'before_update_entity', 'CWRType')
+    hm.register_hook(after_update_ertype, 'after_update_entity', 'CWRType')
+    hm.register_hook(after_update_erdef, 'after_update_entity', 'CWAttribute')
+    hm.register_hook(after_update_erdef, 'after_update_entity', 'CWRelation')
     # before/after delete
-    hm.register_hook(before_del_eetype, 'before_delete_entity', 'EEType')
-    hm.register_hook(after_del_eetype, 'after_delete_entity', 'EEType')
-    hm.register_hook(before_del_ertype, 'before_delete_entity', 'ERType')
+    hm.register_hook(before_del_eetype, 'before_delete_entity', 'CWEType')
+    hm.register_hook(after_del_eetype, 'after_delete_entity', 'CWEType')
+    hm.register_hook(before_del_ertype, 'before_delete_entity', 'CWRType')
     hm.register_hook(after_del_relation_type, 'after_delete_relation', 'relation_type')
     hm.register_hook(rebuild_infered_relations, 'after_add_relation', 'specializes')
     hm.register_hook(rebuild_infered_relations, 'after_delete_relation', 'specializes')    
     # constraints synchronization hooks
-    hm.register_hook(after_add_econstraint, 'after_add_entity', 'EConstraint')
-    hm.register_hook(after_update_econstraint, 'after_update_entity', 'EConstraint')
+    hm.register_hook(after_add_econstraint, 'after_add_entity', 'CWConstraint')
+    hm.register_hook(after_update_econstraint, 'after_update_entity', 'CWConstraint')
     hm.register_hook(before_delete_constrained_by, 'before_delete_relation', 'constrained_by')
     hm.register_hook(after_add_constrained_by, 'after_add_relation', 'constrained_by')
     # permissions synchronisation ################
--- a/server/schemaserial.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/schemaserial.py	Mon May 04 13:09:48 2009 +0200
@@ -1,11 +1,12 @@
 """functions for schema / permissions (de)serialization using RQL
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
+import sys
 from itertools import chain
 
 from logilab.common.shellutils import ProgressBar
@@ -18,12 +19,12 @@
     """create a group mapping from an rql cursor
 
     A group mapping has standard group names as key (managers, owners at least)
-    and the actual EGroup entity's eid as associated value.
+    and the actual CWGroup entity's eid as associated value.
     In interactive mode (the default), missing groups'eid will be prompted
     from the user.
     """
     res = {}
-    for eid, name in cursor.execute('Any G, N WHERE G is EGroup, G name N'):
+    for eid, name in cursor.execute('Any G, N WHERE G is CWGroup, G name N'):
         res[name] = eid
     if not interactive:
         return res
@@ -31,7 +32,7 @@
     if missing:
         print 'some native groups are missing but the following groups have been found:'
         print '\n'.join('* %s (%s)' % (n, eid) for n, eid in res.items())
-        print 
+        print
         print 'enter the eid of a to group to map to each missing native group'
         print 'or just type enter to skip permissions granted to a group'
         for group in missing:
@@ -46,18 +47,71 @@
                     continue
     return res
 
+def _set_sql_prefix(prefix):
+    """3.2.0 migration function: allow to unset/reset SQL_PREFIX"""
+    for module in ('checkintegrity', 'migractions', 'schemahooks',
+                   'sources.rql2sql', 'sources.native'):
+        try:
+            sys.modules['cubicweb.server.%s' % module].SQL_PREFIX = prefix
+            print 'changed SQL_PREFIX for %s' % module
+        except KeyError:
+            pass
+
+def _update_database(schema, sqlcu):
+    """3.2.0 migration function: update database schema by adding SQL_PREFIX to
+    entity type tables and columns
+    """
+    for etype in schema.entities():
+        if etype.is_final():
+            continue
+        try:
+            sql = 'ALTER TABLE %s RENAME TO cw_%s' % (
+                etype, ETYPE_NAME_MAP.get(etype, etype))
+            print sql
+            sqlcu.execute(sql)
+        except:
+            pass
+        for rschema in etype.subject_relations():
+            if rschema == 'has_text':
+                continue
+            if rschema.is_final() or rschema.inlined:
+                sql = 'ALTER TABLE cw_%s RENAME %s TO cw_%s' % (
+                    etype, rschema, rschema)
+                print sql
+                sqlcu.execute(sql)
+
 # schema / perms deserialization ##############################################
 
 def deserialize_schema(schema, session):
     """return a schema according to information stored in an rql database
-    as ERType and EEType entities
+    as CWRType and CWEType entities
     """
+    #
+    repo = session.repo
+    sqlcu = session.pool['system']
+    _3_2_migration = False
+    tables = set(t.lower() for t in repo.system_source.dbhelper.list_tables(sqlcu))
+    if 'eetype' in tables:
+        _3_2_migration = True
+        # 3.2 migration
+        _set_sql_prefix('')
+        # first rename entity types whose name changed in 3.2 without adding the
+        # cw_ prefix
+        for etype in ('EFRDef', 'ENFRDef', 'ERType', 'EEType',
+                      'EConstraintType', 'EConstraint', 'EGroup', 'EUser',
+                      'ECache', 'EPermission', 'EProperty'):
+            if etype.lower() in tables:
+                sql = 'ALTER TABLE %s RENAME TO %s' % (etype,
+                                                       ETYPE_NAME_MAP[etype])
+                print sql
+                sqlcu.execute(sql)
+        # other table renaming done once schema has been readen
     # print 'reading schema from the database...'
     index = {}
     permsdict = deserialize_ertype_permissions(session)
     schema.reading_from_database = True
     for eid, etype, desc, meta in session.execute('Any X, N, D, M WHERE '
-                                                  'X is EEType, X name N, '
+                                                  'X is CWEType, X name N, '
                                                   'X description D, X meta M',
                                                   build_descr=False):
         # base types are already in the schema, skip them
@@ -70,7 +124,7 @@
         if etype in ETYPE_NAME_MAP: # XXX <2.45 bw compat
             print 'fixing etype name from %s to %s' % (etype, ETYPE_NAME_MAP[etype])
             # can't use write rql queries at this point, use raw sql
-            session.system_sql('UPDATE EEType SET name=%(n)s WHERE eid=%(x)s',
+            session.system_sql('UPDATE CWEType SET name=%(n)s WHERE eid=%(x)s',
                                {'x': eid, 'n': ETYPE_NAME_MAP[etype]})
             session.system_sql('UPDATE entities SET type=%(n)s WHERE type=%(x)s',
                                {'x': etype, 'n': ETYPE_NAME_MAP[etype]})
@@ -81,9 +135,9 @@
             except:
                 pass
             tocleanup = [eid]
-            tocleanup += (eid for eid, (eidetype, uri, extid) in session.repo._type_source_cache.items()
+            tocleanup += (eid for eid, (eidetype, uri, extid) in repo._type_source_cache.items()
                           if etype == eidetype)
-            session.repo.clear_caches(tocleanup)
+            repo.clear_caches(tocleanup)
             session.commit(False)
             etype = ETYPE_NAME_MAP[etype]
         etype = ybo.EntityType(name=etype, description=desc, meta=meta, eid=eid)
@@ -91,7 +145,7 @@
         index[eid] = eschema
         set_perms(eschema, permsdict.get(eid, {}))
     try:
-        rset = session.execute('Any XN, ETN WHERE X is EEType, X name XN, '
+        rset = session.execute('Any XN, ETN WHERE X is CWEType, X name XN, '
                                'X specializes ET, ET name ETN')
     except: # `specializes` relation not available for versions prior to 2.50
         session.rollback(False)
@@ -102,7 +156,7 @@
             eschema._specialized_type = stype
             seschema._specialized_by.append(etype)
     for eid, rtype, desc, meta, sym, il in session.execute(
-        'Any X,N,D,M,S,I WHERE X is ERType, X name N, X description D, '
+        'Any X,N,D,M,S,I WHERE X is CWRType, X name N, X description D, '
         'X meta M, X symetric S, X inlined I', build_descr=False):
         try:
             # bw compat: fulltext_container added in 2.47
@@ -116,10 +170,10 @@
                                  fulltext_container=ft_container, eid=eid)
         rschema = schema.add_relation_type(rtype)
         index[eid] = rschema
-        set_perms(rschema, permsdict.get(eid, {}))        
+        set_perms(rschema, permsdict.get(eid, {}))
     cstrsdict = deserialize_rdef_constraints(session)
     for values in session.execute(
-        'Any X,SE,RT,OE,CARD,ORD,DESC,IDX,FTIDX,I18N,DFLT WHERE X is EFRDef,'
+        'Any X,SE,RT,OE,CARD,ORD,DESC,IDX,FTIDX,I18N,DFLT WHERE X is CWAttribute,'
         'X relation_type RT, X cardinality CARD, X ordernum ORD, X indexed IDX,'
         'X description DESC, X internationalizable I18N, X defaultval DFLT,'
         'X fulltextindexed FTIDX, X from_entity SE, X to_entity OE',
@@ -130,14 +184,14 @@
         rtype = index[reid].type
         toetype = index[teid].type
         rdef = ybo.RelationDefinition(frometype, rtype, toetype, cardinality=card,
-                                  order=ord, description=desc, 
+                                  order=ord, description=desc,
                                   constraints=constraints,
                                   indexed=idx, fulltextindexed=ftidx,
                                   internationalizable=i18n,
                                   default=default, eid=rdefeid)
         schema.add_relation_def(rdef)
     for values in session.execute(
-        'Any X,SE,RT,OE,CARD,ORD,DESC,C WHERE X is ENFRDef, X relation_type RT,'
+        'Any X,SE,RT,OE,CARD,ORD,DESC,C WHERE X is CWRelation, X relation_type RT,'
         'X cardinality CARD, X ordernum ORD, X description DESC, '
         'X from_entity SE, X to_entity OE, X composite C', build_descr=False):
         rdefeid, seid, reid, teid, card, ord, desc, c = values
@@ -146,11 +200,14 @@
         toetype = index[teid].type
         constraints = cstrsdict.get(rdefeid, ())
         rdef = ybo.RelationDefinition(frometype, rtype, toetype, cardinality=card,
-                                  order=ord, description=desc, 
+                                  order=ord, description=desc,
                                   composite=c, constraints=constraints,
                                   eid=rdefeid)
         schema.add_relation_def(rdef)
     schema.infer_specialization_rules()
+    if _3_2_migration:
+        _update_database(schema, sqlcu)
+        _set_sql_prefix('cw_')
     session.commit()
     schema.reading_from_database = False
 
@@ -159,11 +216,11 @@
     """return sect action:groups associations for the given
     entity or relation schema with its eid, according to schema's
     permissions stored in the database as [read|add|delete|update]_permission
-    relations between EEType/ERType and EGroup entities
+    relations between CWEType/CWRType and CWGroup entities
     """
     res = {}
     for action in ('read', 'add', 'update', 'delete'):
-        rql = 'Any E,N WHERE G is EGroup, G name N, E %s_permission G' % action
+        rql = 'Any E,N WHERE G is CWGroup, G name N, E %s_permission G' % action
         for eid, gname in session.execute(rql, build_descr=False):
             res.setdefault(eid, {}).setdefault(action, []).append(gname)
         rql = ('Any E,X,EXPR,V WHERE X is RQLExpression, X expression EXPR, '
@@ -187,21 +244,21 @@
                 actperms.append(erschema.rql_expression(*something))
             else: # group name
                 actperms.append(something)
-        erschema.set_permissions(action, actperms)            
+        erschema.set_permissions(action, actperms)
 
 
 def deserialize_rdef_constraints(session):
     """return the list of relation definition's constraints as instances"""
     res = {}
     for rdefeid, ceid, ct, val in session.execute(
-        'Any E, X,TN,V WHERE E constrained_by X, X is EConstraint, '
+        'Any E, X,TN,V WHERE E constrained_by X, X is CWConstraint, '
         'X cstrtype T, T name TN, X value V', build_descr=False):
         cstr = CONSTRAINTS[ct].deserialize(val)
         cstr.eid = ceid
         res.setdefault(rdefeid, []).append(cstr)
     return res
-        
-        
+
+
 # schema / perms serialization ################################################
 
 def serialize_schema(cursor, schema, verbose=False):
@@ -215,7 +272,7 @@
         pb_size = len(aller) + len(CONSTRAINTS) + len([x for x in eschemas if x.specializes()])
         pb = ProgressBar(pb_size)
     for cstrtype in CONSTRAINTS:
-        rql = 'INSERT EConstraintType X: X name "%s"' % cstrtype
+        rql = 'INSERT CWConstraintType X: X name "%s"' % cstrtype
         if verbose:
             print rql
         cursor.execute(rql)
@@ -298,12 +355,12 @@
             value = unicode(value)
         values[amap.get(prop, prop)] = value
     return values
-    
+
 def nfrdef_relations_values(rschema, objtype, props):
     values = _rdef_values(rschema, objtype, props)
     relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
     return relations, values
-    
+
 def frdef_relations_values(rschema, objtype, props):
     values = _rdef_values(rschema, objtype, props)
     default = values['default']
@@ -317,7 +374,7 @@
     relations = ['X %s %%(%s)s' % (attr, attr) for attr in sorted(values)]
     return relations, values
 
-    
+
 def __rdef2rql(genmap, rschema, subjtype=None, objtype=None, props=None):
     if subjtype is None:
         assert objtype is None
@@ -341,7 +398,7 @@
 
 def schema2rql(schema, skip=None, allow=None):
     """return a list of rql insert statements to enter the schema in the
-    database as ERType and EEType entities
+    database as CWRType and CWEType entities
     """
     assert not (skip is not None and allow is not None), \
            'can\'t use both skip and allow'
@@ -351,7 +408,7 @@
     elif allow is not None:
         return chain(*[erschema2rql(schema[t]) for t in all if t in allow])
     return chain(*[erschema2rql(schema[t]) for t in all])
-        
+
 def erschema2rql(erschema):
     if isinstance(erschema, schemamod.EntitySchema):
         return eschema2rql(erschema)
@@ -359,12 +416,12 @@
 
 def eschema2rql(eschema):
     """return a list of rql insert statements to enter an entity schema
-    in the database as an EEType entity
+    in the database as an CWEType entity
     """
     relations, values = eschema_relations_values(eschema)
     # NOTE: 'specializes' relation can't be inserted here since there's no
     # way to make sure the parent type is inserted before the child type
-    yield 'INSERT EEType X: %s' % ','.join(relations) , values
+    yield 'INSERT CWEType X: %s' % ','.join(relations) , values
 
 def specialize2rql(schema):
     for eschema in schema.entities():
@@ -379,16 +436,16 @@
 
 def rschema2rql(rschema, addrdef=True):
     """return a list of rql insert statements to enter a relation schema
-    in the database as an ERType entity
+    in the database as an CWRType entity
     """
     if rschema.type == 'has_text':
         return
     relations, values = rschema_relations_values(rschema)
-    yield 'INSERT ERType X: %s' % ','.join(relations), values
+    yield 'INSERT CWRType X: %s' % ','.join(relations), values
     if addrdef:
         for rql, values in rdef2rql(rschema):
             yield rql, values
-            
+
 def rdef2rql(rschema, subjtype=None, objtype=None, props=None):
     genmap = {True: frdef2rql, False: nfrdef2rql}
     return __rdef2rql(genmap, rschema, subjtype, objtype, props)
@@ -401,18 +458,18 @@
     relations, values = frdef_relations_values(rschema, objtype, props)
     relations.append(_LOCATE_RDEF_RQL0)
     values.update({'se': str(subjtype), 'rt': str(rschema), 'oe': str(objtype)})
-    yield 'INSERT EFRDef X: %s WHERE %s' % (','.join(relations), _LOCATE_RDEF_RQL1), values
+    yield 'INSERT CWAttribute X: %s WHERE %s' % (','.join(relations), _LOCATE_RDEF_RQL1), values
     for rql, values in rdefrelations2rql(rschema, subjtype, objtype, props):
-        yield rql + ', EDEF is EFRDef', values
-            
+        yield rql + ', EDEF is CWAttribute', values
+
 def nfrdef2rql(rschema, subjtype, objtype, props):
     relations, values = nfrdef_relations_values(rschema, objtype, props)
     relations.append(_LOCATE_RDEF_RQL0)
     values.update({'se': str(subjtype), 'rt': str(rschema), 'oe': str(objtype)})
-    yield 'INSERT ENFRDef X: %s WHERE %s' % (','.join(relations), _LOCATE_RDEF_RQL1), values
+    yield 'INSERT CWRelation X: %s WHERE %s' % (','.join(relations), _LOCATE_RDEF_RQL1), values
     for rql, values in rdefrelations2rql(rschema, subjtype, objtype, props):
-        yield rql + ', EDEF is ENFRDef', values
-                
+        yield rql + ', EDEF is CWRelation', values
+
 def rdefrelations2rql(rschema, subjtype, objtype, props):
     iterators = []
     for constraint in props['constraints']:
@@ -423,14 +480,14 @@
     values = {'ctname': unicode(constraint.type()),
               'value': unicode(constraint.serialize()),
               'rt': str(rschema), 'se': str(subjtype), 'oe': str(objtype)}
-    yield 'INSERT EConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE \
+    yield 'INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE \
 CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, \
 ER name %(rt)s, SE name %(se)s, OE name %(oe)s', values
 
 def perms2rql(schema, groupmapping):
     """return rql insert statements to enter the schema's permissions in
     the database as [read|add|delete|update]_permission relations between
-    EEType/ERType and EGroup entities
+    CWEType/CWRType and CWGroup entities
 
     groupmapping is a dictionnary mapping standard group names to
     eids
@@ -443,10 +500,10 @@
 def erperms2rql(erschema, groupmapping):
     """return rql insert statements to enter the entity or relation
     schema's permissions in the database as
-    [read|add|delete|update]_permission relations between EEType/ERType
-    and EGroup entities
+    [read|add|delete|update]_permission relations between CWEType/CWRType
+    and CWGroup entities
     """
-    etype = isinstance(erschema, schemamod.EntitySchema) and 'EEType' or 'ERType'
+    etype = isinstance(erschema, schemamod.EntitySchema) and 'CWEType' or 'CWRType'
     for action in erschema.ACTIONS:
         for group in sorted(erschema.get_groups(action)):
             try:
@@ -465,13 +522,13 @@
 def updateeschema2rql(eschema):
     relations, values = eschema_relations_values(eschema)
     values['et'] = eschema.type
-    yield 'SET %s WHERE X is EEType, X name %%(et)s' % ','.join(relations), values
+    yield 'SET %s WHERE X is CWEType, X name %%(et)s' % ','.join(relations), values
 
 def updaterschema2rql(rschema):
     relations, values = rschema_relations_values(rschema)
     values['rt'] = rschema.type
-    yield 'SET %s WHERE X is ERType, X name %%(rt)s' % ','.join(relations), values
-            
+    yield 'SET %s WHERE X is CWRType, X name %%(rt)s' % ','.join(relations), values
+
 def updaterdef2rql(rschema, subjtype=None, objtype=None, props=None):
     genmap = {True: updatefrdef2rql, False: updatenfrdef2rql}
     return __rdef2rql(genmap, rschema, subjtype, objtype, props)
@@ -479,13 +536,13 @@
 def updatefrdef2rql(rschema, subjtype, objtype, props):
     relations, values = frdef_relations_values(rschema, objtype, props)
     values.update({'se': subjtype, 'rt': str(rschema), 'oe': objtype})
-    yield 'SET %s WHERE %s, %s, X is EFRDef' % (','.join(relations),
+    yield 'SET %s WHERE %s, %s, X is CWAttribute' % (','.join(relations),
                                                  _LOCATE_RDEF_RQL0,
                                                  _LOCATE_RDEF_RQL1), values
-            
+
 def updatenfrdef2rql(rschema, subjtype, objtype, props):
     relations, values = nfrdef_relations_values(rschema, objtype, props)
     values.update({'se': subjtype, 'rt': str(rschema), 'oe': objtype})
-    yield 'SET %s WHERE %s, %s, X is ENFRDef' % (','.join(relations),
+    yield 'SET %s WHERE %s, %s, X is CWRelation' % (','.join(relations),
                                                  _LOCATE_RDEF_RQL0,
                                                  _LOCATE_RDEF_RQL1), values
--- a/server/serverconfig.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/serverconfig.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """server.serverconfig definition
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -232,7 +232,7 @@
                 hooks.setdefault(event, {}).setdefault(ertype, []).append(cb)
         return hooks
     
-    def load_schema(self, expand_cubes=False):
+    def load_schema(self, expand_cubes=False, construction_mode='strict'):
         from cubicweb.schema import CubicWebSchemaLoader
         if expand_cubes:
             # in case some new dependencies have been introduced, we have to
@@ -240,7 +240,7 @@
             origcubes = self.cubes()
             self._cubes = None
             self.init_cubes(self.expand_cubes(origcubes))
-        schema = CubicWebSchemaLoader().load(self)
+        schema = CubicWebSchemaLoader().load(self, construction_mode=construction_mode)
         if expand_cubes:
             # restaure original value
             self._cubes = origcubes
@@ -266,7 +266,7 @@
                     print 'not connecting to source', uri, 'during migration'
         elif 'all' in sources:
             assert len(sources) == 1
-            enabled_sources= None
+            enabled_sources = None
         else:
             known_sources = self.sources()
             for uri in sources:
--- a/server/serverctl.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/serverctl.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """cubicweb-ctl commands and command handlers specific to the server.serverconfig
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -9,10 +9,11 @@
 import os
 
 from logilab.common.configuration import REQUIRED, Configuration, ini_format_section
+from logilab.common.clcommands import register_commands, cmd_run, pop_arg
 
 from cubicweb import AuthenticationError, ExecutionError, ConfigurationError
-from cubicweb.toolsutils import Command, CommandHandler, pop_arg, cmd_run, \
-     register_commands, confirm, restrict_perms_to_user
+from cubicweb.toolsutils import (Command, CommandHandler, confirm,
+                                 restrict_perms_to_user)
 from cubicweb.server.serverconfig import ServerConfiguration
 
 
@@ -88,7 +89,7 @@
         # set_isolation_level() is psycopg specific
         pass
     return cnx
-    
+
 def generate_sources_file(sourcesfile, sourcescfg, keys=None):
     """serialize repository'sources configuration into a INI like file
 
@@ -108,7 +109,7 @@
             # get a Configuration object
             _sconfig = Configuration(options=SOURCE_TYPES[sconfig['adapter']].options)
             for attr, val in sconfig.items():
-                if attr == 'uri': 
+                if attr == 'uri':
                     continue
                 if attr == 'adapter':
                     _sconfig.adapter = val
@@ -139,7 +140,7 @@
         except AuthenticationError:
             print 'wrong user/password'
         login, pwd = manager_userpasswd()
-    
+
 # repository specific command handlers ########################################
 
 class RepositoryCreateHandler(CommandHandler):
@@ -186,14 +187,14 @@
         restrict_perms_to_user(sourcesfile)
         # remember selected cubes for later initialization of the database
         config.write_bootstrap_cubes_file(cubes)
-        
+
     def postcreate(self):
         if confirm('do you want to create repository\'s system database?'):
             verbosity = (self.config.mode == 'installed') and 'y' or 'n'
             cmd_run('db-create', self.config.appid, '--verbose=%s' % verbosity)
         else:
             print 'nevermind, you can do it later using the db-create command'
-            
+
 USER_OPTIONS =  (
     ('login', {'type' : 'string',
                'default': REQUIRED,
@@ -235,7 +236,7 @@
                 cnx.rollback()
                 raise
 
-    
+
 class RepositoryStartHandler(CommandHandler):
     cmdname = 'start'
     cfgname = 'repository'
@@ -246,7 +247,7 @@
             command.append('--debug')
         command.append(self.config.appid)
         return ' '.join(command)
-        
+
 
 class RepositoryStopHandler(CommandHandler):
     cmdname = 'stop'
@@ -259,12 +260,12 @@
         if self.config.pyro_enabled():
             from cubicweb.server.repository import pyro_unregister
             pyro_unregister(self.config)
-    
+
 
 # repository specific commands ################################################
 class CreateApplicationDBCommand(Command):
     """Create the system database of an application (run after 'create').
-    
+
     You will be prompted for a login / password to use to connect to
     the system database.  The given user should have almost all rights
     on the database (ie a super user on the dbms allowed to create
@@ -275,7 +276,7 @@
     """
     name = 'db-create'
     arguments = '<application>'
-    
+
     options = (
         ("create-db",
          {'short': 'c', 'type': "yn", 'metavar': '<y or n>',
@@ -327,11 +328,11 @@
             except:
                 dbcnx.rollback()
                 raise
-        cnx = system_source_cnx(source, special_privs='LANGUAGE C', verbose=verbose) 
+        cnx = system_source_cnx(source, special_privs='LANGUAGE C', verbose=verbose)
         cursor = cnx.cursor()
         indexer = get_indexer(driver)
         indexer.init_extensions(cursor)
-        # postgres specific stuff        
+        # postgres specific stuff
         if driver == 'postgres':
             # install plpythonu/plpgsql language if not installed by the cube
             for extlang in ('plpythonu', 'plpgsql'):
@@ -345,10 +346,10 @@
         else:
             print 'nevermind, you can do it later using the db-init command'
 
-    
+
 class InitApplicationCommand(Command):
     """Initialize the system database of an application (run after 'db-create').
-    
+
     You will be prompted for a login / password to use to connect to
     the system database.  The given user should have the create tables,
     and grant permissions.
@@ -358,7 +359,7 @@
     """
     name = 'db-init'
     arguments = '<application>'
-    
+
     options = (
         ("drop",
          {'short': 'd', 'action': 'store_true',
@@ -376,7 +377,7 @@
 
 class GrantUserOnApplicationCommand(Command):
     """Grant a database user on a repository system database.
-    
+
     <application>
       the identifier of the application
     <user>
@@ -387,7 +388,7 @@
 
     options = (
         ("set-owner",
-         {'short': 'o', 'type' : "yn", 'metavar' : '<yes or no>', 
+         {'short': 'o', 'type' : "yn", 'metavar' : '<yes or no>',
           'default' : False,
           'help': 'Set the user as tables owner if yes (no by default).'}
          ),
@@ -416,10 +417,10 @@
             print 'grants given to %s on application %s' % (appid, user)
 
 
-    
+
 class StartRepositoryCommand(Command):
     """Start an CubicWeb RQL server for a given application.
-    
+
     The server will be accessible through pyro
 
     <application>
@@ -427,7 +428,7 @@
     """
     name = 'start-repository'
     arguments = '<application>'
-    
+
     options = (
         ("debug",
          {'short': 'D', 'action' : 'store_true',
@@ -466,8 +467,8 @@
     if os.system(dmpcmd):
         raise ExecutionError('Error while dumping the database')
     if output is None:
-        from mx.DateTime import today
-        date = today().strftime('%Y-%m-%d')
+        from datetime import date
+        date = date.today().strftime('%Y-%m-%d')
         output = '%s-%s.dump' % (appid, date)
     cmd = 'scp %s:/tmp/%s.dump %s' % (host, appid, output)
     print cmd
@@ -533,7 +534,7 @@
             applversion = vcconf[cube]
         except KeyError:
             print "no cube version information for %s in version configuration" % cube
-            continue            
+            continue
         if softversion == applversion:
             continue
         if softversion > applversion:
@@ -541,11 +542,11 @@
         elif softversion < applversion:
             return 'needapplupgrade'
     return None
-    
+
 
 class DBDumpCommand(Command):
     """Backup the system database of an application.
-    
+
     <application>
       the identifier of the application to backup
       format [[user@]host:]appname
@@ -555,7 +556,7 @@
 
     options = (
         ("output",
-         {'short': 'o', 'type' : "string", 'metavar' : '<file>', 
+         {'short': 'o', 'type' : "string", 'metavar' : '<file>',
           'default' : None,
           'help': 'Specify the backup file where the backup will be stored.'}
          ),
@@ -577,7 +578,7 @@
 
 class DBRestoreCommand(Command):
     """Restore the system database of an application.
-    
+
     <application>
       the identifier of the application to restore
     """
@@ -586,7 +587,7 @@
 
     options = (
         ("no-drop",
-         {'short': 'n', 'action' : 'store_true', 
+         {'short': 'n', 'action' : 'store_true',
           'default' : False,
           'help': 'for some reason the database doesn\'t exist and so '
           'should not be dropped.'}
@@ -601,7 +602,7 @@
 
 class DBCopyCommand(Command):
     """Copy the system database of an application (backup and restore).
-    
+
     <src-application>
       the identifier of the application to backup
       format [[user@]host:]appname
@@ -614,7 +615,7 @@
 
     options = (
         ("no-drop",
-         {'short': 'n', 'action' : 'store_true', 
+         {'short': 'n', 'action' : 'store_true',
           'default' : False,
           'help': 'For some reason the database doesn\'t exist and so '
           'should not be dropped.'}
@@ -647,10 +648,10 @@
         else:
             os.remove(output)
 
-        
+
 class CheckRepositoryCommand(Command):
     """Check integrity of the system database of an application.
-    
+
     <application>
       the identifier of the application to check
     """
@@ -659,25 +660,25 @@
 
     options = (
         ("checks",
-         {'short': 'c', 'type' : "csv", 'metavar' : '<check list>', 
+         {'short': 'c', 'type' : "csv", 'metavar' : '<check list>',
           'default' : ('entities', 'relations', 'metadata', 'schema', 'text_index'),
           'help': 'Comma separated list of check to run. By default run all \
 checks, i.e. entities, relations, text_index and metadata.'}
          ),
-        
+
         ("autofix",
-         {'short': 'a', 'type' : "yn", 'metavar' : '<yes or no>', 
+         {'short': 'a', 'type' : "yn", 'metavar' : '<yes or no>',
           'default' : False,
           'help': 'Automatically correct integrity problems if this option \
 is set to "y" or "yes", else only display them'}
          ),
         ("reindex",
-         {'short': 'r', 'type' : "yn", 'metavar' : '<yes or no>', 
+         {'short': 'r', 'type' : "yn", 'metavar' : '<yes or no>',
           'default' : False,
           'help': 're-indexes the database for full text search if this \
 option is set to "y" or "yes" (may be long for large database).'}
          ),
-        
+
         )
 
     def run(self, args):
@@ -691,7 +692,7 @@
 
 class RebuildFTICommand(Command):
     """Rebuild the full-text index of the system database of an application.
-    
+
     <application>
       the identifier of the application to rebuild
     """
@@ -709,10 +710,10 @@
         reindex_entities(repo.schema, session)
         cnx.commit()
 
-    
+
 class SynchronizeApplicationSchemaCommand(Command):
     """Synchronize persistent schema with cube schema.
-        
+
     Will synchronize common stuff between the cube schema and the
     actual persistent schema, but will not add/remove any entity or relation.
 
@@ -729,7 +730,7 @@
         mih.cmd_synchronize_schema()
 
 
-register_commands( (CreateApplicationDBCommand,                   
+register_commands( (CreateApplicationDBCommand,
                     InitApplicationCommand,
                     GrantUserOnApplicationCommand,
                     StartRepositoryCommand,
--- a/server/session.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/session.py	Mon May 04 13:09:48 2009 +0200
@@ -9,38 +9,16 @@
 import sys
 import threading
 from time import time
-from types import NoneType
-from decimal import Decimal
 
-from mx.DateTime import DateTimeType, DateTimeDeltaType
-from rql.nodes import VariableRef, Function
+from rql.nodes import VariableRef, Function, ETYPE_PYOBJ_MAP, etype_from_pyobj
 from yams import BASE_TYPES
 
 from cubicweb import RequestSessionMixIn, Binary
 from cubicweb.dbapi import ConnectionProperties
-from cubicweb.common.utils import make_uid
+from cubicweb.utils import make_uid
 from cubicweb.server.rqlrewrite import RQLRewriter
 
-_ETYPE_PYOBJ_MAP = { bool: 'Boolean',
-                     int: 'Int',
-                     long: 'Int',
-                     float: 'Float',
-                     Decimal: 'Decimal',
-                     unicode: 'String',
-                     NoneType: None,
-                     Binary: 'Bytes',
-                     DateTimeType: 'Datetime',
-                     DateTimeDeltaType: 'Interval',
-                     }
-
-def etype_from_pyobj(value):
-    """guess yams type from python value"""
-    # note:
-    # * Password is not selectable so no problem)
-    # * use type(value) and not value.__class__ since mx instances have no
-    #   __class__ attribute
-    # * XXX Date, Time
-    return _ETYPE_PYOBJ_MAP[type(value)]
+ETYPE_PYOBJ_MAP[Binary] = 'Bytes'
 
 def is_final(rqlst, variable, args):
     # try to find if this is a final var or not
@@ -575,7 +553,7 @@
             return 'en'
         return None
 
-_IMANAGER= InternalManager()
+_IMANAGER = InternalManager()
 
 from logging import getLogger
 from cubicweb import set_log_methods
--- a/server/sources/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/sources/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -6,10 +6,9 @@
 """
 __docformat__ = "restructuredtext en"
 
+from datetime import datetime, timedelta
 from logging import getLogger
 
-from mx.DateTime import now, DateTimeDelta
-
 from cubicweb import set_log_methods
 from cubicweb.server.sqlutils import SQL_PREFIX
 
@@ -17,16 +16,16 @@
 class TimedCache(dict):
     def __init__(self, ttlm, ttls=0):
         # time to live in minutes
-        self.ttl = DateTimeDelta(0, 0, ttlm, ttls)
+        self.ttl = timedelta(0, ttlm*60 + ttls, 0)
         
     def __setitem__(self, key, value):
-        dict.__setitem__(self, key, (now(), value))
+        dict.__setitem__(self, key, (datetime.now(), value))
         
     def __getitem__(self, key):
         return dict.__getitem__(self, key)[1]
     
     def clear_expired(self):
-        now_ = now()
+        now_ = datetime.now()
         ttl = self.ttl
         for key, (timestamp, value) in self.items():
             if now_ - timestamp > ttl:
@@ -230,8 +229,8 @@
         pass
     
     def authenticate(self, session, login, password):
-        """if the source support EUser entity type, it should implements
-        this method which should return EUser eid for the given login/password
+        """if the source support CWUser entity type, it should implements
+        this method which should return CWUser eid for the given login/password
         if this account is defined in this source and valid login / password is
         given. Else raise `AuthenticationError`
         """
--- a/server/sources/extlite.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/sources/extlite.py	Mon May 04 13:09:48 2009 +0200
@@ -22,7 +22,7 @@
         timeout -= 0.2
         if timeout <= 0:
             raise RuntimeError("svn source is busy, can't acquire connection lock")
-        
+
 class ConnectionWrapper(object):
     def __init__(self, source=None):
         self.source = source
@@ -34,19 +34,19 @@
             timeout_acquire(self.source._cnxlock, 5)
             self._cnx = self.source._sqlcnx
         return self._cnx
-    
+
     def commit(self):
         if self._cnx is not None:
             self._cnx.commit()
-        
+
     def rollback(self):
         if self._cnx is not None:
             self._cnx.rollback()
-        
+
     def cursor(self):
         return self.cnx.cursor()
 
-    
+
 class SQLiteAbstractSource(AbstractSource):
     """an abstract class for external sources using a sqlite database helper
     """
@@ -59,7 +59,7 @@
             native.NONSYSTEM_ETYPES.add(etype)
         for rtype in cls.support_relations:
             native.NONSYSTEM_RELATIONS.add(rtype)
-        
+
     options = (
         ('helper-db-path',
          {'type' : 'string',
@@ -69,10 +69,10 @@
           'inputlevel': 2,
           }),
     )
-            
+
     def __init__(self, repo, appschema, source_config, *args, **kwargs):
         # the helper db is used to easy querying and will store everything but
-        # actual file content 
+        # actual file content
         dbpath = source_config.get('helper-db-path')
         if dbpath is None:
             dbpath = join(repo.config.appdatahome,
@@ -91,7 +91,7 @@
         # * create the connection when needed
         # * use a lock to be sure only one connection is used
         self._cnxlock = threading.Lock()
-        
+
     @property
     def _sqlcnx(self):
         # XXX: sqlite connections can only be used in the same thread, so
@@ -138,13 +138,13 @@
                          self.repo.config['uid'])
             chown(self.dbpath, self.repo.config['uid'])
         restrict_perms_to_user(self.dbpath, self.info)
-        
+
     def set_schema(self, schema):
         super(SQLiteAbstractSource, self).set_schema(schema)
         if self._need_sql_create and self._is_schema_complete() and self.dbpath:
             self._create_database()
         self.rqlsqlgen = self.sqlgen_class(schema, self.sqladapter.dbhelper)
-                
+
     def get_connection(self):
         return ConnectionWrapper(self)
 
@@ -168,11 +168,11 @@
                 cnx._cnx = None
             finally:
                 self._cnxlock.release()
-        
+
     def syntax_tree_search(self, session, union,
                            args=None, cachekey=None, varmap=None, debug=0):
-        """return result from this source for a rql query (actually from a rql 
-        syntax tree and a solution dictionary mapping each used variable to a 
+        """return result from this source for a rql query (actually from a rql
+        syntax tree and a solution dictionary mapping each used variable to a
         possible type). If cachekey is given, the query necessary to fetch the
         results (but not the results themselves) may be cached using this key.
         """
@@ -185,7 +185,7 @@
         args = self.sqladapter.merge_args(args, query_args)
         cursor = session.pool[self.uri]
         cursor.execute(sql, args)
-        return self.sqladapter.process_result(cursor) 
+        return self.sqladapter.process_result(cursor)
 
     def local_add_entity(self, session, entity):
         """insert the entity in the local database.
@@ -198,12 +198,12 @@
         attrs = self.sqladapter.preprocess_entity(entity)
         sql = self.sqladapter.sqlgen.insert(SQL_PREFIX + str(entity.e_schema), attrs)
         cu.execute(sql, attrs)
-        
+
     def add_entity(self, session, entity):
         """add a new entity to the source"""
         raise NotImplementedError()
 
-    def local_update_entity(self, session, entity):
+    def local_update_entity(self, session, entity, attrs=None):
         """update an entity in the source
 
         This is not provided as update_entity implementation since usually
@@ -211,14 +211,16 @@
         and the source implementor may use this method if necessary
         """
         cu = session.pool[self.uri]
-        attrs = self.sqladapter.preprocess_entity(entity)
-        sql = self.sqladapter.sqlgen.update(SQL_PREFIX + str(entity.e_schema), attrs, ['eid'])
+        if attrs is None:
+            attrs = self.sqladapter.preprocess_entity(entity)
+        sql = self.sqladapter.sqlgen.update(SQL_PREFIX + str(entity.e_schema),
+                                            attrs, [SQL_PREFIX + 'eid'])
         cu.execute(sql, attrs)
-        
+
     def update_entity(self, session, entity):
         """update an entity in the source"""
         raise NotImplementedError()
-        
+
     def delete_entity(self, session, etype, eid):
         """delete an entity from the source
 
@@ -226,11 +228,11 @@
         source. Main usage is to delete repository content when a Repository
         entity is deleted.
         """
-        sqlcursor = session.pool[self.uri]        
+        sqlcursor = session.pool[self.uri]
         attrs = {SQL_PREFIX + 'eid': eid}
         sql = self.sqladapter.sqlgen.delete(SQL_PREFIX + etype, attrs)
         sqlcursor.execute(sql, attrs)
-    
+
     def delete_relation(self, session, subject, rtype, object):
         """delete a relation from the source"""
         rschema = self.schema.rschema(rtype)
@@ -244,5 +246,5 @@
         else:
             attrs = {'eid_from': subject, 'eid_to': object}
             sql = self.sqladapter.sqlgen.delete('%s_relation' % rtype, attrs)
-        sqlcursor = session.pool[self.uri]        
+        sqlcursor = session.pool[self.uri]
         sqlcursor.execute(sql, attrs)
--- a/server/sources/ldapuser.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/sources/ldapuser.py	Mon May 04 13:09:48 2009 +0200
@@ -1,6 +1,6 @@
 """cubicweb ldap user source
 
-this source is for now limited to a read-only EUser source
+this source is for now limited to a read-only CWUser source
 
 :organization: Logilab
 :copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
@@ -48,10 +48,11 @@
     1: (636, 'ldaps'),
     2: (0,   'ldapi'),
     }
-                
+
+
 class LDAPUserSource(AbstractSource):
-    """LDAP read-only EUser source"""
-    support_entities = {'EUser': False} 
+    """LDAP read-only CWUser source"""
+    support_entities = {'CWUser': False} 
 
     port = None
     
@@ -199,7 +200,7 @@
         return ConnectionWrapper(self._conn)
     
     def authenticate(self, session, login, password):
-        """return EUser eid for the given login/password if this account is
+        """return CWUser eid for the given login/password if this account is
         defined in this source, else raise `AuthenticationError`
 
         two queries are needed since passwords are stored crypted, so we have
@@ -223,7 +224,7 @@
         except:
             # Something went wrong, most likely bad credentials
             raise AuthenticationError()
-        return self.extid2eid(user['dn'], 'EUser', session)
+        return self.extid2eid(user['dn'], 'CWUser', session)
 
     def ldap_name(self, var):
         if var.stinfo['relations']:
@@ -293,7 +294,7 @@
         mainvars = []
         for varname in rqlst.defined_vars:
             for sol in rqlst.solutions:
-                if sol[varname] == 'EUser':
+                if sol[varname] == 'CWUser':
                     mainvars.append(varname)
                     break
         assert mainvars
@@ -325,7 +326,7 @@
             filteredres = []
             for resdict in res:
                 # get sure the entity exists in the system table
-                eid = self.extid2eid(resdict['dn'], 'EUser', session)
+                eid = self.extid2eid(resdict['dn'], 'CWUser', session)
                 for eidfilter in eidfilters:
                     if not eidfilter(eid):
                         break
@@ -402,7 +403,7 @@
         except ldap.PARTIAL_RESULTS:
             res = cnx.result(all=0)[1]
         except ldap.NO_SUCH_OBJECT:
-            eid = self.extid2eid(base, 'EUser', session, insert=False)
+            eid = self.extid2eid(base, 'CWUser', session, insert=False)
             if eid:
                 self.warning('deleting ldap user with eid %s and dn %s',
                              eid, base)
--- a/server/sources/native.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/sources/native.py	Mon May 04 13:09:48 2009 +0200
@@ -7,8 +7,7 @@
 __docformat__ = "restructuredtext en"
 
 from threading import Lock
-
-from mx.DateTime import now
+from datetime import datetime
 
 from logilab.common.cache import Cache
 from logilab.common.configuration import REQUIRED
@@ -86,9 +85,9 @@
     # need default value on class since migration doesn't call init method
     has_deleted_entitites_table = True
     
-    passwd_rql = "Any P WHERE X is EUser, X login %(login)s, X upassword P"
-    auth_rql = "Any X WHERE X is EUser, X login %(login)s, X upassword %(pwd)s"
-    _sols = ({'X': 'EUser', 'P': 'Password'},)
+    passwd_rql = "Any P WHERE X is CWUser, X login %(login)s, X upassword P"
+    auth_rql = "Any X WHERE X is CWUser, X login %(login)s, X upassword %(pwd)s"
+    _sols = ({'X': 'CWUser', 'P': 'Password'},)
     
     options = (
         ('db-driver',
@@ -200,7 +199,7 @@
             self._rql_sqlgen.schema = schema
         except AttributeError:
             pass # __init__
-        if 'EUser' in schema: # probably an empty schema if not true...
+        if 'CWUser' in schema: # probably an empty schema if not true...
             # rql syntax trees used to authenticate users
             self._passwd_rqlst = self.compile_rql(self.passwd_rql)
             self._auth_rqlst = self.compile_rql(self.auth_rql)
@@ -222,7 +221,7 @@
         return True #not rtype == 'content_for'
 
     def authenticate(self, session, login, password):
-        """return EUser eid for the given login/password if this account is
+        """return CWUser eid for the given login/password if this account is
         defined in this source, else raise `AuthenticationError`
 
         two queries are needed since passwords are stored crypted, so we have
@@ -501,7 +500,7 @@
         """add type and source info for an eid into the system table"""
         # begin by inserting eid/type/source/extid into the entities table
         attrs = {'type': str(entity.e_schema), 'eid': entity.eid,
-                 'extid': extid, 'source': source.uri, 'mtime': now()}
+                 'extid': extid, 'source': source.uri, 'mtime': datetime.now()}
         session.system_sql(self.sqlgen.insert('entities', attrs), attrs)
 
     def delete_info(self, session, eid, etype, uri, extid):
@@ -512,7 +511,7 @@
         session.system_sql(self.sqlgen.delete('entities', attrs), attrs)
         if self.has_deleted_entitites_table:
             attrs = {'type': etype, 'eid': eid, 'extid': extid,
-                     'source': uri, 'dtime': now()}
+                     'source': uri, 'dtime': datetime.now()}
             session.system_sql(self.sqlgen.insert('deleted_entities', attrs), attrs)
         
     def fti_unindex_entity(self, session, eid):
@@ -536,7 +535,7 @@
             if self.indexer is not None:
                 self.exception('error while reindexing %s', entity)
         # update entities.mtime
-        attrs = {'eid': entity.eid, 'mtime': now()}
+        attrs = {'eid': entity.eid, 'mtime': datetime.now()}
         session.system_sql(self.sqlgen.update('entities', attrs, ['eid']), attrs)
         
     def modified_entities(self, session, etypes, mtime):
--- a/server/sources/pyrorql.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/sources/pyrorql.py	Mon May 04 13:09:48 2009 +0200
@@ -9,7 +9,8 @@
 import threading
 from os.path import join
 
-from mx.DateTime import DateTimeFromTicks
+from time import mktime
+from datetime import datetime
 
 from Pyro.errors import PyroError, ConnectionClosedError
 
@@ -23,7 +24,7 @@
 from cubicweb.cwconfig import register_persistent_options
 from cubicweb.server.sources import AbstractSource, ConnectionWrapper, TimedCache
 
-class ReplaceByInOperator:
+class ReplaceByInOperator(Exception):
     def __init__(self, eids):
         self.eids = eids
         
@@ -133,20 +134,20 @@
 
     def last_update_time(self):
         pkey = u'sources.%s.latest-update-time' % self.uri
-        rql = 'Any V WHERE X is EProperty, X value V, X pkey %(k)s'
+        rql = 'Any V WHERE X is CWProperty, X value V, X pkey %(k)s'
         session = self.repo.internal_session()
         try:
             rset = session.execute(rql, {'k': pkey})
             if not rset:
                 # insert it
-                session.execute('INSERT EProperty X: X pkey %(k)s, X value %(v)s',
+                session.execute('INSERT CWProperty X: X pkey %(k)s, X value %(v)s',
                                 {'k': pkey, 'v': u'0'})
                 session.commit()
                 timestamp = 0
             else:
                 assert len(rset) == 1
                 timestamp = int(rset[0][0])
-            return DateTimeFromTicks(timestamp)
+            return datetime.fromtimestamp(timestamp)
         finally:
             session.close()
 
@@ -196,7 +197,7 @@
                     continue
             session.execute('SET X value %(v)s WHERE X pkey %(k)s',
                             {'k': u'sources.%s.latest-update-time' % self.uri,
-                             'v': unicode(int(updatetime.ticks()))})
+                             'v': unicode(int(mktime(updatetime.timetuple())))})
             session.commit()
         finally:
             session.close()
--- a/server/sources/rql2sql.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/sources/rql2sql.py	Mon May 04 13:09:48 2009 +0200
@@ -3,14 +3,14 @@
 
 SQL queries optimization
 ~~~~~~~~~~~~~~~~~~~~~~~~
-1. EUser X WHERE X in_group G, G name 'users':
+1. CWUser X WHERE X in_group G, G name 'users':
 
-   EUser is the only subject entity type for the in_group relation,
+   CWUser is the only subject entity type for the in_group relation,
    which allow us to do ::
 
-     SELECT eid_from FROM in_group, EGroup
-     WHERE in_group.eid_to = EGroup.eid_from
-     AND EGroup.name = 'users'
+     SELECT eid_from FROM in_group, CWGroup
+     WHERE in_group.eid_to = CWGroup.eid_from
+     AND CWGroup.name = 'users'
 
 
 2. Any X WHERE X nonfinal1 Y, Y nonfinal2 Z
@@ -489,8 +489,7 @@
                 sql.insert(1, 'FROM (SELECT 1) AS _T')
             sqls.append('\n'.join(sql))
         if select.need_intersect:
-            # XXX use getattr for lgc bw compat, remove once 0.37.3 is out
-            if distinct or not getattr(self.dbms_helper, 'intersect_all_support', True):
+            if distinct or not self.dbms_helper.intersect_all_support:
                 return '\nINTERSECT\n'.join(sqls)
             else:
                 return '\nINTERSECT ALL\n'.join(sqls)
--- a/server/sqlutils.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/sqlutils.py	Mon May 04 13:09:48 2009 +0200
@@ -6,20 +6,27 @@
 """
 __docformat__ = "restructuredtext en"
 
+from warnings import warn
+from datetime import datetime, date, timedelta
+
 from logilab.common.shellutils import ProgressBar
-from logilab.common.db import get_dbapi_compliant_module
+from logilab.common import db
 from logilab.common.adbh import get_adv_func_helper
 from logilab.common.sqlgen import SQLGenerator
 
 from indexer import get_indexer
 
 from cubicweb import Binary, ConfigurationError
+from cubicweb.utils import todate, todatetime
 from cubicweb.common.uilib import remove_html_tags
 from cubicweb.server import SQL_CONNECT_HOOKS
-from cubicweb.server.utils import crypt_password, cartesian_product
+from cubicweb.server.utils import crypt_password
+
 
+db.USE_MX_DATETIME = False
 SQL_PREFIX = 'cw_'
 
+
 def sqlexec(sqlstmts, cursor_or_execute, withpb=True, delimiter=';'):
     """execute sql statements ignoring DROP/ CREATE GROUP or USER statements
     error. If a cnx is given, commit at each statement
@@ -62,8 +69,8 @@
     w(grant_schema(schema, user, set_owner, skip_entities=skip_entities, prefix=SQL_PREFIX))
     return '\n'.join(output)
 
-                  
-def sqlschema(schema, driver, text_index=True, 
+
+def sqlschema(schema, driver, text_index=True,
               user=None, set_owner=False,
               skip_relations=('has_text', 'identity'), skip_entities=()):
     """return the system sql schema, according to the given parameters"""
@@ -80,7 +87,7 @@
         w(indexer.sql_init_fti())
         w('')
     dbhelper = get_adv_func_helper(driver)
-    w(schema2sql(dbhelper, schema, prefix=SQL_PREFIX, 
+    w(schema2sql(dbhelper, schema, prefix=SQL_PREFIX,
                  skip_entities=skip_entities, skip_relations=skip_relations))
     if dbhelper.users_support and user:
         w('')
@@ -88,8 +95,8 @@
                     skip_relations, skip_entities))
     return '\n'.join(output)
 
-                  
-def sqldropschema(schema, driver, text_index=True, 
+
+def sqldropschema(schema, driver, text_index=True,
                   skip_relations=('has_text', 'identity'), skip_entities=()):
     """return the sql to drop the schema, according to the given parameters"""
     from yams.schema2sql import dropschema2sql
@@ -106,13 +113,16 @@
                      skip_entities=skip_entities, skip_relations=skip_relations))
     return '\n'.join(output)
 
-
+try:
+    from mx.DateTime import DateTimeType, DateTimeDeltaType
+except ImportError:
+    DateTimeType, DateTimeDeltaType = None
 
 class SQLAdapterMixIn(object):
     """Mixin for SQL data sources, getting a connection from a configuration
     dictionary and handling connection locking
     """
-    
+
     def __init__(self, source_config):
         try:
             self.dbdriver = source_config['db-driver'].lower()
@@ -125,11 +135,11 @@
         self.dbuser = source_config.get('db-user')
         self.dbpasswd = source_config.get('db-password')
         self.encoding = source_config.get('db-encoding', 'UTF-8')
-        self.dbapi_module = get_dbapi_compliant_module(self.dbdriver)
+        self.dbapi_module = db.get_dbapi_compliant_module(self.dbdriver)
         self.binary = self.dbapi_module.Binary
         self.dbhelper = self.dbapi_module.adv_func_helper
         self.sqlgen = SQLGenerator()
-        
+
     def get_connection(self, user=None, password=None):
         """open and return a connection to the database"""
         if user or self.dbuser:
@@ -153,6 +163,16 @@
                 # convert cubicweb binary into db binary
                 if isinstance(val, Binary):
                     val = self.binary(val.getvalue())
+                # XXX <3.2 bw compat
+                elif type(val) is DateTimeType:
+                    warn('found mx date time instance, please update to use datetime',
+                         DeprecationWarning)
+                    val = datetime(val.year, val.month, val.day,
+                                   val.hour, val.minute, int(val.second))
+                elif type(val) is DateTimeDeltaType:
+                    warn('found mx date time instance, please update to use datetime',
+                         DeprecationWarning)
+                    val = timedelta(0, int(val.seconds), 0)
                 args[key] = val
             # should not collide
             args.update(query_args)
@@ -197,8 +217,23 @@
                         value = value.getvalue()
                     else:
                         value = crypt_password(value)
+                # XXX needed for sqlite but I don't think it is for other backends
+                elif atype == 'Datetime' and isinstance(value, date):
+                    value = todatetime(value)
+                elif atype == 'Date' and isinstance(value, datetime):
+                    value = todate(value)
                 elif isinstance(value, Binary):
                     value = self.binary(value.getvalue())
+                # XXX <3.2 bw compat
+                elif type(value) is DateTimeType:
+                    warn('found mx date time instance, please update to use datetime',
+                         DeprecationWarning)
+                    value = datetime(value.year, value.month, value.day,
+                                   value.hour, value.minute, int(value.second))
+                elif type(value) is DateTimeDeltaType:
+                    warn('found mx date time instance, please update to use datetime',
+                         DeprecationWarning)
+                    value = timedelta(0, int(value.seconds), 0)
             attrs[SQL_PREFIX+str(attr)] = value
         return attrs
 
@@ -225,7 +260,7 @@
     # some time
     cnx.create_aggregate("CONCAT_STRINGS", 1, concat_strings)
     cnx.create_aggregate("GROUP_CONCAT", 1, concat_strings)
-    
+
     def _limit_size(text, maxsize, format='text/plain'):
         if len(text) < maxsize:
             return text
--- a/server/ssplanner.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/ssplanner.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """plan execution of rql queries on a single source
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
--- a/server/test/data/migrschema/relations.rel	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/data/migrschema/relations.rel	Mon May 04 13:09:48 2009 +0200
@@ -1,6 +1,6 @@
 Personne travaille Societe
 Personne evaluee Note
-EUser evaluee Note
+CWUser evaluee Note
 Societe evaluee Note
 Personne concerne Affaire
 Affaire concerne Societe
@@ -9,3 +9,7 @@
 Personne connait Personne symetric
 
 Societe in_state State inline
+
+Note attachment File
+Note attachment Image
+
--- a/server/test/data/schema/Affaire.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/data/schema/Affaire.py	Mon May 04 13:09:48 2009 +0200
@@ -1,6 +1,6 @@
 from cubicweb.schema import format_constraint
 
-class Affaire(EntityType):
+class Affaire(WorkflowableEntityType):
     permissions = {
         'read':   ('managers', 
                    ERQLExpression('X owned_by U'), ERQLExpression('X concerne S?, S owned_by U')),
@@ -13,9 +13,6 @@
                  constraints=[SizeConstraint(16)])
     sujet = String(fulltextindexed=True,
                    constraints=[SizeConstraint(256)])
-    in_state = SubjectRelation('State', cardinality='1*',
-                               constraints=[RQLConstraint('O state_of ET, ET name "Affaire"')],
-                               description=_('account state'))
     descr_format = String(meta=True, internationalizable=True,
                                 default='text/rest', constraints=[format_constraint])
     descr = String(fulltextindexed=True,
@@ -23,10 +20,9 @@
 
     duration = Int()
     invoiced = Int()
-        
-    wf_info_for = ObjectRelation('TrInfo', cardinality='1*', composite='object')
+
     depends_on = SubjectRelation('Affaire')
-    require_permission = SubjectRelation('EPermission')
+    require_permission = SubjectRelation('CWPermission')
     
 class concerne(RelationType):
     permissions = {
--- a/server/test/data/schema/custom.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/data/schema/custom.py	Mon May 04 13:09:48 2009 +0200
@@ -30,5 +30,5 @@
     subject = ('Bookmark', 'Note')
     object = ('Bookmark', 'Note')
 
-_euser = import_schema('base').EUser
+_euser = import_schema('base').CWUser
 _euser.__relations__[0].fulltextindexed = True
--- a/server/test/data/schema/relations.rel	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/data/schema/relations.rel	Mon May 04 13:09:48 2009 +0200
@@ -1,13 +1,13 @@
 Personne travaille Societe
 Personne evaluee Note
-EUser evaluee Note
+CWUser evaluee Note
 Societe evaluee Note
 Personne concerne Affaire
 Affaire concerne Societe
 Affaire concerne Note
 
 Note ecrit_par Personne inline CONSTRAINT E concerns P, X version_of P
-Note ecrit_par EUser inline CONSTRAINT
+Note ecrit_par CWUser inline CONSTRAINT
 Personne connait Personne symetric
 
 # not inlined intentionaly
@@ -16,7 +16,7 @@
 Note inline1 Affaire inline
 Personne inline2 Affaire inline
 
-Note todo_by EUser
+Note todo_by CWUser
 Affaire todo_by Personne
 
 Folder see_also Folder
@@ -24,10 +24,10 @@
 
 Affaire documented_by Card
 
-EUser copain EUser
+CWUser copain CWUser
 
-Tag tags EUser
-Tag tags EGroup
+Tag tags CWUser
+Tag tags CWGroup
 Tag tags State
 Tag tags Note
 Tag tags Card
@@ -36,11 +36,11 @@
 Note filed_under Folder
 Affaire filed_under Folder
 
-Card require_permission EPermission
-Note require_permission EPermission
-Personne require_permission EPermission
+Card require_permission CWPermission
+Note require_permission CWPermission
+Personne require_permission CWPermission
 
-EPermission require_state State
+CWPermission require_state State
 
 Note migrated_from Note
 
--- a/server/test/runtests.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-from logilab.common.testlib import main
-
-if __name__ == '__main__':
-    import sys, os
-    main(os.path.dirname(sys.argv[0]) or '.')
--- a/server/test/unittest_hookhelper.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_hookhelper.py	Mon May 04 13:09:48 2009 +0200
@@ -59,7 +59,7 @@
             SendMailOp(session, msg=content, recipients=['test@logilab.fr'])
         self.hm.register_hook(in_state_changed,
                              'before_add_relation', 'in_state')
-        self.execute('INSERT EUser X: X login "paf", X upassword "wouf", X in_state S, X in_group G WHERE S name "activated", G name "users"')
+        self.execute('INSERT CWUser X: X login "paf", X upassword "wouf", X in_state S, X in_group G WHERE S name "activated", G name "users"')
         self.assertEquals(result, [None])
         searchedops = [op for op in self.session.pending_operations
                        if isinstance(op, SendMailOp)]
--- a/server/test/unittest_hooks.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_hooks.py	Mon May 04 13:09:48 2009 +0200
@@ -25,14 +25,14 @@
         
     def test_delete_internal_entities(self):
         self.assertRaises(RepositoryError, self.execute,
-                          'DELETE EEType X WHERE X name "EEType"')
+                          'DELETE CWEType X WHERE X name "CWEType"')
         self.assertRaises(RepositoryError, self.execute,
-                          'DELETE ERType X WHERE X name "relation_type"')
+                          'DELETE CWRType X WHERE X name "relation_type"')
         self.assertRaises(RepositoryError, self.execute,
-                          'DELETE EGroup X WHERE X name "owners"')
+                          'DELETE CWGroup X WHERE X name "owners"')
 
     def test_delete_required_relations_subject(self):
-        self.execute('INSERT EUser X: X login "toto", X upassword "hop", X in_group Y, X in_state S '
+        self.execute('INSERT CWUser X: X login "toto", X upassword "hop", X in_group Y, X in_state S '
                      'WHERE Y name "users", S name "activated"')
         self.commit()
         self.execute('DELETE X in_group Y WHERE X login "toto", Y name "users"')
@@ -47,17 +47,17 @@
     def test_static_vocabulary_check(self):
         self.assertRaises(ValidationError,
                           self.execute,
-                          'SET X composite "whatever" WHERE X from_entity FE, FE name "EUser", X relation_type RT, RT name "in_group"')
+                          'SET X composite "whatever" WHERE X from_entity FE, FE name "CWUser", X relation_type RT, RT name "in_group"')
     
     def test_missing_required_relations_subject_inline(self):
         # missing in_group relation 
-        self.execute('INSERT EUser X: X login "toto", X upassword "hop"')
+        self.execute('INSERT CWUser X: X login "toto", X upassword "hop"')
         self.assertRaises(ValidationError,
                           self.commit)
 
     def test_delete_if_singlecard1(self):
         self.assertEquals(self.repo.schema['in_state'].inlined, False)
-        ueid, = self.execute('INSERT EUser X: X login "toto", X upassword "hop", X in_group Y, X in_state S '
+        ueid, = self.execute('INSERT CWUser X: X login "toto", X upassword "hop", X in_group Y, X in_state S '
                              'WHERE Y name "users", S name "activated"')[0]
         self.commit()
         self.execute('SET X in_state S WHERE S name "deactivated", X eid %(x)s', {'x': ueid})
@@ -119,7 +119,7 @@
         self.assertEquals(rset.get_entity(0, 0).reverse_parts[0].messageid, '<2345>')
 
     def test_unsatisfied_constraints(self):
-        self.execute('INSERT ENFRDef X: X from_entity FE, X relation_type RT, X to_entity TE '
+        self.execute('INSERT CWRelation X: X from_entity FE, X relation_type RT, X to_entity TE '
                      'WHERE FE name "Affaire", RT name "concerne", TE name "String"')
         self.assertRaises(ValidationError,
                           self.commit)
@@ -160,7 +160,7 @@
         self.commit()
         cnxid = self.repo.connect(u'toto', 'hop')
         self.failIfEqual(cnxid, self.cnxid)
-        self.execute('DELETE EUser X WHERE X login "toto"')
+        self.execute('DELETE CWUser X WHERE X login "toto"')
         self.repo.execute(cnxid, 'State X')
         self.commit()
         self.assertRaises(BadConnectionId,
@@ -194,47 +194,47 @@
         self.commit()
         self.failIf(self.execute('Any X WHERE X created_by Y, X eid >= %(x)s', {'x': eid}))
         
-class EPropertyHooksTC(RepositoryBasedTC):
+class CWPropertyHooksTC(RepositoryBasedTC):
     
     def test_unexistant_eproperty(self):
         ex = self.assertRaises(ValidationError,
-                          self.execute, 'INSERT EProperty X: X pkey "bla.bla", X value "hop", X for_user U')
+                          self.execute, 'INSERT CWProperty X: X pkey "bla.bla", X value "hop", X for_user U')
         self.assertEquals(ex.errors, {'pkey': 'unknown property key'})
         ex = self.assertRaises(ValidationError,
-                          self.execute, 'INSERT EProperty X: X pkey "bla.bla", X value "hop"')
+                          self.execute, 'INSERT CWProperty X: X pkey "bla.bla", X value "hop"')
         self.assertEquals(ex.errors, {'pkey': 'unknown property key'})
         
     def test_site_wide_eproperty(self):
         ex = self.assertRaises(ValidationError,
-                               self.execute, 'INSERT EProperty X: X pkey "ui.site-title", X value "hop", X for_user U')
+                               self.execute, 'INSERT CWProperty X: X pkey "ui.site-title", X value "hop", X for_user U')
         self.assertEquals(ex.errors, {'for_user': "site-wide property can't be set for user"})
         
     def test_bad_type_eproperty(self):
         ex = self.assertRaises(ValidationError,
-                               self.execute, 'INSERT EProperty X: X pkey "ui.language", X value "hop", X for_user U')
+                               self.execute, 'INSERT CWProperty X: X pkey "ui.language", X value "hop", X for_user U')
         self.assertEquals(ex.errors, {'value': u'unauthorized value'})
         ex = self.assertRaises(ValidationError,
-                          self.execute, 'INSERT EProperty X: X pkey "ui.language", X value "hop"')
+                          self.execute, 'INSERT CWProperty X: X pkey "ui.language", X value "hop"')
         self.assertEquals(ex.errors, {'value': u'unauthorized value'})
         
         
 class SchemaHooksTC(RepositoryBasedTC):
         
     def test_duplicate_etype_error(self):
-        # check we can't add a EEType or ERType entity if it already exists one
+        # check we can't add a CWEType or CWRType entity if it already exists one
         # with the same name
         #
         # according to hook order, we'll get a repository or validation error
         self.assertRaises((ValidationError, RepositoryError),
-                          self.execute, 'INSERT EEType X: X name "Societe"')
+                          self.execute, 'INSERT CWEType X: X name "Societe"')
         self.assertRaises((ValidationError, RepositoryError),
-                          self.execute, 'INSERT ERType X: X name "in_group"')
+                          self.execute, 'INSERT CWRType X: X name "in_group"')
         
     def test_validation_unique_constraint(self):
         self.assertRaises(ValidationError,
-                          self.execute, 'INSERT EUser X: X login "admin"')
+                          self.execute, 'INSERT CWUser X: X login "admin"')
         try:
-            self.execute('INSERT EUser X: X login "admin"')
+            self.execute('INSERT CWUser X: X login "admin"')
         except ValidationError, ex:
             self.assertIsInstance(ex.entity, int)
             self.assertEquals(ex.errors, {'login': 'the value "admin" is already used, use another one'})
@@ -264,26 +264,26 @@
         self.failIf(schema.has_entity('Societe2'))
         self.failIf(schema.has_entity('concerne2'))
         # schema should be update on insertion (after commit)
-        self.execute('INSERT EEType X: X name "Societe2", X description "", X meta FALSE, X final FALSE')
-        self.execute('INSERT ERType X: X name "concerne2", X description "", X meta FALSE, X final FALSE, X symetric FALSE')
+        self.execute('INSERT CWEType X: X name "Societe2", X description "", X meta FALSE, X final FALSE')
+        self.execute('INSERT CWRType X: X name "concerne2", X description "", X meta FALSE, X final FALSE, X symetric FALSE')
         self.failIf(schema.has_entity('Societe2'))
         self.failIf(schema.has_entity('concerne2'))
-        self.execute('SET X read_permission G WHERE X is EEType, X name "Societe2", G is EGroup')
-        self.execute('SET X read_permission G WHERE X is ERType, X name "concerne2", G is EGroup')
-        self.execute('SET X add_permission G WHERE X is EEType, X name "Societe2", G is EGroup, G name "managers"')
-        self.execute('SET X add_permission G WHERE X is ERType, X name "concerne2", G is EGroup, G name "managers"')
-        self.execute('SET X delete_permission G WHERE X is EEType, X name "Societe2", G is EGroup, G name "owners"')
-        self.execute('SET X delete_permission G WHERE X is ERType, X name "concerne2", G is EGroup, G name "owners"')
+        self.execute('SET X read_permission G WHERE X is CWEType, X name "Societe2", G is CWGroup')
+        self.execute('SET X read_permission G WHERE X is CWRType, X name "concerne2", G is CWGroup')
+        self.execute('SET X add_permission G WHERE X is CWEType, X name "Societe2", G is CWGroup, G name "managers"')
+        self.execute('SET X add_permission G WHERE X is CWRType, X name "concerne2", G is CWGroup, G name "managers"')
+        self.execute('SET X delete_permission G WHERE X is CWEType, X name "Societe2", G is CWGroup, G name "owners"')
+        self.execute('SET X delete_permission G WHERE X is CWRType, X name "concerne2", G is CWGroup, G name "owners"')
         # have to commit before adding definition relations
         self.commit()
         self.failUnless(schema.has_entity('Societe2'))
         self.failUnless(schema.has_relation('concerne2'))
-        self.execute('INSERT EFRDef X: X cardinality "11", X defaultval "noname", X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
+        self.execute('INSERT CWAttribute X: X cardinality "11", X defaultval "noname", X indexed TRUE, X relation_type RT, X from_entity E, X to_entity F '
                      'WHERE RT name "nom", E name "Societe2", F name "String"')
         concerne2_rdef_eid = self.execute(
-            'INSERT ENFRDef X: X cardinality "**", X relation_type RT, X from_entity E, X to_entity E '
+            'INSERT CWRelation X: X cardinality "**", X relation_type RT, X from_entity E, X to_entity E '
             'WHERE RT name "concerne2", E name "Societe2"')[0][0]
-        self.execute('INSERT ENFRDef X: X cardinality "?*", X relation_type RT, X from_entity E, X to_entity C '
+        self.execute('INSERT CWRelation X: X cardinality "?*", X relation_type RT, X from_entity E, X to_entity C '
                      'WHERE RT name "comments", E name "Societe2", C name "Comment"')
         self.failIf('nom' in schema['Societe2'].subject_relations())
         self.failIf('concerne2' in schema['Societe2'].subject_relations())
@@ -299,17 +299,17 @@
         rset = self.execute('Any X WHERE X concerne2 Y')
         self.assertEquals(rset.rows, [[s2eid]])
         # check that when a relation definition is deleted, existing relations are deleted
-        self.execute('INSERT ENFRDef X: X cardinality "**", X relation_type RT, X from_entity E, X to_entity E '
+        self.execute('INSERT CWRelation X: X cardinality "**", X relation_type RT, X from_entity E, X to_entity E '
                      'WHERE RT name "concerne2", E name "Societe"')
         self.commit()
-        self.execute('DELETE ENFRDef X WHERE X eid %(x)s', {'x': concerne2_rdef_eid}, 'x')
+        self.execute('DELETE CWRelation X WHERE X eid %(x)s', {'x': concerne2_rdef_eid}, 'x')
         self.commit()
         self.failUnless('concerne2' in schema['Societe'].subject_relations())
         self.failIf('concerne2' in schema['Societe2'].subject_relations())
         self.failIf(self.execute('Any X WHERE X concerne2 Y'))
         # schema should be cleaned on delete (after commit)
-        self.execute('DELETE EEType X WHERE X name "Societe2"')
-        self.execute('DELETE ERType X WHERE X name "concerne2"')
+        self.execute('DELETE CWEType X WHERE X name "Societe2"')
+        self.execute('DELETE CWRType X WHERE X name "concerne2"')
         self.failUnless(self.index_exists('Societe2', 'nom'))
         self.failUnless(schema.has_entity('Societe2'))
         self.failUnless(schema.has_relation('concerne2'))
@@ -336,42 +336,42 @@
         
     def test_perms_synchronization_1(self):
         schema = self.repo.schema
-        self.assertEquals(schema['EUser'].get_groups('read'), set(('managers', 'users')))
-        self.failUnless(self.execute('Any X, Y WHERE X is EEType, X name "EUser", Y is EGroup, Y name "users"')[0])
-        self.execute('DELETE X read_permission Y WHERE X is EEType, X name "EUser", Y name "users"')
-        self.assertEquals(schema['EUser'].get_groups('read'), set(('managers', 'users', )))
+        self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', 'users')))
+        self.failUnless(self.execute('Any X, Y WHERE X is CWEType, X name "CWUser", Y is CWGroup, Y name "users"')[0])
+        self.execute('DELETE X read_permission Y WHERE X is CWEType, X name "CWUser", Y name "users"')
+        self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', 'users', )))
         self.commit()
-        self.assertEquals(schema['EUser'].get_groups('read'), set(('managers', )))
-        self.execute('SET X read_permission Y WHERE X is EEType, X name "EUser", Y name "users"')
+        self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', )))
+        self.execute('SET X read_permission Y WHERE X is CWEType, X name "CWUser", Y name "users"')
         self.commit()
-        self.assertEquals(schema['EUser'].get_groups('read'), set(('managers', 'users',)))
+        self.assertEquals(schema['CWUser'].get_groups('read'), set(('managers', 'users',)))
 
     def test_perms_synchronization_2(self):
         schema = self.repo.schema['in_group']
         self.assertEquals(schema.get_groups('read'), set(('managers', 'users', 'guests')))
-        self.execute('DELETE X read_permission Y WHERE X is ERType, X name "in_group", Y name "guests"')
+        self.execute('DELETE X read_permission Y WHERE X is CWRType, X name "in_group", Y name "guests"')
         self.assertEquals(schema.get_groups('read'), set(('managers', 'users', 'guests')))
         self.commit()
         self.assertEquals(schema.get_groups('read'), set(('managers', 'users')))
-        self.execute('SET X read_permission Y WHERE X is ERType, X name "in_group", Y name "guests"')
+        self.execute('SET X read_permission Y WHERE X is CWRType, X name "in_group", Y name "guests"')
         self.assertEquals(schema.get_groups('read'), set(('managers', 'users')))
         self.commit()
         self.assertEquals(schema.get_groups('read'), set(('managers', 'users', 'guests')))
 
     def test_nonregr_user_edit_itself(self):
         ueid = self.session.user.eid
-        groupeids = [eid for eid, in self.execute('EGroup G WHERE G name in ("managers", "users")')]
+        groupeids = [eid for eid, in self.execute('CWGroup G WHERE G name in ("managers", "users")')]
         self.execute('DELETE X in_group Y WHERE X eid %s' % ueid)
         self.execute('SET X surname "toto" WHERE X eid %s' % ueid)
         self.execute('SET X in_group Y WHERE X eid %s, Y name "managers"' % ueid)
         self.commit()
-        eeid = self.execute('Any X WHERE X is EEType, X name "EEType"')[0][0]
+        eeid = self.execute('Any X WHERE X is CWEType, X name "CWEType"')[0][0]
         self.execute('DELETE X read_permission Y WHERE X eid %s' % eeid)
         self.execute('SET X final FALSE WHERE X eid %s' % eeid)
         self.execute('SET X read_permission Y WHERE X eid %s, Y eid in (%s, %s)'
                      % (eeid, groupeids[0], groupeids[1]))
         self.commit()
-        self.execute('Any X WHERE X is EEType, X name "EEType"')
+        self.execute('Any X WHERE X is CWEType, X name "CWEType"')
 
     # schema modification hooks tests #########################################
     
@@ -432,7 +432,7 @@
         sqlcursor = self.session.pool['system']
         try:
             try:
-                self.execute('INSERT EConstraint X: X cstrtype CT, DEF constrained_by X '
+                self.execute('INSERT CWConstraint X: X cstrtype CT, DEF constrained_by X '
                              'WHERE CT name "UniqueConstraint", DEF relation_type RT, DEF from_entity E,'
                              'RT name "sujet", E name "Affaire"')
                 self.failIf(self.schema['Affaire'].has_unique_values('sujet'))
@@ -461,21 +461,21 @@
         RepositoryBasedTC.setUp(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 "EUser"')[0][0]
+        self.s_dummy = self.execute('INSERT State X: X name "dummy", X state_of E WHERE E name "CWUser"')[0][0]
         self.create_user('stduser')
         # give access to users group on the user's wf transitions
         # 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 "EUser"')
+        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 "EUser"')
+        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 EUser E: E login "x", E upassword "x", E in_group G '
+        ueid = self.execute('INSERT CWUser E: E login "x", E upassword "x", E in_group G '
                             'WHERE G name "users"')[0][0]
         self.failIf(self.execute('Any N WHERE S name N, X in_state S, X eid %(x)s',
                                  {'x' : ueid}))
@@ -488,11 +488,11 @@
         cnx = self.login('stduser')
         cu = cnx.cursor()
         self.assertRaises(ValidationError, cu.execute,
-                          'INSERT EUser X: X login "badaboum", X upassword %(pwd)s, '
+                          '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.execute('INSERT EUser X: X login "badaboum", X upassword %(pwd)s, '
+        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()
         
--- a/server/test/unittest_hooksmanager.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_hooksmanager.py	Mon May 04 13:09:48 2009 +0200
@@ -25,13 +25,13 @@
         self.assertRaises(AssertionError,
                           self.o.register_hook, self._hook, 'before_add_entiti')
         self.assertRaises(AssertionError,
-                          self.o.register_hook, self._hook, 'session_login', 'EEType')
+                          self.o.register_hook, self._hook, 'session_login', 'CWEType')
         self.assertRaises(AssertionError,
-                          self.o.register_hook, self._hook, 'session_logout', 'EEType')
+                          self.o.register_hook, self._hook, 'session_logout', 'CWEType')
         self.assertRaises(AssertionError,
-                          self.o.register_hook, self._hook, 'server_startup', 'EEType')
+                          self.o.register_hook, self._hook, 'server_startup', 'CWEType')
         self.assertRaises(AssertionError,
-                          self.o.register_hook, self._hook, 'server_shutdown', 'EEType')
+                          self.o.register_hook, self._hook, 'server_shutdown', 'CWEType')
         
     def test_register_hook1(self):
         self.o.register_hook(self._hook, 'before_add_entity')
--- a/server/test/unittest_ldapuser.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_ldapuser.py	Mon May 04 13:09:48 2009 +0200
@@ -24,7 +24,7 @@
         # no such user
         raise AuthenticationError()
     # don't check upassword !
-    return self.extid2eid(user['dn'], 'EUser', session)
+    return self.extid2eid(user['dn'], 'CWUser', session)
 
 
 
@@ -45,7 +45,7 @@
         # 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('EUser X')
+        rset = self.execute('CWUser X')
         self.commit()
         # check we get some users from ldap
         self.assert_(len(rset) > 1)
@@ -67,7 +67,7 @@
         
     def test_base(self):
         # check a known one
-        e = self.execute('EUser X WHERE X login "syt"').get_entity(0, 0)
+        e = self.execute('CWUser X WHERE X login "syt"').get_entity(0, 0)
         self.assertEquals(e.login, 'syt')
         e.complete()
         self.assertEquals(e.creation_date, None)
@@ -79,49 +79,49 @@
         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('EUser X WHERE X has_text "thenault"')
+        rset = self.execute('CWUser X WHERE X has_text "thenault"')
         self.assertEquals(rset.rows, [[e.eid]])
 
     def test_not(self):
-        eid = self.execute('EUser X WHERE X login "syt"')[0][0]
-        rset = self.execute('EUser X WHERE NOT X eid %s' % eid)
+        eid = self.execute('CWUser X WHERE X login "syt"')[0][0]
+        rset = self.execute('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('EUser X WHERE X login "syt"')[0][0]
-        aeid = self.execute('EUser X WHERE X login "adim"')[0][0]
-        rset = self.execute('EUser X, Y WHERE X login "syt", Y login "adim"')
+        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"')
         self.assertEquals(rset.rows, [[seid, aeid]])
         rset = self.execute('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('EUser X WHERE X login "syt"')[0][0]
-        aeid = self.execute('EUser X WHERE X login "adim"')[0][0]
+        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')
         self.assertEquals(rset.rows, [[aeid, 'adim'], [seid, 'syt']])
 
     def test_relations(self):
-        eid = self.execute('EUser X WHERE X login "syt"')[0][0]
-        rset = self.execute('Any X,E WHERE X is EUser, X login L, X primary_email E')
+        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')
         self.assert_(eid in (r[0] for r in rset))
-        rset = self.execute('Any X,L,E WHERE X is EUser, X login L, X primary_email E')
+        rset = self.execute('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 EUser')[0][0]
+        nbusers = self.execute('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('EUser X WHERE X login "syt"')[0][0]
+        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)
         self.assertEquals(rset[0][0], 'SYT')
 
     def test_unknown_attr(self):
-        eid = self.execute('EUser X WHERE X login "syt"')[0][0]
+        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, '
                             'X creation_date C, X modification_date M' % eid)
         self.assertEquals(rset[0][0], 'syt')
@@ -145,7 +145,7 @@
         # the related TrInfo has correct owner information
         self.execute('SET X in_group G WHERE X login "syt", G name "managers"')
         self.commit()
-        syt = self.execute('EUser X WHERE X login "syt"').get_entity(0, 0)
+        syt = self.execute('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')
@@ -153,7 +153,7 @@
         cu.execute('SET X in_state S WHERE X login "alf", S name "deactivated"')
         try:
             cnx.commit()
-            alf = self.execute('EUser X WHERE X login "alf"').get_entity(0, 0)
+            alf = self.execute('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')
@@ -177,8 +177,8 @@
         self.failUnless(self.execute('Any X,Y WHERE X login "syt", Y login "cochon"'))
         
     def test_exists1(self):
-        self.add_entity('EGroup', name=u'bougloup1')
-        self.add_entity('EGroup', name=u'bougloup2')
+        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%")')
@@ -210,9 +210,9 @@
         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"')
         # search for group name, login where
-        #   EUser copain with "comme" or "cochon" AND same login as the copain
+        #   CWUser copain with "comme" or "cochon" AND same login as the copain
         # OR
-        #   EUser in_state activated AND not copain with billy
+        #   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, '
@@ -243,13 +243,13 @@
                                               ['users', 'syt']])
         
     def test_cd_restriction(self):
-        rset = self.execute('EUser X WHERE X creation_date > "2009-02-01"')
+        rset = self.execute('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('EUser X')
-        rset = self.execute('(Any X WHERE X is State) UNION (Any X WHERE X is EUser)')
+        ueids = self.execute('CWUser X')
+        rset = self.execute('(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))
 
@@ -301,7 +301,7 @@
         
     def test_nonregr5(self):
         # original jpl query:
-        # Any X, NOW - CD, P WHERE P is Project, U interested_in P, U is EUser, U login "sthenault", X concerns P, X creation_date CD ORDERBY CD DESC LIMIT 5
+        # 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': })
         
@@ -309,7 +309,7 @@
         self.execute('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 EUser)',
+                     'OR (EXISTS(U in_group H, ME in_group H, NOT H name "users")), U login UL, U is CWUser)',
                      {'x': self.session.user.eid})
 
 
@@ -363,12 +363,12 @@
         RQLGeneratorTC.tearDown(self)
         
     def test_base(self):
-        rqlst = self._prepare('EUser X WHERE X login "toto"').children[0]
+        rqlst = self._prepare('CWUser X WHERE X login "toto"').children[0]
         self.assertEquals(self.o.generate(rqlst, 'X')[1],
                           '(&(objectClass=top)(objectClass=posixAccount)(uid=toto))')
         
     def test_kwargs(self):
-        rqlst = self._prepare('EUser X WHERE X login %(x)s').children[0]
+        rqlst = self._prepare('CWUser X WHERE X login %(x)s').children[0]
         self.o._args = {'x': "toto"}
         self.assertEquals(self.o.generate(rqlst, 'X')[1],
                           '(&(objectClass=top)(objectClass=posixAccount)(uid=toto))')
--- a/server/test/unittest_migractions.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_migractions.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """unit tests for module cubicweb.server.migractions
 """
 
-from mx.DateTime import DateTime, today
+from datetime import date
 
 from logilab.common.testlib import TestCase, unittest_main
 from cubicweb.devtools.apptest import RepositoryBasedTC, get_versions
@@ -80,12 +80,12 @@
         self.failUnless('mydate' in self.schema)
         self.assertEquals(self.schema['mydate'].subjects(), ('Note', ))
         self.assertEquals(self.schema['mydate'].objects(), ('Date', ))
-        testdate = DateTime(2005, 12, 13)
+        testdate = date(2005, 12, 13)
         eid1 = self.mh.rqlexec('INSERT Note N')[0][0]
         eid2 = self.mh.rqlexec('INSERT Note N: N mydate %(mydate)s', {'mydate' : testdate})[0][0]
         d1 = self.mh.rqlexec('Any D WHERE X eid %(x)s, X mydate D', {'x': eid1}, 'x')[0][0]
         d2 = self.mh.rqlexec('Any D WHERE X eid %(x)s, X mydate D', {'x': eid2}, 'x')[0][0]
-        self.assertEquals(d1, today())
+        self.assertEquals(d1, date.today())
         self.assertEquals(d2, testdate)
         self.mh.rollback()
             
@@ -127,9 +127,9 @@
         self.failIf('filed_under2' in self.schema)
         self.mh.cmd_add_entity_type('Folder2')
         self.failUnless('Folder2' in self.schema)
-        self.failUnless(self.execute('EEType X WHERE X name "Folder2"'))
+        self.failUnless(self.execute('CWEType X WHERE X name "Folder2"'))
         self.failUnless('filed_under2' in self.schema)
-        self.failUnless(self.execute('ERType X WHERE X name "filed_under2"'))
+        self.failUnless(self.execute('CWRType X WHERE X name "filed_under2"'))
         self.assertEquals(sorted(str(rs) for rs in self.schema['Folder2'].subject_relations()),
                           ['created_by', 'creation_date', 'description', 'description_format', 'eid',
                            'filed_under2', 'has_text', 'identity', 'is', 'is_instance_of',
@@ -154,7 +154,7 @@
         eschema = self.schema.eschema('Folder2')
         self.mh.cmd_drop_entity_type('Folder2')
         self.failIf('Folder2' in self.schema)
-        self.failIf(self.execute('EEType X WHERE X name "Folder2"'))
+        self.failIf(self.execute('CWEType X WHERE X name "Folder2"'))
         # test automatic workflow deletion
         self.failIf(self.execute('State X WHERE NOT X state_of ET'))
         self.failIf(self.execute('Transition X WHERE NOT X transition_of ET'))
@@ -179,7 +179,7 @@
     def test_add_relation_definition(self):
         self.mh.cmd_add_relation_definition('Societe', 'in_state', 'State')
         self.assertEquals(sorted(self.schema['in_state'].subjects()),
-                          ['Affaire', 'Division', 'EUser', 'Note', 'Societe', 'SubDivision'])
+                          ['Affaire', 'Division', 'CWUser', 'Note', 'Societe', 'SubDivision'])
         self.assertEquals(self.schema['in_state'].objects(), ('State',))
 
     def test_add_relation_definition_nortype(self):
@@ -210,7 +210,7 @@
         self.mh.cmd_drop_relation_definition('Personne', 'evaluee', 'Note')
         self.failUnless('evaluee' in self.schema)
         self.assertEquals(sorted(self.schema['evaluee'].subjects()),
-                          ['Division', 'EUser', 'Societe', 'SubDivision'])
+                          ['Division', 'CWUser', 'Societe', 'SubDivision'])
         self.assertEquals(sorted(self.schema['evaluee'].objects()),
                           ['Note'])
 
@@ -257,7 +257,7 @@
         migrschema['Personne'].description = 'blabla bla'
         migrschema['titre'].description = 'usually a title' 
         migrschema['titre']._rproperties[('Personne', 'String')]['description'] = 'title for this person'
-#         rinorderbefore = cursor.execute('Any O,N WHERE X is EFRDef, X relation_type RT, RT name N,'
+#         rinorderbefore = cursor.execute('Any O,N WHERE X is CWAttribute, X relation_type RT, RT name N,'
 #                                         'X from_entity FE, FE name "Personne",'
 #                                         'X ordernum O ORDERBY O')
 #         expected = [u'creation_date', u'modification_date', u'nom', u'prenom',
@@ -279,7 +279,7 @@
         # schema and so behaviour is undefined
         # "civility" is also skipped since it may have been added by
         # test_rename_attribut :o/
-        rinorder = [n for n, in cursor.execute('Any N ORDERBY O WHERE X is EFRDef, X relation_type RT, RT name N,'
+        rinorder = [n for n, in cursor.execute('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') if n not in ('sexe', 'description', 'civility')]
         expected = [u'nom', u'prenom', u'promo', u'ass', u'adel', u'titre',
@@ -338,14 +338,14 @@
         self.mh.rollback()
 
     def _erqlexpr_rset(self, action, ertype):
-        rql = 'RQLExpression X WHERE ET is EEType, ET %s_permission X, ET name %%(name)s' % action
+        rql = 'RQLExpression X WHERE ET is CWEType, ET %s_permission X, ET name %%(name)s' % action
         return self.mh.rqlcursor.execute(rql, {'name': ertype})
     def _erqlexpr_entity(self, action, ertype):
         rset = self._erqlexpr_rset(action, ertype)
         self.assertEquals(len(rset), 1)
         return rset.get_entity(0, 0)
     def _rrqlexpr_rset(self, action, ertype):
-        rql = 'RQLExpression X WHERE ET is ERType, ET %s_permission X, ET name %%(name)s' % action
+        rql = 'RQLExpression X WHERE ET is CWRType, ET %s_permission X, ET name %%(name)s' % action
         return self.mh.rqlcursor.execute(rql, {'name': ertype})
     def _rrqlexpr_entity(self, action, ertype):
         rset = self._rrqlexpr_rset(action, ertype)
@@ -355,14 +355,14 @@
     def test_set_size_constraint(self):
         # existing previous value
         try:
-            self.mh.cmd_set_size_constraint('EEType', 'name', 128)
+            self.mh.cmd_set_size_constraint('CWEType', 'name', 128)
         finally:
-            self.mh.cmd_set_size_constraint('EEType', 'name', 64)
+            self.mh.cmd_set_size_constraint('CWEType', 'name', 64)
         # non existing previous value
         try:
-            self.mh.cmd_set_size_constraint('EEType', 'description', 256)
+            self.mh.cmd_set_size_constraint('CWEType', 'description', 256)
         finally:
-            self.mh.cmd_set_size_constraint('EEType', 'description', None)
+            self.mh.cmd_set_size_constraint('CWEType', 'description', None)
 
     def test_add_remove_cube(self):
         cubes = set(self.config.cubes())
--- a/server/test/unittest_msplanner.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_msplanner.py	Mon May 04 13:09:48 2009 +0200
@@ -19,7 +19,7 @@
 
 class FakeUserROSource(AbstractSource):
     uri = 'zzz'
-    support_entities = {'EUser': False}
+    support_entities = {'CWUser': False}
     support_relations = {}
     def syntax_tree_search(self, *args, **kwargs):
         return []
@@ -38,10 +38,10 @@
 
 X_ALL_SOLS = sorted([{'X': 'Affaire'}, {'X': 'Basket'}, {'X': 'Bookmark'},
                      {'X': 'Card'}, {'X': 'Comment'}, {'X': 'Division'},
-                     {'X': 'ECache'}, {'X': 'EConstraint'}, {'X': 'EConstraintType'},
-                     {'X': 'EEType'}, {'X': 'EFRDef'}, {'X': 'EGroup'},
-                     {'X': 'ENFRDef'}, {'X': 'EPermission'}, {'X': 'EProperty'},
-                     {'X': 'ERType'}, {'X': 'EUser'}, {'X': 'Email'},
+                     {'X': 'CWCache'}, {'X': 'CWConstraint'}, {'X': 'CWConstraintType'},
+                     {'X': 'CWEType'}, {'X': 'CWAttribute'}, {'X': 'CWGroup'},
+                     {'X': 'CWRelation'}, {'X': 'CWPermission'}, {'X': 'CWProperty'},
+                     {'X': 'CWRType'}, {'X': 'CWUser'}, {'X': 'Email'},
                      {'X': 'EmailAddress'}, {'X': 'EmailPart'}, {'X': 'EmailThread'},
                      {'X': 'File'}, {'X': 'Folder'}, {'X': 'Image'},
                      {'X': 'Note'}, {'X': 'Personne'}, {'X': 'RQLExpression'},
@@ -61,7 +61,7 @@
     """test planner related feature on a 3-sources repository:
     
     * system source supporting everything
-    * ldap source supporting EUser
+    * ldap source supporting CWUser
     * rql source supporting Card
     """
     repo = repo
@@ -79,11 +79,11 @@
         # add access to type attribute so S can't be invariant
         affreadperms[-1] = ERQLExpression('X concerne S?, S owned_by U, S type "X"')
         self.schema['Affaire']._groups['read'] = tuple(affreadperms)
-        # hijack EUser security
-        userreadperms = list(self.schema['EUser']._groups['read'])
+        # hijack CWUser security
+        userreadperms = list(self.schema['CWUser']._groups['read'])
         self.prevrqlexpr_user = userreadperms[-1]
         userreadperms[-1] = ERQLExpression('X owned_by U')
-        self.schema['EUser']._groups['read'] = tuple(userreadperms)
+        self.schema['CWUser']._groups['read'] = tuple(userreadperms)
         
         self.sources = self.o._repo.sources
         self.system = self.sources[-1]
@@ -115,10 +115,10 @@
         clear_cache(self.schema['Affaire'], 'ERSchema_get_rqlexprs')
         
     def restore_orig_euser_security(self):
-        userreadperms = list(self.schema['EUser']._groups['read'])
+        userreadperms = list(self.schema['CWUser']._groups['read'])
         userreadperms[-1] = self.prevrqlexpr_user
-        self.schema['EUser']._groups['read'] = tuple(userreadperms)
-        clear_cache(self.schema['EUser'], 'ERSchema_get_rqlexprs')
+        self.schema['CWUser']._groups['read'] = tuple(userreadperms)
+        clear_cache(self.schema['CWUser'], 'ERSchema_get_rqlexprs')
 
                   
 class PartPlanInformationTC(BaseMSPlannerTC):
@@ -143,13 +143,13 @@
         
     def test_simple_system_only(self):
         """retrieve entities only supported by the system source"""
-        self._test('EGroup X',
+        self._test('CWGroup X',
                    {self.system: {'X': s[0]}}, False)
         
     def test_simple_system_ldap(self):
-        """retrieve EUser X from both sources and return concatenation of results
+        """retrieve CWUser X from both sources and return concatenation of results
         """
-        self._test('EUser X',
+        self._test('CWUser X',
                    {self.system: {'X': s[0]}, self.ldap: {'X': s[0]}}, False)
         
     def test_simple_system_rql(self):
@@ -159,41 +159,41 @@
                    {self.system: {'X': s[0]}, self.rql: {'X': s[0]}}, False)
         
     def test_simple_eid_specified(self):
-        """retrieve EUser X from system source (eid is specified, can locate the entity)
+        """retrieve CWUser X from system source (eid is specified, can locate the entity)
         """
         ueid = self.session.user.eid
         self._test('Any X,L WHERE X eid %(x)s, X login L', {'x': ueid},
                    {self.system: {'X': s[0]}}, False)
         
     def test_simple_eid_invariant(self):
-        """retrieve EUser X from system source (eid is specified, can locate the entity)
+        """retrieve CWUser X from system source (eid is specified, can locate the entity)
         """
         ueid = self.session.user.eid
         self._test('Any X WHERE X eid %(x)s', {'x': ueid},
                    {self.system: {'x': s[0]}}, False)
         
     def test_simple_invariant(self):
-        """retrieve EUser X from system source only (X is invariant and in_group not supported by ldap source)
+        """retrieve CWUser X from system source only (X is invariant and in_group not supported by ldap source)
         """
-        self._test('Any X WHERE X is EUser, X in_group G, G name "users"',
+        self._test('Any X WHERE X is CWUser, X in_group G, G name "users"',
                    {self.system: {'X': s[0], 'G': s[0], 'in_group': s[0]}}, False)
         
     def test_security_has_text(self):
-        """retrieve EUser X from system source only (has_text not supported by ldap source)
+        """retrieve CWUser X from system source only (has_text not supported by ldap source)
         """
-        # specify EUser instead of any since the way this test is written we aren't well dealing
+        # specify CWUser instead of any since the way this test is written we aren't well dealing
         # with ambigous query (eg only considering the first solution)
-        self._test('EUser X WHERE X has_text "bla"',
+        self._test('CWUser X WHERE X has_text "bla"',
                    {self.system: {'X': s[0]}}, False)
         
     def test_complex_base(self):
         """
-        1. retrieve Any X, L WHERE X is EUser, X login L from system and ldap sources, store
+        1. retrieve Any X, L WHERE X is CWUser, X login L from system and ldap sources, store
            concatenation of results into a temporary table
         2. return the result of Any X, L WHERE X is TMP, X login L, X in_group G,
            G name 'users' on the system source
         """
-        self._test('Any X,L WHERE X is EUser, X in_group G, X login L, G name "users"',
+        self._test('Any X,L WHERE X is CWUser, X in_group G, X login L, G name "users"',
                    {self.system: {'X': s[0], 'G': s[0], 'in_group': s[0]},
                     self.ldap : {'X': s[0]}}, True)
 
@@ -222,7 +222,7 @@
                     self.ldap : {'X': s[0]}}, True)
 
     def test_complex_ambigous(self):
-        """retrieve EUser X from system and ldap sources, Person X from system source only
+        """retrieve CWUser X from system and ldap sources, Person X from system source only
         """
         self._test('Any X,F WHERE X firstname F',
                    {self.system: {'X': s[0, 1]},
@@ -370,61 +370,61 @@
     def test_simple_system_only(self):
         """retrieve entities only supported by the system source
         """
-        self._test('EGroup X',
-                   [('OneFetchStep', [('Any X WHERE X is EGroup', [{'X': 'EGroup'}])],
+        self._test('CWGroup X',
+                   [('OneFetchStep', [('Any X WHERE X is CWGroup', [{'X': 'CWGroup'}])],
                      None, None, [self.system], {}, [])])
 
     def test_simple_system_only_limit(self):
         """retrieve entities only supported by the system source
         """
-        self._test('EGroup X LIMIT 10',
-                   [('OneFetchStep', [('Any X LIMIT 10 WHERE X is EGroup', [{'X': 'EGroup'}])],
+        self._test('CWGroup X LIMIT 10',
+                   [('OneFetchStep', [('Any X LIMIT 10 WHERE X is CWGroup', [{'X': 'CWGroup'}])],
                      10, None, [self.system], {}, [])])
 
     def test_simple_system_only_limit_offset(self):
         """retrieve entities only supported by the system source
         """
-        self._test('EGroup X LIMIT 10 OFFSET 10',
-                   [('OneFetchStep', [('Any X LIMIT 10 OFFSET 10 WHERE X is EGroup', [{'X': 'EGroup'}])],
+        self._test('CWGroup X LIMIT 10 OFFSET 10',
+                   [('OneFetchStep', [('Any X LIMIT 10 OFFSET 10 WHERE X is CWGroup', [{'X': 'CWGroup'}])],
                      10, 10, [self.system], {}, [])])
         
     def test_simple_system_ldap(self):
-        """retrieve EUser X from both sources and return concatenation of results
+        """retrieve CWUser X from both sources and return concatenation of results
         """
-        self._test('EUser X',
-                   [('OneFetchStep', [('Any X WHERE X is EUser', [{'X': 'EUser'}])],
+        self._test('CWUser X',
+                   [('OneFetchStep', [('Any X WHERE X is CWUser', [{'X': 'CWUser'}])],
                      None, None, [self.ldap, self.system], {}, [])])
         
     def test_simple_system_ldap_limit(self):
-        """retrieve EUser X from both sources and return concatenation of results
+        """retrieve CWUser X from both sources and return concatenation of results
         """
-        self._test('EUser X LIMIT 10',
-                   [('OneFetchStep', [('Any X LIMIT 10 WHERE X is EUser', [{'X': 'EUser'}])],
+        self._test('CWUser X LIMIT 10',
+                   [('OneFetchStep', [('Any X LIMIT 10 WHERE X is CWUser', [{'X': 'CWUser'}])],
                      10, None, [self.ldap, self.system], {}, [])])
 
     def test_simple_system_ldap_limit_offset(self):
-        """retrieve EUser X from both sources and return concatenation of results
+        """retrieve CWUser X from both sources and return concatenation of results
         """
-        self._test('EUser X LIMIT 10 OFFSET 10',
-                   [('OneFetchStep', [('Any X LIMIT 10 OFFSET 10 WHERE X is EUser', [{'X': 'EUser'}])],
+        self._test('CWUser X LIMIT 10 OFFSET 10',
+                   [('OneFetchStep', [('Any X LIMIT 10 OFFSET 10 WHERE X is CWUser', [{'X': 'CWUser'}])],
                      10, 10, [self.ldap, self.system], {}, [])])
 
     def test_simple_system_ldap_ordered_limit_offset(self):
-        """retrieve EUser X from both sources and return concatenation of results
+        """retrieve CWUser X from both sources and return concatenation of results
         """
-        self._test('EUser X ORDERBY X LIMIT 10 OFFSET 10',
+        self._test('CWUser X ORDERBY X LIMIT 10 OFFSET 10',
                    [('AggrStep', 'Any X ORDERBY X', 10, 10, 'table0', None, [
-                       ('FetchStep', [('Any X WHERE X is EUser', [{'X': 'EUser'}])],
+                       ('FetchStep', [('Any X WHERE X is CWUser', [{'X': 'CWUser'}])],
                         [self.ldap, self.system], {}, {'X': 'table0.C0'}, []),
                        ]),
                    ])
     def test_simple_system_ldap_aggregat(self):
-        """retrieve EUser X from both sources and return concatenation of results
+        """retrieve CWUser X from both sources and return concatenation of results
         """
         # COUNT(X) is kept in sub-step and transformed into SUM(X) in the AggrStep
-        self._test('Any COUNT(X) WHERE X is EUser',
+        self._test('Any COUNT(X) WHERE X is CWUser',
                    [('AggrStep', 'Any COUNT(X)', None, None, 'table0', None, [
-                       ('FetchStep', [('Any COUNT(X) WHERE X is EUser', [{'X': 'EUser'}])],
+                       ('FetchStep', [('Any COUNT(X) WHERE X is CWUser', [{'X': 'CWUser'}])],
                         [self.ldap, self.system], {}, {'COUNT(X)': 'table0.C0'}, []),
                        ]),
                    ])
@@ -437,16 +437,16 @@
                      None, None, [self.rql, self.system], {}, [])])
         
     def test_simple_eid_specified(self):
-        """retrieve EUser X from system source (eid is specified, can locate the entity)
+        """retrieve CWUser X from system source (eid is specified, can locate the entity)
         """
         ueid = self.session.user.eid
         self._test('Any X,L WHERE X eid %(x)s, X login L',
-                   [('OneFetchStep', [('Any X,L WHERE X eid %s, X login L'%ueid, [{'X': 'EUser', 'L': 'String'}])],
+                   [('OneFetchStep', [('Any X,L WHERE X eid %s, X login L'%ueid, [{'X': 'CWUser', 'L': 'String'}])],
                      None, None, [self.system], {}, [])],
                    {'x': ueid})
         
     def test_simple_eid_invariant(self):
-        """retrieve EUser X from system source (eid is specified, can locate the entity)
+        """retrieve CWUser X from system source (eid is specified, can locate the entity)
         """
         ueid = self.session.user.eid
         self._test('Any X WHERE X eid %(x)s',
@@ -455,43 +455,43 @@
                    {'x': ueid})
         
     def test_simple_invariant(self):
-        """retrieve EUser X from system source only (X is invariant and in_group not supported by ldap source)
+        """retrieve CWUser X from system source only (X is invariant and in_group not supported by ldap source)
         """
-        self._test('Any X WHERE X is EUser, X in_group G, G name "users"',
-                   [('OneFetchStep', [('Any X WHERE X is EUser, X in_group G, G name "users"',
-                                       [{'X': 'EUser', 'G': 'EGroup'}])],
+        self._test('Any X WHERE X is CWUser, X in_group G, G name "users"',
+                   [('OneFetchStep', [('Any X WHERE X is CWUser, X in_group G, G name "users"',
+                                       [{'X': 'CWUser', 'G': 'CWGroup'}])],
                      None, None, [self.system], {}, [])])
         
     def test_complex_base(self):
         """
-        1. retrieve Any X, L WHERE X is EUser, X login L from system and ldap sources, store
+        1. retrieve Any X, L WHERE X is CWUser, X login L from system and ldap sources, store
            concatenation of results into a temporary table
         2. return the result of Any X, L WHERE X is TMP, X login LX in_group G,
            G name 'users' on the system source
         """
-        self._test('Any X,L WHERE X is EUser, X in_group G, X login L, G name "users"',
-                   [('FetchStep', [('Any X,L WHERE X login L, X is EUser', [{'X': 'EUser', 'L': 'String'}])],
+        self._test('Any X,L WHERE X is CWUser, X in_group G, X login L, G name "users"',
+                   [('FetchStep', [('Any X,L WHERE X login L, X is CWUser', [{'X': 'CWUser', 'L': 'String'}])],
                      [self.ldap, self.system], None,
                      {'X': 'table0.C0', 'X.login': 'table0.C1', 'L': 'table0.C1'}, []),
-                    ('OneFetchStep', [('Any X,L WHERE X in_group G, X login L, G name "users", G is EGroup, X is EUser',
-                                       [{'X': 'EUser', 'L': 'String', 'G': 'EGroup'}])],
+                    ('OneFetchStep', [('Any X,L WHERE X in_group G, X login L, G name "users", G is CWGroup, X is CWUser',
+                                       [{'X': 'CWUser', 'L': 'String', 'G': 'CWGroup'}])],
                      None, None, [self.system],
                      {'X': 'table0.C0', 'X.login': 'table0.C1', 'L': 'table0.C1'}, [])
                     ])
 
     def test_complex_base_limit_offset(self):
         """
-        1. retrieve Any X, L WHERE X is EUser, X login L from system and ldap sources, store
+        1. retrieve Any X, L WHERE X is CWUser, X login L from system and ldap sources, store
            concatenation of results into a temporary table
         2. return the result of Any X, L WHERE X is TMP, X login LX in_group G,
            G name 'users' on the system source
         """
-        self._test('Any X,L LIMIT 10 OFFSET 10 WHERE X is EUser, X in_group G, X login L, G name "users"',
-                   [('FetchStep', [('Any X,L WHERE X login L, X is EUser', [{'X': 'EUser', 'L': 'String'}])],
+        self._test('Any X,L LIMIT 10 OFFSET 10 WHERE X is CWUser, X in_group G, X login L, G name "users"',
+                   [('FetchStep', [('Any X,L WHERE X login L, X is CWUser', [{'X': 'CWUser', 'L': 'String'}])],
                      [self.ldap, self.system], None,
                      {'X': 'table0.C0', 'X.login': 'table0.C1', 'L': 'table0.C1'}, []),
-                    ('OneFetchStep', [('Any X,L LIMIT 10 OFFSET 10 WHERE X in_group G, X login L, G name "users", G is EGroup, X is EUser',
-                                       [{'X': 'EUser', 'L': 'String', 'G': 'EGroup'}])],
+                    ('OneFetchStep', [('Any X,L LIMIT 10 OFFSET 10 WHERE X in_group G, X login L, G name "users", G is CWGroup, X is CWUser',
+                                       [{'X': 'CWUser', 'L': 'String', 'G': 'CWGroup'}])],
                      10, 10,
                      [self.system], {'X': 'table0.C0', 'X.login': 'table0.C1', 'L': 'table0.C1'}, [])
                     ])
@@ -499,8 +499,8 @@
     def test_complex_ordered(self):
         self._test('Any L ORDERBY L WHERE X login L',
                    [('AggrStep', 'Any L ORDERBY L', None, None, 'table0', None, 
-                     [('FetchStep', [('Any L WHERE X login L, X is EUser',
-                                      [{'X': 'EUser', 'L': 'String'}])],
+                     [('FetchStep', [('Any L WHERE X login L, X is CWUser',
+                                      [{'X': 'CWUser', 'L': 'String'}])],
                        [self.ldap, self.system], {}, {'X.login': 'table0.C0', 'L': 'table0.C0'}, []),
                       ])
                     ])
@@ -508,8 +508,8 @@
     def test_complex_ordered_limit_offset(self):
         self._test('Any L ORDERBY L LIMIT 10 OFFSET 10 WHERE X login L',
                    [('AggrStep', 'Any L ORDERBY L', 10, 10, 'table0', None, 
-                     [('FetchStep', [('Any L WHERE X login L, X is EUser',
-                                      [{'X': 'EUser', 'L': 'String'}])],
+                     [('FetchStep', [('Any L WHERE X login L, X is CWUser',
+                                      [{'X': 'CWUser', 'L': 'String'}])],
                        [self.ldap, self.system], {}, {'X.login': 'table0.C0', 'L': 'table0.C0'}, []),
                       ])
                     ])
@@ -526,13 +526,13 @@
         ueid = self.session.user.eid
         self._test('Any X,AA ORDERBY AA WHERE E eid %(x)s, E owned_by X, X modification_date AA',
                    [('FetchStep',
-                     [('Any X,AA WHERE X modification_date AA, X is EUser',
-                       [{'AA': 'Datetime', 'X': 'EUser'}])],
+                     [('Any X,AA WHERE X modification_date AA, X is CWUser',
+                       [{'AA': 'Datetime', 'X': 'CWUser'}])],
                      [self.ldap, self.system], None,
                      {'AA': 'table0.C1', 'X': 'table0.C0', 'X.modification_date': 'table0.C1'}, []),
                     ('OneFetchStep',
-                     [('Any X,AA ORDERBY AA WHERE 5 owned_by X, X modification_date AA, X is EUser',
-                       [{'AA': 'Datetime', 'X': 'EUser'}])],
+                     [('Any X,AA ORDERBY AA WHERE 5 owned_by X, X modification_date AA, X is CWUser',
+                       [{'AA': 'Datetime', 'X': 'CWUser'}])],
                      None, None, [self.system],
                      {'AA': 'table0.C1', 'X': 'table0.C0', 'X.modification_date': 'table0.C1'}, []),
                     ],
@@ -547,23 +547,23 @@
         """
         ueid = self.session.user.eid
         self._test('Any X,L,AA WHERE E eid %(x)s, E owned_by X, X login L, X modification_date AA',
-                   [('FetchStep', [('Any X,L,AA WHERE X login L, X modification_date AA, X is EUser',
-                                    [{'AA': 'Datetime', 'X': 'EUser', 'L': 'String'}])],
+                   [('FetchStep', [('Any X,L,AA WHERE X login L, X modification_date AA, X is CWUser',
+                                    [{'AA': 'Datetime', 'X': 'CWUser', 'L': 'String'}])],
                      [self.ldap, self.system], None,
                      {'AA': 'table0.C2', 'X': 'table0.C0', 'X.login': 'table0.C1', 'X.modification_date': 'table0.C2', 'L': 'table0.C1'}, []),
-                    ('OneFetchStep', [('Any X,L,AA WHERE %s owned_by X, X login L, X modification_date AA, X is EUser'%ueid,
-                                       [{'AA': 'Datetime', 'X': 'EUser', 'L': 'String'}])],
+                    ('OneFetchStep', [('Any X,L,AA WHERE %s owned_by X, X login L, X modification_date AA, X is CWUser'%ueid,
+                                       [{'AA': 'Datetime', 'X': 'CWUser', 'L': 'String'}])],
                      None, None, [self.system],
                      {'AA': 'table0.C2', 'X': 'table0.C0', 'X.login': 'table0.C1', 'X.modification_date': 'table0.C2', 'L': 'table0.C1'}, [])],
                    {'x': ueid})
 
     def test_complex_ambigous(self):
-        """retrieve EUser X from system and ldap sources, Person X from system source only
+        """retrieve CWUser X from system and ldap sources, Person X from system source only
         """
         self._test('Any X,F WHERE X firstname F',
                    [('UnionStep', None, None, [
-                       ('OneFetchStep', [('Any X,F WHERE X firstname F, X is EUser',
-                                          [{'X': 'EUser', 'F': 'String'}])],
+                       ('OneFetchStep', [('Any X,F WHERE X firstname F, X is CWUser',
+                                          [{'X': 'CWUser', 'F': 'String'}])],
                         None, None, [self.ldap, self.system], {}, []),
                        ('OneFetchStep', [('Any X,F WHERE X firstname F, X is Personne',
                                           [{'X': 'Personne', 'F': 'String'}])],
@@ -572,12 +572,12 @@
                     ])
 
     def test_complex_ambigous_limit_offset(self):
-        """retrieve EUser X from system and ldap sources, Person X from system source only
+        """retrieve CWUser X from system and ldap sources, Person X from system source only
         """
         self._test('Any X,F LIMIT 10 OFFSET 10 WHERE X firstname F',
                    [('UnionStep', 10, 10, [
-                       ('OneFetchStep', [('Any X,F WHERE X firstname F, X is EUser',
-                                          [{'X': 'EUser', 'F': 'String'}])],
+                       ('OneFetchStep', [('Any X,F WHERE X firstname F, X is CWUser',
+                                          [{'X': 'CWUser', 'F': 'String'}])],
                         None, None,
                         [self.ldap, self.system], {}, []),
                        ('OneFetchStep', [('Any X,F WHERE X firstname F, X is Personne',
@@ -588,14 +588,14 @@
 
     def test_complex_ambigous_ordered(self):
         """
-        1. retrieve EUser X from system and ldap sources, Person X from system source only, store
+        1. retrieve CWUser X from system and ldap sources, Person X from system source only, store
            each result in the same temp table
         2. return content of the table sorted
         """
         self._test('Any X,F ORDERBY F WHERE X firstname F',
                    [('AggrStep', 'Any X,F ORDERBY F', None, None, 'table0', None, 
-                     [('FetchStep', [('Any X,F WHERE X firstname F, X is EUser',
-                                      [{'X': 'EUser', 'F': 'String'}])],
+                     [('FetchStep', [('Any X,F WHERE X firstname F, X is CWUser',
+                                      [{'X': 'CWUser', 'F': 'String'}])],
                        [self.ldap, self.system], {},
                        {'X': 'table0.C0', 'X.firstname': 'table0.C1', 'F': 'table0.C1'}, []),
                       ('FetchStep', [('Any X,F WHERE X firstname F, X is Personne',
@@ -615,15 +615,15 @@
         ueid = self.session.user.eid
         self._test('Any X,Y WHERE X login "syt", Y login "adim"',
                    [('FetchStep',
-                     [('Any X WHERE X login "syt", X is EUser', [{'X': 'EUser'}])],
+                     [('Any X WHERE X login "syt", X is CWUser', [{'X': 'CWUser'}])],
                      [self.ldap, self.system], None,
                      {'X': 'table0.C0'}, []),
                     ('FetchStep',
-                     [('Any Y WHERE Y login "adim", Y is EUser', [{'Y': 'EUser'}])],
+                     [('Any Y WHERE Y login "adim", Y is CWUser', [{'Y': 'CWUser'}])],
                      [self.ldap, self.system], None,
                      {'Y': 'table1.C0'}, []),
                     ('OneFetchStep',
-                     [('Any X,Y WHERE X is EUser, Y is EUser', [{'X': 'EUser', 'Y': 'EUser'}])],
+                     [('Any X,Y WHERE X is CWUser, Y is CWUser', [{'X': 'CWUser', 'Y': 'CWUser'}])],
                      None, None, [self.system],
                      {'X': 'table0.C0', 'Y': 'table1.C0'}, [])
                     ], {'x': ueid})
@@ -638,13 +638,13 @@
         ueid = self.session.user.eid
         self._test('Any X,Y LIMIT 10 OFFSET 10 WHERE X login "syt", Y login "adim"',
                    [('FetchStep',
-                     [('Any X WHERE X login "syt", X is EUser', [{'X': 'EUser'}])],
+                     [('Any X WHERE X login "syt", X is CWUser', [{'X': 'CWUser'}])],
                      [self.ldap, self.system], None, {'X': 'table0.C0'}, []),
                     ('FetchStep',
-                     [('Any Y WHERE Y login "adim", Y is EUser', [{'Y': 'EUser'}])],
+                     [('Any Y WHERE Y login "adim", Y is CWUser', [{'Y': 'CWUser'}])],
                      [self.ldap, self.system], None, {'Y': 'table1.C0'}, []),
                     ('OneFetchStep',
-                     [('Any X,Y LIMIT 10 OFFSET 10 WHERE X is EUser, Y is EUser', [{'X': 'EUser', 'Y': 'EUser'}])],
+                     [('Any X,Y LIMIT 10 OFFSET 10 WHERE X is CWUser, Y is CWUser', [{'X': 'CWUser', 'Y': 'CWUser'}])],
                      10, 10, [self.system],
                      {'X': 'table0.C0', 'Y': 'table1.C0'}, [])
                     ], {'x': ueid})
@@ -683,7 +683,7 @@
         ueid = self.session.user.eid
         self._test('Any U WHERE WF wf_info_for X, X eid %(x)s, WF owned_by U?, WF from_state FS',
                    [('OneFetchStep', [('Any U WHERE WF wf_info_for 5, WF owned_by U?, WF from_state FS',
-                                       [{'WF': 'TrInfo', 'FS': 'State', 'U': 'EUser'}])],
+                                       [{'WF': 'TrInfo', 'FS': 'State', 'U': 'CWUser'}])],
                      None, None, [self.system], {}, [])],
                    {'x': ueid})
 
@@ -691,7 +691,7 @@
         ueid = self.session.user.eid
         self._test('Any U WHERE WF wf_info_for X, X eid %(x)s, WF owned_by U?, WF from_state FS',
                    [('OneFetchStep', [('Any U WHERE WF wf_info_for 5, WF owned_by U?, WF from_state FS',
-                                       [{'WF': 'TrInfo', 'FS': 'State', 'U': 'EUser'}])],
+                                       [{'WF': 'TrInfo', 'FS': 'State', 'U': 'CWUser'}])],
                      None, None, [self.system], {}, [])],
                    {'x': ueid})
 
@@ -701,16 +701,16 @@
                    [('FetchStep', [('Any X,T WHERE X title T, X is Card', [{'X': 'Card', 'T': 'String'}])],
                      [self.rql, self.system], None,
                      {'T': 'table0.C1', 'X': 'table0.C0', 'X.title': 'table0.C1'}, []),
-                    ('FetchStep', [('Any U WHERE U login "syt", U is EUser', [{'U': 'EUser'}])],
+                    ('FetchStep', [('Any U WHERE U login "syt", U is CWUser', [{'U': 'CWUser'}])],
                      [self.ldap, self.system], None,
                      {'U': 'table1.C0'}, []),
                     ('UnionStep', None, None, [
-                        ('OneFetchStep', [('Any X,T WHERE X owned_by U, X title T, U is EUser, X is IN(Bookmark, EmailThread)',
-                                           [{'T': 'String', 'U': 'EUser', 'X': 'Bookmark'},
-                                            {'T': 'String', 'U': 'EUser', 'X': 'EmailThread'}])],
+                        ('OneFetchStep', [('Any X,T WHERE X owned_by U, X title T, U is CWUser, X is IN(Bookmark, EmailThread)',
+                                           [{'T': 'String', 'U': 'CWUser', 'X': 'Bookmark'},
+                                            {'T': 'String', 'U': 'CWUser', 'X': 'EmailThread'}])],
                          None, None, [self.system], {'U': 'table1.C0'}, []),
-                        ('OneFetchStep', [('Any X,T WHERE X owned_by U, X title T, U is EUser, X is Card',
-                                           [{'X': 'Card', 'U': 'EUser', 'T': 'String'}])],
+                        ('OneFetchStep', [('Any X,T WHERE X owned_by U, X title T, U is CWUser, X is Card',
+                                           [{'X': 'Card', 'U': 'CWUser', 'T': 'String'}])],
                          None, None, [self.system],
                          {'X': 'table0.C0', 'X.title': 'table0.C1', 'T': 'table0.C1', 'U': 'table1.C0'}, []),
                         ]),
@@ -749,7 +749,7 @@
         # both system and rql support all variables, can be 
         self._test('Any X WHERE NOT X identity U, U eid %s' % self.session.user.eid,
                    [('OneFetchStep',
-                     [('Any X WHERE NOT X identity 5, X is EUser', [{'X': 'EUser'}])],
+                     [('Any X WHERE NOT X identity 5, X is CWUser', [{'X': 'CWUser'}])],
                      None, None,
                      [self.ldap, self.system], {}, [])
                     ])
@@ -761,11 +761,11 @@
                                     [{'Y': 'Note', 'A': 'State', 'R': 'String'}])],
                      [self.rql, self.system], None,
                      {'A': 'table0.C0', 'R': 'table0.C1', 'Y.type': 'table0.C1'}, []),
-                    ('FetchStep', [('Any X,R WHERE X login R, X is EUser', [{'X': 'EUser', 'R': 'String'}])],
+                    ('FetchStep', [('Any X,R WHERE X login R, X is CWUser', [{'X': 'CWUser', 'R': 'String'}])],
                      [self.ldap, self.system], None,
                      {'X': 'table1.C0', 'X.login': 'table1.C1', 'R': 'table1.C1'}, []),
-                    ('OneFetchStep', [('Any X,MAX(R) GROUPBY X WHERE X in_state S, X login R, NOT EXISTS(Y type R, S identity A, A is State, Y is Note), S is State, X is EUser',
-                                       [{'Y': 'Note', 'X': 'EUser', 'S': 'State', 'R': 'String', 'A': 'State'}])],
+                    ('OneFetchStep', [('Any X,MAX(R) GROUPBY X WHERE X in_state S, X login R, NOT EXISTS(Y type R, S identity A, A is State, Y is Note), S is State, X is CWUser',
+                                       [{'Y': 'Note', 'X': 'CWUser', 'S': 'State', 'R': 'String', 'A': 'State'}])],
                      None, None, [self.system],
                      {'A': 'table0.C0', 'X': 'table1.C0', 'X.login': 'table1.C1', 'R': 'table1.C1', 'Y.type': 'table0.C1'}, [])
                     ])
@@ -784,8 +784,8 @@
                       ('OneFetchStep',
                        [('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is Basket',
                          [{'X': 'Basket'}]),
-                        ('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is EUser',
-                         [{'X': 'EUser'}]),
+                        ('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is CWUser',
+                         [{'X': 'CWUser'}]),
                         ('Any X WHERE X has_text "bla", X is IN(Card, Comment, Division, Email, EmailThread, File, Folder, Image, Note, Personne, Societe, State, SubDivision, Tag, Transition)',
                          [{'X': 'Card'}, {'X': 'Comment'}, {'X': 'Division'},
                           {'X': 'Email'}, {'X': 'EmailThread'}, {'X': 'File'},
@@ -810,8 +810,8 @@
                          ('FetchStep',
                           [('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is Basket',
                          [{'X': 'Basket'}]),
-                        ('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is EUser',
-                         [{'X': 'EUser'}]),
+                        ('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is CWUser',
+                         [{'X': 'CWUser'}]),
                         ('Any X WHERE X has_text "bla", X is IN(Card, Comment, Division, Email, EmailThread, File, Folder, Image, Note, Personne, Societe, State, SubDivision, Tag, Transition)',
                          [{'X': 'Card'}, {'X': 'Comment'}, {'X': 'Division'},
                           {'X': 'Email'}, {'X': 'EmailThread'}, {'X': 'File'},
@@ -823,7 +823,7 @@
                     ('OneFetchStep',
                      [('Any X LIMIT 10 OFFSET 10',
                        [{'X': 'Affaire'}, {'X': 'Basket'}, {'X': 'Card'},
-                        {'X': 'Comment'}, {'X': 'Division'}, {'X': 'EUser'},
+                        {'X': 'Comment'}, {'X': 'Division'}, {'X': 'CWUser'},
                         {'X': 'Email'}, {'X': 'EmailThread'}, {'X': 'File'},
                         {'X': 'Folder'}, {'X': 'Image'}, {'X': 'Note'},
                         {'X': 'Personne'}, {'X': 'Societe'}, {'X': 'State'},
@@ -837,20 +837,20 @@
         self.session = self._user_session()[1]
         self._test('Any X WHERE X login "bla"',
                    [('FetchStep',
-                     [('Any X WHERE X login "bla", X is EUser', [{'X': 'EUser'}])],
+                     [('Any X WHERE X login "bla", X is CWUser', [{'X': 'CWUser'}])],
                      [self.ldap, self.system], None, {'X': 'table0.C0'}, []),
                     ('OneFetchStep',
-                     [('Any X WHERE EXISTS(X owned_by 5), X is EUser', [{'X': 'EUser'}])],
+                     [('Any X WHERE EXISTS(X owned_by 5), X is CWUser', [{'X': 'CWUser'}])],
                      None, None, [self.system], {'X': 'table0.C0'}, [])])
                 
     def test_security_complex_has_text(self):
         # use a guest user
         self.session = self._user_session()[1]
         self._test('Any X WHERE X has_text "bla", X firstname "bla"',
-                   [('FetchStep', [('Any X WHERE X firstname "bla", X is EUser', [{'X': 'EUser'}])],
+                   [('FetchStep', [('Any X WHERE X firstname "bla", X is CWUser', [{'X': 'CWUser'}])],
                      [self.ldap, self.system], None, {'X': 'table0.C0'}, []),
                     ('UnionStep', None, None, [
-                        ('OneFetchStep', [('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is EUser', [{'X': 'EUser'}])],
+                        ('OneFetchStep', [('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is CWUser', [{'X': 'CWUser'}])],
                          None, None, [self.system], {'X': 'table0.C0'}, []),
                         ('OneFetchStep', [('Any X WHERE X has_text "bla", X firstname "bla", X is Personne', [{'X': 'Personne'}])],
                          None, None, [self.system], {}, []),
@@ -861,16 +861,16 @@
         # use a guest user
         self.session = self._user_session()[1]
         self._test('Any X LIMIT 10 OFFSET 10 WHERE X has_text "bla", X firstname "bla"',
-                   [('FetchStep', [('Any X WHERE X firstname "bla", X is EUser', [{'X': 'EUser'}])],
+                   [('FetchStep', [('Any X WHERE X firstname "bla", X is CWUser', [{'X': 'CWUser'}])],
                      [self.ldap, self.system], None, {'X': 'table1.C0'}, []),
                     ('UnionFetchStep', [
-                        ('FetchStep', [('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is EUser', [{'X': 'EUser'}])],
+                        ('FetchStep', [('Any X WHERE X has_text "bla", EXISTS(X owned_by 5), X is CWUser', [{'X': 'CWUser'}])],
                          [self.system], {'X': 'table1.C0'}, {'X': 'table0.C0'}, []),
                         ('FetchStep', [('Any X WHERE X has_text "bla", X firstname "bla", X is Personne', [{'X': 'Personne'}])],
                          [self.system], {}, {'X': 'table0.C0'}, []),
                         ]),
                      ('OneFetchStep',
-                      [('Any X LIMIT 10 OFFSET 10', [{'X': 'EUser'}, {'X': 'Personne'}])],
+                      [('Any X LIMIT 10 OFFSET 10', [{'X': 'CWUser'}, {'X': 'Personne'}])],
                       10, 10, [self.system], {'X': 'table0.C0'}, [])
                     ])
 
@@ -880,7 +880,7 @@
         self._test('Any MAX(X)',
                    [('FetchStep', [('Any E WHERE E type "X", E is Note', [{'E': 'Note'}])],
                      [self.rql, self.system],  None, {'E': 'table1.C0'}, []), 
-                    ('FetchStep', [('Any X WHERE X is EUser', [{'X': 'EUser'}])],
+                    ('FetchStep', [('Any X WHERE X is CWUser', [{'X': 'CWUser'}])],
                      [self.ldap, self.system], None, {'X': 'table2.C0'}, []),
                     ('UnionFetchStep', [
                         ('FetchStep', [('Any X WHERE EXISTS(X owned_by 5), X is Basket', [{'X': 'Basket'}])],
@@ -890,19 +890,19 @@
                                           [{'X': 'Card'}, {'X': 'Note'}, {'X': 'State'}])],
                            [self.rql, self.system], {}, {'X': 'table0.C0'}, []),
                           ('FetchStep',
-                           [('Any X WHERE X is IN(Bookmark, Comment, Division, ECache, EConstraint, EConstraintType, EEType, EFRDef, EGroup, ENFRDef, EPermission, EProperty, ERType, Email, EmailAddress, EmailPart, EmailThread, File, Folder, Image, Personne, RQLExpression, Societe, SubDivision, Tag, TrInfo, Transition)',
+                           [('Any X WHERE X is IN(Bookmark, Comment, Division, CWCache, CWConstraint, CWConstraintType, CWEType, CWAttribute, CWGroup, CWRelation, CWPermission, CWProperty, CWRType, Email, EmailAddress, EmailPart, EmailThread, File, Folder, Image, Personne, RQLExpression, Societe, SubDivision, Tag, TrInfo, Transition)',
                              sorted([{'X': 'Bookmark'}, {'X': 'Comment'}, {'X': 'Division'},
-                                      {'X': 'ECache'}, {'X': 'EConstraint'}, {'X': 'EConstraintType'},
-                                      {'X': 'EEType'}, {'X': 'EFRDef'}, {'X': 'EGroup'},
-                                      {'X': 'ENFRDef'}, {'X': 'EPermission'}, {'X': 'EProperty'},
-                                      {'X': 'ERType'}, {'X': 'Email'}, {'X': 'EmailAddress'},
+                                      {'X': 'CWCache'}, {'X': 'CWConstraint'}, {'X': 'CWConstraintType'},
+                                      {'X': 'CWEType'}, {'X': 'CWAttribute'}, {'X': 'CWGroup'},
+                                      {'X': 'CWRelation'}, {'X': 'CWPermission'}, {'X': 'CWProperty'},
+                                      {'X': 'CWRType'}, {'X': 'Email'}, {'X': 'EmailAddress'},
                                       {'X': 'EmailPart'}, {'X': 'EmailThread'}, {'X': 'File'},
                                       {'X': 'Folder'}, {'X': 'Image'}, {'X': 'Personne'},
                                       {'X': 'RQLExpression'}, {'X': 'Societe'}, {'X': 'SubDivision'},
                                       {'X': 'Tag'}, {'X': 'TrInfo'}, {'X': 'Transition'}]))],
                            [self.system], {}, {'X': 'table0.C0'}, []),
                           ]),
-                        ('FetchStep', [('Any X WHERE EXISTS(X owned_by 5), X is EUser', [{'X': 'EUser'}])],
+                        ('FetchStep', [('Any X WHERE EXISTS(X owned_by 5), X is CWUser', [{'X': 'CWUser'}])],
                          [self.system], {'X': 'table2.C0'}, {'X': 'table0.C0'}, []),
                         ('FetchStep', [('Any X WHERE (EXISTS(X owned_by 5)) OR ((((EXISTS(D concerne C?, C owned_by 5, C type "X", X identity D, C is Division, D is Affaire)) OR (EXISTS(H concerne G?, G owned_by 5, G type "X", X identity H, G is SubDivision, H is Affaire))) OR (EXISTS(I concerne F?, F owned_by 5, F type "X", X identity I, F is Societe, I is Affaire))) OR (EXISTS(J concerne E?, E owned_by 5, X identity J, E is Note, J is Affaire))), X is Affaire',
                                         [{'C': 'Division', 'E': 'Note', 'D': 'Affaire', 'G': 'SubDivision', 'F': 'Societe', 'I': 'Affaire', 'H': 'Affaire', 'J': 'Affaire', 'X': 'Affaire'}])],
@@ -921,67 +921,67 @@
                      [self.rql, self.system], None, {'X': 'table1.C0'}, []),
                     ('FetchStep', [('Any E WHERE E type "X", E is Note', [{'E': 'Note'}])],
                      [self.rql, self.system],  None, {'E': 'table2.C0'}, []),
-                    ('FetchStep', [('Any X WHERE X is EUser', [{'X': 'EUser'}])],
+                    ('FetchStep', [('Any X WHERE X is CWUser', [{'X': 'CWUser'}])],
                      [self.ldap, self.system], None, {'X': 'table3.C0'}, []),
                     ('UnionFetchStep',
-                     [('FetchStep', [('Any ET,X WHERE X is ET, EXISTS(X owned_by 5), ET is EEType, X is Basket',
-                                      [{'ET': 'EEType', 'X': 'Basket'}])],
+                     [('FetchStep', [('Any ET,X WHERE X is ET, EXISTS(X owned_by 5), ET is CWEType, X is Basket',
+                                      [{'ET': 'CWEType', 'X': 'Basket'}])],
                        [self.system], {}, {'ET': 'table0.C0', 'X': 'table0.C1'}, []),
-                      ('FetchStep', [('Any ET,X WHERE X is ET, (EXISTS(X owned_by 5)) OR ((((EXISTS(D concerne C?, C owned_by 5, C type "X", X identity D, C is Division, D is Affaire)) OR (EXISTS(H concerne G?, G owned_by 5, G type "X", X identity H, G is SubDivision, H is Affaire))) OR (EXISTS(I concerne F?, F owned_by 5, F type "X", X identity I, F is Societe, I is Affaire))) OR (EXISTS(J concerne E?, E owned_by 5, X identity J, E is Note, J is Affaire))), ET is EEType, X is Affaire',
+                      ('FetchStep', [('Any ET,X WHERE X is ET, (EXISTS(X owned_by 5)) OR ((((EXISTS(D concerne C?, C owned_by 5, C type "X", X identity D, C is Division, D is Affaire)) OR (EXISTS(H concerne G?, G owned_by 5, G type "X", X identity H, G is SubDivision, H is Affaire))) OR (EXISTS(I concerne F?, F owned_by 5, F type "X", X identity I, F is Societe, I is Affaire))) OR (EXISTS(J concerne E?, E owned_by 5, X identity J, E is Note, J is Affaire))), ET is CWEType, X is Affaire',
                                       [{'C': 'Division', 'E': 'Note', 'D': 'Affaire',
                                         'G': 'SubDivision', 'F': 'Societe', 'I': 'Affaire',
                                         'H': 'Affaire', 'J': 'Affaire', 'X': 'Affaire',
-                                        'ET': 'EEType'}])],
+                                        'ET': 'CWEType'}])],
                        [self.system], {'E': 'table2.C0'}, {'ET': 'table0.C0', 'X': 'table0.C1'},
                        []),
-                      ('FetchStep', [('Any ET,X WHERE X is ET, EXISTS(X owned_by 5), ET is EEType, X is EUser',
-                                      [{'ET': 'EEType', 'X': 'EUser'}])],
+                      ('FetchStep', [('Any ET,X WHERE X is ET, EXISTS(X owned_by 5), ET is CWEType, X is CWUser',
+                                      [{'ET': 'CWEType', 'X': 'CWUser'}])],
                        [self.system], {'X': 'table3.C0'}, {'ET': 'table0.C0', 'X': 'table0.C1'}, []),
                       # extra UnionFetchStep could be avoided but has no cost, so don't care
                       ('UnionFetchStep',
-                       [('FetchStep', [('Any ET,X WHERE X is ET, ET is EEType, X is IN(Bookmark, Comment, Division, ECache, EConstraint, EConstraintType, EEType, EFRDef, EGroup, ENFRDef, EPermission, EProperty, ERType, Email, EmailAddress, EmailPart, EmailThread, File, Folder, Image, Personne, RQLExpression, Societe, SubDivision, Tag, TrInfo, Transition)',
-                                        [{'X': 'Bookmark', 'ET': 'EEType'}, {'X': 'Comment', 'ET': 'EEType'},
-                                         {'X': 'Division', 'ET': 'EEType'}, {'X': 'ECache', 'ET': 'EEType'},
-                                         {'X': 'EConstraint', 'ET': 'EEType'}, {'X': 'EConstraintType', 'ET': 'EEType'},
-                                         {'X': 'EEType', 'ET': 'EEType'}, {'X': 'EFRDef', 'ET': 'EEType'},
-                                         {'X': 'EGroup', 'ET': 'EEType'}, {'X': 'ENFRDef', 'ET': 'EEType'},
-                                         {'X': 'EPermission', 'ET': 'EEType'}, {'X': 'EProperty', 'ET': 'EEType'},
-                                         {'X': 'ERType', 'ET': 'EEType'}, {'X': 'Email', 'ET': 'EEType'},
-                                         {'X': 'EmailAddress', 'ET': 'EEType'}, {'X': 'EmailPart', 'ET': 'EEType'},
-                                         {'X': 'EmailThread', 'ET': 'EEType'}, {'X': 'File', 'ET': 'EEType'},
-                                         {'X': 'Folder', 'ET': 'EEType'}, {'X': 'Image', 'ET': 'EEType'},
-                                         {'X': 'Personne', 'ET': 'EEType'}, {'X': 'RQLExpression', 'ET': 'EEType'},
-                                         {'X': 'Societe', 'ET': 'EEType'}, {'X': 'SubDivision', 'ET': 'EEType'},
-                                         {'X': 'Tag', 'ET': 'EEType'}, {'X': 'TrInfo', 'ET': 'EEType'},
-                                         {'X': 'Transition', 'ET': 'EEType'}])],
+                       [('FetchStep', [('Any ET,X WHERE X is ET, ET is CWEType, X is IN(Bookmark, Comment, Division, CWCache, CWConstraint, CWConstraintType, CWEType, CWAttribute, CWGroup, CWRelation, CWPermission, CWProperty, CWRType, Email, EmailAddress, EmailPart, EmailThread, File, Folder, Image, Personne, RQLExpression, Societe, SubDivision, Tag, TrInfo, Transition)',
+                                        [{'X': 'Bookmark', 'ET': 'CWEType'}, {'X': 'Comment', 'ET': 'CWEType'},
+                                         {'X': 'Division', 'ET': 'CWEType'}, {'X': 'CWCache', 'ET': 'CWEType'},
+                                         {'X': 'CWConstraint', 'ET': 'CWEType'}, {'X': 'CWConstraintType', 'ET': 'CWEType'},
+                                         {'X': 'CWEType', 'ET': 'CWEType'}, {'X': 'CWAttribute', 'ET': 'CWEType'},
+                                         {'X': 'CWGroup', 'ET': 'CWEType'}, {'X': 'CWRelation', 'ET': 'CWEType'},
+                                         {'X': 'CWPermission', 'ET': 'CWEType'}, {'X': 'CWProperty', 'ET': 'CWEType'},
+                                         {'X': 'CWRType', 'ET': 'CWEType'}, {'X': 'Email', 'ET': 'CWEType'},
+                                         {'X': 'EmailAddress', 'ET': 'CWEType'}, {'X': 'EmailPart', 'ET': 'CWEType'},
+                                         {'X': 'EmailThread', 'ET': 'CWEType'}, {'X': 'File', 'ET': 'CWEType'},
+                                         {'X': 'Folder', 'ET': 'CWEType'}, {'X': 'Image', 'ET': 'CWEType'},
+                                         {'X': 'Personne', 'ET': 'CWEType'}, {'X': 'RQLExpression', 'ET': 'CWEType'},
+                                         {'X': 'Societe', 'ET': 'CWEType'}, {'X': 'SubDivision', 'ET': 'CWEType'},
+                                         {'X': 'Tag', 'ET': 'CWEType'}, {'X': 'TrInfo', 'ET': 'CWEType'},
+                                         {'X': 'Transition', 'ET': 'CWEType'}])],
                          [self.system], {}, {'ET': 'table0.C0', 'X': 'table0.C1'}, []),
                         ('FetchStep',
-                         [('Any ET,X WHERE X is ET, ET is EEType, X is IN(Card, Note, State)',
-                           [{'ET': 'EEType', 'X': 'Card'},
-                            {'ET': 'EEType', 'X': 'Note'},
-                            {'ET': 'EEType', 'X': 'State'}])],
+                         [('Any ET,X WHERE X is ET, ET is CWEType, X is IN(Card, Note, State)',
+                           [{'ET': 'CWEType', 'X': 'Card'},
+                            {'ET': 'CWEType', 'X': 'Note'},
+                            {'ET': 'CWEType', 'X': 'State'}])],
                          [self.system], {'X': 'table1.C0'}, {'ET': 'table0.C0', 'X': 'table0.C1'}, []),
                         ]),
                     ]),
                     ('OneFetchStep',
                      [('Any ET,COUNT(X) GROUPBY ET ORDERBY ET',
-                       sorted([{'ET': 'EEType', 'X': 'Affaire'}, {'ET': 'EEType', 'X': 'Basket'},
-                               {'ET': 'EEType', 'X': 'Bookmark'}, {'ET': 'EEType', 'X': 'Card'},
-                               {'ET': 'EEType', 'X': 'Comment'}, {'ET': 'EEType', 'X': 'Division'},
-                               {'ET': 'EEType', 'X': 'ECache'}, {'ET': 'EEType', 'X': 'EConstraint'},
-                               {'ET': 'EEType', 'X': 'EConstraintType'}, {'ET': 'EEType', 'X': 'EEType'},
-                               {'ET': 'EEType', 'X': 'EFRDef'}, {'ET': 'EEType', 'X': 'EGroup'},
-                               {'ET': 'EEType', 'X': 'ENFRDef'}, {'ET': 'EEType', 'X': 'EPermission'},
-                               {'ET': 'EEType', 'X': 'EProperty'}, {'ET': 'EEType', 'X': 'ERType'},
-                               {'ET': 'EEType', 'X': 'EUser'}, {'ET': 'EEType', 'X': 'Email'},
-                               {'ET': 'EEType', 'X': 'EmailAddress'}, {'ET': 'EEType', 'X': 'EmailPart'},
-                               {'ET': 'EEType', 'X': 'EmailThread'}, {'ET': 'EEType', 'X': 'File'},
-                               {'ET': 'EEType', 'X': 'Folder'}, {'ET': 'EEType', 'X': 'Image'},
-                               {'ET': 'EEType', 'X': 'Note'}, {'ET': 'EEType', 'X': 'Personne'},
-                               {'ET': 'EEType', 'X': 'RQLExpression'}, {'ET': 'EEType', 'X': 'Societe'},
-                               {'ET': 'EEType', 'X': 'State'}, {'ET': 'EEType', 'X': 'SubDivision'},
-                               {'ET': 'EEType', 'X': 'Tag'}, {'ET': 'EEType', 'X': 'TrInfo'},
-                               {'ET': 'EEType', 'X': 'Transition'}]))],
+                       sorted([{'ET': 'CWEType', 'X': 'Affaire'}, {'ET': 'CWEType', 'X': 'Basket'},
+                               {'ET': 'CWEType', 'X': 'Bookmark'}, {'ET': 'CWEType', 'X': 'Card'},
+                               {'ET': 'CWEType', 'X': 'Comment'}, {'ET': 'CWEType', 'X': 'Division'},
+                               {'ET': 'CWEType', 'X': 'CWCache'}, {'ET': 'CWEType', 'X': 'CWConstraint'},
+                               {'ET': 'CWEType', 'X': 'CWConstraintType'}, {'ET': 'CWEType', 'X': 'CWEType'},
+                               {'ET': 'CWEType', 'X': 'CWAttribute'}, {'ET': 'CWEType', 'X': 'CWGroup'},
+                               {'ET': 'CWEType', 'X': 'CWRelation'}, {'ET': 'CWEType', 'X': 'CWPermission'},
+                               {'ET': 'CWEType', 'X': 'CWProperty'}, {'ET': 'CWEType', 'X': 'CWRType'},
+                               {'ET': 'CWEType', 'X': 'CWUser'}, {'ET': 'CWEType', 'X': 'Email'},
+                               {'ET': 'CWEType', 'X': 'EmailAddress'}, {'ET': 'CWEType', 'X': 'EmailPart'},
+                               {'ET': 'CWEType', 'X': 'EmailThread'}, {'ET': 'CWEType', 'X': 'File'},
+                               {'ET': 'CWEType', 'X': 'Folder'}, {'ET': 'CWEType', 'X': 'Image'},
+                               {'ET': 'CWEType', 'X': 'Note'}, {'ET': 'CWEType', 'X': 'Personne'},
+                               {'ET': 'CWEType', 'X': 'RQLExpression'}, {'ET': 'CWEType', 'X': 'Societe'},
+                               {'ET': 'CWEType', 'X': 'State'}, {'ET': 'CWEType', 'X': 'SubDivision'},
+                               {'ET': 'CWEType', 'X': 'Tag'}, {'ET': 'CWEType', 'X': 'TrInfo'},
+                               {'ET': 'CWEType', 'X': 'Transition'}]))],
                      None, None, [self.system], {'ET': 'table0.C0', 'X': 'table0.C1'}, [])
                     ])
 
@@ -993,11 +993,11 @@
                      [('Any X,XT WHERE X title XT, X is Card', [{'X': 'Card', 'XT': 'String'}])],
                      [self.rql, self.system], None, {'X': 'table0.C0', 'X.title': 'table0.C1', 'XT': 'table0.C1'}, []),
                     ('FetchStep',
-                     [('Any U WHERE U login "syt", U is EUser', [{'U': 'EUser'}])],
+                     [('Any U WHERE U login "syt", U is CWUser', [{'U': 'CWUser'}])],
                      [self.ldap, self.system], None, {'U': 'table1.C0'}, []),
                     ('OneFetchStep',
-                     [('Any X,XT WHERE X owned_by U, X title XT, EXISTS(U owned_by 5), U is EUser, X is Card',
-                       [{'X': 'Card', 'U': 'EUser', 'XT': 'String'}])],
+                     [('Any X,XT WHERE X owned_by U, X title XT, EXISTS(U owned_by 5), U is CWUser, X is Card',
+                       [{'X': 'Card', 'U': 'CWUser', 'XT': 'String'}])],
                      None, None, [self.system],
                      {'X': 'table0.C0', 'X.title': 'table0.C1', 'XT': 'table0.C1', 'U': 'table1.C0'}, [])
                     ])
@@ -1011,8 +1011,8 @@
                      [('Any X,XT WHERE X title XT, X is Card', [{'X': 'Card', 'XT': 'String'}])],
                      [self.rql, self.system], None, {'X': 'table0.C0', 'X.title': 'table0.C1', 'XT': 'table0.C1'}, []),
                     ('OneFetchStep',
-                     [('Any X,XT WHERE X owned_by U, X title XT, U login "syt", EXISTS(U identity 5), U is EUser, X is Card',
-                       [{'U': 'EUser', 'X': 'Card', 'XT': 'String'}])],
+                     [('Any X,XT WHERE X owned_by U, X title XT, U login "syt", EXISTS(U identity 5), U is CWUser, X is Card',
+                       [{'U': 'CWUser', 'X': 'Card', 'XT': 'String'}])],
                      None, None, [self.system], {'X': 'table0.C0', 'X.title': 'table0.C1', 'XT': 'table0.C1'}, [])
                     ])
 
@@ -1022,8 +1022,8 @@
         self.session = self._user_session()[1]
         self._test('Any X,XT,U WHERE X is Card, X owned_by U?, X title XT, U login L',
                    [('FetchStep',
-                     [('Any U,L WHERE U identity 5, U login L, U is EUser',
-                       [{'L': 'String', u'U': 'EUser'}])],
+                     [('Any U,L WHERE U identity 5, U login L, U is CWUser',
+                       [{'L': 'String', u'U': 'CWUser'}])],
                      [self.system], {}, {'L': 'table0.C1', 'U': 'table0.C0', 'U.login': 'table0.C1'}, []),
                     ('FetchStep',
                      [('Any X,XT WHERE X title XT, X is Card', [{'X': 'Card', 'XT': 'String'}])],
@@ -1046,47 +1046,47 @@
                      [('Any X,XT WHERE X title XT, X is Card', [{'X': 'Card', 'XT': 'String'}])],
                      [self.rql, self.system], None, {'X': 'table0.C0', 'X.title': 'table0.C1', 'XT': 'table0.C1'}, []),
                     ('FetchStep',
-                     [('Any U WHERE U login "syt", U is EUser', [{'U': 'EUser'}])],
+                     [('Any U WHERE U login "syt", U is CWUser', [{'U': 'CWUser'}])],
                      [self.ldap, self.system], None, {'U': 'table1.C0'}, []),
                     ('OneFetchStep',
-                     [('Any X,XT LIMIT 10 OFFSET 10 WHERE X owned_by U, X title XT, EXISTS(U owned_by 5), U is EUser, X is Card',
-                       [{'X': 'Card', 'U': 'EUser', 'XT': 'String'}])],
+                     [('Any X,XT LIMIT 10 OFFSET 10 WHERE X owned_by U, X title XT, EXISTS(U owned_by 5), U is CWUser, X is Card',
+                       [{'X': 'Card', 'U': 'CWUser', 'XT': 'String'}])],
                      10, 10, [self.system],
                      {'X': 'table0.C0', 'X.title': 'table0.C1', 'XT': 'table0.C1', 'U': 'table1.C0'}, [])
                     ])
     
     def test_exists_base(self):
         self._test('Any X,L,S WHERE X in_state S, X login L, EXISTS(X in_group G, G name "bougloup")',
-                   [('FetchStep', [('Any X,L WHERE X login L, X is EUser', [{'X': 'EUser', 'L': 'String'}])],
+                   [('FetchStep', [('Any X,L WHERE X login L, X is CWUser', [{'X': 'CWUser', 'L': 'String'}])],
                      [self.ldap, self.system], None, {'X': 'table0.C0', 'X.login': 'table0.C1', 'L': 'table0.C1'}, []),
                     ('OneFetchStep', [("Any X,L,S WHERE X in_state S, X login L, "
-                                      'EXISTS(X in_group G, G name "bougloup", G is EGroup), S is State, X is EUser',
-                                       [{'X': 'EUser', 'L': 'String', 'S': 'State', 'G': 'EGroup'}])],
+                                      'EXISTS(X in_group G, G name "bougloup", G is CWGroup), S is State, X is CWUser',
+                                       [{'X': 'CWUser', 'L': 'String', 'S': 'State', 'G': 'CWGroup'}])],
                      None, None, [self.system],
                      {'X': 'table0.C0', 'X.login': 'table0.C1', 'L': 'table0.C1'}, [])])
 
     def test_exists_complex(self):
         self._test('Any G WHERE X in_group G, G name "managers", EXISTS(X copain T, T login in ("comme", "cochon"))',
-                   [('FetchStep', [('Any T WHERE T login IN("comme", "cochon"), T is EUser', [{'T': 'EUser'}])],
+                   [('FetchStep', [('Any T WHERE T login IN("comme", "cochon"), T is CWUser', [{'T': 'CWUser'}])],
                      [self.ldap, self.system], None, {'T': 'table0.C0'}, []),
                     ('OneFetchStep',
-                     [('Any G WHERE X in_group G, G name "managers", EXISTS(X copain T, T is EUser), G is EGroup, X is EUser',
-                       [{'X': 'EUser', 'T': 'EUser', 'G': 'EGroup'}])],
+                     [('Any G WHERE X in_group G, G name "managers", EXISTS(X copain T, T is CWUser), G is CWGroup, X is CWUser',
+                       [{'X': 'CWUser', 'T': 'CWUser', 'G': 'CWGroup'}])],
                      None, None, [self.system], {'T': 'table0.C0'}, [])])
 
     def test_exists3(self):
         self._test('Any G,L WHERE X in_group G, X login L, G name "managers", EXISTS(X copain T, T login in ("comme", "cochon"))',
                    [('FetchStep',
-                     [('Any T WHERE T login IN("comme", "cochon"), T is EUser',
-                       [{'T': 'EUser'}])],
+                     [('Any T WHERE T login IN("comme", "cochon"), T is CWUser',
+                       [{'T': 'CWUser'}])],
                      [self.ldap, self.system], None, {'T': 'table0.C0'}, []),
                     ('FetchStep',
-                     [('Any L,X WHERE X login L, X is EUser', [{'X': 'EUser', 'L': 'String'}])],
+                     [('Any L,X WHERE X login L, X is CWUser', [{'X': 'CWUser', 'L': 'String'}])],
                      [self.ldap, self.system], None,
                      {'X': 'table1.C1', 'X.login': 'table1.C0', 'L': 'table1.C0'}, []),
                     ('OneFetchStep',
-                     [('Any G,L WHERE X in_group G, X login L, G name "managers", EXISTS(X copain T, T is EUser), G is EGroup, X is EUser',
-                       [{'G': 'EGroup', 'L': 'String', 'T': 'EUser', 'X': 'EUser'}])],
+                     [('Any G,L WHERE X in_group G, X login L, G name "managers", EXISTS(X copain T, T is CWUser), G is CWGroup, X is CWUser',
+                       [{'G': 'CWGroup', 'L': 'String', 'T': 'CWUser', 'X': 'CWUser'}])],
                      None, None,
                      [self.system], {'T': 'table0.C0', 'X': 'table1.C1', 'X.login': 'table1.C0', 'L': 'table1.C0'}, [])])
 
@@ -1095,18 +1095,18 @@
                    'EXISTS(X copain T, T login L, T login in ("comme", "cochon")) OR '
                    'EXISTS(X in_state S, S name "pascontent", NOT X copain T2, T2 login "billy")',
                    [('FetchStep',
-                     [('Any T,L WHERE T login L, T login IN("comme", "cochon"), T is EUser', [{'T': 'EUser', 'L': 'String'}])],
+                     [('Any T,L WHERE T login L, T login IN("comme", "cochon"), T is CWUser', [{'T': 'CWUser', 'L': 'String'}])],
                      [self.ldap, self.system], None,
                      {'T': 'table0.C0', 'T.login': 'table0.C1', 'L': 'table0.C1'}, []),
                     ('FetchStep',
-                     [('Any T2 WHERE T2 login "billy", T2 is EUser', [{'T2': 'EUser'}])],
+                     [('Any T2 WHERE T2 login "billy", T2 is CWUser', [{'T2': 'CWUser'}])],
                      [self.ldap, self.system], None, {'T2': 'table1.C0'}, []),
                     ('FetchStep',
-                     [('Any L,X WHERE X login L, X is EUser', [{'X': 'EUser', 'L': 'String'}])],
+                     [('Any L,X WHERE X login L, X is CWUser', [{'X': 'CWUser', 'L': 'String'}])],
                      [self.ldap, self.system], None, {'X': 'table2.C1', 'X.login': 'table2.C0', 'L': 'table2.C0'}, []),
                     ('OneFetchStep',
-                     [('Any G,L WHERE X in_group G, X login L, G name "managers", (EXISTS(X copain T, T login L, T is EUser)) OR (EXISTS(X in_state S, S name "pascontent", NOT X copain T2, S is State, T2 is EUser)), G is EGroup, X is EUser',
-                       [{'G': 'EGroup', 'L': 'String', 'S': 'State', 'T': 'EUser', 'T2': 'EUser', 'X': 'EUser'}])],
+                     [('Any G,L WHERE X in_group G, X login L, G name "managers", (EXISTS(X copain T, T login L, T is CWUser)) OR (EXISTS(X in_state S, S name "pascontent", NOT X copain T2, S is State, T2 is CWUser)), G is CWGroup, X is CWUser',
+                       [{'G': 'CWGroup', 'L': 'String', 'S': 'State', 'T': 'CWUser', 'T2': 'CWUser', 'X': 'CWUser'}])],
                      None, None, [self.system],
                      {'T2': 'table1.C0', 'L': 'table2.C0',
                       'T': 'table0.C0', 'T.login': 'table0.C1', 'X': 'table2.C1', 'X.login': 'table2.C0'}, [])])
@@ -1115,29 +1115,29 @@
         self._test('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")',
-                   [('FetchStep', [('Any T WHERE T login IN("comme", "cochon"), T is EUser',
-                                    [{'T': 'EUser'}])],
+                   [('FetchStep', [('Any T WHERE T login IN("comme", "cochon"), T is CWUser',
+                                    [{'T': 'CWUser'}])],
                      [self.ldap, self.system], None, {'T': 'table0.C0'}, []),
-                    ('FetchStep', [('Any T2 WHERE T2 login "billy", T2 is EUser', [{'T2': 'EUser'}])],
+                    ('FetchStep', [('Any T2 WHERE T2 login "billy", T2 is CWUser', [{'T2': 'CWUser'}])],
                      [self.ldap, self.system], None, {'T2': 'table1.C0'}, []),
-                    ('FetchStep', [('Any L,X WHERE X login L, X is EUser', [{'X': 'EUser', 'L': 'String'}])],
+                    ('FetchStep', [('Any L,X WHERE X login L, X is CWUser', [{'X': 'CWUser', 'L': 'String'}])],
                      [self.ldap, self.system], None,
                      {'X': 'table2.C1', 'X.login': 'table2.C0', 'L': 'table2.C0'}, []),
-                    ('OneFetchStep', [('Any GN,L WHERE X in_group G, X login L, G name GN, EXISTS(X copain T, T is EUser), NOT EXISTS(X copain T2, T2 is EUser), G is EGroup, X is EUser',
-                       [{'G': 'EGroup', 'GN': 'String', 'L': 'String', 'T': 'EUser', 'T2': 'EUser', 'X': 'EUser'}])],
+                    ('OneFetchStep', [('Any GN,L WHERE X in_group G, X login L, G name GN, EXISTS(X copain T, T is CWUser), NOT EXISTS(X copain T2, T2 is CWUser), G is CWGroup, X is CWUser',
+                       [{'G': 'CWGroup', 'GN': 'String', 'L': 'String', 'T': 'CWUser', 'T2': 'CWUser', 'X': 'CWUser'}])],
                      None, None, [self.system],
                      {'T': 'table0.C0', 'T2': 'table1.C0',
                       'X': 'table2.C1', 'X.login': 'table2.C0', 'L': 'table2.C0'}, [])])
             
     def test_exists_security_no_invariant(self):
         ueid = self.session.user.eid
-        self._test('Any X,AA,AB,AC,AD ORDERBY AA WHERE X is EUser, X login AA, X firstname AB, X surname AC, X modification_date AD, A eid %(B)s, \
+        self._test('Any X,AA,AB,AC,AD ORDERBY AA WHERE X is CWUser, X login AA, X firstname AB, X surname AC, X modification_date AD, A eid %(B)s, \
     EXISTS(((X identity A) OR \
-            (EXISTS(X in_group C, C name IN("managers", "staff"), C is EGroup))) OR \
-            (EXISTS(X in_group D, A in_group D, NOT D name "users", D is EGroup)))',
-               [('FetchStep', [('Any X,AA,AB,AC,AD WHERE X login AA, X firstname AB, X surname AC, X modification_date AD, X is EUser',
+            (EXISTS(X in_group C, C name IN("managers", "staff"), C is CWGroup))) OR \
+            (EXISTS(X in_group D, A in_group D, NOT D name "users", D is CWGroup)))',
+               [('FetchStep', [('Any X,AA,AB,AC,AD WHERE X login AA, X firstname AB, X surname AC, X modification_date AD, X is CWUser',
                                 [{'AA': 'String', 'AB': 'String', 'AC': 'String', 'AD': 'Datetime',
-                                  'X': 'EUser'}])],
+                                  'X': 'CWUser'}])],
                  [self.ldap, self.system], None, {'AA': 'table0.C1', 'AB': 'table0.C2',
                                                   'AC': 'table0.C3', 'AD': 'table0.C4',
                                                   'X': 'table0.C0',
@@ -1145,9 +1145,9 @@
                                                   'X.login': 'table0.C1',
                                                   'X.modification_date': 'table0.C4',
                                                   'X.surname': 'table0.C3'}, []),
-                ('OneFetchStep', [('Any X,AA,AB,AC,AD ORDERBY AA WHERE X login AA, X firstname AB, X surname AC, X modification_date AD, EXISTS(((X identity 5) OR (EXISTS(X in_group C, C name IN("managers", "staff"), C is EGroup))) OR (EXISTS(X in_group D, 5 in_group D, NOT D name "users", D is EGroup))), X is EUser',
+                ('OneFetchStep', [('Any X,AA,AB,AC,AD ORDERBY AA WHERE X login AA, X firstname AB, X surname AC, X modification_date AD, EXISTS(((X identity 5) OR (EXISTS(X in_group C, C name IN("managers", "staff"), C is CWGroup))) OR (EXISTS(X in_group D, 5 in_group D, NOT D name "users", D is CWGroup))), X is CWUser',
                                    [{'AA': 'String', 'AB': 'String', 'AC': 'String', 'AD': 'Datetime',
-                                     'C': 'EGroup', 'D': 'EGroup', 'X': 'EUser'}])],
+                                     'C': 'CWGroup', 'D': 'CWGroup', 'X': 'CWUser'}])],
                  None, None, [self.system],
                  {'AA': 'table0.C1', 'AB': 'table0.C2', 'AC': 'table0.C3', 'AD': 'table0.C4',
                   'X': 'table0.C0',
@@ -1158,8 +1158,8 @@
     def test_relation_need_split(self):
         self._test('Any X, S WHERE X in_state S',
                    [('UnionStep', None, None, [
-                       ('OneFetchStep', [('Any X,S WHERE X in_state S, S is State, X is IN(Affaire, EUser)',
-                                          [{'X': 'Affaire', 'S': 'State'}, {'X': 'EUser', 'S': 'State'}])], 
+                       ('OneFetchStep', [('Any X,S WHERE X in_state S, S is State, X is IN(Affaire, CWUser)',
+                                          [{'X': 'Affaire', 'S': 'State'}, {'X': 'CWUser', 'S': 'State'}])], 
                         None, None, [self.system], {}, []),
                        ('OneFetchStep', [('Any X,S WHERE X in_state S, S is State, X is Note',
                                           [{'X': 'Note', 'S': 'State'}])], 
@@ -1172,8 +1172,8 @@
                                     [{'X': 'Note', 'S': 'State'}])],
                      [self.rql, self.system], None, {'X': 'table0.C0', 'S': 'table0.C1'}, []),
                      ('UnionStep', None, None,
-                      [('OneFetchStep', [('Any X,S,U WHERE X in_state S, X todo_by U, S is State, U is EUser, X is Note',
-                                          [{'X': 'Note', 'S': 'State', 'U': 'EUser'}])],
+                      [('OneFetchStep', [('Any X,S,U WHERE X in_state S, X todo_by U, S is State, U is CWUser, X is Note',
+                                          [{'X': 'Note', 'S': 'State', 'U': 'CWUser'}])],
                         None, None, [self.system], {'X': 'table0.C0', 'S': 'table0.C1'}, []),
                        ('OneFetchStep', [('Any X,S,U WHERE X in_state S, X todo_by U, S is State, U is Personne, X is Affaire',
                                           [{'X': 'Affaire', 'S': 'State', 'U': 'Personne'}])],
@@ -1187,8 +1187,8 @@
                                     [{'X': 'Note', 'S': 'State'}])],
                      [self.rql, self.system], None, {'X': 'table0.C0'}, []),
                      ('UnionStep', None, None,
-                      [('OneFetchStep', [('Any X,U WHERE X todo_by U, U is EUser, X is Note',
-                                          [{'X': 'Note', 'U': 'EUser'}])],
+                      [('OneFetchStep', [('Any X,U WHERE X todo_by U, U is CWUser, X is Note',
+                                          [{'X': 'Note', 'U': 'CWUser'}])],
                         None, None, [self.system], {'X': 'table0.C0'}, []),
                        ('OneFetchStep', [('Any X,U WHERE X in_state S, S name "pending", X todo_by U, S is State, U is Personne, X is Affaire',
                                           [{'S': 'State', 'U': 'Personne', 'X': 'Affaire'}])],
@@ -1206,9 +1206,9 @@
                                            [{'X': 'Note', 'T': 'Tag'}])],
                          None, None,
                          [self.system], {'X': 'table0.C0'}, []),
-                        ('OneFetchStep', [('Any X,T WHERE X in_state S, S name "pending", T tags X, S is State, T is Tag, X is IN(Affaire, EUser)',
+                        ('OneFetchStep', [('Any X,T WHERE X in_state S, S name "pending", T tags X, S is State, T is Tag, X is IN(Affaire, CWUser)',
                                            [{'X': 'Affaire', 'S': 'State', 'T': 'Tag'},
-                                            {'X': 'EUser', 'S': 'State', 'T': 'Tag'}])],
+                                            {'X': 'CWUser', 'S': 'State', 'T': 'Tag'}])],
                          None, None,
                          [self.system], {}, []),
                         ])
@@ -1251,9 +1251,9 @@
                        None, None, [self.rql, self.system], {},
                        []),
                       ('OneFetchStep',
-                       [('Any SN WHERE NOT X in_state S, S name SN, S is State, X is IN(Affaire, EUser)',
+                       [('Any SN WHERE NOT X in_state S, S name SN, S is State, X is IN(Affaire, CWUser)',
                          [{'S': 'State', 'SN': 'String', 'X': 'Affaire'},
-                          {'S': 'State', 'SN': 'String', 'X': 'EUser'}])],
+                          {'S': 'State', 'SN': 'String', 'X': 'CWUser'}])],
                        None, None, [self.system], {'S': 'table0.C1', 'S.name': 'table0.C0', 'SN': 'table0.C0'},
                        []),]
                      )])
@@ -1265,10 +1265,10 @@
                                     [{'A': 'Note', 'C': 'Datetime', 'B': 'Datetime'}])],
                      [self.rql], None,
                      {'A': 'table0.C0', 'A.creation_date': 'table0.C1', 'A.modification_date': 'table0.C2', 'C': 'table0.C2', 'B': 'table0.C1'}, []),
-                    #('FetchStep', [('Any D WHERE D is EUser', [{'D': 'EUser'}])],
+                    #('FetchStep', [('Any D WHERE D is CWUser', [{'D': 'CWUser'}])],
                     # [self.ldap, self.system], None, {'D': 'table1.C0'}, []),
-                    ('OneFetchStep', [('Any A,B,C,D WHERE A creation_date B, A modification_date C, A todo_by D?, A is Note, D is EUser',
-                                       [{'A': 'Note', 'C': 'Datetime', 'B': 'Datetime', 'D': 'EUser'}])],
+                    ('OneFetchStep', [('Any A,B,C,D WHERE A creation_date B, A modification_date C, A todo_by D?, A is Note, D is CWUser',
+                                       [{'A': 'Note', 'C': 'Datetime', 'B': 'Datetime', 'D': 'CWUser'}])],
                      None, None, [self.system],
                      {'A': 'table0.C0', 'A.creation_date': 'table0.C1', 'A.modification_date': 'table0.C2', 'C': 'table0.C2', 'B': 'table0.C1'}, [])],
                    {'x': 999999})
@@ -1278,7 +1278,7 @@
         repo._type_source_cache[999999] = ('Note', 'cards', 999999)
         self._test('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',
                    [('OneFetchStep', [('Any 5 WHERE 5 in_group G, (G name IN("managers", "logilab")) OR (X require_permission P?, P name "bla", P require_group G), X eid 999999',
-                                       [{'X': 'Note', 'G': 'EGroup', 'P': 'EPermission'}])],
+                                       [{'X': 'Note', 'G': 'CWGroup', 'P': 'CWPermission'}])],
                      None, None, [self.system], {}, [])],
                    {'x': 999999, 'u': self.session.user.eid})
 
@@ -1382,11 +1382,11 @@
                                     [{'T': 'String', 'X': 'Bookmark'}])],
                      [self.system], {}, {'N': 'table0.C1', 'X': 'table0.C0', 'X.name': 'table0.C1'}, []),
                     ('FetchStep',
-                     [('Any B,C WHERE B login C, B is EUser', [{'B': 'EUser', 'C': 'String'}])],
+                     [('Any B,C WHERE B login C, B is CWUser', [{'B': 'CWUser', 'C': 'String'}])],
                      [self.ldap, self.system], None, {'B': 'table1.C0', 'B.login': 'table1.C1', 'C': 'table1.C1'}, []),
-                    ('OneFetchStep', [('DISTINCT Any B,C ORDERBY C WHERE A created_by B, B login C, EXISTS(B owned_by 5), B is EUser',
-                                       [{'A': 'Bookmark', 'B': 'EUser', 'C': 'String'},
-                                        {'A': 'Tag', 'B': 'EUser', 'C': 'String'}])],
+                    ('OneFetchStep', [('DISTINCT Any B,C ORDERBY C WHERE A created_by B, B login C, EXISTS(B owned_by 5), B is CWUser',
+                                       [{'A': 'Bookmark', 'B': 'CWUser', 'C': 'String'},
+                                        {'A': 'Tag', 'B': 'CWUser', 'C': 'String'}])],
                      None, None, [self.system],
                      {'A': 'table0.C0',
                       'B': 'table1.C0', 'B.login': 'table1.C1',
@@ -1416,11 +1416,11 @@
                         'X.title': 'table0.C1'}, []),
                       ]),
                     ('FetchStep',
-                     [('Any B,C WHERE B login C, B is EUser', [{'B': 'EUser', 'C': 'String'}])],
+                     [('Any B,C WHERE B login C, B is CWUser', [{'B': 'CWUser', 'C': 'String'}])],
                      [self.ldap, self.system], None, {'B': 'table1.C0', 'B.login': 'table1.C1', 'C': 'table1.C1'}, []),
-                    ('OneFetchStep', [('DISTINCT Any B,C ORDERBY C WHERE A created_by B, B login C, EXISTS(B owned_by 5), B is EUser',
-                                       [{'A': 'Card', 'B': 'EUser', 'C': 'String'},
-                                        {'A': 'Tag', 'B': 'EUser', 'C': 'String'}])],
+                    ('OneFetchStep', [('DISTINCT Any B,C ORDERBY C WHERE A created_by B, B login C, EXISTS(B owned_by 5), B is CWUser',
+                                       [{'A': 'Card', 'B': 'CWUser', 'C': 'String'},
+                                        {'A': 'Tag', 'B': 'CWUser', 'C': 'String'}])],
                      None, None, [self.system],
                      {'A': 'table0.C0',
                       'B': 'table1.C0', 'B.login': 'table1.C1',
@@ -1607,8 +1607,8 @@
         ueid = self.session.user.eid
         self._test('DELETE X created_by Y WHERE X eid %(x)s, NOT Y eid %(y)s',
                    [('DeleteRelationsStep', [
-                       ('OneFetchStep', [('Any 5,Y WHERE %s created_by Y, NOT Y eid %s, Y is EUser'%(ueid, ueid),
-                                          [{'Y': 'EUser'}])],
+                       ('OneFetchStep', [('Any 5,Y WHERE %s created_by Y, NOT Y eid %s, Y is CWUser'%(ueid, ueid),
+                                          [{'Y': 'CWUser'}])],
                         None, None, [self.system], {}, []),
                        ]),
                     ],
@@ -1617,10 +1617,10 @@
     def test_delete_relation2(self):
         ueid = self.session.user.eid
         self._test('DELETE X created_by Y WHERE X eid %(x)s, NOT Y login "syt"',
-                   [('FetchStep', [('Any Y WHERE NOT Y login "syt", Y is EUser', [{'Y': 'EUser'}])],
+                   [('FetchStep', [('Any Y WHERE NOT Y login "syt", Y is CWUser', [{'Y': 'CWUser'}])],
                      [self.ldap, self.system], None, {'Y': 'table0.C0'}, []),
                     ('DeleteRelationsStep', [
-                        ('OneFetchStep', [('Any %s,Y WHERE %s created_by Y, Y is EUser'%(ueid,ueid), [{'Y': 'EUser'}])],
+                        ('OneFetchStep', [('Any %s,Y WHERE %s created_by Y, Y is CWUser'%(ueid,ueid), [{'Y': 'CWUser'}])],
                          None, None, [self.system], {'Y': 'table0.C0'}, []),
                         ]),
                     ],
@@ -1651,26 +1651,26 @@
     def test_update(self):
         self._test('SET X copain Y WHERE X login "comme", Y login "cochon"',
                    [('FetchStep',
-                     [('Any X WHERE X login "comme", X is EUser', [{'X': 'EUser'}])],
+                     [('Any X WHERE X login "comme", X is CWUser', [{'X': 'CWUser'}])],
                      [self.ldap, self.system], None, {'X': 'table0.C0'}, []),
                     ('FetchStep',
-                     [('Any Y WHERE Y login "cochon", Y is EUser', [{'Y': 'EUser'}])],
+                     [('Any Y WHERE Y login "cochon", Y is CWUser', [{'Y': 'CWUser'}])],
                      [self.ldap, self.system], None, {'Y': 'table1.C0'}, []),
                     ('UpdateStep',
                      [('OneFetchStep',
-                       [('DISTINCT Any X,Y WHERE X is EUser, Y is EUser',
-                         [{'X': 'EUser', 'Y': 'EUser'}])],
+                       [('DISTINCT Any X,Y WHERE X is CWUser, Y is CWUser',
+                         [{'X': 'CWUser', 'Y': 'CWUser'}])],
                        None, None, [self.system], {'X': 'table0.C0', 'Y': 'table1.C0'}, [])
                       ])
                     ])
 
     def test_update2(self):
         self._test('SET U in_group G WHERE G name ~= "bougloup%", U login "admin"',
-                   [('FetchStep', [('Any U WHERE U login "admin", U is EUser', [{'U': 'EUser'}])],
+                   [('FetchStep', [('Any U WHERE U login "admin", U is CWUser', [{'U': 'CWUser'}])],
                      [self.ldap, self.system], None, {'U': 'table0.C0'}, []),
                      ('UpdateStep', [
-                        ('OneFetchStep', [('DISTINCT Any U,G WHERE G name ILIKE "bougloup%", G is EGroup, U is EUser',
-                                           [{'U': 'EUser', 'G': 'EGroup'}])],
+                        ('OneFetchStep', [('DISTINCT Any U,G WHERE G name ILIKE "bougloup%", G is CWGroup, U is CWUser',
+                                           [{'U': 'CWUser', 'G': 'CWGroup'}])],
                          None, None, [self.system], {'U': 'table0.C0'}, []),
                         ]),
                     ])
@@ -1690,14 +1690,14 @@
 
 #     def test_update4(self):
 #         # since we are adding a in_state relation with a state from the system
-#         # source, EUser should only be searched only in the system source as well
+#         # source, CWUser should only be searched only in the system source as well
 #         rset = self.execute('State X WHERE X name "activated"')
 #         assert len(rset) == 1, rset
 #         activatedeid = rset[0][0]
-#         self._test('SET X in_state S WHERE X is EUser, S eid %s' % activatedeid,
+#         self._test('SET X in_state S WHERE X is CWUser, S eid %s' % activatedeid,
 #                    [('UpdateStep', [
-#                        ('OneFetchStep', [('DISTINCT Any X,%s WHERE X is EUser' % activatedeid,
-#                                           [{'X': 'EUser'}])],
+#                        ('OneFetchStep', [('DISTINCT Any X,%s WHERE X is CWUser' % activatedeid,
+#                                           [{'X': 'CWUser'}])],
 #                         None, None, [self.system], {}, []),
 #                        ]),
 #                     ])
@@ -1707,14 +1707,14 @@
     def test_nonregr1(self):
         self._test('Any X, Y WHERE X copain Y, X login "syt", Y login "cochon"',
                    [('FetchStep',
-                     [('Any X WHERE X login "syt", X is EUser', [{'X': 'EUser'}])],
+                     [('Any X WHERE X login "syt", X is CWUser', [{'X': 'CWUser'}])],
                      [self.ldap, self.system], None, {'X': 'table0.C0'}, []),
                     ('FetchStep',
-                     [('Any Y WHERE Y login "cochon", Y is EUser', [{'Y': 'EUser'}])],
+                     [('Any Y WHERE Y login "cochon", Y is CWUser', [{'Y': 'CWUser'}])],
                      [self.ldap, self.system], None, {'Y': 'table1.C0'}, []),
                     ('OneFetchStep',
-                     [('Any X,Y WHERE X copain Y, X is EUser, Y is EUser',
-                       [{'X': 'EUser', 'Y': 'EUser'}])],
+                     [('Any X,Y WHERE X copain Y, X is CWUser, Y is CWUser',
+                       [{'X': 'CWUser', 'Y': 'CWUser'}])],
                      None, None, [self.system], {'X': 'table0.C0', 'Y': 'table1.C0'}, [])
                     ])
     
@@ -1724,8 +1724,8 @@
                    [('FetchStep', [('Any X,D WHERE X modification_date D, X is Note',
                                     [{'X': 'Note', 'D': 'Datetime'}])],
                      [self.rql, self.system], None, {'X': 'table0.C0', 'X.modification_date': 'table0.C1', 'D': 'table0.C1'}, []),
-                    ('FetchStep', [('Any X,D WHERE X modification_date D, X is EUser',
-                                    [{'X': 'EUser', 'D': 'Datetime'}])],
+                    ('FetchStep', [('Any X,D WHERE X modification_date D, X is CWUser',
+                                    [{'X': 'CWUser', 'D': 'Datetime'}])],
                      [self.ldap, self.system], None, {'X': 'table1.C0', 'X.modification_date': 'table1.C1', 'D': 'table1.C1'}, []),
                     ('AggrStep', 'Any X ORDERBY D DESC', None, None, 'table2', None, [
                         ('FetchStep', [('Any X,D WHERE E eid %s, E wf_info_for X, X modification_date D, E is TrInfo, X is Affaire'%treid,
@@ -1733,8 +1733,8 @@
                          [self.system],
                          {},
                          {'X': 'table2.C0', 'X.modification_date': 'table2.C1', 'D': 'table2.C1', 'E.wf_info_for': 'table2.C0'}, []),
-                        ('FetchStep', [('Any X,D WHERE E eid %s, E wf_info_for X, X modification_date D, E is TrInfo, X is EUser'%treid,
-                                        [{'X': 'EUser', 'E': 'TrInfo', 'D': 'Datetime'}])],
+                        ('FetchStep', [('Any X,D WHERE E eid %s, E wf_info_for X, X modification_date D, E is TrInfo, X is CWUser'%treid,
+                                        [{'X': 'CWUser', 'E': 'TrInfo', 'D': 'Datetime'}])],
                          [self.system],
                          {'X': 'table1.C0', 'X.modification_date': 'table1.C1', 'D': 'table1.C1'},
                          {'X': 'table2.C0', 'X.modification_date': 'table2.C1', 'D': 'table2.C1', 'E.wf_info_for': 'table2.C0'}, []),
@@ -1749,22 +1749,22 @@
         
     def test_nonregr3(self):
         # original jpl query:
-        # Any X, NOW - CD, P WHERE P is Project, U interested_in P, U is EUser, U login "sthenault", X concerns P, X creation_date CD ORDERBY CD DESC LIMIT 5
+        # 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
         self._test('Any X, NOW - CD, P ORDERBY CD DESC LIMIT 5 WHERE P bookmarked_by U, U login "admin", P is X, X creation_date CD',
-                   [('FetchStep', [('Any U WHERE U login "admin", U is EUser', [{'U': 'EUser'}])],
+                   [('FetchStep', [('Any U WHERE U login "admin", U is CWUser', [{'U': 'CWUser'}])],
                      [self.ldap, self.system], None, {'U': 'table0.C0'}, []),
-                    ('OneFetchStep', [('Any X,(NOW - CD),P ORDERBY CD DESC LIMIT 5 WHERE P bookmarked_by U, P is X, X creation_date CD, P is Bookmark, U is EUser, X is EEType',
-                                       [{'P': 'Bookmark', 'U': 'EUser', 'X': 'EEType', 'CD': 'Datetime'}])],
+                    ('OneFetchStep', [('Any X,(NOW - CD),P ORDERBY CD DESC LIMIT 5 WHERE P bookmarked_by U, P is X, X creation_date CD, P is Bookmark, U is CWUser, X is CWEType',
+                                       [{'P': 'Bookmark', 'U': 'CWUser', 'X': 'CWEType', 'CD': 'Datetime'}])],
                      5, None,  [self.system], {'U': 'table0.C0'}, [])]
                    )
         
     def test_nonregr4(self):
         self._test('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',
-                   [#('FetchStep', [('Any U WHERE U is EUser', [{'U': 'EUser'}])],
+                   [#('FetchStep', [('Any U WHERE U is CWUser', [{'U': 'CWUser'}])],
                     # [self.ldap, self.system], None, {'U': 'table0.C0'}, []),
                     ('OneFetchStep', [('Any U ORDERBY D DESC WHERE WF wf_info_for 5, WF creation_date D, WF from_state FS, WF owned_by U?',
-                                       [{'WF': 'TrInfo', 'FS': 'State', 'U': 'EUser', 'D': 'Datetime'}])],
+                                       [{'WF': 'TrInfo', 'FS': 'State', 'U': 'CWUser', 'D': 'Datetime'}])],
                      None, None,
                      [self.system], {}, [])],
                    {'x': self.session.user.eid})
@@ -1837,17 +1837,17 @@
                    {'x': 999999, 'z': 999998})
 
     def test_nonregr10(self):
-        repo._type_source_cache[999999] = ('EUser', 'ldapuser', 999999)
+        repo._type_source_cache[999999] = ('CWUser', 'ldapuser', 999999)
         self._test('Any X,AA,AB ORDERBY AA WHERE E eid %(x)s, E owned_by X, X login AA, X modification_date AB',
                    [('FetchStep',
-                     [('Any X,AA,AB WHERE X login AA, X modification_date AB, X is EUser',
-                       [{'AA': 'String', 'AB': 'Datetime', 'X': 'EUser'}])],
+                     [('Any X,AA,AB WHERE X login AA, X modification_date AB, X is CWUser',
+                       [{'AA': 'String', 'AB': 'Datetime', 'X': 'CWUser'}])],
                      [self.ldap, self.system], None, {'AA': 'table0.C1', 'AB': 'table0.C2',
                                                       'X': 'table0.C0', 'X.login': 'table0.C1', 'X.modification_date': 'table0.C2'},
                      []),
                     ('OneFetchStep',
-                     [('Any X,AA,AB ORDERBY AA WHERE 999999 owned_by X, X login AA, X modification_date AB, X is EUser',
-                       [{'AA': 'String', 'AB': 'Datetime', 'X': 'EUser'}])],
+                     [('Any X,AA,AB ORDERBY AA WHERE 999999 owned_by X, X login AA, X modification_date AB, X is CWUser',
+                       [{'AA': 'String', 'AB': 'Datetime', 'X': 'CWUser'}])],
                      None, None, [self.system], {'AA': 'table0.C1', 'AB': 'table0.C2',
                                                  'X': 'table0.C0', 'X.login': 'table0.C1', 'X.modification_date': 'table0.C2'},
                      [])
@@ -1858,11 +1858,11 @@
         repo._type_source_cache[999999] = ('Bookmark', 'system', 999999)
         self._test('SET X bookmarked_by Y WHERE X eid %(x)s, Y login "hop"',
                    [('FetchStep',
-                     [('Any Y WHERE Y login "hop", Y is EUser', [{'Y': 'EUser'}])],
+                     [('Any Y WHERE Y login "hop", Y is CWUser', [{'Y': 'CWUser'}])],
                      [self.ldap, self.system],
                      None, {'Y': 'table0.C0'}, []),
                     ('UpdateStep',
-                     [('OneFetchStep', [('DISTINCT Any 999999,Y WHERE Y is EUser', [{'Y': 'EUser'}])],
+                     [('OneFetchStep', [('DISTINCT Any 999999,Y WHERE Y is CWUser', [{'Y': 'CWUser'}])],
                        None, None, [self.system], {'Y': 'table0.C0'},
                        [])]
                      )],
@@ -1898,20 +1898,20 @@
         self._test('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 EUser)',
-                   [('FetchStep', [('Any U,UL WHERE U login UL, U is EUser',
-                                    [{'U': 'EUser', 'UL': 'String'}])],
+                   'OR (EXISTS(U in_group H, ME in_group H, NOT H name "users")), U login UL, U is CWUser)',
+                   [('FetchStep', [('Any U,UL WHERE U login UL, U is CWUser',
+                                    [{'U': 'CWUser', 'UL': 'String'}])],
                      [self.ldap, self.system], None,
                      {'U': 'table0.C0', 'U.login': 'table0.C1', 'UL': 'table0.C1'},
                      []),
-                    ('FetchStep', [('Any U,UL WHERE ((EXISTS(U identity 5)) OR (EXISTS(U in_group G, G name IN("managers", "staff"), G is EGroup))) OR (EXISTS(U in_group H, 5 in_group H, NOT H name "users", H is EGroup)), U login UL, U is EUser',
-                                    [{'G': 'EGroup', 'H': 'EGroup', 'U': 'EUser', 'UL': 'String'}])],
+                    ('FetchStep', [('Any U,UL WHERE ((EXISTS(U identity 5)) OR (EXISTS(U in_group G, G name IN("managers", "staff"), G is CWGroup))) OR (EXISTS(U in_group H, 5 in_group H, NOT H name "users", H is CWGroup)), U login UL, U is CWUser',
+                                    [{'G': 'CWGroup', 'H': 'CWGroup', 'U': 'CWUser', 'UL': 'String'}])],
                      [self.system],
                      {'U': 'table0.C0', 'U.login': 'table0.C1', 'UL': 'table0.C1'},
                      {'U': 'table1.C0', 'U.login': 'table1.C1', 'UL': 'table1.C1'},
                      []),
                     ('OneFetchStep', [('Any B,U,UL GROUPBY B,U,UL WHERE B created_by U?, B is File',
-                                       [{'B': 'File', 'U': 'EUser', 'UL': 'String'}])],
+                                       [{'B': 'File', 'U': 'CWUser', 'UL': 'String'}])],
                      None, None, [self.system],
                      {'U': 'table1.C0', 'UL': 'table1.C1'},
                      [])],
@@ -1932,20 +1932,20 @@
         self._test('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, (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 EUser)',
-                   [('FetchStep', [('Any U,UL WHERE U login UL, U is EUser',
-                                    [{'U': 'EUser', 'UL': 'String'}])],
+                   'OR (EXISTS(U in_group H, ME in_group H, NOT H name "users")), U login UL, U is CWUser)',
+                   [('FetchStep', [('Any U,UL WHERE U login UL, U is CWUser',
+                                    [{'U': 'CWUser', 'UL': 'String'}])],
                      [self.ldap, self.system], None,
                      {'U': 'table0.C0', 'U.login': 'table0.C1', 'UL': 'table0.C1'},
                      []),
-                    ('FetchStep', [('Any U,UL WHERE ((U identity 5) OR (EXISTS(U in_group G, G name IN("managers", "staff"), G is EGroup))) OR (EXISTS(U in_group H, 5 in_group H, NOT H name "users", H is EGroup)), U login UL, U is EUser',
-                                    [{'G': 'EGroup', 'H': 'EGroup', 'U': 'EUser', 'UL': 'String'}])],
+                    ('FetchStep', [('Any U,UL WHERE ((U identity 5) OR (EXISTS(U in_group G, G name IN("managers", "staff"), G is CWGroup))) OR (EXISTS(U in_group H, 5 in_group H, NOT H name "users", H is CWGroup)), U login UL, U is CWUser',
+                                    [{'G': 'CWGroup', 'H': 'CWGroup', 'U': 'CWUser', 'UL': 'String'}])],
                      [self.system],
                      {'U': 'table0.C0', 'U.login': 'table0.C1', 'UL': 'table0.C1'},
                      {'U': 'table1.C0', 'U.login': 'table1.C1', 'UL': 'table1.C1'},
                      []),
                     ('OneFetchStep', [('Any B,U,UL GROUPBY B,U,UL WHERE B created_by U?, B is File',
-                                       [{'B': 'File', 'U': 'EUser', 'UL': 'String'}])],
+                                       [{'B': 'File', 'U': 'CWUser', 'UL': 'String'}])],
                      None, None, [self.system],
                      {'U': 'table1.C0', 'UL': 'table1.C1'},
                      [])],
--- a/server/test/unittest_multisources.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_multisources.py	Mon May 04 13:09:48 2009 +0200
@@ -1,6 +1,7 @@
 from os.path import dirname, join, abspath
+from datetime import datetime, timedelta
+
 from logilab.common.decorators import cached
-from mx.DateTime import now
 
 from cubicweb.devtools import TestServerConfiguration, init_test_database
 from cubicweb.devtools.apptest import RepositoryBasedTC
@@ -23,7 +24,7 @@
 aff1 = cu.execute('INSERT Affaire X: X ref "AFFREF", X in_state S WHERE S name "pitetre"')[0][0]
 cnx2.commit()
 
-MTIME = now() - 0.1
+MTIME = datetime.now() - timedelta(0, 10)
 
 repo3, cnx3 = init_test_database('sqlite', config=ExternalSource2Configuration('data'))
 
@@ -194,8 +195,8 @@
         
     def test_union(self):
         afeids = self.execute('Affaire X')
-        ueids = self.execute('EUser X')
-        rset = self.execute('(Any X WHERE X is Affaire) UNION (Any X WHERE X is EUser)')
+        ueids = self.execute('CWUser X')
+        rset = self.execute('(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))
         
--- a/server/test/unittest_querier.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_querier.py	Mon May 04 13:09:48 2009 +0200
@@ -1,19 +1,19 @@
 # -*- coding: iso-8859-1 -*-
 """unit tests for modules cubicweb.server.querier and cubicweb.server.querier_steps
 """
+from datetime import date, datetime
 
 from logilab.common.testlib import TestCase, unittest_main
-from cubicweb.devtools import init_test_database
-from cubicweb.devtools.repotest import tuplify, BaseQuerierTC
-from unittest_session import Variable
-
-from mx.DateTime import today, now, DateTimeType
 from rql import BadRQLQuery, RQLSyntaxError
 
 from cubicweb import QueryError, Unauthorized
 from cubicweb.server.sqlutils import SQL_PREFIX
 from cubicweb.server.utils import crypt_password
 from cubicweb.server.sources.native import make_schema
+from cubicweb.devtools import init_test_database
+from cubicweb.devtools.repotest import tuplify, BaseQuerierTC
+
+from unittest_session import Variable
 
 
 # register priority/severity sorting registered procedure
@@ -39,7 +39,7 @@
 
 class MakeSchemaTC(TestCase):
     def test_known_values(self):
-        solution = {'A': 'String', 'B': 'EUser'}
+        solution = {'A': 'String', 'B': 'CWUser'}
         self.assertEquals(make_schema((Variable('A'), Variable('B')), solution, 
                                       'table0', TYPEMAP),
                           ('C0 text,C1 integer', {'A': 'table0.C0', 'B': 'table0.C1'}))
@@ -60,13 +60,13 @@
         pass
     
     def test_preprocess_1(self):
-        reid = self.execute('Any X WHERE X is ERType, X name "owned_by"')[0][0]
+        reid = self.execute('Any X WHERE X is CWRType, X name "owned_by"')[0][0]
         rqlst = self._prepare('Any COUNT(RDEF) WHERE RDEF relation_type X, X eid %(x)s', {'x': reid})
-        self.assertEquals(rqlst.solutions, [{'RDEF': 'EFRDef'}, {'RDEF': 'ENFRDef'}])
+        self.assertEquals(rqlst.solutions, [{'RDEF': 'CWAttribute'}, {'RDEF': 'CWRelation'}])
         
     def test_preprocess_2(self):
         teid = self.execute("INSERT Tag X: X name 'tag'")[0][0]
-        #geid = self.execute("EGroup G WHERE G name 'users'")[0][0]
+        #geid = self.execute("CWGroup G WHERE G name 'users'")[0][0]
         #self.execute("SET X tags Y WHERE X eid %(t)s, Y eid %(g)s",
         #             {'g': geid, 't': teid}, 'g')
         rqlst = self._prepare('Any X WHERE E eid %(x)s, E tags X', {'x': teid})
@@ -96,7 +96,7 @@
                           ' OR (EXISTS(H concerne G?, G owned_by %(B)s, G is SubDivision, X identity H, H is Affaire)))'
                           ' OR (EXISTS(I concerne F?, F owned_by %(B)s, F is Societe, X identity I, I is Affaire)))'
                           ' OR (EXISTS(J concerne E?, E owned_by %(B)s, E is Note, X identity J, J is Affaire)))'
-                          ', ET is EEType, X is Affaire')
+                          ', ET is CWEType, X is Affaire')
         self.assertEquals(solutions, [{'C': 'Division',
                                        'D': 'Affaire',
                                        'E': 'Note',
@@ -106,47 +106,47 @@
                                        'I': 'Affaire',
                                        'J': 'Affaire',
                                        'X': 'Affaire',
-                                       'ET': 'EEType', 'ETN': 'String'}])
+                                       'ET': 'CWEType', 'ETN': 'String'}])
         rql, solutions = partrqls[1]
-        self.assertEquals(rql,  'Any ETN,X WHERE X is ET, ET name ETN, ET is EEType, '
-                          'X is IN(Bookmark, Card, Comment, Division, ECache, EConstraint, EConstraintType, EEType, EFRDef, EGroup, ENFRDef, EPermission, EProperty, ERType, EUser, Email, EmailAddress, EmailPart, EmailThread, File, Folder, Image, Note, Personne, RQLExpression, Societe, State, SubDivision, Tag, TrInfo, Transition)')
+        self.assertEquals(rql,  'Any ETN,X WHERE X is ET, ET name ETN, ET is CWEType, '
+                          'X is IN(Bookmark, Card, Comment, Division, CWCache, CWConstraint, CWConstraintType, CWEType, CWAttribute, CWGroup, CWRelation, CWPermission, CWProperty, CWRType, CWUser, Email, EmailAddress, EmailPart, EmailThread, File, Folder, Image, Note, Personne, RQLExpression, Societe, State, SubDivision, Tag, TrInfo, Transition)')
         self.assertListEquals(sorted(solutions),
-                              sorted([{'X': 'Bookmark', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Card', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Comment', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Division', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'ECache', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EConstraint', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EConstraintType', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EEType', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EFRDef', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EGroup', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Email', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EmailAddress', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EmailPart', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EmailThread', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'ENFRDef', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EPermission', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EProperty', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'ERType', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'EUser', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'File', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Folder', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Image', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Note', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Personne', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'RQLExpression', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Societe', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'State', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'SubDivision', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Tag', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'Transition', 'ETN': 'String', 'ET': 'EEType'},
-                                      {'X': 'TrInfo', 'ETN': 'String', 'ET': 'EEType'}]))
+                              sorted([{'X': 'Bookmark', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Card', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Comment', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Division', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWCache', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWConstraint', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWConstraintType', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWEType', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWAttribute', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWGroup', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Email', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'EmailAddress', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'EmailPart', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'EmailThread', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWRelation', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWPermission', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWProperty', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWRType', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'CWUser', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'File', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Folder', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Image', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Note', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Personne', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'RQLExpression', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Societe', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'State', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'SubDivision', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Tag', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'Transition', 'ETN': 'String', 'ET': 'CWEType'},
+                                      {'X': 'TrInfo', 'ETN': 'String', 'ET': 'CWEType'}]))
         rql, solutions = partrqls[2]
         self.assertEquals(rql,
                           'Any ETN,X WHERE X is ET, ET name ETN, EXISTS(X owned_by %(C)s), '
-                          'ET is EEType, X is Basket')
-        self.assertEquals(solutions, [{'ET': 'EEType',
+                          'ET is CWEType, X is Basket')
+        self.assertEquals(solutions, [{'ET': 'CWEType',
                                        'X': 'Basket',
                                        'ETN': 'String',
                                        }])
@@ -170,7 +170,7 @@
     def test_build_description(self):
         # should return an empty result set
         rset = self.execute('Any X WHERE X eid %(x)s', {'x': self.session.user.eid})
-        self.assertEquals(rset.description[0][0], 'EUser')
+        self.assertEquals(rset.description[0][0], 'CWUser')
         rset = self.execute('Any 1')
         self.assertEquals(rset.description[0][0], 'Int')
         rset = self.execute('Any TRUE')
@@ -189,7 +189,7 @@
         self.assertEquals(rset.description[0][0], 'Boolean')
         rset = self.execute('Any %(x)s', {'x': 1.0})
         self.assertEquals(rset.description[0][0], 'Float')
-        rset = self.execute('Any %(x)s', {'x': now()})
+        rset = self.execute('Any %(x)s', {'x': datetime.now()})
         self.assertEquals(rset.description[0][0], 'Datetime')
         rset = self.execute('Any %(x)s', {'x': 'str'})
         self.assertEquals(rset.description[0][0], 'String')
@@ -202,7 +202,7 @@
 
     def test_encoding_pb(self):
         self.assertRaises(RQLSyntaxError, self.execute,
-                          'Any X WHERE X is ERType, X name "öwned_by"')
+                          'Any X WHERE X is CWRType, X name "öwned_by"')
 
     def test_unknown_eid(self):
         # should return an empty result set
@@ -211,20 +211,20 @@
     # selection queries tests #################################################
     
     def test_select_1(self):
-        rset = self.execute('Any X ORDERBY X WHERE X is EGroup')
+        rset = self.execute('Any X ORDERBY X WHERE X is CWGroup')
         result, descr = rset.rows, rset.description
         self.assertEquals(tuplify(result), [(1,), (2,), (3,), (4,)])
-        self.assertEquals(descr, [('EGroup',), ('EGroup',), ('EGroup',), ('EGroup',)])
+        self.assertEquals(descr, [('CWGroup',), ('CWGroup',), ('CWGroup',), ('CWGroup',)])
         
     def test_select_2(self):
-        rset = self.execute('Any X ORDERBY N WHERE X is EGroup, X name N')
+        rset = self.execute('Any X ORDERBY N WHERE X is CWGroup, X name N')
         self.assertEquals(tuplify(rset.rows), [(3,), (1,), (4,), (2,)])
-        self.assertEquals(rset.description, [('EGroup',), ('EGroup',), ('EGroup',), ('EGroup',)])
-        rset = self.execute('Any X ORDERBY N DESC WHERE X is EGroup, X name N')
+        self.assertEquals(rset.description, [('CWGroup',), ('CWGroup',), ('CWGroup',), ('CWGroup',)])
+        rset = self.execute('Any X ORDERBY N DESC WHERE X is CWGroup, X name N')
         self.assertEquals(tuplify(rset.rows), [(2,), (4,), (1,), (3,)])
         
     def test_select_3(self):
-        rset = self.execute('Any N GROUPBY N WHERE X is EGroup, X name N')
+        rset = self.execute('Any N GROUPBY N WHERE X is CWGroup, X name N')
         result, descr = rset.rows, rset.description
         result.sort()
         self.assertEquals(tuplify(result), [('guests',), ('managers',), ('owners',), ('users',)])
@@ -240,10 +240,10 @@
         result, descr = rset.rows, rset.description
         self.assertEquals(descr[0][0], 'String')
         self.assertEquals(descr[0][1], 'Int')
-        self.assertEquals(result[0][0], 'ENFRDef')
+        self.assertEquals(result[0][0], 'CWRelation')
         
     def test_select_groupby_orderby(self):
-        rset = self.execute('Any N GROUPBY N ORDERBY N WHERE X is EGroup, X name N')
+        rset = self.execute('Any N GROUPBY N ORDERBY N WHERE X is CWGroup, X name N')
         self.assertEquals(tuplify(rset.rows), [('guests',), ('managers',), ('owners',), ('users',)])
         self.assertEquals(rset.description, [('String',), ('String',), ('String',), ('String',)])
         
@@ -263,9 +263,9 @@
         self.assertEquals(len(rset), 5)
         
     def test_select_5(self):
-        rset = self.execute('Any X, TMP ORDERBY TMP WHERE X name TMP, X is EGroup')
+        rset = self.execute('Any X, TMP ORDERBY TMP WHERE X name TMP, X is CWGroup')
         self.assertEquals(tuplify(rset.rows), [(3, 'guests',), (1, 'managers',), (4, 'owners',), (2, 'users',)])
-        self.assertEquals(rset.description, [('EGroup', 'String',), ('EGroup', 'String',), ('EGroup', 'String',), ('EGroup', 'String',)])
+        self.assertEquals(rset.description, [('CWGroup', 'String',), ('CWGroup', 'String',), ('CWGroup', 'String',), ('CWGroup', 'String',)])
         
     def test_select_6(self):
         self.execute("INSERT Personne X: X nom 'bidule'")[0]
@@ -307,16 +307,16 @@
         self.execute("INSERT Personne X: X nom 'chouette'")
         self.execute("INSERT Personne X: X nom 'autre'")
         self.execute("SET X ecrit_par P WHERE X para 'bidule', P nom 'chouette'")
-        rset = self.execute('Any U,T ORDERBY T DESC WHERE U is EUser, '
+        rset = self.execute('Any U,T ORDERBY T DESC WHERE U is CWUser, '
                             'N ecrit_par U, N type T')#, {'x': self.ueid})
         self.assertEquals(len(rset.rows), 0)
         
     def test_select_nonregr_edition_not(self):
         groupeids = set((1, 2, 3))
-        groupreadperms = set(r[0] for r in self.execute('Any Y WHERE X name "EGroup", Y eid IN(1, 2, 3), X read_permission Y'))
-        rset = self.execute('DISTINCT Any Y WHERE X is EEType, X name "EGroup", Y eid IN(1, 2, 3), NOT X read_permission Y')
+        groupreadperms = set(r[0] for r in self.execute('Any Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), X read_permission Y'))
+        rset = self.execute('DISTINCT Any Y WHERE X is CWEType, X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y')
         self.assertEquals(sorted(r[0] for r in rset.rows), sorted(groupeids - groupreadperms))
-        rset = self.execute('DISTINCT Any Y WHERE X name "EGroup", Y eid IN(1, 2, 3), NOT X read_permission Y')
+        rset = self.execute('DISTINCT Any Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y')
         self.assertEquals(sorted(r[0] for r in rset.rows), sorted(groupeids - groupreadperms))
                      
     def test_select_outer_join(self):
@@ -340,7 +340,7 @@
         self.assertEquals(rset.rows, [[peid1]])
 
     def test_select_left_outer_join(self):
-        ueid = self.execute("INSERT EUser X: X login 'bob', X upassword 'toto', X in_group G "
+        ueid = self.execute("INSERT CWUser X: X login 'bob', X upassword 'toto', X in_group G "
                             "WHERE G name 'users'")[0][0]
         self.commit()
         try:
@@ -359,13 +359,13 @@
                                 {'x': ueid}, 'x')
             self.assertEquals(len(rset), 2)
         finally:
-            self.execute('DELETE EUser X WHERE X eid %s' % ueid)
+            self.execute('DELETE CWUser X WHERE X eid %s' % ueid)
             self.commit()
 
     def test_select_ambigous_outer_join(self):
         teid = self.execute("INSERT Tag X: X name 'tag'")[0][0]
         self.execute("INSERT Tag X: X name 'tagbis'")[0][0]
-        geid = self.execute("EGroup G WHERE G name 'users'")[0][0]
+        geid = self.execute("CWGroup G WHERE G name 'users'")[0][0]
         self.execute("SET X tags Y WHERE X eid %(t)s, Y eid %(g)s",
                      {'g': geid, 't': teid}, 'g')
         rset = self.execute("Any GN,TN ORDERBY GN WHERE T? tags G, T name TN, G name GN")
@@ -423,13 +423,13 @@
         self.assertEquals(rset.description, [('Int',)])
 
     def test_select_custom_aggregat_concat_string(self):
-        rset = self.execute('Any CONCAT_STRINGS(N) WHERE X is EGroup, X name N')
+        rset = self.execute('Any CONCAT_STRINGS(N) WHERE X is CWGroup, X name N')
         self.failUnless(rset)
         self.failUnlessEqual(sorted(rset[0][0].split(', ')), ['guests', 'managers',
                                                              'owners', 'users'])
 
     def test_select_custom_regproc_limit_size(self):
-        rset = self.execute('Any TEXT_LIMIT_SIZE(N, 3) WHERE X is EGroup, X name N, X name "managers"')
+        rset = self.execute('Any TEXT_LIMIT_SIZE(N, 3) WHERE X is CWGroup, X name N, X name "managers"')
         self.failUnless(rset)
         self.failUnlessEqual(rset[0][0], 'man...')
         self.execute("INSERT Basket X: X name 'bidule', X description '<b>hop hop</b>', X description_format 'text/html'")
@@ -438,10 +438,10 @@
         self.failUnlessEqual(rset[0][0], 'hop...')
 
     def test_select_regproc_orderby(self):
-        rset = self.execute('DISTINCT Any X,N ORDERBY GROUP_SORT_VALUE(N) WHERE X is EGroup, X name N, X name "managers"')
+        rset = self.execute('DISTINCT Any X,N ORDERBY GROUP_SORT_VALUE(N) WHERE X is CWGroup, X name N, X name "managers"')
         self.failUnlessEqual(len(rset), 1)
         self.failUnlessEqual(rset[0][1], 'managers')
-        rset = self.execute('Any X,N ORDERBY GROUP_SORT_VALUE(N) WHERE X is EGroup, X name N, NOT U in_group X, U login "admin"')
+        rset = self.execute('Any X,N ORDERBY GROUP_SORT_VALUE(N) WHERE X is CWGroup, X name N, NOT U in_group X, U login "admin"')
         self.failUnlessEqual(len(rset), 3)
         self.failUnlessEqual(rset[0][1], 'owners')
         
@@ -449,7 +449,7 @@
         rset = self.execute('Any G, COUNT(U) GROUPBY G ORDERBY 2 WHERE U in_group G')
         self.assertEquals(len(rset.rows), 2)
         self.assertEquals(len(rset.rows[0]), 2)
-        self.assertEquals(rset.description[0], ('EGroup', 'Int',))
+        self.assertEquals(rset.description[0], ('CWGroup', 'Int',))
 
     def test_select_aggregat_having(self):
         rset = self.execute('Any N,COUNT(RDEF) GROUPBY N ORDERBY 2,N '
@@ -477,12 +477,12 @@
         self.assertEquals(tuplify(result), [(1,), (2,), (3,), (4,), (5,)])
         
     def test_select_upper(self):
-        rset = self.execute('Any X, UPPER(L) ORDERBY L WHERE X is EUser, X login L')
+        rset = self.execute('Any X, UPPER(L) ORDERBY L WHERE X is CWUser, X login L')
         self.assertEquals(len(rset.rows), 2)
         self.assertEquals(rset.rows[0][1], 'ADMIN')
-        self.assertEquals(rset.description[0], ('EUser', 'String',))
+        self.assertEquals(rset.description[0], ('CWUser', 'String',))
         self.assertEquals(rset.rows[1][1], 'ANON')
-        self.assertEquals(rset.description[1], ('EUser', 'String',))
+        self.assertEquals(rset.description[1], ('CWUser', 'String',))
         eid = rset.rows[0][0]
         rset = self.execute('Any UPPER(L) WHERE X eid %s, X login L'%eid)
         self.assertEquals(rset.rows[0][0], 'ADMIN')
@@ -538,16 +538,16 @@
         self.assertEquals(len(rset.rows), 1, rset.rows)
         
     def test_select_no_descr(self):
-        rset = self.execute('Any X WHERE X is EGroup', build_descr=0)
+        rset = self.execute('Any X WHERE X is CWGroup', build_descr=0)
         rset.rows.sort()
         self.assertEquals(tuplify(rset.rows), [(1,), (2,), (3,), (4,)])
         self.assertEquals(rset.description, ())
 
     def test_select_limit_offset(self):
-        rset = self.execute('EGroup X ORDERBY N LIMIT 2 WHERE X name N')
+        rset = self.execute('CWGroup X ORDERBY N LIMIT 2 WHERE X name N')
         self.assertEquals(tuplify(rset.rows), [(3,), (1,)])
-        self.assertEquals(rset.description, [('EGroup',), ('EGroup',)])
-        rset = self.execute('EGroup X ORDERBY N LIMIT 2 OFFSET 2 WHERE X name N')
+        self.assertEquals(rset.description, [('CWGroup',), ('CWGroup',)])
+        rset = self.execute('CWGroup X ORDERBY N LIMIT 2 OFFSET 2 WHERE X name N')
         self.assertEquals(tuplify(rset.rows), [(4,), (2,)])
         
     def test_select_symetric(self):
@@ -637,7 +637,7 @@
         self.assertEqual(rset.rows, [['abcd'], ['important'], ['minor'], ['normal'], ['zou']])
         
     def test_select_ordered_distinct_3(self):
-        rset = self.execute('DISTINCT Any N ORDERBY GROUP_SORT_VALUE(N) WHERE X is EGroup, X name N')
+        rset = self.execute('DISTINCT Any N ORDERBY GROUP_SORT_VALUE(N) WHERE X is CWGroup, X name N')
         self.assertEqual(rset.rows, [['owners'], ['guests'], ['users'], ['managers']])
 
     def test_select_or_value(self):
@@ -658,14 +658,14 @@
 #         self.assertEquals(rset.rows, [[eid]])
         
     def test_today_bug(self):
-        self.execute("INSERT Tag X: X name 'bidule', X creation_date TODAY")
+        self.execute("INSERT Tag X: X name 'bidule', X creation_date NOW")
         self.execute("INSERT Tag Y: Y name 'toto'")
         rset = self.execute("Any D WHERE X name in ('bidule', 'toto') , X creation_date D")
-        self.assert_(isinstance(rset.rows[0][0], DateTimeType), rset.rows)
+        self.assert_(isinstance(rset.rows[0][0], datetime), rset.rows)
         rset = self.execute('Tag X WHERE X creation_date TODAY')
         self.assertEqual(len(rset.rows), 2)
         rset = self.execute('Any MAX(D) WHERE X is Tag, X creation_date D')
-        self.failUnless(isinstance(rset[0][0], DateTimeType), type(rset[0][0]))
+        self.failUnless(isinstance(rset[0][0], datetime), type(rset[0][0]))
 
     def test_today(self):
         self.execute("INSERT Tag X: X name 'bidule', X creation_date TODAY")
@@ -674,7 +674,7 @@
         self.assertEqual(len(rset.rows), 2)
 
     def test_select_boolean(self):
-        rset = self.execute('Any N WHERE X is EEType, X name N, X final %(val)s',
+        rset = self.execute('Any N WHERE X is CWEType, X name N, X final %(val)s',
                             {'val': True})
         self.assertEquals(sorted(r[0] for r in rset.rows), ['Boolean', 'Bytes',
                                                             'Date', 'Datetime',
@@ -682,7 +682,7 @@
                                                             'Int', 'Interval',
                                                             'Password', 'String',
                                                             'Time'])
-        rset = self.execute('Any N WHERE X is EEType, X name N, X final TRUE')
+        rset = self.execute('Any N WHERE X is CWEType, X name N, X final TRUE')
         self.assertEquals(sorted(r[0] for r in rset.rows), ['Boolean', 'Bytes',
                                                             'Date', 'Datetime',
                                                             'Decimal', 'Float',
@@ -691,28 +691,28 @@
                                                             'Time'])
         
     def test_select_constant(self):
-        rset = self.execute('Any X, "toto" ORDERBY X WHERE X is EGroup')
+        rset = self.execute('Any X, "toto" ORDERBY X WHERE X is CWGroup')
         self.assertEquals(rset.rows,
                           map(list, zip((1,2,3,4), ('toto','toto','toto','toto',))))
         self.assertIsInstance(rset[0][1], unicode)
         self.assertEquals(rset.description,
-                          zip(('EGroup', 'EGroup', 'EGroup', 'EGroup'),
+                          zip(('CWGroup', 'CWGroup', 'CWGroup', 'CWGroup'),
                               ('String', 'String', 'String', 'String',)))
-        rset = self.execute('Any X, %(value)s ORDERBY X WHERE X is EGroup', {'value': 'toto'})
+        rset = self.execute('Any X, %(value)s ORDERBY X WHERE X is CWGroup', {'value': 'toto'})
         self.assertEquals(rset.rows,
                           map(list, zip((1,2,3,4), ('toto','toto','toto','toto',))))
         self.assertIsInstance(rset[0][1], unicode)
         self.assertEquals(rset.description,
-                          zip(('EGroup', 'EGroup', 'EGroup', 'EGroup'),
+                          zip(('CWGroup', 'CWGroup', 'CWGroup', 'CWGroup'),
                               ('String', 'String', 'String', 'String',)))
-        rset = self.execute('Any X,GN WHERE X is EUser, G is EGroup, X login "syt", X in_group G, G name GN')
+        rset = self.execute('Any X,GN WHERE X is CWUser, G is CWGroup, X login "syt", X in_group G, G name GN')
 
     def test_select_union(self):
         rset = self.execute('Any X,N ORDERBY N WITH X,N BEING '
                             '((Any X,N WHERE X name N, X transition_of E, E name %(name)s)'
                             ' UNION '
                             '(Any X,N WHERE X name N, X state_of E, E name %(name)s))',
-                            {'name': 'EUser'})
+                            {'name': 'CWUser'})
         self.assertEquals([x[1] for x in rset.rows],
                           ['activate', 'activated', 'deactivate', 'deactivated'])
         self.assertEquals(rset.description,
@@ -748,7 +748,7 @@
                            'start', 'todo'])
         
     def test_exists(self):
-        geid = self.execute("INSERT EGroup X: X name 'lulufanclub'")[0][0]
+        geid = self.execute("INSERT CWGroup X: X name 'lulufanclub'")[0][0]
         self.execute("SET U in_group G WHERE G name 'lulufanclub'")
         peid = self.execute("INSERT Personne X: X prenom 'lulu', X nom 'petit'")[0][0]
         rset = self.execute("Any X WHERE X prenom 'lulu',"
@@ -764,23 +764,19 @@
         self.assertEquals(login, 'admin')
 
     def test_select_date_mathexp(self):
-        rset = self.execute('Any X, TODAY - CD WHERE X is EUser, X creation_date CD')
+        rset = self.execute('Any X, TODAY - CD WHERE X is CWUser, X creation_date CD')
         self.failUnless(rset)
         self.failUnlessEqual(rset.description[0][1], 'Interval')
         eid, = self.execute("INSERT Personne X: X nom 'bidule'")[0]
         rset = self.execute('Any X, NOW - CD WHERE X is Personne, X creation_date CD')
         self.failUnlessEqual(rset.description[0][1], 'Interval')
-        # sqlite bug
-        #from mx.DateTime import DateTimeDeltaType
-        #self.assertIsInstance(rset[0][1], DateTimeDeltaType) 
-        #self.failUnless(rset[0][1].seconds > 0)
 
     def test_select_subquery_aggregat(self):
         # percent users by groups
         self.execute('SET X in_group G WHERE G name "users"')
         rset = self.execute('Any GN, COUNT(X)*100/T GROUPBY GN ORDERBY 2,1'
                             ' WHERE G name GN, X in_group G'
-                            ' WITH T BEING (Any COUNT(U) WHERE U is EUser)')
+                            ' WITH T BEING (Any COUNT(U) WHERE U is CWUser)')
         self.assertEquals(rset.rows, [[u'guests', 50], [u'managers', 50], [u'users', 100]])
         self.assertEquals(rset.description, [('String', 'Int'), ('String', 'Int'), ('String', 'Int')])
 
@@ -888,13 +884,13 @@
 
         self.assertRaises(QueryError,
                           self.execute,
-                          "INSERT EUser X: X login 'toto', X eid %s" % cnx.user(self.session).eid)
+                          "INSERT CWUser X: X login 'toto', X eid %s" % cnx.user(self.session).eid)
 
     def test_insertion_description_with_where(self):
-        rset = self.execute('INSERT EUser E, EmailAddress EM: E login "X", E upassword "X", '
+        rset = self.execute('INSERT CWUser E, EmailAddress EM: E login "X", E upassword "X", '
                             'E primary_email EM, EM address "X", E in_group G '
                             'WHERE G name "managers"')
-        self.assertEquals(list(rset.description[0]), ['EUser', 'EmailAddress'])
+        self.assertEquals(list(rset.description[0]), ['CWUser', 'EmailAddress'])
     
     # deletion queries tests ##################################################
 
@@ -1031,7 +1027,7 @@
         self.assertEquals(self.execute('Any X WHERE X nom "tutu"').rows, [[peid2]])
 
     def test_update_multiple2(self):
-        ueid = self.execute("INSERT EUser X: X login 'bob', X upassword 'toto'")[0][0]
+        ueid = self.execute("INSERT CWUser X: X login 'bob', X upassword 'toto'")[0][0]
         peid1 = self.execute("INSERT Personne Y: Y nom 'turlu'")[0][0]
         peid2 = self.execute("INSERT Personne Y: Y nom 'tutu'")[0][0]
         self.execute('SET P1 owned_by U, P2 owned_by U '
@@ -1069,33 +1065,33 @@
     # upassword encryption tests #################################################
     
     def test_insert_upassword(self):
-        rset = self.execute("INSERT EUser X: X login 'bob', X upassword 'toto'")
+        rset = self.execute("INSERT CWUser X: X login 'bob', X upassword 'toto'")
         self.assertEquals(len(rset.rows), 1)
-        self.assertEquals(rset.description, [('EUser',)])
+        self.assertEquals(rset.description, [('CWUser',)])
         self.assertRaises(Unauthorized,
-                          self.execute, "Any P WHERE X is EUser, X login 'bob', X upassword P")
+                          self.execute, "Any P WHERE X is CWUser, X login 'bob', X upassword P")
         cursor = self.pool['system']
-        cursor.execute("SELECT %supassword from %sEUser WHERE %slogin='bob'"
+        cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
                        % (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
         passwd = cursor.fetchone()[0].getvalue()
         self.assertEquals(passwd, crypt_password('toto', passwd[:2])) 
-        rset = self.execute("Any X WHERE X is EUser, X login 'bob', X upassword '%s'" % passwd)
+        rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword '%s'" % passwd)
         self.assertEquals(len(rset.rows), 1)
-        self.assertEquals(rset.description, [('EUser',)])
+        self.assertEquals(rset.description, [('CWUser',)])
         
     def test_update_upassword(self):
         cursor = self.pool['system']
-        rset = self.execute("INSERT EUser X: X login 'bob', X upassword %(pwd)s", {'pwd': 'toto'})
-        self.assertEquals(rset.description[0][0], 'EUser')
-        rset = self.execute("SET X upassword %(pwd)s WHERE X is EUser, X login 'bob'",
+        rset = self.execute("INSERT CWUser X: X login 'bob', X upassword %(pwd)s", {'pwd': 'toto'})
+        self.assertEquals(rset.description[0][0], 'CWUser')
+        rset = self.execute("SET X upassword %(pwd)s WHERE X is CWUser, X login 'bob'",
                             {'pwd': 'tutu'})
-        cursor.execute("SELECT %supassword from %sEUser WHERE %slogin='bob'"
+        cursor.execute("SELECT %supassword from %sCWUser WHERE %slogin='bob'"
                        % (SQL_PREFIX, SQL_PREFIX, SQL_PREFIX))
         passwd = cursor.fetchone()[0].getvalue()
         self.assertEquals(passwd, crypt_password('tutu', passwd[:2])) 
-        rset = self.execute("Any X WHERE X is EUser, X login 'bob', X upassword '%s'" % passwd)
+        rset = self.execute("Any X WHERE X is CWUser, X login 'bob', X upassword '%s'" % passwd)
         self.assertEquals(len(rset.rows), 1)
-        self.assertEquals(rset.description, [('EUser',)])
+        self.assertEquals(rset.description, [('CWUser',)])
 
     # non regression tests ####################################################
     
@@ -1111,7 +1107,7 @@
 
     def test_nonregr_2(self):
         teid = self.execute("INSERT Tag X: X name 'tag'")[0][0]
-        geid = self.execute("EGroup G WHERE G name 'users'")[0][0]
+        geid = self.execute("CWGroup G WHERE G name 'users'")[0][0]
         self.execute("SET X tags Y WHERE X eid %(t)s, Y eid %(g)s",
                        {'g': geid, 't': teid})
         rset = self.execute('Any X WHERE E eid %(x)s, E tags X',
@@ -1122,7 +1118,7 @@
         """bad sql generated on the second query (destination_state is not
         detected as an inlined relation)
         """
-        rset = self.execute('Any S,ES,T WHERE S state_of ET, ET name "EUser",'
+        rset = self.execute('Any S,ES,T WHERE S state_of ET, ET name "CWUser",'
                              'ES allowed_transition T, T destination_state S')
         self.assertEquals(len(rset.rows), 2)
 
@@ -1131,7 +1127,7 @@
         # union queries and that make for instance a 266Ko sql query which is refused
         # by the server (or client lib)
         rset = self.execute('Any ER,SE,OE WHERE SE name "Comment", ER name "comments", OE name "Comment",'
-                            'ER is ERType, SE is EEType, OE is EEType')
+                            'ER is CWRType, SE is CWEType, OE is CWEType')
         self.assertEquals(len(rset), 1)
 
     def test_nonregr_5(self):
@@ -1247,19 +1243,19 @@
 
     def test_nonregr_set_datetime(self):
         # huum, psycopg specific
-        self.execute('SET X creation_date %(date)s WHERE X eid 1', {'date': today()})
+        self.execute('SET X creation_date %(date)s WHERE X eid 1', {'date': date.today()})
 
     def test_nonregr_set_query(self):
-        ueid = self.execute("INSERT EUser X: X login 'bob', X upassword 'toto'")[0][0]
+        ueid = self.execute("INSERT CWUser X: X login 'bob', X upassword 'toto'")[0][0]
         self.execute("SET E in_group G, E in_state S, "
                       "E firstname %(firstname)s, E surname %(surname)s "
                       "WHERE E eid %(x)s, G name 'users', S name 'activated'",
                       {'x':ueid, 'firstname': u'jean', 'surname': u'paul'}, 'x')
         
     def test_nonregr_u_owned_by_u(self):
-        ueid = self.execute("INSERT EUser X: X login 'bob', X upassword 'toto', X in_group G "
+        ueid = self.execute("INSERT CWUser X: X login 'bob', X upassword 'toto', X in_group G "
                              "WHERE G name 'users'")[0][0]
-        rset = self.execute("EUser U")
+        rset = self.execute("CWUser U")
         self.assertEquals(len(rset), 3) # bob + admin + anon
         rset = self.execute("Any U WHERE NOT U owned_by U")
         self.assertEquals(len(rset), 0) # even admin created at repo initialization time should belong to itself
@@ -1269,18 +1265,18 @@
         self.execute("SET X description D WHERE X is State, X description D")
 
     def test_nonregr_is(self):
-        uteid = self.execute('Any ET WHERE ET name "EUser"')[0][0]
+        uteid = self.execute('Any ET WHERE ET name "CWUser"')[0][0]
         self.execute('Any X, ET WHERE X is ET, ET eid %s' % uteid)
 
     def test_nonregr_orderby(self):
         seid = self.execute('Any X WHERE X name "activated"')[0][0]
-        self.execute('Any X,S, MAX(T) GROUPBY X,S ORDERBY S WHERE X is EUser, T tags X, S eid IN(%s), X in_state S' % seid)
+        self.execute('Any X,S, MAX(T) GROUPBY X,S ORDERBY S WHERE X is CWUser, T tags X, S eid IN(%s), X in_state S' % seid)
 
     def test_nonregr_solution_cache(self):
         self.skip('XXX should be fixed or documented') # (doesn't occur if cache key is provided.)
-        rset = self.execute('Any X WHERE X is EUser, X eid %(x)s', {'x':self.ueid})
+        rset = self.execute('Any X WHERE X is CWUser, X eid %(x)s', {'x':self.ueid})
         self.assertEquals(len(rset), 1)
-        rset = self.execute('Any X WHERE X is EUser, X eid %(x)s', {'x':12345})
+        rset = self.execute('Any X WHERE X is CWUser, X eid %(x)s', {'x':12345})
         self.assertEquals(len(rset), 0)
 
     def test_nonregr_final_norestr(self):
--- a/server/test/unittest_repository.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_repository.py	Mon May 04 13:09:48 2009 +0200
@@ -6,8 +6,7 @@
 import threading
 import time
 from copy import deepcopy
-
-from mx.DateTime import DateTimeType, now
+from datetime import datetime
 
 from logilab.common.testlib import TestCase, unittest_main
 
@@ -47,7 +46,7 @@
         self.repo.config._cubes = None # avoid assertion error
         self.repo.fill_schema()
         pool = self.repo._get_pool()
-        table = SQL_PREFIX + 'EEType'
+        table = SQL_PREFIX + 'CWEType'
         namecol = SQL_PREFIX + 'name'
         finalcol = SQL_PREFIX + 'final'
         try:
@@ -69,12 +68,12 @@
     def test_schema_has_owner(self):
         repo = self.repo
         cnxid = repo.connect(*self.default_user_password())
-        self.failIf(repo.execute(cnxid, 'EEType X WHERE NOT X owned_by U'))
-        self.failIf(repo.execute(cnxid, 'ERType X WHERE NOT X owned_by U'))
-        self.failIf(repo.execute(cnxid, 'EFRDef X WHERE NOT X owned_by U'))
-        self.failIf(repo.execute(cnxid, 'ENFRDef X WHERE NOT X owned_by U'))
-        self.failIf(repo.execute(cnxid, 'EConstraint X WHERE NOT X owned_by U'))
-        self.failIf(repo.execute(cnxid, 'EConstraintType X WHERE NOT X owned_by U'))
+        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'))
+        self.failIf(repo.execute(cnxid, 'CWRelation X WHERE NOT X owned_by U'))
+        self.failIf(repo.execute(cnxid, 'CWConstraint X WHERE NOT X owned_by U'))
+        self.failIf(repo.execute(cnxid, 'CWConstraintType X WHERE NOT X owned_by U'))
         
     def test_connect(self):
         login, passwd = self.default_user_password()
@@ -98,7 +97,7 @@
     def test_login_upassword_accent(self):
         repo = self.repo
         cnxid = repo.connect(*self.default_user_password())
-        repo.execute(cnxid, 'INSERT EUser X: X login %(login)s, X upassword %(passwd)s, X in_state S, X in_group G WHERE S name "activated", G name "users"',
+        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)
         repo.close(cnxid)
@@ -107,10 +106,10 @@
     def test_invalid_entity_rollback(self):
         repo = self.repo
         cnxid = repo.connect(*self.default_user_password())
-        repo.execute(cnxid, 'INSERT EUser X: X login %(login)s, X upassword %(passwd)s, X in_state S WHERE S name "activated"',
+        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'})
         self.assertRaises(ValidationError, repo.commit, cnxid)
-        rset = repo.execute(cnxid, 'EUser X WHERE X login "tutetute"')
+        rset = repo.execute(cnxid, 'CWUser X WHERE X login "tutetute"')
         self.assertEquals(rset.rowcount, 0)
         
     def test_close(self):
@@ -202,7 +201,7 @@
     def test_initial_schema(self):
         schema = self.repo.schema
         # check order of attributes is respected
-        self.assertListEquals([r.type for r in schema.eschema('EFRDef').ordered_relations()
+        self.assertListEquals([r.type for r in schema.eschema('CWAttribute').ordered_relations()
                                if not r.type in ('eid', 'is', 'is_instance_of', 'identity', 
                                                  'creation_date', 'modification_date',
                                                  'owned_by', 'created_by')],
@@ -211,10 +210,10 @@
                                'indexed', 'fulltextindexed', 'internationalizable',
                                'defaultval', 'description_format', 'description'])
 
-        self.assertEquals(schema.eschema('EEType').main_attribute(), 'name')
+        self.assertEquals(schema.eschema('CWEType').main_attribute(), 'name')
         self.assertEquals(schema.eschema('State').main_attribute(), 'name')
 
-        constraints = schema.rschema('name').rproperty('EEType', 'String', 'constraints')
+        constraints = schema.rschema('name').rproperty('CWEType', 'String', 'constraints')
         self.assertEquals(len(constraints), 2)
         for cstr in constraints[:]:
             if isinstance(cstr, UniqueConstraint):
@@ -226,14 +225,14 @@
         self.assertEquals(sizeconstraint.min, None)
         self.assertEquals(sizeconstraint.max, 64)
 
-        constraints = schema.rschema('relation_type').rproperty('EFRDef', 'ERType', 'constraints')
+        constraints = schema.rschema('relation_type').rproperty('CWAttribute', 'CWRType', 'constraints')
         self.assertEquals(len(constraints), 1)
         cstr = constraints[0]
         self.assert_(isinstance(cstr, RQLConstraint))
         self.assertEquals(cstr.restriction, 'O final TRUE')
 
         ownedby = schema.rschema('owned_by')
-        self.assertEquals(ownedby.objects('EEType'), ('EUser',))
+        self.assertEquals(ownedby.objects('CWEType'), ('CWUser',))
 
     def test_pyro(self):
         import Pyro
@@ -268,8 +267,8 @@
         repo = self.repo
         cnxid = repo.connect(*self.default_user_password())
         session = repo._get_session(cnxid, setpool=True)
-        self.assertEquals(repo.type_and_source_from_eid(1, session), ('EGroup', 'system', None))
-        self.assertEquals(repo.type_from_eid(1, session), 'EGroup')
+        self.assertEquals(repo.type_and_source_from_eid(1, session), ('CWGroup', 'system', None))
+        self.assertEquals(repo.type_from_eid(1, session), 'CWGroup')
         self.assertEquals(repo.source_from_eid(1, session).uri, 'system')
         self.assertEquals(repo.eid2extid(repo.system_source, 1, session), None)
         class dummysource: uri = 'toto'
@@ -279,13 +278,13 @@
         self.assertEquals(self.repo.get_schema(), self.repo.schema)
         self.assertEquals(self.repo.source_defs(), {'system': {'adapter': 'native', 'uri': 'system'}})
         # .properties() return a result set
-        self.assertEquals(self.repo.properties().rql, 'Any K,V WHERE P is EProperty,P pkey K, P value V, NOT P for_user U')
+        self.assertEquals(self.repo.properties().rql, 'Any K,V WHERE P is CWProperty,P pkey K, P value V, NOT P for_user U')
 
     def test_session_api(self):
         repo = self.repo
         cnxid = repo.connect(*self.default_user_password())
         self.assertEquals(repo.user_info(cnxid), (5, 'admin', set([u'managers']), {}))
-        self.assertEquals(repo.describe(cnxid, 1), (u'EGroup', u'system', None))
+        self.assertEquals(repo.describe(cnxid, 1), (u'CWGroup', u'system', None))
         repo.close(cnxid)
         self.assertRaises(BadConnectionId, repo.user_info, cnxid)
         self.assertRaises(BadConnectionId, repo.describe, cnxid, 1)
@@ -326,7 +325,7 @@
         self.assertRaises(UnknownEid, self.repo.source_from_eid, -2, self.session)
 
     def test_type_from_eid(self):
-        self.assertEquals(self.repo.type_from_eid(1, self.session), 'EGroup')
+        self.assertEquals(self.repo.type_from_eid(1, self.session), 'CWGroup')
         
     def test_type_from_eid_raise(self):
         self.assertRaises(UnknownEid, self.repo.type_from_eid, -2, self.session)
@@ -339,7 +338,7 @@
         cursor = self.session.pool['system']
         cursor.execute('SELECT * FROM entities WHERE eid = -1')
         data = cursor.fetchall()
-        self.assertIsInstance(data[0][3], DateTimeType)
+        self.assertIsInstance(data[0][3], datetime)
         data[0] = list(data[0])
         data[0][3] = None
         self.assertEquals(tuplify(data), [(-1, 'Personne', 'system', None, None)])
@@ -356,7 +355,7 @@
         cursor = self.session.pool['system']
         eidp = self.execute('INSERT Personne X: X nom "toto", X prenom "tutu"')[0][0]
         self.commit()
-        ts = now()
+        ts = datetime.now()
         self.assertEquals(len(self.execute('Personne X WHERE X has_text "tutu"')), 1)
         cursor.execute('SELECT mtime, eid FROM entities WHERE eid = %s' % eidp)
         omtime = cursor.fetchone()[0]
@@ -450,7 +449,7 @@
         """make sure after_<event>_relation hooks are deferred"""
         self.hm.register_hook(self._after_relation_hook,
                              'after_add_relation', 'in_state')
-        eidp = self.execute('INSERT EUser X: X login "toto", X upassword "tutu", X in_state S WHERE S name "activated"')[0][0]
+        eidp = self.execute('INSERT CWUser X: X login "toto", X upassword "tutu", X in_state S WHERE S name "activated"')[0][0]
         eids = self.execute('State X WHERE X name "activated"')[0][0]
         self.assertEquals(self.called, [(eidp, 'in_state', eids,)])
     
--- a/server/test/unittest_rql2sql.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_rql2sql.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,6 @@
 """unit tests for module cubicweb.server.sources.rql2sql"""
 
 import sys
-from mx.DateTime import today
 
 from logilab.common.testlib import TestCase, unittest_main
 
@@ -122,14 +121,14 @@
 FROM cw_Affaire AS X
 WHERE DATE(X.cw_creation_date)=CURRENT_DATE'''),
 
-    ("Any N WHERE G is EGroup, G name N, E eid 12, E read_permission G",
+    ("Any N WHERE G is CWGroup, G name N, E eid 12, E read_permission G",
      '''SELECT G.cw_name
-FROM cw_EGroup AS G, read_permission_relation AS rel_read_permission0
+FROM cw_CWGroup AS G, read_permission_relation AS rel_read_permission0
 WHERE rel_read_permission0.eid_from=12 AND rel_read_permission0.eid_to=G.cw_eid'''),
 
     ('Any Y WHERE U login "admin", U login Y', # stupid but valid...
      """SELECT U.cw_login
-FROM cw_EUser AS U
+FROM cw_CWUser AS U
 WHERE U.cw_login=admin"""),
 
     ('Any T WHERE T tags X, X is State',
@@ -146,13 +145,13 @@
      "EXISTS(X owned_by U, U in_group G, G name 'lulufanclub' OR G name 'managers');",
      '''SELECT X.cw_eid
 FROM cw_Personne AS X
-WHERE X.cw_prenom=lulu AND EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0, in_group_relation AS rel_in_group1, cw_EGroup AS G WHERE rel_owned_by0.eid_from=X.cw_eid AND rel_in_group1.eid_from=rel_owned_by0.eid_to AND rel_in_group1.eid_to=G.cw_eid AND ((G.cw_name=lulufanclub) OR (G.cw_name=managers)))'''),
+WHERE X.cw_prenom=lulu AND EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0, in_group_relation AS rel_in_group1, cw_CWGroup AS G WHERE rel_owned_by0.eid_from=X.cw_eid AND rel_in_group1.eid_from=rel_owned_by0.eid_to AND rel_in_group1.eid_to=G.cw_eid AND ((G.cw_name=lulufanclub) OR (G.cw_name=managers)))'''),
 
     ("Any X WHERE X prenom 'lulu',"
      "NOT EXISTS(X owned_by U, U in_group G, G name 'lulufanclub' OR G name 'managers');",
      '''SELECT X.cw_eid
 FROM cw_Personne AS X
-WHERE X.cw_prenom=lulu AND NOT EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0, in_group_relation AS rel_in_group1, cw_EGroup AS G WHERE rel_owned_by0.eid_from=X.cw_eid AND rel_in_group1.eid_from=rel_owned_by0.eid_to AND rel_in_group1.eid_to=G.cw_eid AND ((G.cw_name=lulufanclub) OR (G.cw_name=managers)))'''),
+WHERE X.cw_prenom=lulu AND NOT EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0, in_group_relation AS rel_in_group1, cw_CWGroup AS G WHERE rel_owned_by0.eid_from=X.cw_eid AND rel_in_group1.eid_from=rel_owned_by0.eid_to AND rel_in_group1.eid_to=G.cw_eid AND ((G.cw_name=lulufanclub) OR (G.cw_name=managers)))'''),
 ]
 
 ADVANCED= [
@@ -235,11 +234,11 @@
     
     ('Any SEN,RN,OEN WHERE X from_entity SE, SE eid 44, X relation_type R, R eid 139, X to_entity OE, OE eid 42, R name RN, SE name SEN, OE name OEN',
      '''SELECT SE.cw_name, R.cw_name, OE.cw_name
-FROM cw_EEType AS OE, cw_EEType AS SE, cw_EFRDef AS X, cw_ERType AS R
+FROM cw_CWEType AS OE, cw_CWEType AS SE, cw_CWAttribute AS X, cw_CWRType AS R
 WHERE X.cw_from_entity=44 AND SE.cw_eid=44 AND X.cw_relation_type=139 AND R.cw_eid=139 AND X.cw_to_entity=42 AND OE.cw_eid=42
 UNION ALL
 SELECT SE.cw_name, R.cw_name, OE.cw_name
-FROM cw_EEType AS OE, cw_EEType AS SE, cw_ENFRDef AS X, cw_ERType AS R
+FROM cw_CWEType AS OE, cw_CWEType AS SE, cw_CWRelation AS X, cw_CWRType AS R
 WHERE X.cw_from_entity=44 AND SE.cw_eid=44 AND X.cw_relation_type=139 AND R.cw_eid=139 AND X.cw_to_entity=42 AND OE.cw_eid=42'''),
 
     # Any O WHERE NOT S corrected_in O, S eid %(x)s, S concerns P, O version_of P, O in_state ST, NOT ST name "published", O modification_date MTIME ORDERBY MTIME DESC LIMIT 9
@@ -273,8 +272,8 @@
 
     ('Any GN WHERE X in_group G, G name GN, (G name "managers" OR EXISTS(X copain T, T login in ("comme", "cochon")))',
      '''SELECT G.cw_name
-FROM cw_EGroup AS G, in_group_relation AS rel_in_group0
-WHERE rel_in_group0.eid_to=G.cw_eid AND ((G.cw_name=managers) OR (EXISTS(SELECT 1 FROM copain_relation AS rel_copain1, cw_EUser AS T WHERE rel_copain1.eid_from=rel_in_group0.eid_from AND rel_copain1.eid_to=T.cw_eid AND T.cw_login IN(comme, cochon))))'''),
+FROM cw_CWGroup AS G, in_group_relation AS rel_in_group0
+WHERE rel_in_group0.eid_to=G.cw_eid AND ((G.cw_name=managers) OR (EXISTS(SELECT 1 FROM copain_relation AS rel_copain1, cw_CWUser AS T WHERE rel_copain1.eid_from=rel_in_group0.eid_from AND rel_copain1.eid_to=T.cw_eid AND T.cw_login IN(comme, cochon))))'''),
 
     ('Any C WHERE C is Card, EXISTS(X documented_by C)',
       """SELECT C.cw_eid
@@ -293,12 +292,12 @@
 
     ('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"))',
      '''SELECT G.cw_name, X.cw_login
-FROM cw_EGroup AS G, cw_EUser AS X, in_group_relation AS rel_in_group0
-WHERE rel_in_group0.eid_from=X.cw_eid AND rel_in_group0.eid_to=G.cw_eid AND EXISTS(SELECT 1 FROM copain_relation AS rel_copain1, cw_EUser AS T WHERE rel_copain1.eid_from=X.cw_eid AND rel_copain1.eid_to=T.cw_eid AND T.cw_login=X.cw_login AND T.cw_login IN(comme, cochon))'''),
+FROM cw_CWGroup AS G, cw_CWUser AS X, in_group_relation AS rel_in_group0
+WHERE rel_in_group0.eid_from=X.cw_eid AND rel_in_group0.eid_to=G.cw_eid AND EXISTS(SELECT 1 FROM copain_relation AS rel_copain1, cw_CWUser AS T WHERE rel_copain1.eid_from=X.cw_eid AND rel_copain1.eid_to=T.cw_eid AND T.cw_login=X.cw_login AND T.cw_login IN(comme, cochon))'''),
 
-    ('Any X,S, MAX(T) GROUPBY X,S ORDERBY S WHERE X is EUser, T tags X, S eid IN(32), X in_state S',
+    ('Any X,S, MAX(T) GROUPBY X,S ORDERBY S WHERE X is CWUser, T tags X, S eid IN(32), X in_state S',
      '''SELECT X.cw_eid, 32, MAX(rel_tags0.eid_from)
-FROM cw_EUser AS X, tags_relation AS rel_tags0
+FROM cw_CWUser AS X, tags_relation AS rel_tags0
 WHERE rel_tags0.eid_to=X.cw_eid AND X.cw_in_state=32
 GROUP BY X.cw_eid'''),
 
@@ -310,24 +309,24 @@
 ORDER BY 1 DESC
 LIMIT 10'''),
 
-    ('Any X WHERE Y evaluee X, Y is EUser',
+    ('Any X WHERE Y evaluee X, Y is CWUser',
      '''SELECT rel_evaluee0.eid_to
-FROM cw_EUser AS Y, evaluee_relation AS rel_evaluee0
+FROM cw_CWUser AS Y, evaluee_relation AS rel_evaluee0
 WHERE rel_evaluee0.eid_from=Y.cw_eid'''),
 
     ('Any L WHERE X login "admin", X identity Y, Y login L',
      '''SELECT Y.cw_login
-FROM cw_EUser AS X, cw_EUser AS Y
+FROM cw_CWUser AS X, cw_CWUser AS Y
 WHERE X.cw_login=admin AND X.cw_eid=Y.cw_eid'''),
 
     ('Any L WHERE X login "admin", NOT X identity Y, Y login L',
      '''SELECT Y.cw_login
-FROM cw_EUser AS X, cw_EUser AS Y
+FROM cw_CWUser AS X, cw_CWUser AS Y
 WHERE X.cw_login=admin AND NOT X.cw_eid=Y.cw_eid'''),
     
     ('Any L WHERE X login "admin", X identity Y?, Y login L',
      '''SELECT Y.cw_login
-FROM cw_EUser AS X LEFT OUTER JOIN cw_EUser AS Y ON (X.cw_eid=Y.cw_eid)
+FROM cw_CWUser AS X LEFT OUTER JOIN cw_CWUser AS Y ON (X.cw_eid=Y.cw_eid)
 WHERE X.cw_login=admin'''),
 
     ('Any XN ORDERBY XN WHERE X name XN',
@@ -335,22 +334,22 @@
 FROM cw_Basket AS X
 UNION ALL
 SELECT X.cw_name
-FROM cw_ECache AS X
+FROM cw_CWCache AS X
 UNION ALL
 SELECT X.cw_name
-FROM cw_EConstraintType AS X
+FROM cw_CWConstraintType AS X
 UNION ALL
 SELECT X.cw_name
-FROM cw_EEType AS X
+FROM cw_CWEType AS X
 UNION ALL
 SELECT X.cw_name
-FROM cw_EGroup AS X
+FROM cw_CWGroup AS X
 UNION ALL
 SELECT X.cw_name
-FROM cw_EPermission AS X
+FROM cw_CWPermission AS X
 UNION ALL
 SELECT X.cw_name
-FROM cw_ERType AS X
+FROM cw_CWRType AS X
 UNION ALL
 SELECT X.cw_name
 FROM cw_File AS X
@@ -377,108 +376,108 @@
 #     ''''''),
 
     # DISTINCT, can use relatin under exists scope as principal
-    ('DISTINCT Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)',
+    ('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)',
      '''SELECT DISTINCT X.cw_eid, rel_read_permission0.eid_to
-FROM cw_EEType AS X, read_permission_relation AS rel_read_permission0
-WHERE X.cw_name=EGroup AND rel_read_permission0.eid_to IN(1, 2, 3) AND EXISTS(SELECT 1 WHERE rel_read_permission0.eid_from=X.cw_eid)
+FROM cw_CWEType AS X, read_permission_relation AS rel_read_permission0
+WHERE X.cw_name=CWGroup AND rel_read_permission0.eid_to IN(1, 2, 3) AND EXISTS(SELECT 1 WHERE rel_read_permission0.eid_from=X.cw_eid)
 UNION
 SELECT DISTINCT X.cw_eid, rel_read_permission0.eid_to
-FROM cw_ERType AS X, read_permission_relation AS rel_read_permission0
-WHERE X.cw_name=EGroup AND rel_read_permission0.eid_to IN(1, 2, 3) AND EXISTS(SELECT 1 WHERE rel_read_permission0.eid_from=X.cw_eid)'''),
+FROM cw_CWRType AS X, read_permission_relation AS rel_read_permission0
+WHERE X.cw_name=CWGroup AND rel_read_permission0.eid_to IN(1, 2, 3) AND EXISTS(SELECT 1 WHERE rel_read_permission0.eid_from=X.cw_eid)'''),
 
     # no distinct, Y can't be invariant
-    ('Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)',
+    ('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)',
      '''SELECT X.cw_eid, Y.cw_eid
-FROM cw_EEType AS X, cw_EGroup AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWEType AS X, cw_CWGroup AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION ALL
 SELECT X.cw_eid, Y.cw_eid
-FROM cw_EEType AS X, cw_RQLExpression AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWEType AS X, cw_RQLExpression AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION ALL
 SELECT X.cw_eid, Y.cw_eid
-FROM cw_EGroup AS Y, cw_ERType AS X
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWGroup AS Y, cw_CWRType AS X
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION ALL
 SELECT X.cw_eid, Y.cw_eid
-FROM cw_ERType AS X, cw_RQLExpression AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)'''),
+FROM cw_CWRType AS X, cw_RQLExpression AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)'''),
 
     # DISTINCT but NEGED exists, can't be invariant
-    ('DISTINCT Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)',
+    ('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)',
      '''SELECT DISTINCT X.cw_eid, Y.cw_eid
-FROM cw_EEType AS X, cw_EGroup AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWEType AS X, cw_CWGroup AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION
 SELECT DISTINCT X.cw_eid, Y.cw_eid
-FROM cw_EEType AS X, cw_RQLExpression AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWEType AS X, cw_RQLExpression AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION
 SELECT DISTINCT X.cw_eid, Y.cw_eid
-FROM cw_EGroup AS Y, cw_ERType AS X
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWGroup AS Y, cw_CWRType AS X
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION
 SELECT DISTINCT X.cw_eid, Y.cw_eid
-FROM cw_ERType AS X, cw_RQLExpression AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)'''),
+FROM cw_CWRType AS X, cw_RQLExpression AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)'''),
 
     # should generate the same query as above
-    ('DISTINCT Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), NOT X read_permission Y',
+    ('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y',
      '''SELECT DISTINCT X.cw_eid, Y.cw_eid
-FROM cw_EEType AS X, cw_EGroup AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWEType AS X, cw_CWGroup AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION
 SELECT DISTINCT X.cw_eid, Y.cw_eid
-FROM cw_EEType AS X, cw_RQLExpression AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWEType AS X, cw_RQLExpression AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION
 SELECT DISTINCT X.cw_eid, Y.cw_eid
-FROM cw_EGroup AS Y, cw_ERType AS X
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWGroup AS Y, cw_CWRType AS X
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION
 SELECT DISTINCT X.cw_eid, Y.cw_eid
-FROM cw_ERType AS X, cw_RQLExpression AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)'''),
+FROM cw_CWRType AS X, cw_RQLExpression AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)'''),
     
     # neged relation, can't be inveriant
-    ('Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), NOT X read_permission Y',
+    ('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y',
      '''SELECT X.cw_eid, Y.cw_eid
-FROM cw_EEType AS X, cw_EGroup AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWEType AS X, cw_CWGroup AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION ALL
 SELECT X.cw_eid, Y.cw_eid
-FROM cw_EEType AS X, cw_RQLExpression AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWEType AS X, cw_RQLExpression AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION ALL
 SELECT X.cw_eid, Y.cw_eid
-FROM cw_EGroup AS Y, cw_ERType AS X
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
+FROM cw_CWGroup AS Y, cw_CWRType AS X
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)
 UNION ALL
 SELECT X.cw_eid, Y.cw_eid
-FROM cw_ERType AS X, cw_RQLExpression AS Y
-WHERE X.cw_name=EGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)'''),
+FROM cw_CWRType AS X, cw_RQLExpression AS Y
+WHERE X.cw_name=CWGroup AND Y.cw_eid IN(1, 2, 3) AND NOT EXISTS(SELECT 1 FROM read_permission_relation AS rel_read_permission0 WHERE rel_read_permission0.eid_from=X.cw_eid AND rel_read_permission0.eid_to=Y.cw_eid)'''),
 
     ('Any MAX(X)+MIN(X), N GROUPBY N WHERE X name N;',
      '''SELECT (MAX(T1.C0) + MIN(T1.C0)), T1.C1 FROM (SELECT X.cw_eid AS C0, X.cw_name AS C1
 FROM cw_Basket AS X
 UNION ALL
 SELECT X.cw_eid AS C0, X.cw_name AS C1
-FROM cw_ECache AS X
+FROM cw_CWCache AS X
 UNION ALL
 SELECT X.cw_eid AS C0, X.cw_name AS C1
-FROM cw_EConstraintType AS X
+FROM cw_CWConstraintType AS X
 UNION ALL
 SELECT X.cw_eid AS C0, X.cw_name AS C1
-FROM cw_EEType AS X
+FROM cw_CWEType AS X
 UNION ALL
 SELECT X.cw_eid AS C0, X.cw_name AS C1
-FROM cw_EGroup AS X
+FROM cw_CWGroup AS X
 UNION ALL
 SELECT X.cw_eid AS C0, X.cw_name AS C1
-FROM cw_EPermission AS X
+FROM cw_CWPermission AS X
 UNION ALL
 SELECT X.cw_eid AS C0, X.cw_name AS C1
-FROM cw_ERType AS X
+FROM cw_CWRType AS X
 UNION ALL
 SELECT X.cw_eid AS C0, X.cw_name AS C1
 FROM cw_File AS X
@@ -524,25 +523,25 @@
 '''),
 
     # ambiguity in EXISTS() -> should union the sub-query
-    ('Any T WHERE T is Tag, NOT T name in ("t1", "t2"), EXISTS(T tags X, X is IN (EUser, EGroup))',
+    ('Any T WHERE T is Tag, NOT T name in ("t1", "t2"), EXISTS(T tags X, X is IN (CWUser, CWGroup))',
      '''SELECT T.cw_eid
 FROM cw_Tag AS T
-WHERE NOT (T.cw_name IN(t1, t2)) AND EXISTS(SELECT 1 FROM tags_relation AS rel_tags0, cw_EGroup AS X WHERE rel_tags0.eid_from=T.cw_eid AND rel_tags0.eid_to=X.cw_eid UNION SELECT 1 FROM tags_relation AS rel_tags1, cw_EUser AS X WHERE rel_tags1.eid_from=T.cw_eid AND rel_tags1.eid_to=X.cw_eid)'''),
+WHERE NOT (T.cw_name IN(t1, t2)) AND EXISTS(SELECT 1 FROM tags_relation AS rel_tags0, cw_CWGroup AS X WHERE rel_tags0.eid_from=T.cw_eid AND rel_tags0.eid_to=X.cw_eid UNION SELECT 1 FROM tags_relation AS rel_tags1, cw_CWUser AS X WHERE rel_tags1.eid_from=T.cw_eid AND rel_tags1.eid_to=X.cw_eid)'''),
 
     # must not use a relation in EXISTS scope to inline a variable 
     ('Any U WHERE U eid IN (1,2), EXISTS(X owned_by U)',
      '''SELECT U.cw_eid
-FROM cw_EUser AS U
+FROM cw_CWUser AS U
 WHERE U.cw_eid IN(1, 2) AND EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0 WHERE rel_owned_by0.eid_to=U.cw_eid)'''),
 
     ('Any U WHERE EXISTS(U eid IN (1,2), X owned_by U)',
      '''SELECT U.cw_eid
-FROM cw_EUser AS U
+FROM cw_CWUser AS U
 WHERE EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0 WHERE U.cw_eid IN(1, 2) AND rel_owned_by0.eid_to=U.cw_eid)'''),
 
     ('Any COUNT(U) WHERE EXISTS (P owned_by U, P is IN (Note, Affaire))',
      '''SELECT COUNT(U.cw_eid)
-FROM cw_EUser AS U
+FROM cw_CWUser AS U
 WHERE EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0, cw_Affaire AS P WHERE rel_owned_by0.eid_from=P.cw_eid AND rel_owned_by0.eid_to=U.cw_eid UNION SELECT 1 FROM owned_by_relation AS rel_owned_by1, cw_Note AS P WHERE rel_owned_by1.eid_from=P.cw_eid AND rel_owned_by1.eid_to=U.cw_eid)'''),
 
     ('Any MAX(X)',
@@ -585,7 +584,7 @@
     
     ('Any X GROUPBY X ORDERBY Y WHERE X eid 12, X login Y',
      '''SELECT X.cw_eid
-FROM cw_EUser AS X
+FROM cw_CWUser AS X
 WHERE X.cw_eid=12
 GROUP BY X.cw_eid
 ORDER BY X.cw_login'''),
@@ -599,12 +598,12 @@
 
     ('DISTINCT Any X ORDERBY stockproc(X) WHERE U login X',
      '''SELECT T1.C0 FROM (SELECT DISTINCT U.cw_login AS C0, STOCKPROC(U.cw_login) AS C1
-FROM cw_EUser AS U
+FROM cw_CWUser AS U
 ORDER BY 2) AS T1'''),
     
     ('DISTINCT Any X ORDERBY Y WHERE B bookmarked_by X, X login Y',
      '''SELECT T1.C0 FROM (SELECT DISTINCT X.cw_eid AS C0, X.cw_login AS C1
-FROM bookmarked_by_relation AS rel_bookmarked_by0, cw_EUser AS X
+FROM bookmarked_by_relation AS rel_bookmarked_by0, cw_CWUser AS X
 WHERE rel_bookmarked_by0.eid_to=X.cw_eid
 ORDER BY 2) AS T1'''),
 
@@ -614,7 +613,7 @@
 WHERE X.cw_in_state=S.cw_eid
 UNION
 SELECT DISTINCT X.cw_eid AS C0, S.cw_name AS C1
-FROM cw_EUser AS X, cw_State AS S
+FROM cw_CWUser AS X, cw_State AS S
 WHERE X.cw_in_state=S.cw_eid
 UNION
 SELECT DISTINCT X.cw_eid AS C0, S.cw_name AS C1
@@ -680,7 +679,7 @@
     
     ('Any S WHERE T is Tag, T name TN, NOT T eid 28258, T tags S, S name SN',
      '''SELECT S.cw_eid
-FROM cw_EGroup AS S, cw_Tag AS T, tags_relation AS rel_tags0
+FROM cw_CWGroup AS S, cw_Tag AS T, tags_relation AS rel_tags0
 WHERE NOT (T.cw_eid=28258) AND rel_tags0.eid_from=T.cw_eid AND rel_tags0.eid_to=S.cw_eid
 UNION ALL
 SELECT S.cw_eid
@@ -708,7 +707,7 @@
 WHERE NOT EXISTS(SELECT 1 FROM evaluee_relation AS rel_evaluee0 WHERE rel_evaluee0.eid_from=Y.cw_eid)
 UNION ALL
 SELECT Y.cw_eid
-FROM cw_EUser AS Y
+FROM cw_CWUser AS Y
 WHERE NOT EXISTS(SELECT 1 FROM evaluee_relation AS rel_evaluee0 WHERE rel_evaluee0.eid_from=Y.cw_eid)
 UNION ALL
 SELECT Y.cw_eid
@@ -723,10 +722,10 @@
 FROM cw_SubDivision AS Y
 WHERE NOT EXISTS(SELECT 1 FROM evaluee_relation AS rel_evaluee0 WHERE rel_evaluee0.eid_from=Y.cw_eid)'''),
 
-    ('Any X WHERE NOT Y evaluee X, Y is EUser',
+    ('Any X WHERE NOT Y evaluee X, Y is CWUser',
      '''SELECT X.cw_eid
 FROM cw_Note AS X
-WHERE NOT EXISTS(SELECT 1 FROM evaluee_relation AS rel_evaluee0,cw_EUser AS Y WHERE rel_evaluee0.eid_from=Y.cw_eid AND rel_evaluee0.eid_to=X.cw_eid)'''),
+WHERE NOT EXISTS(SELECT 1 FROM evaluee_relation AS rel_evaluee0,cw_CWUser AS Y WHERE rel_evaluee0.eid_from=Y.cw_eid AND rel_evaluee0.eid_to=X.cw_eid)'''),
     
     ('Any X,T WHERE X title T, NOT X is Bookmark',
      '''SELECT DISTINCT X.cw_eid, X.cw_title
@@ -735,18 +734,18 @@
 SELECT DISTINCT X.cw_eid, X.cw_title
 FROM cw_EmailThread AS X'''),
 
-    ('Any K,V WHERE P is EProperty, P pkey K, P value V, NOT P for_user U',
+    ('Any K,V WHERE P is CWProperty, P pkey K, P value V, NOT P for_user U',
      '''SELECT DISTINCT P.cw_pkey, P.cw_value
-FROM cw_EProperty AS P
+FROM cw_CWProperty AS P
 WHERE P.cw_for_user IS NULL'''),
 
-    ('Any S WHERE NOT X in_state S, X is IN(Affaire, EUser)',
+    ('Any S WHERE NOT X in_state S, X is IN(Affaire, CWUser)',
      '''SELECT DISTINCT S.cw_eid
 FROM cw_Affaire AS X, cw_State AS S
 WHERE (X.cw_in_state IS NULL OR X.cw_in_state!=S.cw_eid)
 INTERSECT
 SELECT DISTINCT S.cw_eid
-FROM cw_EUser AS X, cw_State AS S
+FROM cw_CWUser AS X, cw_State AS S
 WHERE (X.cw_in_state IS NULL OR X.cw_in_state!=S.cw_eid)'''),
     ]
 
@@ -792,7 +791,7 @@
     ('Any X WHERE X is Affaire, S is Societe, EXISTS(X owned_by U OR (X concerne S?, S owned_by U))',
      '''SELECT X.cw_eid
 FROM cw_Affaire AS X
-WHERE EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0, cw_EUser AS U, cw_Affaire AS A LEFT OUTER JOIN concerne_relation AS rel_concerne1 ON (rel_concerne1.eid_from=A.cw_eid) LEFT OUTER JOIN cw_Societe AS S ON (rel_concerne1.eid_to=S.cw_eid), owned_by_relation AS rel_owned_by2 WHERE ((rel_owned_by0.eid_from=A.cw_eid AND rel_owned_by0.eid_to=U.cw_eid) OR (rel_owned_by2.eid_from=S.cw_eid AND rel_owned_by2.eid_to=U.cw_eid)) AND X.cw_eid=A.cw_eid)'''),
+WHERE EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0, cw_CWUser AS U, cw_Affaire AS A LEFT OUTER JOIN concerne_relation AS rel_concerne1 ON (rel_concerne1.eid_from=A.cw_eid) LEFT OUTER JOIN cw_Societe AS S ON (rel_concerne1.eid_to=S.cw_eid), owned_by_relation AS rel_owned_by2 WHERE ((rel_owned_by0.eid_from=A.cw_eid AND rel_owned_by0.eid_to=U.cw_eid) OR (rel_owned_by2.eid_from=S.cw_eid AND rel_owned_by2.eid_to=U.cw_eid)) AND X.cw_eid=A.cw_eid)'''),
 
     ('Any C,M WHERE C travaille G?, G evaluee M?, G is Societe',
      '''SELECT C.cw_eid, rel_evaluee1.eid_to
@@ -805,7 +804,7 @@
      'F name "read", F require_group E, U in_group E)), U eid 1',
      '''SELECT A.cw_eid, rel_documented_by0.eid_to
 FROM cw_Affaire AS A LEFT OUTER JOIN documented_by_relation AS rel_documented_by0 ON (rel_documented_by0.eid_from=A.cw_eid)
-WHERE ((rel_documented_by0.eid_to IS NULL) OR (EXISTS(SELECT 1 FROM require_permission_relation AS rel_require_permission1, cw_EPermission AS F, require_group_relation AS rel_require_group2, in_group_relation AS rel_in_group3 WHERE rel_documented_by0.eid_to=rel_require_permission1.eid_from AND rel_require_permission1.eid_to=F.cw_eid AND F.cw_name=read AND rel_require_group2.eid_from=F.cw_eid AND rel_in_group3.eid_to=rel_require_group2.eid_to AND rel_in_group3.eid_from=1)))'''),
+WHERE ((rel_documented_by0.eid_to IS NULL) OR (EXISTS(SELECT 1 FROM require_permission_relation AS rel_require_permission1, cw_CWPermission AS F, require_group_relation AS rel_require_group2, in_group_relation AS rel_in_group3 WHERE rel_documented_by0.eid_to=rel_require_permission1.eid_from AND rel_require_permission1.eid_to=F.cw_eid AND F.cw_name=read AND rel_require_group2.eid_from=F.cw_eid AND rel_in_group3.eid_to=rel_require_group2.eid_to AND rel_in_group3.eid_from=1)))'''),
 
     ("Any X WHERE X eid 12, P? connait X",
      '''SELECT X.cw_eid
@@ -819,7 +818,7 @@
     ('Any GN, TN ORDERBY GN WHERE T tags G?, T name TN, G name GN',
     '''SELECT _T0.C1, T.cw_name
 FROM cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN (SELECT G.cw_eid AS C0, G.cw_name AS C1
-FROM cw_EGroup AS G
+FROM cw_CWGroup AS G
 UNION ALL
 SELECT G.cw_eid AS C0, G.cw_name AS C1
 FROM cw_State AS G
@@ -830,9 +829,9 @@
 
 
     # optional variable with additional restriction
-    ('Any T,G WHERE T tags G?, G name "hop", G is EGroup',
+    ('Any T,G WHERE T tags G?, G name "hop", G is CWGroup',
      '''SELECT T.cw_eid, G.cw_eid
-FROM cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN cw_EGroup AS G ON (rel_tags0.eid_to=G.cw_eid AND G.cw_name=hop)'''),
+FROM cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN cw_CWGroup AS G ON (rel_tags0.eid_to=G.cw_eid AND G.cw_name=hop)'''),
 
     # optional variable with additional invariant restriction
     ('Any T,G WHERE T tags G?, G eid 12',
@@ -840,33 +839,33 @@
 FROM cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid AND rel_tags0.eid_to=12)'''),
 
     # optional variable with additional restriction appearing before the relation
-    ('Any T,G WHERE G name "hop", T tags G?, G is EGroup',
+    ('Any T,G WHERE G name "hop", T tags G?, G is CWGroup',
      '''SELECT T.cw_eid, G.cw_eid
-FROM cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN cw_EGroup AS G ON (rel_tags0.eid_to=G.cw_eid AND G.cw_name=hop)'''),
+FROM cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN cw_CWGroup AS G ON (rel_tags0.eid_to=G.cw_eid AND G.cw_name=hop)'''),
 
     # optional variable with additional restriction on inlined relation
     # XXX the expected result should be as the query below. So what, raise BadRQLQuery ?
-    ('Any T,G,S WHERE T tags G?, G in_state S, S name "hop", G is EUser',
+    ('Any T,G,S WHERE T tags G?, G in_state S, S name "hop", G is CWUser',
      '''SELECT T.cw_eid, G.cw_eid, S.cw_eid
-FROM cw_State AS S, cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN cw_EUser AS G ON (rel_tags0.eid_to=G.cw_eid)
+FROM cw_State AS S, cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN cw_CWUser AS G ON (rel_tags0.eid_to=G.cw_eid)
 WHERE G.cw_in_state=S.cw_eid AND S.cw_name=hop
 '''),
 
     # optional variable with additional invariant restriction on an inlined relation
-    ('Any T,G,S WHERE T tags G, G in_state S?, S eid 1, G is EUser',
+    ('Any T,G,S WHERE T tags G, G in_state S?, S eid 1, G is CWUser',
      '''SELECT rel_tags0.eid_from, G.cw_eid, G.cw_in_state
-FROM cw_EUser AS G, tags_relation AS rel_tags0
+FROM cw_CWUser AS G, tags_relation AS rel_tags0
 WHERE rel_tags0.eid_to=G.cw_eid AND (G.cw_in_state=1 OR G.cw_in_state IS NULL)'''),
 
     # two optional variables with additional invariant restriction on an inlined relation
-    ('Any T,G,S WHERE T tags G?, G in_state S?, S eid 1, G is EUser',
+    ('Any T,G,S WHERE T tags G?, G in_state S?, S eid 1, G is CWUser',
      '''SELECT T.cw_eid, G.cw_eid, G.cw_in_state
-FROM cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN cw_EUser AS G ON (rel_tags0.eid_to=G.cw_eid AND (G.cw_in_state=1 OR G.cw_in_state IS NULL))'''),
+FROM cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN cw_CWUser AS G ON (rel_tags0.eid_to=G.cw_eid AND (G.cw_in_state=1 OR G.cw_in_state IS NULL))'''),
 
     # two optional variables with additional restriction on an inlined relation
-    ('Any T,G,S WHERE T tags G?, G in_state S?, S name "hop", G is EUser',
+    ('Any T,G,S WHERE T tags G?, G in_state S?, S name "hop", G is CWUser',
      '''SELECT T.cw_eid, G.cw_eid, S.cw_eid
-FROM cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN cw_EUser AS G ON (rel_tags0.eid_to=G.cw_eid) LEFT OUTER JOIN cw_State AS S ON (G.cw_in_state=S.cw_eid AND S.cw_name=hop)'''),
+FROM cw_Tag AS T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_from=T.cw_eid) LEFT OUTER JOIN cw_CWUser AS G ON (rel_tags0.eid_to=G.cw_eid) LEFT OUTER JOIN cw_State AS S ON (G.cw_in_state=S.cw_eid AND S.cw_name=hop)'''),
     
     # two optional variables with additional restriction on an ambigous inlined relation
     ('Any T,G,S WHERE T tags G?, G in_state S?, S name "hop"',
@@ -875,7 +874,7 @@
 FROM cw_Affaire AS G LEFT OUTER JOIN cw_State AS S ON (G.cw_in_state=S.cw_eid AND S.cw_name=hop) 
 UNION ALL
 SELECT G.cw_eid AS C0, S.cw_eid AS C1
-FROM cw_EUser AS G LEFT OUTER JOIN cw_State AS S ON (G.cw_in_state=S.cw_eid AND S.cw_name=hop) 
+FROM cw_CWUser AS G LEFT OUTER JOIN cw_State AS S ON (G.cw_in_state=S.cw_eid AND S.cw_name=hop) 
 UNION ALL
 SELECT G.cw_eid AS C0, S.cw_eid AS C1
 FROM cw_Note AS G LEFT OUTER JOIN cw_State AS S ON (G.cw_in_state=S.cw_eid AND S.cw_name=hop) ) AS _T0 ON (rel_tags0.eid_to=_T0.C0)'''),
@@ -1040,17 +1039,17 @@
 FROM cw_Note AS N, cw_Personne AS P
 WHERE (N.cw_ecrit_par IS NULL OR N.cw_ecrit_par!=P.cw_eid) AND N.cw_eid=512'''),
 
-    ('Any S,ES,T WHERE S state_of ET, ET name "EUser", ES allowed_transition T, T destination_state S',
+    ('Any S,ES,T WHERE S state_of ET, ET name "CWUser", ES allowed_transition T, T destination_state S',
      '''SELECT T.cw_destination_state, rel_allowed_transition1.eid_from, T.cw_eid
-FROM allowed_transition_relation AS rel_allowed_transition1, cw_EEType AS ET, cw_Transition AS T, state_of_relation AS rel_state_of0
-WHERE T.cw_destination_state=rel_state_of0.eid_from AND rel_state_of0.eid_to=ET.cw_eid AND ET.cw_name=EUser AND rel_allowed_transition1.eid_to=T.cw_eid'''),
+FROM allowed_transition_relation AS rel_allowed_transition1, cw_CWEType AS ET, cw_Transition AS T, state_of_relation AS rel_state_of0
+WHERE T.cw_destination_state=rel_state_of0.eid_from AND rel_state_of0.eid_to=ET.cw_eid AND ET.cw_name=CWUser AND rel_allowed_transition1.eid_to=T.cw_eid'''),
     ('Any O WHERE S eid 0, S in_state O',
      '''SELECT S.cw_in_state
 FROM cw_Affaire AS S
 WHERE S.cw_eid=0 AND S.cw_in_state IS NOT NULL
 UNION ALL
 SELECT S.cw_in_state
-FROM cw_EUser AS S
+FROM cw_CWUser AS S
 WHERE S.cw_eid=0 AND S.cw_in_state IS NOT NULL
 UNION ALL
 SELECT S.cw_in_state
@@ -1066,7 +1065,7 @@
 WHERE (X.cw_in_state IS NULL OR X.cw_in_state!=S.cw_eid)
 INTERSECT
 SELECT DISTINCT S.cw_name
-FROM cw_EUser AS X, cw_State AS S
+FROM cw_CWUser AS X, cw_State AS S
 WHERE (X.cw_in_state IS NULL OR X.cw_in_state!=S.cw_eid)
 INTERSECT
 SELECT DISTINCT S.cw_name
@@ -1172,11 +1171,11 @@
     def test1(self):
         self._checkall('Any count(RDEF) WHERE RDEF relation_type X, X eid %(x)s',
                        ("""SELECT COUNT(T1.C0) FROM (SELECT RDEF.cw_eid AS C0
-FROM cw_EFRDef AS RDEF
+FROM cw_CWAttribute AS RDEF
 WHERE RDEF.cw_relation_type=%(x)s
 UNION ALL
 SELECT RDEF.cw_eid AS C0
-FROM cw_ENFRDef AS RDEF
+FROM cw_CWRelation AS RDEF
 WHERE RDEF.cw_relation_type=%(x)s) AS T1""", {}),
                        )
 
@@ -1199,14 +1198,14 @@
 WHERE rel_in_basket0.eid_to=12''')
 
     def test_varmap(self):
-        self._check('Any X,L WHERE X is EUser, X in_group G, X login L, G name "users"',
+        self._check('Any X,L WHERE X is CWUser, X in_group G, X login L, G name "users"',
                     '''SELECT T00.x, T00.l
-FROM T00, cw_EGroup AS G, in_group_relation AS rel_in_group0
+FROM T00, cw_CWGroup AS G, in_group_relation AS rel_in_group0
 WHERE rel_in_group0.eid_from=T00.x AND rel_in_group0.eid_to=G.cw_eid AND G.cw_name=users''',
                     varmap={'X': 'T00.x', 'X.login': 'T00.l'})
-        self._check('Any X,L,GN WHERE X is EUser, X in_group G, X login L, G name GN',
+        self._check('Any X,L,GN WHERE X is CWUser, X in_group G, X login L, G name GN',
                     '''SELECT T00.x, T00.l, G.cw_name
-FROM T00, cw_EGroup AS G, in_group_relation AS rel_in_group0
+FROM T00, cw_CWGroup AS G, in_group_relation AS rel_in_group0
 WHERE rel_in_group0.eid_from=T00.x AND rel_in_group0.eid_to=G.cw_eid''',
                     varmap={'X': 'T00.x', 'X.login': 'T00.l'})
 
@@ -1312,7 +1311,7 @@
 UNION ALL
 (SELECT X.cw_eid AS C0
 FROM cw_Affaire AS X
-WHERE ((EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0 WHERE rel_owned_by0.eid_from=X.cw_eid AND rel_owned_by0.eid_to=1)) OR (((EXISTS(SELECT 1 FROM cw_Affaire AS D LEFT OUTER JOIN concerne_relation AS rel_concerne1 ON (rel_concerne1.eid_from=D.cw_eid) LEFT OUTER JOIN cw_Note AS B ON (rel_concerne1.eid_to=B.cw_eid), owned_by_relation AS rel_owned_by2 WHERE rel_owned_by2.eid_from=B.cw_eid AND rel_owned_by2.eid_to=1 AND X.cw_eid=D.cw_eid)) OR (EXISTS(SELECT 1 FROM cw_Affaire AS F LEFT OUTER JOIN concerne_relation AS rel_concerne3 ON (rel_concerne3.eid_from=F.cw_eid) LEFT OUTER JOIN cw_Societe AS E ON (rel_concerne3.eid_to=E.cw_eid), owned_by_relation AS rel_owned_by4 WHERE rel_owned_by4.eid_from=E.cw_eid AND rel_owned_by4.eid_to=1 AND X.cw_eid=F.cw_eid))))))) AS _T0, cw_EEType AS ET, is_relation AS rel_is0
+WHERE ((EXISTS(SELECT 1 FROM owned_by_relation AS rel_owned_by0 WHERE rel_owned_by0.eid_from=X.cw_eid AND rel_owned_by0.eid_to=1)) OR (((EXISTS(SELECT 1 FROM cw_Affaire AS D LEFT OUTER JOIN concerne_relation AS rel_concerne1 ON (rel_concerne1.eid_from=D.cw_eid) LEFT OUTER JOIN cw_Note AS B ON (rel_concerne1.eid_to=B.cw_eid), owned_by_relation AS rel_owned_by2 WHERE rel_owned_by2.eid_from=B.cw_eid AND rel_owned_by2.eid_to=1 AND X.cw_eid=D.cw_eid)) OR (EXISTS(SELECT 1 FROM cw_Affaire AS F LEFT OUTER JOIN concerne_relation AS rel_concerne3 ON (rel_concerne3.eid_from=F.cw_eid) LEFT OUTER JOIN cw_Societe AS E ON (rel_concerne3.eid_to=E.cw_eid), owned_by_relation AS rel_owned_by4 WHERE rel_owned_by4.eid_from=E.cw_eid AND rel_owned_by4.eid_to=1 AND X.cw_eid=F.cw_eid))))))) AS _T0, cw_CWEType AS ET, is_relation AS rel_is0
 WHERE rel_is0.eid_from=_T0.C0 AND rel_is0.eid_to=ET.cw_eid
 GROUP BY ET.cw_name'''),
             )):
@@ -1390,9 +1389,9 @@
 
 
     def test_from_clause_needed(self):
-        queries = [("Any 1 WHERE EXISTS(T is EGroup, T name 'managers')",
+        queries = [("Any 1 WHERE EXISTS(T is CWGroup, T name 'managers')",
                     '''SELECT 1
-WHERE EXISTS(SELECT 1 FROM cw_EGroup AS T WHERE T.cw_name=managers)'''),
+WHERE EXISTS(SELECT 1 FROM cw_CWGroup AS T WHERE T.cw_name=managers)'''),
                    ('Any X,Y WHERE NOT X created_by Y, X eid 5, Y eid 6',
                     '''SELECT 5, 6
 WHERE NOT EXISTS(SELECT 1 FROM created_by_relation AS rel_created_by0 WHERE rel_created_by0.eid_from=5 AND rel_created_by0.eid_to=6)'''),
@@ -1549,10 +1548,10 @@
         return sql.strip().replace(' ILIKE ', ' LIKE ')
 
     def test_from_clause_needed(self):
-        queries = [("Any 1 WHERE EXISTS(T is EGroup, T name 'managers')",
+        queries = [("Any 1 WHERE EXISTS(T is CWGroup, T name 'managers')",
                     '''SELECT 1
 FROM (SELECT 1) AS _T
-WHERE EXISTS(SELECT 1 FROM cw_EGroup AS T WHERE T.cw_name=managers)'''),
+WHERE EXISTS(SELECT 1 FROM cw_CWGroup AS T WHERE T.cw_name=managers)'''),
                    ('Any X,Y WHERE NOT X created_by Y, X eid 5, Y eid 6',
                     '''SELECT 5, 6
 FROM (SELECT 1) AS _T
--- a/server/test/unittest_rqlannotation.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_rqlannotation.py	Mon May 04 13:09:48 2009 +0200
@@ -37,7 +37,7 @@
         self.assert_(rqlst.defined_vars['B'].stinfo['attrvar'])
         self.assertEquals(rqlst.defined_vars['C']._q_invariant, False)
         self.assertEquals(rqlst.solutions, [{'A': 'TrInfo', 'B': 'String', 'C': 'Affaire'},
-                                      {'A': 'TrInfo', 'B': 'String', 'C': 'EUser'},
+                                      {'A': 'TrInfo', 'B': 'String', 'C': 'CWUser'},
                                       {'A': 'TrInfo', 'B': 'String', 'C': 'Note'}])
 
     def test_0_5(self):        
@@ -176,7 +176,7 @@
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
         
     def test_not_relation_4_4(self):
-        rqlst = self._prepare('Any X WHERE NOT Y evaluee X, Y is EUser')
+        rqlst = self._prepare('Any X WHERE NOT Y evaluee X, Y is CWUser')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
 
@@ -186,12 +186,12 @@
         self.assertEquals(rqlst.solutions, [{'X': 'Note'}])
         
     def test_not_relation_5_1(self):
-        rqlst = self._prepare('Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), NOT X read_permission Y')
+        rqlst = self._prepare('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
 
     def test_not_relation_5_2(self):
-        rqlst = self._prepare('DISTINCT Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), NOT X read_permission Y')
+        rqlst = self._prepare('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT X read_permission Y')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
 
@@ -201,7 +201,7 @@
         self.assertEquals(rqlst.defined_vars['A']._q_invariant, True)
 
     def test_not_relation_7(self):
-        rqlst = self._prepare('Any K,V WHERE P is EProperty, P pkey K, P value V, NOT P for_user U') 
+        rqlst = self._prepare('Any K,V WHERE P is CWProperty, P pkey K, P value V, NOT P for_user U') 
         self.assertEquals(rqlst.defined_vars['P']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['U']._q_invariant, True)
        
@@ -221,12 +221,12 @@
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)
         
     def test_exists_4(self):
-        rqlst = self._prepare('Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)')
+        rqlst = self._prepare('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
         
     def test_exists_5(self):
-        rqlst = self._prepare('DISTINCT Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)')
+        rqlst = self._prepare('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), EXISTS(X read_permission Y)')
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, False)
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, True)
 
@@ -236,11 +236,11 @@
         self.assertEquals(rqlst.defined_vars['X']._q_invariant, True)        
 
     def test_not_exists_2(self):        
-        rqlst = self._prepare('Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)')
+        rqlst = self._prepare('Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)')
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
 
     def test_not_exists_distinct_1(self):        
-        rqlst = self._prepare('DISTINCT Any X,Y WHERE X name "EGroup", Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)')
+        rqlst = self._prepare('DISTINCT Any X,Y WHERE X name "CWGroup", Y eid IN(1, 2, 3), NOT EXISTS(X read_permission Y)')
         self.assertEquals(rqlst.defined_vars['Y']._q_invariant, False)
         
     def test_or_1(self):        
--- a/server/test/unittest_rqlrewrite.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_rqlrewrite.py	Mon May 04 13:09:48 2009 +0200
@@ -22,7 +22,7 @@
     repotest.undo_monkey_patch()
     
 def eid_func_map(eid):
-    return {1: 'EUser',
+    return {1: 'CWUser',
             2: 'Card'}[eid]
 
 def rewrite(rqlst, snippets_map, kwargs):
@@ -74,7 +74,7 @@
         self.failUnlessEqual(rqlst.as_string(),
                              u"Any C WHERE C is Card, B eid %(D)s, "
                              "EXISTS(C in_state A, B in_group E, F require_state A, "
-                             "F name 'read', F require_group E, A is State, E is EGroup, F is EPermission)")
+                             "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission)")
         
     def test_multiple_var(self):
         card_constraint = ('X in_state S, U in_group G, P require_state S,'
@@ -87,8 +87,8 @@
         self.assertTextEquals(rqlst.as_string(),
                              "Any S WHERE S documented_by C, C eid %(u)s, B eid %(D)s, "
                              "EXISTS(C in_state A, B in_group E, F require_state A, "
-                             "F name 'read', F require_group E, A is State, E is EGroup, F is EPermission), "
-                             "(EXISTS(S ref LIKE 'PUBLIC%')) OR (EXISTS(B in_group G, G name 'public', G is EGroup)), "
+                             "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission), "
+                             "(EXISTS(S ref LIKE 'PUBLIC%')) OR (EXISTS(B in_group G, G name 'public', G is CWGroup)), "
                              "S is Affaire")
         self.failUnless('D' in kwargs)
         
@@ -99,8 +99,8 @@
         self.failUnlessEqual(rqlst.as_string(),
                              "Any S WHERE S owned_by C, C eid %(u)s, A eid %(B)s, "
                              "EXISTS((C identity A) OR (C in_state D, E identity A, "
-                             "E in_state D, D name 'subscribed'), D is State, E is EUser), "
-                             "S is IN(Affaire, Basket, Bookmark, Card, Comment, Division, ECache, EConstraint, EConstraintType, EEType, EFRDef, EGroup, ENFRDef, EPermission, EProperty, ERType, EUser, Email, EmailAddress, EmailPart, EmailThread, File, Folder, Image, Note, Personne, RQLExpression, Societe, State, SubDivision, Tag, TrInfo, Transition)")
+                             "E in_state D, D name 'subscribed'), D is State, E is CWUser), "
+                             "S is IN(Affaire, Basket, Bookmark, Card, Comment, Division, CWCache, CWConstraint, CWConstraintType, CWEType, CWAttribute, CWGroup, CWRelation, CWPermission, CWProperty, CWRType, CWUser, Email, EmailAddress, EmailPart, EmailThread, File, Folder, Image, Note, Personne, RQLExpression, Societe, State, SubDivision, Tag, TrInfo, Transition)")
 
     def test_simplified_rqlst(self):
         card_constraint = ('X in_state S, U in_group G, P require_state S,'
@@ -110,7 +110,7 @@
         self.failUnlessEqual(rqlst.as_string(),
                              u"Any 2 WHERE B eid %(C)s, "
                              "EXISTS(2 in_state A, B in_group D, E require_state A, "
-                             "E name 'read', E require_group D, A is State, D is EGroup, E is EPermission)")
+                             "E name 'read', E require_group D, A is State, D is CWGroup, E is CWPermission)")
 
     def test_optional_var(self):
         card_constraint = ('X in_state S, U in_group G, P require_state S,'
@@ -141,22 +141,22 @@
         self.failUnlessEqual(rqlst.as_string(),
                              u"Any C WHERE C in_state STATE, C is Card, A eid %(B)s, "
                              "EXISTS(A in_group D, E require_state STATE, "
-                             "E name 'read', E require_group D, D is EGroup, E is EPermission), "
+                             "E name 'read', E require_group D, D is CWGroup, E is CWPermission), "
                              "STATE is State")
 
     def test_unsupported_constraint_1(self):
-        # EUser doesn't have require_permission
+        # CWUser doesn't have require_permission
         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
-        rqlst = parse('Any U,T WHERE U is EUser, T wf_info_for U')
+        rqlst = parse('Any U,T WHERE U is CWUser, T wf_info_for U')
         self.assertRaises(Unauthorized, rewrite, rqlst, {'T': (trinfo_constraint,)}, {})
         
     def test_unsupported_constraint_2(self):
         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
-        rqlst = parse('Any U,T WHERE U is EUser, T wf_info_for U')
+        rqlst = parse('Any U,T WHERE U is CWUser, T wf_info_for U')
         rewrite(rqlst, {'T': (trinfo_constraint, 'X wf_info_for Y, Y in_group G, G name "managers"')}, {})
         self.failUnlessEqual(rqlst.as_string(),
-                             u"Any U,T WHERE U is EUser, T wf_info_for U, "
-                             "EXISTS(U in_group B, B name 'managers', B is EGroup), T is TrInfo")
+                             u"Any U,T WHERE U is CWUser, T wf_info_for U, "
+                             "EXISTS(U in_group B, B name 'managers', B is CWGroup), T is TrInfo")
 
     def test_unsupported_constraint_3(self):
         self.skip('raise unauthorized for now')
--- a/server/test/unittest_schemaserial.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_schemaserial.py	Mon May 04 13:09:48 2009 +0200
@@ -20,16 +20,16 @@
 class Schema2RQLTC(TestCase):
         
     def test_eschema2rql1(self):
-        self.assertListEquals(list(eschema2rql(schema.eschema('EFRDef'))),
+        self.assertListEquals(list(eschema2rql(schema.eschema('CWAttribute'))),
                               [
-            ('INSERT EEType X: X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s',
+            ('INSERT CWEType X: X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s',
              {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the application schema',
-              'meta': True, 'name': u'EFRDef', 'final': False})
+              'meta': True, 'name': u'CWAttribute', 'final': False})
             ])
         
     def test_eschema2rql2(self):
         self.assertListEquals(list(eschema2rql(schema.eschema('String'))), [
-                ('INSERT EEType X: X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s',
+                ('INSERT CWEType X: X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s',
                  {'description': u'', 'final': True, 'meta': True, 'name': u'String'})])
     
     def test_eschema2rql_specialization(self):
@@ -43,32 +43,32 @@
     def test_rschema2rql1(self):
         self.assertListEquals(list(rschema2rql(schema.rschema('relation_type'))),
                              [
-            ('INSERT ERType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s',
+            ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s',
              {'description': u'link a relation definition to its relation type', 'meta': True, 'symetric': False, 'name': u'relation_type', 'final' : False, 'fulltext_container': None, 'inlined': True}),
-            ('INSERT ENFRDef X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
-             {'rt': 'relation_type', 'description': u'', 'composite': u'object', 'oe': 'ERType',
-              'ordernum': 1, 'cardinality': u'1*', 'se': 'EFRDef'}),
-            ('INSERT EConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is ENFRDef',
-             {'rt': 'relation_type', 'oe': 'ERType', 'ctname': u'RQLConstraint', 'se': 'EFRDef', 'value': u'O final TRUE'}),
-            ('INSERT ENFRDef X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
-             {'rt': 'relation_type', 'description': u'', 'composite': u'object', 'oe': 'ERType',
-              'ordernum': 1, 'cardinality': u'1*', 'se': 'ENFRDef'}),
-            ('INSERT EConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is ENFRDef',
-             {'rt': 'relation_type', 'oe': 'ERType', 'ctname': u'RQLConstraint', 'se': 'ENFRDef', 'value': u'O final FALSE'}),
+            ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
+             {'rt': 'relation_type', 'description': u'', 'composite': u'object', 'oe': 'CWRType',
+              'ordernum': 1, 'cardinality': u'1*', 'se': 'CWAttribute'}),
+            ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is CWRelation',
+             {'rt': 'relation_type', 'oe': 'CWRType', 'ctname': u'RQLConstraint', 'se': 'CWAttribute', 'value': u'O final TRUE'}),
+            ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
+             {'rt': 'relation_type', 'description': u'', 'composite': u'object', 'oe': 'CWRType',
+              'ordernum': 1, 'cardinality': u'1*', 'se': 'CWRelation'}),
+            ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is CWRelation',
+             {'rt': 'relation_type', 'oe': 'CWRType', 'ctname': u'RQLConstraint', 'se': 'CWRelation', 'value': u'O final FALSE'}),
             ])
         
     def test_rschema2rql2(self):
         expected = [
-            ('INSERT ERType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s', {'description': u'core relation giving to a group the permission to add an entity or relation type', 'meta': True, 'symetric': False, 'name': u'add_permission', 'final': False, 'fulltext_container': None, 'inlined': False}),
-            ('INSERT ENFRDef X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
-             {'rt': 'add_permission', 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'oe': 'RQLExpression', 'ordernum': 5, 'cardinality': u'*?', 'se': 'EEType'}),
-            ('INSERT ENFRDef X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
-             {'rt': 'add_permission', 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'oe': 'RQLExpression', 'ordernum': 5, 'cardinality': u'*?', 'se': 'ERType'}),
+            ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s', {'description': u'core relation giving to a group the permission to add an entity or relation type', 'meta': True, 'symetric': False, 'name': u'add_permission', 'final': False, 'fulltext_container': None, 'inlined': False}),
+            ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
+             {'rt': 'add_permission', 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'oe': 'RQLExpression', 'ordernum': 5, 'cardinality': u'*?', 'se': 'CWEType'}),
+            ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
+             {'rt': 'add_permission', 'description': u'rql expression allowing to add entities/relations of this type', 'composite': 'subject', 'oe': 'RQLExpression', 'ordernum': 5, 'cardinality': u'*?', 'se': 'CWRType'}),
             
-            ('INSERT ENFRDef X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
-             {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'EGroup', 'ordernum': 3, 'cardinality': u'**', 'se': 'EEType'}),
-            ('INSERT ENFRDef X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
-             {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'EGroup', 'ordernum': 3, 'cardinality': u'**', 'se': 'ERType'}),
+            ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
+             {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'CWGroup', 'ordernum': 3, 'cardinality': u'**', 'se': 'CWEType'}),
+            ('INSERT CWRelation X: X cardinality %(cardinality)s,X composite %(composite)s,X description %(description)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
+             {'rt': 'add_permission', 'description': u'groups allowed to add entities/relations of this type', 'composite': None, 'oe': 'CWGroup', 'ordernum': 3, 'cardinality': u'**', 'se': 'CWRType'}),
             ]
         for i, (rql, args) in enumerate(rschema2rql(schema.rschema('add_permission'))):
             yield self.assertEquals, (rql, args), expected[i]
@@ -76,41 +76,41 @@
     def test_rschema2rql3(self):
         self.assertListEquals(list(rschema2rql(schema.rschema('cardinality'))), 
                              [
-            ('INSERT ERType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s',
+            ('INSERT CWRType X: X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s',
              {'description': u'', 'meta': False, 'symetric': False, 'name': u'cardinality', 'final': True, 'fulltext_container': None, 'inlined': False}),
 
-            ('INSERT EFRDef X: X cardinality %(cardinality)s,X defaultval %(defaultval)s,X description %(description)s,X fulltextindexed %(fulltextindexed)s,X indexed %(indexed)s,X internationalizable %(internationalizable)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
-             {'rt': 'cardinality', 'description': u'subject/object cardinality', 'internationalizable': True, 'fulltextindexed': False, 'ordernum': 5, 'defaultval': None, 'indexed': False, 'cardinality': u'?1', 'oe': 'String', 'se': 'EFRDef'}),
-            ('INSERT EConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is EFRDef',
-             {'rt': 'cardinality', 'oe': 'String', 'ctname': u'SizeConstraint', 'se': 'EFRDef', 'value': u'max=2'}),
-            ('INSERT EConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is EFRDef',
-             {'rt': 'cardinality', 'oe': 'String', 'ctname': u'StaticVocabularyConstraint', 'se': 'EFRDef', 'value': u"u'?1', u'11', u'??', u'1?'"}),
+            ('INSERT CWAttribute X: X cardinality %(cardinality)s,X defaultval %(defaultval)s,X description %(description)s,X fulltextindexed %(fulltextindexed)s,X indexed %(indexed)s,X internationalizable %(internationalizable)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
+             {'rt': 'cardinality', 'description': u'subject/object cardinality', 'internationalizable': True, 'fulltextindexed': False, 'ordernum': 5, 'defaultval': None, 'indexed': False, 'cardinality': u'?1', 'oe': 'String', 'se': 'CWAttribute'}),
+            ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is CWAttribute',
+             {'rt': 'cardinality', 'oe': 'String', 'ctname': u'SizeConstraint', 'se': 'CWAttribute', 'value': u'max=2'}),
+            ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is CWAttribute',
+             {'rt': 'cardinality', 'oe': 'String', 'ctname': u'StaticVocabularyConstraint', 'se': 'CWAttribute', 'value': u"u'?1', u'11', u'??', u'1?'"}),
 
-            ('INSERT EFRDef X: X cardinality %(cardinality)s,X defaultval %(defaultval)s,X description %(description)s,X fulltextindexed %(fulltextindexed)s,X indexed %(indexed)s,X internationalizable %(internationalizable)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
-             {'rt': 'cardinality', 'description': u'subject/object cardinality', 'internationalizable': True, 'fulltextindexed': False, 'ordernum': 5, 'defaultval': None, 'indexed': False, 'cardinality': u'?1', 'oe': 'String', 'se': 'ENFRDef'}),
-            ('INSERT EConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is EFRDef',
-             {'rt': 'cardinality', 'oe': 'String', 'ctname': u'SizeConstraint', 'se': 'ENFRDef', 'value': u'max=2'}),
-            ('INSERT EConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is EFRDef',
-             {'rt': 'cardinality', 'oe': 'String', 'ctname': u'StaticVocabularyConstraint', 'se': 'ENFRDef', 'value': u"u'?*', u'1*', u'+*', u'**', u'?+', u'1+', u'++', u'*+', u'?1', u'11', u'+1', u'*1', u'??', u'1?', u'+?', u'*?'"}),
+            ('INSERT CWAttribute X: X cardinality %(cardinality)s,X defaultval %(defaultval)s,X description %(description)s,X fulltextindexed %(fulltextindexed)s,X indexed %(indexed)s,X internationalizable %(internationalizable)s,X ordernum %(ordernum)s,X relation_type ER,X from_entity SE,X to_entity OE WHERE SE name %(se)s,ER name %(rt)s,OE name %(oe)s',
+             {'rt': 'cardinality', 'description': u'subject/object cardinality', 'internationalizable': True, 'fulltextindexed': False, 'ordernum': 5, 'defaultval': None, 'indexed': False, 'cardinality': u'?1', 'oe': 'String', 'se': 'CWRelation'}),
+            ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is CWAttribute',
+             {'rt': 'cardinality', 'oe': 'String', 'ctname': u'SizeConstraint', 'se': 'CWRelation', 'value': u'max=2'}),
+            ('INSERT CWConstraint X: X value %(value)s, X cstrtype CT, EDEF constrained_by X WHERE CT name %(ctname)s, EDEF relation_type ER, EDEF from_entity SE, EDEF to_entity OE, ER name %(rt)s, SE name %(se)s, OE name %(oe)s, EDEF is CWAttribute',
+             {'rt': 'cardinality', 'oe': 'String', 'ctname': u'StaticVocabularyConstraint', 'se': 'CWRelation', 'value': u"u'?*', u'1*', u'+*', u'**', u'?+', u'1+', u'++', u'*+', u'?1', u'11', u'+1', u'*1', u'??', u'1?', u'+?', u'*?'"}),
             ])
         
 
     def test_updateeschema2rql1(self):
-        self.assertListEquals(list(updateeschema2rql(schema.eschema('EFRDef'))),
-                              [('SET X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s WHERE X is EEType, X name %(et)s',
-                                {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the application schema', 'meta': True, 'et': 'EFRDef', 'final': False, 'name': u'EFRDef'}),
+        self.assertListEquals(list(updateeschema2rql(schema.eschema('CWAttribute'))),
+                              [('SET X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s WHERE X is CWEType, X name %(et)s',
+                                {'description': u'define a final relation: link a final relation type from a non final entity to a final entity type. used to build the application schema', 'meta': True, 'et': 'CWAttribute', 'final': False, 'name': u'CWAttribute'}),
                                ])
         
     def test_updateeschema2rql2(self):
         self.assertListEquals(list(updateeschema2rql(schema.eschema('String'))),
-                              [('SET X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s WHERE X is EEType, X name %(et)s',
+                              [('SET X description %(description)s,X final %(final)s,X meta %(meta)s,X name %(name)s WHERE X is CWEType, X name %(et)s',
                                 {'description': u'', 'meta': True, 'et': 'String', 'final': True, 'name': u'String'})
                                ])
         
     def test_updaterschema2rql1(self):
         self.assertListEquals(list(updaterschema2rql(schema.rschema('relation_type'))),
                              [
-            ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s WHERE X is ERType, X name %(rt)s',
+            ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s WHERE X is CWRType, X name %(rt)s',
              {'rt': 'relation_type', 'symetric': False,
               'description': u'link a relation definition to its relation type',
               'meta': True, 'final': False, 'fulltext_container': None, 'inlined': True, 'name': u'relation_type'})
@@ -118,7 +118,7 @@
         
     def test_updaterschema2rql2(self):
         expected = [
-            ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s WHERE X is ERType, X name %(rt)s',
+            ('SET X description %(description)s,X final %(final)s,X fulltext_container %(fulltext_container)s,X inlined %(inlined)s,X meta %(meta)s,X name %(name)s,X symetric %(symetric)s WHERE X is CWRType, X name %(rt)s',
              {'rt': 'add_permission', 'symetric': False,
               'description': u'core relation giving to a group the permission to add an entity or relation type',
               'meta': True, 'final': False, 'fulltext_container': None, 'inlined': False, 'name': u'add_permission'})
@@ -135,41 +135,41 @@
         }
     
     def test_eperms2rql1(self):
-        self.assertListEquals([rql for rql, kwargs in erperms2rql(schema.eschema('EEType'), self.GROUP_MAPPING)],
-                              ['SET X read_permission Y WHERE X is EEType, X name "EEType", Y eid 2',
-                               'SET X read_permission Y WHERE X is EEType, X name "EEType", Y eid 0',
-                               'SET X read_permission Y WHERE X is EEType, X name "EEType", Y eid 1',
-                               'SET X add_permission Y WHERE X is EEType, X name "EEType", Y eid 0',
-                               'SET X update_permission Y WHERE X is EEType, X name "EEType", Y eid 0',
-                               'SET X update_permission Y WHERE X is EEType, X name "EEType", Y eid 3',
-                               'SET X delete_permission Y WHERE X is EEType, X name "EEType", Y eid 0',
+        self.assertListEquals([rql for rql, kwargs in erperms2rql(schema.eschema('CWEType'), self.GROUP_MAPPING)],
+                              ['SET X read_permission Y WHERE X is CWEType, X name "CWEType", Y eid 2',
+                               'SET X read_permission Y WHERE X is CWEType, X name "CWEType", Y eid 0',
+                               'SET X read_permission Y WHERE X is CWEType, X name "CWEType", Y eid 1',
+                               'SET X add_permission Y WHERE X is CWEType, X name "CWEType", Y eid 0',
+                               'SET X update_permission Y WHERE X is CWEType, X name "CWEType", Y eid 0',
+                               'SET X update_permission Y WHERE X is CWEType, X name "CWEType", Y eid 3',
+                               'SET X delete_permission Y WHERE X is CWEType, X name "CWEType", Y eid 0',
                                ])
         
     def test_rperms2rql2(self):
         self.assertListEquals([rql for rql, kwargs in erperms2rql(schema.rschema('read_permission'), self.GROUP_MAPPING)],
-                              ['SET X read_permission Y WHERE X is ERType, X name "read_permission", Y eid 2',
-                               'SET X read_permission Y WHERE X is ERType, X name "read_permission", Y eid 0',
-                               'SET X read_permission Y WHERE X is ERType, X name "read_permission", Y eid 1',
-                               'SET X add_permission Y WHERE X is ERType, X name "read_permission", Y eid 0',
-                               'SET X delete_permission Y WHERE X is ERType, X name "read_permission", Y eid 0',
+                              ['SET X read_permission Y WHERE X is CWRType, X name "read_permission", Y eid 2',
+                               'SET X read_permission Y WHERE X is CWRType, X name "read_permission", Y eid 0',
+                               'SET X read_permission Y WHERE X is CWRType, X name "read_permission", Y eid 1',
+                               'SET X add_permission Y WHERE X is CWRType, X name "read_permission", Y eid 0',
+                               'SET X delete_permission Y WHERE X is CWRType, X name "read_permission", Y eid 0',
                                ])
         
     def test_rperms2rql3(self):
         self.assertListEquals([rql for rql, kwargs in erperms2rql(schema.rschema('name'), self.GROUP_MAPPING)],
-                              ['SET X read_permission Y WHERE X is ERType, X name "name", Y eid 2',
-                               'SET X read_permission Y WHERE X is ERType, X name "name", Y eid 0',
-                               'SET X read_permission Y WHERE X is ERType, X name "name", Y eid 1',
-                               'SET X add_permission Y WHERE X is ERType, X name "name", Y eid 2',
-                               'SET X add_permission Y WHERE X is ERType, X name "name", Y eid 0',
-                               'SET X add_permission Y WHERE X is ERType, X name "name", Y eid 1',
-                               'SET X delete_permission Y WHERE X is ERType, X name "name", Y eid 2',
-                               'SET X delete_permission Y WHERE X is ERType, X name "name", Y eid 0',
-                               'SET X delete_permission Y WHERE X is ERType, X name "name", Y eid 1',
+                              ['SET X read_permission Y WHERE X is CWRType, X name "name", Y eid 2',
+                               'SET X read_permission Y WHERE X is CWRType, X name "name", Y eid 0',
+                               'SET X read_permission Y WHERE X is CWRType, X name "name", Y eid 1',
+                               'SET X add_permission Y WHERE X is CWRType, X name "name", Y eid 2',
+                               'SET X add_permission Y WHERE X is CWRType, X name "name", Y eid 0',
+                               'SET X add_permission Y WHERE X is CWRType, X name "name", Y eid 1',
+                               'SET X delete_permission Y WHERE X is CWRType, X name "name", Y eid 2',
+                               'SET X delete_permission Y WHERE X is CWRType, X name "name", Y eid 0',
+                               'SET X delete_permission Y WHERE X is CWRType, X name "name", Y eid 1',
                                ])
         
     #def test_perms2rql(self):
     #    self.assertListEquals(perms2rql(schema, self.GROUP_MAPPING),
-    #                         ['INSERT EEType X: X name 'Societe', X final FALSE'])
+    #                         ['INSERT CWEType X: X name 'Societe', X final FALSE'])
         
 
 
--- a/server/test/unittest_security.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_security.py	Mon May 04 13:09:48 2009 +0200
@@ -41,12 +41,12 @@
             
     def test_upassword_not_selectable(self):
         self.assertRaises(Unauthorized,
-                          self.execute, 'Any X,P WHERE X is EUser, X upassword P')
+                          self.execute, 'Any X,P WHERE X is CWUser, X upassword P')
         self.rollback()
         cnx = self.login('iaminusersgrouponly')
         cu = cnx.cursor()
         self.assertRaises(Unauthorized,
-                          cu.execute, 'Any X,P WHERE X is EUser, X upassword P')
+                          cu.execute, 'Any X,P WHERE X is CWUser, X upassword P')
         
     
 class SecurityTC(BaseSecurityTC):
@@ -57,7 +57,7 @@
         self.execute("INSERT Affaire X: X sujet 'cool'")
         self.execute("INSERT Societe X: X nom 'logilab'")
         self.execute("INSERT Personne X: X nom 'bidule'")
-        self.execute('INSERT EGroup X: X name "staff"')
+        self.execute('INSERT CWGroup X: X name "staff"')
         self.commit()
 
     def test_insert_security(self):
@@ -134,11 +134,11 @@
         # exception is raised
         #user._groups = {'guests':1}
         #self.assertRaises(Unauthorized,
-        #                  self.o.execute, user, "DELETE EUser X WHERE X login 'bidule'")
+        #                  self.o.execute, user, "DELETE CWUser X WHERE X login 'bidule'")
         # check local security
         cnx = self.login('iaminusersgrouponly')
         cu = cnx.cursor()
-        self.assertRaises(Unauthorized, cu.execute, "DELETE EGroup Y WHERE Y name 'staff'")
+        self.assertRaises(Unauthorized, cu.execute, "DELETE CWGroup Y WHERE Y name 'staff'")
         
     def test_delete_rql_permission(self):
         self.execute("SET A concerne S WHERE A is Affaire, S is Societe")
@@ -369,11 +369,11 @@
 
     def test_attribute_read_security(self):
         # anon not allowed to see users'login, but they can see users
-        self.repo.schema['EUser'].set_groups('read', ('guests', 'users', 'managers'))
+        self.repo.schema['CWUser'].set_groups('read', ('guests', 'users', 'managers'))
         self.repo.schema['login'].set_groups('read', ('users', 'managers'))
         cnx = self.login('anon')
         cu = cnx.cursor()
-        rset = cu.execute('EUser X')
+        rset = cu.execute('CWUser X')
         self.failUnless(rset)
         x = rset.get_entity(0, 0)
         self.assertEquals(x.login, None)
@@ -420,20 +420,20 @@
         # anonymous user can only read itself
         rset = cu.execute('Any L WHERE X owned_by U, U login L')
         self.assertEquals(rset.rows, [['anon']])
-        rset = cu.execute('EUser X')
+        rset = cu.execute('CWUser X')
         self.assertEquals(rset.rows, [[anon.eid]])
         # anonymous user can read groups (necessary to check allowed transitions for instance)
-        self.assert_(cu.execute('EGroup X'))
+        self.assert_(cu.execute('CWGroup X'))
         # should only be able to read the anonymous user, not another one
         origuser = self.session.user
         self.assertRaises(Unauthorized, 
-                          cu.execute, 'EUser X WHERE X eid %(x)s', {'x': origuser.eid}, 'x')
+                          cu.execute, 'CWUser X WHERE X eid %(x)s', {'x': origuser.eid}, 'x')
         # nothing selected, nothing updated, no exception raised
         #self.assertRaises(Unauthorized,
         #                  cu.execute, 'SET X login "toto" WHERE X eid %(x)s',
         #                  {'x': self.user.eid})
         
-        rset = cu.execute('EUser X WHERE X eid %(x)s', {'x': anon.eid}, 'x')
+        rset = cu.execute('CWUser X WHERE X eid %(x)s', {'x': anon.eid}, 'x')
         self.assertEquals(rset.rows, [[anon.eid]])
         # but can't modify it
         cu.execute('SET X login "toto" WHERE X eid %(x)s', {'x': anon.eid})
--- a/server/test/unittest_session.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_session.py	Mon May 04 13:09:48 2009 +0200
@@ -21,9 +21,9 @@
 
 class MakeDescriptionTC(TestCase):
     def test_known_values(self):
-        solution = {'A': 'Int', 'B': 'EUser'}
+        solution = {'A': 'Int', 'B': 'CWUser'}
         self.assertEquals(_make_description((Function('max', 'A'), Variable('B')), {}, solution),
-                          ['Int','EUser'])
+                          ['Int','CWUser'])
 
 if __name__ == '__main__':
     unittest_main()
--- a/server/test/unittest_sqlutils.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_sqlutils.py	Mon May 04 13:09:48 2009 +0200
@@ -2,7 +2,6 @@
 """
 
 import sys
-from mx.DateTime import now
 
 from logilab.common.testlib import TestCase, unittest_main
 
--- a/server/test/unittest_ssplanner.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/test/unittest_ssplanner.py	Mon May 04 13:09:48 2009 +0200
@@ -21,12 +21,12 @@
         self._test('Any XN ORDERBY XN WHERE X name XN',
                    [('OneFetchStep', [('Any XN ORDERBY XN WHERE X name XN',
                                        [{'X': 'Basket', 'XN': 'String'},
-                                        {'X': 'ECache', 'XN': 'String'},
-                                        {'X': 'EConstraintType', 'XN': 'String'},
-                                        {'X': 'EEType', 'XN': 'String'},
-                                        {'X': 'EGroup', 'XN': 'String'},
-                                        {'X': 'EPermission', 'XN': 'String'},
-                                        {'X': 'ERType', 'XN': 'String'},
+                                        {'X': 'CWCache', 'XN': 'String'},
+                                        {'X': 'CWConstraintType', 'XN': 'String'},
+                                        {'X': 'CWEType', 'XN': 'String'},
+                                        {'X': 'CWGroup', 'XN': 'String'},
+                                        {'X': 'CWPermission', 'XN': 'String'},
+                                        {'X': 'CWRType', 'XN': 'String'},
                                         {'X': 'File', 'XN': 'String'},
                                         {'X': 'Folder', 'XN': 'String'},
                                         {'X': 'Image', 'XN': 'String'},
@@ -40,12 +40,12 @@
         self._test('Any XN,COUNT(X) GROUPBY XN WHERE X name XN',
                    [('OneFetchStep', [('Any XN,COUNT(X) GROUPBY XN WHERE X name XN',
                                        [{'X': 'Basket', 'XN': 'String'},
-                                        {'X': 'ECache', 'XN': 'String'},
-                                        {'X': 'EConstraintType', 'XN': 'String'},
-                                        {'X': 'EEType', 'XN': 'String'},
-                                        {'X': 'EGroup', 'XN': 'String'},
-                                        {'X': 'EPermission', 'XN': 'String'},
-                                        {'X': 'ERType', 'XN': 'String'},
+                                        {'X': 'CWCache', 'XN': 'String'},
+                                        {'X': 'CWConstraintType', 'XN': 'String'},
+                                        {'X': 'CWEType', 'XN': 'String'},
+                                        {'X': 'CWGroup', 'XN': 'String'},
+                                        {'X': 'CWPermission', 'XN': 'String'},
+                                        {'X': 'CWRType', 'XN': 'String'},
                                         {'X': 'File', 'XN': 'String'},
                                         {'X': 'Folder', 'XN': 'String'},
                                         {'X': 'Image', 'XN': 'String'},
--- a/server/utils.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/server/utils.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """Some utilities for the CubicWeb server.
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -117,7 +117,7 @@
                 func()
             finally:
                 self.running_threads.remove(self)
-        Thread.__init__(self, target=target)
+        Thread.__init__(self, target=auto_remove_func)
         self.running_threads = running_threads
         self._name = target.__name__
         
@@ -128,11 +128,3 @@
     @property
     def name(self):
         return '%s(%s)' % (self._name, Thread.getName(self))
-
-
-from logilab.common.deprecation import class_moved
-from cubicweb.server import pool
-Operation = class_moved(pool.Operation)
-PreCommitOperation = class_moved(pool.PreCommitOperation)
-LateOperation = class_moved(pool.LateOperation)
-SingleLastOperation = class_moved(pool.SingleLastOperation)
--- a/skeleton/MANIFEST.in	Thu Apr 30 16:16:39 2009 +0200
+++ b/skeleton/MANIFEST.in	Mon May 04 13:09:48 2009 +0200
@@ -2,4 +2,4 @@
 
 recursive-include data external_resources *.gif *.png *.css *.ico *.js
 recursive-include i18n *.pot *.po
-recursive-include migration *.sql *.py depends.map
+recursive-include migration *.py
--- a/skeleton/debian/rules.tmpl	Thu Apr 30 16:16:39 2009 +0200
+++ b/skeleton/debian/rules.tmpl	Mon May 04 13:09:48 2009 +0200
@@ -5,12 +5,12 @@
 # Uncomment this to turn on verbose mode.
 #export DH_VERBOSE=1
 build: build-stamp
-build-stamp: 
+build-stamp:
 	dh_testdir
 	python setup.py -q build
 	touch build-stamp
 
-clean: 
+clean:
 	dh_testdir
 	dh_testroot
 	rm -f build-stamp configure-stamp
@@ -24,6 +24,8 @@
 	dh_clean -k
 	dh_installdirs -i
 	python setup.py -q install --no-compile --prefix=debian/%(distname)s/usr/
+	# remove generated .egg-info file
+	rm -rf debian/cubicweb-comment/usr/lib/python*
 
 
 # Build architecture-independent files here.
@@ -39,13 +41,13 @@
 	dh_compress -i -X.py -X.ini -X.xml -Xtest
 	dh_fixperms -i
 	dh_installdeb -i
-	dh_gencontrol -i 
+	dh_gencontrol -i
 	dh_md5sums -i
 	dh_builddeb -i
 
 
 # Build architecture-dependent files here.
-binary-arch: 
+binary-arch:
 
-binary: binary-indep 
+binary: binary-indep
 .PHONY: build clean binary-arch binary-indep binary
--- a/skeleton/migration/precreate.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/skeleton/migration/precreate.py	Mon May 04 13:09:48 2009 +0200
@@ -1,3 +1,3 @@
 # Instructions here will be read before reading the schema
 # You could create your own groups here, like in :
-#   add_entity('EGroup', name=u'mygroup')
+#   add_entity('CWGroup', name=u'mygroup')
--- a/sobjects/email.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/sobjects/email.py	Mon May 04 13:09:48 2009 +0200
@@ -14,7 +14,8 @@
     already setting the relation
     """
     rtype = 'use_email'
-
+    fromeid = toeid = None # make pylint happy
+    
     def condition(self):
         """check entity has use_email set for the email address"""
         return not self.session.unsafe_execute(
--- a/sobjects/hooks.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/sobjects/hooks.py	Mon May 04 13:09:48 2009 +0200
@@ -1,18 +1,20 @@
 """various library content hooks
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
+from cubicweb.common.uilib import soup2xhtml
 from cubicweb.server.hooksmanager import Hook
 from cubicweb.server.pool import PreCommitOperation
 
-class AddUpdateEUserHook(Hook):
+
+class AddUpdateCWUserHook(Hook):
     """ensure user logins are stripped"""
     events = ('before_add_entity', 'before_update_entity',)
-    accepts = ('EUser',)
+    accepts = ('CWUser',)
     
     def call(self, session, entity):
         if 'login' in entity and entity['login']:
@@ -20,6 +22,7 @@
 
 
 class AutoDeleteBookmark(PreCommitOperation):
+    beid = None # make pylint happy
     def precommit_event(self):
         session = self.session
         if not self.beid in session.query_data('pendingeids', ()):
@@ -35,3 +38,26 @@
     
     def call(self, session, subj, rtype, obj):
         AutoDeleteBookmark(session, beid=subj)
+
+
+class TidyHtmlFields(Hook):
+    """tidy HTML in rich text strings"""
+    events = ('before_add_entity', 'before_update_entity')
+    accepts = ('Any',)
+
+    def call(self, session, entity):
+        metaattrs = entity.e_schema.meta_attributes()
+        for metaattr, (metadata, attr) in metaattrs.iteritems():
+            if metadata == 'format':
+                try:
+                    value = entity[attr]
+                except KeyError:
+                    continue # no text to tidy
+                if isinstance(value, unicode): # filter out None and Binary
+                    if self.event == 'before_add_entity':
+                        fmt = entity.get(metaattr)
+                    else:
+                        fmt = entity.get_value(metaattr)
+                    if fmt == 'text/html':
+                        entity[attr] = soup2xhtml(value, session.encoding)
+
--- a/sobjects/notification.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/sobjects/notification.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """some hooks and views to handle notification on entity's changes
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -16,12 +16,11 @@
         return 'XXX'
 
 from logilab.common.textutils import normalize_text
+from logilab.common.deprecation import class_renamed
 
 from cubicweb import RegistryException
-from cubicweb.common.view import EntityView
-from cubicweb.common.appobject import Component
-from cubicweb.common.registerers import accepts_registerer
-from cubicweb.common.selectors import accept
+from cubicweb.selectors import implements, yes
+from cubicweb.view import EntityView, Component
 from cubicweb.common.mail import format_mail
 
 from cubicweb.server.pool import PreCommitOperation
@@ -37,12 +36,10 @@
     email addresses specified in the configuration are used
     """
     id = 'recipients_finder'
-    __registerer__ = accepts_registerer
-    __selectors__ = (accept,)
-    accepts = ('Any',)
-    user_rql = ('Any X,E,A WHERE X is EUser, X in_state S, S name "activated",'
+    __select__ = yes()
+    user_rql = ('Any X,E,A WHERE X is CWUser, X in_state S, S name "activated",'
                 'X primary_email E, E address A')
-    
+
     def recipients(self):
         mode = self.config['default-recipients-mode']
         if mode == 'users':
@@ -58,7 +55,7 @@
             dests = []
         return dests
 
-    
+
 # hooks #######################################################################
 
 class RenderAndSendNotificationView(PreCommitOperation):
@@ -67,12 +64,12 @@
         if self.view.rset[0][0] in self.session.query_data('pendingeids', ()):
             return # entity added and deleted in the same transaction
         self.view.render_and_send(**getattr(self, 'viewargs', {}))
-        
+
 class StatusChangeHook(Hook):
     """notify when a workflowable entity has its state modified"""
     events = ('after_add_entity',)
     accepts = ('TrInfo',)
-    
+
     def call(self, session, entity):
         if not entity.from_state: # not a transition
             return
@@ -135,15 +132,12 @@
     * set a content attribute to define the content of the email (unless you
       override call)
     """
-    accepts = ()
-    id = None
     msgid_timestamp = True
-    
+
     def recipients(self):
-        finder = self.vreg.select_component('recipients_finder',
-                                            req=self.req, rset=self.rset)
+        finder = self.vreg.select_component('recipients_finder', self.req, self.rset)
         return finder.recipients()
-        
+
     def subject(self):
         entity = self.entity(0, 0)
         subject = self.req._(self.message)
@@ -156,7 +150,7 @@
         # req is actually a session (we are on the server side), and we have to
         # prevent nested internal session
         return self.req.actual_session().user.login
-    
+
     def context(self, **kwargs):
         entity = self.entity(0, 0)
         for key, val in kwargs.iteritems():
@@ -168,7 +162,7 @@
                        'url': entity.absolute_url(),
                        'title': entity.dc_long_title(),})
         return kwargs
-    
+
     def cell_call(self, row, col=0, **kwargs):
         self.w(self.req._(self.content) % self.context(**kwargs))
 
@@ -180,8 +174,7 @@
         self._kwargs = kwargs
         recipients = self.recipients()
         if not recipients:
-            self.info('skipping %s%s notification which has no recipients',
-                      self.id, self.accepts)
+            self.info('skipping %s notification, no recipients', self.id)
             return
         if not isinstance(recipients[0], tuple):
             from warnings import warn
@@ -244,7 +237,7 @@
     if appid != fromappid or host != gethostname():
         return None
     return values
-    
+
 
 class StatusChangeMixIn(object):
     id = 'notif_status_change'
@@ -260,10 +253,17 @@
 """)
 
 
-class ContentAddedMixIn(object):
-    """define emailcontent view for entity types for which you want to be notified
-    """
-    id = 'notif_after_add_entity' 
+###############################################################################
+# Actual notification views.                                                  #
+#                                                                             #
+# disable them at the recipients_finder level if you don't want them          #
+###############################################################################
+
+# XXX should be based on dc_title/dc_description, no?
+
+class ContentAddedView(NotificationView):
+    __abstract__ = True
+    id = 'notif_after_add_entity'
     msgid_timestamp = False
     message = _('new')
     content = """
@@ -274,25 +274,17 @@
 url: %(url)s
 """
 
-###############################################################################
-# Actual notification views.                                                  #
-#                                                                             #
-# disable them at the recipients_finder level if you don't want them          #
-###############################################################################
-
-# XXX should be based on dc_title/dc_description, no?
-
-class NormalizedTextView(ContentAddedMixIn, NotificationView):
     def context(self, **kwargs):
         entity = self.entity(0, 0)
         content = entity.printable_value(self.content_attr, format='text/plain')
         if content:
             contentformat = getattr(entity, self.content_attr + '_format', 'text/rest')
             content = normalize_text(content, 80, rest=contentformat=='text/rest')
-        return super(NormalizedTextView, self).context(content=content, **kwargs)
-    
+        return super(ContentAddedView, self).context(content=content, **kwargs)
+
     def subject(self):
         entity = self.entity(0, 0)
         return  u'%s #%s (%s)' % (self.req.__('New %s' % entity.e_schema),
                                   entity.eid, self.user_login())
 
+NormalizedTextView = class_renamed('NormalizedTextView', ContentAddedView)
--- a/sobjects/supervising.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/sobjects/supervising.py	Mon May 04 13:09:48 2009 +0200
@@ -2,13 +2,14 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 from cubicweb import UnknownEid
-from cubicweb.common.view import ComponentMixIn, StartupView
+from cubicweb.selectors import none_rset
+from cubicweb.view import Component
 from cubicweb.common.mail import format_mail
 from cubicweb.server.hooksmanager import Hook
 from cubicweb.server.hookhelper import SendMailOp
@@ -28,7 +29,7 @@
             SupervisionMailOp(session)
         
     def _call(self, *args):
-        if self._event() == 'update_entity' and args[0].e_schema == 'EUser':
+        if self._event() == 'update_entity' and args[0].e_schema == 'CWUser':
             updated = set(args[0].iterkeys())
             if not (updated - frozenset(('eid', 'modification_date', 'last_login_time'))):
                 # don't record last_login_time update which are done 
@@ -137,9 +138,10 @@
             yield change
 
 
-class SupervisionEmailView(ComponentMixIn, StartupView):
+class SupervisionEmailView(Component):
     """view implementing the email API for data changes supervision notification
     """
+    __select__ = none_rset()
     id = 'supervision_notif'
 
     def recipients(self):
--- a/sobjects/test/data/sobjects/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/sobjects/test/data/sobjects/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -1,6 +1,6 @@
 from cubicweb.sobjects.notification import StatusChangeMixIn, NotificationView
 
 class UserStatusChangeView(StatusChangeMixIn, NotificationView):
-    accepts = ('EUser',)
+    accepts = ('CWUser',)
     
     
--- a/sobjects/test/unittest_notification.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/sobjects/test/unittest_notification.py	Mon May 04 13:09:48 2009 +0200
@@ -4,8 +4,6 @@
 from logilab.common.testlib import unittest_main, TestCase
 from cubicweb.devtools.apptest import EnvBasedTC
 
-from mx.DateTime import now
-
 from cubicweb.sobjects.notification import construct_message_id, parse_message_id
 
 class MessageIdTC(TestCase):
@@ -45,10 +43,10 @@
 
 class RecipientsFinderTC(EnvBasedTC):
     def test(self):
-        urset = self.execute('EUser X WHERE X login "admin"')
+        urset = self.execute('CWUser X WHERE X login "admin"')
         self.execute('INSERT EmailAddress X: X address "admin@logilab.fr", U primary_email X '
                      'WHERE U eid %(x)s', {'x': urset[0][0]})
-        self.execute('INSERT EProperty X: X pkey "ui.language", X value "fr", X for_user U '
+        self.execute('INSERT CWProperty X: X pkey "ui.language", X value "fr", X for_user U '
                      'WHERE U eid %(x)s', {'x': urset[0][0]})
         self.commit() # commit so that admin get its properties updated
         finder = self.vreg.select_component('recipients_finder', self.request(), urset)
--- a/sobjects/test/unittest_supervising.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/sobjects/test/unittest_supervising.py	Mon May 04 13:09:48 2009 +0200
@@ -4,8 +4,6 @@
 from logilab.common.testlib import unittest_main
 from cubicweb.devtools.apptest import EnvBasedTC
 
-from mx.DateTime import now
-
 from cubicweb.sobjects.supervising import SendMailOp, SupervisionMailOp
 
 
@@ -23,7 +21,7 @@
     def test_supervision(self):
         session = self.session()
         # do some modification
-        ueid = self.execute('INSERT EUser X: X login "toto", X upassword "sosafe", X in_group G, X in_state S '
+        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]        
         self.execute('SET X last_login_time NOW WHERE X eid %(x)s', {'x': ueid}, 'x')
         self.execute('SET X in_state S WHERE X login "anon", S name "deactivated"')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/bootstrap_cubes	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,1 @@
+card, file, tag
--- a/test/data/bootstrap_packages	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/cubes/file/__pkginfo__.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,54 @@
+# pylint: disable-msg=W0622
+"""cubicweb-file packaging information"""
+
+distname = "cubicweb-file"
+modname = distname.split('-', 1)[1]
+
+numversion = (1, 4, 3)
+version = '.'.join(str(num) for num in numversion)
+
+license = 'LGPL'
+copyright = '''Copyright (c) 2003-2009 LOGILAB S.A. (Paris, FRANCE).
+http://www.logilab.fr/ -- mailto:contact@logilab.fr'''
+
+author = "Logilab"
+author_email = "contact@logilab.fr"
+web = ''
+
+short_desc = "Raw file support for the CubicWeb framework"
+long_desc = """CubicWeb is a entities / relations bases knowledge management system
+developped at Logilab.
+.
+This package provides schema and views to store files and images in cubicweb
+applications.
+.
+"""
+
+from os import listdir
+from os.path import join
+
+CUBES_DIR = join('share', 'cubicweb', 'cubes')
+try:
+    data_files = [
+        [join(CUBES_DIR, 'file'),
+         [fname for fname in listdir('.')
+          if fname.endswith('.py') and fname != 'setup.py']],
+        [join(CUBES_DIR, 'file', 'data'),
+         [join('data', fname) for fname in listdir('data')]],
+        [join(CUBES_DIR, 'file', 'wdoc'),
+         [join('wdoc', fname) for fname in listdir('wdoc')]],
+        [join(CUBES_DIR, 'file', 'views'),
+         [join('views', fname) for fname in listdir('views') if fname.endswith('.py')]],
+        [join(CUBES_DIR, 'file', 'i18n'),
+         [join('i18n', fname) for fname in listdir('i18n')]],
+        [join(CUBES_DIR, 'file', 'migration'),
+         [join('migration', fname) for fname in listdir('migration')]],
+        ]
+except OSError:
+    # we are in an installed directory
+    pass
+
+
+cube_eid = 20320
+# used packages
+__use__ = ()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/cubes/mycube/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,1 @@
+"""mycube's __init__"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/cubes/mycube/__pkginfo__.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,1 @@
+distname = 'cubicweb-mycube'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/entities.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,15 @@
+from cubicweb.entities import AnyEntity, fetch_config
+
+class Societe(AnyEntity):
+    id = 'Societe'
+    fetch_attrs = ('nom',)
+
+class Personne(Societe):
+    """customized class forne Person entities"""
+    id = 'Personne'
+    fetch_attrs, fetch_order = fetch_config(['nom', 'prenom'])
+    rest_attr = 'nom'
+
+
+class Note(AnyEntity):
+    id = 'Note'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/data/schema.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,27 @@
+class Personne(EntityType):
+    nom = String(required=True)
+    prenom = String()
+    type = String()
+    travaille = SubjectRelation('Societe')
+    evaluee = SubjectRelation(('Note', 'Personne'))
+    connait = SubjectRelation('Personne', symetric=True)
+
+class Societe(EntityType):
+    nom = String()
+    evaluee = SubjectRelation('Note')
+
+class Note(EntityType):
+    type = String()
+    ecrit_par = SubjectRelation('Personne')
+
+class SubNote(Note):
+    __specializes_schema__ = True
+    description = String()
+
+class tags(RelationDefinition):
+    subject = 'Tag'
+    object = ('Personne', 'Note')
+
+class evaluee(RelationDefinition):
+    subject = 'CWUser'
+    object = 'Note'
--- a/test/unittest_cwconfig.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/test/unittest_cwconfig.py	Mon May 04 13:09:48 2009 +0200
@@ -11,15 +11,18 @@
 def unabsolutize(path):
     parts = path.split(os.sep)
     for i, part in reversed(tuple(enumerate(parts))):
-        if part in ('cubicweb', 'cubes', 'cubes'):
+        if part.startswith('cubicweb') or part == 'cubes':
             return '/'.join(parts[i+1:])
     raise Exception('duh? %s' % path)
-    
+
 class CubicWebConfigurationTC(TestCase):
     def setUp(self):
         self.config = ApptestConfiguration('data')
         self.config._cubes = ('email', 'file')
 
+    def tearDown(self):
+        os.environ.pop('CW_CUBES_PATH', None)
+
     def test_reorder_cubes(self):
         # jpl depends on email and file and comment
         # email depends on file
@@ -35,7 +38,7 @@
                           ('jpl', 'email', 'file'))
         self.assertEquals(self.config.reorder_cubes(('jpl', 'email', 'file')),
                           ('jpl', 'email', 'file'))
-        
+
     def test_reorder_cubes_recommends(self):
         from cubes.comment import __pkginfo__ as comment_pkginfo
         comment_pkginfo.__recommend__ = ('file',)
@@ -52,8 +55,8 @@
                               ('jpl', 'email', 'comment', 'file'))
         finally:
             comment_pkginfo.__use__ = ()
-            
-        
+
+
 #     def test_vc_config(self):
 #         vcconf = self.config.vc_config()
 #         self.assertIsInstance(vcconf['EEMAIL'], Version)
@@ -61,7 +64,7 @@
 #         self.assertEquals(vcconf['CW'], (2, 31, 2))
 #         self.assertRaises(KeyError, vcconf.__getitem__, 'CW_VERSION')
 #         self.assertRaises(KeyError, vcconf.__getitem__, 'CRM')
-        
+
     def test_expand_cubes(self):
         self.assertEquals(self.config.expand_cubes(('email', 'eblog')),
                           ['email', 'eblog', 'file'])
@@ -70,7 +73,8 @@
         self.assertEquals([unabsolutize(p) for p in self.config.vregistry_path()],
                           ['entities', 'web/views', 'sobjects',
                            'file/entities.py', 'file/views', 'file/hooks.py',
-                           'email/entities.py', 'email/views', 'email/hooks.py'])
+                           'email/entities.py', 'email/views', 'email/hooks.py',
+                           'test/data/entities.py'])
 
     def test_cubes_path(self):
         # make sure we don't import the email cube, but the stdlib email package
@@ -100,7 +104,7 @@
         del cubes.file
         from cubes import file
         self.assertEquals(file.__path__, [abspath(join(dirname(__file__), 'data', 'cubes', 'file'))])
-                                       
-                          
+
+
 if __name__ == '__main__':
     unittest_main()
--- a/test/unittest_dbapi.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/test/unittest_dbapi.py	Mon May 04 13:09:48 2009 +0200
@@ -29,7 +29,7 @@
     def test_api(self):
         cnx = self.cnx
         self.assertEquals(cnx.user(None).login, 'anon')
-        self.assertEquals(cnx.describe(1), (u'EGroup', u'system', None))
+        self.assertEquals(cnx.describe(1), (u'CWGroup', u'system', None))
         self.restore_connection() # proper way to close cnx
         self.assertRaises(ConnectionError, cnx.user, None)
         self.assertRaises(ConnectionError, cnx.describe, 1)
@@ -73,7 +73,7 @@
 
 #     def test_api(self):
 #         cu = self.cursor
-#         self.assertEquals(cu.describe(1), (u'EGroup', u'system', None))
+#         self.assertEquals(cu.describe(1), (u'CWGroup', u'system', None))
 #         #cu.close()
 #         #self.assertRaises(ConnectionError, cu.describe, 1)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unittest_entity.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,389 @@
+# -*- coding: utf-8 -*-
+"""unit tests for cubicweb.web.views.entities module"""
+
+from datetime import datetime
+
+from cubicweb import Binary
+from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.common.mttransforms import HAS_TAL
+
+class EntityTC(EnvBasedTC):
+
+##     def setup_database(self):
+##         self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
+##         self.add_entity('Task', title=u'fait ca !', description=u'et plus vite', start=now())
+##         self.add_entity('Tag', name=u'x')
+##         self.add_entity('Link', title=u'perdu', url=u'http://www.perdu.com',
+##                         embed=False)
+
+    def test_boolean_value(self):
+        e = self.etype_instance('CWUser')
+        self.failUnless(e)
+
+    def test_yams_inheritance(self):
+        from entities import Note
+        e = self.etype_instance('SubNote')
+        self.assertIsInstance(e, Note)
+        e2 = self.etype_instance('SubNote')
+        self.assertIs(e.__class__, e2.__class__)
+
+    def test_has_eid(self):
+        e = self.etype_instance('CWUser')
+        self.assertEquals(e.eid, None)
+        self.assertEquals(e.has_eid(), False)
+        e.eid = 'X'
+        self.assertEquals(e.has_eid(), False)
+        e.eid = 0
+        self.assertEquals(e.has_eid(), True)
+        e.eid = 2
+        self.assertEquals(e.has_eid(), True)
+
+    def test_copy(self):
+        self.add_entity('Tag', name=u'x')
+        p = self.add_entity('Personne', nom=u'toto')
+        oe = self.add_entity('Note', type=u'x')
+        self.execute('SET T ecrit_par U WHERE T eid %(t)s, U eid %(u)s',
+                     {'t': oe.eid, 'u': p.eid}, ('t','u'))
+        self.execute('SET TAG tags X WHERE X eid %(x)s', {'x': oe.eid}, 'x')
+        e = self.add_entity('Note', type=u'z')
+        e.copy_relations(oe.eid)
+        self.assertEquals(len(e.ecrit_par), 1)
+        self.assertEquals(e.ecrit_par[0].eid, p.eid)
+        self.assertEquals(len(e.reverse_tags), 0)
+
+    def test_copy_with_nonmeta_composite_inlined(self):
+        p = self.add_entity('Personne', nom=u'toto')
+        oe = self.add_entity('Note', type=u'x')
+        self.schema['ecrit_par'].set_rproperty('Note', 'Personne', 'composite', 'subject')
+        self.execute('SET T ecrit_par U WHERE T eid %(t)s, U eid %(u)s',
+                     {'t': oe.eid, 'u': p.eid}, ('t','u'))
+        e = self.add_entity('Note', type=u'z')
+        e.copy_relations(oe.eid)
+        self.failIf(e.ecrit_par)
+        self.failUnless(oe.ecrit_par)
+
+    def test_copy_with_composite(self):
+        user = self.user()
+        adeleid = self.execute('INSERT EmailAddress X: X address "toto@logilab.org", U use_email X WHERE U login "admin"')[0][0]
+        e = self.entity('Any X WHERE X eid %(x)s', {'x':user.eid}, 'x')
+        self.assertEquals(e.use_email[0].address, "toto@logilab.org")
+        self.assertEquals(e.use_email[0].eid, adeleid)
+        usereid = self.execute('INSERT CWUser X: X login "toto", X upassword "toto", X in_group G, X in_state S '
+                               'WHERE G name "users", S name "activated"')[0][0]
+        e = self.entity('Any X WHERE X eid %(x)s', {'x':usereid}, 'x')
+        e.copy_relations(user.eid)
+        self.failIf(e.use_email)
+        self.failIf(e.primary_email)
+
+    def test_copy_with_non_initial_state(self):
+        user = self.user()
+        eid = self.execute('INSERT CWUser X: X login "toto", X upassword %(pwd)s, X in_group G WHERE G name "users"',
+                           {'pwd': 'toto'})[0][0]
+        self.commit()
+        self.execute('SET X in_state S WHERE X eid %(x)s, S name "deactivated"', {'x': eid}, 'x')
+        self.commit()
+        eid2 = self.execute('INSERT CWUser X: X login "tutu", X upassword %(pwd)s', {'pwd': 'toto'})[0][0]
+        e = self.entity('Any X WHERE X eid %(x)s', {'x': eid2}, 'x')
+        e.copy_relations(eid)
+        self.commit()
+        e.clear_related_cache('in_state', 'subject')
+        self.assertEquals(e.state, 'activated')
+
+    def test_related_cache_both(self):
+        user = self.entity('Any X WHERE X eid %(x)s', {'x':self.user().eid}, 'x')
+        adeleid = self.execute('INSERT EmailAddress X: X address "toto@logilab.org", U use_email X WHERE U login "admin"')[0][0]
+        self.commit()
+        self.assertEquals(user._related_cache.keys(), [])
+        email = user.primary_email[0]
+        self.assertEquals(sorted(user._related_cache), ['primary_email_subject'])
+        self.assertEquals(email._related_cache.keys(), ['primary_email_object'])
+        groups = user.in_group
+        self.assertEquals(sorted(user._related_cache), ['in_group_subject', 'primary_email_subject'])
+        for group in groups:
+            self.failIf('in_group_subject' in group._related_cache, group._related_cache.keys())
+
+    def test_related_limit(self):
+        p = self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
+        for tag in u'abcd':
+            self.add_entity('Tag', name=tag)
+        self.execute('SET X tags Y WHERE X is Tag, Y is Personne')
+        self.assertEquals(len(p.related('tags', 'object', limit=2)), 2)
+        self.assertEquals(len(p.related('tags', 'object')), 4)
+
+
+    def test_fetch_rql(self):
+        user = self.user()
+        Personne = self.vreg.etype_class('Personne')
+        Societe = self.vreg.etype_class('Societe')
+        Note = self.vreg.etype_class('Note')
+        peschema = Personne.e_schema
+        seschema = Societe.e_schema
+        peschema.subject_relation('travaille').set_rproperty(peschema, seschema, 'cardinality', '1*')
+        peschema.subject_relation('connait').set_rproperty(peschema, peschema, 'cardinality', '11')
+        peschema.subject_relation('evaluee').set_rproperty(peschema, Note.e_schema, 'cardinality', '1*')
+        seschema.subject_relation('evaluee').set_rproperty(seschema, Note.e_schema, 'cardinality', '1*')
+        # testing basic fetch_attrs attribute
+        self.assertEquals(Personne.fetch_rql(user),
+                          'Any X,AA,AB,AC ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB, X modification_date AC')
+        pfetch_attrs = Personne.fetch_attrs
+        sfetch_attrs = Societe.fetch_attrs
+        try:
+            # testing unknown attributes
+            Personne.fetch_attrs = ('bloug', 'beep')
+            self.assertEquals(Personne.fetch_rql(user), 'Any X WHERE X is Personne')
+            # testing one non final relation
+            Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
+            self.assertEquals(Personne.fetch_rql(user),
+                              'Any X,AA,AB,AC,AD ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB, X travaille AC, AC nom AD')
+            # testing two non final relations
+            Personne.fetch_attrs = ('nom', 'prenom', 'travaille', 'evaluee')
+            self.assertEquals(Personne.fetch_rql(user),
+                              'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA ASC,AF DESC WHERE X is Personne, X nom AA, '
+                              'X prenom AB, X travaille AC, AC nom AD, X evaluee AE, AE modification_date AF')
+            # testing one non final relation with recursion
+            Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
+            Societe.fetch_attrs = ('nom', 'evaluee')
+            self.assertEquals(Personne.fetch_rql(user),
+                              'Any X,AA,AB,AC,AD,AE,AF ORDERBY AA ASC,AF DESC WHERE X is Personne, X nom AA, X prenom AB, '
+                              'X travaille AC, AC nom AD, AC evaluee AE, AE modification_date AF'
+                              )
+            # testing symetric relation
+            Personne.fetch_attrs = ('nom', 'connait')
+            self.assertEquals(Personne.fetch_rql(user), 'Any X,AA,AB ORDERBY AA ASC WHERE X is Personne, X nom AA, X connait AB')
+            # testing optional relation
+            peschema.subject_relation('travaille').set_rproperty(peschema, seschema, 'cardinality', '?*')
+            Personne.fetch_attrs = ('nom', 'prenom', 'travaille')
+            Societe.fetch_attrs = ('nom',)
+            self.assertEquals(Personne.fetch_rql(user),
+                              'Any X,AA,AB,AC,AD ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB, X travaille AC?, AC nom AD')
+            # testing relation with cardinality > 1
+            peschema.subject_relation('travaille').set_rproperty(peschema, seschema, 'cardinality', '**')
+            self.assertEquals(Personne.fetch_rql(user),
+                              'Any X,AA,AB ORDERBY AA ASC WHERE X is Personne, X nom AA, X prenom AB')
+            # XXX test unauthorized attribute
+        finally:
+            Personne.fetch_attrs = pfetch_attrs
+            Societe.fetch_attrs = sfetch_attrs
+
+    def test_related_rql(self):
+        from cubicweb.entities import fetch_config
+        Personne = self.vreg.etype_class('Personne')
+        Note = self.vreg.etype_class('Note')
+        Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', 'type'))
+        Note.fetch_attrs, Note.fetch_order = fetch_config(('type',))
+        aff = self.add_entity('Personne', nom=u'pouet')
+        self.assertEquals(aff.related_rql('evaluee'),
+                          'Any X,AA,AB ORDERBY AA ASC WHERE E eid %(x)s, E evaluee X, '
+                          'X type AA, X modification_date AB')
+        Personne.fetch_attrs, Personne.fetch_order = fetch_config(('nom', ))
+        # XXX
+        self.assertEquals(aff.related_rql('evaluee'),
+                          'Any X,AA ORDERBY Z DESC WHERE X modification_date Z, E eid %(x)s, E evaluee X, X modification_date AA')
+
+    def test_entity_unrelated(self):
+        p = self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
+        e = self.add_entity('Tag', name=u'x')
+        related = [r.eid for r in e.tags]
+        self.failUnlessEqual(related, [])
+        unrelated = [r[0] for r in e.unrelated('tags', 'Personne', 'subject')]
+        self.failUnless(p.eid in unrelated)
+        self.execute('SET X tags Y WHERE X is Tag, Y is Personne')
+        e = self.entity('Any X WHERE X is Tag')
+        unrelated = [r[0] for r in e.unrelated('tags', 'Personne', 'subject')]
+        self.failIf(p.eid in unrelated)
+
+    def test_entity_unrelated_limit(self):
+        e = self.add_entity('Tag', name=u'x')
+        self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
+        self.add_entity('Personne', nom=u'di mascio', prenom=u'gwen')
+        self.assertEquals(len(e.unrelated('tags', 'Personne', 'subject', limit=1)),
+                          1)
+
+    def test_new_entity_unrelated(self):
+        e = self.etype_instance('CWUser')
+        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)
+
+    def test_printable_value_string(self):
+        e = self.add_entity('Card', title=u'rest test', content=u'du :eid:`1:*ReST*`',
+                            content_format=u'text/rest')
+        self.assertEquals(e.printable_value('content'),
+                          '<p>du <a class="reference" href="http://testing.fr/cubicweb/cwgroup/managers">*ReST*</a></p>\n')
+        e['content'] = 'du <em>html</em> <ref rql="CWUser X">users</ref>'
+        e['content_format'] = 'text/html'
+        self.assertEquals(e.printable_value('content'),
+                          'du <em>html</em> <a href="http://testing.fr/cubicweb/view?rql=CWUser%20X">users</a>')
+        e['content'] = 'du *texte*'
+        e['content_format'] = 'text/plain'
+        self.assertEquals(e.printable_value('content'),
+                          '<p>\ndu *texte*\n</p>')
+        e['title'] = 'zou'
+        #e = self.etype_instance('Task')
+        e['content'] = '''\
+a title
+=======
+du :eid:`1:*ReST*`'''
+        e['content_format'] = 'text/rest'
+        self.assertEquals(e.printable_value('content', format='text/plain'),
+                          e['content'])
+
+        e['content'] = u'<b>yo (zou éà ;)</b>'
+        e['content_format'] = 'text/html'
+        self.assertEquals(e.printable_value('content', format='text/plain').strip(),
+                          u'**yo (zou éà ;)**')
+        if HAS_TAL:
+            e['content'] = '<h1 tal:content="self/title">titre</h1>'
+            e['content_format'] = 'text/cubicweb-page-template'
+            self.assertEquals(e.printable_value('content'),
+                              '<h1>zou</h1>')
+
+
+    def test_printable_value_bytes(self):
+        e = self.add_entity('File', data=Binary('lambda x: 1'), data_format=u'text/x-python',
+                            data_encoding=u'ascii', name=u'toto.py')
+        from cubicweb.common import mttransforms
+        if mttransforms.HAS_PYGMENTS_TRANSFORMS:
+            self.assertEquals(e.printable_value('data'),
+                              '''<div class="highlight"><pre><span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="mf">1</span>
+</pre></div>
+''')
+        else:
+            self.assertEquals(e.printable_value('data'),
+                              '''<pre class="python">
+<span style="color: #C00000;">lambda</span> <span style="color: #000000;">x</span><span style="color: #0000C0;">:</span> <span style="color: #0080C0;">1</span>
+</pre>
+''')
+
+        e = self.add_entity('File', data=Binary('*héhéhé*'), data_format=u'text/rest',
+                            data_encoding=u'utf-8', name=u'toto.txt')
+        self.assertEquals(e.printable_value('data'),
+                          u'<p><em>héhéhé</em></p>\n')
+
+    def test_printable_value_bad_html(self):
+        """make sure we don't crash if we try to render invalid XHTML strings"""
+        e = self.add_entity('Card', title=u'bad html', content=u'<div>R&D<br>',
+                            content_format=u'text/html')
+        tidy = lambda x: x.replace('\n', '')
+        self.assertEquals(tidy(e.printable_value('content')),
+                          '<div>R&amp;D<br/></div>')
+        e['content'] = u'yo !! R&D <div> pas fermé'
+        self.assertEquals(tidy(e.printable_value('content')),
+                          u'yo !! R&amp;D <div> pas fermé</div>')
+        e['content'] = u'R&D'
+        self.assertEquals(tidy(e.printable_value('content')), u'R&amp;D')
+        e['content'] = u'R&D;'
+        self.assertEquals(tidy(e.printable_value('content')), u'R&amp;D;')
+        e['content'] = u'yo !! R&amp;D <div> pas fermé'
+        self.assertEquals(tidy(e.printable_value('content')),
+                          u'yo !! R&amp;D <div> pas fermé</div>')
+        e['content'] = u'été <div> été'
+        self.assertEquals(tidy(e.printable_value('content')),
+                          u'été <div> été</div>')
+        e['content'] = u'C&apos;est un exemple s&eacute;rieux'
+        self.assertEquals(tidy(e.printable_value('content')),
+                          u"C'est un exemple sérieux")
+        # make sure valid xhtml is left untouched
+        e['content'] = u'<div>R&amp;D<br/></div>'
+        self.assertEquals(e.printable_value('content'), e['content'])
+        e['content'] = u'<div>été</div>'
+        self.assertEquals(e.printable_value('content'), e['content'])
+        e['content'] = u'été'
+        self.assertEquals(e.printable_value('content'), e['content'])
+
+
+    def test_fulltextindex(self):
+        e = self.etype_instance('File')
+        e['name'] = 'an html file'
+        e['description'] = 'du <em>html</em>'
+        e['description_format'] = 'text/html'
+        e['data'] = Binary('some <em>data</em>')
+        e['data_format'] = 'text/html'
+        e['data_encoding'] = 'ascii'
+        self.assertEquals(set(e.get_words()),
+                          set(['an', 'html', 'file', 'du', 'html', 'some', 'data']))
+
+
+    def test_nonregr_relation_cache(self):
+        p1 = self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
+        p2 = self.add_entity('Personne', nom=u'toto')
+        self.execute('SET X evaluee Y WHERE X nom "di mascio", Y nom "toto"')
+        self.assertEquals(p1.evaluee[0].nom, "toto")
+        self.failUnless(not p1.reverse_evaluee)
+
+    def test_complete_relation(self):
+        self.execute('SET RT add_permission G WHERE RT name "wf_info_for", G name "managers"')
+        self.commit()
+        try:
+            eid = self.execute('INSERT TrInfo X: X comment "zou", X wf_info_for U,'
+                               'X from_state S1, X to_state S2 WHERE '
+                               'U login "admin", S1 name "activated", S2 name "deactivated"')[0][0]
+            trinfo = self.entity('Any X WHERE X eid %(x)s', {'x': eid}, 'x')
+            trinfo.complete()
+            self.failUnless(trinfo.relation_cached('from_state', 'subject'))
+            self.failUnless(trinfo.relation_cached('to_state', 'subject'))
+            self.failUnless(trinfo.relation_cached('wf_info_for', 'subject'))
+            # check with a missing relation
+            eid = self.execute('INSERT TrInfo X: X comment "zou", X wf_info_for U,'
+                               'X to_state S2 WHERE '
+                               'U login "admin", S2 name "activated"')[0][0]
+            trinfo = self.entity('Any X WHERE X eid %(x)s', {'x': eid}, 'x')
+            trinfo.complete()
+            self.failUnless(isinstance(trinfo.creation_date, datetime))
+            self.failUnless(trinfo.relation_cached('from_state', 'subject'))
+            self.failUnless(trinfo.relation_cached('to_state', 'subject'))
+            self.failUnless(trinfo.relation_cached('wf_info_for', 'subject'))
+            self.assertEquals(trinfo.from_state, [])
+        finally:
+            self.rollback()
+            self.execute('DELETE RT add_permission G WHERE RT name "wf_info_for", G name "managers"')
+            self.commit()
+
+    def test_request_cache(self):
+        req = self.request()
+        user = self.entity('CWUser X WHERE X login "admin"', req=req)
+        state = user.in_state[0]
+        samestate = self.entity('State X WHERE X name "activated"', req=req)
+        self.failUnless(state is samestate)
+
+    def test_rest_path(self):
+        note = self.add_entity('Note', type=u'z')
+        self.assertEquals(note.rest_path(), 'note/%s' % note.eid)
+        # unique attr
+        tag = self.add_entity('Tag', name=u'x')
+        self.assertEquals(tag.rest_path(), 'tag/x')
+        # test explicit rest_attr
+        person = self.add_entity('Personne', prenom=u'john', nom=u'doe')
+        self.assertEquals(person.rest_path(), 'personne/doe')
+        # ambiguity test
+        person2 = self.add_entity('Personne', prenom=u'remi', nom=u'doe')
+        self.assertEquals(person.rest_path(), 'personne/eid/%s' % person.eid)
+        self.assertEquals(person2.rest_path(), 'personne/eid/%s' % person2.eid)
+        # unique attr with None value (wikiid in this case)
+        card1 = self.add_entity('Card', title=u'hop')
+        self.assertEquals(card1.rest_path(), 'card/eid/%s' % card1.eid)
+        card2 = self.add_entity('Card', title=u'pod', wikiid=u'zob/i')
+        self.assertEquals(card2.rest_path(), 'card/zob%2Fi')
+
+    def test_set_attributes(self):
+        person = self.add_entity('Personne', nom=u'di mascio', prenom=u'adrien')
+        self.assertEquals(person.prenom, u'adrien')
+        self.assertEquals(person.nom, u'di mascio')
+        person.set_attributes(prenom=u'sylvain', nom=u'thénault')
+        person = self.entity('Personne P') # XXX retreival needed ?
+        self.assertEquals(person.prenom, u'sylvain')
+        self.assertEquals(person.nom, u'thénault')
+
+    def test_metainformation(self):
+        note = self.add_entity('Note', type=u'z')
+        metainf = note.metainformation()
+        self.assertEquals(metainf, {'source': {'adapter': 'native', 'uri': 'system'}, 'type': u'Note', 'extid': None})
+        self.assertEquals(note.absolute_url(), 'http://testing.fr/cubicweb/note/%s' % note.eid)
+        metainf['source'] = metainf['source'].copy()
+        metainf['source']['base-url']  = 'http://cubicweb2.com/'
+        self.assertEquals(note.absolute_url(), 'http://cubicweb2.com/note/%s' % note.eid)
+
+if __name__ == '__main__':
+    from logilab.common.testlib import unittest_main
+    unittest_main()
+
--- a/test/unittest_rset.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/test/unittest_rset.py	Mon May 04 13:09:48 2009 +0200
@@ -1,30 +1,33 @@
 # coding: utf-8
 """unit tests for module cubicweb.common.utils"""
+from __future__ import with_statement
 
 from logilab.common.testlib import TestCase, unittest_main
+
 from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.selectors import traced_selection
 
 from urlparse import urlsplit
 from rql import parse
 
 from cubicweb.rset import NotAnEntity, ResultSet, attr_desc_iterator
 
-        
+
 def pprelcachedict(d):
     res = {}
     for k, (rset, related) in d.items():
         res[k] = sorted(v.eid for v in related)
     return sorted(res.items())
-        
+
 
 class AttrDescIteratorTC(TestCase):
     """TestCase for cubicweb.rset.attr_desc_iterator"""
-    
+
     def test_relations_description(self):
         """tests relations_description() function"""
         queries = {
-            'Any U,L,M where U is EUser, U login L, U mail M' : [(1, 'login', 'subject'), (2, 'mail', 'subject')],
-            'Any U,L,M where U is EUser, L is Foo, U mail M' : [(2, 'mail', 'subject')],
+            'Any U,L,M where U is CWUser, U login L, U mail M' : [(1, 'login', 'subject'), (2, 'mail', 'subject')],
+            'Any U,L,M where U is CWUser, L is Foo, U mail M' : [(2, 'mail', 'subject')],
             'Any C,P where C is Company, C employs P' : [(1, 'employs', 'subject')],
             'Any C,P where C is Company, P employed_by P' : [],
             'Any C where C is Company, C employs P' : [],
@@ -32,11 +35,11 @@
         for rql, relations in queries.items():
             result = list(attr_desc_iterator(parse(rql).children[0]))
             self.assertEquals((rql, result), (rql, relations))
-            
+
     def test_relations_description_indexed(self):
         """tests relations_description() function"""
         queries = {
-            'Any C,U,P,L,M where C is Company, C employs P, U is EUser, U login L, U mail M' :
+            'Any C,U,P,L,M where C is Company, C employs P, U is CWUser, U login L, U mail M' :
             {0: [(2,'employs', 'subject')], 1: [(3,'login', 'subject'), (4,'mail', 'subject')]},
             }
         for rql, results in queries.items():
@@ -46,13 +49,13 @@
 
 
 
-class ResultSetTC(EnvBasedTC):    
+class ResultSetTC(EnvBasedTC):
 
     def setUp(self):
         super(ResultSetTC, self).setUp()
         self.rset = ResultSet([[12, 'adim'], [13, 'syt']],
-                              'Any U,L where U is EUser, U login L',
-                              description=[['EUser', 'String'], ['Bar', 'String']])
+                              'Any U,L where U is CWUser, U login L',
+                              description=[['CWUser', 'String'], ['Bar', 'String']])
         self.rset.vreg = self.vreg
 
     def compare_urls(self, url1, url2):
@@ -64,7 +67,7 @@
             params2 = dict(pair.split('=') for pair in info1[3].split('&'))
             self.assertDictEquals(params1, params2)
 
-        
+
     def test_build_url(self):
         req = self.request()
         baseurl = req.base_url()
@@ -76,20 +79,20 @@
         #                  '%stask/title/go' % baseurl)
         # empty _restpath should not crash
         self.compare_urls(req.build_url('view', _restpath=''), baseurl)
-                
-        
+
+
     def test_resultset_build(self):
         """test basic build of a ResultSet"""
-        rs = ResultSet([1,2,3], 'EGroup X', description=['EGroup', 'EGroup', 'EGroup'])
+        rs = ResultSet([1,2,3], 'CWGroup X', description=['CWGroup', 'CWGroup', 'CWGroup'])
         self.assertEquals(rs.rowcount, 3)
         self.assertEquals(rs.rows, [1,2,3])
-        self.assertEquals(rs.description, ['EGroup', 'EGroup', 'EGroup'])
+        self.assertEquals(rs.description, ['CWGroup', 'CWGroup', 'CWGroup'])
 
 
     def test_resultset_limit(self):
         rs = ResultSet([[12000, 'adim'], [13000, 'syt'], [14000, 'nico']],
-                       'Any U,L where U is EUser, U login L',
-                       description=[['EUser', 'String']] * 3)
+                       'Any U,L where U is CWUser, U login L',
+                       description=[['CWUser', 'String']] * 3)
         rs.req = self.request()
         rs.vreg = self.env.vreg
 
@@ -99,25 +102,25 @@
         self.assertEquals(rs2.get_entity(0, 0).row, 0)
         self.assertEquals(rs.limit(2, offset=2).rows, [[14000, 'nico']])
         self.assertEquals(rs.limit(2, offset=3).rows, [])
-        
+
 
     def test_resultset_filter(self):
         rs = ResultSet([[12000, 'adim'], [13000, 'syt'], [14000, 'nico']],
-                       'Any U,L where U is EUser, U login L',
-                       description=[['EUser', 'String']] * 3)
+                       'Any U,L where U is CWUser, U login L',
+                       description=[['CWUser', 'String']] * 3)
         rs.req = self.request()
         rs.vreg = self.env.vreg
         def test_filter(entity):
             return entity.login != 'nico'
-        
+
         rs2 = rs.filtered_rset(test_filter)
         self.assertEquals(len(rs2), 2)
         self.assertEquals([login for _, login in rs2], ['adim', 'syt'])
-        
+
     def test_resultset_transform(self):
         rs = ResultSet([[12, 'adim'], [13, 'syt'], [14, 'nico']],
-                       'Any U,L where U is EUser, U login L',
-                       description=[['EUser', 'String']] * 3)
+                       'Any U,L where U is CWUser, U login L',
+                       description=[['CWUser', 'String']] * 3)
         rs.req = self.request()
         def test_transform(row, desc):
             return row[1:], desc[1:]
@@ -125,20 +128,20 @@
 
         self.assertEquals(len(rs2), 3)
         self.assertEquals(list(rs2), [['adim'],['syt'],['nico']])
-        
+
     def test_resultset_sort(self):
         rs = ResultSet([[12000, 'adim'], [13000, 'syt'], [14000, 'nico']],
-                       'Any U,L where U is EUser, U login L',
-                       description=[['EUser', 'String']] * 3)
+                       'Any U,L where U is CWUser, U login L',
+                       description=[['CWUser', 'String']] * 3)
         rs.req = self.request()
         rs.vreg = self.env.vreg
-        
+
         rs2 = rs.sorted_rset(lambda e:e['login'])
         self.assertEquals(len(rs2), 3)
         self.assertEquals([login for _, login in rs2], ['adim', 'nico', 'syt'])
         # make sure rs is unchanged
         self.assertEquals([login for _, login in rs], ['adim', 'syt', 'nico'])
-        
+
         rs2 = rs.sorted_rset(lambda e:e['login'], reverse=True)
         self.assertEquals(len(rs2), 3)
         self.assertEquals([login for _, login in rs2], ['syt', 'nico', 'adim'])
@@ -157,12 +160,12 @@
                         [13000, 'syt',  u'Le carrelage en 42 leçons'],
                         [14000, 'nico', u'La tarte tatin en 15 minutes'],
                         [14000, 'nico', u"L'épluchage du castor commun"]],
-                       'Any U, L, T WHERE U is EUser, U login L,'\
+                       'Any U, L, T WHERE U is CWUser, U login L,'\
                        'D created_by U, D title T',
-                       description=[['EUser', 'String', 'String']] * 5)
+                       description=[['CWUser', 'String', 'String']] * 5)
         rs.req = self.request()
         rs.vreg = self.env.vreg
-        
+
         rsets = rs.split_rset(lambda e:e['login'])
         self.assertEquals(len(rsets), 3)
         self.assertEquals([login for _, login,_ in rsets[0]], ['adim', 'adim'])
@@ -170,7 +173,7 @@
         self.assertEquals([login for _, login,_ in rsets[2]], ['nico', 'nico'])
         # make sure rs is unchanged
         self.assertEquals([login for _, login,_ in rs], ['adim', 'adim', 'syt', 'nico', 'nico'])
-        
+
         rsets = rs.split_rset(lambda e:e['login'], return_dict=True)
         self.assertEquals(len(rsets), 3)
         self.assertEquals([login for _, login,_ in rsets['nico']], ['nico', 'nico'])
@@ -195,7 +198,7 @@
                            u'Le carrelage en 42 leçons',
                            u'La tarte tatin en 15 minutes',
                            u"L'épluchage du castor commun"])
-        
+
     def test_cached_syntax_tree(self):
         """make sure syntax tree is cached"""
         rqlst1 = self.rset.syntax_tree()
@@ -203,7 +206,7 @@
         self.assert_(rqlst1 is rqlst2)
 
     def test_get_entity_simple(self):
-        self.add_entity('EUser', login=u'adim', upassword='adim',
+        self.add_entity('CWUser', login=u'adim', upassword='adim',
                         surname=u'di mascio', firstname=u'adrien')
         e = self.entity('Any X,T WHERE X login "adim", X surname T')
         self.assertEquals(e['surname'], 'di mascio')
@@ -213,12 +216,12 @@
         e.complete()
         self.assertEquals(e['firstname'], 'adrien')
         self.assertEquals(pprelcachedict(e._related_cache), [])
-        
+
     def test_get_entity_advanced(self):
         self.add_entity('Bookmark', title=u'zou', path=u'/view')
         self.execute('SET X bookmarked_by Y WHERE X is Bookmark, Y login "anon"')
         rset = self.execute('Any X,Y,XT,YN WHERE X bookmarked_by Y, X title XT, Y login YN')
-        
+
         e = rset.get_entity(0, 0)
         self.assertEquals(e.row, 0)
         self.assertEquals(e.col, 0)
@@ -226,7 +229,7 @@
         self.assertRaises(KeyError, e.__getitem__, 'path')
         self.assertEquals(e.view('text'), 'zou')
         self.assertEquals(pprelcachedict(e._related_cache), [])
-        
+
         e = rset.get_entity(0, 1)
         self.assertEquals(e.row, 0)
         self.assertEquals(e.col, 1)
@@ -239,7 +242,7 @@
         self.assertEquals(e.view('text'), 'anon')
         self.assertEquals(pprelcachedict(e._related_cache),
                           [])
-        
+
         self.assertRaises(NotAnEntity, rset.get_entity, 0, 2)
         self.assertRaises(NotAnEntity, rset.get_entity, 0, 3)
 
@@ -247,7 +250,7 @@
         rset = self.execute('Any X,S WHERE X in_state S, X login "anon"')
         e = rset.get_entity(0, 0)
         seid = self.execute('State X WHERE X name "activated"')[0][0]
-        # for_user / in_group are prefetched in EUser __init__, in_state should
+        # for_user / in_group are prefetched in CWUser __init__, in_state should
         # be filed from our query rset
         self.assertEquals(pprelcachedict(e._related_cache),
                           [('in_state_subject', [seid])])
@@ -270,9 +273,9 @@
         self.assertEquals(s['name'], 'activated')
         self.assertRaises(KeyError, s.__getitem__, 'description')
 
-        
+
     def test_get_entity_cache_with_left_outer_join(self):
-        eid = self.execute('INSERT EUser E: E login "joe", E upassword "joe", E in_group G '
+        eid = self.execute('INSERT CWUser E: E login "joe", E upassword "joe", E in_group G '
                            'WHERE G name "users"')[0][0]
         rset = self.execute('Any X,E WHERE X eid %(x)s, X primary_email E?', {'x': eid})
         e = rset.get_entity(0, 0)
@@ -283,63 +286,63 @@
         cached = e.related_cache('primary_email', 'subject', False)
         self.assertIsInstance(cached, ResultSet)
         self.assertEquals(cached.rowcount, 0)
-        
+
 
     def test_get_entity_union(self):
         e = self.add_entity('Bookmark', title=u'manger', path=u'path')
         rset = self.execute('Any X,N ORDERBY N WITH X,N BEING '
                             '((Any X,N WHERE X is Bookmark, X title N)'
                             ' UNION '
-                            ' (Any X,N WHERE X is EGroup, X name N))')
-        expected = (('EGroup', 'guests'), ('EGroup', 'managers'),
-                    ('Bookmark', 'manger'), ('EGroup', 'owners'),
-                    ('EGroup', 'users'))
+                            ' (Any X,N WHERE X is CWGroup, X name N))')
+        expected = (('CWGroup', 'guests'), ('CWGroup', 'managers'),
+                    ('Bookmark', 'manger'), ('CWGroup', 'owners'),
+                    ('CWGroup', 'users'))
         for entity in rset.entities(): # test get_entity for each row actually
             etype, n = expected[entity.row]
             self.assertEquals(entity.id, etype)
             attr = etype == 'Bookmark' and 'title' or 'name'
             self.assertEquals(entity[attr], n)
-        
+
     def test_related_entity_optional(self):
         e = self.add_entity('Bookmark', title=u'aaaa', path=u'path')
         rset = self.execute('Any B,U,L WHERE B bookmarked_by U?, U login L')
         entity, rtype = rset.related_entity(0, 2)
         self.assertEquals(entity, None)
         self.assertEquals(rtype, None)
-        
+
     def test_related_entity_union_subquery(self):
         e = self.add_entity('Bookmark', title=u'aaaa', path=u'path')
         rset = self.execute('Any X,N ORDERBY N WITH X,N BEING '
-                            '((Any X,N WHERE X is EGroup, X name N)'
+                            '((Any X,N WHERE X is CWGroup, X name N)'
                             ' UNION '
                             ' (Any X,N WHERE X is Bookmark, X title N))')
         entity, rtype = rset.related_entity(0, 1)
         self.assertEquals(entity.eid, e.eid)
         self.assertEquals(rtype, 'title')
         entity, rtype = rset.related_entity(1, 1)
-        self.assertEquals(entity.id, 'EGroup')
+        self.assertEquals(entity.id, 'CWGroup')
         self.assertEquals(rtype, 'name')
         rset = self.execute('Any X,N ORDERBY N WHERE X is Bookmark WITH X,N BEING '
-                            '((Any X,N WHERE X is EGroup, X name N)'
+                            '((Any X,N WHERE X is CWGroup, X name N)'
                             ' UNION '
                             ' (Any X,N WHERE X is Bookmark, X title N))')
         entity, rtype = rset.related_entity(0, 1)
         self.assertEquals(entity.eid, e.eid)
         self.assertEquals(rtype, 'title')
-        
+
     def test_entities(self):
         rset = self.execute('Any U,G WHERE U in_group G')
         # make sure we have at least one element
         self.failUnless(rset)
         self.assertEquals(set(e.e_schema.type for e in rset.entities(0)),
-                          set(['EUser',]))
+                          set(['CWUser',]))
         self.assertEquals(set(e.e_schema.type for e in rset.entities(1)),
-                          set(['EGroup',]))
+                          set(['CWGroup',]))
 
-    def test_printable_rql(self):        
-        rset = self.execute(u'EEType X WHERE X final FALSE, X meta FALSE')
+    def test_printable_rql(self):
+        rset = self.execute(u'CWEType X WHERE X final FALSE, X meta FALSE')
         self.assertEquals(rset.printable_rql(),
-                          'Any X WHERE X final FALSE, X meta FALSE, X is EEType')
+                          'Any X WHERE X final FALSE, X meta FALSE, X is CWEType')
 
 
     def test_searched_text(self):
@@ -347,7 +350,7 @@
         self.assertEquals(rset.searched_text(), 'foobar')
         rset = self.execute(u'Any X WHERE X has_text %(text)s', {'text' : 'foo'})
         self.assertEquals(rset.searched_text(), 'foo')
-        
-   
+
+
 if __name__ == '__main__':
     unittest_main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unittest_rtags.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,45 @@
+from logilab.common.testlib import TestCase, unittest_main
+from cubicweb.rtags import RelationTags, RelationTagsSet
+
+class RelationTagsTC(TestCase):
+
+    def test_rtags_expansion(self):
+        rtags = RelationTags()
+        rtags.tag_relation('primary', ('Societe', 'travaille', '*'), 'subject', )
+        rtags.tag_relation('secondary', ('*', 'evaluee', '*'), 'subject')
+        rtags.tag_relation('generated', ('*', 'tags', '*'), 'object')        
+        self.assertEquals(rtags.get('evaluee', 'subject', 'Note'), 'secondary')
+        self.assertEquals(rtags.get('travaille', 'subject', 'Societe'), 'primary')
+        self.assertEquals(rtags.get('travaille', 'subject', 'Note'), None)
+        self.assertEquals(rtags.get('tags', 'subject', 'Note'), None)
+        self.assertEquals(rtags.get('tags', 'object', 'Note'), 'generated')
+
+#         self.assertEquals(rtags.rtag('evaluee', 'Note', 'subject'), set(('secondary', 'link')))
+#         self.assertEquals(rtags.is_inlined('evaluee', 'Note', 'subject'), False)
+#         self.assertEquals(rtags.rtag('evaluee', 'Personne', 'subject'), set(('secondary', 'link')))
+#         self.assertEquals(rtags.is_inlined('evaluee', 'Personne', 'subject'), False)
+#         self.assertEquals(rtags.rtag('ecrit_par', 'Note', 'object'), set(('inlineview', 'link')))
+#         self.assertEquals(rtags.is_inlined('ecrit_par', 'Note', 'object'), True)
+#         class Personne2(Personne):
+#             id = 'Personne'
+#             __rtags__ = {
+#                 ('evaluee', 'Note', 'subject') : set(('inlineview',)),
+#                 }
+#         self.vreg.register_vobject_class(Personne2)
+#         rtags = Personne2.rtags
+#         self.assertEquals(rtags.rtag('evaluee', 'Note', 'subject'), set(('inlineview', 'link')))
+#         self.assertEquals(rtags.is_inlined('evaluee', 'Note', 'subject'), True)
+#         self.assertEquals(rtags.rtag('evaluee', 'Personne', 'subject'), set(('secondary', 'link')))
+#         self.assertEquals(rtags.is_inlined('evaluee', 'Personne', 'subject'), False)
+
+
+    def test_rtagset_expansion(self):
+        rtags = RelationTagsSet()
+        rtags.tag_relation('primary', ('Societe', 'travaille', '*'), 'subject', )
+        rtags.tag_relation('secondary', ('*', 'travaille', '*'), 'subject')
+        self.assertEquals(rtags.get('travaille', 'subject', 'Societe'), set(('primary', 'secondary')))
+        self.assertEquals(rtags.get('travaille', 'subject', 'Note'), set(('secondary',)))
+        self.assertEquals(rtags.get('tags', 'subject', 'Note'), set())
+
+if __name__ == '__main__':
+    unittest_main()
--- a/test/unittest_schema.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/test/unittest_schema.py	Mon May 04 13:09:48 2009 +0200
@@ -134,7 +134,6 @@
         self.assertListEquals([basename(f) for f in schema_files], ['Bookmark.py'])
 
     def test_knownValues_load_schema(self):
-        """read an url and return a Schema instance"""
         schema = loader.load(config)
         self.assert_(isinstance(schema, CubicWebSchema))
         self.assertEquals(schema.name, 'data')
@@ -142,13 +141,13 @@
         entities.sort()
         expected_entities = ['Bookmark', 'Boolean', 'Bytes', 'Card', 
                              'Date', 'Datetime', 'Decimal',
-                             'ECache', 'EConstraint', 'EConstraintType', 'EEType',
-                             'EFRDef', 'EGroup', 'EmailAddress', 'ENFRDef',
-                             'EPermission', 'EProperty', 'ERType', 'EUser',
-                             'Float', 'Int', 'Interval', 
-                             'Password', 
+                             'CWCache', 'CWConstraint', 'CWConstraintType', 'CWEType',
+                             'CWAttribute', 'CWGroup', 'EmailAddress', 'CWRelation',
+                             'CWPermission', 'CWProperty', 'CWRType', 'CWUser',
+                             'File', 'Float', 'Image', 'Int', 'Interval', 'Note',
+                             'Password', 'Personne',
                              'RQLExpression', 
-                             'State', 'String', 'Time', 
+                             'Societe', 'State', 'String', 'SubNote', 'Tag', 'Time', 
                              'Transition', 'TrInfo']
         self.assertListEquals(entities, sorted(expected_entities))
         relations = [str(r) for r in schema.relations()]
@@ -157,13 +156,13 @@
                               'allowed_transition', 'bookmarked_by', 'canonical',
 
                               'cardinality', 'comment', 'comment_format', 
-                              'composite', 'condition', 'constrained_by', 'content',
+                              'composite', 'condition', 'connait', 'constrained_by', 'content',
                               'content_format', 'created_by', 'creation_date', 'cstrtype',
 
-                              'defaultval', 'delete_permission', 'description',
-                              'description_format', 'destination_state',
+                              'data', 'data_encoding', 'data_format', 'defaultval', 'delete_permission',
+                              'description', 'description_format', 'destination_state',
 
-                              'eid', 'expression', 'exprtype',
+                              'ecrit_par', 'eid', 'evaluee', 'expression', 'exprtype',
 
                               'final', 'firstname', 'for_user',
                               'from_entity', 'from_state', 'fulltext_container', 'fulltextindexed',
@@ -176,17 +175,17 @@
 
                               'mainvars', 'meta', 'modification_date',
 
-                              'name', 
+                              'name', 'nom',
 
                               'ordernum', 'owned_by',
 
-                              'path', 'pkey', 'primary_email', 
+                              'path', 'pkey', 'prenom', 'primary_email', 
 
                               'read_permission', 'relation_type', 'require_group',
                               
                               'specializes', 'state_of', 'surname', 'symetric', 'synopsis',
 
-                              'timestamp', 'title', 'to_entity', 'to_state', 'transition_of',
+                              'tags', 'timestamp', 'title', 'to_entity', 'to_state', 'transition_of', 'travaille', 'type',
 
                               'upassword', 'update_permission', 'use_email',
 
@@ -196,10 +195,10 @@
     
         self.assertListEquals(relations, expected_relations)
 
-        eschema = schema.eschema('EUser')
+        eschema = schema.eschema('CWUser')
         rels = sorted(str(r) for r in eschema.subject_relations())
         self.assertListEquals(rels, ['created_by', 'creation_date', 'eid',
-                                     'firstname', 'has_text', 'identity',
+                                     'evaluee', 'firstname', 'has_text', 'identity',
                                      'in_group', 'in_state', 'is',
                                      'is_instance_of', 'last_login_time',
                                      'login', 'modification_date', 'owned_by',
@@ -209,7 +208,7 @@
         self.assertListEquals(rels, ['bookmarked_by', 'created_by', 'for_user',
                                      'identity', 'owned_by', 'wf_info_for'])
         rschema = schema.rschema('relation_type')
-        properties = rschema.rproperties('EFRDef', 'ERType')
+        properties = rschema.rproperties('CWAttribute', 'CWRType')
         self.assertEquals(properties['cardinality'], '1*')
         constraints = properties['constraints']
         self.failUnlessEqual(len(constraints), 1, constraints)
@@ -219,7 +218,7 @@
 
     def test_fulltext_container(self):
         schema = loader.load(config)
-        self.failUnless('has_text' in schema['EUser'].subject_relations())
+        self.failUnless('has_text' in schema['CWUser'].subject_relations())
         self.failIf('has_text' in schema['EmailAddress'].subject_relations())
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unittest_selectors.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,136 @@
+"""unit tests for selectors mechanism
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from cubicweb.devtools.testlib import EnvBasedTC
+from cubicweb.vregistry import Selector, AndSelector, OrSelector
+from cubicweb.selectors import implements, match_user_groups
+from cubicweb.interfaces import IDownloadable
+from cubicweb.web import action
+
+class _1_(Selector):
+    def __call__(self, *args, **kwargs):
+        return 1
+
+class _0_(Selector):
+    def __call__(self, *args, **kwargs):
+        return 0
+
+def _2_(*args, **kwargs):
+    return 2
+
+
+class SelectorsTC(TestCase):
+    def test_basic_and(self):
+        selector = _1_() & _1_()
+        self.assertEquals(selector(None), 2)
+        selector = _1_() & _0_()
+        self.assertEquals(selector(None), 0)
+        selector = _0_() & _1_()
+        self.assertEquals(selector(None), 0)
+
+    def test_basic_or(self):
+        selector = _1_() | _1_()
+        self.assertEquals(selector(None), 1)
+        selector = _1_() | _0_()
+        self.assertEquals(selector(None), 1)
+        selector = _0_() | _1_()
+        self.assertEquals(selector(None), 1)
+        selector = _0_() | _0_()
+        self.assertEquals(selector(None), 0)
+
+    def test_selector_and_function(self):
+        selector = _1_() & _2_
+        self.assertEquals(selector(None), 3)
+        selector = _2_ & _1_()
+        self.assertEquals(selector(None), 3)
+
+    def test_three_and(self):
+        selector = _1_() & _1_() & _1_()
+        self.assertEquals(selector(None), 3)
+        selector = _1_() & _0_() & _1_()
+        self.assertEquals(selector(None), 0)
+        selector = _0_() & _1_() & _1_()
+        self.assertEquals(selector(None), 0)
+
+    def test_three_or(self):
+        selector = _1_() | _1_() | _1_()
+        self.assertEquals(selector(None), 1)
+        selector = _1_() | _0_() | _1_()
+        self.assertEquals(selector(None), 1)
+        selector = _0_() | _1_() | _1_()
+        self.assertEquals(selector(None), 1)
+        selector = _0_() | _0_() | _0_()
+        self.assertEquals(selector(None), 0)
+
+    def test_composition(self):
+        selector = (_1_() & _1_()) & (_1_() & _1_())
+        self.failUnless(isinstance(selector, AndSelector))
+        self.assertEquals(len(selector.selectors), 4)
+        self.assertEquals(selector(None), 4)
+        selector = (_1_() & _0_()) | (_1_() & _1_())
+        self.failUnless(isinstance(selector, OrSelector))
+        self.assertEquals(len(selector.selectors), 2)
+        self.assertEquals(selector(None), 2)
+
+    def test_search_selectors(self):
+        sel = implements('something')
+        self.assertIs(sel.search_selector(implements), sel)
+        csel = AndSelector(sel, Selector())
+        self.assertIs(csel.search_selector(implements), sel)
+        csel = AndSelector(Selector(), sel)
+        self.assertIs(csel.search_selector(implements), sel)
+
+
+class ImplementsSelectorTC(EnvBasedTC):
+    def test_etype_priority(self):
+        req = self.request()
+        cls = self.vreg.etype_class('File')
+        anyscore = implements('Any').score_class(cls, req)
+        idownscore = implements(IDownloadable).score_class(cls, req)
+        self.failUnless(idownscore > anyscore, (idownscore, anyscore))
+        filescore = implements('File').score_class(cls, req)
+        self.failUnless(filescore > idownscore, (filescore, idownscore))
+
+    def test_etype_inheritance_no_yams_inheritance(self):
+        cls = self.vreg.etype_class('Personne')
+        self.failIf(implements('Societe').score_class(cls, self.request()))
+
+
+class MatchUserGroupsTC(EnvBasedTC):
+    def test_owners_group(self):
+        """tests usage of 'owners' group with match_user_group"""
+        class SomeAction(action.Action):
+            id = 'yo'
+            category = 'foo'
+            __select__ = match_user_groups('owners')
+        self.vreg._loadedmods[__name__] = {}
+        self.vreg.register_vobject_class(SomeAction)
+        self.failUnless(SomeAction in self.vreg['actions']['yo'], self.vreg['actions'])
+        try:
+            # login as a simple user
+            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"')
+            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"')
+            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"')
+            self.failIf('yo' in dict(self.pactions(req, rset)))
+        finally:
+            del self.vreg[SomeAction.__registry__][SomeAction.id]
+
+if __name__ == '__main__':
+    unittest_main()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/unittest_utils.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,47 @@
+"""unit tests for module cubicweb.common.utils"""
+
+from logilab.common.testlib import TestCase, unittest_main
+
+from cubicweb.common.utils import make_uid, UStringIO, SizeConstrainedList
+
+
+class MakeUidTC(TestCase):
+    def test_1(self):
+        self.assertNotEquals(make_uid('xyz'), make_uid('abcd'))
+        self.assertNotEquals(make_uid('xyz'), make_uid('xyz'))
+        
+    def test_2(self):
+        d = {}
+        while len(d)<10000:
+            uid = make_uid('xyz')
+            if d.has_key(uid):
+                self.fail(len(d))
+            d[uid] = 1
+
+        
+class UStringIOTC(TestCase):
+    def test_boolean_value(self):
+        self.assert_(UStringIO())
+
+
+class SizeConstrainedListTC(TestCase):
+
+    def test_append(self):
+        l = SizeConstrainedList(10)
+        for i in xrange(12):
+            l.append(i)
+        self.assertEquals(l, range(2, 12))
+    
+    def test_extend(self):
+        testdata = [(range(5), range(5)),
+                    (range(10), range(10)),
+                    (range(12), range(2, 12)),
+                    ]
+        for extension, expected in testdata:
+            l = SizeConstrainedList(10)
+            l.extend(extension)
+            yield self.assertEquals, l, expected
+
+   
+if __name__ == '__main__':
+    unittest_main()
--- a/test/unittest_vregistry.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/test/unittest_vregistry.py	Mon May 04 13:09:48 2009 +0200
@@ -5,45 +5,72 @@
 from cubicweb import CW_SOFTWARE_ROOT as BASE
 from cubicweb.vregistry import VObject
 from cubicweb.cwvreg import CubicWebRegistry, UnknownProperty
-from cubicweb.cwconfig import CubicWebConfiguration
+from cubicweb.devtools import TestServerConfiguration
+from cubicweb.interfaces import IMileStone
+
+from cubes.card.entities import Card
 
 class YesSchema:
     def __contains__(self, something):
         return True
-    
+
+WEBVIEWSDIR = join(BASE, 'web', 'views')
+
 class VRegistryTC(TestCase):
 
     def setUp(self):
-        config = CubicWebConfiguration('data')
+        config = TestServerConfiguration('data')
         self.vreg = CubicWebRegistry(config)
-        self.vreg.schema = YesSchema()
+        config.bootstrap_cubes()
+        self.vreg.schema = config.load_schema()
 
     def test_load(self):
-        self.vreg.load_file(join(BASE, 'web', 'views'), 'euser.py')
-        self.vreg.load_file(join(BASE, 'web', 'views'), 'baseviews.py')
-        fpvc = [v for v in self.vreg.registry_objects('views', 'primary') if v.accepts[0] == 'EUser'][0]
+        self.vreg.init_registration([WEBVIEWSDIR])
+        self.vreg.load_file(join(WEBVIEWSDIR, 'cwuser.py'), 'cubicweb.web.views.cwuser')
+        self.vreg.load_file(join(WEBVIEWSDIR, 'baseviews.py'), 'cubicweb.web.views.baseviews')
+        fpvc = [v for v in self.vreg.registry_objects('views', 'primary')
+               if v.__module__ == 'cubicweb.web.views.cwuser'][0]
         fpv = fpvc(None, None)
         # don't want a TypeError due to super call
         self.assertRaises(AttributeError, fpv.render_entity_attributes, None, None)
 
     def test_load_interface_based_vojects(self):
-        self.vreg.load_file(join(BASE, 'web', 'views'), 'idownloadable.py')
-        self.vreg.load_file(join(BASE, 'web', 'views'), 'baseviews.py')
-        # check loading baseviews after idownloadable isn't kicking interface based views
+        self.vreg.init_registration([WEBVIEWSDIR])
+        self.vreg.load_file(join(BASE, 'entities', '__init__.py'), 'cubicweb.entities.__init__')
+        self.vreg.load_file(join(WEBVIEWSDIR, 'idownloadable.py'), 'cubicweb.web.views.idownloadable')
+        self.vreg.load_file(join(WEBVIEWSDIR, 'primary.py'), 'cubicweb.web.views.primary')
         self.assertEquals(len(self.vreg['views']['primary']), 2)
-                              
-    def test_autoselectors(self):
+        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(VObject):
             __selectors__ = (myselector1, myselector2)
-        self.assertEquals(AnAppObject.__select__(), 2)
+        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'))
         self.assertRaises(UnknownProperty, self.vreg.property_info, 'a.non.existent.key')
-        
+
+    def test_load_subinterface_based_vobjects(self):
+        self.vreg.reset()
+        self.vreg.register_objects([join(BASE, 'web', 'views', 'iprogress.py')])
+        # check progressbar was kicked
+        self.failIf(self.vreg['views'].get('progressbar'))
+        class MyCard(Card):
+            __implements__ = (IMileStone,)
+        self.vreg.reset()
+        self.vreg._loadedmods[__name__] = {}
+        self.vreg.register_vobject_class(MyCard)
+        self.vreg.register_objects([join(BASE, 'entities', '__init__.py'),
+                                    join(BASE, 'web', 'views', 'iprogress.py')])
+        # check progressbar isn't kicked
+        self.assertEquals(len(self.vreg['views']['progressbar']), 1)
+
 
 if __name__ == '__main__':
     unittest_main()
--- a/toolsutils.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/toolsutils.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """some utilities for cubicweb tools
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -11,7 +11,7 @@
 from os.path import exists, join, abspath, normpath
 
 from logilab.common.clcommands import Command as BaseCommand, \
-     main_run as base_main_run, register_commands, pop_arg, cmd_run
+     main_run as base_main_run
 from logilab.common.compat import any
 
 from cubicweb import warning
@@ -114,7 +114,7 @@
                 tfpath = tfpath[:-5]
                 if not askconfirm or not exists(tfpath) or \
                        confirm('%s exists, overwrite?' % tfpath):
-                    fname = fill_templated_file(fpath, tfpath, context)
+                    fill_templated_file(fpath, tfpath, context)
                     print '[generate] %s <-- %s' % (tfpath, fpath)
             elif exists(tfpath):
                 show_diffs(tfpath, fpath, askconfirm)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/utils.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,302 @@
+"""Some utilities for CubicWeb server/clients.
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+import locale
+from md5 import md5
+from datetime import datetime, timedelta, date
+from time import time
+from random import randint, seed
+from calendar import monthrange
+
+# initialize random seed from current time
+seed()
+try:
+    strptime = datetime.strptime
+except AttributeError: # py < 2.5
+    from time import strptime as time_strptime
+    def strptime(value, format):
+        return datetime(*time_strptime(value, format)[:6])
+
+def todate(somedate):
+    """return a date from a date (leaving unchanged) or a datetime"""
+    if isinstance(somedate, datetime):
+        return date(somedate.year, somedate.month, somedate.day)
+    assert isinstance(somedate, date), repr(somedate)
+    return somedate
+
+def todatetime(somedate):
+    """return a date from a date (leaving unchanged) or a datetime"""
+    if isinstance(somedate, date):
+        return datetime(somedate.year, somedate.month, somedate.day)
+    assert isinstance(somedate, datetime), repr(somedate)
+    return somedate
+
+ONEDAY = timedelta(days=1)
+
+def days_in_month(date_):
+    return monthrange(date_.year, date_.month)[1]
+
+def previous_month(date_):
+    return first_day(date_) - ONEDAY
+
+def next_month(date_):
+    return last_day(date_) + ONEDAY
+
+def first_day(date_):
+    return date(date_.year, date_.month, 1)
+
+def last_day(date_):
+    return date(date_.year, date_.month, days_in_month(date_))
+
+def date_range(begin, end, incr=1, include=None):
+    """yields each date between begin and end
+    :param begin: the start date
+    :param end: the end date
+    :param incr: the step to use to iterate over dates. Default is
+                 one day.
+    :param include: None (means no exclusion) or a function taking a
+                    date as parameter, and returning True if the date
+                    should be included.
+    """
+    incr = timedelta(incr, 0, 0)
+    while begin <= end:
+        if include is None or include(begin):
+            yield begin
+        begin += incr
+
+def ustrftime(date, fmt='%Y-%m-%d'):
+    """like strftime, but returns a unicode string instead of an encoded
+    string which may be problematic with localized date.
+
+    encoding is guessed by locale.getpreferredencoding()
+    """
+    # date format may depend on the locale
+    encoding = locale.getpreferredencoding(do_setlocale=False) or 'UTF-8'
+    return unicode(date.strftime(str(fmt)), encoding)
+
+def make_uid(key):
+    """forge a unique identifier"""
+    msg = str(key) + "%.10f"%time() + str(randint(0, 1000000))
+    return md5(msg).hexdigest()
+
+
+def dump_class(cls, clsname):
+    """create copy of a class by creating an empty class inheriting
+    from the given cls.
+
+    Those class will be used as place holder for attribute and relation
+    description
+    """
+    # type doesn't accept unicode name
+    # return type.__new__(type, str(clsname), (cls,), {})
+    # __autogenerated__ attribute is just a marker
+    return type(str(clsname), (cls,), {'__autogenerated__': True})
+
+
+def merge_dicts(dict1, dict2):
+    """update a copy of `dict1` with `dict2`"""
+    dict1 = dict(dict1)
+    dict1.update(dict2)
+    return dict1
+
+
+class SizeConstrainedList(list):
+    """simple list that makes sure the list does not get bigger
+    than a given size.
+
+    when the list is full and a new element is added, the first
+    element of the list is removed before appending the new one
+
+    >>> l = SizeConstrainedList(2)
+    >>> l.append(1)
+    >>> l.append(2)
+    >>> l
+    [1, 2]
+    >>> l.append(3)
+    [2, 3]
+    """
+    def __init__(self, maxsize):
+        self.maxsize = maxsize
+
+    def append(self, element):
+        if len(self) == self.maxsize:
+            del self[0]
+        super(SizeConstrainedList, self).append(element)
+
+    def extend(self, sequence):
+        super(SizeConstrainedList, self).extend(sequence)
+        keepafter = len(self) - self.maxsize
+        if keepafter > 0:
+            del self[:keepafter]
+
+    __iadd__ = extend
+
+
+class UStringIO(list):
+    """a file wrapper which automatically encode unicode string to an encoding
+    specifed in the constructor
+    """
+
+    def __nonzero__(self):
+        return True
+
+    def write(self, value):
+        assert isinstance(value, unicode), u"unicode required not %s : %s"\
+                                     % (type(value).__name__, repr(value))
+        self.append(value)
+
+    def getvalue(self):
+        return u''.join(self)
+
+    def __repr__(self):
+        return '<%s at %#x>' % (self.__class__.__name__, id(self))
+
+
+class HTMLHead(UStringIO):
+    """wraps HTML header's stream
+
+    Request objects use a HTMLHead instance to ease adding of
+    javascripts and stylesheets
+    """
+    js_unload_code = u'jQuery(window).unload(unloadPageData);'
+
+    def __init__(self):
+        super(HTMLHead, self).__init__()
+        self.jsvars = []
+        self.jsfiles = []
+        self.cssfiles = []
+        self.ie_cssfiles = []
+        self.post_inlined_scripts = []
+        self.pagedata_unload = False
+
+
+    def add_raw(self, rawheader):
+        self.write(rawheader)
+
+    def define_var(self, var, value):
+        self.jsvars.append( (var, value) )
+
+    def add_post_inline_script(self, content):
+        self.post_inlined_scripts.append(content)
+
+    def add_onload(self, jscode):
+        self.add_post_inline_script(u"""jQuery(document).ready(function () {
+ %s
+ });""" % jscode)
+
+
+    def add_js(self, jsfile):
+        """adds `jsfile` to the list of javascripts used in the webpage
+
+        This function checks if the file has already been added
+        :param jsfile: the script's URL
+        """
+        if jsfile not in self.jsfiles:
+            self.jsfiles.append(jsfile)
+
+    def add_css(self, cssfile, media):
+        """adds `cssfile` to the list of javascripts used in the webpage
+
+        This function checks if the file has already been added
+        :param cssfile: the stylesheet's URL
+        """
+        if (cssfile, media) not in self.cssfiles:
+            self.cssfiles.append( (cssfile, media) )
+
+    def add_ie_css(self, cssfile, media='all'):
+        """registers some IE specific CSS"""
+        if (cssfile, media) not in self.ie_cssfiles:
+            self.ie_cssfiles.append( (cssfile, media) )
+
+    def add_unload_pagedata(self):
+        """registers onunload callback to clean page data on server"""
+        if not self.pagedata_unload:
+            self.post_inlined_scripts.append(self.js_unload_code)
+            self.pagedata_unload = True
+
+    def getvalue(self, skiphead=False):
+        """reimplement getvalue to provide a consistent (and somewhat browser
+        optimzed cf. http://stevesouders.com/cuzillion) order in external
+        resources declaration
+        """
+        w = self.write
+        # 1/ variable declaration if any
+        if self.jsvars:
+            from simplejson import dumps
+            w(u'<script type="text/javascript">\n')
+            for var, value in self.jsvars:
+                w(u'%s = %s;\n' % (var, dumps(value)))
+            w(u'</script>\n')
+        # 2/ css files
+        for cssfile, media in self.cssfiles:
+            w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
+              (media, cssfile))
+        # 3/ ie css if necessary
+        if self.ie_cssfiles:
+            w(u'<!--[if lt IE 8]>\n')
+            for cssfile, media in self.ie_cssfiles:
+                w(u'<link rel="stylesheet" type="text/css" media="%s" href="%s"/>\n' %
+                  (media, cssfile))
+            w(u'<![endif]--> \n')
+        # 4/ js files
+        for jsfile in self.jsfiles:
+            w(u'<script type="text/javascript" src="%s"></script>\n' % jsfile)
+        # 5/ post inlined scripts (i.e. scripts depending on other JS files)
+        if self.post_inlined_scripts:
+            w(u'<script type="text/javascript">\n')
+            w(u'\n\n'.join(self.post_inlined_scripts))
+            w(u'\n</script>\n')
+        header = super(HTMLHead, self).getvalue()
+        if skiphead:
+            return header
+        return u'<head>\n%s</head>\n' % header
+
+
+class HTMLStream(object):
+    """represents a HTML page.
+
+    This is used my main templates so that HTML headers can be added
+    at any time during the page generation.
+
+    HTMLStream uses the (U)StringIO interface to be compliant with
+    existing code.
+    """
+
+    def __init__(self, req):
+        # stream for <head>
+        self.head = req.html_headers
+        # main stream
+        self.body = UStringIO()
+        self.doctype = u''
+        # xmldecl and html opening tag
+        self.xmldecl = u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding
+        self.htmltag = u'<html xmlns="http://www.w3.org/1999/xhtml" ' \
+                       'xmlns:cubicweb="http://www.logilab.org/2008/cubicweb" ' \
+                       'xml:lang="%s" lang="%s">' % (req.lang, req.lang)
+
+
+    def write(self, data):
+        """StringIO interface: this method will be assigned to self.w
+        """
+        self.body.write(data)
+
+    def getvalue(self):
+        """writes HTML headers, closes </head> tag and writes HTML body"""
+        return u'%s\n%s\n%s\n%s\n%s\n</html>' % (self.xmldecl, self.doctype,
+                                                 self.htmltag,
+                                                 self.head.getvalue(),
+                                                 self.body.getvalue())
+
+
+class AcceptMixIn(object):
+    """Mixin class for vobjects defining the 'accepts' attribute describing
+    a set of supported entity type (Any by default).
+    """
+    # XXX deprecated, no more necessary
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/view.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,489 @@
+"""abstract views and templates classes for CubicWeb web client
+
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from cStringIO import StringIO
+
+from logilab.common.deprecation import obsolete
+from logilab.mtconverter import html_escape
+
+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 AppRsetObject
+from cubicweb.utils import UStringIO, HTMLStream
+
+_ = unicode
+
+
+# robots control
+NOINDEX = u'<meta name="ROBOTS" content="NOINDEX" />'
+NOFOLLOW = u'<meta name="ROBOTS" content="NOFOLLOW" />'
+
+CW_XHTML_EXTENSIONS = '''[
+  <!ATTLIST html xmlns:cubicweb CDATA  #FIXED \'http://www.logilab.org/2008/cubicweb\'  >
+
+<!ENTITY % coreattrs
+ "id          ID            #IMPLIED
+  class       CDATA         #IMPLIED
+  style       CDATA         #IMPLIED
+  title       CDATA         #IMPLIED
+
+ cubicweb:sortvalue         CDATA   #IMPLIED
+ cubicweb:target            CDATA   #IMPLIED
+ cubicweb:limit             CDATA   #IMPLIED
+ cubicweb:type              CDATA   #IMPLIED
+ cubicweb:loadtype          CDATA   #IMPLIED
+ cubicweb:wdgtype           CDATA   #IMPLIED
+ cubicweb:initfunc          CDATA   #IMPLIED
+ cubicweb:inputid           CDATA   #IMPLIED
+ cubicweb:tindex            CDATA   #IMPLIED
+ cubicweb:inputname         CDATA   #IMPLIED
+ cubicweb:value             CDATA   #IMPLIED
+ cubicweb:required          CDATA   #IMPLIED
+ cubicweb:accesskey         CDATA   #IMPLIED
+ cubicweb:maxlength         CDATA   #IMPLIED
+ cubicweb:variables         CDATA   #IMPLIED
+ cubicweb:displayactions    CDATA   #IMPLIED
+ cubicweb:fallbackvid       CDATA   #IMPLIED
+ cubicweb:fname             CDATA   #IMPLIED
+ cubicweb:vid               CDATA   #IMPLIED
+ cubicweb:rql               CDATA   #IMPLIED
+ cubicweb:actualrql         CDATA   #IMPLIED
+ cubicweb:rooteid           CDATA   #IMPLIED
+ cubicweb:dataurl           CDATA   #IMPLIED
+ cubicweb:size              CDATA   #IMPLIED
+ cubicweb:tlunit            CDATA   #IMPLIED
+ cubicweb:loadurl           CDATA   #IMPLIED
+ cubicweb:uselabel          CDATA   #IMPLIED
+ cubicweb:facetargs         CDATA   #IMPLIED
+ cubicweb:facetName         CDATA   #IMPLIED
+  "> ] '''
+
+TRANSITIONAL_DOCTYPE = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" %s>\n' % CW_XHTML_EXTENSIONS
+TRANSITIONAL_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">\n'
+STRICT_DOCTYPE = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" %s>\n' % CW_XHTML_EXTENSIONS
+STRICT_DOCTYPE_NOEXT = u'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n'
+
+# base view object ############################################################
+
+class View(AppRsetObject):
+    """abstract view class, used as base for every renderable object such
+    as views, templates, some components...web
+
+    A view is instantiated to render a [part of a] result set. View
+    subclasses may be parametred using the following class attributes:
+
+    * `templatable` indicates if the view may be embeded in a main
+      template or if it has to be rendered standalone (i.e. XML for
+      instance)
+    * if the view is not templatable, it should set the `content_type` class
+      attribute to the correct MIME type (text/xhtml by default)
+    * the `category` attribute may be used in the interface to regroup related
+      objects together
+
+    At instantiation time, the standard `req`, `rset`, and `cursor`
+    attributes are added and the `w` attribute will be set at rendering
+    time to a write function to use.
+    """
+    __registry__ = 'views'
+    registered = require_group_compat(AppRsetObject.registered)
+
+    templatable = True
+    need_navigation = True
+    # content_type = 'application/xhtml+xml' # text/xhtml'
+    binary = False
+    add_to_breadcrumbs = True
+    category = 'view'
+
+    def __init__(self, req=None, rset=None, **kwargs):
+        super(View, self).__init__(req, rset, **kwargs)
+        self.w = None
+
+    @property
+    def content_type(self):
+        return self.req.html_content_type()
+
+    def set_stream(self, w=None):
+        if self.w is not None:
+            return
+        if w is None:
+            if self.binary:
+                self._stream = stream = StringIO()
+            else:
+                self._stream = stream = UStringIO()
+            w = stream.write
+        else:
+            stream = None
+        self.w = w
+        return stream
+
+    # main view interface #####################################################
+
+    def dispatch(self, w=None, **context):
+        """called to render a view object for a result set.
+
+        This method is a dispatched to an actual method selected
+        according to optional row and col parameters, which are locating
+        a particular row or cell in the result set:
+
+        * if row is specified, `cell_call` is called
+        * if none of them is supplied, the view is considered to apply on
+          the whole result set (which may be None in this case), `call` is
+          called
+        """
+        row = context.get('row')
+        if row is not None:
+            context.setdefault('col', 0)
+            view_func = self.cell_call
+        else:
+            view_func = self.call
+        stream = self.set_stream(w)
+        # stream = self.set_stream(context)
+        view_func(**context)
+        # return stream content if we have created it
+        if stream is not None:
+            return self._stream.getvalue()
+
+    # should default .call() method add a <div classs="section"> around each
+    # rset item
+    add_div_section = True
+
+    def call(self, **kwargs):
+        """the view is called for an entire result set, by default loop
+        other rows of the result set and call the same view on the
+        particular row
+
+        Views applicable on None result sets have to override this method
+        """
+        rset = self.rset
+        if rset is None:
+            raise NotImplementedError, self
+        wrap = self.templatable and len(rset) > 1 and self.add_div_section
+        for i in xrange(len(rset)):
+            if wrap:
+                self.w(u'<div class="section">')
+            self.wview(self.id, rset, row=i, **kwargs)
+            if wrap:
+                self.w(u"</div>")
+
+    def cell_call(self, row, col, **kwargs):
+        """the view is called for a particular result set cell"""
+        raise NotImplementedError, self
+
+    def linkable(self):
+        """return True if the view may be linked in a menu
+
+        by default views without title are not meant to be displayed
+        """
+        if not getattr(self, 'title', None):
+            return False
+        return True
+
+    def is_primary(self):
+        return self.id == 'primary'
+
+    def url(self):
+        """return the url associated with this view. Should not be
+        necessary for non linkable views, but a default implementation
+        is provided anyway.
+        """
+        try:
+            return self.build_url(vid=self.id, rql=self.req.form['rql'])
+        except KeyError:
+            return self.build_url(vid=self.id)
+
+    def set_request_content_type(self):
+        """set the content type returned by this view"""
+        self.req.set_content_type(self.content_type)
+
+    # view utilities ##########################################################
+
+    def wview(self, __vid, rset, __fallback_vid=None, **kwargs):
+        """shortcut to self.view method automatically passing self.w as argument
+        """
+        self.view(__vid, rset, __fallback_vid, w=self.w, **kwargs)
+
+    # XXX Template bw compat
+    template = obsolete('.template is deprecated, use .view')(wview)
+
+    def whead(self, data):
+        self.req.html_headers.write(data)
+
+    def wdata(self, data):
+        """simple helper that escapes `data` and writes into `self.w`"""
+        self.w(html_escape(data))
+
+    def action(self, actionid, row=0):
+        """shortcut to get action object with id `actionid`"""
+        return self.vreg.select_action(actionid, self.req, self.rset,
+                                       row=row)
+
+    def action_url(self, actionid, label=None, row=0):
+        """simple method to be able to display `actionid` as a link anywhere
+        """
+        action = self.vreg.select_action(actionid, self.req, self.rset,
+                                         row=row)
+        if action:
+            label = label or self.req._(action.title)
+            return u'<a href="%s">%s</a>' % (html_escape(action.url()), label)
+        return u''
+
+    def html_headers(self):
+        """return a list of html headers (eg something to be inserted between
+        <head> and </head> of the returned page
+
+        by default return a meta tag to disable robot indexation of the page
+        """
+        return [NOINDEX]
+
+    def page_title(self):
+        """returns a title according to the result set - used for the
+        title in the HTML header
+        """
+        vtitle = self.req.form.get('vtitle')
+        if vtitle:
+            return self.req._(vtitle)
+        # class defined title will only be used if the resulting title doesn't
+        # seem clear enough
+        vtitle = getattr(self, 'title', None) or u''
+        if vtitle:
+            vtitle = self.req._(vtitle)
+        rset = self.rset
+        if rset and rset.rowcount:
+            if rset.rowcount == 1:
+                try:
+                    entity = self.complete_entity(0)
+                    # use long_title to get context information if any
+                    clabel = entity.dc_long_title()
+                except NotAnEntity:
+                    clabel = display_name(self.req, rset.description[0][0])
+                    clabel = u'%s (%s)' % (clabel, vtitle)
+            else :
+                etypes = rset.column_types(0)
+                if len(etypes) == 1:
+                    etype = iter(etypes).next()
+                    clabel = display_name(self.req, etype, 'plural')
+                else :
+                    clabel = u'#[*] (%s)' % vtitle
+        else:
+            clabel = vtitle
+        return u'%s (%s)' % (clabel, self.req.property_value('ui.site-title'))
+
+    def output_url_builder( self, name, url, args ):
+        self.w(u'<script language="JavaScript"><!--\n' \
+               u'function %s( %s ) {\n' % (name, ','.join(args) ) )
+        url_parts = url.split("%s")
+        self.w(u' url="%s"' % url_parts[0] )
+        for arg, part in zip(args, url_parts[1:]):
+            self.w(u'+str(%s)' % arg )
+            if part:
+                self.w(u'+"%s"' % part)
+        self.w('\n document.window.href=url;\n')
+        self.w('}\n-->\n</script>\n')
+
+    def create_url(self, etype, **kwargs):
+        """ return the url of the entity creation form for a given entity type"""
+        return self.req.build_url('add/%s'%etype, **kwargs)
+
+    def field(self, label, value, row=True, show_label=True, w=None, tr=True):
+        """ read-only field """
+        if w is None:
+            w = self.w
+        if row:
+            w(u'<div class="row">')
+        if show_label:
+            if tr:
+                label = display_name(self.req, label)
+            w(u'<span class="label">%s</span>' % label)
+        w(u'<div class="field">%s</div>' % value)
+        if row:
+            w(u'</div>')
+
+    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
+
+
+
+# concrete views base classes #################################################
+
+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'
+
+
+class StartupView(View):
+    """base class for views which doesn't need a particular result set to be
+    displayed (so they can always be displayed !)
+    """
+    __select__ = none_rset()
+    registered = require_group_compat(View.registered)
+
+    category = 'startupview'
+
+    def url(self):
+        """return the url associated with this view. We can omit rql here"""
+        return self.build_url('view', vid=self.id)
+
+    def html_headers(self):
+        """return a list of html headers (eg something to be inserted between
+        <head> and </head> of the returned page
+
+        by default startup views are indexed
+        """
+        return []
+
+
+class EntityStartupView(EntityView):
+    """base class for entity views which may also be applied to None
+    result set (usually a default rql is provided by the view class)
+    """
+    __select__ = none_rset() | non_final_entity()
+
+    default_rql = None
+
+    def __init__(self, req, rset, **kwargs):
+        super(EntityStartupView, self).__init__(req, rset, **kwargs)
+        if rset is None:
+            # this instance is not in the "entityview" category
+            self.category = 'startupview'
+
+    def startup_rql(self):
+        """return some rql to be executed if the result set is None"""
+        return self.default_rql
+
+    def call(self, **kwargs):
+        """override call to execute rql returned by the .startup_rql method if
+        necessary
+        """
+        if self.rset is None:
+            self.rset = self.req.execute(self.startup_rql())
+        rset = self.rset
+        for i in xrange(len(rset)):
+            self.wview(self.id, rset, row=i, **kwargs)
+
+    def url(self):
+        """return the url associated with this view. We can omit rql if we are
+        on a result set on which we do not apply.
+        """
+        if self.rset is None:
+            return self.build_url(vid=self.id)
+        return super(EntityStartupView, self).url()
+
+
+class AnyRsetView(View):
+    """base class for views applying on any non empty result sets"""
+    __select__ = nonempty_rset()
+
+    category = 'anyrsetview'
+
+    def columns_labels(self, tr=True):
+        if tr:
+            translate = display_name
+        else:
+            translate = lambda req, val: val
+        rqlstdescr = self.rset.syntax_tree().get_description()[0] # XXX missing Union support
+        labels = []
+        for colindex, attr in enumerate(rqlstdescr):
+            # compute column header
+            if colindex == 0 or attr == 'Any': # find a better label
+                label = ','.join(translate(self.req, et)
+                                 for et in self.rset.column_types(colindex))
+            else:
+                label = translate(self.req, attr)
+            labels.append(label)
+        return labels
+
+
+# concrete template base classes ##############################################
+
+class MainTemplate(View):
+    """main template are primary access point to render a full HTML page.
+    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):
+        if self.req.xhtml_browser():
+            return STRICT_DOCTYPE
+        return STRICT_DOCTYPE_NOEXT
+
+    def set_stream(self, w=None):
+        if self.w is not None:
+            return
+        if w is None:
+            if self.binary:
+                self._stream = stream = StringIO()
+            else:
+                self._stream = stream = HTMLStream(self.req)
+            w = stream.write
+        else:
+            stream = None
+        self.w = w
+        return stream
+
+    def write_doctype(self, xmldecl=True):
+        assert isinstance(self._stream, HTMLStream)
+        self._stream.doctype = self.doctype
+        if not xmldecl:
+            self._stream.xmldecl = u''
+
+    def linkable(self):
+        return False
+
+# concrete component base classes #############################################
+
+class ReloadableMixIn(object):
+    """simple mixin for reloadable parts of UI"""
+
+    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.req.add_js('cubicweb.ajax.js')
+        if nonify:
+            _cb = cb
+            def cb(*args):
+                _cb(*args)
+        cbname = self.req.register_onetime_callback(cb, *args)
+        return self.build_js(cbname, html_escape(msg or ''))
+
+    def build_update_js_call(self, cbname, msg):
+        rql = html_escape(self.rset.printable_rql())
+        return "javascript:userCallbackThenUpdateUI('%s', '%s', '%s', '%s', '%s', '%s')" % (
+            cbname, self.id, rql, msg, self.__registry__, self.div_id())
+
+    def build_reload_js_call(self, cbname, msg):
+        return "javascript:userCallbackThenReloadPage('%s', '%s')" % (cbname, msg)
+
+    build_js = build_update_js_call # expect updatable component by default
+
+    def div_id(self):
+        return ''
+
+
+class Component(ReloadableMixIn, View):
+    """base class for components"""
+    __registry__ = 'components'
+    __select__ = yes()
+    property_defs = {}
+
+    def div_class(self):
+        return '%s %s' % (self.propval('htmlclass'), self.id)
+
+    def div_id(self):
+        return '%sComponent' % self.id
--- a/vregistry.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/vregistry.py	Mon May 04 13:09:48 2009 +0200
@@ -6,12 +6,7 @@
 
 * to interact with the vregistry, object should inherit from the
   VObject abstract class
-  
-* the registration procedure is delegated to a registerer. Each
-  registerable vobject must defines its registerer class using the
-  __registerer__ attribute.  A registerer is instantianted at
-  registration time after what the instance is lost
-  
+
 * the selection procedure has been generalized by delegating to a
   selector, which is responsible to score the vobject according to the
   current state (req, rset, row, col). At the end of the selection, if
@@ -27,124 +22,53 @@
 
 import sys
 from os import listdir, stat
-from os.path import dirname, join, realpath, split, isdir
+from os.path import dirname, join, realpath, split, isdir, exists
 from logging import getLogger
+import types
 
 from cubicweb import CW_SOFTWARE_ROOT, set_log_methods
 from cubicweb import RegistryNotFound, ObjectNotFound, NoSelectableObject
 
 
-class vobject_helper(object):
-    """object instantiated at registration time to help a wrapped
-    VObject subclass
+def _toload_info(path, _toload=None):
+    """return a dictionary of <modname>: <modpath> and an ordered list of
+    (file, module name) to load
     """
-
-    def __init__(self, registry, vobject):
-        self.registry = registry
-        self.vobject = vobject
-        self.config = registry.config
-        self.schema = registry.schema
-
-
-class registerer(vobject_helper):
-    """do whatever is needed at registration time for the wrapped
-    class, according to current application schema and already
-    registered objects of the same kind (i.e. same registry name and
-    same id).
-
-    The wrapped class may be skipped, some previously selected object
-    may be kicked out... After whatever works needed, if the object or
-    a transformed object is returned, it will be added to previously
-    registered objects.
-    """
-
-    def __init__(self, registry, vobject):
-        super(registerer, self).__init__(registry, vobject)
-        self.kicked = set()
-    
-    def do_it_yourself(self, registered):
-        raise NotImplementedError(str(self.vobject))
-        
-    def kick(self, registered, kicked, msg='?'):
-        self.debug('kicking vobject %s because %s', kicked, msg)
-        registered.remove(kicked)
-        self.kicked.add(kicked.classid())
-        
-    def skip(self):
-        self.debug('no schema compat, skipping %s', self.vobject)
-
+    from logilab.common.modutils import modpath_from_file
+    if _toload is None:
+        _toload = {}, []
+    for fileordir in path:
+        if isdir(fileordir) and exists(join(fileordir, '__init__.py')):
+            subfiles = [join(fileordir, fname) for fname in listdir(fileordir)]
+            _toload_info(subfiles, _toload)
+        elif fileordir[-3:] == '.py':
+            modname = '.'.join(modpath_from_file(fileordir))
+            _toload[0][modname] = fileordir
+            _toload[1].append((fileordir, modname))
+    return _toload
 
-def selector(cls, *args, **kwargs):
-    """selector is called to help choosing the correct object for a
-    particular request and result set by returning a score.
-
-    it must implement a .score_method taking a request, a result set and
-    optionaly row and col arguments which return an int telling how well
-    the wrapped class apply to the given request and result set. 0 score
-    means that it doesn't apply.
-    
-    rset may be None. If not, row and col arguments may be optionally
-    given if the registry is scoring a given row or a given cell of
-    the result set (both row and col are int if provided).
-    """    
-    raise NotImplementedError(cls)
-
-
-class autoselectors(type):
-    """implements __selectors__ / __select__ compatibility layer so that:
-
-    __select__ = chainall(classmethod(A, B, C))
-
-    can be replaced by something like:
-    
-    __selectors__ = (A, B, C)
-    """
-    def __new__(mcs, name, bases, classdict):
-        if '__select__' in classdict and '__selectors__' in classdict:
-            raise TypeError("__select__ and __selectors__ "
-                            "can't be used together")
-        if '__select__' not in classdict and '__selectors__' in classdict:
-            selectors = classdict['__selectors__']
-            if len(selectors) > 1:
-                classdict['__select__'] = classmethod(chainall(*selectors))
-            else:
-                classdict['__select__'] = classmethod(selectors[0])
-        return super(autoselectors, mcs).__new__(mcs, name, bases, classdict)
-
-    def __setattr__(self, attr, value):
-        if attr == '__selectors__':
-            self.__select__ = classmethod(chainall(*value))
-        super(autoselectors, self).__setattr__(attr, value)
-            
 
 class VObject(object):
     """visual object, use to be handled somehow by the visual components
     registry.
 
     The following attributes should be set on concret vobject subclasses:
-    
+
     :__registry__:
       name of the registry for this object (string like 'views',
       'templates'...)
     :id:
       object's identifier in the registry (string like 'main',
       'primary', 'folder_box')
-    :__registerer__:
-      registration helper class
     :__select__:
-      selection helper function
-    :__selectors__:
-      tuple of selectors to be chained
-      (__select__ and __selectors__ are mutually exclusive)
-      
+      class'selector
+
     Moreover, the `__abstract__` attribute may be set to True to indicate
     that a vobject is abstract and should not be registered
     """
-    __metaclass__ = autoselectors
     # necessary attributes to interact with the registry
     id = None
     __registry__ = None
-    __registerer__ = None
     __select__ = None
 
     @classmethod
@@ -155,12 +79,13 @@
         may be the right hook to create an instance for example). By
         default the vobject is returned without any transformation.
         """
+        cls.build___select__()
         return cls
 
     @classmethod
     def selected(cls, *args, **kwargs):
         """called by the registry when the vobject has been selected.
-        
+
         It must return the  object that will be actually returned by the
         .select method (this may be the right hook to create an
         instance for example). By default the selected object is
@@ -173,13 +98,36 @@
         """returns a unique identifier for the vobject"""
         return '%s.%s' % (cls.__module__, cls.__name__)
 
+    # XXX bw compat code
+    @classmethod
+    def build___select__(cls):
+        for klass in cls.mro():
+            if klass.__name__ == 'AppRsetObject':
+                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
+
 
 class VRegistry(object):
     """class responsible to register, propose and select the various
     elements used to build the web interface. Currently, we have templates,
     views, actions and components.
     """
-    
+
     def __init__(self, config):#, cache_size=1000):
         self.config = config
         # dictionnary of registry (themself dictionnary) by name
@@ -204,45 +152,290 @@
 
     def __contains__(self, key):
         return key in self._registries
-        
-    def register_vobject_class(self, cls, _kicked=set()):
+
+    def registry(self, name):
+        """return the registry (dictionary of class objects) associated to
+        this name
+        """
+        try:
+            return self._registries[name]
+        except KeyError:
+            raise RegistryNotFound(name), None, sys.exc_info()[-1]
+
+    def registry_objects(self, name, oid=None):
+        """returns objects registered with the given oid in the given registry.
+        If no oid is given, return all objects in this registry
+        """
+        registry = self.registry(name)
+        if oid:
+            try:
+                return registry[oid]
+            except KeyError:
+                raise ObjectNotFound(oid), None, sys.exc_info()[-1]
+        else:
+            result = []
+            for objs in registry.values():
+                result += objs
+            return result
+
+    def object_by_id(self, registry, cid, *args, **kwargs):
+        """return the most specific component according to the resultset"""
+        objects = self[registry][cid]
+        assert len(objects) == 1, objects
+        return objects[0].selected(*args, **kwargs)
+
+    # methods for explicit (un)registration ###################################
+
+#     def clear(self, key):
+#         regname, oid = key.split('.')
+#         self[regname].pop(oid, None)
+    def register_all(self, objects, modname, butclasses=()):
+        for obj in objects:
+            try:
+                if obj.__module__ != modname or obj in butclasses:
+                    continue
+                oid = obj.id
+            except AttributeError:
+                continue
+            if oid:
+                self.register(obj)
+
+    def register(self, obj, registryname=None, oid=None, clear=False):
+        """base method to add an object in the registry"""
+        assert not '__abstract__' in obj.__dict__
+        registryname = registryname or obj.__registry__
+        oid = oid or obj.id
+        assert oid
+        registry = self._registries.setdefault(registryname, {})
+        if clear:
+            vobjects = registry[oid] =  []
+        else:
+            vobjects = registry.setdefault(oid, [])
+        # registered() is technically a classmethod but is not declared
+        # as such because we need to compose registered in some cases
+        vobject = obj.registered.im_func(obj, self)
+        assert not vobject in vobjects, vobject
+        assert callable(vobject.__select__), vobject
+        vobjects.append(vobject)
+        try:
+            vname = vobject.__name__
+        except AttributeError:
+            vname = vobject.__class__.__name__
+        self.debug('registered vobject %s in registry %s with id %s',
+                   vname, registryname, oid)
+        # automatic reloading management
+        self._loadedmods[obj.__module__]['%s.%s' % (obj.__module__, oid)] = obj
+
+    def unregister(self, obj, registryname=None):
+        registryname = registryname or obj.__registry__
+        registry = self.registry(registryname)
+        removed_id = obj.classid()
+        for registered in registry[obj.id]:
+            # use classid() to compare classes because vreg will probably
+            # have its own version of the class, loaded through execfile
+            if registered.classid() == removed_id:
+                # XXX automatic reloading management
+                try:
+                    registry[obj.id].remove(registered)
+                except ValueError:
+                    self.warning('can\'t remove %s, no id %s in the %s registry',
+                                 removed_id, obj.id, registryname)
+                except ValueError:
+                    self.warning('can\'t remove %s, not in the %s registry with id %s',
+                                 removed_id, registryname, obj.id)
+#                 else:
+#                     # if objects is empty, remove oid from registry
+#                     if not registry[obj.id]:
+#                         del regcontent[oid]
+                break
+
+    def register_and_replace(self, obj, replaced, registryname=None):
+        if hasattr(replaced, 'classid'):
+            replaced = replaced.classid()
+        registryname = registryname or obj.__registry__
+        registry = self.registry(registryname)
+        registered_objs = registry[obj.id]
+        for index, registered in enumerate(registered_objs):
+            if registered.classid() == replaced:
+                del registry[obj.id][index]
+                break
+        self.register(obj, registryname=registryname)
+
+    # dynamic selection methods ###############################################
+
+    def select(self, vobjects, *args, **kwargs):
+        """return an instance of the most specific object according
+        to parameters
+
+        raise NoSelectableObject if not object apply
+        """
+        score, winners = 0, []
+        for vobject in vobjects:
+            vobjectscore = vobject.__select__(vobject, *args, **kwargs)
+            if vobjectscore > score:
+                score, winners = vobjectscore, [vobject]
+            elif vobjectscore > 0 and vobjectscore == score:
+                winners.append(vobject)
+        if not winners:
+            raise NoSelectableObject('args: %s\nkwargs: %s %s'
+                                     % (args, kwargs.keys(),
+                                        [repr(v) for v in vobjects]))
+        if len(winners) > 1:
+            if self.config.mode == 'installed':
+                self.error('select ambiguity, args: %s\nkwargs: %s %s',
+                           args, kwargs.keys(), [repr(v) for v in winners])
+            else:
+                raise Exception('select ambiguity, args: %s\nkwargs: %s %s'
+                                % (args, kwargs.keys(),
+                                   [repr(v) for v in winners]))
+        winner = winners[0]
+        # return the result of the .selected method of the vobject
+        return winner.selected(*args, **kwargs)
+
+    def possible_objects(self, registry, *args, **kwargs):
+        """return an iterator on possible objects in a registry for this result set
+
+        actions returned are classes, not instances
+        """
+        for vobjects in self.registry(registry).values():
+            try:
+                yield self.select(vobjects, *args, **kwargs)
+            except NoSelectableObject:
+                continue
+
+    def select_object(self, registry, cid, *args, **kwargs):
+        """return the most specific component according to the resultset"""
+        return self.select(self.registry_objects(registry, cid), *args, **kwargs)
+
+    # intialization methods ###################################################
+
+    def init_registration(self, path):
+        # compute list of all modules that have to be loaded
+        self._toloadmods, filemods = _toload_info(path)
+        self._loadedmods = {}
+        return filemods
+
+    def register_objects(self, path, force_reload=None):
+        if force_reload is None:
+            force_reload = self.config.mode == 'dev'
+        elif not force_reload:
+            # force_reload == False usually mean modules have been reloaded
+            # by another connection, so we want to update the registry
+            # content even if there has been no module content modification
+            self.reset()
+        # need to clean sys.path this to avoid import confusion pb (i.e.
+        # having the same module loaded as 'cubicweb.web.views' subpackage and
+        # as views'  or 'web.views' subpackage
+        # this is mainly for testing purpose, we should'nt need this in
+        # production environment
+        for webdir in (join(dirname(realpath(__file__)), 'web'),
+                       join(dirname(__file__), 'web')):
+            if webdir in sys.path:
+                sys.path.remove(webdir)
+        if CW_SOFTWARE_ROOT in sys.path:
+            sys.path.remove(CW_SOFTWARE_ROOT)
+        # load views from each directory in the application's path
+        filemods = self.init_registration(path)
+        change = False
+        for filepath, modname in filemods:
+            if self.load_file(filepath, modname, force_reload):
+                change = True
+        return change
+
+    def load_file(self, filepath, modname, force_reload=False):
+        """load visual objects from a python file"""
+        from logilab.common.modutils import load_module_from_name
+        if modname in self._loadedmods:
+            return
+        self._loadedmods[modname] = {}
+        try:
+            modified_on = stat(filepath)[-2]
+        except OSError:
+            # this typically happens on emacs backup files (.#foo.py)
+            self.warning('Unable to load %s. It is likely to be a backup file',
+                         filepath)
+            return False
+        if filepath in self._lastmodifs:
+            # only load file if it was modified
+            if modified_on <= self._lastmodifs[filepath]:
+                return
+            # if it was modified, unregister all exisiting objects
+            # from this module, and keep track of what was unregistered
+            unregistered = self.unregister_module_vobjects(modname)
+        else:
+            unregistered = None
+        # load the module
+        module = load_module_from_name(modname, use_sys=not force_reload)
+        self.load_module(module)
+        # if something was unregistered, we need to update places where it was
+        # referenced
+        if unregistered:
+            # oldnew_mapping = {}
+            registered = self._loadedmods[modname]
+            oldnew_mapping = dict((unregistered[name], registered[name])
+                                  for name in unregistered if name in registered)
+            self.update_registered_subclasses(oldnew_mapping)
+        self._lastmodifs[filepath] = modified_on
+        return True
+
+    def load_module(self, module):
+        self.info('loading %s', module)
+        if hasattr(module, 'registration_callback'):
+            module.registration_callback(self)
+        else:
+            for objname, obj in vars(module).items():
+                if objname.startswith('_'):
+                    continue
+                self._load_ancestors_then_object(module.__name__, obj)
+        self.debug('loaded %s', module)
+
+    def _load_ancestors_then_object(self, modname, obj):
+        # imported classes
+        objmodname = getattr(obj, '__module__', None)
+        if objmodname != modname:
+            if objmodname in self._toloadmods:
+                self.load_file(self._toloadmods[objmodname], objmodname)
+            return
+        # skip non registerable object
+        try:
+            if not issubclass(obj, VObject):
+                return
+        except TypeError:
+            return
+        objname = '%s.%s' % (modname, obj.__name__)
+        if objname in self._loadedmods[modname]:
+            return
+        self._loadedmods[modname][objname] = obj
+        for parent in obj.__bases__:
+            self._load_ancestors_then_object(modname, parent)
+        self.load_object(obj)
+
+    def load_object(self, obj):
+        try:
+            self.register_vobject_class(obj)
+        except Exception, ex:
+            if self.config.mode in ('test', 'dev'):
+                raise
+            self.exception('vobject %s registration failed: %s', obj, ex)
+
+    # old automatic registration XXX deprecated ###############################
+
+    def register_vobject_class(self, cls):
         """handle vobject class registration
-        
+
         vobject class with __abstract__ == True in their local dictionnary or
         with a name starting starting by an underscore are not registered.
         Also a vobject class needs to have __registry__ and id attributes set
         to a non empty string to be registered.
-
-        Registration is actually handled by vobject's registerer.
         """
         if (cls.__dict__.get('__abstract__') or cls.__name__[0] == '_'
             or not cls.__registry__ or not cls.id):
             return
-        # while reloading a module :
-        # if cls was previously kicked, it means that there is a more specific
-        # vobject defined elsewhere re-registering cls would kick it out
-        if cls.classid() in _kicked:
-            self.debug('not re-registering %s because it was previously kicked',
-                      cls.classid())
-        else:
-            regname = cls.__registry__
-            if cls.id in self.config['disable-%s' % regname]:
-                return
-            registry = self._registries.setdefault(regname, {})
-            vobjects = registry.setdefault(cls.id, [])
-            registerer = cls.__registerer__(self, cls)
-            cls = registerer.do_it_yourself(vobjects)
-            #_kicked |= registerer.kicked
-            if cls:
-                vobject = cls.registered(self)
-                try:
-                    vname = vobject.__name__
-                except AttributeError:
-                    vname = vobject.__class__.__name__
-                self.debug('registered vobject %s in registry %s with id %s',
-                          vname, cls.__registry__, cls.id)
-                vobjects.append(vobject)
-            
+        regname = cls.__registry__
+        if '%s.%s' % (regname, cls.id) in self.config['disable-appobjects']:
+            return
+        self.register(cls)
+
     def unregister_module_vobjects(self, modname):
         """removes registered objects coming from a given module
 
@@ -279,7 +472,6 @@
                     del objdict[oid]
         return unregistered
 
-
     def update_registered_subclasses(self, oldnew_mapping):
         """updates subclasses of re-registered vobjects
 
@@ -306,240 +498,189 @@
                                   obj.__module__, obj.__name__)
                         obj.__bases__ = newbases
 
-    def registry(self, name):
-        """return the registry (dictionary of class objects) associated to
-        this name
-        """
-        try:
-            return self._registries[name]
-        except KeyError:
-            raise RegistryNotFound(name), None, sys.exc_info()[-1]
-
-    def registry_objects(self, name, oid=None):
-        """returns objects registered with the given oid in the given registry.
-        If no oid is given, return all objects in this registry
-        """
-        registry = self.registry(name)
-        if oid:
-            try:
-                return registry[oid]
-            except KeyError:
-                raise ObjectNotFound(oid), None, sys.exc_info()[-1]
-        else:
-            result = []
-            for objs in registry.values():
-                result += objs
-            return result
-        
-    def select(self, vobjects, *args, **kwargs):
-        """return an instance of the most specific object according
-        to parameters
-
-        raise NoSelectableObject if not object apply
-        """
-        score, winners = 0, []
-        for vobject in vobjects:
-            vobjectscore = vobject.__select__(*args, **kwargs)
-            if vobjectscore > score:
-                score, winners = vobjectscore, [vobject]
-            elif vobjectscore > 0 and vobjectscore == score:
-                winners.append(vobject)
-        if not winners:
-            raise NoSelectableObject('args: %s\nkwargs: %s %s'
-                                     % (args, kwargs.keys(), [repr(v) for v in vobjects]))
-        if len(winners) > 1:
-            if self.config.mode == 'installed':
-                self.error('select ambiguity, args: %s\nkwargs: %s %s',
-                           args, kwargs.keys(), [repr(v) for v in winners])
-            else:
-                raise Exception('select ambiguity, args: %s\nkwargs: %s %s'
-                                % (args, kwargs.keys(), [repr(v) for v in winners]))
-        winner = winners[0]
-        # return the result of the .selected method of the vobject
-        return winner.selected(*args, **kwargs)
-    
-    def possible_objects(self, registry, *args, **kwargs):
-        """return an iterator on possible objects in a registry for this result set
-
-        actions returned are classes, not instances
-        """
-        for vobjects in self.registry(registry).values():
-            try:
-                yield self.select(vobjects, *args, **kwargs)
-            except NoSelectableObject:
-                continue
-
-    def select_object(self, registry, cid, *args, **kwargs):
-        """return the most specific component according to the resultset"""
-        return self.select(self.registry_objects(registry, cid), *args, **kwargs)
-
-    def object_by_id(self, registry, cid, *args, **kwargs):
-        """return the most specific component according to the resultset"""
-        objects = self[registry][cid]
-        assert len(objects) == 1, objects
-        return objects[0].selected(*args, **kwargs)
-    
-    # intialization methods ###################################################
-
-    
-    def register_objects(self, path, force_reload=None):
-        if force_reload is None:
-            force_reload = self.config.mode == 'dev'
-        elif not force_reload:
-            # force_reload == False usually mean modules have been reloaded
-            # by another connection, so we want to update the registry
-            # content even if there has been no module content modification
-            self.reset()
-        # need to clean sys.path this to avoid import confusion pb (i.e.
-        # having the same module loaded as 'cubicweb.web.views' subpackage and
-        # as views'  or 'web.views' subpackage
-        # this is mainly for testing purpose, we should'nt need this in
-        # production environment
-        for webdir in (join(dirname(realpath(__file__)), 'web'),
-                       join(dirname(__file__), 'web')):
-            if webdir in sys.path:
-                sys.path.remove(webdir)
-        if CW_SOFTWARE_ROOT in sys.path:
-            sys.path.remove(CW_SOFTWARE_ROOT)        
-        # load views from each directory in the application's path
-        change = False
-        for fileordirectory in path:
-            if isdir(fileordirectory):
-                if self.read_directory(fileordirectory, force_reload):
-                    change = True
-            else:
-                directory, filename = split(fileordirectory)
-                if self.load_file(directory, filename, force_reload):
-                    change = True
-        if change:
-            for registry, objects in self.items():
-                self.debug('available in registry %s: %s', registry,
-                           sorted(objects))
-        return change
-    
-    def read_directory(self, directory, force_reload=False):
-        """read a directory and register available views"""
-        modified_on = stat(realpath(directory))[-2]
-        # only read directory if it was modified
-        _lastmodifs = self._lastmodifs
-        if directory in _lastmodifs and modified_on <= _lastmodifs[directory]:
-            return False
-        self.info('loading directory %s', directory)
-        for filename in listdir(directory):
-            if filename[-3:] == '.py':
-                try:
-                    self.load_file(directory, filename, force_reload)
-                except OSError:
-                    # this typically happens on emacs backup files (.#foo.py)
-                    self.warning('Unable to load file %s. It is likely to be a backup file',
-                                 filename)
-                except Exception, ex:
-                    if self.config.mode in ('dev', 'test'):
-                        raise
-                    self.exception('%r while loading file %s', ex, filename)
-        _lastmodifs[directory] = modified_on
-        return True
-
-    def load_file(self, directory, filename, force_reload=False):
-        """load visual objects from a python file"""
-        from logilab.common.modutils import load_module_from_modpath, modpath_from_file
-        filepath = join(directory, filename)
-        modified_on = stat(filepath)[-2]
-        modpath = modpath_from_file(join(directory, filename))
-        modname = '.'.join(modpath)
-        unregistered = {}
-        _lastmodifs = self._lastmodifs
-        if filepath in _lastmodifs:
-            # only load file if it was modified
-            if modified_on <= _lastmodifs[filepath]:
-                return
-            else:
-                # if it was modified, unregister all exisiting objects
-                # from this module, and keep track of what was unregistered
-                unregistered = self.unregister_module_vobjects(modname)
-        # load the module
-        module = load_module_from_modpath(modpath, use_sys=not force_reload)
-        registered = self.load_module(module)
-        # if something was unregistered, we need to update places where it was
-        # referenced 
-        if unregistered:
-            # oldnew_mapping = {}
-            oldnew_mapping = dict((unregistered[name], registered[name])
-                                  for name in unregistered if name in registered)
-            self.update_registered_subclasses(oldnew_mapping)
-        _lastmodifs[filepath] = modified_on
-        return True
-
-    def load_module(self, module):
-        registered = {}
-        self.info('loading %s', module)
-        for objname, obj in vars(module).items():
-            if objname.startswith('_'):
-                continue
-            self.load_ancestors_then_object(module.__name__, registered, obj)
-        return registered
-    
-    def load_ancestors_then_object(self, modname, registered, obj):
-        # skip imported classes
-        if getattr(obj, '__module__', None) != modname:
-            return
-        # skip non registerable object
-        try:
-            if not issubclass(obj, VObject):
-                return
-        except TypeError:
-            return
-        objname = '%s.%s' % (modname, obj.__name__)
-        if objname in registered:
-            return
-        registered[objname] = obj
-        for parent in obj.__bases__:
-            self.load_ancestors_then_object(modname, registered, parent)
-        self.load_object(obj)
-            
-    def load_object(self, obj):
-        try:
-            self.register_vobject_class(obj)
-        except Exception, ex:
-            if self.config.mode in ('test', 'dev'):
-                raise
-            self.exception('vobject %s registration failed: %s', obj, ex)
-        
-# init logging 
+# init logging
 set_log_methods(VObject, getLogger('cubicweb'))
 set_log_methods(VRegistry, getLogger('cubicweb.registry'))
-set_log_methods(registerer, getLogger('cubicweb.registration'))
+
+
+# selector base classes and operations ########################################
+
+class Selector(object):
+    """base class for selector classes providing implementation
+    for operators ``&`` and ``|``
+
+    This class is only here to give access to binary operators, the
+    selector logic itself should be implemented in the __call__ method
 
 
-# advanced selector building functions ########################################
+    a selector is called to help choosing the correct object for a
+    particular context by returning a score (`int`) telling how well
+    the class given as first argument apply to the given context.
+
+    0 score means that the class doesn't apply.
+    """
+
+    @property
+    def func_name(self):
+        # backward compatibility
+        return self.__class__.__name__
+
+    def search_selector(self, selector):
+        """search for the given selector or selector instance in the selectors
+        tree. Return it of None if not found
+        """
+        if self is selector:
+            return self
+        if isinstance(selector, type) and isinstance(self, selector):
+            return self
+        return None
+
+    def __str__(self):
+        return self.__class__.__name__
+
+    def __and__(self, other):
+        return AndSelector(self, other)
+    def __rand__(self, other):
+        return AndSelector(other, self)
+
+    def __or__(self, other):
+        return OrSelector(self, other)
+    def __ror__(self, other):
+        return OrSelector(other, self)
+
+    def __invert__(self):
+        return NotSelector(self)
+
+    # XXX (function | function) or (function & function) not managed yet
+
+    def __call__(self, cls, *args, **kwargs):
+        return NotImplementedError("selector %s must implement its logic "
+                                   "in its __call__ method" % self.__class__)
+
+class MultiSelector(Selector):
+    """base class for compound selector classes"""
+
+    def __init__(self, *selectors):
+        self.selectors = self.merge_selectors(selectors)
+
+    def __str__(self):
+        return '%s(%s)' % (self.__class__.__name__,
+                           ','.join(str(s) for s in self.selectors))
+
+    @classmethod
+    def merge_selectors(cls, selectors):
+        """deal with selector instanciation when necessary and merge
+        multi-selectors if possible:
+
+        AndSelector(AndSelector(sel1, sel2), AndSelector(sel3, sel4))
+        ==> AndSelector(sel1, sel2, sel3, sel4)
+        """
+        merged_selectors = []
+        for selector in selectors:
+            try:
+                selector = _instantiate_selector(selector)
+            except:
+                pass
+            #assert isinstance(selector, Selector), selector
+            if isinstance(selector, cls):
+                merged_selectors += selector.selectors
+            else:
+                merged_selectors.append(selector)
+        return merged_selectors
 
-def chainall(*selectors):
+    def search_selector(self, selector):
+        """search for the given selector or selector instance in the selectors
+        tree. Return it of None if not found
+        """
+        for childselector in self.selectors:
+            if childselector is selector:
+                return childselector
+            found = childselector.search_selector(selector)
+            if found is not None:
+                return found
+        return None
+
+
+def objectify_selector(selector_func):
+    """convenience decorator for simple selectors where a class definition
+    would be overkill::
+
+        @objectify_selector
+        def yes(cls, *args, **kwargs):
+            return 1
+
+    """
+    return type(selector_func.__name__, (Selector,),
+                {'__call__': lambda self, *args, **kwargs: selector_func(*args, **kwargs)})
+
+def _instantiate_selector(selector):
+    """ensures `selector` is a `Selector` instance
+
+    NOTE: This should only be used locally in build___select__()
+    XXX: then, why not do it ??
+    """
+    if isinstance(selector, types.FunctionType):
+        return objectify_selector(selector)()
+    if isinstance(selector, type) and issubclass(selector, Selector):
+        return selector()
+    return selector
+
+
+class AndSelector(MultiSelector):
+    """and-chained selectors (formerly known as chainall)"""
+    def __call__(self, cls, *args, **kwargs):
+        score = 0
+        for selector in self.selectors:
+            partscore = selector(cls, *args, **kwargs)
+            if not partscore:
+                return 0
+            score += partscore
+        return score
+
+
+class OrSelector(MultiSelector):
+    """or-chained selectors (formerly known as chainfirst)"""
+    def __call__(self, cls, *args, **kwargs):
+        for selector in self.selectors:
+            partscore = selector(cls, *args, **kwargs)
+            if partscore:
+                return partscore
+        return 0
+
+class NotSelector(Selector):
+    """negation selector"""
+    def __init__(self, selector):
+        self.selector = selector
+
+    def __call__(self, cls, *args, **kwargs):
+        score = self.selector(cls, *args, **kwargs)
+        return int(not score)
+
+    def __str__(self):
+        return 'NOT(%s)' % super(NotSelector, self).__str__()
+
+
+# XXX bw compat functions #####################################################
+
+def chainall(*selectors, **kwargs):
     """return a selector chaining given selectors. If one of
     the selectors fail, selection will fail, else the returned score
     will be the sum of each selector'score
     """
     assert selectors
-    def selector(cls, *args, **kwargs):
-        score = 0
-        for selector in selectors:
-            partscore = selector(cls, *args, **kwargs)
-            if not partscore:
-                return 0
-            score += partscore
-        return score
+    # XXX do we need to create the AndSelector here, a tuple might be enough
+    selector = AndSelector(*selectors)
+    if 'name' in kwargs:
+        selector.__name__ = kwargs['name']
     return selector
 
-def chainfirst(*selectors):
+def chainfirst(*selectors, **kwargs):
     """return a selector chaining given selectors. If all
     the selectors fail, selection will fail, else the returned score
     will be the first non-zero selector score
     """
     assert selectors
-    def selector(cls, *args, **kwargs):
-        for selector in selectors:
-            partscore = selector(cls, *args, **kwargs)
-            if partscore:
-                return partscore
-        return 0
+    selector = OrSelector(*selectors)
+    if 'name' in kwargs:
+        selector.__name__ = kwargs['name']
     return selector
-
--- a/web/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -3,14 +3,16 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
-from cubicweb.web._exceptions import *    
+from simplejson import dumps
 
-_ = unicode
+from cubicweb.web._exceptions import *
+
 
 INTERNAL_FIELD_VALUE = '__cubicweb_internal_field__'
 
@@ -35,12 +37,46 @@
 from logging import getLogger
 LOGGER = getLogger('cubicweb.web')
 
+# XXX deprecated
 FACETTES = set()
 
 
-## FACETTES = set( (
-##     # (relation, role, target's attribute)
-##     ('created_by', 'subject', 'login'),
-##     ('in_group', 'subject', 'name'),
-##     ('in_state', 'subject', 'name'),
-##     ))
+
+def json_dumps(value):
+    if isinstance(value, decimal.Decimal):
+        value = float(value)
+    elif isinstance(value, (date, datetime)):
+        value = value.strftime('%Y-%m-%d %H:%M')
+    elif isinstance(value, timedelta):
+        value = (value.days * 24*60*60) + value.seconds
+    try:
+        return simplejson.dumps(value)
+    except TypeError:
+        return simplejson.dumps(repr(value))
+
+def jsonize(function):
+    def newfunc(*args, **kwargs):
+        return json_dumps(function(*args, **kwargs))
+    return newfunc
+
+def ajax_replace_url(nodeid, rql, vid=None, swap=False, **extraparams):
+    """builds a replacePageChunk-like url
+    >>> ajax_replace_url('foo', 'Person P')
+    "javascript: replacePageChunk('foo', 'Person%20P');"
+    >>> ajax_replace_url('foo', 'Person P', 'oneline')
+    "javascript: replacePageChunk('foo', 'Person%20P', 'oneline');"
+    >>> ajax_replace_url('foo', 'Person P', 'oneline', name='bar', age=12)
+    "javascript: replacePageChunk('foo', 'Person%20P', 'oneline', {'age':12, 'name':'bar'});"
+    >>> ajax_replace_url('foo', 'Person P', name='bar', age=12)
+    "javascript: replacePageChunk('foo', 'Person%20P', 'null', {'age':12, 'name':'bar'});"
+    """
+    params = [repr(nodeid), repr(urlquote(rql))]
+    if extraparams and not vid:
+        params.append("'null'")
+    elif vid:
+        params.append(repr(vid))
+    if extraparams:
+        params.append(json_dumps(extraparams))
+    if swap:
+        params.append('true')
+    return "javascript: replacePageChunk(%s);" % ', '.join(params)
--- a/web/action.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/action.py	Mon May 04 13:09:48 2009 +0200
@@ -1,29 +1,27 @@
 """abstract action classes for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb.common.appobject import AppRsetObject
-from cubicweb.common.registerers import action_registerer
-from cubicweb.common.selectors import add_etype_selector, \
-     match_search_state, searchstate_accept_one, \
-     searchstate_accept_one_but_etype
-    
+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)
+from cubicweb.appobject import AppRsetObject
+
 _ = unicode
 
 
 class Action(AppRsetObject):
     """abstract action. Handle the .search_states attribute to match
-    request search state. 
+    request search state.
     """
     __registry__ = 'actions'
-    __registerer__ = action_registerer
-    __selectors__ = (match_search_state,)
-    # by default actions don't appear in link search mode
-    search_states = ('normal',) 
+    __select__ = match_search_state('normal')
+
     property_defs = {
         'visible':  dict(type='Boolean', default=True,
                          help=_('display the action or not')),
@@ -36,186 +34,61 @@
     }
     site_wide = True # don't want user to configuration actions eproperties
     category = 'moreactions'
-    
-    @classmethod
-    def accept_rset(cls, req, rset, row, col):
-        user = req.user
-        action = cls.schema_action
-        if row is None:
-            score = 0
-            need_local_check = [] 
-            geteschema = cls.schema.eschema
-            for etype in rset.column_types(0):
-                accepted = cls.accept(user, etype)
-                if not accepted:
-                    return 0
-                if action:
-                    eschema = geteschema(etype)
-                    if not user.matching_groups(eschema.get_groups(action)):
-                        if eschema.has_local_role(action):
-                            # have to ckeck local roles
-                            need_local_check.append(eschema)
-                            continue
-                        else:
-                            # even a local role won't be enough
-                            return 0
-                score += accepted
-            if need_local_check:
-                # check local role for entities of necessary types
-                for i, row in enumerate(rset):
-                    if not rset.description[i][0] in need_local_check:
-                        continue
-                    if not cls.has_permission(rset.get_entity(i, 0), action):
-                        return 0
-                    score += 1
-            return score
-        col = col or 0
-        etype = rset.description[row][col]
-        score = cls.accept(user, etype)
-        if score and action:
-            if not cls.has_permission(rset.get_entity(row, col), action):
-                return 0
-        return score
-    
-    @classmethod
-    def has_permission(cls, entity, action):
-        """defined in a separated method to ease overriding (see ModifyAction
-        for instance)
-        """
-        return entity.has_perm(action)
-    
+
     def url(self):
         """return the url associated with this action"""
         raise NotImplementedError
-    
+
     def html_class(self):
         if self.req.selected(self.url()):
             return 'selected'
         if self.category:
             return 'box' + self.category.capitalize()
 
+
 class UnregisteredAction(Action):
     """non registered action used to build boxes. Unless you set them
     explicitly, .vreg and .schema attributes at least are None.
     """
     category = None
     id = None
-    
+
     def __init__(self, req, rset, title, path, **kwargs):
         Action.__init__(self, req, rset)
         self.title = req._(title)
         self._path = path
         self.__dict__.update(kwargs)
-        
+
     def url(self):
         return self._path
 
 
-class AddEntityAction(Action):
-    """link to the entity creation form. Concrete class must set .etype and
-    may override .vid
-    """
-    __selectors__ = (add_etype_selector, match_search_state)
-    vid = 'creation'
-    etype = None
-    
-    def url(self):
-        return self.build_url(vid=self.vid, etype=self.etype)
-
-
-class EntityAction(Action):
-    """an action for an entity. By default entity actions are only
-    displayable on single entity result if accept match.
-    """
-    __selectors__ = (searchstate_accept_one,)
-    schema_action = None
-    condition = None
-    
-    @classmethod
-    def accept(cls, user, etype):
-        score = super(EntityAction, cls).accept(user, etype)
-        if not score:
-            return 0
-        # check if this type of entity has the necessary relation
-        if hasattr(cls, 'rtype') and not cls.relation_possible(etype):
-            return 0
-        return score
-
-    
-class LinkToEntityAction(EntityAction):
+class LinkToEntityAction(Action):
     """base class for actions consisting to create a new object
     with an initial relation set to an entity.
     Additionaly to EntityAction behaviour, this class is parametrized
     using .etype, .rtype and .target attributes to check if the
     action apply and if the logged user has access to it
     """
-    etype = None
-    rtype = None
-    target = None
+    __select__ = (match_search_state('normal') & one_line_rset()
+                  & partial_relation_possible(action='add')
+                  & partial_may_add_relation())
+    registered = accepts_compat(Action.registered)
+
     category = 'addrelated'
 
-    @classmethod
-    def accept_rset(cls, req, rset, row, col):
-        entity = rset.get_entity(row or 0, col or 0)
-        # check if this type of entity has the necessary relation
-        if hasattr(cls, 'rtype') and not cls.relation_possible(entity.e_schema):
-            return 0
-        score = cls.accept(req.user, entity.e_schema)
-        if not score:
-            return 0
-        if not cls.check_perms(req, entity):
-            return 0
-        return score
-
-    @classmethod
-    def check_perms(cls, req, entity):
-        if not cls.check_rtype_perm(req, entity):
-            return False
-        # XXX document this:
-        # if user can create the relation, suppose it can create the entity
-        # this is because we usually can't check "add" permission before the
-        # entity has actually been created, and schema security should be
-        # defined considering this
-        #if not cls.check_etype_perm(req, entity):
-        #    return False
-        return True
-        
-    @classmethod
-    def check_etype_perm(cls, req, entity):
-        eschema = cls.schema.eschema(cls.etype)
-        if not eschema.has_perm(req, 'add'):
-            #print req.user.login, 'has no add perm on etype', cls.etype
-            return False
-        #print 'etype perm ok', cls
-        return True
-
-    @classmethod
-    def check_rtype_perm(cls, req, entity):
-        rschema = cls.schema.rschema(cls.rtype)
-        # cls.target is telling us if we want to add the subject or object of
-        # the relation
-        if cls.target == 'subject':
-            if not rschema.has_perm(req, 'add', toeid=entity.eid):
-                #print req.user.login, 'has no add perm on subject rel', cls.rtype, 'with', entity
-                return False
-        elif not rschema.has_perm(req, 'add', fromeid=entity.eid):
-            #print req.user.login, 'has no add perm on object rel', cls.rtype, 'with', entity
-            return False
-        #print 'rtype perm ok', cls
-        return True
-            
     def url(self):
         current_entity = self.rset.get_entity(self.row or 0, self.col or 0)
-        linkto = '%s:%s:%s' % (self.rtype, current_entity.eid, self.target)
+        linkto = '%s:%s:%s' % (self.rtype, current_entity.eid, target(self))
         return self.build_url(vid='creation', etype=self.etype,
                               __linkto=linkto,
                               __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')
 
-class LinkToEntityAction2(LinkToEntityAction):
-    """LinkToEntity action where the action is not usable on the same
-    entity's type as the one refered by the .etype attribute
-    """
-    __selectors__ = (searchstate_accept_one_but_etype,)
-    
--- a/web/application.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/application.py	Mon May 04 13:09:48 2009 +0200
@@ -14,20 +14,19 @@
 from cubicweb import set_log_methods
 from cubicweb import (ValidationError, Unauthorized, AuthenticationError,
                    NoSelectableObject, RepositoryError)
-from cubicweb.cwconfig import CubicWebConfiguration
 from cubicweb.cwvreg import CubicWebRegistry
 from cubicweb.web import (LOGGER, StatusResponse, DirectResponse, Redirect, NotFound,
                        RemoteCallFailed, ExplicitLogin, InvalidSession)
-from cubicweb.web.component import SingletonComponent
+from cubicweb.web.component import Component
 
 # make session manager available through a global variable so the debug view can
 # print information about web session
 SESSION_MANAGER = None
 
-class AbstractSessionManager(SingletonComponent):
+class AbstractSessionManager(Component):
     """manage session data associated to a session identifier"""
     id = 'sessionmanager'
-    
+
     def __init__(self):
         self.session_time = self.vreg.config['http-session-time'] or None
         assert self.session_time is None or self.session_time > 0
@@ -40,7 +39,7 @@
             assert self.cleanup_anon_session_time < self.session_time
         self.authmanager = self.vreg.select_component('authmanager')
         assert self.authmanager, 'no authentication manager found'
-        
+
     def clean_sessions(self):
         """cleanup sessions which has not been unused since a given amount of
         time. Return the number of sessions which have been closed.
@@ -58,28 +57,28 @@
                 self.close_session(session)
                 closed += 1
         return closed, total - closed
-    
+
     def has_expired(self, session):
         """return True if the web session associated to the session is expired
         """
         return not (self.session_time is None or
                     time() < session.last_usage_time + self.session_time)
-                
+
     def current_sessions(self):
         """return currently open sessions"""
         raise NotImplementedError()
-            
+
     def get_session(self, req, sessionid):
         """return existing session for the given session identifier"""
         raise NotImplementedError()
 
     def open_session(self, req):
         """open and return a new session for the given request
-        
+
         :raise ExplicitLogin: if authentication is required
         """
         raise NotImplementedError()
-    
+
     def close_session(self, session):
         """close session on logout or on invalid session detected (expired out,
         corrupted...)
@@ -87,19 +86,19 @@
         raise NotImplementedError()
 
 
-class AbstractAuthenticationManager(SingletonComponent):
+class AbstractAuthenticationManager(Component):
     """authenticate user associated to a request and check session validity"""
     id = 'authmanager'
 
     def authenticate(self, req):
         """authenticate user and return corresponding user object
-        
+
         :raise ExplicitLogin: if authentication is required (no authentication
         info found or wrong user/password)
         """
         raise NotImplementedError()
 
-    
+
 class CookieSessionHandler(object):
     """a session handler using a cookie to store the session identifier
 
@@ -108,7 +107,7 @@
       identifier
     """
     SESSION_VAR = '__session'
-    
+
     def __init__(self, appli):
         self.session_manager = appli.vreg.select_component('sessionmanager')
         assert self.session_manager, 'no session manager found'
@@ -122,7 +121,7 @@
         time
         """
         self.session_manager.clean_sessions()
-        
+
     def set_session(self, req):
         """associate a session to the request
 
@@ -133,7 +132,7 @@
         if no session id is found, open a new session for the connected user
         or request authentification as needed
 
-        :raise Redirect: if authentication has occured and succeed        
+        :raise Redirect: if authentication has occured and succeed
         """
         assert req.cnx is None # at this point no cnx should be set on the request
         cookie = req.get_cookie()
@@ -155,7 +154,7 @@
 
     def get_session(self, req, sessionid):
         return self.session_manager.get_session(req, sessionid)
-    
+
     def open_session(self, req):
         session = self.session_manager.open_session(req)
         cookie = req.get_cookie()
@@ -178,7 +177,7 @@
         except:
             req.cnx.rollback()
             raise
-        
+
     def _postlogin(self, req):
         """postlogin: the user has been authenticated, redirect to the original
         page (index by default) with a welcome message
@@ -198,7 +197,7 @@
         if path == 'login':
             path = 'view'
         raise Redirect(req.build_url(path, **args))
-    
+
     def logout(self, req):
         """logout from the application by cleaning the session and raising
         `AuthenticationError`
@@ -212,7 +211,7 @@
     """Central registry for the web application. This is one of the central
     object in the web application, coupling dynamically loaded objects with
     the application's schema and the application's configuration objects.
-    
+
     It specializes the VRegistry by adding some convenience methods to
     access to stored objects. Currently we have the following registries
     of objects known by the web application (library may use some others
@@ -224,7 +223,7 @@
     * components
     * actions
     """
-    
+
     def __init__(self, config, debug=None,
                  session_handler_fact=CookieSessionHandler,
                  vreg=None):
@@ -244,14 +243,14 @@
             from threading import Lock
             self._query_log = open(config['query-log-file'], 'a')
             self.publish = self.log_publish
-            self._logfile_lock = Lock()            
+            self._logfile_lock = Lock()
         else:
             self._query_log = None
             self.publish = self.main_publish
         # instantiate session and url resolving helpers
         self.session_handler = session_handler_fact(self)
         self.url_resolver = vreg.select_component('urlpublisher')
-    
+
     def connect(self, req):
         """return a connection for a logged user object according to existing
         sessions (i.e. a new connection may be created or an already existing
@@ -267,9 +266,9 @@
                                req=req, appli=self)
         except NoSelectableObject:
             raise Unauthorized(req._('not authorized'))
-            
+
     # publish methods #########################################################
-        
+
     def log_publish(self, path, req):
         """wrapper around _publish to log all queries executed for a given
         accessed path
@@ -294,13 +293,13 @@
 
     def main_publish(self, path, req):
         """method called by the main publisher to process <path>
-        
+
         should return a string containing the resulting page or raise a
         `NotFound` exception
 
         :type path: str
         :param path: the path part of the url to publish
-        
+
         :type req: `web.Request`
         :param req: the request object
 
@@ -371,7 +370,7 @@
             req.set_session_data(req.form['__errorurl'], forminfo)
             raise Redirect(req.form['__errorurl'])
         self.error_handler(req, ex, tb=False)
-        
+
     def error_handler(self, req, ex, tb=False):
         excinfo = sys.exc_info()
         self.exception(repr(ex))
@@ -384,21 +383,30 @@
             if tb:
                 req.data['excinfo'] = excinfo
             req.form['vid'] = 'error'
-            content = self.vreg.main_template(req, 'main')
+            errview = self.vreg.select_view('error', req, None)
+            template = self.main_template_id(req)
+            content = self.vreg.main_template(req, template, view=errview)
         except:
-            content = self.vreg.main_template(req, 'error')
+            content = self.vreg.main_template(req, 'error-template')
         raise StatusResponse(500, content)
-    
+
     def need_login_content(self, req):
         return self.vreg.main_template(req, 'login')
-    
+
     def loggedout_content(self, req):
         return self.vreg.main_template(req, 'loggedout')
-    
+
     def notfound_content(self, req):
-        template = req.property_value('ui.main-template') or 'main'
         req.form['vid'] = '404'
-        return self.vreg.main_template(req, template)
+        view = self.vreg.select_view('404', req, None)
+        template = self.main_template_id(req)
+        return self.vreg.main_template(req, template, view=view)
+
+    def main_template_id(self, req):
+        template = req.property_value('ui.main-template')
+        if template not in self.vreg.registry('views') :
+            template = 'main-template'
+        return template
 
 
 set_log_methods(CubicWebPublisher, LOGGER)
--- a/web/box.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/box.py	Mon May 04 13:09:48 2009 +0200
@@ -1,24 +1,19 @@
 """abstract box classes for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from logilab.common.decorators import cached
 from logilab.mtconverter import html_escape
 
-from cubicweb import Unauthorized, role as get_role
-from cubicweb.common.registerers import (
-    accepts_registerer, extresources_registerer,
-    etype_rtype_priority_registerer)
-from cubicweb.common.selectors import (
-    etype_rtype_selector, one_line_rset, accept, has_relation,
-    primary_view, match_context_prop, has_related_entities,
-    _rql_condition)
-from cubicweb.common.view import Template
-from cubicweb.common.appobject import ReloadableMixIn
+from cubicweb import Unauthorized, role as get_role, target as get_target
+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)
+from cubicweb.view import View, ReloadableMixIn
 
 from cubicweb.web.htmlwidgets import (BoxLink, BoxWidget, SideBoxWidget,
                                       RawBoxItem, BoxSeparator)
@@ -27,23 +22,24 @@
 _ = unicode
 
 
-class BoxTemplate(Template):
+class BoxTemplate(View):
     """base template for boxes, usually a (contextual) list of possible
-    
+
     actions. Various classes attributes may be used to control the box
     rendering.
-    
+
     You may override on of the formatting callbacks is this is not necessary
     for your custom box.
-    
+
     Classes inheriting from this class usually only have to override call
     to fetch desired actions, and then to do something like  ::
 
         box.render(self.w)
     """
     __registry__ = 'boxes'
-    __selectors__ = Template.__selectors__ + (match_context_prop,)
-    
+    __select__ = match_context_prop()
+    registered = classmethod(require_group_compat(View.registered))
+
     categories_in_order = ()
     property_defs = {
         _('visible'): dict(type='Boolean', default=True,
@@ -80,9 +76,9 @@
         if escape:
             title = html_escape(title)
         return self.box_action(self._action(title, path, **kwargs))
-    
+
     def _action(self, title, path, **kwargs):
-        return UnregisteredAction(self.req, self.rset, title, path, **kwargs)        
+        return UnregisteredAction(self.req, self.rset, title, path, **kwargs)
 
     # formating callbacks
 
@@ -95,25 +91,24 @@
         cls = getattr(action, 'html_class', lambda: None)() or self.htmlitemclass
         return BoxLink(action.url(), self.req._(action.title),
                        cls, self.boxitem_link_tooltip(action))
-        
+
 
 class RQLBoxTemplate(BoxTemplate):
     """abstract box for boxes displaying the content of a rql query not
     related to the current result set.
-    
+
     It rely on etype, rtype (both optional, usable to control registration
     according to application schema and display according to connected
     user's rights) and rql attributes
     """
-    __registerer__ = etype_rtype_priority_registerer
-    __selectors__ = BoxTemplate.__selectors__ + (etype_rtype_selector,)
+#XXX    __selectors__ = BoxTemplate.__selectors__ + (etype_rtype_selector,)
 
     rql  = None
-    
+
     def to_display_rql(self):
         assert self.rql is not None, self.id
         return (self.rql,)
-    
+
     def call(self, **kwargs):
         try:
             rset = self.req.execute(*self.to_display_rql())
@@ -128,7 +123,7 @@
             box.append(self.mk_action(tname, entity.absolute_url()))
         box.render(w=self.w)
 
-        
+
 class UserRQLBoxTemplate(RQLBoxTemplate):
     """same as rql box template but the rql is build using the eid of the
     request's user
@@ -137,34 +132,22 @@
     def to_display_rql(self):
         assert self.rql is not None, self.id
         return (self.rql, {'x': self.req.user.eid}, 'x')
-    
-
-class ExtResourcesBoxTemplate(BoxTemplate):
-    """base class for boxes displaying external resources such as the RSS logo.
-    It should list necessary resources with the .need_resources attribute.
-    """
-    __registerer__ = extresources_registerer
-    need_resources = ()
 
 
 class EntityBoxTemplate(BoxTemplate):
     """base class for boxes related to a single entity"""
-    __registerer__ = accepts_registerer
-    __selectors__ = (one_line_rset, primary_view,
-                     match_context_prop, etype_rtype_selector,
-                     has_relation, accept, _rql_condition)
-    accepts = ('Any',)
+    __select__ = BoxTemplate.__select__ & one_line_rset() & primary_view()
+    registered = accepts_compat(has_relation_compat(condition_compat(BoxTemplate.registered)))
     context = 'incontext'
-    condition = None
-    
+
     def call(self, row=0, col=0, **kwargs):
         """classes inheriting from EntityBoxTemplate should define cell_call"""
         self.cell_call(row, col, **kwargs)
 
 
 class RelatedEntityBoxTemplate(EntityBoxTemplate):
-    __selectors__ = EntityBoxTemplate.__selectors__ + (has_related_entities,)
-    
+    __select__ = EntityBoxTemplate.__select__ & partial_has_related_entities()
+
     def cell_call(self, row, col, **kwargs):
         entity = self.entity(row, col)
         limit = self.req.property_value('navigation.related-limit') + 1
@@ -182,8 +165,8 @@
     subclasses should define at least id, rtype and target
     class attributes.
     """
-    
-    def cell_call(self, row, col, view=None):
+
+    def cell_call(self, row, col, view=None, **kwargs):
         self.req.add_js('cubicweb.ajax.js')
         entity = self.entity(row, col)
         box = SideBoxWidget(display_name(self.req, self.rtype), self.id)
@@ -196,23 +179,17 @@
     def div_id(self):
         return self.id
 
-    @cached
-    def xtarget(self):
-        if self.target == 'subject':
-            return 'object', 'subject'
-        return 'subject', 'object'
-        
     def box_item(self, entity, etarget, rql, label):
         """builds HTML link to edit relation between `entity` and `etarget`
         """
-        x, target = self.xtarget()
-        args = {x[0] : entity.eid, target[0] : etarget.eid}
+        role, target = get_role(self), get_target(self)
+        args = {role[0] : entity.eid, target[0] : etarget.eid}
         url = self.user_rql_callback((rql, args))
         # for each target, provide a link to edit the relation
         label = u'[<a href="%s">%s</a>] %s' % (url, label,
                                                etarget.view('incontext'))
         return RawBoxItem(label, liclass=u'invisible')
-    
+
     def w_related(self, box, entity):
         """appends existing relations to the `box`"""
         rql = 'DELETE S %s O WHERE S eid %%(s)s, O eid %%(o)s' % self.rtype
@@ -220,7 +197,7 @@
         for etarget in related:
             box.append(self.box_item(entity, etarget, rql, u'-'))
         return len(related)
-    
+
     def w_unrelated(self, box, entity):
         """appends unrelated entities to the `box`"""
         rql = 'SET S %s O WHERE S eid %%(s)s, O eid %%(o)s' % self.rtype
@@ -233,19 +210,17 @@
         if etype is not defined on the Box's class, the default
         behaviour is to use the entity's appropraite vocabulary function
         """
-        x, target = self.xtarget()
         # use entity.unrelated if we've been asked for a particular etype
         if hasattr(self, 'etype'):
-            return entity.unrelated(self.rtype, self.etype, x).entities()
+            return entity.unrelated(self.rtype, self.etype, get_role(self)).entities()
         # in other cases, use vocabulary functions
         entities = []
-        for _, eid in entity.vocabulary(self.rtype, x):
+        for _, eid in entity.vocabulary(self.rtype, get_role(self)):
             if eid is not None:
                 rset = self.req.eid_rset(eid)
                 entities.append(rset.get_entity(0, 0))
         return entities
-        
+
     def related_entities(self, entity):
-        x, target = self.xtarget()
-        return entity.related(self.rtype, x, entities=True)
+        return entity.related(self.rtype, get_role(self), entities=True)
 
--- a/web/component.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/component.py	Mon May 04 13:09:48 2009 +0200
@@ -1,71 +1,72 @@
 """abstract component class and base components definition for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb.common.appobject import Component, SingletonComponent
-from cubicweb.common.utils import merge_dicts
-from cubicweb.common.view import VComponent, SingletonVComponent
-from cubicweb.common.registerers import action_registerer
-from cubicweb.common.selectors import (paginated_rset, one_line_rset,
-                                       rql_condition, accept, primary_view,
-                                       match_context_prop, has_relation,
-                                       etype_rtype_selector)
-from cubicweb.common.uilib import html_escape
+from logilab.common.deprecation import class_renamed
+from logilab.mtconverter import html_escape
+
+from cubicweb import role
+from cubicweb.utils import merge_dicts
+from cubicweb.view import View, 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)
 
 _ = unicode
 
-
-class EntityVComponent(VComponent):
+class EntityVComponent(Component):
     """abstract base class for additinal components displayed in content
     headers and footer according to:
-    
+
     * the displayed entity's type
     * a context (currently 'header' or 'footer')
 
     it should be configured using .accepts, .etype, .rtype, .target and
     .context class attributes
     """
-    
+
     __registry__ = 'contentnavigation'
-    __registerer__ = action_registerer    
-    __selectors__ = (one_line_rset, primary_view,
-                     match_context_prop, etype_rtype_selector,
-                     has_relation, accept,
-                     rql_condition)
-    
+    __select__ = one_line_rset() & primary_view() & match_context_prop()
+    registered = accepts_compat(has_relation_compat(condition_compat(View.registered)))
+
     property_defs = {
         _('visible'):  dict(type='Boolean', default=True,
                             help=_('display the box or not')),
         _('order'):    dict(type='Int', default=99,
                             help=_('display order of the component')),
         _('context'):  dict(type='String', default='header',
-                            vocabulary=(_('navtop'), _('navbottom'), 
+                            vocabulary=(_('navtop'), _('navbottom'),
                                         _('navcontenttop'), _('navcontentbottom')),
                             #vocabulary=(_('header'), _('incontext'), _('footer')),
                             help=_('context where this component should be displayed')),
         _('htmlclass'):dict(type='String', default='mainRelated',
                             help=_('html class of the component')),
     }
-    
-    accepts = ('Any',)
+
     context = 'navcontentbottom' # 'footer' | 'header' | 'incontext'
-    condition = None
-    
-    def call(self, view):
+
+    def call(self, view=None):
         return self.cell_call(0, 0, view)
 
-    def cell_call(self, row, col, view):
+    def cell_call(self, row, col, view=None):
         raise NotImplementedError()
 
-    
-class NavigationComponent(VComponent):
+
+class NavigationComponent(Component):
     """abstract base class for navigation components"""
-    __selectors__ = (paginated_rset,)
     id = 'navigation'
+    __select__ = paginated_rset()
+
+    property_defs = {
+        _('visible'):  dict(type='Boolean', default=True,
+                            help=_('display the component or not')),
+        }
+
     page_size_property = 'navigation.page-size'
     start_param = '__start'
     stop_param = '__stop'
@@ -74,21 +75,8 @@
     previous_page_link_templ = next_page_link_templ = page_link_templ
     no_previous_page_link = no_next_page_link = u''
 
-    @classmethod
-    def selected(cls, req, rset, row=None, col=None, page_size=None, **kwargs):
-        """by default web app objects are usually instantiated on
-        selection according to a request, a result set, and optional
-        row and col
-        """
-        instance = super(NavigationComponent, cls).selected(req, rset, row, col, **kwargs)
-        if page_size is not None:
-            instance.page_size = page_size
-        elif 'page_size' in req.form:
-            instance.page_size = int(req.form['page_size'])
-        return instance
-    
-    def __init__(self, req, rset):
-        super(NavigationComponent, self).__init__(req, rset)
+    def __init__(self, req, rset, **kwargs):
+        super(NavigationComponent, self).__init__(req, rset, **kwargs)
         self.starting_from = 0
         self.total = rset.rowcount
 
@@ -96,14 +84,20 @@
         try:
             return self._page_size
         except AttributeError:
-            self._page_size = self.req.property_value(self.page_size_property)
-            return self._page_size
+            page_size = self.extra_kwargs.get('page_size')
+            if page_size is None:
+                if 'page_size' in self.req.form:
+                    page_size = int(self.req.form['page_size'])
+                else:
+                    page_size = self.req.property_value(self.page_size_property)
+            self._page_size = page_size
+            return page_size
 
     def set_page_size(self, page_size):
         self._page_size = page_size
-        
+
     page_size = property(get_page_size, set_page_size)
-    
+
     def page_boundaries(self):
         try:
             stop = int(self.req.form[self.stop_param]) + 1
@@ -112,7 +106,7 @@
             start, stop = 0, self.page_size
         self.starting_from = start
         return start, stop
-        
+
     def clean_params(self, params):
         if self.start_param in params:
             del params[self.start_param]
@@ -151,25 +145,19 @@
 
 class RelatedObjectsVComponent(EntityVComponent):
     """a section to display some related entities"""
-    __selectors__ = (one_line_rset, primary_view,
-                     etype_rtype_selector, has_relation,
-                     match_context_prop, accept)
+    __select__ = EntityVComponent.__select__ & partial_has_related_entities()
+
     vid = 'list'
 
     def rql(self):
-        """override this method if you want to use a custom rql query.
-        """
+        """override this method if you want to use a custom rql query"""
         return None
-    
+
     def cell_call(self, row, col, view=None):
         rql = self.rql()
         if rql is None:
             entity = self.rset.get_entity(row, col)
-            if self.target == 'object':
-                role = 'subject'
-            else:
-                role = 'object'
-            rset = entity.related(self.rtype, role)
+            rset = entity.related(self.rtype, role(self))
         else:
             eid = self.rset[row][col]
             rset = self.req.execute(self.rql(), {'x': eid}, 'x')
@@ -178,3 +166,10 @@
         self.w(u'<div class="%s">' % self.div_class())
         self.wview(self.vid, rset, title=self.req._(self.title).capitalize())
         self.w(u'</div>')
+
+
+VComponent = class_renamed('VComponent', Component,
+                           'VComponent is deprecated, use Component')
+SingletonVComponent = class_renamed('SingletonVComponent', Component,
+                                    'SingletonVComponent is deprecated, use '
+                                    'Component and explicit registration control')
--- a/web/controller.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/controller.py	Mon May 04 13:09:48 2009 +0200
@@ -2,17 +2,17 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from mx.DateTime import strptime, Error as MxDTError, TimeDelta
+import datetime
 
 from cubicweb import typed_eid
-from cubicweb.common.registerers import priority_registerer
-from cubicweb.common.selectors import match_user_group
-from cubicweb.common.appobject import AppObject
+from cubicweb.utils import strptime
+from cubicweb.selectors import yes, require_group_compat
+from cubicweb.appobject import AppObject
 from cubicweb.web import LOGGER, Redirect, RequestError
 
 
@@ -21,7 +21,7 @@
                          ('__redirectpath', '__redirectpath'),
                          ('__redirectparams', '__redirectparams'),
                          )
-NAV_FORM_PARAMETERS = [fp for ap, fp in NAVIGATION_PARAMETERS]
+NAV_FORM_PARAMETERS = tuple(fp for ap, fp in NAVIGATION_PARAMETERS)
 
 def redirect_params(form):
     """transform redirection parameters into navigation parameters
@@ -47,7 +47,7 @@
         for subj in subjs.split('_'):
             for obj in objs.split('_'):
                 yield typed_eid(subj), rtype, typed_eid(obj)
-        
+
 def append_url_params(url, params):
     """append raw parameters to the url. Given parameters, if any, are expected
     to be already url-quoted.
@@ -67,16 +67,15 @@
     and another linked by forms to edit objects ("edit").
     """
     __registry__ = 'controllers'
-    __registerer__ = priority_registerer
-    __selectors__ = (match_user_group,)
-    require_groups = ()
+    __select__ = yes()
+    registered = require_group_compat(AppObject.registered)
 
     def __init__(self, *args, **kwargs):
         super(Controller, self).__init__(*args, **kwargs)
         # attributes use to control after edition redirection
         self._after_deletion_path = None
         self._edited_entity = None
-        
+
     def publish(self, rset=None):
         """publish the current request, with an option input rql string
         (already processed if necessary)
@@ -84,7 +83,18 @@
         raise NotImplementedError
 
     # generic methods useful for concret implementations ######################
-    
+
+    def process_rql(self, rql):
+        """execute rql if specified"""
+        if rql:
+            self.ensure_ro_rql(rql)
+            if not isinstance(rql, unicode):
+                rql = unicode(rql, self.req.encoding)
+            pp = self.vreg.select_component('magicsearch', self.req)
+            self.rset = pp.process_query(rql, self.req)
+            return self.rset
+        return None
+
     def check_expected_params(self, params):
         """check that the given list of parameters are specified in the form
         dictionary
@@ -96,7 +106,7 @@
         if missing:
             raise RequestError('missing required parameter(s): %s'
                                % ','.join(missing))
-    
+
     def parse_datetime(self, value, etype='Datetime'):
         """get a datetime or time from a string (according to etype)
         Datetime formatted as Date are accepted
@@ -107,20 +117,21 @@
             format = self.req.property_value('ui.datetime-format')
             try:
                 return strptime(value, format)
-            except MxDTError:
+            except:
                 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 TimeDelta(date.hour, date.minute, date.second)
-            except MxDTError:
+                return datetime.timedelta(0, date.hour *60*60 + date.minute*60 + date.second, 0)
+            except:
                 raise ValueError('can\'t parse %r (expected %s)' % (value, format))
         try:
             format = self.req.property_value('ui.date-format')
-            return strptime(value, format)
-        except MxDTError:
+            dt = strptime(value, format)
+            return datetime.date(dt.year, dt.month, dt.day)
+        except:
             raise ValueError('can\'t parse %r (expected %s)' % (value, format))
 
 
@@ -131,7 +142,7 @@
         #       relation) that might not be satisfied yet (in case of creations)
         if not self._edited_entity:
             self._edited_entity = entity
-        
+
     def delete_entities(self, eidtypes):
         """delete entities from the repository"""
         redirect_info = set()
@@ -150,7 +161,7 @@
             self.req.set_message(self.req._('entities deleted'))
         else:
             self.req.set_message(self.req._('entity deleted'))
-        
+
     def delete_relations(self, rdefs):
         """delete relations from the repository"""
         # FIXME convert to using the syntax subject:relation:eids
@@ -159,7 +170,7 @@
             rql = 'DELETE X %s Y where X eid %%(x)s, Y eid %%(y)s' % rtype
             execute(rql, {'x': subj, 'y': obj}, ('x', 'y'))
         self.req.set_message(self.req._('relations deleted'))
-    
+
     def insert_relations(self, rdefs):
         """insert relations into the repository"""
         execute = self.req.execute
@@ -167,7 +178,7 @@
             rql = 'SET X %s Y where X eid %%(x)s, Y eid %%(y)s' % rtype
             execute(rql, {'x': subj, 'y': obj}, ('x', 'y'))
 
-    
+
     def reset(self):
         """reset form parameters and redirect to a view determinated by given
         parameters
@@ -211,7 +222,7 @@
         url = self.build_url(path, **newparams)
         url = append_url_params(url, self.req.form.get('__redirectparams'))
         raise Redirect(url)
-    
+
 
     def _return_to_edition_view(self, newparams):
         """apply-button case"""
--- a/web/data/cubicweb.ajax.js	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.ajax.js	Mon May 04 13:09:48 2009 +0200
@@ -1,6 +1,6 @@
 /*
  *  :organization: Logilab
- *  :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+ *  :copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
  *  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
  */
 
@@ -9,6 +9,36 @@
 
 var JSON_BASE_URL = baseuri() + 'json?';
 
+function _loadAjaxHtmlHead(node, head, tag, srcattr) {
+    var loaded = [];
+    jQuery('head ' + tag).each(function(i) {
+	loaded.push(this.getAttribute(srcattr));
+    });
+    node.find(tag).each(function(i) {
+	if (!loaded.contains(this.getAttribute(srcattr))) {
+	    jQuery(this).appendTo(head);
+	}
+    });
+    node.find(tag).remove();
+}
+
+/*
+ * inspect dom response, search for a <div class="ajaxHtmlHead"> node and
+ * put its content into the real document's head.
+ * This enables dynamic css and js loading and is used by replacePageChunk
+ */
+function loadAjaxHtmlHead(node) {
+    var head = jQuery('head');
+    node = jQuery(node).find('div.ajaxHtmlHead');
+    _loadAjaxHtmlHead(node, head, 'script', 'src');
+    _loadAjaxHtmlHead(node, head, 'link', 'href');
+    node.find('*').appendTo(head);
+}
+
+function preprocessAjaxLoad(node, newdomnode) {
+    loadAjaxHtmlHead(newdomnode);
+}
+
 function postAjaxLoad(node) {
     // find sortable tables if there are some
     if (typeof(Sortable) != 'undefined') {
@@ -27,11 +57,12 @@
     if (typeof roundedCornersOnLoad != 'undefined') {
 	roundedCornersOnLoad();
     }
+    loadDynamicFragments(node);
+    jQuery(CubicWeb).trigger('ajax-loaded');
 }
 
 // cubicweb loadxhtml plugin to make jquery handle xhtml response
 jQuery.fn.loadxhtml = function(url, data, reqtype, mode) {
-
     var ajax = null;
     if (reqtype == 'post') {
 	ajax = jQuery.post;
@@ -41,16 +72,16 @@
     if (this.size() > 1) {
 	log('loadxhtml was called with more than one element');
     }
+    var node = this.get(0); // only consider the first element
     mode = mode || 'replace';
     var callback = null;
     if (data && data.callback) {
 	callback = data.callback;
 	delete data.callback;
     }
-    var node = this.get(0); // only consider the first element
-    ajax = jQuery.post;
     ajax(url, data, function(response) {
 	var domnode = getDomFromResponse(response);
+	preprocessAjaxLoad(node, domnode);
 	if (mode == 'swap') {
 	    var origId = node.id;
 	    node = swapDOM(node, domnode);
@@ -74,8 +105,12 @@
 /* finds each dynamic fragment in the page and executes the
  * the associated RQL to build them (Async call)
  */
-function loadDynamicFragments() {
-    var fragments = jQuery('div.dynamicFragment');
+function loadDynamicFragments(node) {
+    if (node) {
+	var fragments = jQuery(node).find('div.dynamicFragment');
+    } else {
+	var fragments = jQuery('div.dynamicFragment');
+    }
     if (fragments.length == 0) {
 	return;
     }
@@ -97,26 +132,10 @@
     }
 }
 
-jQuery(document).ready(loadDynamicFragments);
+jQuery(document).ready(function() {loadDynamicFragments();});
 
 //============= base AJAX functions to make remote calls =====================//
 
-
-/*
- * This function will call **synchronously** a remote method on the cubicweb server
- * @param fname: the function name to call (as exposed by the JSONController)
- * @param args: the list of arguments to pass the function
- */
-function remote_exec(fname) {
-    setProgressCursor();
-    var props = {'mode' : "remote", 'fname' : fname, 'pageid' : pageid,
-     		 'arg': map(jQuery.toJSON, sliceList(arguments, 1))};
-    var result  = jQuery.ajax({url: JSON_BASE_URL, data: props, async: false}).responseText;
-    result = evalJSON(result);
-    resetCursor();
-    return result;
-}
-
 function remoteCallFailed(err, req) {
     if (req.status == 500) {
 	updateMessage(err);
@@ -125,92 +144,64 @@
     }
 }
 
+
 /*
- * This function is the equivalent of MochiKit's loadJSONDoc but
- * uses POST instead of GET
+ * This function will call **synchronously** a remote method on the cubicweb server
+ * @param fname: the function name to call (as exposed by the JSONController)
+ *
+ * additional arguments will be directly passed to the specified function
+ *
+ * It looks at http headers to guess the response type.
  */
-function loadJSONDocUsingPOST(url, queryargs, mode) {
-    mode = mode || 'remote';
+function remoteExec(fname /* ... */) {
     setProgressCursor();
-    var dataType = (mode == 'remote') ? "json":null;
-    var deferred = loadJSON(url, queryargs, 'POST', dataType);
+    var props = {'fname' : fname, 'pageid' : pageid,
+     		 'arg': map(jQuery.toJSON, sliceList(arguments, 1))};
+    var result  = jQuery.ajax({url: JSON_BASE_URL, data: props, async: false}).responseText;
+    if (result) {
+	result = evalJSON(result);
+    }
+    resetCursor();
+    return result;
+}
+
+/*
+ * This function will call **asynchronously** a remote method on the json
+ * controller of the cubicweb http server
+ *
+ * @param fname: the function name to call (as exposed by the JSONController)
+ *
+ * additional arguments will be directly passed to the specified function
+ *
+ * It looks at http headers to guess the response type.
+ */
+
+function asyncRemoteExec(fname /* ... */) {
+    setProgressCursor();
+    var props = {'fname' : fname, 'pageid' : pageid,
+     		 'arg': map(jQuery.toJSON, sliceList(arguments, 1))};
+    var deferred = loadRemote(JSON_BASE_URL, props, 'POST');
     deferred = deferred.addErrback(remoteCallFailed);
-//     if (mode == 'remote') {
-// 	deferred = deferred.addCallbacks(evalJSONRequest);
-//     }
+    deferred = deferred.addErrback(resetCursor);
     deferred = deferred.addCallback(resetCursor);
     return deferred;
 }
 
 
-function _buildRemoteArgs(fname) {
-    return  {'mode' : "remote", 'fname' : fname, 'pageid' : pageid,
-     	     'arg': map(jQuery.toJSON, sliceList(arguments, 1))};
-}
-
-/*
- * This function will call **asynchronously** a remote method on the cubicweb server
- * This function is a low level one. You should use `async_remote_exec` or
- * `async_rawremote_exec` instead.
- *
- * @param fname: the function name to call (as exposed by the JSONController)
- * @param funcargs: the function's arguments
- * @param mode: rawremote or remote
- */
-function _async_exec(fname, funcargs, mode) {
-    var props = {'mode' : mode, 'fname' : fname, 'pageid' : pageid};
-    var args = map(urlEncode, map(jQuery.toJSON, funcargs));
-    args.unshift(''); // this is to be able to use join() directly
-    var queryargs = as_url(props) + args.join('&arg=');
-    return loadJSONDocUsingPOST(JSON_BASE_URL, queryargs, mode);
-}
-
-/*
- * This function will call **asynchronously** a remote method on the cubicweb server
- * @param fname: the function name to call (as exposed by the JSONController)
- * additional arguments will be directly passed to the specified function
- * Expected response type is Json.
- */
-function async_remote_exec(fname /* ... */) {
-    return _async_exec(fname, sliceList(arguments, 1), 'remote');
-}
-
-/*
- * This version of _async_exec doesn't expect a json response.
- * It looks at http headers to guess the response type.
- */
-function async_rawremote_exec(fname /* ... */) {
-    return _async_exec(fname, sliceList(arguments, 1), 'rawremote');
-}
-
-/*
- * This function will call **asynchronously** a remote method on the cubicweb server
- * @param fname: the function name to call (as exposed by the JSONController)
- * @param varargs: the list of arguments to pass to the function
- * This is an alternative form of `async_remote_exec` provided for convenience
- */
-function async_remote_exec_varargs(fname, varargs) {
-    return _async_exec(fname, varargs, 'remote');
-}
-
 /* emulation of gettext's _ shortcut
  */
 function _(message) {
-    return remote_exec('i18n', [message])[0];
-}
-
-function rqlexec(rql) {
-    return async_remote_exec('rql', rql);
+    return remoteExec('i18n', [message])[0];
 }
 
 function userCallback(cbname) {
-    async_remote_exec('user_callback', cbname);
+    asyncRemoteExec('user_callback', cbname);
 }
 
 function unloadPageData() {
     // NOTE: do not make async calls on unload if you want to avoid
     //       strange bugs
-    remote_exec('unload_page_data');
+    remoteExec('unload_page_data');
 }
 
 function openHash() {
@@ -228,7 +219,7 @@
     nodeid = nodeid || (compid + 'Component');
     extraargs = extraargs || {};
     var node = getNode(nodeid);
-    var d = async_rawremote_exec('component', compid, rql, registry, extraargs);
+    var d = asyncRemoteExec('component', compid, rql, registry, extraargs);
     d.addCallback(function(result, req) {
 	var domnode = getDomFromResponse(result);
 	if (node) {
@@ -251,7 +242,7 @@
 }
 
 function userCallbackThenUpdateUI(cbname, compid, rql, msg, registry, nodeid) {
-    var d = async_remote_exec('user_callback', cbname);
+    var d = asyncRemoteExec('user_callback', cbname);
     d.addCallback(function() {
 	reloadComponent(compid, rql, registry, nodeid);
 	if (msg) { updateMessage(msg); }
@@ -265,7 +256,7 @@
 }
 
 function userCallbackThenReloadPage(cbname, msg) {
-    var d = async_remote_exec('user_callback', cbname);
+    var d = asyncRemoteExec('user_callback', cbname);
     d.addCallback(function() {
 	window.location.reload();
 	if (msg) { updateMessage(msg); }
@@ -283,7 +274,7 @@
  * while the page was generated.
  */
 function unregisterUserCallback(cbname) {
-    var d = async_remote_exec('unregister_user_callback', cbname);
+    var d = asyncRemoteExec('unregister_user_callback', cbname);
     d.addCallback(function() {resetCursor();});
     d.addErrback(function(xxx) {
 	updateMessage(_("an error occured"));
@@ -311,14 +302,15 @@
     var props = {};
     if (node) {
 	props['rql'] = rql;
+	props['fname'] = 'view';
 	props['pageid'] = pageid;
 	if (vid) { props['vid'] = vid; }
 	if (extraparams) { jQuery.extend(props, extraparams); }
-	// FIXME we need to do as_url(props) manually instead of
+	// FIXME we need to do asURL(props) manually instead of
 	// passing `props` directly to loadxml because replacePageChunk
 	// is sometimes called (abusively) with some extra parameters in `vid`
 	var mode = swap?'swap':'replace';
-	var url = JSON_BASE_URL + as_url(props);
+	var url = JSON_BASE_URL + asURL(props);
 	jQuery(node).loadxhtml(url, params, 'get', mode);
     } else {
 	log('Node', nodeId, 'not found');
@@ -349,6 +341,22 @@
 jQuery(document).ready(buildWysiwygEditors);
 
 
+/*
+ * takes a list of DOM nodes and removes all empty text nodes
+ */
+function stripEmptyTextNodes(nodelist) {
+    var stripped = [];
+    for (var i=0; i < nodelist.length; i++) {
+	var node = nodelist[i];
+	if (isTextNode(node) && !node.textContent.strip()) {
+	    continue;
+	} else {
+	    stripped.push(node);
+	}
+    }
+    return stripped;
+}
+
 /* convenience function that returns a DOM node based on req's result. */
 function getDomFromResponse(response) {
     if (typeof(response) == 'string') {
@@ -360,6 +368,7 @@
 	// no child (error cases) => return the whole document
 	return doc.cloneNode(true);
     }
+    children = stripEmptyTextNodes(children);
     if (children.length == 1) {
 	// only one child => return it
 	return children[0].cloneNode(true);
--- a/web/data/cubicweb.bookmarks.js	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.bookmarks.js	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 CubicWeb.require('ajax.js');
 
 function removeBookmark(beid) {
-    d = async_remote_exec('delete_bookmark', beid);
+    d = asyncRemoteExec('delete_bookmark', beid);
     d.addCallback(function(boxcontent) {
 	    reloadComponent('bookmarks_box', '', 'boxes', 'bookmarks_box');
   	document.location.hash = '#header';
--- a/web/data/cubicweb.calendar.js	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.calendar.js	Mon May 04 13:09:48 2009 +0200
@@ -235,7 +235,7 @@
     //      the only way understood by both IE and Mozilla. Otherwise,
     //      IE accepts innerText and mozilla accepts textContent
     var selectedDate = new Date(cal.year, cal.month, cell.innerHTML, 12);
-    var xxx = remote_exec("format_date", toISOTimestamp(selectedDate));
+    var xxx = remoteExec("format_date", toISOTimestamp(selectedDate));
     input.value = xxx;
     cal.hide();
 }
--- a/web/data/cubicweb.compat.js	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.compat.js	Mon May 04 13:09:48 2009 +0200
@@ -365,19 +365,26 @@
 };
 
 
-function loadJSON(url, data, type, dataType) {
+/*
+ * Asynchronously load an url and return a deferred
+ * whose callbacks args are decoded according to
+ * the Content-Type response header
+ */
+function loadRemote(url, data, reqtype) {
     var d = new Deferred();
     jQuery.ajax({
 	url: url,
-	type: type,
+	type: reqtype,
 	data: data,
-	dataType: dataType,
 
 	beforeSend: function(xhr) {
 	    d.req = xhr;
 	},
 
 	success: function(data, status) {
+            if (d.req.getResponseHeader("content-type") == 'application/json') {
+              data = evalJSON(data);
+            }
 	    d.success(data);
 	},
 
@@ -507,23 +514,7 @@
 var KEYS = {
     KEY_ESC: 27,
     KEY_ENTER: 13
-}
+};
 
-// XHR = null;
-// function test() {
-//     var d = loadJSON('http://crater:9876/json?mode=remote&fname=i18n&pageid=xxx&arg=' + jQuery.toJSON(['modify']));
-//     d = d.addCallback(function (result, xhr) {
-// 	XHR = xhr;
-// 	log('got ajax result 1' + result + xhr);
-// 	log('got ajax result 1' + xhr);
-// 	log('got ajax result 1' + xhr + 'arguments =', arguments.length);
-//     });
-//     d.addCallback(function (x, req, y, z) {
-// 	log('callback 2 x =' + x, ' req=', req, 'y =', y, 'z=',z);
-//     }, 12, 13)
-//     d.addErrback(function (error, xhr) {
-// 	XHR = xhr;
-// 	log('got err', error, ' code =', xhr.status, 'arguments length=', arguments.length);
-//     })
-// }
 
+
--- a/web/data/cubicweb.edition.js	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.edition.js	Mon May 04 13:09:48 2009 +0200
@@ -1,6 +1,6 @@
 /*
  *  :organization: Logilab
- *  :copyright: 2003-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+ *  :copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
  *  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
  */
 
@@ -20,10 +20,11 @@
  * @param tabindex the tabindex that should be set on the widget
  */
 function setPropValueWidget(varname, tabindex) {
-    var key = firstSelected(jQuery('#pkey:'+varname));
+    var key = firstSelected(document.getElementById('pkey:'+varname));
     if (key) {
-	var args = _buildRemoteArgs('prop_widget', key, varname, tabindex);
-	jQuery('#div:value:'+varname).loadxhtml(JSON_BASE_URL, args, 'post');
+	var args = {fname: 'prop_widget', pageid: pageid,
+     		    arg: map(jQuery.toJSON, [key, varname, tabindex])};
+	jqNode('div:value:'+varname).loadxhtml(JSON_BASE_URL, args, 'post');
     }
 }
 
@@ -51,40 +52,25 @@
     });
 }
 
+
 function showMatchingSelect(selectedValue, eid) {
     if (selectedValue) {
 	divId = 'div' + selectedValue + '_' + eid;
 	var divNode = jQuery('#' + divId);
 	if (!divNode.length) {
 	    var args = {vid: 'unrelateddivs', relation: selectedValue,
-			rql: rql_for_eid(eid), pageid: pageid,
-			'__notemplate': 1};
-	    jQuery.get(JSON_BASE_URL, args, function(response) {
-		// append generated HTML to the cell
-		jQuery('#unrelatedDivs_' + eid).append(getDomFromResponse(response));
-		_showMatchingSelect(eid, jQuery('#' + divId));
-	    });
-	    // deferred = doXHR(JSON_BASE_URL + queryString(args));
-	    // deferred.addCallback(_buildAndShowMatchingSelect, eid, divId);
+			rql: rql_for_eid(eid), '__notemplate': 1,
+			callback: function() {_showMatchingSelect(eid, jQuery('#' + divId))}};
+	    jQuery('#unrelatedDivs_' + eid).loadxhtml(baseuri() + 'view', args, 'post', 'append');
 	} else {
 	    _showMatchingSelect(eid, divNode);
 	}
-    }
-    else {
+    } else {
 	_showMatchingSelect(eid, null);
     }
 }
 
 
-
-// @param divStr a HTML string returned by the server
-// function _buildAndShowMatchingSelect(eid, divId, req) {
-//     var tdNode = jQuery('#unrelatedDivs_' + eid);
-//     // append generated HTML to the cell
-//     tdNode.appendChild(getDomFromRequest(req));
-//     _showMatchingSelect(eid, jQuery('#' + divId));
-// }
-
 // @param divNode is a jQuery selection
 function _showMatchingSelect(eid, divNode) {
     // hide all divs, and then show the matching one
@@ -154,7 +140,7 @@
     // add hidden parameter
     var entityForm = jQuery('#entityForm');
     var oid = optionNode.id.substring(2); // option id is prefixed by "id"
-    remote_exec('add_pending_insert', oid.split(':'));
+    remoteExec('add_pending_inserts', [oid.split(':')]);
     var selectNode = optionNode.parentNode;
     // remove option node
     selectNode.removeChild(optionNode);
@@ -186,7 +172,7 @@
 	   options[options.length] = OPTION({'id' : elementId, 'value' : node_id}, entityView);
 	}
     }
-    remote_exec('remove_pending_insert', elementId.split(':'));
+    remoteExec('remove_pending_insert', elementId.split(':'));
 }
 
 // this function builds a Handle to cancel pending insertion
@@ -198,7 +184,7 @@
 
 // @param nodeId eid_from:r_type:eid_to
 function addPendingDelete(nodeId, eid) {
-    var d = async_remote_exec('add_pending_delete', nodeId.split(':'));
+    var d = asyncRemoteExec('add_pending_delete', nodeId.split(':'));
     d.addCallback(function () {
 	// and strike entity view
 	jqNode('span' + nodeId).addClass('pendingDelete');
@@ -209,7 +195,7 @@
 
 // @param nodeId eid_from:r_type:eid_to
 function cancelPendingDelete(nodeId, eid) {
-    var d = async_remote_exec('remove_pending_delete', nodeId.split(':'));
+    var d = asyncRemoteExec('remove_pending_delete', nodeId.split(':'));
     d.addCallback(function () {
 	// reset link's CSS class
 	jqNode('span' + nodeId).removeClass('pendingDelete');
@@ -232,11 +218,11 @@
 function selectForAssociation(tripletIdsString, originalEid) {
     var tripletlist = map(function (x) { return x.split(':'); },
 			  tripletIdsString.split('-'));
-    var d = async_remote_exec('add_pending_inserts', tripletlist);
+    var d = asyncRemoteExec('add_pending_inserts', tripletlist);
     d.addCallback(function () {
 	var args = {vid: 'edition', __mode: 'normal',
 		    rql: rql_for_eid(originalEid)};
-	document.location = 'view?' + as_url(args);
+	document.location = 'view?' + asURL(args);
     });
 
 }
@@ -246,36 +232,27 @@
     jQuery('#inline' + rtype + 'slot span.icounter').each(function (i) {
 	this.innerHTML = i+1;
     });
-    // var divnode = jQuery('#inline' + rtype + 'slot');
-    // var iforms = getElementsByTagAndClassName('span', 'icounter', divnode);
-    // for (var i=0; i<iforms.length; i++) {
-    //   iforms[i].innerHTML = i+1;
-    // }
 }
 
+
 /*
  * makes an AJAX request to get an inline-creation view's content
  * @param peid : the parent entity eid
- * @param ptype : the parent entity type
  * @param ttype : the target (inlined) entity type
  * @param rtype : the relation type between both entities
  */
-function addInlineCreationForm(peid, ptype, ttype, rtype, role) {
-    var d = async_rawremote_exec('inline_creation_form', peid, ptype, ttype, rtype, role);
+function addInlineCreationForm(peid, ttype, rtype, role) {
+    var d = asyncRemoteExec('inline_creation_form', peid, ttype, rtype, role);
     d.addCallback(function (response) {
 	var linknode = getNode('add' + rtype + ':' + peid + 'link');
         var dom = getDomFromResponse(response);
 	var form = jQuery(dom);
 	form.css('display', 'none');
 	form.insertBefore(linknode.parentNode).slideDown('fast');
-	// setStyle(form, {display: 'none'});
-	// insertSiblingNodesBefore(linknode.parentNode, form);
 	updateInlinedEntitiesCounters(rtype);
-	// slideDown(form, {'duration':0.6});
 	reorderTabindex();
 	form.trigger('inlinedform-added');
         postAjaxLoad(dom);
-	// MochiKit.Signal.signal(CubicWeb, 'inlinedform-added', form);
     });
     d.addErrback(function (xxx) {
 	log('xxx =', xxx);
@@ -301,15 +278,13 @@
  */
 function removeInlinedEntity(peid, rtype, eid) {
     var nodeid = ['rel', peid, rtype, eid].join('-');
-    var divid = ['div', peid, rtype, eid].join('-');
-    var noticeid = ['notice', peid, rtype, eid].join('-');
     var node = jqNode(nodeid);
     if (node && node.length) {
 	node.remove();
+	var divid = ['div', peid, rtype, eid].join('-');
 	jqNode(divid).fadeTo('fast', 0.5);
-	// setOpacity(divid, 0.4);
+	var noticeid = ['notice', peid, rtype, eid].join('-');
 	jqNode(noticeid).fadeIn('fast');
-	// appear(jQuery('#' + noticeid), {'duration': 0.5});
     }
 }
 
@@ -322,11 +297,8 @@
 	node = INPUT({type: 'hidden', id: nodeid,
 		      name: rtype+':'+peid, value: eid});
 	jqNode(['fs', peid, rtype, eid].join('-')).append(node);
-	// appendChildNodes(fs, node);
 	jqNode(divid).fadeTo('fast', 1);
-	// setOpacity(divid, 1);
 	jqNode(noticeid).hide();
-	// jQuery('#' + noticeid).hide();
     }
 }
 
@@ -434,8 +406,8 @@
 	var target = form.attr('cubicweb:target');
 	if (target) {
 	    form.attr('target', target);
-	    /* do not use display: none because some browser ignore iframe
-             *     with no display */
+	    /* do not use display: none because some browsers ignore iframe
+             * with no display */
 	    form.append(IFRAME({name: target, id: target,
 				src: 'javascript: void(0)',
 				width: '0px', height: '0px'}));
@@ -445,10 +417,6 @@
 
 $(document).ready(setFormsTarget);
 
-function _sendForm(formid, action) {
-    var zipped = formContents(formid);
-    return async_remote_exec('validate_form', action, zipped[0], zipped[1]);
-}
 
 /*
  * called on traditionnal form submission : the idea is to try
@@ -458,7 +426,8 @@
  */
 function validateForm(formid, action, onsuccess) {
     try {
-	var d = _sendForm(formid, action);
+	var zipped = formContents(formid);
+	var d = asyncRemoteExec('validate_form', action, zipped[0], zipped[1]);
     } catch (ex) {
 	log('got exception', ex);
 	return false;
@@ -466,11 +435,11 @@
     function _callback(result, req) {
 	handleFormValidationResponse(formid, onsuccess, result);
     }
-    // d.addCallback(handleFormValidationResponse, formid, onsuccess);
     d.addCallback(_callback);
     return false;
 }
 
+
 /*
  * called by live-edit forms to submit changes
  * @param formid : the dom id of the form used
@@ -490,7 +459,7 @@
 	    }
 	}
 	var zipped = formContents(form);
-	var d = async_remote_exec('edit_field', 'apply', zipped[0], zipped[1], rtype, eid);
+	var d = asyncRemoteExec('edit_field', 'apply', zipped[0], zipped[1], rtype, eid);
     } catch (ex) {
 	log('got exception', ex);
 	return false;
--- a/web/data/cubicweb.form.css	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.form.css	Mon May 04 13:09:48 2009 +0200
@@ -56,7 +56,8 @@
 table.attributeForm {
   border: 1px solid #E4EAD8;
   margin-bottom: 1em;
-  padding: 0.8em;
+  padding: 0.8em 1.2em;
+  width: 100%;
 }
 
 fieldset.subentity table td {
@@ -77,11 +78,12 @@
 
 table.attributeForm th,
 table.attributeForm td {
-  padding : 0px 2px;
+  padding : .7em 2px;
 }
 
 table.attributeForm th {
-  text-align: right;
+  text-align: left;
+  width: 12em;
 }
 
 table.attributeForm div#comfirmPsw {
@@ -100,7 +102,7 @@
 
 table.attributeForm label,
 .entityForm .label {
-  padding : 0.2em  10px 0.2em 0.4em;
+  padding : 0.2em  1em 0.2em 0;
 }
 
 table.attributeForm label.required {
--- a/web/data/cubicweb.formfilter.js	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.formfilter.js	Mon May 04 13:09:48 2009 +0200
@@ -43,7 +43,7 @@
     var zipped = facetFormContent(form);
     zipped[0].push('facetargs');
     zipped[1].push(vidargs);
-    var d = async_remote_exec('filter_build_rql', zipped[0], zipped[1]);
+    var d = asyncRemoteExec('filter_build_rql', zipped[0], zipped[1]);
     d.addCallback(function(result) {
 	var rql = result[0];
 	var $bkLink = jQuery('#facetBkLink');
@@ -80,7 +80,7 @@
 		reloadComponent('edit_box', rql, 'boxes', 'edit_box');
 	    }
 	}
-	var d = async_remote_exec('filter_select_content', toupdate, rql);
+	var d = asyncRemoteExec('filter_select_content', toupdate, rql);
 	d.addCallback(function(updateMap) {
 	    for (facetId in updateMap) {
 		var values = updateMap[facetId];
--- a/web/data/cubicweb.htmlhelpers.js	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.htmlhelpers.js	Mon May 04 13:09:48 2009 +0200
@@ -111,13 +111,13 @@
 }
 
 /* builds an url from an object (used as a dictionnary)
- * Notable difference with MochiKit's queryString: as_url does not
+ * Notable difference with MochiKit's queryString: asURL does not
  * *url_quote* each value found in the dictionnary
  *
- * >>> as_url({'rql' : "RQL", 'x': [1, 2], 'itemvid' : "oneline"})
+ * >>> asURL({'rql' : "RQL", 'x': [1, 2], 'itemvid' : "oneline"})
  * rql=RQL&vid=list&itemvid=oneline&x=1&x=2
  */
-function as_url(props) {
+function asURL(props) {
     var chunks = [];
     for(key in props) {
 	var value = props[key];
--- a/web/data/cubicweb.mailform.css	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.mailform.css	Mon May 04 13:09:48 2009 +0200
@@ -5,7 +5,7 @@
  *  :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
  */
 
-div#compose {
+form#sendmail {
   border: 1px solid #DBDCE3;
   background-color: #E9F5F7;
   font-family:Verdana,Tahoma,Arial,sans-serif;
@@ -16,7 +16,7 @@
   width: 100%;
 }
 
-div#compose td#buttonbar {
+form#sendmail td#buttonbar {
   padding: 0.5ex 0ex;
 }
 
@@ -36,12 +36,12 @@
   width: 47em; 
 }
 
-div#compose div#toolbar {
+form#sendmail div#toolbar {
   margin: 0.5em 0em;
   height: 29px;
 }
 
-div#compose div#toolbar ul {
+form#sendmail div#toolbar ul {
   list-style-image: none;
   list-style-position: outside;
   list-style-type:none;
@@ -50,13 +50,13 @@
   /* border: 1px solid #DBDCE3; */
 }
 
-div#compose div#toolbar li {
+form#sendmail div#toolbar li {
   background: none;
   padding-left: 1em;
   float: left;
 }
 
-div#compose div#toolbar li a {
+form#sendmail div#toolbar li a {
   font-family: Verdana,Tahoma,Arial,sans-serif;
   color: #444444;
 }
--- a/web/data/cubicweb.preferences.js	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.preferences.js	Mon May 04 13:09:48 2009 +0200
@@ -7,6 +7,8 @@
 function toggleVisibility(elemId) {
     _clearPreviousMessages();
     jqNode(elemId).toggleClass('hidden');
+    asyncRemoteExec('set_cookie', cookiename,
+                      jQuery('#' + elemId).attr('class'));
 }
 
 function closeFieldset(fieldsetid){
@@ -38,7 +40,6 @@
 		linkLabel = (_('open all'));
 	    }
 	});
- 
 }
 
 function validatePrefsForm(formid){
--- a/web/data/cubicweb.tabs.js	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.tabs.js	Mon May 04 13:09:48 2009 +0200
@@ -1,6 +1,6 @@
 function set_tab(tabname, cookiename) {
     // set appropriate cookie
-    async_remote_exec('set_cookie', cookiename, tabname);
+    asyncRemoteExec('set_cookie', cookiename, tabname);
     // trigger show + tabname event
     trigger_load(tabname);
 }
--- a/web/data/cubicweb.widgets.js	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/data/cubicweb.widgets.js	Mon May 04 13:09:48 2009 +0200
@@ -222,46 +222,46 @@
 
     __init__ : function(wdgnode) {
 	this.variables = getNodeAttribute(wdgnode, 'cubicweb:variables').split(',');
-	this.options = {'name' : wdgnode.getAttribute('cubicweb:inputname'),
-			'id'   : wdgnode.getAttribute('cubicweb:inputid'),
+	this.options = {'name'   : wdgnode.getAttribute('cubicweb:inputid'),
 			'rows' : wdgnode.getAttribute('cubicweb:rows') || 40,
 			'cols' : wdgnode.getAttribute('cubicweb:cols') || 80
 		       };
 	// this.variableRegexp = /%\((\w+)\)s/;
-	this.parentnode = wdgnode;
-    },
-
-    show : function(parentnode) {
-	parentnode = parentnode || this.parentnode;
-	this.errorField = DIV({'class' : "textfieldErrors"});
+	this.errorField = DIV({'class' : "errorMessage"});
 	this.textField = TEXTAREA(this.options);
-	connect(this.textField, 'onkeyup', this, this.highlightInvalidVariables);
-	appendChildNodes(parentnode, this.textField, this.errorField);
-	appendChildNodes(parentnode, this.textField);
+	jQuery(this.textField).bind('keyup', {'self': this}, this.highlightInvalidVariables);
+	jQuery('#substitutions').prepend(this.errorField);
+	jQuery('#substitutions .errorMessage').hide();
+	wdgnode.appendChild(this.textField);
     },
 
     /* signal callbacks */
 
-    highlightInvalidVariables : function() {
-	var text = this.textField.value;
+    highlightInvalidVariables : function(event) {
+	var self = event.data.self;
+	var text = self.textField.value;
 	var unknownVariables = [];
-	var it=0;
+	var it = 0;
 	var group = null;
 	var variableRegexp = /%\((\w+)\)s/g;
 	// emulates rgx.findAll()
 	while ( group=variableRegexp.exec(text) ) {
-	    if ( !this.variables.contains(group[1]) ) {
+	    if ( !self.variables.contains(group[1]) ) {
 		unknownVariables.push(group[1]);
 	    }
 	    it++;
-	    if (it > 5)
+	    if (it > 5) {
 		break;
+	    }
 	}
 	var errText = '';
 	if (unknownVariables.length) {
 	    errText = "Detected invalid variables : " + ", ".join(unknownVariables);
+	    jQuery('#substitutions .errorMessage').show();
+	} else {
+	    jQuery('#substitutions .errorMessage').hide();
 	}
-	this.errorField.innerHTML = errText;
+	self.errorField.innerHTML = errText;
     }
 
 });
@@ -282,14 +282,14 @@
 	  this.eid_to = name[1];
           this.etype_to = wdgnode.getAttribute('cubicweb:etype_to');
           this.etype_from = wdgnode.getAttribute('cubicweb:etype_from');
-     	  var d = async_remote_exec('add_and_link_new_entity', this.etype_to, this.rel, this.eid_to, this.etype_from, 'new_val');
+     	  var d = asyncRemoteExec('add_and_link_new_entity', this.etype_to, this.rel, this.eid_to, this.etype_from, 'new_val');
           d.addCallback(function (eid) {
-          jQuery(wdgnode).find("option[selected]").removeAttr("selected");
-          var new_option = OPTION({'value':eid, 'selected':'selected'}, value=new_val);
-          wdgnode.appendChild(new_option);
+	      jQuery(wdgnode).find("option[selected]").removeAttr("selected");
+              var new_option = OPTION({'value':eid, 'selected':'selected'}, value=new_val);
+              wdgnode.appendChild(new_option);
           });
           d.addErrback(function (xxx) {
-             log('xxx =', xxx);
+              log('xxx =', xxx);
           });
      });
    }
--- a/web/facet.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/facet.py	Mon May 04 13:09:48 2009 +0200
@@ -19,10 +19,8 @@
 from rql import parse, nodes
 
 from cubicweb import Unauthorized, typed_eid
-from cubicweb.common.selectors import match_context_prop, one_has_relation
-from cubicweb.common.registerers import priority_registerer
-from cubicweb.common.appobject import AppRsetObject
-from cubicweb.common.utils import AcceptMixIn
+from cubicweb.selectors import match_context_prop, partial_relation_possible
+from cubicweb.appobject import AppRsetObject
 from cubicweb.web.htmlwidgets import HTMLWidget
 
 ## rqlst manipulation functions used by facets ################################
@@ -119,12 +117,17 @@
     return None
 
 def _add_rtype_relation(rqlst, mainvar, rtype, role):
+    """add a relation relying `mainvar` to entities linked by the `rtype`
+    relation (where `mainvar` has `role`)
+
+    return the inserted variable for linked entities.
+    """
     newvar = rqlst.make_variable()
     if role == 'object':
-        rel = rqlst.add_relation(newvar, rtype, mainvar)
+        rqlst.add_relation(newvar, rtype, mainvar)
     else:
-        rel = rqlst.add_relation(mainvar, rtype, newvar)
-    return newvar, rel
+        rqlst.add_relation(mainvar, rtype, newvar)
+    return newvar
 
 def _prepare_vocabulary_rqlst(rqlst, mainvar, rtype, role):
     """prepare a syntax tree to generate a filter vocabulary rql using the given
@@ -134,11 +137,11 @@
     * add the new variable to GROUPBY clause if necessary
     * add the new variable to the selection
     """
-    newvar, rel = _add_rtype_relation(rqlst, mainvar, rtype, role)
+    newvar = _add_rtype_relation(rqlst, mainvar, rtype, role)
     if rqlst.groupby:
         rqlst.add_group_var(newvar)
     rqlst.add_selected(newvar)
-    return newvar, rel
+    return newvar
 
 def _remove_relation(rqlst, rel, var):
     """remove a constraint relation from the syntax tree"""
@@ -171,10 +174,10 @@
                                 sortfuncname=None, sortasc=True):
     """modify a syntax tree to retrieve only relevant attribute `attr` of `var`"""
     _cleanup_rqlst(rqlst, mainvar)
-    var, mainrel = _prepare_vocabulary_rqlst(rqlst, mainvar, rtype, role)
+    var = _prepare_vocabulary_rqlst(rqlst, mainvar, rtype, role)
     # not found, create one
     attrvar = rqlst.make_variable()
-    attrrel = rqlst.add_relation(var, attrname, attrvar)
+    rqlst.add_relation(var, attrname, attrvar)
     # if query is grouped, we have to add the attribute variable
     if rqlst.groupby:
         if not attrvar in rqlst.groupby:
@@ -234,8 +237,7 @@
 
 
 ## base facet classes #########################################################
-class AbstractFacet(AcceptMixIn, AppRsetObject):
-    __registerer__ = priority_registerer
+class AbstractFacet(AppRsetObject):
     __abstract__ = True
     __registry__ = 'facets'
     property_defs = {
@@ -334,8 +336,8 @@
 
 
 class RelationFacet(VocabularyFacet):
-    __selectors__ = (one_has_relation, match_context_prop)
-    # class attributes to configure the relation facet
+    __select__ = partial_relation_possible() & match_context_prop()
+    # class attributes to configure the rel ation facet
     rtype = None
     role = 'subject'
     target_attr = 'eid'
@@ -411,7 +413,7 @@
         if not value:
             return
         mainvar = self.filtered_variable
-        restrvar = _add_rtype_relation(self.rqlst, mainvar, self.rtype, self.role)[0]
+        restrvar = _add_rtype_relation(self.rqlst, mainvar, self.rtype, self.role)
         if isinstance(value, basestring):
             # only one value selected
             self.rqlst.add_eid_restriction(restrvar, value)
@@ -425,7 +427,7 @@
             # multiple values with AND operator
             self.rqlst.add_eid_restriction(restrvar, value.pop())
             while value:
-                restrvar = _add_rtype_relation(self.rqlst, mainvar, self.rtype, self.role)[0]
+                restrvar = _add_rtype_relation(self.rqlst, mainvar, self.rtype, self.role)
                 self.rqlst.add_eid_restriction(restrvar, value.pop())
 
 
@@ -443,7 +445,7 @@
         try:
             mainvar = self.filtered_variable
             _cleanup_rqlst(rqlst, mainvar)
-            newvar, rel = _prepare_vocabulary_rqlst(rqlst, mainvar, self.rtype, self.role)
+            newvar = _prepare_vocabulary_rqlst(rqlst, mainvar, self.rtype, self.role)
             _set_orderby(rqlst, newvar, self.sortasc, self.sortfunc)
             try:
                 rset = self.rqlexec(rqlst.as_string(), self.rset.args, self.rset.cachekey)
@@ -521,7 +523,7 @@
         if not self.facet.start_unfolded:
             cssclass += ' hidden'
         if len(self.items) > 6:
-            cssclass +=' overflowed'
+            cssclass += ' overflowed'
         self.w(u'<div class="facetBody%s">\n' % cssclass)
         for item in self.items:
             item.render(self.w)
--- a/web/form.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/form.py	Mon May 04 13:09:48 2009 +0200
@@ -1,55 +1,83 @@
 """abstract form classes for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from simplejson import dumps
+from warnings import warn
+
+from logilab.common.compat import any
+from logilab.common.decorators import iclassmethod
 
-from logilab.mtconverter import html_escape
+from cubicweb.appobject import AppRsetObject
+from cubicweb.selectors import yes, non_final_entity, match_kwargs, one_line_rset
+from cubicweb.view import NOINDEX, NOFOLLOW
+from cubicweb.common import tags
+from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param, stdmsgs
+from cubicweb.web.httpcache import NoHTTPCacheManager
+from cubicweb.web.controller import NAV_FORM_PARAMETERS
+from cubicweb.web.formfields import (Field, StringField, RelationField,
+                                     HiddenInitialValueField)
+from cubicweb.web.formrenderers import FormRenderer
+from cubicweb.web import formwidgets as fwdgs
 
-from cubicweb import typed_eid
-from cubicweb.common.selectors import match_form_params
-from cubicweb.common.registerers import accepts_registerer
-from cubicweb.common.view import NOINDEX, NOFOLLOW, View, EntityView, AnyRsetView
-from cubicweb.web import stdmsgs
-from cubicweb.web.httpcache import NoHTTPCacheManager
-from cubicweb.web.controller import redirect_params
+class FormViewMixIn(object):
+    """abstract form view mix-in"""
+    category = 'form'
+    controller = 'edit'
+    http_cache_manager = NoHTTPCacheManager
+    add_to_breadcrumbs = False
+
+    def html_headers(self):
+        """return a list of html headers (eg something to be inserted between
+        <head> and </head> of the returned page
+
+        by default forms are neither indexed nor followed
+        """
+        return [NOINDEX, NOFOLLOW]
+
+    def linkable(self):
+        """override since forms are usually linked by an action,
+        so we don't want them to be listed by appli.possible_views
+        """
+        return False
 
 
-def relation_id(eid, rtype, target, reid):
-    if target == 'subject':
-        return u'%s:%s:%s' % (eid, rtype, reid)
-    return u'%s:%s:%s' % (reid, rtype, eid)
-
-
+# XXX should disappear
 class FormMixIn(object):
-    """abstract form mix-in"""
-    category = 'form'
-    controller = 'edit'
-    domid = 'entityForm'
-    
-    http_cache_manager = NoHTTPCacheManager
-    add_to_breadcrumbs = False
-    skip_relations = set()
-    
-    def __init__(self, req, rset):
-        super(FormMixIn, self).__init__(req, rset)
-        self.maxrelitems = self.req.property_value('navigation.related-limit')
-        self.maxcomboitems = self.req.property_value('navigation.combobox-limit')
-        self.force_display = not not req.form.get('__force_display')
+    """abstract form mix-in
+    XXX: you should inherit from this FIRST (obscure pb with super call)
+    """
+
+    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
+
+    def session_key(self):
+        """return the key that may be used to store / retreive data about a
+        previous post which failed because of a validation error
+        """
+        return '%s#%s' % (self.req.url(), self.domid)
+
+    def __init__(self, req, rset, **kwargs):
+        super(FormMixIn, self).__init__(req, rset, **kwargs)
+        self.restore_previous_post(self.session_key())
+
+    def restore_previous_post(self, sessionkey):
         # get validation session data which may have been previously set.
         # deleting validation errors here breaks form reloading (errors are
         # no more available), they have to be deleted by application's publish
         # method on successful commit
-        formurl = req.url()
-        forminfo = req.get_session_data(formurl)
+        forminfo = self.req.get_session_data(sessionkey, pop=True)
         if forminfo:
-            req.data['formvalues'] = forminfo['values']
-            req.data['formerrors'] = errex = forminfo['errors']
-            req.data['displayederrors'] = set()
+            self.req.data['formvalues'] = forminfo['values']
+            self.req.data['formerrors'] = errex = forminfo['errors']
+            self.req.data['displayederrors'] = set()
             # if some validation error occured on entity creation, we have to
             # get the original variable name from its attributed eid
             foreid = errex.entity
@@ -59,7 +87,15 @@
                     break
             else:
                 errex.eid = foreid
-        
+
+    # XXX deprecated with new form system. Should disappear
+
+    domid = 'entityForm'
+    category = 'form'
+    controller = 'edit'
+    http_cache_manager = NoHTTPCacheManager
+    add_to_breadcrumbs = False
+
     def html_headers(self):
         """return a list of html headers (eg something to be inserted between
         <head> and </head> of the returned page
@@ -67,18 +103,46 @@
         by default forms are neither indexed nor followed
         """
         return [NOINDEX, NOFOLLOW]
-        
+
     def linkable(self):
         """override since forms are usually linked by an action,
         so we don't want them to be listed by appli.possible_views
         """
         return False
 
-    @property
-    def limit(self):
-        if self.force_display:
-            return None
-        return self.maxrelitems + 1
+
+    def button(self, label, klass='validateButton', tabindex=None, **kwargs):
+        if tabindex is None:
+            tabindex = self.req.next_tabindex()
+        return tags.input(value=label, klass=klass, **kwargs)
+
+    def action_button(self, label, onclick=None, __action=None, **kwargs):
+        if onclick is None:
+            onclick = "postForm('__action_%s', \'%s\', \'%s\')" % (
+                __action, label, self.domid)
+        return self.button(label, onclick=onclick, **kwargs)
+
+    def button_ok(self, label=None, type='submit', name='defaultsubmit',
+                  **kwargs):
+        label = self.req._(label or stdmsgs.BUTTON_OK).capitalize()
+        return self.button(label, name=name, type=type, **kwargs)
+
+    def button_apply(self, label=None, type='button', **kwargs):
+        label = self.req._(label or stdmsgs.BUTTON_APPLY).capitalize()
+        return self.action_button(label, __action='apply', type=type, **kwargs)
+
+    def button_delete(self, label=None, type='button', **kwargs):
+        label = self.req._(label or stdmsgs.BUTTON_DELETE).capitalize()
+        return self.action_button(label, __action='delete', type=type, **kwargs)
+
+    def button_cancel(self, label=None, type='button', **kwargs):
+        label = self.req._(label or stdmsgs.BUTTON_CANCEL).capitalize()
+        return self.action_button(label, __action='cancel', type=type, **kwargs)
+
+    def button_reset(self, label=None, type='reset', name='__action_cancel',
+                     **kwargs):
+        label = self.req._(label or stdmsgs.BUTTON_CANCEL).capitalize()
+        return self.button(label, type=type, **kwargs)
 
     def need_multipart(self, entity, categories=('primary', 'secondary')):
         """return a boolean indicating if form's enctype should be multipart
@@ -111,7 +175,7 @@
                             if not field in displayed)
             if errors:
                 if len(errors) > 1:
-                    templstr = '<li>%s</li>\n' 
+                    templstr = '<li>%s</li>\n'
                 else:
                     templstr = '&nbsp;%s\n'
                 for field, err in errors:
@@ -123,131 +187,484 @@
                     errormsg = '<ul>%s</ul>' % errormsg
             return u'<div class="errorMessage">%s</div>' % errormsg
         return u''
-    
-    def restore_pending_inserts(self, entity, cell=False):
-        """used to restore edition page as it was before clicking on
-        'search for <some entity type>'
-        
-        """
-        eid = entity.eid
-        cell = cell and "div_insert_" or "tr"
-        pending_inserts = set(self.req.get_pending_inserts(eid))
-        for pendingid in pending_inserts:
-            eidfrom, rtype, eidto = pendingid.split(':')
-            if typed_eid(eidfrom) == entity.eid: # subject
-                label = display_name(self.req, rtype, 'subject')
-                reid = eidto
+
+
+###############################################################################
+
+class metafieldsform(type):
+    """metaclass for FieldsForm to retrieve fields defined as class attributes
+    and put them into a single ordered list: '_fields_'.
+    """
+    def __new__(mcs, name, bases, classdict):
+        allfields = []
+        for base in bases:
+            if hasattr(base, '_fields_'):
+                allfields += base._fields_
+        clsfields = (item for item in classdict.items()
+                     if isinstance(item[1], Field))
+        for fieldname, field in sorted(clsfields, key=lambda x: x[1].creation_rank):
+            if not field.name:
+                field.set_name(fieldname)
+            allfields.append(field)
+        classdict['_fields_'] = allfields
+        return super(metafieldsform, mcs).__new__(mcs, name, bases, classdict)
+
+
+class FieldNotFound(Exception):
+    """raised by field_by_name when a field with the given name has not been
+    found
+    """
+
+class FieldsForm(FormMixIn, AppRsetObject):
+    __metaclass__ = metafieldsform
+    __registry__ = 'forms'
+    __select__ = yes()
+
+    is_subform = False
+
+    # attributes overrideable through __init__
+    internal_fields = ('__errorurl',) + NAV_FORM_PARAMETERS
+    needs_js = ('cubicweb.ajax.js', 'cubicweb.edition.js',)
+    needs_css = ('cubicweb.form.css',)
+    domid = 'form'
+    title = None
+    action = None
+    onsubmit = "return freezeFormButtons('%(domid)s');"
+    cssclass = None
+    cssstyle = None
+    cwtarget = None
+    redirect_path = None
+    set_error_url = True
+    copy_nav_params = False
+    form_buttons = None # form buttons (button widgets instances)
+
+    def __init__(self, req, rset=None, row=None, col=None, submitmsg=None,
+                 **kwargs):
+        super(FieldsForm, self).__init__(req, rset, row=row, col=col)
+        self.fields = list(self.__class__._fields_)
+        for key, val in kwargs.items():
+            if key in NAV_FORM_PARAMETERS:
+                self.form_add_hidden(key, val)
             else:
-                label = display_name(self.req, rtype, 'object')
-                reid = eidfrom
-            jscall = "javascript: cancelPendingInsert('%s', '%s', null, %s);" \
-                     % (pendingid, cell, eid)
-            rset = self.req.eid_rset(reid)
-            eview = self.view('text', rset, row=0)
-            # XXX find a clean way to handle baskets
-            if rset.description[0][0] == 'Basket':
-                eview = '%s (%s)' % (eview, display_name(self.req, 'Basket'))
-            yield rtype, pendingid, jscall, label, reid, eview
-        
-    
-    def force_display_link(self):
-        return (u'<span class="invisible">' 
-                u'[<a href="javascript: window.location.href+=\'&amp;__force_display=1\'">%s</a>]'
-                u'</span>' % self.req._('view all'))
-    
-    def relations_table(self, entity):
-        """yiels 3-tuples (rtype, target, related_list)
-        where <related_list> itself a list of :
-          - node_id (will be the entity element's DOM id)
-          - appropriate javascript's togglePendingDelete() function call
-          - status 'pendingdelete' or ''
-          - oneline view of related entity
+                assert hasattr(self.__class__, key) and not key[0] == '_', key
+                setattr(self, key, val)
+        if self.set_error_url:
+            self.form_add_hidden('__errorurl', self.session_key())
+        if self.copy_nav_params:
+            for param in NAV_FORM_PARAMETERS:
+                if not param in kwargs:
+                    value = req.form.get(param)
+                    if value:
+                        self.form_add_hidden(param, value)
+        if submitmsg is not None:
+            self.form_add_hidden('__message', submitmsg)
+        self.context = None
+        if 'domid' in kwargs:# session key changed
+            self.restore_previous_post(self.session_key())
+
+    @iclassmethod
+    def field_by_name(cls_or_self, name, role='subject'):
+        """return field with the given name and role"""
+        if isinstance(cls_or_self, type):
+            fields = cls_or_self._fields_
+        else:
+            fields = cls_or_self.fields
+        for field in fields:
+            if field.name == name and field.role == role:
+                return field
+        raise FieldNotFound(name)
+
+    @iclassmethod
+    def remove_field(cls_or_self, field):
+        """remove a field from form class or instance"""
+        if isinstance(cls_or_self, type):
+            fields = cls_or_self._fields_
+        else:
+            fields = cls_or_self.fields
+        fields.remove(field)
+
+    @iclassmethod
+    def append_field(cls_or_self, field):
+        """append a field to form class or instance"""
+        if isinstance(cls_or_self, type):
+            fields = cls_or_self._fields_
+        else:
+            fields = cls_or_self.fields
+        fields.append(field)
+
+    @property
+    def form_needs_multipart(self):
+        """true if the form needs enctype=multipart/form-data"""
+        return any(field.needs_multipart for field in self.fields)
+
+    def form_add_hidden(self, name, value=None, **kwargs):
+        """add an hidden field to the form"""
+        field = StringField(name=name, widget=fwdgs.HiddenInput, initial=value,
+                            **kwargs)
+        if 'id' in kwargs:
+            # by default, hidden input don't set id attribute. If one is
+            # explicitly specified, ensure it will be set
+            field.widget.setdomid = True
+        self.append_field(field)
+        return field
+
+    def add_media(self):
+        """adds media (CSS & JS) required by this widget"""
+        if self.needs_js:
+            self.req.add_js(self.needs_js)
+        if self.needs_css:
+            self.req.add_css(self.needs_css)
+
+    def form_render(self, **values):
+        """render this form, using the renderer given in args or the default
+        FormRenderer()
+        """
+        renderer = values.pop('renderer', FormRenderer())
+        return renderer.render(self, values)
+
+    def form_build_context(self, rendervalues=None):
+        """build form context values (the .context attribute which is a
+        dictionary with field instance as key associated to a dictionary
+        containing field 'name' (qualified), 'id', 'value' (for display, always
+        a string).
+
+        rendervalues is an optional dictionary containing extra kwargs given to
+        form_render()
         """
-        eid = entity.eid
-        pending_deletes = self.req.get_pending_deletes(eid)
-        # XXX (adim) : quick fix to get Folder relations
-        for label, rschema, target in entity.srelations_by_category(('generic', 'metadata'), 'add'):
-            if rschema in self.skip_relations:
-                continue
-            relatedrset = entity.related(rschema, target, limit=self.limit)
-            toggable_rel_link = self.toggable_relation_link_func(rschema)
-            related = []
-            for row in xrange(relatedrset.rowcount):
-                nodeid = relation_id(eid, rschema, target, relatedrset[row][0])
-                if nodeid in pending_deletes:
-                    status = u'pendingDelete'
-                    label = '+'
-                else:
-                    status = u''
-                    label = 'x'
-                dellink = toggable_rel_link(eid, nodeid, label)
-                eview = self.view('oneline', relatedrset, row=row)
-                related.append((nodeid, dellink, status, eview))
-            yield (rschema, target, related)
-        
-    def toggable_relation_link_func(self, rschema):
-        if not rschema.has_perm(self.req, 'delete'):
-            return lambda x, y, z: u''
-        return toggable_relation_link
+        self.context = context = {}
+        # on validation error, we get a dictionary of previously submitted
+        # values
+        self._previous_values = self.req.data.get('formvalues', {})
+        # ensure rendervalues is a dict
+        if rendervalues is None:
+            rendervalues = {}
+        for field in self.fields:
+            for field in field.actual_fields(self):
+                field.form_init(self)
+                value = self.form_field_display_value(field, rendervalues)
+                context[field] = {'value': value,
+                                  'name': self.form_field_name(field),
+                                  'id': self.form_field_id(field),
+                                  }
+
+    def form_field_display_value(self, field, rendervalues, load_bytes=False):
+        """return field's *string* value to use for display
+
+        looks in
+        1. previously submitted form values if any (eg on validation error)
+        2. req.form
+        3. extra kw args given to render_form
+        4. field's typed value
+
+        values found in 1. and 2. are expected te be already some 'display'
+        value while those found in 3. and 4. are expected to be correctly typed.
+        """
+        qname = self.form_field_name(field)
+        if qname in self._previous_values:
+            value = self._previous_values[qname]
+        elif qname in self.req.form:
+            value = self.req.form[qname]
+        else:
+            if field.name in rendervalues:
+                value = rendervalues[field.name]
+            else:
+                value = self.form_field_value(field, load_bytes)
+                if callable(value):
+                    value = value(self)
+            if value != INTERNAL_FIELD_VALUE:
+                value = field.format_value(self.req, value)
+        return value
+
+    def form_field_value(self, field, load_bytes=False):
+        """return field's *typed* value"""
+        value = field.initial
+        if callable(value):
+            value = value(self)
+        return value
+
+    def form_field_error(self, field):
+        """return validation error for widget's field, if any"""
+        errex = self.req.data.get('formerrors')
+        if errex and self._errex_match_field(errex, field):
+            self.req.data['displayederrors'].add(field.name)
+            return u'<span class="error">%s</span>' % errex.errors[field.name]
+        return u''
+
+    def form_field_format(self, field):
+        """return MIME type used for the given (text or bytes) field"""
+        return self.req.property_value('ui.default-text-format')
+
+    def form_field_encoding(self, field):
+        """return encoding used for the given (text) field"""
+        return self.req.encoding
+
+    def form_field_name(self, field):
+        """return qualified name for the given field"""
+        return field.name
+
+    def form_field_id(self, field):
+        """return dom id for the given field"""
+        return field.id
+
+    def form_field_vocabulary(self, field, limit=None):
+        """return vocabulary for the given field. Should be overriden in
+        specific forms using fields which requires some vocabulary
+        """
+        raise NotImplementedError
+
+    def _errex_match_field(self, errex, field):
+        """return true if the field has some error in given validation exception
+        """
+        return field.name in errex.errors
 
 
-    def redirect_url(self, entity=None):
-        """return a url to use as next direction if there are some information
-        specified in current form params, else return the result the reset_url
-        method which should be defined in concrete classes
+class EntityFieldsForm(FieldsForm):
+    __select__ = (match_kwargs('entity') | (one_line_rset & non_final_entity()))
+
+    internal_fields = FieldsForm.internal_fields + ('__type', 'eid', '__maineid')
+    domid = 'entityForm'
+
+    def __init__(self, *args, **kwargs):
+        self.edited_entity = kwargs.pop('entity', None)
+        msg = kwargs.pop('submitmsg', None)
+        super(EntityFieldsForm, self).__init__(*args, **kwargs)
+        if self.edited_entity is None:
+            self.edited_entity = self.complete_entity(self.row, self.col)
+        self.form_add_hidden('__type', eidparam=True)
+        self.form_add_hidden('eid')
+        if msg is not None:
+            # If we need to directly attach the new object to another one
+            for linkto in self.req.list_form_param('__linkto'):
+                self.form_add_hidden('__linkto', linkto)
+                msg = '%s %s' % (msg, self.req._('and linked'))
+            self.form_add_hidden('__message', msg)
+        # in case of direct instanciation
+        self.schema = self.edited_entity.schema
+        self.vreg = self.edited_entity.vreg
+
+    def _errex_match_field(self, errex, field):
+        """return true if the field has some error in given validation exception
+        """
+        return errex.eid == self.edited_entity.eid and field.name in errex.errors
+
+    def _relation_vocabulary(self, rtype, targettype, role,
+                            limit=None, done=None):
+        """return unrelated entities for a given relation and target entity type
+        for use in vocabulary
         """
-        rparams = redirect_params(self.req.form)
-        if rparams:
-            return self.build_url('view', **rparams)
-        return self.reset_url(entity)
+        if done is None:
+            done = set()
+        rset = self.edited_entity.unrelated(rtype, targettype, role, limit)
+        res = []
+        for entity in rset.entities():
+            if entity.eid in done:
+                continue
+            done.add(entity.eid)
+            res.append((entity.view('combobox'), entity.eid))
+        return res
 
-    def reset_url(self, entity):
-        raise NotImplementedError('implement me in concrete classes')
+    def _form_field_default_value(self, field, load_bytes):
+        defaultattr = 'default_%s' % field.name
+        if hasattr(self.edited_entity, defaultattr):
+            # XXX bw compat, default_<field name> on the entity
+            warn('found %s on %s, should be set on a specific form'
+                 % (defaultattr, self.edited_entity.id), DeprecationWarning)
+            value = getattr(self.edited_entity, defaultattr)
+            if callable(value):
+                value = value()
+        else:
+            value = super(EntityFieldsForm, self).form_field_value(field,
+                                                                   load_bytes)
+        return value
+
+    def form_build_context(self, values=None):
+        """overriden to add edit[s|o] hidden fields and to ensure schema fields
+        have eidparam set to True
 
-    BUTTON_STR = u'<input class="validateButton" type="submit" name="%s" value="%s" tabindex="%s"/>'
-    ACTION_SUBMIT_STR = u'<input class="validateButton" type="button" onclick="postForm(\'%s\', \'%s\', \'%s\')" value="%s" tabindex="%s"/>'
+        edit[s|o] hidden fields are used to indicate the value for the
+        associated field before the (potential) modification made when
+        submitting the form.
+        """
+        eschema = self.edited_entity.e_schema
+        for field in self.fields[:]:
+            for field in field.actual_fields(self):
+                fieldname = field.name
+                if fieldname != 'eid' and (
+                    (eschema.has_subject_relation(fieldname) or
+                     eschema.has_object_relation(fieldname))):
+                    field.eidparam = True
+                    self.fields.append(HiddenInitialValueField(field))
+        return super(EntityFieldsForm, self).form_build_context(values)
+
+    def form_field_value(self, field, load_bytes=False):
+        """return field's *typed* value
 
-    def button_ok(self, label=None, tabindex=None):
-        label = self.req._(label or stdmsgs.BUTTON_OK).capitalize()
-        return self.BUTTON_STR % ('defaultsubmit', label, tabindex or 2)
-    
-    def button_apply(self, label=None, tabindex=None):
-        label = self.req._(label or stdmsgs.BUTTON_APPLY).capitalize()
-        return self.ACTION_SUBMIT_STR % ('__action_apply', label, self.domid, label, tabindex or 3)
+        overriden to deal with
+        * special eid / __type / edits- / edito- fields
+        * lookup for values on edited entities
+        """
+        attr = field.name
+        entity = self.edited_entity
+        if attr == 'eid':
+            return entity.eid
+        if not field.eidparam:
+            return super(EntityFieldsForm, self).form_field_value(field, load_bytes)
+        if attr.startswith('edits-') or attr.startswith('edito-'):
+            # edit[s|o]- fieds must have the actual value stored on the entity
+            assert hasattr(field, 'visible_field')
+            vfield = field.visible_field
+            assert vfield.eidparam
+            if entity.has_eid():
+                return self.form_field_value(vfield)
+            return INTERNAL_FIELD_VALUE
+        if attr == '__type':
+            return entity.id
+        if field.role == 'object':
+            attr = 'reverse_' + attr
+        elif entity.e_schema.subject_relation(attr).is_final():
+            attrtype = entity.e_schema.destination(attr)
+            if attrtype == 'Password':
+                return entity.has_eid() and INTERNAL_FIELD_VALUE or ''
+            if attrtype == 'Bytes':
+                if entity.has_eid():
+                    if load_bytes:
+                        return getattr(entity, attr)
+                    # XXX value should reflect if some file is already attached
+                    return True
+                return False
+            if entity.has_eid():
+                value = getattr(entity, attr)
+            else:
+                value = self._form_field_default_value(field, load_bytes)
+            return value
+        # non final relation field
+        if entity.has_eid():
+            value = [ent.eid for ent in getattr(entity, attr)]
+        else:
+            value = self._form_field_default_value(field, load_bytes)
+        return value
+
+    def form_field_format(self, field):
+        """return MIME type used for the given (text or bytes) field"""
+        entity = self.edited_entity
+        if field.eidparam and entity.e_schema.has_metadata(field.name, 'format') and (
+            entity.has_eid() or '%s_format' % field.name in entity):
+            return self.edited_entity.attr_metadata(field.name, 'format')
+        return self.req.property_value('ui.default-text-format')
+
+    def form_field_encoding(self, field):
+        """return encoding used for the given (text) field"""
+        entity = self.edited_entity
+        if field.eidparam and entity.e_schema.has_metadata(field.name, 'encoding') and (
+            entity.has_eid() or '%s_encoding' % field.name in entity):
+            return self.edited_entity.attr_metadata(field.name, 'encoding')
+        return super(EntityFieldsForm, self).form_field_encoding(field)
+
+    def form_field_name(self, field):
+        """return qualified name for the given field"""
+        if field.eidparam:
+            return eid_param(field.name, self.edited_entity.eid)
+        return field.name
+
+    def form_field_id(self, field):
+        """return dom id for the given field"""
+        if field.eidparam:
+            return eid_param(field.id, self.edited_entity.eid)
+        return field.id
 
-    def button_delete(self, label=None, tabindex=None):
-        label = self.req._(label or stdmsgs.BUTTON_DELETE).capitalize()
-        return self.ACTION_SUBMIT_STR % ('__action_delete', label, self.domid, label, tabindex or 3)
-    
-    def button_cancel(self, label=None, tabindex=None):
-        label = self.req._(label or stdmsgs.BUTTON_CANCEL).capitalize()
-        return self.ACTION_SUBMIT_STR % ('__action_cancel', label, self.domid, label, tabindex or 4)
-    
-    def button_reset(self, label=None, tabindex=None):
-        label = self.req._(label or stdmsgs.BUTTON_CANCEL).capitalize()
-        return u'<input class="validateButton" type="reset" value="%s" tabindex="%s"/>' % (
-            label, tabindex or 4)
-        
-def toggable_relation_link(eid, nodeid, label='x'):
-    js = u"javascript: togglePendingDelete('%s', %s);" % (nodeid, html_escape(dumps(eid)))
-    return u'[<a class="handle" href="%s" id="handle%s">%s</a>]' % (js, nodeid, label)
+    def form_field_vocabulary(self, field, limit=None):
+        """return vocabulary for the given field"""
+        role, rtype = field.role, field.name
+        method = '%s_%s_vocabulary' % (role, rtype)
+        try:
+            vocabfunc = getattr(self, method)
+        except AttributeError:
+            try:
+                # XXX bw compat, <role>_<rtype>_vocabulary on the entity
+                vocabfunc = getattr(self.edited_entity, method)
+            except AttributeError:
+                vocabfunc = getattr(self, '%s_relation_vocabulary' % role)
+            else:
+                warn('found %s on %s, should be set on a specific form'
+                     % (method, self.edited_entity.id), DeprecationWarning)
+        # NOTE: it is the responsibility of `vocabfunc` to sort the result
+        #       (direclty through RQL or via a python sort). This is also
+        #       important because `vocabfunc` might return a list with
+        #       couples (label, None) which act as separators. In these
+        #       cases, it doesn't make sense to sort results afterwards.
+        return vocabfunc(rtype, limit)
+
+    def subject_relation_vocabulary(self, rtype, limit=None):
+        """defaut vocabulary method for the given relation, looking for
+        relation's object entities (i.e. self is the subject)
+        """
+        entity = self.edited_entity
+        if isinstance(rtype, basestring):
+            rtype = entity.schema.rschema(rtype)
+        done = None
+        assert not rtype.is_final(), rtype
+        if entity.has_eid():
+            done = set(e.eid for e in getattr(entity, str(rtype)))
+        result = []
+        rsetsize = None
+        for objtype in rtype.objects(entity.e_schema):
+            if limit is not None:
+                rsetsize = limit - len(result)
+            result += self._relation_vocabulary(rtype, objtype, 'subject',
+                                                rsetsize, done)
+            if limit is not None and len(result) >= limit:
+                break
+        return result
+
+    def object_relation_vocabulary(self, rtype, limit=None):
+        """defaut vocabulary method for the given relation, looking for
+        relation's subject entities (i.e. self is the object)
+        """
+        entity = self.edited_entity
+        if isinstance(rtype, basestring):
+            rtype = entity.schema.rschema(rtype)
+        done = None
+        if entity.has_eid():
+            done = set(e.eid for e in getattr(entity, 'reverse_%s' % rtype))
+        result = []
+        rsetsize = None
+        for subjtype in rtype.subjects(entity.e_schema):
+            if limit is not None:
+                rsetsize = limit - len(result)
+            result += self._relation_vocabulary(rtype, subjtype, 'object',
+                                                rsetsize, done)
+            if limit is not None and len(result) >= limit:
+                break
+        return result
+
+    def subject_in_state_vocabulary(self, rtype, limit=None):
+        """vocabulary method for the in_state relation, looking for relation's
+        object entities (i.e. self is the subject) according to initial_state,
+        state_of and next_state relation
+        """
+        entity = self.edited_entity
+        if not entity.has_eid() or not entity.in_state:
+            # get the initial state
+            rql = 'Any S where S state_of ET, ET name %(etype)s, ET initial_state S'
+            rset = self.req.execute(rql, {'etype': str(entity.e_schema)})
+            if rset:
+                return [(rset.get_entity(0, 0).view('combobox'), rset[0][0])]
+            return []
+        results = []
+        for tr in entity.in_state[0].transitions(entity):
+            state = tr.destination_state[0]
+            results.append((state.view('combobox'), state.eid))
+        return sorted(results)
 
 
-class Form(FormMixIn, View):
-    """base class for forms. Apply by default according to request form
-    parameters specified using the `form_params` class attribute which
-    should list necessary parameters in the form to be accepted.
-    """
-    __registerer__ = accepts_registerer
-    __select__ = classmethod(match_form_params)
+class CompositeForm(FieldsForm):
+    """form composed for sub-forms"""
 
-    form_params = ()
+    def __init__(self, *args, **kwargs):
+        super(CompositeForm, self).__init__(*args, **kwargs)
+        self.forms = []
 
-class EntityForm(FormMixIn, EntityView):
-    """base class for forms applying on an entity (i.e. uniform result set)
-    """
-
-class AnyRsetForm(FormMixIn, AnyRsetView):
-    """base class for forms applying on any empty result sets
-    """
-
+    def form_add_subform(self, subform):
+        """mark given form as a subform and append it"""
+        subform.is_subform = True
+        self.forms.append(subform)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/formfields.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,492 @@
+"""field classes for form construction
+
+:organization: Logilab
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from datetime import datetime
+
+from logilab.mtconverter import html_escape
+from yams.constraints import SizeConstraint, StaticVocabularyConstraint
+
+from cubicweb.schema import FormatConstraint
+from cubicweb.utils import ustrftime
+from cubicweb.common import tags, uilib
+from cubicweb.web import INTERNAL_FIELD_VALUE
+from cubicweb.web.formwidgets import (
+    HiddenInput, TextInput, FileInput, PasswordInput, TextArea, FCKEditor,
+    Radio, Select, DateTimePicker)
+
+class Field(object):
+    """field class is introduced to control what's displayed in forms. It makes
+    the link between something to edit and its display in the form. Actual
+    display is handled by a widget associated to the field.
+
+    Attributes
+    ----------
+    all the attributes described below have sensible default value which may be
+    overriden by value given to field's constructor.
+
+    :name:
+       name of the field (basestring), should be unique in a form.
+    :id:
+       dom identifier (default to the same value as `name`), should be unique in
+       a form.
+    :label:
+       label of the field (default to the same value as `name`).
+    :help:
+       help message about this field.
+    :widget:
+       widget associated to the field. Each field class has a default widget
+       class which may be overriden per instance.
+    :required:
+       bool flag telling if the field is required or not.
+    :initial:
+       initial value, used when no value specified by other means.
+    :choices:
+       static vocabulary for this field. May be a list of values or a list of
+       (label, value) tuples if specified.
+    :sort:
+       bool flag telling if the vocabulary (either static vocabulary specified
+       in `choices` or dynamic vocabulary fetched from the form) should be
+       sorted on label.
+    :internationalizable:
+       bool flag telling if the vocabulary labels should be translated using the
+       current request language.
+    :eidparam:
+       bool flag telling if this field is linked to a specific entity
+    :role:
+       when the field is linked to an entity attribute or relation, tells the
+       role of the entity in the relation (eg 'subject' or 'object')
+
+    """
+    # default widget associated to this class of fields. May be overriden per
+    # instance
+    widget = TextInput
+    # does this field requires a multipart form
+    needs_multipart = False
+    # class attribute used for ordering of fields in a form
+    __creation_rank = 0
+
+    def __init__(self, name=None, id=None, label=None, help=None,
+                 widget=None, required=False, initial=None,
+                 choices=None, sort=True, internationalizable=False,
+                 eidparam=False, role='subject'):
+        self.name = name
+        self.id = id or name
+        self.label = label or name
+        self.help = help
+        self.required = required
+        if widget is not None:
+            self.widget = widget
+        if isinstance(self.widget, type):
+            self.widget = self.widget()
+        self.initial = initial
+        self.choices = choices
+        self.sort = sort
+        self.internationalizable = internationalizable
+        self.eidparam = eidparam
+        self.role = role
+        # ordering number for this field instance
+        self.creation_rank = Field.__creation_rank
+        Field.__creation_rank += 1
+
+    def __unicode__(self):
+        return u'<%s name=%r label=%r id=%r initial=%r visible=%r @%x>' % (
+            self.__class__.__name__, self.name, self.label,
+            self.id, self.initial, self.is_visible(), id(self))
+
+    def __repr__(self):
+        return self.__unicode__().encode('utf-8')
+
+    def set_name(self, name):
+        """automatically set .id and .label when name is set"""
+        assert name
+        self.name = name
+        if not self.id:
+            self.id = name
+        if not self.label:
+            self.label = name
+
+    def is_visible(self):
+        """return true if the field is not an hidden field"""
+        return not isinstance(self.widget, HiddenInput)
+
+    def actual_fields(self, form):
+        """return actual fields composing this field in case of a compound
+        field, usually simply return self
+        """
+        yield self
+
+    def format_value(self, req, value):
+        """return value suitable for display where value may be a list or tuple
+        of values
+        """
+        if isinstance(value, (list, tuple)):
+            return [self.format_single_value(req, val) for val in value]
+        return self.format_single_value(req, value)
+
+    def format_single_value(self, req, value):
+        """return value suitable for display"""
+        if value is None or value is False:
+            return u''
+        if value is True:
+            return u'1'
+        return unicode(value)
+
+    def get_widget(self, form):
+        """return the widget instance associated to this field"""
+        return self.widget
+
+    def example_format(self, req):
+        """return a sample string describing what can be given as input for this
+        field
+        """
+        return u''
+
+    def render(self, form, renderer):
+        """render this field, which is part of form, using the given form
+        renderer
+        """
+        return self.get_widget(form).render(form, self)
+
+    def vocabulary(self, form):
+        """return vocabulary for this field. This method will be called by
+        widgets which desire it."""
+        if self.choices is not None:
+            if callable(self.choices):
+                vocab = self.choices(req=form.req)
+            else:
+                vocab = self.choices
+            if vocab and not isinstance(vocab[0], (list, tuple)):
+                vocab = [(x, x) for x in vocab]
+        else:
+            vocab = form.form_field_vocabulary(self)
+        if self.internationalizable:
+            vocab = [(form.req._(label), value) for label, value in vocab]
+        if self.sort:
+            vocab = sorted(vocab)
+        return vocab
+
+    def form_init(self, form):
+        """method called before by form_build_context to trigger potential field
+        initialization requiring the form instance
+        """
+        pass
+
+class StringField(Field):
+    def __init__(self, max_length=None, **kwargs):
+        super(StringField, self).__init__(**kwargs)
+        self.max_length = max_length
+        if isinstance(self.widget, TextArea):
+            self.init_text_area(self.widget)
+
+    def init_text_area(self, widget):
+        if self.max_length < 513:
+            widget.attrs.setdefault('cols', 60)
+            widget.attrs.setdefault('rows', 5)
+
+
+class RichTextField(StringField):
+    widget = None
+    def __init__(self, format_field=None, **kwargs):
+        super(RichTextField, self).__init__(**kwargs)
+        self.format_field = format_field
+
+    def get_widget(self, form):
+        if self.widget is None:
+            if self.use_fckeditor(form):
+                return FCKEditor()
+            widget = TextArea()
+            self.init_text_area(widget)
+            return widget
+        return self.widget
+
+    def get_format_field(self, form):
+        if self.format_field:
+            return self.format_field
+        # we have to cache generated field since it's use as key in the
+        # context dictionnary
+        req = form.req
+        try:
+            return req.data[self]
+        except KeyError:
+            if self.use_fckeditor(form):
+                # if fckeditor is used and format field isn't explicitly
+                # deactivated, we want an hidden field for the format
+                widget = HiddenInput()
+                choices = None
+            else:
+                # else we want a format selector
+                # XXX compute vocabulary
+                widget = Select()
+                fcstr = FormatConstraint()
+                choices = [(req._(fmt), fmt) for fmt in fcstr.vocabulary(req=req)]
+                widget.attrs['size'] = 1
+            field = StringField(name=self.name + '_format', widget=widget,
+                                choices=choices)
+            req.data[self] = field
+            return field
+
+    def actual_fields(self, form):
+        yield self
+        format_field = self.get_format_field(form)
+        if format_field:
+            yield format_field
+
+    def use_fckeditor(self, form):
+        """return True if fckeditor should be used to edit entity's attribute named
+        `attr`, according to user preferences
+        """
+        if form.req.use_fckeditor():
+            return form.form_field_format(self) == 'text/html'
+        return False
+
+    def render(self, form, renderer):
+        format_field = self.get_format_field(form)
+        if format_field:
+            result = format_field.render(form, renderer)
+        else:
+            result = u''
+        return result + self.get_widget(form).render(form, self)
+
+
+class FileField(StringField):
+    widget = FileInput
+    needs_multipart = True
+
+    def __init__(self, format_field=None, encoding_field=None, **kwargs):
+        super(FileField, self).__init__(**kwargs)
+        self.format_field = format_field
+        self.encoding_field = encoding_field
+
+    def actual_fields(self, form):
+        yield self
+        if self.format_field:
+            yield self.format_field
+        if self.encoding_field:
+            yield self.encoding_field
+
+    def render(self, form, renderer):
+        wdgs = [self.get_widget(form).render(form, self)]
+        if self.format_field or self.encoding_field:
+            divid = '%s-advanced' % form.context[self]['name']
+            wdgs.append(u'<a href="%s" title="%s"><img src="%s" alt="%s"/></a>' %
+                        (html_escape(uilib.toggle_action(divid)),
+                         form.req._('show advanced fields'),
+                         html_escape(form.req.build_url('data/puce_down.png')),
+                         form.req._('show advanced fields')))
+            wdgs.append(u'<div id="%s" class="hidden">' % divid)
+            if self.format_field:
+                wdgs.append(self.render_subfield(form, self.format_field, renderer))
+            if self.encoding_field:
+                wdgs.append(self.render_subfield(form, self.encoding_field, renderer))
+            wdgs.append(u'</div>')
+        if not self.required and form.context[self]['value']:
+            # trick to be able to delete an uploaded file
+            wdgs.append(u'<br/>')
+            wdgs.append(tags.input(name=u'%s__detach' % form.context[self]['name'],
+                                   type=u'checkbox'))
+            wdgs.append(form.req._('detach attached file'))
+        return u'\n'.join(wdgs)
+
+    def render_subfield(self, form, field, renderer):
+        return (renderer.render_label(form, field)
+                + field.render(form, renderer)
+                + renderer.render_help(form, field)
+                + u'<br/>')
+
+
+class EditableFileField(FileField):
+    editable_formats = ('text/plain', 'text/html', 'text/rest')
+
+    def render(self, form, renderer):
+        wdgs = [super(EditableFileField, self).render(form, renderer)]
+        if form.form_field_format(self) in self.editable_formats:
+            data = form.form_field_value(self, load_bytes=True)
+            if data:
+                encoding = form.form_field_encoding(self)
+                try:
+                    form.context[self]['value'] = unicode(data.getvalue(), encoding)
+                except UnicodeError:
+                    pass
+                else:
+                    if not self.required:
+                        msg = form.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 = form.req._(
+                            'You can either submit a new file using the browse button above'
+                            ', or edit file content online with the widget below.')
+                    wdgs.append(u'<p><b>%s</b></p>' % msg)
+                    wdgs.append(TextArea(setdomid=False).render(form, self))
+                    # XXX restore form context?
+        return '\n'.join(wdgs)
+
+
+class IntField(Field):
+    def __init__(self, min=None, max=None, **kwargs):
+        super(IntField, self).__init__(**kwargs)
+        self.min = min
+        self.max = max
+
+class BooleanField(Field):
+    widget = Radio
+
+    def vocabulary(self, form):
+        if self.choices:
+            return self.choices
+        return [(form.req._('yes'), '1'), (form.req._('no'), '')]
+
+
+class FloatField(IntField):
+    def format_single_value(self, req, value):
+        formatstr = req.property_value('ui.float-format')
+        if value is None:
+            return u''
+        return formatstr % float(value)
+
+    def render_example(self, req):
+        return self.format_value(req, 1.234)
+
+
+class DateField(StringField):
+    format_prop = 'ui.date-format'
+    widget = DateTimePicker
+
+    def format_single_value(self, req, value):
+        return value and ustrftime(value, req.property_value(self.format_prop)) or u''
+
+    def render_example(self, req):
+        return self.format_value(req, datetime.now())
+
+
+class DateTimeField(DateField):
+    format_prop = 'ui.datetime-format'
+
+
+class TimeField(DateField):
+    format_prop = 'ui.datetime-format'
+
+
+class HiddenInitialValueField(Field):
+    def __init__(self, visible_field):
+        name = 'edit%s-%s' % (visible_field.role[0], visible_field.name)
+        super(HiddenInitialValueField, self).__init__(
+            name=name, widget=HiddenInput, eidparam=True)
+        self.visible_field = visible_field
+
+
+class RelationField(Field):
+    def __init__(self, **kwargs):
+        super(RelationField, self).__init__(**kwargs)
+
+    @staticmethod
+    def fromcardinality(card, **kwargs):
+        return RelationField(widget=Select(multiple=card in '*+'), **kwargs)
+
+    def vocabulary(self, form):
+        entity = form.edited_entity
+        req = entity.req
+        # first see if its specified by __linkto form parameters
+        linkedto = entity.linked_to(self.name, self.role)
+        if linkedto:
+            entities = (req.eid_rset(eid).get_entity(0, 0) 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
+        res = []
+        if not self.required:
+            res.append(('', INTERNAL_FIELD_VALUE))
+        # 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 = []
+        vocab = res + form.form_field_vocabulary(self) + relatedvocab
+        if self.sort:
+            vocab = sorted(vocab)
+        return vocab
+
+    def format_single_value(self, req, value):
+        return value
+
+
+def guess_field(eschema, rschema, role='subject', skip_meta_attr=True, **kwargs):
+    """return the most adapated widget to edit the relation
+    'subjschema rschema objschema' according to information found in the schema
+    """
+    fieldclass = None
+    if role == 'subject':
+        targetschema = rschema.objects(eschema)[0]
+        card = rschema.rproperty(eschema, targetschema, 'cardinality')[0]
+        help = rschema.rproperty(eschema, targetschema, 'description')
+        if rschema.is_final():
+            if rschema.rproperty(eschema, targetschema, 'internationalizable'):
+                kwargs['internationalizable'] = True
+            kwargs['initial'] = rschema.rproperty(eschema, targetschema, 'default')
+    else:
+        targetschema = rschema.subjects(eschema)[0]
+        card = rschema.rproperty(targetschema, eschema, 'cardinality')[1]
+        help = rschema.rproperty(targetschema, eschema, 'description')
+    kwargs['required'] = card in '1+'
+    kwargs['name'] = rschema.type
+    kwargs['help'] = help
+    if rschema.is_final():
+        if skip_meta_attr and rschema in eschema.meta_attributes():
+            return None
+        fieldclass = FIELDS[targetschema]
+        if fieldclass is StringField:
+            if targetschema == 'Password':
+                # special case for Password field: specific PasswordInput widget
+                kwargs.setdefault('widget', PasswordInput())
+                return StringField(**kwargs)
+            if eschema.has_metadata(rschema, 'format'):
+                # use RichTextField instead of StringField if the attribute has
+                # a "format" metadata. But getting information from constraints
+                # may be useful anyway...
+                constraints = rschema.rproperty(eschema, targetschema, 'constraints')
+                for cstr in constraints:
+                    if isinstance(cstr, StaticVocabularyConstraint):
+                        raise Exception('rich text field with static vocabulary')
+                return RichTextField(**kwargs)
+            constraints = rschema.rproperty(eschema, targetschema, 'constraints')
+            # init StringField parameters according to constraints
+            for cstr in constraints:
+                if isinstance(cstr, StaticVocabularyConstraint):
+                    kwargs.setdefault('widget', Select())
+                    kwargs.setdefault('choices', cstr.vocabulary)
+                    if card in '?1':
+                        kwargs['widget'].attrs.setdefault('size', 1)
+                if isinstance(cstr, SizeConstraint) and cstr.max is not None:
+                    if cstr.max > 257:
+                        kwargs.setdefault('widget', TextArea)
+                    kwargs['max_length'] = cstr.max
+            return StringField(**kwargs)
+        if fieldclass is FileField:
+            for metadata in ('format', 'encoding'):
+                metaschema = eschema.has_metadata(rschema, metadata)
+                if metaschema is not None:
+                    kwargs['%s_field' % metadata] = guess_field(eschema, metaschema,
+                                                                skip_meta_attr=False)
+        return fieldclass(**kwargs)
+    kwargs['role'] = role
+    return RelationField.fromcardinality(card, **kwargs)
+
+FIELDS = {
+    'Boolean':  BooleanField,
+    'Bytes':    FileField,
+    'Date':     DateField,
+    'Datetime': DateTimeField,
+    'Int':      IntField,
+    'Float':    FloatField,
+    'Decimal':  StringField,
+    'Password': StringField,
+    'String' :  StringField,
+    'Time':     TimeField,
+    }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/formrenderers.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,473 @@
+"""form renderers, responsible to layout a form to html
+
+:organization: Logilab
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from logilab.common import dictattr
+from logilab.mtconverter import html_escape
+
+from simplejson import dumps
+
+from cubicweb.common import tags
+from cubicweb.web import eid_param
+from cubicweb.web import formwidgets as fwdgs
+from cubicweb.web.widgets import checkbox
+from cubicweb.web.formfields import HiddenInitialValueField
+
+
+class FormRenderer(object):
+    """basic renderer displaying fields in a two columns table label | value
+
+    +--------------+--------------+
+    | field1 label | field1 input |
+    +--------------+--------------+
+    | field1 label | field2 input |
+    +--------------+--------------+
+    +---------+
+    | buttons |
+    +---------+
+    """
+    _options = ('display_fields', 'display_label', 'display_help',
+                'display_progress_div', 'button_bar_class')
+    display_fields = None # None -> all fields
+    display_label = True
+    display_help = True
+    display_progress_div = True
+    button_bar_class = u'formButtonBar'
+
+    def __init__(self, **kwargs):
+        if self._set_options(kwargs):
+            raise ValueError('unconsumed arguments %s' % kwargs)
+
+    def _set_options(self, kwargs):
+        for key in self._options:
+            try:
+                setattr(self, key, kwargs.pop(key))
+            except KeyError:
+                continue
+        return kwargs
+
+    # renderer interface ######################################################
+
+    def render(self, form, values):
+        self._set_options(values)
+        form.add_media()
+        data = []
+        w = data.append
+        w(self.open_form(form, values))
+        if self.display_progress_div:
+            w(u'<div id="progress">%s</div>' % form.req._('validating...'))
+        w(u'<fieldset>')
+        w(tags.input(type=u'hidden', name=u'__form_id',
+                     value=values.get('formvid', form.id)))
+        if form.redirect_path:
+            w(tags.input(type='hidden', name='__redirectpath', value=form.redirect_path))
+        self.render_fields(w, form, values)
+        self.render_buttons(w, form)
+        w(u'</fieldset>')
+        w(u'</form>')
+        errormsg = self.error_message(form)
+        if errormsg:
+            data.insert(0, errormsg)
+        return '\n'.join(data)
+
+    def render_label(self, form, field):
+        label = form.req._(field.label)
+        attrs = {'for': form.context[field]['id']}
+        if field.required:
+            attrs['class'] = 'required'
+        return tags.label(label, **attrs)
+
+    def render_help(self, form, field):
+        help = [ u'<br/>' ]
+        descr = field.help
+        if descr:
+            help.append('<span class="helper">%s</span>' % form.req._(descr))
+        example = field.example_format(form.req)
+        if example:
+            help.append('<span class="helper">(%s: %s)</span>'
+                        % (form.req._('sample format'), example))
+        return u'&nbsp;'.join(help)
+
+    # specific methods (mostly to ease overriding) #############################
+
+    def error_message(self, form):
+        """return formatted error message
+
+        This method should be called once inlined field errors has been consumed
+        """
+        req = form.req
+        errex = req.data.get('formerrors')
+        # get extra errors
+        if errex is not None:
+            errormsg = req._('please correct the following errors:')
+            displayed = req.data['displayederrors']
+            errors = sorted((field, err) for field, err in errex.errors.items()
+                            if not field in displayed)
+            if errors:
+                if len(errors) > 1:
+                    templstr = '<li>%s</li>\n'
+                else:
+                    templstr = '&nbsp;%s\n'
+                for field, err in errors:
+                    if field is None:
+                        errormsg += templstr % err
+                    else:
+                        errormsg += templstr % '%s: %s' % (req._(field), err)
+                if len(errors) > 1:
+                    errormsg = '<ul>%s</ul>' % errormsg
+            return u'<div class="errorMessage">%s</div>' % errormsg
+        return u''
+
+    def open_form(self, form, values):
+        if form.form_needs_multipart:
+            enctype = 'multipart/form-data'
+        else:
+            enctype = 'application/x-www-form-urlencoded'
+        if form.action is None:
+            action = form.req.build_url('edit')
+        else:
+            action = form.action
+        tag = ('<form action="%s" method="post" enctype="%s"' % (
+            html_escape(action or '#'), enctype))
+        if form.domid:
+            tag += ' id="%s"' % form.domid
+        if form.onsubmit:
+            tag += ' onsubmit="%s"' % html_escape(form.onsubmit % dictattr(form))
+        if form.cssstyle:
+            tag += ' style="%s"' % html_escape(form.cssstyle)
+        if form.cssclass:
+            tag += ' class="%s"' % html_escape(form.cssclass)
+        if form.cwtarget:
+            tag += ' cubicweb:target="%s"' % html_escape(form.cwtarget)
+        return tag + '>'
+
+    def display_field(self, form, field):
+        if isinstance(field, HiddenInitialValueField):
+            field = field.visible_field
+        return (self.display_fields is None
+                or field.name in form.internal_fields
+                or (field.name, field.role) in self.display_fields
+                or (field.name, field.role) in form.internal_fields)
+
+    def render_fields(self, w, form, values):
+        form.form_build_context(values)
+        fields = self._render_hidden_fields(w, form)
+        if fields:
+            self._render_fields(fields, w, form)
+        self.render_child_forms(w, form, values)
+
+    def render_child_forms(self, w, form, values):
+        # render
+        for childform in getattr(form, 'forms', []):
+            self.render_fields(w, childform, values)
+
+    def _render_hidden_fields(self, w, form):
+        fields = form.fields[:]
+        for field in form.fields:
+            if not self.display_field(form, field):
+                fields.remove(field)
+            elif not field.is_visible():
+                w(field.render(form, self))
+                fields.remove(field)
+        return fields
+
+    def _render_fields(self, fields, w, form):
+        w(u'<table class="attributeForm">')
+        for field in fields:
+            w(u'<tr>')
+            if self.display_label:
+                w(u'<th class="labelCol">%s</th>' % self.render_label(form, field))
+            error = form.form_field_error(field)
+            if error:
+                w(u'<td class="error">')
+                w(error)
+            else:
+                w(u'<td>')
+            w(field.render(form, self))
+            if self.display_help:
+                w(self.render_help(form, field))
+            w(u'</td></tr>')
+        w(u'</table>')
+
+    def render_buttons(self, w, form):
+        w(u'<table class="%s">\n<tr>\n' % self.button_bar_class)
+        for button in form.form_buttons:
+            w(u'<td>%s</td>\n' % button.render(form))
+        w(u'</tr></table>')
+
+
+class HTableFormRenderer(FormRenderer):
+    """display fields horizontally in a table
+
+    +--------------+--------------+---------+
+    | field1 label | field2 label |         |
+    +--------------+--------------+---------+
+    | field1 input | field2 input | buttons
+    +--------------+--------------+---------+
+    """
+    display_help = False
+    def _render_fields(self, fields, w, form):
+        w(u'<table border="0">')
+        w(u'<tr>')
+        for field in fields:
+            if self.display_label:
+                w(u'<th class="labelCol">%s</th>' % self.render_label(form, field))
+            if self.display_help:
+                w(self.render_help(form, field))
+        # empty slot for buttons
+        w(u'<th class="labelCol">&nbsp;</th>')
+        w(u'</tr>')
+        w(u'<tr>')
+        for field in fields:
+            error = form.form_field_error(field)
+            if error:
+                w(u'<td class="error" style="width:100%;">')
+                w(error)
+            else:
+                w(u'<td style="width:100%;">')
+            w(field.render(form, self))
+            w(u'</td>')
+        w(u'<td>')
+        for button in form.form_buttons:
+            w(button.render(form))
+        w(u'</td>')
+        w(u'</tr>')
+        w(u'</table>')
+
+    def render_buttons(self, w, form):
+        pass
+
+
+class EntityCompositeFormRenderer(FormRenderer):
+    """specific renderer for multiple entities edition form (muledit)"""
+
+    def render_fields(self, w, form, values):
+        if not form.is_subform:
+            w(u'<table class="listing">')
+        super(EntityCompositeFormRenderer, self).render_fields(w, form, values)
+        if not form.is_subform:
+            w(u'</table>')
+
+    def _render_fields(self, fields, w, form):
+        if form.is_subform:
+            entity = form.edited_entity
+            values = form._previous_values
+            qeid = eid_param('eid', entity.eid)
+            cbsetstate = "setCheckboxesState2('eid', %s, 'checked')" % html_escape(dumps(entity.eid))
+            w(u'<tr class="%s">' % (entity.row % 2 and u'even' or u'odd'))
+            # XXX turn this into a widget used on the eid field
+            w(u'<td>%s</td>' % checkbox('eid', entity.eid, checked=qeid in values))
+            for field in fields:
+                error = form.form_field_error(field)
+                if error:
+                    w(u'<td class="error">')
+                    w(error)
+                else:
+                    w(u'<td>')
+                if isinstance(field.widget, (fwdgs.Select, fwdgs.CheckBox, fwdgs.Radio)):
+                    field.widget.attrs['onchange'] = cbsetstate
+                elif isinstance(field.widget, fwdgs.Input):
+                    field.widget.attrs['onkeypress'] = cbsetstate
+                w(u'<div>%s</div>' % field.render(form, self))
+                w(u'/<td>')
+        else:
+            # main form, display table headers
+            w(u'<tr class="header">')
+            w(u'<th align="left">%s</th>'
+              % tags.input(type='checkbox', title=form.req._('toggle check boxes'),
+                           onclick="setCheckboxesState('eid', this.checked)"))
+            for field in self.forms[0].fields:
+                if self.display_field(form, field) and field.is_visible():
+                    w(u'<th>%s</th>' % form.req._(field.label))
+        w(u'</tr>')
+
+
+
+class EntityFormRenderer(FormRenderer):
+    """specific renderer for entity edition form (edition)"""
+    _options = FormRenderer._options + ('display_relations_form',)
+    display_relations_form = True
+
+    def render(self, form, values):
+        rendered = super(EntityFormRenderer, self).render(form, values)
+        return rendered + u'</div>' # close extra div introducted by open_form
+
+    def open_form(self, form, values):
+        attrs_fs_label = ('<div class="iformTitle"><span>%s</span></div>'
+                          % form.req._('main informations'))
+        attrs_fs_label += '<div class="formBody">'
+        return attrs_fs_label + super(EntityFormRenderer, self).open_form(form, values)
+
+    def render_fields(self, w, form, values):
+        super(EntityFormRenderer, self).render_fields(w, form, values)
+        self.inline_entities_form(w, form)
+        if form.edited_entity.has_eid() and self.display_relations_form:
+            self.relations_form(w, form)
+
+    def _render_fields(self, fields, w, form):
+        if not form.edited_entity.has_eid() or form.edited_entity.has_perm('update'):
+            super(EntityFormRenderer, self)._render_fields(fields, w, form)
+
+    def render_buttons(self, w, form):
+        if len(form.form_buttons) == 3:
+            w("""<table width="100%%">
+  <tbody>
+   <tr><td align="center">
+     %s
+   </td><td style="align: right; width: 50%%;">
+     %s
+     %s
+   </td></tr>
+  </tbody>
+ </table>""" % tuple(button.render(form) for button in form.form_buttons))
+        else:
+            super(EntityFormRenderer, self).render_buttons(w, form)
+
+    def relations_form(self, w, form):
+        srels_by_cat = form.srelations_by_category(('generic', 'metadata'), 'add')
+        if not srels_by_cat:
+            return u''
+        req = form.req
+        _ = req._
+        label = u'%s :' % _('This %s' % form.edited_entity.e_schema).capitalize()
+        eid = form.edited_entity.eid
+        w(u'<fieldset class="subentity">')
+        w(u'<legend class="iformTitle">%s</legend>' % label)
+        w(u'<table id="relatedEntities">')
+        for rschema, target, related in form.relations_table():
+            # already linked entities
+            if related:
+                w(u'<tr><th class="labelCol">%s</th>' % rschema.display_name(req, target))
+                w(u'<td>')
+                w(u'<ul>')
+                for viewparams in related:
+                    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 form.force_display and form.maxrelitems < len(related):
+                    link = (u'<span class="invisible">'
+                            '[<a href="javascript: window.location.href+=\'&amp;__force_display=1\'">%s</a>]'
+                            '</span>' % form.req._('view all'))
+                    w(u'<li class="invisible">%s</li>' % link)
+                w(u'</ul>')
+                w(u'</td>')
+                w(u'</tr>')
+        pendings = list(form.restore_pending_inserts())
+        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], html_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(), html_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>')
+
+    def inline_entities_form(self, w, form):
+        """create a form to edit entity's inlined relations"""
+        entity = form.edited_entity
+        __ = form.req.__
+        for rschema, targettypes, role in form.inlined_relations():
+            # 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 form.should_inline_relation_form(rschema, targettype, role):
+                w(u'<div id="inline%sslot">' % rschema)
+                existant = entity.has_eid() and entity.related(rschema)
+                if existant:
+                    # display inline-edition view for all existing related entities
+                    w(form.view('inline-edition', existant, rtype=rschema, role=role,
+                                ptype=entity.e_schema, peid=entity.eid))
+                if role == 'subject':
+                    card = rschema.rproperty(entity.e_schema, targettype, 'cardinality')[0]
+                else:
+                    card = rschema.rproperty(targettype, entity.e_schema, 'cardinality')[1]
+                # there is no related entity and we need at least one: we need to
+                # display one explicit inline-creation view
+                if form.should_display_inline_creation_form(rschema, existant, card):
+                    w(form.view('inline-creation', None, etype=targettype,
+                                peid=entity.eid, ptype=entity.e_schema,
+                                rtype=rschema, role=role))
+                # we can create more than one related entity, we thus display a link
+                # to add new related entities
+                if form.should_display_add_new_relation_link(rschema, existant, card):
+                    divid = "addNew%s%s%s:%s" % (targettype, rschema, role, entity.eid)
+                    w(u'<div class="inlinedform" id="%s" cubicweb:limit="true">'
+                      % divid)
+                    js = "addInlineCreationForm('%s', '%s', '%s', '%s')" % (
+                        entity.eid, targettype, rschema, role)
+                    if card in '1?':
+                        js = "toggleVisibility('%s'); %s" % (divid, js)
+                    w(u'<a class="addEntity" id="add%s:%slink" href="javascript: %s" >+ %s.</a>'
+                      % (rschema, entity.eid, js, __('add a %s' % targettype)))
+                    w(u'</div>')
+                    w(u'<div class="trame_grise">&nbsp;</div>')
+                w(u'</div>')
+
+
+class EntityInlinedFormRenderer(EntityFormRenderer):
+    """specific renderer for entity inlined edition form
+    (inline-[creation|edition])
+    """
+    def render(self, form, values):
+        form.add_media()
+        data = []
+        w = data.append
+        try:
+            w(u'<div id="div-%(divid)s" onclick="%(divonclick)s">' % values)
+        except KeyError:
+            w(u'<div id="div-%(divid)s">' % values)
+        else:
+            w(u'<div id="notice-%s" class="notice">%s</div>' % (
+                values['divid'], form.req._('click on the box to cancel the deletion')))
+        w(u'<div class="iformBody">')
+        values['removemsg'] = form.req.__('remove this %s' % form.edited_entity.e_schema)
+        w(u'<div class="iformTitle"><span>%(title)s</span> '
+          '#<span class="icounter">1</span> '
+          '[<a href="javascript: %(removejs)s;noop();">%(removemsg)s</a>]</div>'
+          % values)
+        self.render_fields(w, form, values)
+        w(u'</div></div>')
+        return '\n'.join(data)
+
+    def render_fields(self, w, form, values):
+        form.form_build_context(values)
+        w(u'<fieldset id="fs-%(divid)s">' % values)
+        fields = self._render_hidden_fields(w, form)
+        w(u'</fieldset>')
+        w(u'<fieldset class="subentity">')
+        if fields:
+            self._render_fields(fields, w, form)
+        self.render_child_forms(w, form, values)
+        self.inline_entities_form(w, form)
+        w(u'</fieldset>')
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/formwidgets.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,423 @@
+"""widget classes for form construction
+
+:organization: Logilab
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from datetime import date
+
+from cubicweb.common import tags
+from cubicweb.web import stdmsgs
+
+
+class FieldWidget(object):
+    """abstract widget class"""
+    # javascript / css files required by the widget
+    needs_js = ()
+    needs_css = ()
+    # automatically set id and tabindex attributes ?
+    setdomid = True
+    settabindex = True
+
+    def __init__(self, attrs=None, setdomid=None, settabindex=None):
+        if attrs is None:
+            attrs = {}
+        self.attrs = attrs
+        if setdomid is not None:
+            # override class's default value
+            self.setdomid = setdomid
+        if settabindex is not None:
+            # override class's default value
+            self.settabindex = settabindex
+
+    def add_media(self, form):
+        """adds media (CSS & JS) required by this widget"""
+        if self.needs_js:
+            form.req.add_js(self.needs_js)
+        if self.needs_css:
+            form.req.add_css(self.needs_css)
+
+    def render(self, form, field):
+        """render the widget for the given `field` of `form`.
+        To override in concrete class
+        """
+        raise NotImplementedError
+
+    def _render_attrs(self, form, field):
+        """return html tag name, attributes and a list of values for the field
+        """
+        name = form.context[field]['name']
+        values = form.context[field]['value']
+        if not isinstance(values, (tuple, list)):
+            values = (values,)
+        attrs = dict(self.attrs)
+        if self.setdomid:
+            attrs['id'] = form.context[field]['id']
+        if self.settabindex and not 'tabindex' in attrs:
+            attrs['tabindex'] = form.req.next_tabindex()
+        return name, values, attrs
+
+
+class Input(FieldWidget):
+    """abstract widget class for <input> tag based widgets"""
+    type = None
+
+    def render(self, form, field):
+        """render the widget for the given `field` of `form`.
+
+        Generate one <input> tag for each field's value
+        """
+        self.add_media(form)
+        name, values, attrs = self._render_attrs(form, field)
+        inputs = [tags.input(name=name, value=value, type=self.type, **attrs)
+                  for value in values]
+        return u'\n'.join(inputs)
+
+
+# basic html widgets ###########################################################
+
+class TextInput(Input):
+    """<input type='text'>"""
+    type = 'text'
+
+
+class PasswordInput(Input):
+    """<input type='password'> and its confirmation field (using
+    <field's name>-confirm as name)
+    """
+    type = 'password'
+
+    def render(self, form, field):
+        self.add_media(form)
+        name, values, attrs = self._render_attrs(form, field)
+        assert len(values) == 1
+        id = attrs.pop('id')
+        try:
+            confirmname = '%s-confirm:%s' % tuple(name.rsplit(':', 1))
+        except TypeError:
+            confirmname = '%s-confirm' % name
+        inputs = [tags.input(name=name, value=values[0], type=self.type, id=id, **attrs),
+                  '<br/>',
+                  tags.input(name=confirmname, value=values[0], type=self.type, **attrs),
+                  '&nbsp;', tags.span(form.req._('confirm password'),
+                                      **{'class': 'emphasis'})]
+        return u'\n'.join(inputs)
+
+
+class FileInput(Input):
+    """<input type='file'>"""
+    type = 'file'
+
+    def _render_attrs(self, form, field):
+        # ignore value which makes no sense here (XXX even on form validation error?)
+        name, values, attrs = super(FileInput, self)._render_attrs(form, field)
+        return name, ('',), attrs
+
+
+class HiddenInput(Input):
+    """<input type='hidden'>"""
+    type = 'hidden'
+    setdomid = False # by default, don't set id attribute on hidden input
+    settabindex = False
+
+
+class ButtonInput(Input):
+    """<input type='button'>
+
+    if you want a global form button, look at the Button, SubmitButton,
+    ResetButton and ImgButton classes below.
+    """
+    type = 'button'
+
+
+class TextArea(FieldWidget):
+    """<textarea>"""
+    def render(self, form, field):
+        name, values, attrs = self._render_attrs(form, field)
+        attrs.setdefault('onkeypress', 'autogrow(this)')
+        attrs.setdefault('cols', 80)
+        attrs.setdefault('rows', 20)
+        if not values:
+            value = u''
+        elif len(values) == 1:
+            value = values[0]
+        else:
+            raise ValueError('a textarea is not supposed to be multivalued')
+        return tags.textarea(value, name=name, **attrs)
+
+
+class FCKEditor(TextArea):
+    """FCKEditor enabled <textarea>"""
+    def __init__(self, *args, **kwargs):
+        super(FCKEditor, self).__init__(*args, **kwargs)
+        self.attrs['cubicweb:type'] = 'wysiwyg'
+
+    def render(self, form, field):
+        form.req.fckeditor_config()
+        return super(FCKEditor, self).render(form, field)
+
+
+class Select(FieldWidget):
+    """<select>, for field having a specific vocabulary"""
+    def __init__(self, attrs=None, multiple=False):
+        super(Select, self).__init__(attrs)
+        self._multiple = multiple
+
+    def render(self, form, field):
+        name, curvalues, attrs = self._render_attrs(form, field)
+        if not 'size' in attrs and self._multiple:
+            attrs['size'] = '5'
+        options = []
+        for label, value in field.vocabulary(form):
+            if value is None:
+                # handle separator
+                options.append(u'<optgroup label="%s"/>' % (label or ''))
+            elif value in curvalues:
+                options.append(tags.option(label, value=value, selected='selected'))
+            else:
+                options.append(tags.option(label, value=value))
+        return tags.select(name=name, multiple=self._multiple,
+                           options=options, **attrs)
+
+
+class CheckBox(Input):
+    """<input type='checkbox'>, for field having a specific vocabulary. One
+    input will be generated for each possible value.
+    """
+    type = 'checkbox'
+
+    def render(self, form, field):
+        name, curvalues, attrs = self._render_attrs(form, field)
+        options = []
+        for label, value in field.vocabulary(form):
+            if value in curvalues:
+                tag = tags.input(name=name, value=value, type=self.type,
+                                 checked='checked', **attrs)
+            else:
+                tag = tags.input(name=name, value=value, type=self.type,
+                                 **attrs)
+            options.append(tag + label)
+        return '<br/>\n'.join(options)
+
+
+class Radio(Input):
+    """<input type='radio'>, for field having a specific vocabulary. One
+    input will be generated for each possible value.
+    """
+    type = 'radio'
+
+    def render(self, form, field):
+        name, curvalues, attrs = self._render_attrs(form, field)
+        domid = attrs.pop('id', None)
+        options = []
+        for i, (label, value) in enumerate(field.vocabulary(form)):
+            iattrs = attrs.copy()
+            if i == 0 and domid is not None:
+                iattrs['id'] = domid
+            if value in curvalues:
+                iattrs['checked'] = u'checked'
+            tag = tags.input(name=name, type=self.type, value=value, **iattrs)
+            options.append(tag + label + '<br/>')
+        return '\n'.join(options)
+
+
+# javascript widgets ###########################################################
+
+class DateTimePicker(TextInput):
+    """<input type='text' + javascript date/time picker for date or datetime
+    fields
+    """
+    monthnames = ('january', 'february', 'march', 'april',
+                  'may', 'june', 'july', 'august',
+                  'september', 'october', 'november', 'december')
+    daynames = ('monday', 'tuesday', 'wednesday', 'thursday',
+                'friday', 'saturday', 'sunday')
+
+    needs_js = ('cubicweb.calendar.js',)
+    needs_css = ('cubicweb.calendar_popup.css',)
+
+    @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 render(self, form, field):
+        txtwidget = super(DateTimePicker, self).render(form, field)
+        self.add_localized_infos(form.req)
+        cal_button = self._render_calendar_popup(form, field)
+        return txtwidget + cal_button
+
+    def _render_calendar_popup(self, form, field):
+        value = form.form_field_value(field)
+        if not value:
+            value = date.today()
+        inputid = form.context[field]['id']
+        helperid = '%shelper' % inputid
+        year, month = value.year, value.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,
+                   form.req.external_resource('CALENDAR_ICON'),
+                   form.req._('calendar'), helperid) )
+
+
+
+# ajax widgets ################################################################
+
+def init_ajax_attributes(attrs, wdgtype, loadtype=u'auto'):
+    try:
+        attrs['klass'] += u' widget'
+    except KeyError:
+        attrs['klass'] = u'widget'
+    attrs.setdefault('cubicweb:wdgtype', wdgtype)
+    attrs.setdefault('cubicweb:loadtype', loadtype)
+
+
+class AjaxWidget(FieldWidget):
+    """simple <div> based ajax widget"""
+    def __init__(self, wdgtype, inputid=None, **kwargs):
+        super(AjaxWidget, self).__init__(**kwargs)
+        init_ajax_attributes(self.attrs, wdgtype)
+        if inputid is not None:
+            self.attrs['cubicweb:inputid'] = inputid
+
+    def render(self, form, field):
+        self.add_media(form)
+        attrs = self._render_attrs(form, field)[-1]
+        return tags.div(**attrs)
+
+
+class AutoCompletionWidget(TextInput):
+    """ajax widget for StringField, proposing matching existing values as you
+    type.
+    """
+    needs_js = ('cubicweb.widgets.js', 'jquery.autocomplete.js')
+    needs_css = ('jquery.autocomplete.css',)
+    wdgtype = 'SuggestField'
+    loadtype = 'auto'
+
+    def _render_attrs(self, form, field):
+        name, values, attrs = super(AutoCompletionWidget, self)._render_attrs(form, field)
+        if not values[0]:
+            values = (INTERNAL_FIELD_VALUE,)
+        init_ajax_attributes(attrs, self.wdgtype, self.loadtype)
+        # XXX entity form specific
+        attrs['cubicweb:dataurl'] = self._get_url(form.edited_entity)
+        return name, values, attrs
+
+    def _get_url(self, entity):
+        return entity.req.build_url('json', fname=entity.autocomplete_initfuncs[self.rschema],
+                                pageid=entity.req.pageid, mode='remote')
+
+
+class StaticFileAutoCompletionWidget(AutoCompletionWidget):
+    """XXX describe me"""
+    wdgtype = 'StaticFileSuggestField'
+
+    def _get_url(self, entity):
+        return entity.req.datadir_url + entity.autocomplete_initfuncs[self.rschema]
+
+
+class RestrictedAutoCompletionWidget(AutoCompletionWidget):
+    """XXX describe me"""
+    wdgtype = 'RestrictedSuggestField'
+
+
+class AddComboBoxWidget(Select):
+    def _render_attrs(self, form, field):
+        name, values, attrs = super(AddComboBoxWidget, self)._render_attrs(form, field)
+        init_ajax_attributes(self.attrs, 'AddComboBox')
+        # XXX entity form specific
+        entity = form.edited_entity
+        attrs['cubicweb:etype_to'] = entity.e_schema
+        etype_from = entity.e_schema.subject_relation(self.name).objects(entity.e_schema)[0]
+        attrs['cubicweb:etype_from'] = etype_from
+
+    def render(self, form, field):
+        return super(AddComboBoxWidget, self).render(form, field) + u'''
+<div id="newvalue">
+  <input type="text" id="newopt" />
+  <a href="javascript:noop()" id="add_newopt">&nbsp;</a></div>
+'''
+
+# buttons ######################################################################
+
+class Button(Input):
+    """<input type='button'>, base class for global form buttons
+
+    note label is a msgid which will be translated at form generation time, you
+    should not give an already translated string.
+    """
+    type = 'button'
+    def __init__(self, label=stdmsgs.BUTTON_OK, attrs=None,
+                 setdomid=None, settabindex=None,
+                 name='', value='', onclick=None, cwaction=None):
+        super(Button, self).__init__(attrs, setdomid, settabindex)
+        self.label = label
+        self.name = name
+        self.value = ''
+        self.onclick = onclick
+        self.cwaction = cwaction
+        self.attrs.setdefault('klass', 'validateButton')
+
+    def render(self, form, field=None):
+        label = form.req._(self.label)
+        attrs = self.attrs.copy()
+        if self.cwaction:
+            assert self.onclick is None
+            attrs['onclick'] = "postForm('__action_%s', \'%s\', \'%s\')" % (
+                self.cwaction, self.label, form.domid)
+        elif self.onclick:
+            attrs['onclick'] = self.onclick
+        if self.name:
+            attrs['name'] = name
+            if self.setdomid:
+                attrs['id'] = self.name
+        if self.settabindex and not 'tabindex' in attrs:
+            attrs['tabindex'] = form.req.next_tabindex()
+        return tags.input(value=label, type=self.type, **attrs)
+
+
+class SubmitButton(Button):
+    """<input type='submit'>, main button to submit a form"""
+    type = 'submit'
+
+
+class ResetButton(Button):
+    """<input type='reset'>, main button to reset a form.
+    You usually don't want this.
+    """
+    type = 'reset'
+
+
+class ImgButton(object):
+    """<img> wrapped into a <a> tag with href triggering something (usually a
+    javascript call)
+
+    note label is a msgid which will be translated at form generation time, you
+    should not give an already translated string.
+    """
+    def __init__(self, domid, href, label, imgressource):
+        self.domid = domid
+        self.href = href
+        self.imgressource = imgressource
+        self.label = label
+
+    def render(self, form, field=None):
+        label = form.req._(self.label)
+        imgsrc = form.req.external_resource(self.imgressource)
+        return '<a id="%(domid)s" href="%(href)s">'\
+               '<img src="%(imgsrc)s" alt="%(label)s"/>%(label)s</a>' % {
+            'label': label, 'imgsrc': imgsrc,
+            'domid': self.domid, 'href': self.href}
+
+
+
+# XXX EntityLinkComboBoxWidget, [Raw]DynamicComboBoxWidget
--- a/web/htmlwidgets.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/htmlwidgets.py	Mon May 04 13:09:48 2009 +0200
@@ -10,7 +10,7 @@
 
 from logilab.mtconverter import html_escape
 
-from cubicweb.common.utils import UStringIO
+from cubicweb.utils import UStringIO
 from cubicweb.common.uilib import toggle_action
 
 # XXX HTMLWidgets should have access to req (for datadir / static urls,
--- a/web/httpcache.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/httpcache.py	Mon May 04 13:09:48 2009 +0200
@@ -2,15 +2,15 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from mx.DateTime import DateTimeFromTicks, now, gmtime
+from datetime import datetime
 
 # time delta usable to convert localized time to GMT time
-GMTOFFSET = - (now() - gmtime())
+GMTOFFSET = - (datetime.now() - datetime.utcnow())
 
 class NoHTTPCacheManager(object):
     """default cache manager: set no-cache cache control policy"""
@@ -92,11 +92,11 @@
 # monkey patching, so view doesn't depends on this module and we have all
 # http cache related logic here
 
-from cubicweb.common import view
+from cubicweb import view as viewmod
 
 def set_http_cache_headers(self):
     self.http_cache_manager(self).set_headers()
-view.View.set_http_cache_headers = set_http_cache_headers
+viewmod.View.set_http_cache_headers = set_http_cache_headers
 
 def last_modified(self):
     """return the date/time where this view should be considered as
@@ -105,11 +105,12 @@
     /!\ must return GMT time /!\
     """
     # XXX check view module's file modification time in dev mod ?
-    ctime = gmtime()
+    ctime = datetime.utcnow()
     if self.cache_max_age:
         mtime = self.req.header_if_modified_since()
         if mtime:
-            if (ctime - mtime).seconds > self.cache_max_age:
+            tdelta = (ctime - mtime)
+            if tdelta.days * 24*60*60 + tdelta.seconds > self.cache_max_age:
                 mtime = ctime
         else:
             mtime = ctime
@@ -117,15 +118,15 @@
         mtime = ctime
     # mtime = ctime will force page rerendering
     return mtime
-view.View.last_modified = last_modified
+viewmod.View.last_modified = last_modified
 
 # configure default caching
-view.View.http_cache_manager = NoHTTPCacheManager
+viewmod.View.http_cache_manager = NoHTTPCacheManager
 # max-age=0 to actually force revalidation when needed
-view.View.cache_max_age = 0
+viewmod.View.cache_max_age = 0
 
 
-view.EntityView.http_cache_manager = EntityHTTPCacheManager
+viewmod.EntityView.http_cache_manager = EntityHTTPCacheManager
 
-view.StartupView.http_cache_manager = MaxAgeHTTPCacheManager
-view.StartupView.cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
+viewmod.StartupView.http_cache_manager = MaxAgeHTTPCacheManager
+viewmod.StartupView.cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
--- a/web/request.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/request.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """abstract class for http request
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -18,14 +18,14 @@
 
 from logilab.common.decorators import cached
 
-# XXX move _MARKER here once AppObject.external_resource has been removed
 from cubicweb.dbapi import DBAPIRequest
-from cubicweb.common.appobject import _MARKER 
 from cubicweb.common.mail import header
 from cubicweb.common.uilib import remove_html_tags
-from cubicweb.common.utils import SizeConstrainedList, HTMLHead
+from cubicweb.utils import SizeConstrainedList, HTMLHead
 from cubicweb.web import (INTERNAL_FIELD_VALUE, LOGGER, NothingToEdit, RequestError,
-                       StatusResponse)
+                          StatusResponse)
+
+_MARKER = object()
 
 
 def list_form_param(form, param, pop=False):
@@ -56,8 +56,8 @@
 
 
 class CubicWebRequestBase(DBAPIRequest):
-    """abstract HTTP request, should be extended according to the HTTP backend"""    
-    
+    """abstract HTTP request, should be extended according to the HTTP backend"""
+
     def __init__(self, vreg, https, form=None):
         super(CubicWebRequestBase, self).__init__(vreg)
         self.message = None
@@ -73,7 +73,7 @@
         self.data = {}
         # search state: 'normal' or 'linksearch' (eg searching for an object
         # to create a relation with another)
-        self.search_state = ('normal',) 
+        self.search_state = ('normal',)
         # tabindex generator
         self.tabindexgen = count()
         self.next_tabindex = self.tabindexgen.next
@@ -106,27 +106,27 @@
                     return
         # 3. default language
         self.set_default_language(vreg)
-            
+
     def set_language(self, lang):
         self._ = self.__ = self.translations[lang]
         self.lang = lang
         self.debug('request language: %s', lang)
-        
+
     # input form parameters management ########################################
-    
+
     # common form parameters which should be protected against html values
     # XXX can't add 'eid' for instance since it may be multivalued
     # dont put rql as well, if query contains < and > it will be corrupted!
-    no_script_form_params = set(('vid', 
-                                 'etype', 
+    no_script_form_params = set(('vid',
+                                 'etype',
                                  'vtitle', 'title',
                                  '__message',
                                  '__redirectvid', '__redirectrql'))
-        
+
     def setup_params(self, params):
         """WARNING: we're intentionaly leaving INTERNAL_FIELD_VALUE here
 
-        subclasses should overrides to 
+        subclasses should overrides to
         """
         if params is None:
             params = {}
@@ -146,7 +146,7 @@
                 del self.form[k]
             else:
                 self.form[k] = v
-    
+
     def no_script_form_param(self, param, default=None, value=None):
         """ensure there is no script in a user form param
 
@@ -167,11 +167,11 @@
                 value = value[0]
             return remove_html_tags(value)
         return value
-        
+
     def list_form_param(self, param, form=None, pop=False):
         """get param from form parameters and return its value as a list,
         skipping internal markers if any
-        
+
         * if the parameter isn't defined, return an empty list
         * if the parameter is a single (unicode) value, return a list
           containing that value
@@ -182,8 +182,8 @@
         """
         if form is None:
             form = self.form
-        return list_form_param(form, param, pop)            
-    
+        return list_form_param(form, param, pop)
+
 
     def reset_headers(self):
         """used by AutomaticWebTest to clear html headers between tests on
@@ -193,11 +193,11 @@
         return self
 
     # web state helpers #######################################################
-    
+
     def set_message(self, msg):
         assert isinstance(msg, unicode)
         self.message = msg
-    
+
     def update_search_state(self):
         """update the current search state"""
         searchstate = self.form.get('__mode')
@@ -215,6 +215,19 @@
         if self.cnx is not None:
             self.set_session_data('search_state', searchstate)
 
+    def match_search_state(self, rset):
+        """when searching an entity to create a relation, return True if entities in
+        the given rset may be used as relation end
+        """
+        try:
+            searchedtype = self.search_state[1][-1]
+        except IndexError:
+            return False # no searching for association
+        for etype in rset.column_types(0):
+            if etype != searchedtype:
+                return False
+        return True
+
     def update_breadcrumbs(self):
         """stores the last visisted page in session data"""
         searchstate = self.get_session_data('search_state')
@@ -234,7 +247,7 @@
     def register_onetime_callback(self, func, *args):
         cbname = 'cb_%s' % (
             sha.sha('%s%s%s%s' % (time.time(), func.__name__,
-                                  random.random(), 
+                                  random.random(),
                                   self.user.login)).hexdigest())
         def _cb(req):
             try:
@@ -242,12 +255,12 @@
             except TypeError:
                 from warnings import warn
                 warn('user callback should now take request as argument')
-                ret = func(*args)            
+                ret = func(*args)
             self.unregister_callback(self.pageid, cbname)
             return ret
         self.set_page_data(cbname, _cb)
         return cbname
-    
+
     def unregister_callback(self, pageid, cbname):
         assert pageid is not None
         assert cbname.startswith('cb_')
@@ -260,14 +273,17 @@
             callbacks = [key for key in sessdata if key.startswith('cb_')]
             for callback in callbacks:
                 self.del_session_data(callback)
-    
+
     # web edition helpers #####################################################
-    
+
     @cached # so it's writed only once
     def fckeditor_config(self):
+        self.add_js('fckeditor/fckeditor.js')
         self.html_headers.define_var('fcklang', self.lang)
         self.html_headers.define_var('fckconfigpath',
-                                     self.build_url('data/fckcwconfig.js'))
+                                     self.build_url('data/cubicweb.fckcwconfig.js'))
+    def use_fckeditor(self):
+        return self.vreg.config.fckeditor_installed() and self.property_value('ui.fckeditor')
 
     def edited_eids(self, withtype=False):
         """return a list of edited eids"""
@@ -314,7 +330,7 @@
             print eid, params
             raise RequestError(self._('missing parameters for entity %s') % eid)
         return params
-    
+
     def get_pending_operations(self, entity, relname, role):
         operations = {'insert' : [], 'delete' : []}
         for optype in ('insert', 'delete'):
@@ -326,7 +342,7 @@
                     if role == 'object' and entity.eid == eidto:
                         operations[optype].append(eidfrom)
         return operations
-    
+
     def get_pending_inserts(self, eid=None):
         """shortcut to access req's pending_insert entry
 
@@ -361,7 +377,7 @@
         """
         self.del_session_data(errorurl)
         self.remove_pending_operations()
-    
+
     # high level methods for HTTP headers management ##########################
 
     # must be cached since login/password are popped from the form dictionary
@@ -379,7 +395,7 @@
                 return None, None
         else:
             return self.header_authorization()
-    
+
     def get_cookie(self):
         """retrieve request cookies, returns an empty cookie if not found"""
         try:
@@ -407,7 +423,7 @@
         morsel['Max-Age'] = 0
         # The only way to set up cookie age for IE is to use an old "expired"
         # syntax. IE doesn't support Max-Age there is no library support for
-        # managing 
+        # managing
         # ===> Do _NOT_ comment this line :
         morsel['expires'] = 'Thu, 01-Jan-1970 00:00:00 GMT'
         self.add_header('Set-Cookie', morsel.OutputString())
@@ -460,9 +476,9 @@
             if localfile:
                 cssfile = self.datadir_url + cssfile
             add_css(cssfile, media)
-    
+
     # urls/path management ####################################################
-    
+
     def url(self, includeparams=True):
         """return currently accessed url"""
         return self.base_url() + self.relative_path(includeparams)
@@ -470,7 +486,7 @@
     def _datadir_url(self):
         """return url of the application's data directory"""
         return self.base_url() + 'data%s/' % self.vreg.config.instance_md5_version()
-    
+
     def selected(self, url):
         """return True if the url is equivalent to currently accessed url"""
         reqpath = self.relative_path().lower()
@@ -486,7 +502,7 @@
     def base_url_path(self):
         """returns the absolute path of the base url"""
         return urlsplit(self.base_url())[2]
-        
+
     @cached
     def from_controller(self):
         """return the id (string) of the controller issuing the request"""
@@ -496,7 +512,7 @@
         if controller in registered_controllers:
             return controller
         return 'view'
-    
+
     def external_resource(self, rid, default=_MARKER):
         """return a path to an external resource, using its identifier
 
@@ -525,9 +541,9 @@
         self._validate_cache()
         if self.http_method() == 'HEAD':
             raise StatusResponse(200, '')
-        
+
     # abstract methods to override according to the web front-end #############
-        
+
     def http_method(self):
         """returns 'POST', 'GET', 'HEAD', etc."""
         raise NotImplementedError()
@@ -537,7 +553,7 @@
         exists and is still usable
         """
         raise NotImplementedError()
-        
+
     def relative_path(self, includeparams=True):
         """return the normalized path of the request (ie at least relative
         to the application's root, but some other normalization may be needed
@@ -561,11 +577,11 @@
     def add_header(self, header, value):
         """add an output HTTP header"""
         raise NotImplementedError()
-    
+
     def remove_header(self, header):
         """remove an output HTTP header"""
         raise NotImplementedError()
-        
+
     def header_authorization(self):
         """returns a couple (auth-type, auth-value)"""
         auth = self.get_header("Authorization", None)
@@ -603,21 +619,21 @@
         mx date time value (GMT), else return None
         """
         raise NotImplementedError()
-    
+
     # page data management ####################################################
 
     def get_page_data(self, key, default=None):
         """return value associated to `key` in curernt page data"""
         page_data = self.cnx.get_session_data(self.pageid, {})
         return page_data.get(key, default)
-        
+
     def set_page_data(self, key, value):
         """set value associated to `key` in current page data"""
         self.html_headers.add_unload_pagedata()
         page_data = self.cnx.get_session_data(self.pageid, {})
         page_data[key] = value
         return self.cnx.set_session_data(self.pageid, page_data)
-        
+
     def del_page_data(self, key=None):
         """remove value associated to `key` in current page data
         if `key` is None, all page data will be cleared
@@ -638,16 +654,21 @@
     def ie_browser(self):
         useragent = self.useragent()
         return useragent and 'MSIE' in useragent
-    
+
     def xhtml_browser(self):
         useragent = self.useragent()
-        # MSIE does not support xml content-type
-        # quick fix: Opera supports xhtml and handles namespaces
-        # properly but it breaks jQuery.attr()
+        # * MSIE/Konqueror does not support xml content-type
+        # * Opera supports xhtml and handles namespaces properly but it breaks
+        #   jQuery.attr()
         if useragent and ('MSIE' in useragent or 'KHTML' in useragent
                           or 'Opera' in useragent):
             return False
         return True
 
+    def html_content_type(self):
+        if self.xhtml_browser():
+            return 'application/xhtml+xml'
+        return 'text/html'
+
 from cubicweb import set_log_methods
 set_log_methods(CubicWebRequestBase, LOGGER)
--- a/web/test/data/schema/relations.rel	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/data/schema/relations.rel	Mon May 04 13:09:48 2009 +0200
@@ -1,2 +1,2 @@
 Personne travaille Societe
-EUser connait Personne
+CWUser connait Personne
--- a/web/test/data/schema/testschema.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/data/schema/testschema.py	Mon May 04 13:09:48 2009 +0200
@@ -4,11 +4,11 @@
     
 class tags(RelationDefinition):
     subject = 'Tag'
-    object = ('BlogEntry', 'EUser')
+    object = ('BlogEntry', 'CWUser')
 
 class checked_by(RelationType):
     subject = 'BlogEntry'
-    object = 'EUser'
+    object = 'CWUser'
     cardinality = '?*'
     permissions = {
         'add': ('managers',),
--- a/web/test/runtests.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-# -*- coding: ISO-8859-1 -*-
-"""Script used to fire all tests"""
-
-__revision__ = '$Id: runtests.py,v 1.1 2005-06-17 14:09:18 adim Exp $'
-
-from logilab.common.testlib import main
-
-if __name__ == '__main__':
-    import sys, os
-    main(os.path.dirname(sys.argv[0]) or '.')
--- a/web/test/test_views.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/test_views.py	Mon May 04 13:09:48 2009 +0200
@@ -1,14 +1,12 @@
 """automatic tests"""
 
-from mx.DateTime import now
-
 from cubicweb.devtools.testlib import WebTest, AutomaticWebTest
-from cubicweb.common.view import AnyRsetView
+from cubicweb.view import AnyRsetView
 
 AutomaticWebTest.application_rql = [
-    'Any L,F WHERE E is EUser, E login L, E firstname F',
-    'Any L,F,E WHERE E is EUser, E login L, E firstname F',
-    'Any COUNT(X) WHERE X is EUser',
+    'Any L,F WHERE E is CWUser, E login L, E firstname F',
+    'Any L,F,E WHERE E is CWUser, E login L, E firstname F',
+    'Any COUNT(X) WHERE X is CWUser',
     ]
 
 class ComposityCopy(WebTest):
@@ -17,7 +15,7 @@
         """regression test: make sure we can ask a copy of a
         composite entity
         """
-        rset = self.execute('EUser X WHERE X login "admin"')
+        rset = self.execute('CWUser X WHERE X login "admin"')
         self.view('copy', rset)
 
 
@@ -35,26 +33,21 @@
         self.auto_populate(10)
 
     def test_manual_tests(self):
-        rset = self.execute('Any P,F,S WHERE P is EUser, P firstname F, P surname S')
-        self.view('table', rset, template=None, displayfilter=True, displaycols=[0,2])
-        rset = self.execute('Any P,F,S LIMIT 1 WHERE P is EUser, P firstname F, P surname S')
-        rset.req.form['rtype'] = 'firstname'
-        self.view('editrelation', rset, template=None)
-        rset.req.form['rtype'] = 'use_email'
-        self.view('editrelation', rset, template=None)
-        
+        rset = self.execute('Any P,F,S WHERE P is CWUser, P firstname F, P surname S')
+        self.view('table', rset, template=None, displayfilter=True, displaycols=[0,2])        
 
     def test_sortable_js_added(self):
-        rset = self.execute('EUser X')
+        rset = self.execute('CWUser X')
         # sortable.js should not be included by default
         self.failIf('jquery.tablesorter.js' in self.view('oneline', rset))
         # but should be included by the tableview
-        rset = self.execute('Any P,F,S LIMIT 1 WHERE P is EUser, P firstname F, P surname S')
+        rset = self.execute('Any P,F,S LIMIT 1 WHERE P is CWUser, P firstname F, P surname S')
         self.failUnless('jquery.tablesorter.js' in self.view('table', rset))
 
     def test_js_added_only_once(self):
+        self.vreg._loadedmods[__name__] = {}
         self.vreg.register_vobject_class(SomeView)
-        rset = self.execute('EUser X')
+        rset = self.execute('CWUser X')
         source = self.view('someview', rset).source
         self.assertEquals(source.count('spam.js'), 1)
 
@@ -63,8 +56,8 @@
 class ExplicitViewsTest(WebTest):
     
     def test_unrelateddivs(self):
-        rset = self.execute('Any X WHERE X is EUser, X login "admin"')
-        group = self.add_entity('EGroup', name=u'R&D')
+        rset = self.execute('Any X WHERE X is CWUser, X login "admin"')
+        group = self.add_entity('CWGroup', name=u'R&D')
         req = self.request(relation='in_group_subject')
         self.view('unrelateddivs', rset, req)
         
--- a/web/test/unittest_application.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_application.py	Mon May 04 13:09:48 2009 +0200
@@ -170,7 +170,7 @@
         self.commit()
     
     def test_nonregr_publish1(self):
-        req = self.request(u'EEType X WHERE X final FALSE, X meta FALSE')
+        req = self.request(u'CWEType X WHERE X final FALSE, X meta FALSE')
         self.app.publish('view', req)
         
     def test_nonregr_publish2(self):
@@ -183,7 +183,7 @@
         user = self.user()
         req.form = {
             'eid':       `user.eid`,
-            '__type:'+`user.eid`:    'EUser',
+            '__type:'+`user.eid`:    'CWUser',
             'login:'+`user.eid`:     '', # ERROR: no login specified
             'edits-login:'+`user.eid`: unicode(user.login),
              # just a sample, missing some necessary information for real life
@@ -207,7 +207,7 @@
         """        
         req = self.request()
         form = {'eid': ['X', 'Y'],
-                '__type:X': 'EUser',
+                '__type:X': 'CWUser',
                 # missing required field
                 'login:X': u'', 'edits-login:X': '', 
                 'surname:X': u'Mr Ouaoua', 'edits-surname:X': '',
@@ -253,7 +253,7 @@
         vreg = self.app.vreg
         # default value
         self.assertEquals(vreg.property_value('ui.language'), 'en')
-        self.execute('INSERT EProperty X: X value "fr", X pkey "ui.language"')
+        self.execute('INSERT CWProperty X: X value "fr", X pkey "ui.language"')
         self.assertEquals(vreg.property_value('ui.language'), 'en')
         self.commit()
         self.assertEquals(vreg.property_value('ui.language'), 'fr')
@@ -261,7 +261,7 @@
         self.assertEquals(vreg.property_value('ui.language'), 'fr')
         self.commit()
         self.assertEquals(vreg.property_value('ui.language'), 'de')
-        self.execute('DELETE EProperty X WHERE X pkey "ui.language"')
+        self.execute('DELETE CWProperty X WHERE X pkey "ui.language"')
         self.assertEquals(vreg.property_value('ui.language'), 'de')
         self.commit()
         self.assertEquals(vreg.property_value('ui.language'), 'en')
@@ -333,6 +333,26 @@
         self.assertRaises(AuthenticationError, self.publish, req, 'logout')
         self.assertEquals(len(self.open_sessions), 0) 
 
+    def test_login_by_email(self):
+        login = self.request().user.login
+        address = login + u'@localhost'
+        self.execute('INSERT EmailAddress X: X address %(address)s, U primary_email X '
+                     'WHERE U login %(login)s', {'address': address, 'login': login})
+        self.commit()
+        # option allow-email-login not set
+        req, origcnx = self._init_auth('cookie')
+        req.form['__login'] = address
+        req.form['__password'] = origcnx.password
+        self._test_auth_fail(req)
+        # option allow-email-login set
+        self.set_option('allow-email-login', True)
+        req, origcnx = self._init_auth('cookie')
+        req.form['__login'] = address
+        req.form['__password'] = origcnx.password
+        self._test_auth_succeed(req, origcnx)
+        self.assertRaises(AuthenticationError, self.publish, req, 'logout')
+        self.assertEquals(len(self.open_sessions), 0) 
+
     def _test_auth_anon(self, req):
         self.app.connect(req)
         acnx = req.cnx
@@ -384,8 +404,6 @@
         self.assertRaises(AuthenticationError, self.publish, req, 'logout')
         self.assertEquals(len(self.open_sessions), 0) 
 
-    
 
-        
 if __name__ == '__main__':
     unittest_main()
--- a/web/test/unittest_controller.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_controller.py	Mon May 04 13:09:48 2009 +0200
@@ -2,7 +2,7 @@
 
 """
 
-from mx.DateTime import DateTimeType, DateTimeDeltaType
+from datetime import datetime, date, time
 
 from logilab.common.testlib import unittest_main
 
@@ -10,12 +10,12 @@
 
 class BaseControllerTC(apptest.ControllerTC):
     def test_parse_datetime(self):
-        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24 12:18'), DateTimeType)
-        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24'), DateTimeType)
-        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24 12:18', 'Datetime'), DateTimeType)
-        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24', 'Datetime'), DateTimeType)
-        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24', 'Date'), DateTimeType)
-        self.assertIsInstance(self.ctrl.parse_datetime('12:18', 'Time'), DateTimeDeltaType)
+        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24 12:18'), datetime)
+        self.assertIsInstance(self.ctrl.parse_datetime('2006/06/24'), datetime)
+        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)
         self.assertRaises(ValueError,
                           self.ctrl.parse_datetime, '2006/06/24 12:188', 'Datetime')
         self.assertRaises(ValueError,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/unittest_form.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,182 @@
+from logilab.common.testlib import unittest_main, mock_object
+from cubicweb import Binary
+from cubicweb.devtools.testlib import WebTest
+from cubicweb.web.form import EntityFieldsForm, FieldsForm, FormRenderer
+from cubicweb.web.formfields import (IntField, StringField, RichTextField,
+                                     DateTimeField, DateTimePicker,
+                                     FileField, EditableFileField)
+from cubicweb.web.formwidgets import PasswordInput
+from cubicweb.web.views.workflow import ChangeStateForm
+
+
+class FieldsFormTC(WebTest):
+
+    def test_form_field_format(self):
+        form = FieldsForm(self.request(), None)
+        self.assertEquals(form.form_field_format(None), 'text/html')
+        self.execute('INSERT CWProperty X: X pkey "ui.default-text-format", X value "text/rest", X for_user U WHERE U login "admin"')
+        self.commit()
+        self.assertEquals(form.form_field_format(None), 'text/rest')
+
+
+class EntityFieldsFormTC(WebTest):
+
+    def setUp(self):
+        super(EntityFieldsFormTC, self).setUp()
+        self.req = self.request()
+        self.entity = self.user(self.req)
+        self.renderer = FormRenderer()
+
+    def test_form_field_vocabulary_unrelated(self):
+        b = self.add_entity('BlogEntry', title=u'di mascii code', content=u'a best-seller')
+        t = self.add_entity('Tag', name=u'x')
+        form1 = EntityFieldsForm(self.request(), None, entity=t)
+        unrelated = [reid for rview, reid in form1.subject_relation_vocabulary('tags')]
+        self.failUnless(b.eid in unrelated, unrelated)
+        form2 = EntityFieldsForm(self.request(), None, entity=b)
+        unrelated = [reid for rview, reid in form2.object_relation_vocabulary('tags')]
+        self.failUnless(t.eid in unrelated, unrelated)
+        self.execute('SET X tags Y WHERE X is Tag, Y is BlogEntry')
+        unrelated = [reid for rview, reid in form1.subject_relation_vocabulary('tags')]
+        self.failIf(b.eid in unrelated, unrelated)
+        unrelated = [reid for rview, reid in form2.object_relation_vocabulary('tags')]
+        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)
+        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)
+        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)
+        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)
+
+
+
+    # form view tests #########################################################
+
+    def test_massmailing_formview(self):
+        self.execute('INSERT EmailAddress X: X address L + "@cubicweb.org", '
+                     'U use_email X WHERE U is CWUser, U login L')
+        rset = self.execute('CWUser X')
+        self.view('massmailing', rset, template=None)
+
+
+    # form tests ##############################################################
+
+    def test_form_inheritance(self):
+        class CustomChangeStateForm(ChangeStateForm):
+            hello = IntField(name='youlou')
+            creation_date = DateTimeField(widget=DateTimePicker)
+        form = CustomChangeStateForm(self.req, redirect_path='perdu.com',
+                                     entity=self.entity)
+        form.form_render(state=123, trcomment=u'')
+
+    def test_change_state_form(self):
+        form = ChangeStateForm(self.req, redirect_path='perdu.com',
+                               entity=self.entity)
+        form.form_render(state=123, trcomment=u'')
+
+    # fields tests ############################################################
+
+    def _render_entity_field(self, name, form):
+        form.form_build_context({})
+        return form.field_by_name(name).render(form, self.renderer)
+
+    def _test_richtextfield(self, expected):
+        class RTFForm(EntityFieldsForm):
+            description = RichTextField()
+        state = self.execute('State X WHERE X name "activated", X state_of ET, ET name "CWUser"').get_entity(0, 0)
+        form = RTFForm(self.req, redirect_path='perdu.com', entity=state)
+        # make it think it can use fck editor anyway
+        form.form_field_format = lambda x: 'text/html'
+        self.assertTextEquals(self._render_entity_field('description', form),
+                              expected % {'eid': state.eid})
+
+
+    def test_richtextfield_1(self):
+        self.req.use_fckeditor = lambda: False
+        self._test_richtextfield('''<select name="description_format:%(eid)s" size="1" id="description_format:%(eid)s" tabindex="0">
+<option value="text/cubicweb-page-template">text/cubicweb-page-template</option>
+<option value="text/html">text/html</option>
+<option value="text/plain">text/plain</option>
+<option selected="selected" value="text/rest">text/rest</option>
+</select><textarea rows="5" name="description:564" onkeypress="autogrow(this)" cols="60" id="description:564" tabindex="1"/>''')
+
+
+    def test_richtextfield_2(self):
+        self.req.use_fckeditor = lambda: True
+        self._test_richtextfield('<input type="hidden" name="description_format:%(eid)s" value="text/rest"/><textarea rows="20" name="description:564" onkeypress="autogrow(this)" cols="80" cubicweb:type="wysiwyg" id="description:564" tabindex="0"/>')
+
+
+    def test_filefield(self):
+        class FFForm(EntityFieldsForm):
+            data = FileField(format_field=StringField(name='data_format'),
+                             encoding_field=StringField(name='data_encoding'))
+        file = self.add_entity('File', name=u"pouet.txt",
+                               data=Binary('new widgets system'))
+        form = FFForm(self.req, redirect_path='perdu.com', entity=file)
+        self.assertTextEquals(self._render_entity_field('data', form),
+                              '''<input id="data:%(eid)s" type="file" name="data:%(eid)s" value="" tabindex="0"/>
+<a href="javascript: toggleVisibility(&#39;data:%(eid)s-advanced&#39;)" title="show advanced fields"><img src="http://testing.fr/cubicweb/data/puce_down.png" alt="show advanced fields"/></a>
+<div id="data:%(eid)s-advanced" class="hidden">
+<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" type="text" name="data_format:%(eid)s" value="text/plain" tabindex="1"/><br/><br/>
+<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" type="text" name="data_encoding:%(eid)s" value="UTF-8" tabindex="2"/><br/><br/>
+</div>
+<br/>
+<input type="checkbox" name="data:%(eid)s__detach"/>
+detach attached file
+''' % {'eid': file.eid})
+
+
+    def test_editablefilefield(self):
+        class EFFForm(EntityFieldsForm):
+            data = EditableFileField(format_field=StringField(name='data_format'),
+                                     encoding_field=StringField(name='data_encoding'))
+            def form_field_encoding(self, field):
+                return 'ascii'
+            def form_field_format(self, field):
+                return 'text/plain'
+        file = self.add_entity('File', name=u"pouet.txt",
+                               data=Binary('new widgets system'))
+        form = EFFForm(self.req, redirect_path='perdu.com', entity=file)
+        self.assertTextEquals(self._render_entity_field('data', form),
+                              '''<input id="data:%(eid)s" type="file" name="data:%(eid)s" value="" tabindex="0"/>
+<a href="javascript: toggleVisibility(&#39;data:%(eid)s-advanced&#39;)" title="show advanced fields"><img src="http://testing.fr/cubicweb/data/puce_down.png" alt="show advanced fields"/></a>
+<div id="data:%(eid)s-advanced" class="hidden">
+<label for="data_format:%(eid)s">data_format</label><input id="data_format:%(eid)s" type="text" name="data_format:%(eid)s" value="text/plain" tabindex="1"/><br/><br/>
+<label for="data_encoding:%(eid)s">data_encoding</label><input id="data_encoding:%(eid)s" type="text" name="data_encoding:%(eid)s" value="UTF-8" tabindex="2"/><br/><br/>
+</div>
+<br/>
+<input type="checkbox" name="data:%(eid)s__detach"/>
+detach attached file
+<p><b>You can either submit a new file using the browse button above, or choose to remove already uploaded file by checking the "detach attached file" check-box, or edit file content online with the widget below.</b></p>
+<textarea tabindex="3" rows="20" cols="80" name="data:%(eid)s" onkeypress="autogrow(this)">new widgets system</textarea>''' % {'eid': file.eid})
+
+
+    def test_passwordfield(self):
+        class PFForm(EntityFieldsForm):
+            upassword = StringField(widget=PasswordInput)
+        form = PFForm(self.req, redirect_path='perdu.com', entity=self.entity)
+        self.assertTextEquals(self._render_entity_field('upassword', form),
+                              '''<input id="upassword:%(eid)s" type="password" name="upassword:%(eid)s" value="__cubicweb_internal_field__" tabindex="0"/>
+<br/>
+<input type="password" name="upassword-confirm:%(eid)s" value="__cubicweb_internal_field__" tabindex="0"/>
+&nbsp;
+<span class="emphasis">confirm password</span>''' % {'eid': self.entity.eid})
+
+
+if __name__ == '__main__':
+    unittest_main()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/unittest_formfields.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,84 @@
+"""unittests for cw.web.formfields"""
+
+from logilab.common.testlib import TestCase, unittest_main
+from cubicweb.devtools import TestServerConfiguration
+from cubicweb.web.formwidgets import PasswordInput, TextArea
+from cubicweb.web.formfields import *
+from cubicweb.entities.wfobjs import State
+from cubicweb.entities.authobjs import CWUser
+from cubes.file.entities import File
+
+config = TestServerConfiguration('data')
+config.bootstrap_cubes()
+schema = config.load_schema()
+state_schema = schema['State']
+cwuser_schema = schema['CWUser']
+file_schema.schema = schema['File']
+
+class GuessFieldTC(TestCase):
+
+    def test_state_fields(self):
+        title_field = guess_field(state_schema, schema['name'])
+        self.assertIsInstance(title_field, StringField)
+        self.assertEquals(title_field.required, True)
+
+#         synopsis_field = guess_field(state_schema, schema['synopsis'])
+#         self.assertIsInstance(synopsis_field, StringField)
+#         self.assertIsInstance(synopsis_field.widget, TextArea)
+#         self.assertEquals(synopsis_field.required, False)
+#         self.assertEquals(synopsis_field.help, 'an abstract for this state')
+
+        description_field = guess_field(state_schema, schema['description'])
+        self.assertIsInstance(description_field, RichTextField)
+        self.assertEquals(description_field.required, False)
+        self.assertEquals(description_field.format_field, None)
+
+        description_format_field = guess_field(state_schema, schema['description_format'])
+        self.assertEquals(description_format_field, None)
+
+        description_format_field = guess_field(state_schema, schema['description_format'], skip_meta_attr=False)
+        self.assertEquals(description_format_field.internationalizable, True)
+        self.assertEquals(description_format_field.sort, True)
+        self.assertEquals(description_format_field.initial, 'text/rest')
+
+#         wikiid_field = guess_field(state_schema, schema['wikiid'])
+#         self.assertIsInstance(wikiid_field, StringField)
+#         self.assertEquals(wikiid_field.required, False)
+
+
+    def test_euser_fields(self):
+        upassword_field = guess_field(cwuser_schema, schema['upassword'])
+        self.assertIsInstance(upassword_field, StringField)
+        self.assertIsInstance(upassword_field.widget, PasswordInput)
+        self.assertEquals(upassword_field.required, True)
+
+        last_login_time_field = guess_field(cwuser_schema, schema['last_login_time'])
+        self.assertIsInstance(last_login_time_field, DateTimeField)
+        self.assertEquals(last_login_time_field.required, False)
+
+        in_group_field = guess_field(cwuser_schema, schema['in_group'])
+        self.assertIsInstance(in_group_field, RelationField)
+        self.assertEquals(in_group_field.required, True)
+        self.assertEquals(in_group_field.role, 'subject')
+        self.assertEquals(in_group_field.help, 'groups grant permissions to the user')
+
+        owned_by_field = guess_field(cwuser_schema, schema['owned_by'], 'object')
+        self.assertIsInstance(owned_by_field, RelationField)
+        self.assertEquals(owned_by_field.required, False)
+        self.assertEquals(owned_by_field.role, 'object')
+
+
+    def test_file_fields(self):
+        data_format_field = guess_field(file_schema, schema['data_format'])
+        self.assertEquals(data_format_field, None)
+        data_encoding_field = guess_field(file_schema, schema['data_encoding'])
+        self.assertEquals(data_encoding_field, None)
+
+        data_field = guess_field(file_schema, schema['data'])
+        self.assertIsInstance(data_field, FileField)
+        self.assertEquals(data_field.required, True)
+        self.assertIsInstance(data_field.format_field, StringField)
+        self.assertIsInstance(data_field.encoding_field, StringField)
+
+if __name__ == '__main__':
+    unittest_main()
--- a/web/test/unittest_magicsearch.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_magicsearch.py	Mon May 04 13:09:48 2009 +0200
@@ -11,7 +11,7 @@
 
 
 translations = {
-    u'EUser' : u"Utilisateur",
+    u'CWUser' : u"Utilisateur",
 #    u'Workcase' : u"Affaire",
     u'EmailAddress' : u"Adresse",
 #    u'Division' : u"Division",
@@ -54,7 +54,7 @@
         self.assertEquals(rql, "Any P WHERE P use_email C, C is EmailAddress, C alias 'Logilab'")
         rql = "Any P WHERE P is Utilisateur, P adel C, P nom 'Smith'"
         rql, = self.proc.preprocess_query(rql, self.req)
-        self.assertEquals(rql, "Any P WHERE P is EUser, P use_email C, P surname 'Smith'")
+        self.assertEquals(rql, "Any P WHERE P is CWUser, P use_email C, P surname 'Smith'")
 
 
 class QSPreProcessorTC(EnvBasedTC):
@@ -79,7 +79,7 @@
     def test_attribute_translation(self):
         """tests QSPreProcessor._get_attribute_name"""
         translate = self.proc._get_attribute_name
-        eschema = self.schema.eschema('EUser')
+        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'))
@@ -95,10 +95,10 @@
         transform = self.proc._one_word_query
         self.assertEquals(transform('123'),
                           ('Any X WHERE X eid %(x)s', {'x': 123}, 'x'))
-        self.assertEquals(transform('EUser'),
-                          ('EUser E',))
+        self.assertEquals(transform('CWUser'),
+                          ('CWUser E',))
         self.assertEquals(transform('Utilisateur'),
-                          ('EUser E',))
+                          ('CWUser E',))
         self.assertEquals(transform('Adresse'),
                           ('EmailAddress E',))
         self.assertEquals(transform('adresse'),
@@ -108,37 +108,37 @@
     def test_two_words_query(self):
         """tests the 'two words shortcut queries'"""
         transform = self.proc._two_words_query
-        self.assertEquals(transform('EUser', 'E'),
-                          ("EUser E",))
-        self.assertEquals(transform('EUser', 'Smith'),
-                          ('EUser E WHERE E has_text %(text)s', {'text': 'Smith'}))
+        self.assertEquals(transform('CWUser', 'E'),
+                          ("CWUser E",))
+        self.assertEquals(transform('CWUser', 'Smith'),
+                          ('CWUser E WHERE E has_text %(text)s', {'text': 'Smith'}))
         self.assertEquals(transform('utilisateur', 'Smith'),
-                          ('EUser E WHERE E has_text %(text)s', {'text': 'Smith'}))
+                          ('CWUser E WHERE E has_text %(text)s', {'text': 'Smith'}))
         self.assertEquals(transform(u'adresse', 'Logilab'),
                           ('EmailAddress E WHERE E has_text %(text)s', {'text': 'Logilab'}))
         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('EUser', '%mi'), 'EUser E WHERE P surname LIKE "%mi"')
+        #self.assertEquals(transform('CWUser', '%mi'), 'CWUser E WHERE P surname LIKE "%mi"')
 
     def test_three_words_query(self):
         """tests the 'three words shortcut queries'"""
         transform = self.proc._three_words_query
         self.assertEquals(transform('utilisateur', u'prénom', 'cubicweb'),
-                          ('EUser E WHERE E firstname %(text)s', {'text': 'cubicweb'}))
+                          ('CWUser E WHERE E firstname %(text)s', {'text': 'cubicweb'}))
         self.assertEquals(transform('utilisateur', 'nom', 'cubicweb'),
-                          ('EUser E WHERE E surname %(text)s', {'text': 'cubicweb'}))
+                          ('CWUser E WHERE E surname %(text)s', {'text': 'cubicweb'}))
         self.assertEquals(transform(u'adresse', 'nom', 'cubicweb'),
                           ('EmailAddress E WHERE E alias %(text)s', {'text': 'cubicweb'}))
         self.assertEquals(transform('EmailAddress', 'nom', 'cubicweb'),
                           ('EmailAddress E WHERE E alias %(text)s', {'text': 'cubicweb'})) 
         self.assertEquals(transform('utilisateur', u'prénom', 'cubicweb%'),
-                          ('EUser E WHERE E firstname LIKE %(text)s', {'text': 'cubicweb%'}))
+                          ('CWUser E WHERE E firstname LIKE %(text)s', {'text': 'cubicweb%'}))
         # expanded shortcuts
-        self.assertEquals(transform('EUser', 'use_email', 'Logilab'),
-                          ('EUser E WHERE E use_email E1, E1 has_text %(text)s', {'text': 'Logilab'}))
-        self.assertEquals(transform('EUser', 'use_email', '%Logilab'),
-                          ('EUser E WHERE E use_email E1, E1 alias LIKE %(text)s', {'text': '%Logilab'}))
+        self.assertEquals(transform('CWUser', 'use_email', 'Logilab'),
+                          ('CWUser E WHERE E use_email E1, E1 has_text %(text)s', {'text': 'Logilab'}))
+        self.assertEquals(transform('CWUser', 'use_email', '%Logilab'),
+                          ('CWUser E WHERE E use_email E1, E1 alias LIKE %(text)s', {'text': '%Logilab'}))
         self.assertRaises(BadRQLQuery, transform, 'word1', 'word2', 'word3')
         
     def test_multiple_words_query(self):
@@ -150,24 +150,24 @@
         """tests how quoted queries are handled"""
         queries = [
             (u'Adresse "My own EmailAddress"', ('EmailAddress E WHERE E has_text %(text)s', {'text': u'My own EmailAddress'})),
-            (u'Utilisateur prénom "Jean Paul"', ('EUser E WHERE E firstname %(text)s', {'text': 'Jean Paul'})),
-            (u'Utilisateur firstname "Jean Paul"', ('EUser E WHERE E firstname %(text)s', {'text': 'Jean Paul'})),
-            (u'EUser firstname "Jean Paul"', ('EUser E WHERE E firstname %(text)s', {'text': 'Jean Paul'})),
+            (u'Utilisateur prénom "Jean Paul"', ('CWUser E WHERE E firstname %(text)s', {'text': 'Jean Paul'})),
+            (u'Utilisateur firstname "Jean Paul"', ('CWUser E WHERE E firstname %(text)s', {'text': 'Jean Paul'})),
+            (u'CWUser firstname "Jean Paul"', ('CWUser E WHERE E firstname %(text)s', {'text': 'Jean Paul'})),
             ]
         transform = self.proc._quoted_words_query
         for query, expected in queries:
             self.assertEquals(transform(query), expected)
         self.assertRaises(BadRQLQuery, transform, "unquoted rql")
         self.assertRaises(BadRQLQuery, transform, 'pers "Jean Paul"')
-        self.assertRaises(BadRQLQuery, transform, 'EUser firstname other "Jean Paul"')
+        self.assertRaises(BadRQLQuery, transform, 'CWUser firstname other "Jean Paul"')
     
     def test_process_query(self):
         """tests how queries are processed"""
         queries = [
-            (u'Utilisateur', (u"EUser E",)),
-            (u'Utilisateur P', (u"EUser P",)),
-            (u'Utilisateur cubicweb', (u'EUser E WHERE E has_text %(text)s', {'text': u'cubicweb'})),
-            (u'EUser prénom cubicweb', (u'EUser E WHERE E firstname %(text)s', {'text': 'cubicweb'},)),
+            (u'Utilisateur', (u"CWUser E",)),
+            (u'Utilisateur P', (u"CWUser P",)),
+            (u'Utilisateur cubicweb', (u'CWUser E WHERE E has_text %(text)s', {'text': u'cubicweb'})),
+            (u'CWUser prénom cubicweb', (u'CWUser E WHERE E firstname %(text)s', {'text': 'cubicweb'},)),
             (u'Any X WHERE X is Something', (u"Any X WHERE X is Something",)),
             ]
         for query, expected in queries:
@@ -195,11 +195,11 @@
             # XXX this sounds like a language translator test...
             # and it fail
             (u'Utilisateur Smith',
-             ('EUser E WHERE E has_text %(text)s', {'text': u'Smith'})),
+             ('CWUser E WHERE E has_text %(text)s', {'text': u'Smith'})),
             (u'utilisateur nom Smith',
-             ('EUser E WHERE E surname %(text)s', {'text': u'Smith'})),
+             ('CWUser E WHERE E surname %(text)s', {'text': u'Smith'})),
             (u'Any P WHERE P is Utilisateur, P nom "Smith"',
-             ('Any P WHERE P is EUser, P surname "Smith"', None)),
+             ('Any P WHERE P is CWUser, P surname "Smith"', None)),
             ]
         for query, expected in queries:
             rset = self.proc.process_query(query, self.req)
@@ -213,9 +213,9 @@
 
     def test_explicit_component(self):
         self.assertRaises(RQLSyntaxError,
-                          self.proc.process_query, u'rql: EUser E WHERE E noattr "Smith",', self.req)
+                          self.proc.process_query, u'rql: CWUser E WHERE E noattr "Smith",', self.req)
         self.assertRaises(BadRQLQuery,
-                          self.proc.process_query, u'rql: EUser E WHERE E noattr "Smith"', self.req)
+                          self.proc.process_query, u'rql: CWUser E WHERE E noattr "Smith"', self.req)
         rset = self.proc.process_query(u'text: utilisateur Smith', self.req)
         self.assertEquals(rset.rql, 'Any X WHERE X has_text %(text)s')
         self.assertEquals(rset.args, {'text': u'utilisateur Smith'})
--- a/web/test/unittest_owl.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,4075 +0,0 @@
-"""unittests for schema2dot"""
-
-import os
-
-from logilab.common.testlib import TestCase, unittest_main
-from logilab.common.compat import set
-from cubicweb.devtools.testlib import WebTest
-
-from lxml import etree
-from StringIO import StringIO
-
-       
-class OWLTC(WebTest):
-    
-    def test_schema2owl(self):
-
-        parser = etree.XMLParser(dtd_validation=True)
-
-        owl= StringIO('''<xsd:schema 
- xmlns:xsd="http://www.w3.org/2001/XMLSchema"
- xmlns:owl="http://www.w3.org/2002/07/owl#"
- targetNamespace="http://www.w3.org/2002/07/owl#"
- elementFormDefault="qualified" attributeFormDefault="unqualified">
-
-<xsd:import namespace="http://www.w3.org/XML/1998/namespace" schemaLocation="http://www.w3.org/2001/xml.xsd"/>
-
-<!-- The ontology -->
-  
-<xsd:element name="Import">
-  <xsd:complexType>
-    <xsd:simpleContent>
-      <xsd:extension base="xsd:anyURI">
-        <xsd:attributeGroup ref="xml:specialAttrs"/>
-      </xsd:extension>
-    </xsd:simpleContent>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="Ontology">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:element ref="owl:Import" minOccurs="0" maxOccurs="unbounded"/>
-      <xsd:group ref="owl:ontologyAnnotations"/>
-      <xsd:group ref="owl:Axiom" minOccurs="0" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attribute name="ontologyIRI" type="xsd:anyURI" use="optional"/>
-    <xsd:attribute name="versionIRI" type="xsd:anyURI" use="optional"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<!-- Entities, anonymous individuals, and literals -->
-
-<xsd:group name="Entity">
-  <xsd:choice>
-    <xsd:element ref="owl:Class"/>
-    <xsd:element ref="owl:Datatype"/>
-    <xsd:element ref="owl:ObjectProperty"/>
-    <xsd:element ref="owl:DataProperty"/>
-    <xsd:element ref="owl:AnnotationProperty"/>
-    <xsd:element ref="owl:NamedIndividual"/>
-  </xsd:choice>
-</xsd:group>
-
-<xsd:element name="Class">
-  <xsd:complexType>
-    <xsd:attribute name="IRI" type="xsd:anyURI" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="Datatype">
-  <xsd:complexType>
-    <xsd:attribute name="IRI" type="xsd:anyURI" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
- 
-<xsd:element name="ObjectProperty">
-  <xsd:complexType>
-    <xsd:attribute name="IRI" type="xsd:anyURI" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataProperty">
-  <xsd:complexType>
-    <xsd:attribute name="IRI" type="xsd:anyURI" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="AnnotationProperty">
-  <xsd:complexType>
-    <xsd:attribute name="IRI" type="xsd:anyURI" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:group name="Individual">
-  <xsd:choice>
-    <xsd:element ref="owl:NamedIndividual"/>
-    <xsd:element ref="owl:AnonymousIndividual"/>
-  </xsd:choice>
-</xsd:group>
-  
-<xsd:element name="NamedIndividual">
-  <xsd:complexType>
-    <xsd:attribute name="IRI" type="xsd:anyURI" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="AnonymousIndividual">
-  <xsd:complexType>
-    <xsd:attribute name="nodeID" type="xsd:NCName" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="Literal">
- <xsd:complexType>
-   <xsd:simpleContent>
-     <xsd:extension base="xsd:string">
-       <xsd:attribute name="datatypeIRI" type="xsd:anyURI"/>
-       <xsd:attributeGroup ref="xml:specialAttrs"/>
-     </xsd:extension>
-   </xsd:simpleContent>
- </xsd:complexType>
-</xsd:element>
-
-<!-- Declarations -->
-
-<xsd:element name="Declaration">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:Entity"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-  
-<!-- Object property expressions -->
-
-<xsd:group name="ObjectPropertyExpression">
-  <xsd:choice>
-    <xsd:element ref="owl:ObjectProperty"/>
-    <xsd:element ref="owl:InverseObjectProperty"/>
-  </xsd:choice>
-</xsd:group>
-
-<xsd:element name="InverseObjectProperty">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:element ref="owl:ObjectProperty"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<!-- Data property expressions -->
-
-<xsd:group name="DataPropertyExpression">
-  <xsd:sequence>
-    <xsd:element ref="owl:DataProperty"/>
-  </xsd:sequence>
-</xsd:group>
-
-<!-- Data ranges -->
-
-<xsd:group name="DataRange">
-  <xsd:choice>
-    <xsd:element ref="owl:Datatype"/>
-    <xsd:element ref="owl:DataIntersectionOf"/>
-    <xsd:element ref="owl:DataUnionOf"/>
-    <xsd:element ref="owl:DataComplementOf"/>
-    <xsd:element ref="owl:DataOneOf"/>
-    <xsd:element ref="owl:DatatypeRestriction"/>
-  </xsd:choice>
-</xsd:group>
-
-<xsd:element name="DataIntersectionOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:DataRange" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataUnionOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:DataRange" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataComplementOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:DataRange"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataOneOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:element ref="owl:Literal" minOccurs="1" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DatatypeRestriction">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:element ref="owl:Datatype"/>
-      <xsd:element name="FacetRestriction" minOccurs="1" maxOccurs="unbounded">
-        <xsd:complexType>
-          <xsd:sequence>
-            <xsd:element ref="owl:Literal"/>
-          </xsd:sequence>
-          <xsd:attribute name="facet" type="xsd:anyURI" use="required"/>
-        </xsd:complexType>
-      </xsd:element>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<!-- Class expressions -->
-
-<xsd:group name="ClassExpression">
-  <xsd:choice>
-    <xsd:element ref="owl:Class"/>
-    <xsd:element ref="owl:ObjectIntersectionOf"/>
-    <xsd:element ref="owl:ObjectUnionOf"/>
-    <xsd:element ref="owl:ObjectComplementOf"/>
-    <xsd:element ref="owl:ObjectOneOf"/>
-    <xsd:element ref="owl:ObjectSomeValuesFrom"/>
-    <xsd:element ref="owl:ObjectAllValuesFrom"/>
-    <xsd:element ref="owl:ObjectHasValue"/>
-    <xsd:element ref="owl:ObjectHasSelf"/>
-    <xsd:element ref="owl:ObjectMinCardinality"/>
-    <xsd:element ref="owl:ObjectMaxCardinality"/>
-    <xsd:element ref="owl:ObjectExactCardinality"/>
-    <xsd:element ref="owl:DataSomeValuesFrom"/>
-    <xsd:element ref="owl:DataAllValuesFrom"/>
-    <xsd:element ref="owl:DataHasValue"/>
-    <xsd:element ref="owl:DataMinCardinality"/>
-    <xsd:element ref="owl:DataMaxCardinality"/>
-    <xsd:element ref="owl:DataExactCardinality"/>
-  </xsd:choice>
-</xsd:group>
-
-<xsd:element name="ObjectIntersectionOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:ClassExpression" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectUnionOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:ClassExpression" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectComplementOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:ClassExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectOneOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:Individual" minOccurs="1" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectSomeValuesFrom">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-      <xsd:group ref="owl:ClassExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectAllValuesFrom">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-      <xsd:group ref="owl:ClassExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectHasValue">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-      <xsd:group ref="owl:Individual"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectHasSelf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectMinCardinality">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-      <xsd:group ref="owl:ClassExpression" minOccurs="0" maxOccurs="1"/>
-    </xsd:sequence>
-    <xsd:attribute name="cardinality" type="xsd:nonNegativeInteger" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectMaxCardinality">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-      <xsd:group ref="owl:ClassExpression" minOccurs="0" maxOccurs="1"/>
-    </xsd:sequence>
-    <xsd:attribute name="cardinality" type="xsd:nonNegativeInteger" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectExactCardinality">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-      <xsd:group ref="owl:ClassExpression" minOccurs="0" maxOccurs="1"/>
-    </xsd:sequence>
-    <xsd:attribute name="cardinality" type="xsd:nonNegativeInteger" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataSomeValuesFrom">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:DataPropertyExpression" minOccurs="1" maxOccurs="unbounded"/>
-      <xsd:group ref="owl:DataRange"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataAllValuesFrom">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:DataPropertyExpression" minOccurs="1" maxOccurs="unbounded"/>
-      <xsd:group ref="owl:DataRange"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataHasValue">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:DataPropertyExpression"/>
-      <xsd:element ref="owl:Literal"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataMinCardinality">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:DataPropertyExpression"/>
-      <xsd:group ref="owl:DataRange" minOccurs="0" maxOccurs="1"/>
-    </xsd:sequence>
-    <xsd:attribute name="cardinality" type="xsd:nonNegativeInteger" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataMaxCardinality">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:DataPropertyExpression"/>
-      <xsd:group ref="owl:DataRange" minOccurs="0" maxOccurs="1"/>
-    </xsd:sequence>
-    <xsd:attribute name="cardinality" type="xsd:nonNegativeInteger" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataExactCardinality">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:DataPropertyExpression"/>
-      <xsd:group ref="owl:DataRange" minOccurs="0" maxOccurs="1"/>
-    </xsd:sequence>
-    <xsd:attribute name="cardinality" type="xsd:nonNegativeInteger" use="required"/>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<!-- Axioms -->
-
-<xsd:group name="Axiom">
-  <xsd:choice>
-    <xsd:element ref="owl:Declaration"/>
-    <xsd:group ref="owl:ClassAxiom"/>
-    <xsd:group ref="owl:ObjectPropertyAxiom"/>
-    <xsd:group ref="owl:DataPropertyAxiom"/>
-    <xsd:element ref="owl:HasKey"/>
-    <xsd:group ref="owl:Assertion"/>
-    <xsd:group ref="owl:AnnotationAxiom"/>
-  </xsd:choice>
-</xsd:group>
-
-<!-- Class expression axioms -->
-
-<xsd:group name="ClassAxiom">
-  <xsd:choice>
-    <xsd:element ref="owl:SubClassOf"/>
-    <xsd:element ref="owl:EquivalentClasses"/>
-    <xsd:element ref="owl:DisjointClasses"/>
-    <xsd:element ref="owl:DisjointUnion"/>
-  </xsd:choice>
-</xsd:group>
-
-<xsd:element name="SubClassOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ClassExpression"/> <!-- This is the subexpression -->
-      <xsd:group ref="owl:ClassExpression"/> <!-- This is the superexpression -->
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="EquivalentClasses">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ClassExpression" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DisjointClasses">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ClassExpression" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DisjointUnion">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:element ref="owl:Class"/>
-      <xsd:group ref="owl:ClassExpression" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<!-- Object property axioms -->
-
-<xsd:group name="ObjectPropertyAxiom">
-  <xsd:choice>
-    <xsd:element ref="owl:SubObjectPropertyOf"/>
-    <xsd:element ref="owl:EquivalentObjectProperties"/>
-    <xsd:element ref="owl:DisjointObjectProperties"/>
-    <xsd:element ref="owl:InverseObjectProperties"/>
-    <xsd:element ref="owl:ObjectPropertyDomain"/>
-    <xsd:element ref="owl:ObjectPropertyRange"/>
-    <xsd:element ref="owl:FunctionalObjectProperty"/>
-    <xsd:element ref="owl:InverseFunctionalObjectProperty"/>
-    <xsd:element ref="owl:ReflexiveObjectProperty"/>
-    <xsd:element ref="owl:IrreflexiveObjectProperty"/>
-    <xsd:element ref="owl:SymmetricObjectProperty"/>
-    <xsd:element ref="owl:AsymmetricObjectProperty"/>
-    <xsd:element ref="owl:TransitiveObjectProperty"/>
-  </xsd:choice>
-</xsd:group>
-
-<xsd:element name="SubObjectPropertyOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:choice> <!-- This is the subproperty expression or the property chain -->
-        <xsd:group ref="owl:ObjectPropertyExpression"/>
-        <xsd:element name="PropertyChain">
-          <xsd:complexType>
-            <xsd:sequence>
-              <xsd:group ref="owl:ObjectPropertyExpression" minOccurs="2" maxOccurs="unbounded"/>
-            </xsd:sequence>
-            <xsd:attributeGroup ref="xml:specialAttrs"/>
-          </xsd:complexType>
-        </xsd:element>
-      </xsd:choice>
-      <xsd:group ref="owl:ObjectPropertyExpression"/> <!-- This is the superproperty expression -->  
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="EquivalentObjectProperties">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DisjointObjectProperties">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectPropertyDomain">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-      <xsd:group ref="owl:ClassExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectPropertyRange">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-      <xsd:group ref="owl:ClassExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="InverseObjectProperties">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression" minOccurs="2" maxOccurs="2"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="FunctionalObjectProperty">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="InverseFunctionalObjectProperty">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ReflexiveObjectProperty">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="IrreflexiveObjectProperty">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="SymmetricObjectProperty">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="AsymmetricObjectProperty">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
- 
-<xsd:element name="TransitiveObjectProperty">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<!-- Data property axioms -->
-
-<xsd:group name="DataPropertyAxiom">
-  <xsd:choice>
-    <xsd:element ref="owl:SubDataPropertyOf"/>
-    <xsd:element ref="owl:EquivalentDataProperties"/>
-    <xsd:element ref="owl:DisjointDataProperties"/>
-    <xsd:element ref="owl:DataPropertyDomain"/>
-    <xsd:element ref="owl:DataPropertyRange"/>
-    <xsd:element ref="owl:FunctionalDataProperty"/>
-  </xsd:choice>
-</xsd:group>
-
-<xsd:element name="SubDataPropertyOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:DataPropertyExpression"/> <!-- This is the subproperty expression -->
-      <xsd:group ref="owl:DataPropertyExpression"/> <!-- This is the superproperty expression -->
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="EquivalentDataProperties">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:DataPropertyExpression" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DisjointDataProperties">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:DataPropertyExpression" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataPropertyDomain">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:DataPropertyExpression"/>
-      <xsd:group ref="owl:ClassExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataPropertyRange">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:DataPropertyExpression"/>
-      <xsd:group ref="owl:DataRange"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="FunctionalDataProperty">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:DataPropertyExpression"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<!-- Key axioms -->
-
-<xsd:element name="HasKey">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ClassExpression"/>
-      <xsd:choice minOccurs="1" maxOccurs="unbounded">
-        <xsd:group ref="owl:ObjectPropertyExpression"/>
-        <xsd:group ref="owl:DataPropertyExpression"/>
-      </xsd:choice>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<!-- Assertions -->
-
-<xsd:group name="Assertion">
-  <xsd:choice>
-    <xsd:element ref="owl:SameIndividual"/>
-    <xsd:element ref="owl:DifferentIndividuals"/>
-    <xsd:element ref="owl:ClassAssertion"/>
-    <xsd:element ref="owl:ObjectPropertyAssertion"/>
-    <xsd:element ref="owl:NegativeObjectPropertyAssertion"/>
-    <xsd:element ref="owl:DataPropertyAssertion"/>
-    <xsd:element ref="owl:NegativeDataPropertyAssertion"/>
-  </xsd:choice>
-</xsd:group> 
-
-<xsd:element name="SameIndividual">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:Individual" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DifferentIndividuals">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:Individual" minOccurs="2" maxOccurs="unbounded"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ClassAssertion">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ClassExpression"/>
-      <xsd:group ref="owl:Individual"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="ObjectPropertyAssertion">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-      <xsd:group ref="owl:Individual"/> <!-- This is the source invididual  -->
-      <xsd:group ref="owl:Individual"/> <!-- This is the target individual -->
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="NegativeObjectPropertyAssertion">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:ObjectPropertyExpression"/>
-      <xsd:group ref="owl:Individual"/> <!-- This is the source invididual  -->
-      <xsd:group ref="owl:Individual"/> <!-- This is the target individual -->
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="DataPropertyAssertion">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:DataPropertyExpression"/>
-      <xsd:group ref="owl:Individual"/> <!-- This is the source invididual  -->
-      <xsd:element ref="owl:Literal"/> <!-- This is the target value -->
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="NegativeDataPropertyAssertion">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:group ref="owl:DataPropertyExpression"/>
-      <xsd:group ref="owl:Individual"/> <!-- This is the source invididual  -->
-      <xsd:element ref="owl:Literal"/> <!-- This is the target value -->
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<!-- Annotations  -->
-
-<xsd:element name="IRI">
-  <xsd:complexType>
-    <xsd:simpleContent>
-      <xsd:extension base="xsd:anyURI">
-        <xsd:attributeGroup ref="xml:specialAttrs"/>
-      </xsd:extension>
-    </xsd:simpleContent>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:group name="AnnotationSubject">
-  <xsd:choice>
-    <xsd:element ref="owl:IRI"/>
-    <xsd:element ref="owl:AnonymousIndividual"/>
-  </xsd:choice>
-</xsd:group>
-
-<xsd:group name="AnnotationValue">
-  <xsd:choice>
-    <xsd:element ref="owl:IRI"/>
-    <xsd:element ref="owl:AnonymousIndividual"/>
-    <xsd:element ref="owl:Literal"/>
-  </xsd:choice>
-</xsd:group>
-
-<xsd:element name="Annotation">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:annotationAnnotations"/>
-      <xsd:element ref="owl:AnnotationProperty"/>
-      <xsd:group ref="owl:AnnotationValue"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:group name="axiomAnnotations">
-  <xsd:sequence>
-    <xsd:element ref="owl:Annotation" minOccurs="0" maxOccurs="unbounded"/>
-  </xsd:sequence>
-</xsd:group>
-
-<xsd:group name="ontologyAnnotations">
-  <xsd:sequence>
-    <xsd:element ref="owl:Annotation" minOccurs="0" maxOccurs="unbounded"/>
-  </xsd:sequence>
-</xsd:group>
-
-<xsd:group name="annotationAnnotations">
-  <xsd:sequence>
-    <xsd:element ref="owl:Annotation" minOccurs="0" maxOccurs="unbounded"/>
-  </xsd:sequence>
-</xsd:group>
-
-<!-- Annotation axioms -->
-
-<xsd:group name="AnnotationAxiom">
-  <xsd:choice>
-    <xsd:element ref="owl:AnnotationAssertion"/>
-    <xsd:element ref="owl:SubAnnotationPropertyOf"/>
-    <xsd:element ref="owl:AnnotationPropertyDomain"/>
-    <xsd:element ref="owl:AnnotationPropertyRange"/>
-  </xsd:choice>
-</xsd:group>
-
-<xsd:element name="AnnotationAssertion">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:element ref="owl:AnnotationProperty"/>
-      <xsd:group ref="owl:AnnotationSubject"/>
-      <xsd:group ref="owl:AnnotationValue"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="SubAnnotationPropertyOf">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:element ref="owl:AnnotationProperty"/> <!-- This is the subproperty -->
-      <xsd:element ref="owl:AnnotationProperty"/> <!-- This is the superproperty -->
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="AnnotationPropertyDomain">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:element ref="owl:AnnotationProperty"/>
-      <xsd:element ref="owl:IRI"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-<xsd:element name="AnnotationPropertyRange">
-  <xsd:complexType>
-    <xsd:sequence>
-      <xsd:group ref="owl:axiomAnnotations"/>
-      <xsd:element ref="owl:AnnotationProperty"/>
-      <xsd:element ref="owl:IRI"/>
-    </xsd:sequence>
-    <xsd:attributeGroup ref="xml:specialAttrs"/>
-  </xsd:complexType>
-</xsd:element>
-
-</xsd:schema>
-
-''')
-
-        rdf = StringIO('''<xsd:schema
-        xmlns:xsd="http://www.w3.org/1999/XMLSchema"
-        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-        targetNamespace="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
-      
-        <xsd:element name="RDF">
-                <xsd:complexType  content="elementOnly" >
-                        <xsd:sequence  maxOccurs="*" >
-                                <xsd:choice>
-                                        <xsd:element ref="rdf:TypedNode"   /><!-- abstract !-->
-                                        <xsd:element ref="rdf:Bag" />
-                                        <xsd:element ref="rdf:Seq" />
-                                        <xsd:element ref="rdf:Alt" />
-                                </xsd:choice>
-                        </xsd:sequence>
-                </xsd:complexType>
-        </xsd:element>
-
-        <!-- RDF Typed nodes -->
-       <xsd:complexType   name="TypedNodeType" content="elementOnly" >
-                <xsd:sequence maxOccurs="*" >
-                        <xsd:element ref="rdf:PropertyElt"   /><!--abstract !-->
-                </xsd:sequence>
-                <xsd:attribute  name="id" minOccurs="0" type="ID"  />
-                <xsd:attribute  name="type" minOccurs="0" type="string" />
-                <xsd:attribute name="about" minOccurs="0" type="string" />
-                <xsd:attribute  name="aboutEach" minOccurs="0" type="string" />
-                <xsd:attribute   name="aboutEachPrefix" minOccurs="0" type="string" />
-                <xsd:attribute  name="badID" minOccurs="0" type="ID" />
-        </xsd:complexType>
-        <xsd:element name="TypedNode"  abstract="true"  type="rdf:TypedNodeType" />
-
-        <xsd:element name="Description"
-                type="rdf:TypedNodeType" equivClass="rdf:TypedNode" />
-
-
-        <!-- RDF Property Elements -->
-        <xsd:complexType  name="PropertyEltType" >
-                <xsd:any minOccurs="0" />
-                <xsd:attribute name="id"  minOccurs="0"  type="ID" />
-                <xsd:attribute  name="resource" minOccurs="0"  type="string" />
-                <xsd:attribute  name="value" minOccurs="0"  type="string" />
-                <xsd:attribute  name="badID" minOccurs="0" type="ID"  />
-                <xsd:attribute name="parseType"  minOccurs="0" >
-                        <xsd:simpleType base="NMTOKEN">
-                                 <xsd:enumeration value="Resource"/>
-                                 <xsd:enumeration value="Literal" />
-                       </xsd:simpleType>
-                </xsd:attribute>
-                <xsd:anyAttribute  />
-        </xsd:complexType>
-
-        <xsd:element name="PropertyElt"  abstract="true" type="rdf:PropertyEltType" />
-
-        <xsd:element   name="subject"   equivClass="rdf:PropertyElt"  />
-        <xsd:element name="predicate"   equivClass="rdf:PropertyElt" />
-        <xsd:element name="object"  equivClass="rdf:PropertyElt" />
-        <xsd:element   name="type"  equivClass="rdf:PropertyElt" />
-
-        <xsd:element name="value">
-                <xsd:complexType>
-                        <xsd:any />
-                        <xsd:anyAttribute />
-                </xsd:complexType>
-        </xsd:element>
-
-
-        <!-- RDF Containers -->
-        <xsd:complexType name="Container" abstract="true" content="elementOnly" >
-                <xsd:sequence maxOccurs="*">
-                        <xsd:element name="li">
-                                <xsd:complexType>
-                                        <xsd:any/>
-                                        <xsd:attribute name="id"  minOccurs="0" type="ID" />
-                                        <xsd:attribute name="parseType" minOccurs="0" >
-                                                <xsd:simpleType base="NMTOKEN">
-                                                     <xsd:enumeration value="Resource"/>
-                                                     <xsd:enumeration value="Literal" />
-                                                </xsd:simpleType>
-                                        </xsd:attribute>
-                                        <xsd:anyAttribute />
-                                </xsd:complexType>
-                        </xsd:element>
-                </xsd:sequence>
-                <xsd:attribute name="id" type="ID" />
-                <xsd:anyAttribute />
-        </xsd:complexType>
-
-        <xsd:element name="Alt" type="rdf:Container" />
-        <xsd:element name="Bag" type="rdf:Container" />
-        <xsd:element name="Seq" type="rdf:Container" />
-
-</xsd:schema>
-
- ''')
-        
-        
-        xmlschema_rdf = etree.parse(rdf)
-        xmlschema_owl = etree.parse(owl)
-        
-        owlschema = etree.XMLSchema(xmlschema_owl)
-        valid = StringIO('''<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE rdf:RDF [
-        <!ENTITY owl "http://www.w3.org/2002/07/owl#" >
-        <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#" >
-        <!ENTITY rdfs "http://www.w3.org/2000/01/rdf-schema#" >
-        <!ENTITY rdf "http://www.w3.org/1999/02/22-rdf-syntax-ns#" >
-        <!ENTITY inst_jplorg2 "http://logilab.org/owl/ontologies/inst_jplorg2#" >
-        
-        ]>
-<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" xmlns:xsd="http://www.w3.org/2001/XMLSchema#" xmlns:owl="http://www.w3.org/2002/07/owl#" xmlns="http://logilab.org/owl/ontologies/inst_jplorg2#" xmlns:inst_jplorg2="http://logilab.org/owl/ontologies/inst_jplorg2#" xml:base="http://logilab.org/owl/ontologies/inst_jplorg2#">
-
-    <owl:Ontology rdf:about="">
-        <rdfs:comment>
-        inst_jplorg2 Cubicweb OWL Ontology                           
-                                        
-        </rdfs:comment>
-        <!-- classes definition --><owl:Class rdf:ID="Blog"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#interested_in"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#entry_of"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#title"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="BlogEntry"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#entry_of"/>
-                                <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#filed_under"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#interested_in"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#comments"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#title"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#content_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#content"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="Card"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#filed_under"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#require_permission"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#test_case_for"/>
-                                <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#test_case_of"/>
-                                <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#documented_by"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#instance_of"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#comments"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#title"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#synopsis"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#content_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#content"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#wikiid"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="Email"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#sent_on"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#sender"/>
-                                <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minCardinality>
-                        <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#recipients"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#cc"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#parts"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#attachment"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#reply_to"/>
-                                <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#cites"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_thread"/>
-                                <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#generated_by"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#generated_by"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#comments"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#reply_to"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#cites"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#subject"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#messageid"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#headers"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="EmailThread"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#forked_from"/>
-                                <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_thread"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#forked_from"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#title"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="ExtProject"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#filed_under"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#require_permission"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#recommends"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#uses"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#name"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#url"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="File"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#filed_under"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#require_permission"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#documented_by"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#comments"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#attachment"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#attachment"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#data"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#data_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#data_encoding"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#name"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="Image"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#require_permission"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#attachment"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#screenshot"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#data"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#data_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#data_encoding"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#name"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="License"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#license_of"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#name"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#shortdesc"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#longdesc_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#longdesc"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#url"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="Link"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#filed_under"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#comments"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#title"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#url"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#embed"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="MailingList"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#use_email"/>
-                                <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minCardinality>
-                        <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#mailinglist_of"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#sent_on"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#name"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#mlid"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#archive"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#homepage"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="Project"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#uses"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#uses"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#recommends"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#recommends"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#documented_by"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#documented_by"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#screenshot"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_state"/>
-                                <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minCardinality>
-                        <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#filed_under"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#require_permission"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#recommends"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#concerns"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#test_case_of"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#mailinglist_of"/>
-                                <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#uses"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#interested_in"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#license_of"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#version_of"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#wf_info_for"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#name"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#summary"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#url"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#vcsurl"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#reporturl"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#downloadurl"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#debian_source_package"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="TestInstance"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#instance_of"/>
-                                <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minCardinality>
-                        <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#for_version"/>
-                                <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minCardinality>
-                        <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#generate_bug"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_state"/>
-                                <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minCardinality>
-                        <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#require_permission"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#comments"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#wf_info_for"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#name"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="Ticket"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#see_also"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#concerns"/>
-                                <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minCardinality>
-                        <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#appeared_in"/>
-                                <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#done_in"/>
-                                <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_state"/>
-                                <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minCardinality>
-                        <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#attachment"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#attachment"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#identical_to"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#depends_on"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#require_permission"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#depends_on"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#comments"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#generate_bug"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#wf_info_for"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#test_case_for"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#title"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#type"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#priority"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#load"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#load_left"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#debian_bug_number"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><owl:Class rdf:ID="Version"><rdfs:subClassOf rdf:resource="http://www.w3.org/2002/07/owl#Thing"/>
-                                <!-- relations --><rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_basket"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#version_of"/>
-                                <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minCardinality>
-                        <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#todo_by"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#in_state"/>
-                                <owl:minCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:minCardinality>
-                        <owl:maxCardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:maxCardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#conflicts"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#depends_on"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#require_permission"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#done_in"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#tags"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#depends_on"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#for_version"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#wf_info_for"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <rdfs:subClassOf>
-                              <owl:Restriction>
-                              <owl:onProperty rdf:resource="#appeared_in"/>
-                                <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">n</owl:cardinality>
-                              </owl:Restriction>
-                           </rdfs:subClassOf>
-                                <!-- attributes --><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#num"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description_format"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#description"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#starting_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#prevision_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#publication_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#creation_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf><rdfs:subClassOf>
-                              <owl:Restriction>
-                                  <owl:onProperty rdf:resource="#modification_date"/>
-                                  <owl:cardinality rdf:datatype="http://www.w3.org/2001/XMLSchema#nonNegativeInteger">1</owl:cardinality>
-                              </owl:Restriction>
-                        </rdfs:subClassOf></owl:Class><!-- property definition --><!-- object property --><owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#Blog"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="interested_in">
-                              <rdfs:domain rdf:resource="#Blog"/>
-                              <rdfs:range rdf:resource="#EUser"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="entry_of">
-                              <rdfs:domain rdf:resource="#Blog"/>
-                              <rdfs:range rdf:resource="#BlogEntry"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#Link"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#ExtProject"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#BlogEntry"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="entry_of">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#Blog"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="filed_under">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#Folder"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="interested_in">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#EUser"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="comments">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#Comment"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#BlogEntry"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#ExtProject"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Link"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#BlogEntry"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="filed_under">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Folder"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="require_permission">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#EPermission"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="test_case_for">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="test_case_of">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="documented_by">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="instance_of">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#TestInstance"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="comments">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Comment"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#Card"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="sent_on">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#MailingList"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="sender">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#EmailAddress"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="recipients">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#EmailAddress"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="cc">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#EmailAddress"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="parts">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#EmailPart"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="attachment">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="reply_to">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#Email"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="cites">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#Email"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_thread">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#EmailThread"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="generated_by">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#TrInfo"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="generated_by">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#Comment"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="comments">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#Comment"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="reply_to">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#Email"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="cites">
-                              <rdfs:domain rdf:resource="#Email"/>
-                              <rdfs:range rdf:resource="#Email"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#EmailThread"/>
-                              <rdfs:range rdf:resource="#EmailThread"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="forked_from">
-                              <rdfs:domain rdf:resource="#EmailThread"/>
-                              <rdfs:range rdf:resource="#EmailThread"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#EmailThread"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_thread">
-                              <rdfs:domain rdf:resource="#EmailThread"/>
-                              <rdfs:range rdf:resource="#Email"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="forked_from">
-                              <rdfs:domain rdf:resource="#EmailThread"/>
-                              <rdfs:range rdf:resource="#EmailThread"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#BlogEntry"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#Link"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#ExtProject"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="filed_under">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#Folder"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="require_permission">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#EPermission"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="recommends">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="uses">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#ExtProject"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Link"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#BlogEntry"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#ExtProject"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="filed_under">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Folder"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="require_permission">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#EPermission"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="documented_by">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="comments">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Comment"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="attachment">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Email"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="attachment">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#File"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#ExtProject"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#BlogEntry"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#Link"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#Email"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="require_permission">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#EPermission"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="attachment">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="screenshot">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#Image"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#License"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="license_of">
-                              <rdfs:domain rdf:resource="#License"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#License"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#BlogEntry"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#ExtProject"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#Link"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="filed_under">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#Folder"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="comments">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#Comment"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#Link"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#MailingList"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="use_email">
-                              <rdfs:domain rdf:resource="#MailingList"/>
-                              <rdfs:range rdf:resource="#EmailAddress"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="mailinglist_of">
-                              <rdfs:domain rdf:resource="#MailingList"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="sent_on">
-                              <rdfs:domain rdf:resource="#MailingList"/>
-                              <rdfs:range rdf:resource="#Email"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#MailingList"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#BlogEntry"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Link"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#ExtProject"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="uses">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#ExtProject"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="uses">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="recommends">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#ExtProject"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="recommends">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="documented_by">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="documented_by">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="screenshot">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_state">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#State"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="filed_under">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Folder"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="require_permission">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#EPermission"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="recommends">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="concerns">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="test_case_of">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="mailinglist_of">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#MailingList"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="uses">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="interested_in">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#EUser"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="license_of">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#License"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="version_of">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#Version"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="wf_info_for">
-                              <rdfs:domain rdf:resource="#Project"/>
-                              <rdfs:range rdf:resource="#TrInfo"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="instance_of">
-                              <rdfs:domain rdf:resource="#TestInstance"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="for_version">
-                              <rdfs:domain rdf:resource="#TestInstance"/>
-                              <rdfs:range rdf:resource="#Version"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="generate_bug">
-                              <rdfs:domain rdf:resource="#TestInstance"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#TestInstance"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_state">
-                              <rdfs:domain rdf:resource="#TestInstance"/>
-                              <rdfs:range rdf:resource="#State"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="require_permission">
-                              <rdfs:domain rdf:resource="#TestInstance"/>
-                              <rdfs:range rdf:resource="#EPermission"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="comments">
-                              <rdfs:domain rdf:resource="#TestInstance"/>
-                              <rdfs:range rdf:resource="#Comment"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="wf_info_for">
-                              <rdfs:domain rdf:resource="#TestInstance"/>
-                              <rdfs:range rdf:resource="#TrInfo"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#ExtProject"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#BlogEntry"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Link"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Email"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="see_also">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="concerns">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="appeared_in">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Version"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="done_in">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Version"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_state">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#State"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="attachment">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Image"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="attachment">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#File"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="identical_to">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="depends_on">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="require_permission">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#EPermission"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="depends_on">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="comments">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Comment"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="generate_bug">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#TestInstance"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="wf_info_for">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#TrInfo"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="test_case_for">
-                              <rdfs:domain rdf:resource="#Ticket"/>
-                              <rdfs:range rdf:resource="#Card"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_basket">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#Basket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="version_of">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#Project"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="todo_by">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#EUser"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="in_state">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#State"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="conflicts">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#Version"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="depends_on">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#Version"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="require_permission">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#EPermission"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="done_in">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="tags">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#Tag"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="depends_on">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#Version"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="for_version">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#TestInstance"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="wf_info_for">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#TrInfo"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <owl:ObjectProperty rdf:ID="appeared_in">
-                              <rdfs:domain rdf:resource="#Version"/>
-                              <rdfs:range rdf:resource="#Ticket"/>
-                           </owl:ObjectProperty>                   
-                             
-                                <!-- datatype property --><owl:DatatypeProperty rdf:ID="title">
-                          <rdfs:domain rdf:resource="#Blog"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description">
-                          <rdfs:domain rdf:resource="#Blog"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#Blog"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#Blog"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="title">
-                          <rdfs:domain rdf:resource="#BlogEntry"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="content_format">
-                          <rdfs:domain rdf:resource="#BlogEntry"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="content">
-                          <rdfs:domain rdf:resource="#BlogEntry"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#BlogEntry"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#BlogEntry"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="title">
-                          <rdfs:domain rdf:resource="#Card"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="synopsis">
-                          <rdfs:domain rdf:resource="#Card"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="content_format">
-                          <rdfs:domain rdf:resource="#Card"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="content">
-                          <rdfs:domain rdf:resource="#Card"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="wikiid">
-                          <rdfs:domain rdf:resource="#Card"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#Card"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#Card"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="subject">
-                          <rdfs:domain rdf:resource="#Email"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="date">
-                          <rdfs:domain rdf:resource="#Email"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="messageid">
-                          <rdfs:domain rdf:resource="#Email"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="headers">
-                          <rdfs:domain rdf:resource="#Email"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#Email"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#Email"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="title">
-                          <rdfs:domain rdf:resource="#EmailThread"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#EmailThread"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#EmailThread"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="name">
-                          <rdfs:domain rdf:resource="#ExtProject"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description_format">
-                          <rdfs:domain rdf:resource="#ExtProject"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description">
-                          <rdfs:domain rdf:resource="#ExtProject"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="url">
-                          <rdfs:domain rdf:resource="#ExtProject"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#ExtProject"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#ExtProject"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="data">
-                          <rdfs:domain rdf:resource="#File"/>
-                          <rdfs:range rdf:resource="xsd:byte"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="data_format">
-                          <rdfs:domain rdf:resource="#File"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="data_encoding">
-                          <rdfs:domain rdf:resource="#File"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="name">
-                          <rdfs:domain rdf:resource="#File"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description_format">
-                          <rdfs:domain rdf:resource="#File"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description">
-                          <rdfs:domain rdf:resource="#File"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#File"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#File"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="data">
-                          <rdfs:domain rdf:resource="#Image"/>
-                          <rdfs:range rdf:resource="xsd:byte"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="data_format">
-                          <rdfs:domain rdf:resource="#Image"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="data_encoding">
-                          <rdfs:domain rdf:resource="#Image"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="name">
-                          <rdfs:domain rdf:resource="#Image"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description_format">
-                          <rdfs:domain rdf:resource="#Image"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description">
-                          <rdfs:domain rdf:resource="#Image"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#Image"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#Image"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="name">
-                          <rdfs:domain rdf:resource="#License"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="shortdesc">
-                          <rdfs:domain rdf:resource="#License"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="longdesc_format">
-                          <rdfs:domain rdf:resource="#License"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="longdesc">
-                          <rdfs:domain rdf:resource="#License"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="url">
-                          <rdfs:domain rdf:resource="#License"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#License"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#License"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="title">
-                          <rdfs:domain rdf:resource="#Link"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="url">
-                          <rdfs:domain rdf:resource="#Link"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="embed">
-                          <rdfs:domain rdf:resource="#Link"/>
-                          <rdfs:range rdf:resource="xsd:boolean"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description_format">
-                          <rdfs:domain rdf:resource="#Link"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description">
-                          <rdfs:domain rdf:resource="#Link"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#Link"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#Link"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="name">
-                          <rdfs:domain rdf:resource="#MailingList"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="mlid">
-                          <rdfs:domain rdf:resource="#MailingList"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description_format">
-                          <rdfs:domain rdf:resource="#MailingList"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description">
-                          <rdfs:domain rdf:resource="#MailingList"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="archive">
-                          <rdfs:domain rdf:resource="#MailingList"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="homepage">
-                          <rdfs:domain rdf:resource="#MailingList"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#MailingList"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#MailingList"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="name">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="summary">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="url">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="vcsurl">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="reporturl">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="downloadurl">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="debian_source_package">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description_format">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#Project"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="name">
-                          <rdfs:domain rdf:resource="#TestInstance"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#TestInstance"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#TestInstance"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="title">
-                          <rdfs:domain rdf:resource="#Ticket"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="type">
-                          <rdfs:domain rdf:resource="#Ticket"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="priority">
-                          <rdfs:domain rdf:resource="#Ticket"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="load">
-                          <rdfs:domain rdf:resource="#Ticket"/>
-                          <rdfs:range rdf:resource="xsd:float"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="load_left">
-                          <rdfs:domain rdf:resource="#Ticket"/>
-                          <rdfs:range rdf:resource="xsd:float"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="debian_bug_number">
-                          <rdfs:domain rdf:resource="#Ticket"/>
-                          <rdfs:range rdf:resource="xsd:int"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description_format">
-                          <rdfs:domain rdf:resource="#Ticket"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description">
-                          <rdfs:domain rdf:resource="#Ticket"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#Ticket"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#Ticket"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="num">
-                          <rdfs:domain rdf:resource="#Version"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description_format">
-                          <rdfs:domain rdf:resource="#Version"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="description">
-                          <rdfs:domain rdf:resource="#Version"/>
-                          <rdfs:range rdf:resource="xsd:string"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="starting_date">
-                          <rdfs:domain rdf:resource="#Version"/>
-                          <rdfs:range rdf:resource="xsd:date"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="prevision_date">
-                          <rdfs:domain rdf:resource="#Version"/>
-                          <rdfs:range rdf:resource="xsd:date"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="publication_date">
-                          <rdfs:domain rdf:resource="#Version"/>
-                          <rdfs:range rdf:resource="xsd:date"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="creation_date">
-                          <rdfs:domain rdf:resource="#Version"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty><owl:DatatypeProperty rdf:ID="modification_date">
-                          <rdfs:domain rdf:resource="#Version"/>
-                          <rdfs:range rdf:resource="xsd:dateTime"/>
-                       </owl:DatatypeProperty> </owl:Ontology></rdf:RDF> ''')
-        doc = etree.parse(valid)
-        owlschema.validate(doc)
-
-if __name__ == '__main__':
-    unittest_main()
-
--- a/web/test/unittest_urlpublisher.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_urlpublisher.py	Mon May 04 13:09:48 2009 +0200
@@ -41,32 +41,32 @@
 
     def test_rest_path(self):
         """tests the rest path resolution"""
-        ctrl, rset = self.process('EUser')
+        ctrl, rset = self.process('CWUser')
         self.assertEquals(ctrl, 'view')
-        self.assertEquals(rset.description[0][0], 'EUser')
+        self.assertEquals(rset.description[0][0], 'CWUser')
         self.assertEquals(rset.printable_rql(),
-                          "Any X,AA,AB,AC,AD ORDERBY AA WHERE X is EUser, X login AA, X firstname AB, X surname AC, X modification_date AD")
-        ctrl, rset = self.process('EUser/login/admin')
+                          "Any X,AA,AB,AC,AD ORDERBY AA WHERE X is CWUser, X login AA, X firstname AB, X surname AC, X modification_date AD")
+        ctrl, rset = self.process('CWUser/login/admin')
         self.assertEquals(ctrl, 'view')
         self.assertEquals(len(rset), 1)
-        self.assertEquals(rset.description[0][0], 'EUser')
-        self.assertEquals(rset.printable_rql(), 'Any X WHERE X is EUser, X login "admin"')
+        self.assertEquals(rset.description[0][0], 'CWUser')
+        self.assertEquals(rset.printable_rql(), 'Any X WHERE X is CWUser, X login "admin"')
         ctrl, rset = self.process('euser/admin')
         self.assertEquals(ctrl, 'view')
         self.assertEquals(len(rset), 1)
-        self.assertEquals(rset.description[0][0], 'EUser')
-        self.assertEquals(rset.printable_rql(), 'Any X WHERE X is EUser, X login "admin"')
+        self.assertEquals(rset.description[0][0], 'CWUser')
+        self.assertEquals(rset.printable_rql(), 'Any X WHERE X is CWUser, X login "admin"')
         ctrl, rset = self.process('euser/eid/%s'%rset[0][0])
         self.assertEquals(ctrl, 'view')
         self.assertEquals(len(rset), 1)
-        self.assertEquals(rset.description[0][0], 'EUser')
-        self.assertEquals(rset.printable_rql(), 'Any X WHERE X is EUser, X eid 5')
+        self.assertEquals(rset.description[0][0], 'CWUser')
+        self.assertEquals(rset.printable_rql(), 'Any X WHERE X is CWUser, X eid 5')
         # test non-ascii paths
-        ctrl, rset = self.process('EUser/login/%C3%BFsa%C3%BFe')
+        ctrl, rset = self.process('CWUser/login/%C3%BFsa%C3%BFe')
         self.assertEquals(ctrl, 'view')
         self.assertEquals(len(rset), 1)
-        self.assertEquals(rset.description[0][0], 'EUser')
-        self.assertEquals(rset.printable_rql(), u'Any X WHERE X is EUser, X login "ÿsaÿe"')
+        self.assertEquals(rset.description[0][0], 'CWUser')
+        self.assertEquals(rset.printable_rql(), u'Any X WHERE X is CWUser, X login "ÿsaÿe"')
         # test quoted paths
         ctrl, rset = self.process('BlogEntry/title/hell%27o')
         self.assertEquals(ctrl, 'view')
@@ -74,9 +74,9 @@
         self.assertEquals(rset.description[0][0], 'BlogEntry')
         self.assertEquals(rset.printable_rql(), u'Any X WHERE X is BlogEntry, X title "hell\'o"')
         # errors
-        self.assertRaises(NotFound, self.process, 'EUser/eid/30000')
+        self.assertRaises(NotFound, self.process, 'CWUser/eid/30000')
         self.assertRaises(NotFound, self.process, 'Workcases')
-        self.assertRaises(NotFound, self.process, 'EUser/inexistant_attribute/joe')
+        self.assertRaises(NotFound, self.process, 'CWUser/inexistant_attribute/joe')
     
     def test_action_path(self):
         """tests the action path resolution"""
@@ -85,7 +85,7 @@
         self.assertRaises(Redirect, self.process, 'Tag/yo/edit')
         self.assertRaises(NotFound, self.process, 'view/edit')
         self.assertRaises(NotFound, self.process, '1/non_action')
-        self.assertRaises(NotFound, self.process, 'EUser/login/admin/non_action')
+        self.assertRaises(NotFound, self.process, 'CWUser/login/admin/non_action')
 
 
     def test_regexp_path(self):
--- a/web/test/unittest_urlrewrite.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_urlrewrite.py	Mon May 04 13:09:48 2009 +0200
@@ -28,7 +28,7 @@
             ('/manage', dict(vid='manage')),
             ('/notfound', {'vid': '404'}),
             ('/error', {'vid': 'error'}),
-            ('/schema/([^/]+?)/?$', {'rql': r'Any X WHERE X is EEType, X name "\1"', 'vid': 'eschema'}),
+            ('/schema/([^/]+?)/?$', {'rql': r'Any X WHERE X is CWEType, X name "\1"', 'vid': 'eschema'}),
             ('/add/([^/]+?)/?$' , dict(vid='creation', etype=r'\1')),
             ('/doc/images/(.+?)/?$', dict(fid='\\1', vid='wdocimages')),
             ('/doc/?$', dict(fid='main', vid='wdoc')),
--- a/web/test/unittest_views_actions.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_views_actions.py	Mon May 04 13:09:48 2009 +0200
@@ -4,11 +4,11 @@
 
 class ActionsTC(EnvBasedTC):
     def test_view_action(self):
-        req = self.request(__message='bla bla bla', vid='rss', rql='EUser X')
-        rset = self.execute('EUser X')
+        req = self.request(__message='bla bla bla', vid='rss', rql='CWUser X')
+        rset = self.execute('CWUser X')
         vaction = [action for action in self.vreg.possible_vobjects('actions', req, rset)
                    if action.id == 'view'][0]
-        self.assertEquals(vaction.url(), 'http://testing.fr/cubicweb/view?rql=EUser%20X')
+        self.assertEquals(vaction.url(), 'http://testing.fr/cubicweb/view?rql=CWUser%20X')
 
     def test_sendmail_action(self):
         req = self.request()
--- a/web/test/unittest_views_basecontrollers.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_views_basecontrollers.py	Mon May 04 13:09:48 2009 +0200
@@ -11,34 +11,34 @@
 from cubicweb.common.uilib import rql_for_eid
 
 from cubicweb.web import INTERNAL_FIELD_VALUE, Redirect, RequestError
-from cubicweb.web.views.basecontrollers import xmlize
+from cubicweb.web.views.basecontrollers import xhtml_wrap
 
-from cubicweb.entities.authobjs import EUser
+from cubicweb.entities.authobjs import CWUser
 
 
 class EditControllerTC(ControllerTC):
     def setUp(self):
         ControllerTC.setUp(self)
-        self.failUnless('users' in self.schema.eschema('EGroup').get_groups('read'))
-        
+        self.failUnless('users' in self.schema.eschema('CWGroup').get_groups('read'))
+
     def tearDown(self):
         ControllerTC.tearDown(self)
-        self.failUnless('users' in self.schema.eschema('EGroup').get_groups('read'))
-        
+        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)
-        
+
     def test_validation_unique(self):
         """test creation of two linked entities
-        """        
+        """
         user = self.user()
-        self.req.form = {'eid': 'X', '__type:X': 'EUser',
-                         'login:X': u'admin', 'edits-login:X': u'', 
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'toto', 'edits-upassword:X': u'', 
+        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)
 
@@ -47,13 +47,13 @@
         """checking that a manager user can edit itself
         """
         user = self.user()
-        basegroups = [str(eid) for eid, in self.execute('EGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
-        groupeids = [eid for eid, in self.execute('EGroup G WHERE G name in ("managers", "users")')]
+        basegroups = [str(eid) for eid, in self.execute('CWGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
+        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 = {
             'eid':       `user.eid`,
-            '__type:'+`user.eid`:    'EUser',
+            '__type:'+`user.eid`:    'CWUser',
             'login:'+`user.eid`:     unicode(user.login),
             'firstname:'+`user.eid`: u'Th\xe9nault',
             'surname:'+`user.eid`:   u'Sylvain',
@@ -78,10 +78,10 @@
         user = self.create_user('user')
         cnx = self.login('user')
         req = self.request()
-        #self.assertEquals(self.ctrl.schema['EUser']._groups['read'],
+        #self.assertEquals(self.ctrl.schema['CWUser']._groups['read'],
         #                  ('managers', 'users'))
         req.form = {
-            'eid': `user.eid`, '__type:'+`user.eid`: 'EUser',
+            'eid': `user.eid`, '__type:'+`user.eid`: 'CWUser',
             '__maineid' : str(user.eid),
             'upassword:'+`user.eid`: 'tournicoton',
             'upassword-confirm:'+`user.eid`: 'tournicoton',
@@ -97,10 +97,10 @@
         relations (meaning no changes)
         """
         user = self.user()
-        groupeids = [eid for eid, in self.execute('EGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
+        groupeids = [eid for eid, in self.execute('CWGroup G WHERE X in_group G, X eid %(x)s', {'x': user.eid})]
         self.req.form = {
             'eid':       `user.eid`,
-            '__type:'+`user.eid`:    'EUser',
+            '__type:'+`user.eid`:    'CWUser',
             'login:'+`user.eid`:     unicode(user.login),
             'firstname:'+`user.eid`: u'Th\xe9nault',
             'surname:'+`user.eid`:   u'Sylvain',
@@ -117,23 +117,23 @@
         self.assertEquals([g.eid for g in e.in_group], groupeids)
         stateeids = [eid for eid, in self.execute('State S WHERE S name "activated"')]
         self.assertEquals([s.eid for s in e.in_state], stateeids)
-        
-        
+
+
     def test_create_multiple_linked(self):
-        gueid = self.execute('EGroup G WHERE G name "users"')[0][0]
+        gueid = self.execute('CWGroup G WHERE G name "users"')[0][0]
         self.req.form = {'eid': ['X', 'Y'],
-                         
-                         '__type:X': 'EUser',
+
+                         '__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'', 
+                         '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': '',
 
-                         'in_group:X': `gueid`, 'edits-in_group:X': INTERNAL_FIELD_VALUE, 
-                         
+                         '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, 
+                         'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
                          }
         path, params = self.expect_redirect_publish()
         # should be redirected on the created person
@@ -142,17 +142,17 @@
         self.assertEquals(e.surname, 'Di Mascio')
         email = e.use_email[0]
         self.assertEquals(email.address, 'dima@logilab.fr')
-        
+
     def test_edit_multiple_linked(self):
         peid = self.create_user('adim').eid
         self.req.form = {'eid': [`peid`, 'Y'],
-                         '__type:%s'%peid: 'EUser',
+                         '__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,
-                         
+
                          '__redirectrql': 'Any X WHERE X eid %s'%peid,
                          }
         path, params = self.expect_redirect_publish()
@@ -162,14 +162,14 @@
         self.assertEquals(e.surname, 'Di Masci')
         email = e.use_email[0]
         self.assertEquals(email.address, 'dima@logilab.fr')
-        
+
         emaileid = email.eid
         self.req.form = {'eid': [`peid`, `emaileid`],
-                         '__type:%s'%peid: 'EUser',
+                         '__type:%s'%peid: 'CWUser',
                          'surname:%s'%peid: u'Di Masci', 'edits-surname:%s'%peid: 'Di Masci',
                          '__type:%s'%emaileid: 'EmailAddress',
                          'address:%s'%emaileid: u'adim@logilab.fr', 'edits-address:%s'%emaileid: 'dima@logilab.fr',
-                         'use_email:%s'%peid: `emaileid`, 'edits-use_email:%s'%peid: `emaileid`, 
+                         '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()
@@ -180,21 +180,21 @@
         email = e.use_email[0]
         self.assertEquals(email.address, 'adim@logilab.fr')
 
-        
+
     def test_password_confirm(self):
         """test creation of two linked entities
-        """        
+        """
         user = self.user()
         self.req.form = {'__cloned_eid:X': user.eid,
-                         'eid': 'X', '__type:X': 'EUser',
-                         'login:X': u'toto', 'edits-login:X': u'', 
-                         'upassword:X': u'toto', 'edits-upassword:X': u'', 
+                         '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': 'EUser',
-                         'login:X': u'toto', 'edits-login:X': u'', 
-                         'upassword:X': u'toto', 'upassword-confirm:X': u'tutu', 'edits-upassword:X': u'', 
+                         '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)
 
@@ -220,14 +220,14 @@
                          'described_by_test:X': str(feid), 'edits-described_by_test:X': INTERNAL_FIELD_VALUE,
                          }
         self.expect_redirect_publish()
-        # should be redirected on the created 
+        # should be redirected on the created
         #eid = params['rql'].split()[-1]
         e = self.execute('Salesterm X').get_entity(0, 0)
         self.assertEquals(e.amount, 10)
 
     def test_req_pending_insert(self):
         """make sure req's pending insertions are taken into account"""
-        tmpgroup = self.add_entity('EGroup', name=u"test")
+        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()
@@ -240,7 +240,7 @@
     def test_req_pending_delete(self):
         """make sure req's pending deletions are taken into account"""
         user = self.user()
-        groupeid = self.execute('INSERT EGroup G: G name "test", U in_group G WHERE U eid %(x)s',
+        groupeid = self.execute('INSERT CWGroup G: G name "test", U in_group G WHERE U eid %(x)s',
                                 {'x': user.eid})[0][0]
         usergroups = [gname for gname, in
                       self.execute('Any N WHERE G name N, U in_group G, U eid %(u)s', {'u': user.eid})]
@@ -258,13 +258,13 @@
         def custom_login_edit(self, formparams, value, relations):
             formparams['login'] = value.upper()
             relations.append('X login %(login)s')
-        EUser.custom_login_edit = custom_login_edit
+        CWUser.custom_login_edit = custom_login_edit
         try:
             user = self.user()
             eid = repr(user.eid)
             self.req.form = {
                 'eid': eid,
-                '__type:'+eid:  'EUser',
+                '__type:'+eid:  'CWUser',
                 'login:'+eid: u'foo',
                 'edits-login:'+eid:  unicode(user.login),
                 }
@@ -272,8 +272,8 @@
             rset = self.execute('Any L WHERE X eid %(x)s, X login L', {'x': user.eid}, 'x')
             self.assertEquals(rset[0][0], 'FOO')
         finally:
-            del EUser.custom_login_edit
-        
+            del CWUser.custom_login_edit
+
     def test_redirect_apply_button(self):
         redirectrql = rql_for_eid(4012) # whatever
         self.req.form = {
@@ -342,74 +342,74 @@
         self.assertEquals(params, {u'__message': u'entities deleted'})
 
     def test_nonregr_egroup_etype_editing(self):
-        """non-regression test checking that a manager user can edit a EEType entity (EGroup)
+        """non-regression test checking that a manager user can edit a CWEType entity (CWGroup)
         """
-        groupeids = [eid for eid, in self.execute('EGroup G WHERE G name "managers"')]
+        groupeids = [eid for eid, in self.execute('CWGroup G WHERE G name "managers"')]
         groups = [str(eid) for eid in groupeids]
-        eeetypeeid = self.execute('EEType X WHERE X name "EGroup"')[0][0]
-        basegroups = [str(eid) for eid, in self.execute('EGroup G WHERE X read_permission G, X eid %(x)s', {'x': eeetypeeid})]
+        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`:   'EEType',
-                'name:'+`eeetypeeid`:     u'EGroup',
+                '__type:'+`eeetypeeid`:   'CWEType',
+                'name:'+`eeetypeeid`:     u'CWGroup',
                 'final:'+`eeetypeeid`:    False,
                 'meta:'+`eeetypeeid`:     True,
-                'description:'+`eeetypeeid`:     u'users group', 
+                'description:'+`eeetypeeid`:     u'users group',
                 'read_permission:'+`eeetypeeid`:  groups,
                 #
-                'edits-name:'+`eeetypeeid`:     u'EGroup',
+                'edits-name:'+`eeetypeeid`:     u'CWGroup',
                 'edits-final:'+`eeetypeeid`:    False,
                 'edits-meta:'+`eeetypeeid`:     True,
-                'edits-description:'+`eeetypeeid`:     u'users group', 
+                'edits-description:'+`eeetypeeid`:     u'users group',
                 'edits-read_permission:'+`eeetypeeid`:  basegroups,
                 }
         try:
             path, params = self.expect_redirect_publish()
             e = self.execute('Any X WHERE X eid %(x)s', {'x': eeetypeeid}, 'x').get_entity(0, 0)
-            self.assertEquals(e.name, 'EGroup')
+            self.assertEquals(e.name, 'CWGroup')
             self.assertEquals([g.eid for g in e.read_permission], groupeids)
         finally:
             # restore
-            self.execute('SET X read_permission Y WHERE X name "EGroup", Y eid IN (%s), NOT X read_permission Y' % (','.join(basegroups)))
+            self.execute('SET X read_permission Y WHERE X name "CWGroup", Y eid IN (%s), NOT X read_permission Y' % (','.join(basegroups)))
             self.commit()
-            
+
     def test_nonregr_eetype_etype_editing(self):
-        """non-regression test checking that a manager user can edit a EEType entity (EEType)
+        """non-regression test checking that a manager user can edit a CWEType entity (CWEType)
         """
-        groupeids = sorted(eid for eid, in self.execute('EGroup G WHERE G name in ("managers", "users")'))
+        groupeids = sorted(eid for eid, in self.execute('CWGroup G WHERE G name in ("managers", "users")'))
         groups = [str(eid) for eid in groupeids]
-        eeetypeeid = self.execute('EEType X WHERE X name "EEType"')[0][0]
-        basegroups = [str(eid) for eid, in self.execute('EGroup G WHERE X read_permission G, X eid %(x)s', {'x': eeetypeeid})]
+        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`:  'EEType',
-                'name:'+`eeetypeeid`:     u'EEType',
+                '__type:'+`eeetypeeid`:  'CWEType',
+                'name:'+`eeetypeeid`:     u'CWEType',
                 'final:'+`eeetypeeid`:    False,
                 'meta:'+`eeetypeeid`:     True,
-                'description:'+`eeetypeeid`:     u'users group', 
+                'description:'+`eeetypeeid`:     u'users group',
                 'read_permission:'+`eeetypeeid`:  groups,
 
-                'edits-name:'+`eeetypeeid`:     u'EEType',
+                'edits-name:'+`eeetypeeid`:     u'CWEType',
                 'edits-final:'+`eeetypeeid`:    False,
                 'edits-meta:'+`eeetypeeid`:     True,
-                'edits-description:'+`eeetypeeid`:     u'users group', 
+                'edits-description:'+`eeetypeeid`:     u'users group',
                 'edits-read_permission:'+`eeetypeeid`:  basegroups,
                 }
         try:
             path, params = self.expect_redirect_publish()
             e = self.execute('Any X WHERE X eid %(x)s', {'x': eeetypeeid}, 'x').get_entity(0, 0)
-            self.assertEquals(e.name, 'EEType')
+            self.assertEquals(e.name, 'CWEType')
             self.assertEquals(sorted(g.eid for g in e.read_permission), groupeids)
         finally:
             # restore
-            self.execute('SET X read_permission Y WHERE X name "EEType", Y eid IN (%s), NOT X read_permission Y' % (','.join(basegroups)))
+            self.execute('SET X read_permission Y WHERE X name "CWEType", Y eid IN (%s), NOT X read_permission Y' % (','.join(basegroups)))
             self.commit()
-        
+
     def test_nonregr_strange_text_input(self):
         """non-regression test checking text input containing "13:03:43"
 
         this seems to be postgres (tsearch?) specific
-        """        
+        """
         self.req.form = {
                          'eid': 'A', '__type:A': 'BlogEntry',
                          '__maineid' : 'A',
@@ -424,32 +424,32 @@
 
 
     def test_nonregr_multiple_empty_email_addr(self):
-        gueid = self.execute('EGroup G WHERE G name "users"')[0][0]
+        gueid = self.execute('CWGroup G WHERE G name "users"')[0][0]
         self.req.form = {'eid': ['X', 'Y'],
-                         
-                         '__type:X': 'EUser',
-                         '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: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, 
+                         'use_email:X': 'Y', 'edits-use_email:X': INTERNAL_FIELD_VALUE,
                          }
         self.assertRaises(ValidationError, self.publish, self.req)
 
     def test_nonregr_copy(self):
         user = self.user()
         self.req.form = {'__cloned_eid:X': user.eid,
-                         'eid': 'X', '__type:X': 'EUser',
+                         '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'', 
+                         '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()
         self.assertEquals(path, 'euser/toto')
-        e = self.execute('Any X WHERE X is EUser, X login "toto"').get_entity(0, 0)
+        e = self.execute('Any X WHERE X is CWUser, X login "toto"').get_entity(0, 0)
         self.assertEquals(e.login, 'toto')
         self.assertEquals(e.in_group[0].name, 'managers')
 
@@ -464,8 +464,8 @@
             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': 'EUser',
-                             'login': u'dodo', 'edits-login': u'dodo', 
+                             'eid': 'X', '__type:X': 'CWUser',
+                             'login': u'dodo', 'edits-login': u'dodo',
                              'surname:X': u'Boom', 'edits-surname:X': u'',
                              '__errorurl' : "whatever but required",
                              }
@@ -479,7 +479,7 @@
                 self.req.form['rql'] = 'Any X WHERE X eid %s' % p.eid
                 self.req.form['vid'] = 'copy'
                 self.env.app.publish('view', self.req)
-            rset = self.execute('EUser P WHERE P surname "Boom"')
+            rset = self.execute('CWUser P WHERE P surname "Boom"')
             self.assertEquals(len(rset), 0)
         finally:
             p.__class__.skip_copy_for = old_skips
@@ -510,7 +510,7 @@
         self.login('anon')
         req = self.request()
         self.assertRaises(Unauthorized, self.env.app.select_controller, 'sendmail', req)
-   
+
 
 
 class JSONControllerTC(EnvBasedTC):
@@ -527,10 +527,10 @@
 
     ## tests ##################################################################
     def test_simple_exec(self):
-        ctrl = self.ctrl(self.request(rql='EUser P WHERE P login "John"',
+        ctrl = self.ctrl(self.request(rql='CWUser P WHERE P login "John"',
                                       pageid='123'))
         self.assertTextEquals(ctrl.publish(),
-                              xmlize(self.john.view('primary')))
+                              xhtml_wrap(self.john.view('primary')))
 
     def test_json_exec(self):
         rql = 'Any T,N WHERE T is Tag, T name N'
@@ -542,26 +542,26 @@
         self.remote_call('tag_entity', self.john.eid, ['python'])
         self.assertUnorderedIterableEquals([tname for tname, in self.execute('Any N WHERE T is Tag, T name N')],
                              ['python', 'cubicweb'])
-        self.assertEquals(self.execute('Any N WHERE T tags P, P is EUser, T name N').rows,
+        self.assertEquals(self.execute('Any N WHERE T tags P, P is CWUser, T name N').rows,
                           [['python']])
-    
+
     def test_remote_add_new_tag(self):
         self.remote_call('tag_entity', self.john.eid, ['javascript'])
         self.assertUnorderedIterableEquals([tname for tname, in self.execute('Any N WHERE T is Tag, T name N')],
                              ['python', 'cubicweb', 'javascript'])
-        self.assertEquals(self.execute('Any N WHERE T tags P, P is EUser, T name N').rows,
+        self.assertEquals(self.execute('Any N WHERE T tags P, P is CWUser, T name N').rows,
                           [['javascript']])
 
     def test_edit_field(self):
-        nbusers = len(self.execute('EUser P'))
+        nbusers = len(self.execute('CWUser P'))
         eid = self.john.eid
         self.remote_call('edit_field', 'apply',
                          ('eid', 'firstname:%s' % eid, '__maineid', '__type:%s'% eid, 'edits-firstname:%s' % eid ),
-                         (str(eid), u'Remi', str(eid), 'EUser', self.john.firstname),
+                         (str(eid), u'Remi', str(eid), 'CWUser', self.john.firstname),
                          'firstname',
                          eid)
         self.commit()
-        rset = self.execute('EUser P')
+        rset = self.execute('CWUser P')
         # make sure we did not insert a new euser here
         self.assertEquals(len(rset), nbusers)
         john = self.execute('Any X WHERE X eid %(x)s', {'x': self.john.eid}, 'x').get_entity(0, 0)
@@ -617,7 +617,7 @@
         req.remove_pending_operations()
         self.assertEquals(req.get_pending_deletes(), [])
         self.assertEquals(req.get_pending_inserts(), [])
-        
+
 
     def test_add_inserts(self):
         res, req = self.remote_call('add_pending_inserts',
@@ -625,7 +625,7 @@
         inserts = req.get_pending_inserts()
         self.assertEquals(inserts, ['12:tags:13', '12:tags:14'])
         req.remove_pending_operations()
-        
+
 
     # silly tests
     def test_external_resource(self):
@@ -639,8 +639,8 @@
         self.assertEquals(self.remote_call('format_date', '"2007-01-01 12:00:00"')[0],
                           simplejson.dumps('2007/01/01'))
 
-        
+
 
-        
+
 if __name__ == '__main__':
     unittest_main()
--- a/web/test/unittest_views_baseforms.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_views_baseforms.py	Mon May 04 13:09:48 2009 +0200
@@ -1,26 +1,25 @@
 """cubicweb.web.views.baseforms unit tests"""
 
 from StringIO import StringIO
+from datetime import date
 import re
 
+
 from logilab.common.testlib import unittest_main
+from logilab.common.decorators import clear_cache
 from cubicweb.devtools.apptest import EnvBasedTC
-
 from cubicweb.entities import AnyEntity
+from cubicweb.web import widgets
 
-from mx.DateTime import DateTime
-from cubicweb.web import widgets
-orig_today = widgets.today
-orig_now = widgets.now
+orig_now = widgets.datetime.now
 
 def setup_module(options):
     def _today():
-        return DateTime(0000, 1, 1)
-    widgets.today = widgets.now = _today
+        return date(0000, 1, 1)
+    widgets.datetime.now = _today
 
 def teardown_module(options, results):
-    widgets.today = orig_today
-    widgets.now = orig_now
+    widgets.datetime.now = orig_now
 
 
 def cleanup_text(text):
@@ -50,7 +49,7 @@
         self.assertTextEquals(expected, cleanup_text(self._build_creation_form(etype)))
         
     def test_base(self):
-        self._test_view_for('EGroup', '''\
+        self._test_view_for('CWGroup', '''\
 <form id="entityForm" class="entityForm" cubicweb:target="eformframe"
 method="post" onsubmit="return freezeFormButtons('entityForm')" enctype="application/x-www-form-urlencoded" action="http://testing.fr/cubicweb/validateform">
 <div class="formTitle"><span>egroup (creation)</span></div>
@@ -58,7 +57,7 @@
 <div class="iformTitle"><span>main informations</span></div>
 <div class="formBody"><fieldset>
 <input type="hidden" name="eid" value="A" />
-<input type="hidden" name="__type:A" value="EGroup" />
+<input type="hidden" name="__type:A" value="CWGroup" />
 <input type="hidden" name="__maineid" value="A" />
 <input id="errorurl" type="hidden" name="__errorurl" value="http://testing.fr/cubicweb/view?rql=Blop&amp;vid=blop" />
 <input type="hidden" name="__form_id" value="edition" />
@@ -89,14 +88,14 @@
 
     def test_with_inline_view(self):
         activated = self.execute('Any X WHERE X is State, X name "activated"')[0][0]
-        self._test_view_for('EUser', '''<form id="entityForm" class="entityForm" cubicweb:target="eformframe"
+        self._test_view_for('CWUser', '''<form id="entityForm" class="entityForm" cubicweb:target="eformframe"
 method="post" onsubmit="return freezeFormButtons('entityForm')" enctype="application/x-www-form-urlencoded" action="http://testing.fr/cubicweb/validateform">
 <div class="formTitle"><span>euser (creation)</span></div>
 <div id="progress">validating...</div>
 <div class="iformTitle"><span>main informations</span></div>
 <div class="formBody"><fieldset>
 <input type="hidden" name="eid" value="A" />
-<input type="hidden" name="__type:A" value="EUser" />
+<input type="hidden" name="__type:A" value="CWUser" />
 <input type="hidden" name="__maineid" value="A" />
 <input id="errorurl" type="hidden" name="__errorurl" value="http://testing.fr/cubicweb/view?rql=Blop&amp;vid=blop" />
 <input type="hidden" name="__form_id" value="edition" />
@@ -160,7 +159,7 @@
 </table>
 <div id="inlineuse_emailslot">
 <div class="inlinedform" id="addNewEmailAddressuse_emailsubject:A" cubicweb:limit="true">
-<a class="addEntity" id="adduse_email:Alink" href="javascript: addInlineCreationForm('A', 'EUser', 'EmailAddress', 'use_email', 'subject')" >+ add a EmailAddress.</a>
+<a class="addEntity" id="adduse_email:Alink" href="javascript: addInlineCreationForm('A', 'CWUser', 'EmailAddress', 'use_email', 'subject')" >+ add a EmailAddress.</a>
 </div>
 <div class="trame_grise">&nbsp;</div>
 </div>
@@ -180,7 +179,7 @@
 
     def test_redirection_after_creation(self):
         req = self.request()
-        req.form['etype'] = 'EUser'
+        req.form['etype'] = 'CWUser'
         view = self.vreg.select_view('creation', req, None)
         self.assertEquals(view.redirect_url(), 'http://testing.fr/cubicweb/euser')
         req.form['__redirectrql'] = 'Any X WHERE X eid 3012'
@@ -207,6 +206,7 @@
         class BlogEntryPlus(BlogEntry):
             __rtags__ = {'checked_by': 'primary'}
         self.vreg.register_vobject_class(BlogEntryPlus)
+        clear_cache(self.vreg, 'etype_class')
         # an admin should be able to edit the checked_by relation
         html = self._build_creation_form('BlogEntry')
         self.failUnless('name="edits-checked_by:A"' in html)
@@ -229,7 +229,7 @@
 
 
     def test_cloned_elements_in_copy_form(self):
-        rset = self.execute('EUser P WHERE P login "Doe"')
+        rset = self.execute('CWUser P WHERE P login "Doe"')
         output = self.view('copy', rset)
         clones = [attrs for _, attrs in output.input_tags
                   if attrs.get('name', '').startswith('__cloned_eid')]
--- a/web/test/unittest_views_baseviews.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_views_baseviews.py	Mon May 04 13:09:48 2009 +0200
@@ -39,7 +39,7 @@
         
     def test_more_than_one_entity(self):
         req = self.request()
-        rset = self.execute('Any X WHERE X is EUser')
+        rset = self.execute('Any X WHERE X is CWUser')
         self.assertEquals(vid_from_rset(req, rset, self.schema), 'list')
         rset = self.execute('Any X, L WHERE X login L')
         self.assertEquals(vid_from_rset(req, rset, self.schema), 'list')
@@ -58,16 +58,16 @@
         req = self.request()
         rset = self.execute('Any X, COUNT(T) GROUPBY X WHERE X is T')
         self.assertEquals(vid_from_rset(req, rset, self.schema), 'table')
-        rset = self.execute('Any MAX(X) WHERE X is EUser')
+        rset = self.execute('Any MAX(X) WHERE X is CWUser')
         self.assertEquals(vid_from_rset(req, rset, self.schema), 'table')
 
     def test_subquery(self):
         rset = self.execute(
 'DISTINCT Any X,N ORDERBY N '
 'WITH X,N BEING ('
-'     (DISTINCT Any P,N WHERE P is EUser, P login N)'
+'     (DISTINCT Any P,N WHERE P is CWUser, P login N)'
 '       UNION'
-'     (DISTINCT Any W,N WHERE W is EGroup, W name N))')
+'     (DISTINCT Any W,N WHERE W is CWGroup, W name N))')
         req = self.request()
         self.assertEquals(vid_from_rset(req, rset, self.schema), 'table')
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/test/unittest_views_editforms.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,153 @@
+from logilab.common.testlib import unittest_main
+from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb.devtools.testlib import WebTest
+from cubicweb.web.views.editforms 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):
+
+    def test_custom_widget(self):
+        AEF.rwidgets.tag_relation(AutoCompletionWidget,
+                                  ('CWUser', 'login', '*'), 'subject')
+        form = self.vreg.select_object('forms', 'edition', self.request(), None,
+                                       entity=self.user())
+        field = form.field_by_name('login')
+        self.assertIsInstance(field.widget, AutoCompletionWidget)
+        AEF.rwidgets.del_rtag('login', 'subject', 'CWUser')
+
+
+    def test_euser_relations_by_category(self):
+        #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')
+        # see custom configuration in views.euser
+        self.assertEquals(rbc(e, 'primary'),
+                          [('login', 'subject'),
+                           ('upassword', 'subject'),
+                           ('in_group', 'subject'),
+                           ('in_state', 'subject'),
+                           ('eid', 'subject'),
+                           ])
+        self.assertListEquals(rbc(e, 'secondary'),
+                              [('firstname', 'subject'),
+                               ('surname', 'subject')
+                               ])
+        self.assertListEquals(rbc(e, 'metadata'),
+                              [('last_login_time', 'subject'),
+                               ('created_by', 'subject'),
+                               ('creation_date', 'subject'),
+                               ('modification_date', 'subject'),
+                               ('owned_by', 'subject'),
+                               ('bookmarked_by', 'object'),
+                               ])
+        self.assertListEquals(rbc(e, 'generic'),
+                              [('primary_email', 'subject'),
+                               ('use_email', 'subject'),
+                               ('connait', 'subject'),
+                               ('checked_by', 'object'),
+                               ])
+        # owned_by is defined both as subject and object relations on CWUser
+        self.assertListEquals(rbc(e, 'generated'),
+                              [('has_text', 'subject'),
+                               ('identity', 'subject'),
+                               ('is', 'subject'),
+                               ('is_instance_of', 'subject'),
+                               ('tags', 'object'),
+                               ('for_user', 'object'),
+                               ('created_by', 'object'),
+                               ('wf_info_for', 'object'),
+                               ('owned_by', 'object'),
+                               ('identity', 'object'),
+                               ])
+
+    def test_inlined_view(self):
+        self.failUnless(AEF.rinlined.etype_get('CWUser', 'use_email', 'subject'))
+        self.failIf(AEF.rinlined.etype_get('CWUser', 'primary_email', 'subject'))
+
+    def test_personne_relations_by_category(self):
+        e = self.etype_instance('Personne')
+        self.assertListEquals(rbc(e, 'primary'),
+                              [('nom', 'subject'),
+                               ('eid', 'subject')
+                               ])
+        self.assertListEquals(rbc(e, 'secondary'),
+                              [('prenom', 'subject'),
+                               ('sexe', 'subject'),
+                               ('promo', 'subject'),
+                               ('titre', 'subject'),
+                               ('ass', 'subject'),
+                               ('web', 'subject'),
+                               ('tel', 'subject'),
+                               ('fax', 'subject'),
+                               ('datenaiss', 'subject'),
+                               ('test', 'subject'),
+                               ('description', 'subject'),
+                               ('salary', 'subject')
+                               ])
+        self.assertListEquals(rbc(e, 'metadata'),
+                              [('created_by', 'subject'),
+                               ('creation_date', 'subject'),
+                               ('modification_date', 'subject'),
+                               ('owned_by', 'subject'),
+                               ])
+        self.assertListEquals(rbc(e, 'generic'),
+                              [('travaille', 'subject'),
+                               ('connait', 'object')
+                               ])
+        self.assertListEquals(rbc(e, 'generated'),
+                              [('has_text', 'subject'),
+                               ('identity', 'subject'),
+                               ('is', 'subject'),
+                               ('is_instance_of', 'subject'),
+                               ('identity', 'object'),
+                               ])
+
+    def test_edition_form(self):
+        rset = self.execute('CWUser X LIMIT 1')
+        form = self.vreg.select_object('forms', 'edition', rset.req, rset,
+                                       row=0, col=0)
+        # should be also selectable by specifying entity
+        self.vreg.select_object('forms', 'edition', self.request(), None,
+                                entity=rset.get_entity(0, 0))
+        self.failIf(any(f for f in form.fields if f is None))
+
+
+class FormViewsTC(WebTest):
+    def test_delete_conf_formview(self):
+        rset = self.execute('CWGroup X')
+        self.view('deleteconf', rset, template=None).source
+
+    def test_automatic_edition_formview(self):
+        rset = self.execute('CWUser X')
+        self.view('edition', rset, row=0, template=None).source
+
+    def test_automatic_edition_formview(self):
+        rset = self.execute('CWUser X')
+        self.view('copy', rset, row=0, template=None).source
+
+    def test_automatic_creation_formview(self):
+        self.view('creation', None, etype='CWUser', template=None).source
+
+    def test_automatic_muledit_formview(self):
+        rset = self.execute('CWUser X')
+        self.view('muledit', rset, template=None).source
+
+    def test_automatic_reledit_formview(self):
+        rset = self.execute('CWUser X')
+        self.view('reledit', rset, row=0, rtype='login', template=None).source
+
+    def test_automatic_inline_edit_formview(self):
+        geid = self.execute('CWGroup X LIMIT 1')[0][0]
+        rset = self.execute('CWUser X LIMIT 1')
+        self.view('inline-edition', rset, row=0, rtype='in_group', peid=geid, template=None).source
+
+    def test_automatic_inline_creation_formview(self):
+        geid = self.execute('CWGroup X LIMIT 1')[0][0]
+        self.view('inline-creation', None, etype='CWUser', rtype='in_group', peid=geid, template=None).source
+
+
+if __name__ == '__main__':
+    unittest_main()
--- a/web/test/unittest_views_navigation.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_views_navigation.py	Mon May 04 13:09:48 2009 +0200
@@ -53,7 +53,7 @@
         rset = self.execute('Any RDEF ORDERBY RDEF WHERE RDEF relation_type RT')
         navcomp = self.vreg.select_component('navigation', req, rset)
         html = navcomp.dispatch()
-        rset = self.execute('EFRDef RDEF ORDERBY RDEF')
+        rset = self.execute('CWAttribute RDEF ORDERBY RDEF')
         navcomp = self.vreg.select_component('navigation', req, rset)
         html = navcomp.dispatch()
         rset = self.execute('Any RDEF ORDERBY N WHERE RDEF relation_type RT, RT name N')
@@ -69,7 +69,7 @@
 
     def test_component_context(self):
         view = mock_object(is_primary=lambda x: True)
-        rset = self.execute('EUser X LIMIT 1')
+        rset = self.execute('CWUser X LIMIT 1')
         req = self.request()
         objs = self.vreg.possible_vobjects('contentnavigation', req, rset,
                                            view=view, context='navtop')
@@ -81,7 +81,7 @@
         # breadcrumbs should _NOT_ be in footers by default
         clsids = set(obj.id for obj in objs)
         self.failIf('breadcrumbs' in clsids)
-        self.execute('INSERT EProperty P: P pkey "contentnavigation.breadcrumbs.context", '
+        self.execute('INSERT CWProperty P: P pkey "contentnavigation.breadcrumbs.context", '
                      'P value "navbottom"')
         # breadcrumbs should now be in footers
         req.cnx.commit()
--- a/web/test/unittest_views_searchrestriction.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_views_searchrestriction.py	Mon May 04 13:09:48 2009 +0200
@@ -28,25 +28,25 @@
     def test_1(self):
         self.assertEquals(self._generate(self.select, 'in_state', 'subject', 'name'),
                           "DISTINCT Any A,C ORDERBY C WHERE B in_group P, P name 'managers', "
-                          "B in_state A, A name C, B is EUser")
+                          "B in_state A, A name C, B is CWUser")
         
     def test_2(self):
         self.assertEquals(self._generate(self.select, 'tags', 'object', 'name'),
                           "DISTINCT Any A,C ORDERBY C WHERE B in_group P, P name 'managers', "
-                          "A tags B, A name C, B is EUser")
+                          "A tags B, A name C, B is CWUser")
         
     def test_3(self):
         self.assertEquals(self._generate(self.select, 'created_by', 'subject', 'login'),
                           "DISTINCT Any A,C ORDERBY C WHERE B in_group P, P name 'managers', "
-                          "B created_by A, A login C, B is EUser")
+                          "B created_by A, A login C, B is CWUser")
         
     def test_4(self):
-        self.assertEquals(self._generate(self.parse('Any X WHERE X is EUser'), 'created_by', 'subject', 'login'),
-                          "DISTINCT Any A,B ORDERBY B WHERE X is EUser, X created_by A, A login B")
+        self.assertEquals(self._generate(self.parse('Any X WHERE X is CWUser'), 'created_by', 'subject', 'login'),
+                          "DISTINCT Any A,B ORDERBY B WHERE X is CWUser, X created_by A, A login B")
         
     def test_5(self):
-        self.assertEquals(self._generate(self.parse('Any X,L WHERE X is EUser, X login L'), 'created_by', 'subject', 'login'),
-                          "DISTINCT Any A,B ORDERBY B WHERE X is EUser, X created_by A, A login B")
+        self.assertEquals(self._generate(self.parse('Any X,L WHERE X is CWUser, X login L'), 'created_by', 'subject', 'login'),
+                          "DISTINCT Any A,B ORDERBY B WHERE X is CWUser, X created_by A, A login B")
         
     def test_nonregr1(self):
         select = self.parse('Any T,V WHERE T bookmarked_by V?, '
@@ -57,14 +57,14 @@
 
     def test_nonregr2(self):
         #'DISTINCT Any X,TMP,N WHERE P name TMP, X version_of P, P is Project, X is Version, not X in_state S,S name "published", X num N ORDERBY TMP,N'
-        select = self.parse('DISTINCT Any V,TN,L ORDERBY TN,L WHERE T nom TN, V connait T, T is Personne, V is EUser,'
+        select = self.parse('DISTINCT Any V,TN,L ORDERBY TN,L WHERE T nom TN, V connait T, T is Personne, V is CWUser,'
                             'NOT V in_state VS, VS name "published", V login L')
         rschema = self.schema['connait']
         for s, o in rschema.iter_rdefs():
             rschema.set_rproperty(s, o, 'cardinality', '++')
         try:
             self.assertEquals(self._generate(select, 'in_state', 'subject', 'name'),
-                              "DISTINCT Any A,B ORDERBY B WHERE V is EUser, "
+                              "DISTINCT Any A,B ORDERBY B WHERE V is CWUser, "
                               "NOT V in_state VS, VS name 'published', "
                               "V in_state A, A name B")
         finally:
@@ -73,9 +73,9 @@
 
     def test_nonregr3(self):
         #'DISTINCT Any X,TMP,N WHERE P name TMP, X version_of P, P is Project, X is Version, not X in_state S,S name "published", X num N ORDERBY TMP,N'
-        select = self.parse('DISTINCT Any X, MAX(Y) GROUPBY X WHERE X is EUser, Y is Bookmark, X in_group A')
+        select = self.parse('DISTINCT Any X, MAX(Y) GROUPBY X WHERE X is CWUser, Y is Bookmark, X in_group A')
         self.assertEquals(self._generate(select, 'in_group', 'subject', 'name'),
-                          "DISTINCT Any B,C ORDERBY C WHERE X is EUser, X in_group B, B name C")
+                          "DISTINCT Any B,C ORDERBY C WHERE X is CWUser, X in_group B, B name C")
 
         
 if __name__ == '__main__':
--- a/web/test/unittest_viewselector.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_viewselector.py	Mon May 04 13:09:48 2009 +0200
@@ -1,32 +1,28 @@
 # -*- coding: iso-8859-1 -*-
 """XXX rename, split, reorganize this
 """
-
-import os.path as osp
+from __future__ import with_statement
 
-from logilab.common.testlib import TestCase, unittest_main
-from cubicweb.devtools.apptest import EnvBasedTC
-
-
-from cubicweb import CW_SOFTWARE_ROOT as BASE, Binary
-from cubicweb.common.selectors import match_user_group
+from logilab.common.testlib import unittest_main
 
-from cubicweb.web._exceptions import NoSelectableObject
+from cubicweb.devtools.apptest import EnvBasedTC
+from cubicweb import CW_SOFTWARE_ROOT as BASE, Binary
+from cubicweb.selectors import (match_user_groups, implements,
+                                specified_etype_implements, rql_condition,
+                                traced_selection)
+from cubicweb.web import NoSelectableObject
 from cubicweb.web.action import Action
-from cubicweb.web.views import (baseviews, tableview, baseforms, calendar, 
-                                management, embedding, actions, startup, 
-                                euser, schemaentities, xbel, vcard,
-                                treeview, idownloadable, wdoc, debug)
-from cubicweb.entities.lib import Card
-from cubicweb.interfaces import IMileStone
-from cubicweb.web.views import owl
+from cubicweb.web.views import (baseviews, tableview, baseforms, calendar,
+                                management, embedding, actions, startup,
+                                euser, schemaentities, xbel, vcard, owl,
+                                treeview, idownloadable, wdoc, debug, eproperties)
 
 USERACTIONS = [('myprefs', actions.UserPreferencesAction),
                ('myinfos', actions.UserInfoAction),
                ('logout', actions.LogoutAction)]
 SITEACTIONS = [('siteconfig', actions.SiteConfigurationAction),
                ('manage', actions.ManageAction),
-               ('schema', actions.ViewSchemaAction)]        
+               ('schema', actions.ViewSchemaAction)]
 
 
 class ViewSelectorTC(EnvBasedTC):
@@ -34,7 +30,6 @@
     def setup_database(self):
         self.add_entity('BlogEntry', title=u"une news !", content=u"cubicweb c'est beau")
         self.add_entity('Bookmark', title=u"un signet !", path=u"view?vid=index")
-        self.add_entity('Card', title=u'mandatory', content=u"DoC !")
         self.add_entity('EmailAddress', address=u"devel@logilab.fr", alias=u'devel')
         self.add_entity('Tag', name=u'x')
 
@@ -63,34 +58,28 @@
             print 'no more', [v for v in expected if not v in content.keys()]
             print 'missing', [v for v in content.keys() if not v in expected]
             raise
-        
-    
-    def test_possible_views(self):
-        # no entity
+
+
+    def test_possible_views_none_rset(self):
         req = self.request()
         self.assertListEqual(self.pviews(req, None),
                              [('changelog', wdoc.ChangeLogView),
                               ('debug', debug.DebugView),
-                              ('epropertiesform', management.EpropertiesForm),
+                              ('epropertiesform', eproperties.EPropertiesForm),
                               ('index', startup.IndexView),
                               ('info', management.ProcessInformationView),
                               ('manage', startup.ManageView),
                               ('owl', owl.OWLView),
                               ('schema', startup.SchemaView),
-                              ('systemepropertiesform', management.SystemEpropertiesForm)])
-        # no entity but etype
+                              ('systemepropertiesform', eproperties.SystemEPropertiesForm)])
+
+    def test_possible_views_noresult(self):
         rset, req = self.env.get_rset_and_req('Any X WHERE X eid 999999')
         self.assertListEqual(self.pviews(req, rset),
-                             [#('changelog', wdoc.ChangeLogView),
-                              #('epropertiesform', management.EpropertiesForm),
-                              #('index', startup.IndexView),
-                              #('info', management.ProcessInformationView),
-                              #('manage', startup.ManageView),
-                              #('schema', startup.SchemaView),
-                              #('systemepropertiesform', management.SystemEpropertiesForm)
-                                 ])
-        # one entity
-        rset, req = self.env.get_rset_and_req('EGroup X WHERE X name "managers"')
+                             [])
+
+    def test_possible_views_one_egroup(self):
+        rset, req = self.env.get_rset_and_req('CWGroup X WHERE X name "managers"')
         self.assertListEqual(self.pviews(req, rset),
                              [('csvexport', baseviews.CSVRsetView),
                               ('ecsvexport', baseviews.CSVEntityView),
@@ -110,8 +99,9 @@
                               ('xbel', xbel.XbelView),
                               ('xml', baseviews.XmlView),
                               ])
-        # list of entities of the same type
-        rset, req = self.env.get_rset_and_req('EGroup X')
+
+    def test_possible_views_multiple_egroups(self):
+        rset, req = self.env.get_rset_and_req('CWGroup X')
         self.assertListEqual(self.pviews(req, rset),
                              [('csvexport', baseviews.CSVRsetView),
                               ('ecsvexport', baseviews.CSVEntityView),
@@ -131,7 +121,8 @@
                               ('xbel', xbel.XbelView),
                               ('xml', baseviews.XmlView),
                               ])
-        # list of entities of different types
+
+    def test_possible_views_multiple_different_types(self):
         rset, req = self.env.get_rset_and_req('Any X')
         self.assertListEqual(self.pviews(req, rset),
                              [('csvexport', baseviews.CSVRsetView),
@@ -152,7 +143,8 @@
                               ('xbel', xbel.XbelView),
                               ('xml', baseviews.XmlView),
                               ])
-        # whatever
+
+    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')
         self.assertListEqual(self.pviews(req, rset),
                              [('csvexport', baseviews.CSVRsetView),
@@ -160,8 +152,9 @@
                               ('rsetxml', baseviews.XMLRsetView),
                               ('table', tableview.TableView),
                               ])
-        # list of euser entities
-        rset, req = self.env.get_rset_and_req('EUser X')
+
+    def test_possible_views_multiple_eusers(self):
+        rset, req = self.env.get_rset_and_req('CWUser X')
         self.assertListEqual(self.pviews(req, rset),
                              [('csvexport', baseviews.CSVRsetView),
                               ('ecsvexport', baseviews.CSVEntityView),
@@ -171,7 +164,7 @@
                               ('list', baseviews.ListView),
                               ('oneline', baseviews.OneLineView),
                               ('owlabox', owl.OWLABOXView),
-                              ('primary', euser.EUserPrimaryView),
+                              ('primary', euser.CWUserPrimaryView),
                               ('rsetxml', baseviews.XMLRsetView),
                               ('rss', baseviews.RssView),
                               ('secondary', baseviews.SecondaryView),
@@ -179,16 +172,17 @@
                               ('table', tableview.TableView),
                               ('text', baseviews.TextView),
                               ('treeview', treeview.TreeView),
-                              ('vcard', vcard.VCardEUserView),
+                              ('vcard', vcard.VCardCWUserView),
                               ('xbel', xbel.XbelView),
                               ('xml', baseviews.XmlView),
                               ])
-        
+
     def test_possible_actions_none_rset(self):
         req = self.request()
         self.assertDictEqual(self.pactions(req, None),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
+
                               })
     def test_possible_actions_no_entity(self):
         rset, req = self.env.get_rset_and_req('Any X WHERE X eid 999999')
@@ -196,8 +190,9 @@
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
                               })
+
     def test_possible_actions_same_type_entities(self):
-        rset, req = self.env.get_rset_and_req('EGroup X')
+        rset, req = self.env.get_rset_and_req('CWGroup X')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
@@ -205,6 +200,7 @@
                               'moreactions': [('delete', actions.DeleteAction),
                                               ('addentity', actions.AddNewAction)],
                               })
+
     def test_possible_actions_different_types_entities(self):
         rset, req = self.env.get_rset_and_req('Any X')
         self.assertDictEqual(self.pactions(req, rset),
@@ -212,53 +208,42 @@
                               'siteactions': SITEACTIONS,
                               'moreactions': [('delete', actions.DeleteAction)],
                               })
+
     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')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS})
-        
+
     def test_possible_actions_eetype_euser_entity(self):
-        rset, req = self.env.get_rset_and_req('EEType X WHERE X name "EUser"')
+        rset, req = self.env.get_rset_and_req('CWEType X WHERE X name "CWUser"')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
                               'mainactions': [('edit', actions.ModifyAction),
                                               ('workflow', schemaentities.ViewWorkflowAction),],
                               'moreactions': [('delete', actions.DeleteAction),
-                                              ('copy', actions.CopyAction)],
+                                              ('copy', actions.CopyAction),
+                                              ('managepermission', actions.ManagePermissionsAction)],
                               })
 
-    def test_load_subinterface_based_vojects(self):
-        self.vreg._lastmodifs = {} # clear cache
-        self.vreg.register_objects([osp.join(BASE, 'web', 'views', 'iprogress.py')])
-        # check progressbar was kicked
-        self.failIf('progressbar' in self.vreg['views'])
-        class MyCard(Card):
-            __implements__ = (IMileStone,)
-        self.vreg.register_vobject_class(MyCard)
-        self.vreg._lastmodifs = {} # clear cache
-        self.vreg.register_objects([osp.join(BASE, 'web', 'views', 'iprogress.py')])
-        # check progressbar isn't kicked
-        self.assertEquals(len(self.vreg['views']['progressbar']), 1)
-        
 
     def test_select_creation_form(self):
         rset = None
         req = self.request()
         # creation form
-        req.form['etype'] = 'EGroup'
+        req.form['etype'] = 'CWGroup'
         self.assertIsInstance(self.vreg.select_view('creation', req, rset),
                                   baseforms.CreationForm)
         del req.form['etype']
         # custom creation form
-        class EUserCreationForm(baseforms.CreationForm):
-            accepts = ('EUser',)
-        self.vreg.register_vobject_class(EUserCreationForm)
-        req.form['etype'] = 'EUser'
+        class CWUserCreationForm(baseforms.CreationForm):
+            __select__ = specified_etype_implements('CWUser')
+        self.vreg.register_vobject_class(CWUserCreationForm)
+        req.form['etype'] = 'CWUser'
         self.assertIsInstance(self.vreg.select_view('creation', req, rset),
-                              EUserCreationForm)
-            
+                              CWUserCreationForm)
+
     def test_select_view(self):
         # no entity
         rset = None
@@ -269,7 +254,7 @@
                              self.vreg.select_view, 'primary', req, rset)
         self.failUnlessRaises(NoSelectableObject,
                              self.vreg.select_view, 'table', req, rset)
-        
+
         # no entity
         rset, req = self.env.get_rset_and_req('Any X WHERE X eid 999999')
         self.failUnlessRaises(NoSelectableObject,
@@ -281,7 +266,7 @@
         self.failUnlessRaises(NoSelectableObject,
                              self.vreg.select_view, 'table', req, rset)
         # one entity
-        rset, req = self.env.get_rset_and_req('EGroup X WHERE X name "managers"')
+        rset, req = self.env.get_rset_and_req('CWGroup X WHERE X name "managers"')
         self.assertIsInstance(self.vreg.select_view('primary', req, rset),
                              baseviews.PrimaryView)
         self.assertIsInstance(self.vreg.select_view('list', req, rset),
@@ -295,7 +280,7 @@
         self.failUnlessRaises(NoSelectableObject,
                               self.vreg.select_view, 'index', req, rset)
         # list of entities of the same type
-        rset, req = self.env.get_rset_and_req('EGroup X')
+        rset, req = self.env.get_rset_and_req('CWGroup X')
         self.assertIsInstance(self.vreg.select_view('primary', req, rset),
                              baseviews.PrimaryView)
         self.assertIsInstance(self.vreg.select_view('list', req, rset),
@@ -331,7 +316,7 @@
         self.failUnlessRaises(NoSelectableObject,
                              self.vreg.select_view, 'edition', req, rset)
         # mixed query
-        rset, req = self.env.get_rset_and_req('Any U,G WHERE U is EUser, G is EGroup')
+        rset, req = self.env.get_rset_and_req('Any U,G WHERE U is CWUser, G is CWGroup')
         self.failUnlessRaises(NoSelectableObject,
                               self.vreg.select_view, 'edition', req, rset)
         self.failUnlessRaises(NoSelectableObject,
@@ -339,9 +324,9 @@
         self.assertIsInstance(self.vreg.select_view('table', req, rset),
                               tableview.TableView)
         # euser primary view priority
-        rset, req = self.env.get_rset_and_req('EUser X WHERE X login "admin"')
+        rset, req = self.env.get_rset_and_req('CWUser X WHERE X login "admin"')
         self.assertIsInstance(self.vreg.select_view('primary', req, rset),
-                             euser.EUserPrimaryView)
+                             euser.CWUserPrimaryView)
         self.assertIsInstance(self.vreg.select_view('text', req, rset),
                              baseviews.TextView)
 
@@ -351,8 +336,8 @@
         rset, req = self.env.get_rset_and_req('Image X WHERE X name "bim.png"')
         self.assertIsInstance(self.vreg.select_view('primary', req, rset),
                               idownloadable.IDownloadablePrimaryView)
-        
-        
+
+
     def test_score_entity_selector(self):
         image = self.add_entity('Image', name=u'bim.png', data=Binary('bim'))
         # image primary view priority
@@ -363,9 +348,9 @@
         # image primary view priority
         rset, req = self.env.get_rset_and_req('File X WHERE X name "bim.txt"')
         self.assertRaises(NoSelectableObject, self.vreg.select_view, 'image', req, rset)
-        
-        
-        
+
+
+
     def _test_view(self, vid, rql, args):
         if rql is None:
             rset = None
@@ -381,9 +366,9 @@
     def test_form(self):
         for vid, rql, args in (
             #('creation', 'Any X WHERE X eid 999999', {}),
-            ('edition', 'EGroup X WHERE X name "managers"', {}),
-            ('copy', 'EGroup X WHERE X name "managers"', {}),
-            ('muledit', 'EGroup X', {}),
+            ('edition', 'CWGroup X WHERE X name "managers"', {}),
+            ('copy', 'CWGroup X WHERE X name "managers"', {}),
+            ('muledit', 'CWGroup X', {}),
             #('muledit', 'Any X', {}),
             ):
             self._test_view(vid, rql, args)
@@ -403,78 +388,46 @@
         self.assertEquals(self.vreg.property_value('boxes.possible_views_box.visible'), False)
         self.assertEquals(self.vreg.property_value('boxes.possible_views_box.order'), 10)
         self.assertRaises(KeyError, self.vreg.property_value, 'boxes.actions_box')
-        
+
+
 
 
-    def test_owners_match_user_group(self):
-        """tests usage of 'owners' group with match_user_group"""
-        class SomeAction(Action):
-            id = 'yo'
-            category = 'foo'
-            __selectors__ = (match_user_group,)
-            require_groups = ('owners', )            
-        self.vreg.register_vobject_class(SomeAction)
-        self.failUnless(SomeAction in self.vreg['actions']['yo'], self.vreg['actions'])
-        try:
-            # login as a simple user
-            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 EGroup, G name "managers"')
-            self.failIf('foo' in 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"')
-            self.failUnless('foo' in 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"')
-            self.failIf('foo' in self.pactions(req, rset))
-        finally:
-            del self.vreg[SomeAction.__registry__][SomeAction.id]
-
-
-        
-
-
-from cubicweb.web.action import EntityAction
-
-class EETypeRQLAction(EntityAction):
+class CWETypeRQLAction(Action):
     id = 'testaction'
-    accepts = ('EEType',)
-    condition = 'X name "EEType"'
+    __select__ = implements('CWEType') & rql_condition('X name "CWEType"')
     title = 'bla'
 
 class RQLActionTC(ViewSelectorTC):
-            
+
     def setUp(self):
         super(RQLActionTC, self).setUp()
-        self.vreg.register_vobject_class(EETypeRQLAction)
-        
+        self.vreg.register_vobject_class(CWETypeRQLAction)
+
     def tearDown(self):
-        super(RQLActionTC, self).tearDown()        
+        super(RQLActionTC, self).tearDown()
         del self.vreg._registries['actions']['testaction']
-        
+
     def test(self):
-        rset, req = self.env.get_rset_and_req('EEType X WHERE X name "EEType"')
+        rset, req = self.env.get_rset_and_req('CWEType X WHERE X name "CWEType"')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
                               'mainactions': [('edit', actions.ModifyAction)],
                               'moreactions': [('delete', actions.DeleteAction),
                                               ('copy', actions.CopyAction),
-                                              ('testaction', EETypeRQLAction)],
+                                              ('testaction', CWETypeRQLAction),
+                                              ('managepermission', actions.ManagePermissionsAction)],
                               })
-        rset, req = self.env.get_rset_and_req('EEType X WHERE X name "ERType"')
+        rset, req = self.env.get_rset_and_req('CWEType X WHERE X name "CWRType"')
         self.assertDictEqual(self.pactions(req, rset),
                              {'useractions': USERACTIONS,
                               'siteactions': SITEACTIONS,
                               'mainactions': [('edit', actions.ModifyAction)],
                               'moreactions': [('delete', actions.DeleteAction),
-                                              ('copy', actions.CopyAction)],
+                                              ('copy', actions.CopyAction),
+                                              ('managepermission', actions.ManagePermissionsAction)],
                               })
-        
+
 
 
 if __name__ == '__main__':
--- a/web/test/unittest_widgets.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/test/unittest_widgets.py	Mon May 04 13:09:48 2009 +0200
@@ -2,11 +2,13 @@
 
 """
 
-from mx.DateTime import now
-NOW = now()
+from datetime import datetime
+NOW = datetime.now()
+
 from logilab.common.testlib import unittest_main
 from cubicweb.devtools.apptest import EnvBasedTC
 
+from cubicweb.common.mttransforms import HAS_TAL
 from cubicweb.web.widgets import widget, AutoCompletionWidget
 
 
@@ -30,7 +32,7 @@
                            u'<input type="hidden" name="eid" value="X" />')
 
     def test_textarea_widget(self):
-        self.add_entity('EProperty', pkey=u'ui.fckeditor', value=u'')
+        self.add_entity('CWProperty', pkey=u'ui.fckeditor', value=u'')
         self.commit()
         w = self.get_widget('State', 'description', 'String')
         self.assertEquals(w.name, 'description')
@@ -41,6 +43,10 @@
         entity
         self.assertEquals(w.required(entity), False)
         self.assertEquals(w.render(entity), '')
+        if HAS_TAL:
+            tal_format = u'\n<option value="text/cubicweb-page-template" >text/cubicweb-page-template</option>'
+        else:
+            tal_format = u''
         self.assertTextEquals(w.edit_render(entity),
                            u'''<input type="hidden" name="edits-description:X" value="__cubicweb_internal_field__"/>
 <input type="hidden" name="edits-description_format:X" value="__cubicweb_internal_field__"/>
@@ -48,12 +54,11 @@
 <select name="description_format:X" id="description_format:X" tabindex="0">
 <option value="text/rest" >text/rest</option>
 <option value="text/html" selected="selected">text/html</option>
-<option value="text/plain" >text/plain</option>
-<option value="text/cubicweb-page-template" >text/cubicweb-page-template</option>
-</select><br/><textarea onkeypress="autogrow(this)" name="description:X" accesskey="d" cols="80" id="description:X" rows="20" tabindex="1"></textarea>''')
+<option value="text/plain" >text/plain</option>%s
+</select><br/><textarea onkeypress="autogrow(this)" name="description:X" accesskey="d" cols="80" id="description:X" rows="20" tabindex="1"></textarea>''' % tal_format)
 
     def test_textarea_widget_previous_value(self):
-        self.add_entity('EProperty', pkey=u'ui.fckeditor', value=u'')
+        self.add_entity('CWProperty', pkey=u'ui.fckeditor', value=u'')
         self.commit()
         w = self.get_widget('State', 'description', 'String')
         req = self.request()
@@ -62,6 +67,10 @@
         entity.eid = 'X'
         self.assertEquals(w.required(entity), False)
         self.assertEquals(w.render(entity), '')
+        if HAS_TAL:
+            tal_format = u'\n<option value="text/cubicweb-page-template" >text/cubicweb-page-template</option>'
+        else:
+            tal_format = u''
         self.assertTextEquals(w.edit_render(entity),
                            u'''<input type="hidden" name="edits-description:X" value="__cubicweb_internal_field__"/>
 <input type="hidden" name="edits-description_format:X" value="__cubicweb_internal_field__"/>
@@ -69,9 +78,8 @@
 <select name="description_format:X" id="description_format:X" tabindex="0">
 <option value="text/rest" >text/rest</option>
 <option value="text/html" selected="selected">text/html</option>
-<option value="text/plain" >text/plain</option>
-<option value="text/cubicweb-page-template" >text/cubicweb-page-template</option>
-</select><br/><textarea onkeypress="autogrow(this)" name="description:X" accesskey="d" cols="80" id="description:X" rows="20" tabindex="1">a description</textarea>''')
+<option value="text/plain" >text/plain</option>%s
+</select><br/><textarea onkeypress="autogrow(this)" name="description:X" accesskey="d" cols="80" id="description:X" rows="20" tabindex="1">a description</textarea>''' % tal_format)
 
     def test_fckeditor_widget(self):
         w = self.get_widget('State', 'description', 'String')
@@ -179,10 +187,9 @@
     def test_datetime_widget(self):
         w = self.get_widget('Personne', 'datenaiss', 'Datetime')
         self.assertEquals(w.name, 'datenaiss')
-        now_ = now()
         example = '%s, or without time: %s' % (        
-            now_.strftime(self.vreg.property_value('ui.datetime-format')),
-            now_.strftime(self.vreg.property_value('ui.date-format')))
+            NOW.strftime(self.vreg.property_value('ui.datetime-format')),
+            NOW.strftime(self.vreg.property_value('ui.date-format')))
         self.assertEquals(w.render_example(self.request()), example)
         self.assertDictEquals(w.attrs, {'accesskey': 'd', 'maxlength': 16, 'size': 16})
         entity = self.etype_instance('Personne')
@@ -214,7 +221,7 @@
     def test_float_widget(self):
         w = self.get_widget('Personne', 'salary', 'Float')
         self.assertEquals(w.name, 'salary')
-        format = now().strftime(self.vreg.property_value('ui.float-format'))
+        format = self.vreg.property_value('ui.float-format')
         self.assertEquals(w.render_example(self.request()), format % 1.23)
         self.assertDictEquals(w.attrs, {'accesskey': 's', 'maxlength': 15, 'size': 5})
         entity = self.etype_instance('Personne')
@@ -229,7 +236,7 @@
     def test_float_widget_previous_value(self):
         w = self.get_widget('Personne', 'salary', 'Float')
         self.assertEquals(w.name, 'salary')
-        format = now().strftime(self.vreg.property_value('ui.float-format'))
+        format = self.vreg.property_value('ui.float-format')
         self.assertEquals(w.render_example(self.request()), format % 1.23)
         self.assertDictEquals(w.attrs, {'accesskey': 's', 'maxlength': 15, 'size': 5})
         req = self.request()
@@ -277,11 +284,11 @@
 
 
     def test_password_widget(self):
-        w = self.get_widget('EUser', 'upassword', 'Password')
+        w = self.get_widget('CWUser', 'upassword', 'Password')
         self.assertEquals(w.name, 'upassword')
         self.assertEquals(w.render_example(self.request()), '')
         self.assertDictEquals(w.attrs, {'accesskey': 'u'})
-        entity = self.etype_instance('EUser')
+        entity = self.etype_instance('CWUser')
         entity.eid = 'X'
         self.assertEquals(w.required(entity), True)
         self.assertEquals(w.render(entity), '')
@@ -338,7 +345,7 @@
     def test_nonregr_float_widget_with_none(self):
         w = self.get_widget('Personne', 'salary', 'Float')
         self.assertEquals(w.name, 'salary')
-        format = now().strftime(self.vreg.property_value('ui.float-format'))
+        format = self.vreg.property_value('ui.float-format')
         self.assertEquals(w.render_example(self.request()), format % 1.23)
         self.assertDictEquals(w.attrs, {'accesskey': 's', 'maxlength': 15, 'size': 5})
         req = self.request()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/uicfg.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,125 @@
+"""schema driven ui configuration.
+
+set of properties configuring edition, actions box, ... rendering using tags
+on schema relations. Those properties are defined here so we don't get module
+reloading problems.
+
+:organization: Logilab
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from cubicweb.rtags import RelationTags, RelationTagsSet
+
+# primary view configuration ##################################################
+
+# how to display a relation in primary view.
+# values a dict with the following keys:
+#
+# 'where', whose value may be one of:
+#  * 'attributes', display in the attributes section
+#  * 'relations', display in the relations section (below attributes)
+#  * 'sideboxes', display in the side boxes (beside attributes)
+# if this key is missing, the relation won't be displayed at all.
+#
+# 'vid' is an optional view identifier
+#
+# 'label' is an optional label
+#
+# 'limit' is a boolean telling if the results should be limited according to
+#  the configuration
+class RDisplayRelationTags(RelationTags):
+    def __init__(self):
+        super(RDisplayRelationTags, self).__init__()
+        self._counter = 0
+
+    def tag_relation(self, values, *args, **kwargs):
+        super(RDisplayRelationTags, self).tag_relation(values, *args, **kwargs)
+        if values:
+            values['order'] = self.get_timestamp()
+
+    def get_timestamp(self):
+        self._counter += 1
+        return self._counter
+
+rdisplay = RDisplayRelationTags()
+for rtype in ('eid', 'creation_date', 'modification_date',
+              'is', 'is_instance_of', 'identity',
+              'owned_by', 'created_by',
+              'in_state', 'wf_info_for', 'require_permission',
+              'from_entity', 'to_entity',
+              'see_also'):
+    rdisplay.tag_relation({}, ('*', rtype, '*'), 'subject')
+    rdisplay.tag_relation({}, ('*', rtype, '*'), 'object')
+
+
+# index view configuration ####################################################
+# entity type category in the index/manage page. May be one of
+# * 'application'
+# * 'system'
+# * 'schema'
+# * 'subobject' (not displayed by default)
+
+etypecat = {'EmailAddress': 'subobject'}
+
+
+# autoform.AutomaticEntityForm configuration ##################################
+
+# relations'category (eg primary/secondary/generic/metadata/generated)
+rcategories = RelationTags()
+# use primary and not generated for eid since it has to be an hidden
+rcategories.tag_relation('primary', ('*', 'eid', '*'), 'subject')
+rcategories.tag_relation('primary', ('*', 'in_state', '*'), 'subject')
+rcategories.tag_relation('secondary', ('*', 'description', '*'), 'subject')
+rcategories.tag_relation('metadata', ('*', 'creation_date', '*'), 'subject')
+rcategories.tag_relation('metadata', ('*', 'modification_date', '*'), 'subject')
+rcategories.tag_relation('metadata', ('*', 'owned_by', '*'), 'subject')
+rcategories.tag_relation('metadata', ('*', 'created_by', '*'), 'subject')
+rcategories.tag_relation('generated', ('*', 'has_text', '*'), 'subject')
+rcategories.tag_relation('generated', ('*', 'is', '*'), 'subject')
+rcategories.tag_relation('generated', ('*', 'is', '*'), 'object')
+rcategories.tag_relation('generated', ('*', 'is_instance_of', '*'), 'subject')
+rcategories.tag_relation('generated', ('*', 'is_instance_of', '*'), 'object')
+rcategories.tag_relation('generated', ('*', 'identity', '*'), 'subject')
+rcategories.tag_relation('generated', ('*', 'identity', '*'), 'object')
+rcategories.tag_relation('generated', ('*', 'require_permission', '*'), 'subject')
+rcategories.tag_relation('generated', ('*', 'wf_info_for', '*'), 'subject')
+rcategories.tag_relation('generated', ('*', 'wf_info_for', '*'), 'object')
+rcategories.tag_relation('generated', ('*', 'for_user', '*'), 'subject')
+rcategories.tag_relation('generated', ('*', 'for_user', '*'), 'object')
+
+# relations'field class
+rfields = RelationTags()
+
+# relations'widget class
+rwidgets = RelationTags()
+
+# inlined view flag for non final relations: when True for an entry, the
+# entity(ies) at the other end of the relation will be editable from the
+# form of the edited entity
+rinlined = RelationTags()
+rinlined.tag_relation(True, ('*', 'use_email', '*'), 'subject')
+
+
+# set of tags of the form <action>_on_new on relations. <action> is a
+# schema action (add/update/delete/read), and when such a tag is found
+# permissions checking is by-passed and supposed to be ok
+rpermissions_overrides = RelationTagsSet()
+
+
+# boxes.EditBox configuration #################################################
+
+# 'link' / 'create' relation tags, used to control the "add entity" submenu
+rmode = RelationTags()
+rmode.tag_relation('link', ('*', 'is', '*'), 'subject')
+rmode.tag_relation('link', ('*', 'is', '*'), 'object')
+rmode.tag_relation('link', ('*', 'is_instance_of', '*'), 'subject')
+rmode.tag_relation('link', ('*', 'is_instance_of', '*'), 'object')
+rmode.tag_relation('link', ('*', 'identity', '*'), 'subject')
+rmode.tag_relation('link', ('*', 'identity', '*'), 'object')
+rmode.tag_relation('link', ('*', 'owned_by', '*'), 'subject')
+rmode.tag_relation('link', ('*', 'created_by', '*'), 'subject')
+rmode.tag_relation('link', ('*', 'require_permission', '*'), 'subject')
+rmode.tag_relation('link', ('*', 'wf_info_for', '*'), 'subject')
+rmode.tag_relation('link', ('*', 'wf_info_for', '*'), 'object')
--- a/web/views/__init__.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/__init__.py	Mon May 04 13:09:48 2009 +0200
@@ -1,13 +1,17 @@
-"""Views/forms and actions for the CubicWeb web client
+"""Views, forms, actions... for the CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
-    
+
+import os
+from tempfile import mktemp
+
 from rql import nodes
 
+
 def need_table_view(rset, schema):
     """return True if we think that a table view is more appropriate than a
     list or primary view to display the given result set
@@ -68,29 +72,34 @@
         return 'list'
     return 'table'
 
-def linksearch_match(req, rset):
-    """when searching an entity to create a relation, return True if entities in
-    the given rset may be used as relation end
-    """
-    try:
-        searchedtype = req.search_state[1][-1]
-    except IndexError:
-        return 0 # no searching for association
-    for etype in rset.column_types(0):
-        if etype != searchedtype:
-            return 0
-    return 1
     
 def linksearch_select_url(req, rset):
     """when searching an entity to create a relation, return an url to select
     entities in the given rset
     """
     req.add_js( ('cubicweb.ajax.js', 'cubicweb.edition.js') )
-    target, link_eid, r_type, searchedtype = req.search_state[1]
+    target, eid, r_type, searchedtype = req.search_state[1]
     if target == 'subject':
-        id_fmt = '%s:%s:%%s' % (link_eid, r_type)
+        id_fmt = '%s:%s:%%s' % (eid, r_type)
     else:
-        id_fmt = '%%s:%s:%s' % (r_type, link_eid)
+        id_fmt = '%%s:%s:%s' % (r_type, eid)
     triplets = '-'.join(id_fmt % row[0] for row in rset.rows)
-    return "javascript: selectForAssociation('%s', '%s');" % (triplets,
-                                                              link_eid)
+    return "javascript: selectForAssociation('%s', '%s');" % (triplets, eid)
+                
+        
+class TmpFileViewMixin(object):
+    binary = True
+    content_type = 'application/octet-stream'
+    cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
+    
+    def call(self):
+        self.cell_call()
+        
+    def cell_call(self, row=0, col=0):
+        self.row, self.col = row, col # in case one need it
+        tmpfile = mktemp('.png')
+        try:
+            self._generate(tmpfile)
+            self.w(open(tmpfile).read())
+        finally:
+            os.unlink(tmpfile)
--- a/web/views/actions.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/actions.py	Mon May 04 13:09:48 2009 +0200
@@ -6,71 +6,109 @@
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb.common.selectors import (searchstate_accept, match_user_group, yes,
-                                       one_line_rset, two_lines_rset, one_etype_rset,
-                                       authenticated_user, none_rset,
-                                       match_search_state, chainfirst, chainall)
-
-from cubicweb.web.action import Action, EntityAction,  LinkToEntityAction
-from cubicweb.web.views import linksearch_select_url, linksearch_match
-from cubicweb.web.views.baseviews import vid_from_rset
+from cubicweb.vregistry import objectify_selector
+from cubicweb.selectors import (EntitySelector,
+    one_line_rset, two_lines_rset, one_etype_rset, relation_possible,
+    non_final_entity,
+    authenticated_user, match_user_groups, match_search_state,
+    has_permission, has_add_permission,
+    )
+from cubicweb.web.action import Action
+from cubicweb.web.views import linksearch_select_url, vid_from_rset
+from cubicweb.web.views.autoform import AutomaticEntityForm
 
 _ = unicode
 
+
+class has_editable_relation(EntitySelector):
+    """accept if some relations for an entity found in the result set is
+    editable by the logged user.
+
+    See `EntitySelector` documentation for behaviour when row is not specified.
+    """
+
+    def score_entity(self, entity):
+        # if user has no update right but it can modify some relation,
+        # display action anyway
+        for dummy in AutomaticEntityForm.esrelations_by_category(
+            entity, 'generic', 'add'):
+            return 1
+        for rschema, targetschemas, role in AutomaticEntityForm.erelations_by_category(
+            entity, ('primary', 'secondary'), 'add'):
+            if not rschema.is_final():
+                return 1
+        return 0
+
+@objectify_selector
+def match_searched_etype(cls, req, rset, **kwargs):
+    return req.match_search_state(rset)
+
+@objectify_selector
+def view_is_not_default_view(cls, req, rset, **kwargs):
+    # interesting if it propose another view than the current one
+    vid = req.form.get('vid')
+    if vid and vid != vid_from_rset(req, rset, cls.schema):
+        return 1
+    return 0
+
+@objectify_selector
+def addable_etype_empty_rset(cls, req, rset, **kwargs):
+    if rset is not None and not rset.rowcount:
+        rqlst = rset.syntax_tree()
+        if len(rqlst.children) > 1:
+            return 0
+        select = rqlst.children[0]
+        if len(select.defined_vars) == 1 and len(select.solutions) == 1:
+            rset._searched_etype = select.solutions[0].itervalues().next()
+            eschema = cls.schema.eschema(rset._searched_etype)
+            if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
+                   and eschema.has_perm(req, 'add'):
+                return 1
+    return 0
+
 # generic primary actions #####################################################
 
-class SelectAction(EntityAction):
+class SelectAction(Action):
     """base class for link search actions. By default apply on
     any size entity result search it the current state is 'linksearch'
     if accept match.
     """
-    category = 'mainactions'    
-    __selectors__ = (searchstate_accept,)
-    search_states = ('linksearch',)
+    id = 'select'
+    __select__ = match_search_state('linksearch') & match_searched_etype()
+
+    title = _('select')
+    category = 'mainactions'
     order = 0
-    
-    id = 'select'
-    title = _('select')
-    
-    @classmethod
-    def accept_rset(cls, req, rset, row, col):
-        return linksearch_match(req, rset)
-    
+
     def url(self):
         return linksearch_select_url(self.req, self.rset)
 
 
 class CancelSelectAction(Action):
+    id = 'cancel'
+    __select__ = match_search_state('linksearch')
+
+    title = _('cancel select')
     category = 'mainactions'
-    search_states = ('linksearch',)
     order = 10
-    
-    id = 'cancel'
-    title = _('cancel select')
-    
+
     def url(self):
-        target, link_eid, r_type, searched_type = self.req.search_state[1]
-        return self.build_url(rql="Any X WHERE X eid %s" % link_eid,
+        target, eid, r_type, searched_type = self.req.search_state[1]
+        return self.build_url(str(eid),
                               vid='edition', __mode='normal')
 
 
 class ViewAction(Action):
-    category = 'mainactions'    
-    __selectors__ = (match_user_group, searchstate_accept)
-    require_groups = ('users', 'managers')
-    order = 0
-    
     id = 'view'
+    __select__ = (match_search_state('normal') &
+                  match_user_groups('users', 'managers') &
+                  view_is_not_default_view() &
+                  non_final_entity())
+
     title = _('view')
-    
-    @classmethod
-    def accept_rset(cls, req, rset, row, col):
-        # interesting if it propose another view than the current one
-        vid = req.form.get('vid')
-        if vid and vid != vid_from_rset(req, rset, cls.schema):
-            return 1
-        return 0
-    
+    category = 'mainactions'
+    order = 0
+
     def url(self):
         params = self.req.form.copy()
         params.pop('vid', None)
@@ -79,90 +117,82 @@
                               **params)
 
 
-class ModifyAction(EntityAction):
-    category = 'mainactions'
-    __selectors__ = (one_line_rset, searchstate_accept)
-    schema_action = 'update'
-    order = 10
-    
+class ModifyAction(Action):
     id = 'edit'
+    __select__ = (match_search_state('normal') &
+                  one_line_rset() &
+                  (has_permission('update') | has_editable_relation('add')))
+
     title = _('modify')
-    
-    @classmethod
-    def has_permission(cls, entity, action):
-        if entity.has_perm(action):
-            return True
-        # if user has no update right but it can modify some relation,
-        # display action anyway
-        for dummy in entity.srelations_by_category(('generic', 'metadata'),
-                                                   'add'):
-            return True
-        for rschema, targetschemas, role in entity.relations_by_category(
-            ('primary', 'secondary'), 'add'):
-            if not rschema.is_final():
-                return True
-        return False
+    category = 'mainactions'
+    order = 10
 
     def url(self):
         entity = self.rset.get_entity(self.row or 0, self.col or 0)
         return entity.absolute_url(vid='edition')
-        
+
 
-class MultipleEditAction(EntityAction):
+class MultipleEditAction(Action):
+    id = 'muledit' # XXX get strange conflicts if id='edit'
+    __select__ = (match_search_state('normal') &
+                  two_lines_rset() & one_etype_rset() &
+                  has_permission('update'))
+
+    title = _('modify')
     category = 'mainactions'
-    __selectors__ = (two_lines_rset, one_etype_rset,
-                     searchstate_accept)
-    schema_action = 'update'
     order = 10
-    
-    id = 'muledit' # XXX get strange conflicts if id='edit'
-    title = _('modify')
-    
+
     def url(self):
         return self.build_url('view', rql=self.rset.rql, vid='muledit')
 
 
 # generic secondary actions ###################################################
 
-class ManagePermissions(LinkToEntityAction):
-    accepts = ('Any',)
-    category = 'moreactions'
-    id = 'addpermission'
+class ManagePermissionsAction(Action):
+    id = 'managepermission'
+    __select__ = one_line_rset() & non_final_entity() & match_user_groups('managers')
+
     title = _('manage permissions')
-    order = 100
+    category = 'moreactions'
+    order = 15
 
-    etype = 'EPermission'
-    rtype = 'require_permission'
-    target = 'object'
-    
+    @classmethod
+    def registered(cls, vreg):
+        super(ManagePermissionsAction, cls).registered(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)
+
     def url(self):
-        return self.rset.get_entity(0, 0).absolute_url(vid='security')
+        return self.rset.get_entity(self.row or 0, self.col or 0).absolute_url(vid='security')
+
 
-    
-class DeleteAction(EntityAction):
-    category = 'moreactions' 
-    __selectors__ = (searchstate_accept,)
-    schema_action = 'delete'
+class DeleteAction(Action):
+    id = 'delete'
+    __select__ = has_permission('delete')
+
+    title = _('delete')
+    category = 'moreactions'
     order = 20
-    
-    id = 'delete'
-    title = _('delete')
-    
+
     def url(self):
         if len(self.rset) == 1:
-            entity = self.rset.get_entity(0, 0)
+            entity = self.rset.get_entity(self.row or 0, self.col or 0)
             return self.build_url(entity.rest_path(), vid='deleteconf')
         return self.build_url(rql=self.rset.printable_rql(), vid='deleteconf')
-    
-        
-class CopyAction(EntityAction):
+
+
+class CopyAction(Action):
+    id = 'copy'
+    __select__ = one_line_rset() & has_permission('add')
+
+    title = _('copy')
     category = 'moreactions'
-    schema_action = 'add'
     order = 30
-    
-    id = 'copy'
-    title = _('copy')
-    
+
     def url(self):
         entity = self.rset.get_entity(self.row or 0, self.col or 0)
         return entity.absolute_url(vid='copy')
@@ -172,34 +202,14 @@
     """when we're seeing more than one entity with the same type, propose to
     add a new one
     """
+    id = 'addentity'
+    __select__ = (match_search_state('normal') &
+                  (addable_etype_empty_rset()
+                   | (two_lines_rset() & one_etype_rset & has_add_permission()))
+                  )
+
     category = 'moreactions'
-    id = 'addentity'
     order = 40
-    
-    def etype_rset_selector(cls, req, rset, **kwargs):
-        if rset is not None and not rset.rowcount:
-            rqlst = rset.syntax_tree()
-            if len(rqlst.children) > 1:
-                return 0
-            select = rqlst.children[0]
-            if len(select.defined_vars) == 1 and len(select.solutions) == 1:
-                rset._searched_etype = select.solutions[0].itervalues().next()
-                eschema = cls.schema.eschema(rset._searched_etype)
-                if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
-                       and eschema.has_perm(req, 'add'):
-                    return 1
-        return 0
-
-    def has_add_perm_selector(cls, req, rset, **kwargs):
-        eschema = cls.schema.eschema(rset.description[0][0])
-        if not (eschema.is_final() or eschema.is_subobject(strict=True)) \
-               and eschema.has_perm(req, 'add'):
-            return 1
-        return 0
-    __selectors__ = (match_search_state,
-                     chainfirst(etype_rset_selector,
-                                chainall(two_lines_rset, one_etype_rset,
-                                         has_add_perm_selector)))
 
     @property
     def rsettype(self):
@@ -218,114 +228,66 @@
 # logged user actions #########################################################
 
 class UserPreferencesAction(Action):
+    id = 'myprefs'
+    __select__ = authenticated_user()
+
+    title = _('user preferences')
     category = 'useractions'
-    __selectors__ = authenticated_user,
     order = 10
-    
-    id = 'myprefs'
-    title = _('user preferences')
 
     def url(self):
         return self.build_url(self.id)
 
 
 class UserInfoAction(Action):
+    id = 'myinfos'
+    __select__ = authenticated_user()
+
+    title = _('personnal informations')
     category = 'useractions'
-    __selectors__ = authenticated_user,
     order = 20
-    
-    id = 'myinfos'
-    title = _('personnal informations')
 
     def url(self):
-        return self.build_url('euser/%s'%self.req.user.login, vid='edition')
+        return self.build_url('cwuser/%s'%self.req.user.login, vid='edition')
 
 
 class LogoutAction(Action):
-    category = 'useractions'
-    __selectors__ = authenticated_user,
-    order = 30
-    
     id = 'logout'
-    title = _('logout')
+    __select__ = authenticated_user()
 
-    def url(self):
-        return self.build_url(self.id)
-
-    
-# site actions ################################################################
-
-class ManagersAction(Action):
-    category = 'siteactions'
-    __abstract__ = True
-    __selectors__ = match_user_group,
-    require_groups = ('managers',)
+    title = _('logout')
+    category = 'useractions'
+    order = 30
 
     def url(self):
         return self.build_url(self.id)
 
-    
-class SiteConfigurationAction(ManagersAction):
-    order = 10
-    id = 'siteconfig'
-    title = _('site configuration')
+
+# site actions ################################################################
 
-    
-class ManageAction(ManagersAction):
-    order = 20
-    id = 'manage'
-    title = _('manage')
-
+class ManagersAction(Action):
+    __abstract__ = True
+    __select__ = match_user_groups('managers')
 
-class ViewSchemaAction(Action):
     category = 'siteactions'
-    id = 'schema'
-    title = _("site schema")
-    __selectors__ = yes,
-    order = 30
-    
+
     def url(self):
         return self.build_url(self.id)
 
 
-# content type specific actions ###############################################
+class SiteConfigurationAction(ManagersAction):
+    id = 'siteconfig'
+    title = _('site configuration')
+    order = 10
 
-class FollowAction(EntityAction):
-    category = 'mainactions'
-    accepts = ('Bookmark',)
-    
-    id = 'follow'
-    title = _('follow')
-    
-    def url(self):
-        return self.rset.get_entity(self.row or 0, self.col or 0).actual_url()
 
-class UserPreferencesEntityAction(EntityAction):
-    __selectors__ = EntityAction.__selectors__ + (one_line_rset, match_user_group,)
-    require_groups = ('owners', 'managers')
-    category = 'mainactions'
-    accepts = ('EUser',)
-    
-    id = 'prefs'
-    title = _('preferences')
-    
-    def url(self):
-        login = self.rset.get_entity(self.row or 0, self.col or 0).login
-        return self.build_url('euser/%s'%login, vid='epropertiesform')
+class ManageAction(ManagersAction):
+    id = 'manage'
+    title = _('manage')
+    order = 20
 
-# schema view action
-def schema_view(cls, req, rset, row=None, col=None, view=None,
-                **kwargs):
-    if view is None or not view.id == 'schema':
-        return 0
-    return 1
 
-class DownloadOWLSchemaAction(Action):
-    category = 'mainactions'
-    id = 'download_as_owl'
-    title = _('download schema as owl')
-    __selectors__ = none_rset, schema_view
-   
-    def url(self):
-        return self.build_url('view', vid='owl')
+from logilab.common.deprecation import class_moved
+from cubicweb.web.views.bookmark import FollowAction
+FollowAction = class_moved(FollowAction)
 
--- a/web/views/ajaxedit.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/ajaxedit.py	Mon May 04 13:09:48 2009 +0200
@@ -1,13 +1,13 @@
 """Set of views allowing edition of entities/relations using ajax
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb.common.selectors import (chainfirst, match_form_params,
-                                    match_kwargs)
+from cubicweb import role
+from cubicweb.selectors import match_form_params, match_kwargs
 from cubicweb.web.box import EditRelationBoxTemplate
 
 class AddRelationView(EditRelationBoxTemplate):
@@ -18,13 +18,14 @@
     class attributes.
     """
     __registry__ = 'views'
-    __selectors__ = (chainfirst(match_form_params, match_kwargs),)
+    __select__ = (match_form_params('rtype', 'target')
+                  | match_kwargs('rtype', 'target'))
     property_defs = {} # don't want to inherit this from Box
     id = 'xaddrelation'
     expected_kwargs = form_params = ('rtype', 'target')
 
     build_js = EditRelationBoxTemplate.build_reload_js_call
-    
+
     def cell_call(self, row, col, rtype=None, target=None, etype=None):
         self.rtype = rtype or self.req.form['rtype']
         self.target = target or self.req.form['target']
@@ -41,7 +42,7 @@
         fakebox = []
         self.w(u'<div id="%s">' % self.id)
         self.w(u'<h1>%s</h1>' % self.req._('relation %(relname)s of %(ent)s')
-               % {'relname': rschema.display_name(self.req, self.xtarget()[0]),
+               % {'relname': rschema.display_name(self.req, role(self)),
                   'ent': entity.view('incontext')})
         self.w(u'<ul>')
         self.w_unrelated(fakebox, entity)
@@ -55,15 +56,16 @@
         if etype is not defined on the Box's class, the default
         behaviour is to use the entity's appropraite vocabulary function
         """
-        x, target = self.xtarget()
         # use entity.unrelated if we've been asked for a particular etype
         if getattr(self, 'etype', None):
-            rset = entity.unrelated(self.rtype, self.etype, x, ordermethod='fetch_order')
+            rset = entity.unrelated(self.rtype, self.etype, role(self),
+                                    ordermethod='fetch_order')
             self.pagination(self.req, rset, w=self.w)
             return rset.entities()
         # in other cases, use vocabulary functions
         entities = []
-        for _, eid in entity.vocabulary(self.rtype, x):
+        # XXX to update for 3.2
+        for _, eid in entity.vocabulary(self.rtype, role(self)):
             if eid is not None:
                 rset = self.req.eid_rset(eid)
                 entities.append(rset.get_entity(0, 0))
--- a/web/views/apacherewrite.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/apacherewrite.py	Mon May 04 13:09:48 2009 +0200
@@ -2,7 +2,7 @@
 are much more limited for the moment)
 
 :organization: Logilab
-:copyright: 2007-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
@@ -11,7 +11,7 @@
 from re import compile
 
 from cubicweb.web import Redirect
-from cubicweb.web.component import SingletonComponent
+from cubicweb.web.component import Component
 
 class RewriteCond(object):
     def __init__(self, condition, match='host', rules=(), action='rewrite'):
@@ -46,7 +46,7 @@
         return path
 
     
-class ApacheURLRewrite(SingletonComponent):
+class ApacheURLRewrite(Component):
     """inherit from this class with actual rules to activate apache style rewriting
 
     rules should have the form :
--- a/web/views/authentication.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/authentication.py	Mon May 04 13:09:48 2009 +0200
@@ -51,10 +51,18 @@
         # associate the connection to the current request
         req.set_connection(cnx, user)
         return cnx
-        
+
+    def login_from_email(self, login):
+        session = self.repo.internal_session()
+        rset = session.execute('Any L WHERE U login L, U primary_email M, '
+                               'M address %(login)s', {'login': login})
+        if rset.rowcount == 1:
+            login = rset[0][0]
+        return login
+
     def authenticate(self, req, _login=None, _password=None):
         """authenticate user and return corresponding user object
-        
+
         :raise ExplicitLogin: if authentication is required (no authentication
         info found or wrong user/password)
 
@@ -66,6 +74,8 @@
             login, password = _login, _password
         else:
             login, password = req.get_authorization()
+        if self.vreg.config['allow-email-login'] and '@' in (login or u''):
+            login = self.login_from_email(login)
         if not login:
             # No session and no login -> try anonymous
             login, password = self.vreg.config.anonymous_user()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/autoform.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,332 @@
+"""The automatic entity form.
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from logilab.common.decorators import iclassmethod
+
+from cubicweb import typed_eid
+from cubicweb.web import stdmsgs, uicfg
+from cubicweb.web.form import FieldNotFound, EntityFieldsForm
+from cubicweb.web.formfields import guess_field
+from cubicweb.web.formwidgets import Button, SubmitButton
+from cubicweb.web.views.editforms import toggleable_relation_link, relation_id
+
+_ = unicode
+
+class AutomaticEntityForm(EntityFieldsForm):
+    """base automatic form to edit any entity.
+
+    Designed to be fully generated from schema but highly configurable through:
+    * rtags (rcategories, rfields, rwidgets, inlined, rpermissions)
+    * various standard form parameters
+
+    You can also easily customise it by adding/removing fields in
+    AutomaticEntityForm instances.
+    """
+    id = 'edition'
+
+    cwtarget = 'eformframe'
+    cssclass = 'entityForm'
+    copy_nav_params = True
+    form_buttons = [SubmitButton(stdmsgs.BUTTON_OK),
+                    Button(stdmsgs.BUTTON_APPLY, cwaction='apply'),
+                    Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
+    attrcategories = ('primary', 'secondary')
+    # class attributes below are actually stored in the uicfg module since we
+    # don't want them to be reloaded
+    rcategories = uicfg.rcategories
+    rfields = uicfg.rfields
+    rwidgets = uicfg.rwidgets
+    rinlined = uicfg.rinlined
+    rpermissions_overrides = uicfg.rpermissions_overrides
+
+    @classmethod
+    def vreg_initialization_completed(cls):
+        """set default category tags for relations where it's not yet defined in
+        the category relation tags
+        """
+        for eschema in cls.schema.entities():
+            for rschema, tschemas, role in eschema.relation_definitions(True):
+                for tschema in tschemas:
+                    if role == 'subject':
+                        X, Y = eschema, tschema
+                        card = rschema.rproperty(X, Y, 'cardinality')[0]
+                        composed = rschema.rproperty(X, Y, 'composite') == 'object'
+                    else:
+                        X, Y = tschema, eschema
+                        card = rschema.rproperty(X, Y, 'cardinality')[1]
+                        composed = rschema.rproperty(X, Y, 'composite') == 'subject'
+                    if not cls.rcategories.get(rschema, role, X, Y):
+                        if eschema.is_metadata(rschema):
+                            category = 'generated'
+                        elif card in '1+':
+                            if not rschema.is_final() and composed:
+                                category = 'generated'
+                            else:
+                                category = 'primary'
+                        elif rschema.is_final():
+                            category = 'secondary'
+                        else:
+                            category = 'generic'
+                        cls.rcategories.tag_relation(category, (X, rschema, Y), role)
+
+    @classmethod
+    def erelations_by_category(cls, entity, categories=None, permission=None, rtags=None):
+        """return a list of (relation schema, target schemas, role) matching
+        categories and permission
+        """
+        if categories is not None:
+            if not isinstance(categories, (list, tuple, set, frozenset)):
+                categories = (categories,)
+            if not isinstance(categories, (set, frozenset)):
+                categories = frozenset(categories)
+        eschema  = entity.e_schema
+        if rtags is None:
+            rtags = cls.rcategories
+        permsoverrides = cls.rpermissions_overrides
+        if entity.has_eid():
+            eid = entity.eid
+        else:
+            eid = None
+        for rschema, targetschemas, role in eschema.relation_definitions(True):
+            # check category first, potentially lower cost than checking
+            # permission which may imply rql queries
+            if categories is not None:
+                targetschemas = [tschema for tschema in targetschemas
+                                 if rtags.etype_get(eschema, rschema, role, tschema) in categories]
+                if not targetschemas:
+                    continue
+            if permission is not None:
+                # tag allowing to hijack the permission machinery when
+                # permission is not verifiable until the entity is actually
+                # created...
+                if eid is None and '%s_on_new' % permission in permsoverrides.etype_get(eschema, rschema, role):
+                    yield (rschema, targetschemas, role)
+                    continue
+                if rschema.is_final():
+                    if not rschema.has_perm(entity.req, permission, eid):
+                        continue
+                elif role == 'subject':
+                    if not ((eid is None and rschema.has_local_role(permission)) or
+                            rschema.has_perm(entity.req, permission, fromeid=eid)):
+                        continue
+                    # on relation with cardinality 1 or ?, we need delete perm as well
+                    # if the relation is already set
+                    if (permission == 'add'
+                        and rschema.cardinality(eschema, targetschemas[0], role) in '1?'
+                        and eid and entity.related(rschema.type, role)
+                        and not rschema.has_perm(entity.req, 'delete', fromeid=eid,
+                                                 toeid=entity.related(rschema.type, role)[0][0])):
+                        continue
+                elif role == 'object':
+                    if not ((eid is None and rschema.has_local_role(permission)) or
+                            rschema.has_perm(entity.req, permission, toeid=eid)):
+                        continue
+                    # on relation with cardinality 1 or ?, we need delete perm as well
+                    # if the relation is already set
+                    if (permission == 'add'
+                        and rschema.cardinality(targetschemas[0], eschema, role) in '1?'
+                        and eid and entity.related(rschema.type, role)
+                        and not rschema.has_perm(entity.req, 'delete', toeid=eid,
+                                                 fromeid=entity.related(rschema.type, role)[0][0])):
+                        continue
+            yield (rschema, targetschemas, role)
+
+    @classmethod
+    def esrelations_by_category(cls, entity, categories=None, permission=None):
+        """filter out result of relations_by_category(categories, permission) by
+        removing final relations
+
+        return a sorted list of (relation's label, relation'schema, role)
+        """
+        result = []
+        for rschema, ttypes, role in cls.erelations_by_category(
+            entity, categories, permission):
+            if rschema.is_final():
+                continue
+            result.append((rschema.display_name(entity.req, role), rschema, role))
+        return sorted(result)
+
+    @iclassmethod
+    def field_by_name(cls_or_self, name, role='subject', eschema=None):
+        """return field with the given name and role. If field is not explicitly
+        defined for the form but `eclass` is specified, guess_field will be
+        called.
+        """
+        try:
+            return super(AutomaticEntityForm, cls_or_self).field_by_name(name, role)
+        except FieldNotFound: # XXX should raise more explicit exception
+            if eschema is None or not name in cls_or_self.schema:
+                raise
+            rschema = cls_or_self.schema.rschema(name)
+            fieldcls = cls_or_self.rfields.etype_get(eschema, rschema, role)
+            if fieldcls:
+                return fieldcls(name=name, role=role, eidparam=True)
+            widget = cls_or_self.rwidgets.etype_get(eschema, rschema, role)
+            if widget:
+                field = guess_field(eschema, rschema, role,
+                                    eidparam=True, widget=widget)
+            else:
+                field = guess_field(eschema, rschema, role, eidparam=True)
+            if field is None:
+                raise
+            return field
+
+    def __init__(self, *args, **kwargs):
+        super(AutomaticEntityForm, self).__init__(*args, **kwargs)
+        entity = self.edited_entity
+        if entity.has_eid():
+            entity.complete()
+        for rschema, role in self.editable_attributes():
+            try:
+                self.field_by_name(rschema.type, role)
+                continue # explicitly specified
+            except FieldNotFound:
+                # has to be guessed
+                try:
+                    field = self.field_by_name(rschema.type, role,
+                                               eschema=entity.e_schema)
+                    self.fields.append(field)
+                except FieldNotFound:
+                    # meta attribute such as <attr>_format
+                    continue
+        self.maxrelitems = self.req.property_value('navigation.related-limit')
+        self.force_display = bool(self.req.form.get('__force_display'))
+
+    @property
+    def related_limit(self):
+        if self.force_display:
+            return None
+        return self.maxrelitems + 1
+
+    def relations_by_category(self, categories=None, permission=None):
+        """return a list of (relation schema, target schemas, role) matching
+        given category(ies) and permission
+        """
+        return self.erelations_by_category(self.edited_entity, categories,
+                                           permission)
+
+    def inlined_relations(self):
+        """return a list of (relation schema, target schemas, role) matching
+        given category(ies) and permission
+        """
+        # we'll need an initialized varmaker if there are some inlined relation
+        self.initialize_varmaker()
+        return self.erelations_by_category(self.edited_entity, True, 'add', self.rinlined)
+
+    def srelations_by_category(self, categories=None, permission=None):
+        """filter out result of relations_by_category(categories, permission) by
+        removing final relations
+
+        return a sorted list of (relation's label, relation'schema, role)
+        """
+        return self.esrelations_by_category(self.edited_entity, categories,
+                                           permission)
+
+    def action(self):
+        """return the form's action attribute. Default to validateform if not
+        explicitly overriden.
+        """
+        try:
+            return self._action
+        except AttributeError:
+            return self.build_url('validateform')
+
+    def set_action(self, value):
+        """override default action"""
+        self._action = value
+
+    action = property(action, set_action)
+
+    def editable_attributes(self):
+        """return a list of (relation schema, role) to edit for the entity"""
+        return [(rschema, x) for rschema, _, x in self.relations_by_category(
+                self.attrcategories, 'add') if rschema != 'eid']
+
+    def relations_table(self):
+        """yiels 3-tuples (rtype, target, related_list)
+        where <related_list> itself a list of :
+          - node_id (will be the entity element's DOM id)
+          - appropriate javascript's togglePendingDelete() function call
+          - status 'pendingdelete' or ''
+          - oneline view of related entity
+        """
+        entity = self.edited_entity
+        pending_deletes = self.req.get_pending_deletes(entity.eid)
+        for label, rschema, role in self.srelations_by_category('generic', 'add'):
+            relatedrset = entity.related(rschema, role, limit=self.related_limit)
+            if rschema.has_perm(self.req, 'delete'):
+                toggleable_rel_link_func = toggleable_relation_link
+            else:
+                toggleable_rel_link_func = lambda x, y, z: u''
+            related = []
+            for row in xrange(relatedrset.rowcount):
+                nodeid = relation_id(entity.eid, rschema, role,
+                                     relatedrset[row][0])
+                if nodeid in pending_deletes:
+                    status = u'pendingDelete'
+                    label = '+'
+                else:
+                    status = u''
+                    label = 'x'
+                dellink = toggleable_rel_link_func(entity.eid, nodeid, label)
+                eview = self.view('oneline', relatedrset, row=row)
+                related.append((nodeid, dellink, status, eview))
+            yield (rschema, role, related)
+
+    def restore_pending_inserts(self, cell=False):
+        """used to restore edition page as it was before clicking on
+        'search for <some entity type>'
+        """
+        eid = self.edited_entity.eid
+        cell = cell and "div_insert_" or "tr"
+        pending_inserts = set(self.req.get_pending_inserts(eid))
+        for pendingid in pending_inserts:
+            eidfrom, rtype, eidto = pendingid.split(':')
+            if typed_eid(eidfrom) == eid: # subject
+                label = display_name(self.req, rtype, 'subject')
+                reid = eidto
+            else:
+                label = display_name(self.req, rtype, 'object')
+                reid = eidfrom
+            jscall = "javascript: cancelPendingInsert('%s', '%s', null, %s);" \
+                     % (pendingid, cell, eid)
+            rset = self.req.eid_rset(reid)
+            eview = self.view('text', rset, row=0)
+            # XXX find a clean way to handle baskets
+            if rset.description[0][0] == 'Basket':
+                eview = '%s (%s)' % (eview, display_name(self.req, 'Basket'))
+            yield rtype, pendingid, jscall, label, reid, eview
+
+    # should_* method extracted to allow overriding
+
+    def should_inline_relation_form(self, rschema, targettype, role):
+        """return true if the given relation with entity has role and a
+        targettype target should be inlined
+        """
+        return self.rinlined.etype_get(self.edited_entity.id, rschema, role, targettype)
+
+    def should_display_inline_creation_form(self, rschema, existant, card):
+        """return true if a creation form should be inlined
+
+        by default true if there is no related entity and we need at least one
+        """
+        return not existant and card in '1+'
+
+    def should_display_add_new_relation_link(self, rschema, existant, card):
+        """return true if we should add a link to add a new creation form
+        (through ajax call)
+
+        by default true if there is no related entity or if the relation has
+        multiple cardinality
+        """
+        return not existant or card in '+*'
+
+
+def etype_relation_field(etype, rtype, role='subject'):
+    eschema = AutomaticEntityForm.schema.eschema(etype)
+    return AutomaticEntityForm.field_by_name(rtype, role, eschema)
--- a/web/views/basecomponents.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/basecomponents.py	Mon May 04 13:09:48 2009 +0200
@@ -2,35 +2,34 @@
 
 * the rql input form
 * the logged user link
-* the workflow history section for workflowable objects
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 from rql import parse
 
-from cubicweb import Unauthorized
+from cubicweb.selectors import yes, two_etypes_rset, match_form_params
 from cubicweb.common.uilib import html_escape, toggle_action
-from cubicweb.common.selectors import yes, non_final_entity, one_line_rset
 from cubicweb.schema import display_name
-from cubicweb.common.selectors import (chainfirst, two_etypes_rset,
-                                    match_form_params)
 
 from cubicweb.web.htmlwidgets import MenuWidget, PopupBoxMenu, BoxSeparator, BoxLink
-from cubicweb.web.component import (VComponent, SingletonVComponent, EntityVComponent, 
-                                    RelatedObjectsVComponent)
+from cubicweb.web import uicfg, component
 
 _ = unicode
 
+VISIBLE_PROP_DEF = {
+    _('visible'):  dict(type='Boolean', default=False,
+                        help=_('display the component or not')),
+    }
 
-class RQLInputForm(SingletonVComponent):
+class RQLInputForm(component.Component):
     """build the rql input form, usually displayed in the header"""
     id = 'rqlinput'
-    visible = False
-       
+    property_defs = VISIBLE_PROP_DEF
+
     def call(self, view=None):
         if hasattr(view, 'filter_box_context_info'):
             rset = view.filter_box_context_info()[0]
@@ -55,30 +54,36 @@
         self.w(u'</form></div>')
 
 
-class ApplLogo(SingletonVComponent):
+class ApplLogo(component.Component):
     """build the application logo, usually displayed in the header"""
     id = 'logo'
-    site_wide = True # don't want user to hide this component using an eproperty
+    property_defs = VISIBLE_PROP_DEF
+    # don't want user to hide this component using an cwproperty
+    site_wide = True
+
     def call(self):
         self.w(u'<a href="%s"><img class="logo" src="%s" alt="logo"/></a>'
                % (self.req.base_url(), self.req.external_resource('LOGO')))
 
 
-class ApplHelp(SingletonVComponent):
+class ApplHelp(component.Component):
     """build the help button, usually displayed in the header"""
     id = 'help'
+    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'),
                   self.req._(u'help'),))
 
 
-class UserLink(SingletonVComponent):
+class UserLink(component.Component):
     """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
+    # don't want user to hide this component using an cwproperty
+    site_wide = True
     id = 'loggeduserlink'
-    site_wide = True # don't want user to hide this component using an eproperty
 
     def call(self):
         if not self.req.cnx.anonymous_connection:
@@ -98,25 +103,32 @@
             box.render(w=self.w)
         else:
             self.anon_user_link()
-            
+
     def anon_user_link(self):
         if self.config['auth-mode'] == 'cookie':
             self.w(self.req._('anonymous'))
             self.w(u'''&nbsp;[<a class="logout" href="javascript: popupLoginBox();">%s</a>]'''
                    % (self.req._('i18n_login_popup')))
+            # FIXME maybe have an other option to explicitely authorise registration
+            #       also provide a working register view
+#             if self.config['anonymous-user']:
+#                 self.w(u'''&nbsp;[<a class="logout" href="?vid=register">%s</a>]'''
+#                        % (self.req._('i18n_register_user')))
         else:
             self.w(self.req._('anonymous'))
             self.w(u'&nbsp;[<a class="logout" href="%s">%s</a>]'
                    % (self.build_url('login'), self.req._('login')))
 
 
-class ApplicationMessage(SingletonVComponent):
+class ApplicationMessage(component.Component):
     """display application's messages given using the __message parameter
     into a special div section
     """
-    __selectors__ = yes,
+    __select__ = yes()
     id = 'applmessages'
-    site_wide = True # don't want user to hide this component using an eproperty
+    property_defs = VISIBLE_PROP_DEF
+    # don't want user to hide this component using an cwproperty
+    site_wide = True
 
     def call(self):
         msgs = [msg for msg in (self.req.get_shared_data('sources_error', pop=True),
@@ -129,72 +141,43 @@
         self.w(u'</div>')
 
 
-class WFHistoryVComponent(EntityVComponent):
-    """display the workflow history for entities supporting it"""
-    id = 'wfhistory'
-    accepts = ('Any',)
-    context = 'navcontentbottom'
-    rtype = 'wf_info_for'
-    target = 'subject'
-    title = _('Workflow history')
-
-    def cell_call(self, row, col, view=None):
-        _ = self.req._
-        eid = self.rset[row][col]
-        sel = 'Any FS,TS,WF,D'
-        rql = ' ORDERBY D DESC WHERE WF wf_info_for X,'\
-              'WF from_state FS, WF to_state TS, WF comment C,'\
-              'WF creation_date D'
-        if self.vreg.schema.eschema('EUser').has_perm(self.req, 'read'):
-            sel += ',U,C'
-            rql += ', WF owned_by U?'
-            displaycols = range(5)
-            headers = (_('from_state'), _('to_state'), _('comment'), _('date'),
-                       _('EUser'))            
-        else:
-            sel += ',C'
-            displaycols = range(4)
-            headers = (_('from_state'), _('to_state'), _('comment'), _('date'))
-        rql = '%s %s, X eid %%(x)s' % (sel, rql)
-        try:
-            rset = self.req.execute(rql, {'x': eid}, 'x')
-        except Unauthorized:
-            return
-        if rset:
-            self.wview('table', rset, title=_(self.title), displayactions=False,
-                       displaycols=displaycols, headers=headers)
-
-
-class ApplicationName(SingletonVComponent):
+class ApplicationName(component.Component):
     """display the application name"""
     id = 'appliname'
+    property_defs = VISIBLE_PROP_DEF
 
     def call(self):
         self.w(u'<span id="appliName"><a href="%s">%s</a></span>' % (self.req.base_url(),
                                                          self.req.property_value('ui.site-title')))
-        
+
 
-class SeeAlsoVComponent(RelatedObjectsVComponent):
+uicfg.rdisplay.tag_relation({}, ('*', 'see_also', '*'), 'subject')
+uicfg.rdisplay.tag_relation({}, ('*', 'see_also', '*'), 'object')
+
+class SeeAlsoVComponent(component.RelatedObjectsVComponent):
     """display any entity's see also"""
     id = 'seealso'
     context = 'navcontentbottom'
     rtype = 'see_also'
-    target = 'object'
+    role = 'subject'
     order = 40
     # register msg not generated since no entity use see_also in cubicweb itself
     title = _('contentnavigation_seealso')
     help = _('contentnavigation_seealso_description')
 
-    
-class EtypeRestrictionComponent(SingletonVComponent):
+
+class EtypeRestrictionComponent(component.Component):
     """displays the list of entity types contained in the resultset
     to be able to filter accordingly.
     """
     id = 'etypenavigation'
-    __select__ = classmethod(chainfirst(two_etypes_rset, match_form_params))
-    form_params = ('__restrtype', '__restrtypes', '__restrrql')
+    __select__ = two_etypes_rset() | match_form_params('__restrtype', '__restrtypes',
+                                                       '__restrrql')
+    property_defs = VISIBLE_PROP_DEF
+    # don't want user to hide this component using an cwproperty
+    site_wide = True
     visible = False # disabled by default
-    
+
     def call(self):
         _ = self.req._
         self.w(u'<div id="etyperestriction">')
@@ -233,20 +216,9 @@
             html.insert(0, u'<span class="selected">%s</span>' % _('Any'))
         self.w(u'&nbsp;|&nbsp;'.join(html))
         self.w(u'</div>')
-        
 
 
-class RSSFeedURL(VComponent):
-    id = 'rss_feed_url'
-    __selectors__ = (non_final_entity,)
-    
-    def feed_url(self):
-        return self.build_url(rql=self.limited_rql(), vid='rss')
-
-class RSSEntityFeedURL(VComponent):
-    id = 'rss_feed_url'
-    __selectors__ = (non_final_entity, one_line_rset)
-    
-    def feed_url(self):
-        return self.entity(0, 0).rss_feed_url()
-
+def registration_callback(vreg):
+    vreg.register_all(globals().values(), __name__, (SeeAlsoVComponent,))
+    if 'see_also' in vreg.schema:
+        vreg.register(SeeAlsoVComponent)
--- a/web/views/basecontrollers.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/basecontrollers.py	Mon May 04 13:09:48 2009 +0200
@@ -13,16 +13,15 @@
 
 import simplejson
 
-from mx.DateTime.Parser import DateFromString
-
 from logilab.common.decorators import cached
 
-from cubicweb import NoSelectableObject, ValidationError, typed_eid
-from cubicweb.common.selectors import yes
+from cubicweb import NoSelectableObject, ValidationError, ObjectNotFound, typed_eid
+from cubicweb.utils import strptime
+from cubicweb.selectors import yes, match_user_groups
+from cubicweb.view import STRICT_DOCTYPE
 from cubicweb.common.mail import format_mail
-from cubicweb.common.view import STRICT_DOCTYPE, CW_XHTML_EXTENSIONS
-
-from cubicweb.web import ExplicitLogin, Redirect, RemoteCallFailed
+from cubicweb.web import ExplicitLogin, Redirect, RemoteCallFailed, json_dumps
+from cubicweb.web.formrenderers import FormRenderer
 from cubicweb.web.controller import Controller
 from cubicweb.web.views import vid_from_rset
 try:
@@ -31,8 +30,43 @@
     HAS_SEARCH_RESTRICTION = True
 except ImportError: # gae
     HAS_SEARCH_RESTRICTION = False
-    
-    
+
+
+def xhtml_wrap(source):
+    head = u'<?xml version="1.0"?>\n' + STRICT_DOCTYPE
+    return head + u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">%s</div>' % source.strip()
+
+def jsonize(func):
+    """decorator to sets correct content_type and calls `simplejson.dumps` on
+    results
+    """
+    def wrapper(self, *args, **kwargs):
+        self.req.set_content_type('application/json')
+        return json_dumps(func(self, *args, **kwargs))
+    wrapper.__name__ = func.__name__
+    return wrapper
+
+def xhtmlize(func):
+    """decorator to sets correct content_type and calls `xmlize` on results"""
+    def wrapper(self, *args, **kwargs):
+        self.req.set_content_type(self.req.html_content_type())
+        result = func(self, *args, **kwargs)
+        return xhtml_wrap(result)
+    wrapper.__name__ = func.__name__
+    return wrapper
+
+def check_pageid(func):
+    """decorator which checks the given pageid is found in the
+    user's session data
+    """
+    def wrapper(self, *args, **kwargs):
+        data = self.req.get_session_data(self.req.pageid)
+        if data is None:
+            raise RemoteCallFailed(self.req._('pageid-not-found'))
+        return func(self, *args, **kwargs)
+    return wrapper
+
+
 class LoginController(Controller):
     id = 'login'
 
@@ -45,25 +79,72 @@
             # Cookie authentication
             return self.appli.need_login_content(self.req)
 
-    
+
 class LogoutController(Controller):
     id = 'logout'
-    
+
     def publish(self, rset=None):
         """logout from the application"""
         return self.appli.session_handler.logout(self.req)
 
 
 class ViewController(Controller):
+    """standard entry point :
+    - build result set
+    - select and call main template
+    """
     id = 'view'
-    template = 'main'
-    
+    template = 'main-template'
+
     def publish(self, rset=None):
         """publish a request, returning an encoded string"""
-        template = self.req.property_value('ui.main-template')
-        if template not in self.vreg.registry('templates') :
-            template = self.template
-        return self.vreg.main_template(self.req, template, rset=rset)
+        view, rset = self._select_view_and_rset(rset)
+        self.add_to_breadcrumbs(view)
+        self.validate_cache(view)
+        template = self.appli.main_template_id(self.req)
+        return self.vreg.main_template(self.req, template, rset=rset, view=view)
+
+    def _select_view_and_rset(self, rset):
+        req = self.req
+        if rset is None and not hasattr(req, '_rql_processed'):
+            req._rql_processed = True
+            rset = self.process_rql(req.form.get('rql'))
+        if rset and rset.rowcount == 1 and '__method' in req.form:
+            entity = rset.get_entity(0, 0)
+            try:
+                method = getattr(entity, req.form.pop('__method'))
+                method()
+            except Exception, ex:
+                self.exception('while handling __method')
+                req.set_message(req._("error while handling __method: %s") % req._(ex))
+        vid = req.form.get('vid') or vid_from_rset(req, rset, self.schema)
+        try:
+            view = self.vreg.select_view(vid, req, rset)
+        except ObjectNotFound:
+            self.warning("the view %s could not be found", vid)
+            req.set_message(req._("The view %s could not be found") % vid)
+            vid = vid_from_rset(req, rset, self.schema)
+            view = self.vreg.select_view(vid, req, rset)
+        except NoSelectableObject:
+            if rset:
+                req.set_message(req._("The view %s can not be applied to this query") % vid)
+            else:
+                req.set_message(req._("You have no access to this view or it's not applyable to current data"))
+            self.warning("the view %s can not be applied to this query", vid)
+            vid = vid_from_rset(req, rset, self.schema)
+            view = self.vreg.select_view(vid, req, rset)
+        return view, rset
+
+    def add_to_breadcrumbs(self, view):
+        # update breadcrumps **before** validating cache, unless the view
+        # specifies explicitly it should not be added to breadcrumb or the
+        # view is a binary view
+        if view.add_to_breadcrumbs and not view.binary:
+            self.req.update_breadcrumbs()
+
+    def validate_cache(self, view):
+        view.set_http_cache_headers()
+        self.req.validate_cache()
 
     def execute_linkto(self, eid=None):
         """XXX __linkto parameter may cause security issue
@@ -85,7 +166,7 @@
             else:
                 rql = 'SET Y %s X WHERE X eid %%(x)s, Y eid %%(y)s' % rtype
             for teid in eids:
-                req.execute(rql, {'x': eid, 'y': typed_eid(teid)}, ('x', 'y')) 
+                req.execute(rql, {'x': eid, 'y': typed_eid(teid)}, ('x', 'y'))
 
 
 class FormValidatorController(Controller):
@@ -132,56 +213,65 @@
         except AttributeError:
             eid = err.entity
         return (False, (eid, err.errors))
-        
-def xmlize(source):
-    head = u'<?xml version="1.0"?>\n' + STRICT_DOCTYPE % CW_XHTML_EXTENSIONS
-    return head + u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">%s</div>' % source.strip()
 
-def jsonize(func):
-    """sets correct content_type and calls `simplejson.dumps` on results
-    """
-    def wrapper(self, *args, **kwargs):
-        self.req.set_content_type('application/json')
-        result = func(self, *args, **kwargs)
-        return simplejson.dumps(result)
-    return wrapper
-
-
-def check_pageid(func):
-    """decorator which checks the given pageid is found in the
-    user's session data
-    """
-    def wrapper(self, *args, **kwargs):
-        data = self.req.get_session_data(self.req.pageid)
-        if data is None:
-            raise RemoteCallFailed(self.req._('pageid-not-found'))
-        return func(self, *args, **kwargs)
-    return wrapper
-    
 
 class JSonController(Controller):
     id = 'json'
-    template = 'main'
 
     def publish(self, rset=None):
-        mode = self.req.form.get('mode', 'html')
+        """call js_* methods. Expected form keys:
+
+        :fname: the method name without the js_ prefix
+        :args: arguments list (json)
+
+        note: it's the responsability of js_* methods to set the correct
+        response content type
+        """
         self.req.pageid = self.req.form.get('pageid')
+        fname = self.req.form['fname']
+        try:
+            func = getattr(self, 'js_%s' % fname)
+        except AttributeError:
+            raise RemoteCallFailed('no %s method' % fname)
+        # no <arg> attribute means the callback takes no argument
+        args = self.req.form.get('arg', ())
+        if not isinstance(args, (list, tuple)):
+            args = (args,)
+        args = [simplejson.loads(arg) for arg in args]
         try:
-            func = getattr(self, '%s_exec' % mode)
-        except AttributeError, ex:
-            self.error('json controller got an unknown mode %r', mode)
-            self.error('\t%s', ex)
-            result = u''
-        else:
-            try:
-                result = func(rset)
-            except RemoteCallFailed:
-                raise
-            except Exception, ex:
-                self.exception('an exception occured on json request(rset=%s): %s',
-                               rset, ex)
-                raise RemoteCallFailed(repr(ex))
-        return result.encode(self.req.encoding)
+            result = func(*args)
+        except RemoteCallFailed:
+            raise
+        except Exception, ex:
+            self.exception('an exception occured while calling js_%s(%s): %s',
+                           fname, args, ex)
+            raise RemoteCallFailed(repr(ex))
+        if result is None:
+            return ''
+        # get unicode on @htmlize methods, encoded string on @jsonize methods
+        elif isinstance(result, unicode):
+            return result.encode(self.req.encoding)
+        return result
+
+    def _rebuild_posted_form(self, names, values, action=None):
+        form = {}
+        for name, value in zip(names, values):
+            # remove possible __action_xxx inputs
+            if name.startswith('__action'):
+                continue
+            # form.setdefault(name, []).append(value)
+            if name in form:
+                curvalue = form[name]
+                if isinstance(curvalue, list):
+                    curvalue.append(value)
+                else:
+                    form[name] = [curvalue, value]
+            else:
+                form[name] = value
+        # simulate click on __action_%s button to help the controller
+        if action:
+            form['__action_%s' % action] = u'whatever'
+        return form
 
     def _exec(self, rql, args=None, eidkey=None, rocheck=True):
         """json mode: execute RQL and return resultset as json"""
@@ -194,31 +284,15 @@
             return None
         return None
 
-    @jsonize
-    def json_exec(self, rset=None):
-        """json mode: execute RQL and return resultset as json"""
-        rql = self.req.form.get('rql')
-        if rset is None and rql:
-            rset = self._exec(rql)
-        return rset and rset.rows or []
-
-    def _set_content_type(self, vobj, data):
-        """sets req's content type according to vobj's content type
-        (and xmlize data if needed)
-        """
-        content_type = vobj.content_type
-        if content_type == 'application/xhtml+xml':
-            self.req.set_content_type(content_type)
-            return xmlize(data)
-        return data
-
-    def html_exec(self, rset=None):
-        """html mode: execute query and return the view as HTML"""
+    @xhtmlize
+    def js_view(self):
+        # XXX try to use the page-content template
         req = self.req
         rql = req.form.get('rql')
-        if rset is None and rql:
+        if rql:
             rset = self._exec(rql)
-            
+        else:
+            rset = None
         vid = req.form.get('vid') or vid_from_rset(req, rset, self.schema)
         try:
             view = self.vreg.select_view(vid, req, rset)
@@ -239,49 +313,57 @@
             if divid == 'pageContent':
                 stream.write(u'<div id="contentmain">')
         view.dispatch()
+        extresources = req.html_headers.getvalue(skiphead=True)
+        if extresources:
+            stream.write(u'<div class="ajaxHtmlHead">\n') # XXX use a widget ?
+            stream.write(extresources)
+            stream.write(u'</div>\n')
         if req.form.get('paginate') and divid == 'pageContent':
             stream.write(u'</div></div>')
-        source = stream.getvalue()
-        return self._set_content_type(view, source)
+        return stream.getvalue()
 
-    def rawremote_exec(self, rset=None):
-        """like remote_exec but doesn't change content type"""
-        # no <arg> attribute means the callback takes no argument
-        args = self.req.form.get('arg', ())
-        if not isinstance(args, (list, tuple)):
-            args = (args,)
-        fname = self.req.form['fname']
-        args = [simplejson.loads(arg) for arg in args]
-        try:
-            func = getattr(self, 'js_%s' % fname)
-        except AttributeError:
-            self.exception('rawremote_exec fname=%s', fname)
-            return u""
-        return func(*args)
+    @xhtmlize
+    def js_prop_widget(self, propkey, varname, tabindex=None):
+        """specific method for CWProperty handling"""
+        entity = self.vreg.etype_class('CWProperty')(self.req, None, None)
+        entity.eid = varname
+        entity['pkey'] = propkey
+        form = self.vreg.select_object('forms', 'edition', self.req, None,
+                                       entity=entity)
+        form.form_build_context()
+        vfield = form.field_by_name('value')
+        renderer = FormRenderer()
+        return vfield.render(form, renderer, tabindex=tabindex) \
+               + renderer.render_help(form, vfield)
 
-    remote_exec = jsonize(rawremote_exec)
-        
-    def _rebuild_posted_form(self, names, values, action=None):
-        form = {}
-        for name, value in zip(names, values):
-            # remove possible __action_xxx inputs
-            if name.startswith('__action'):
-                continue
-            # form.setdefault(name, []).append(value)
-            if name in form:
-                curvalue = form[name]
-                if isinstance(curvalue, list):
-                    curvalue.append(value)
-                else:
-                    form[name] = [curvalue, value]
-            else:
-                form[name] = value
-        # simulate click on __action_%s button to help the controller
-        if action:
-            form['__action_%s' % action] = u'whatever'
-        return form
-    
+    @xhtmlize
+    def js_component(self, compid, rql, registry='components', extraargs=None):
+        if rql:
+            rset = self._exec(rql)
+        else:
+            rset = None
+        comp = self.vreg.select_object(registry, compid, self.req, rset)
+        if extraargs is None:
+            extraargs = {}
+        else: # we receive unicode keys which is not supported by the **syntax
+            extraargs = dict((str(key), value)
+                             for key, value in extraargs.items())
+        extraargs = extraargs or {}
+        return comp.dispatch(**extraargs)
+
+    @check_pageid
+    @xhtmlize
+    def js_inline_creation_form(self, peid, ttype, rtype, role):
+        view = self.vreg.select_view('inline-creation', self.req, None,
+                                     etype=ttype, peid=peid, rtype=rtype,
+                                     role=role)
+        return view.dispatch(etype=ttype, peid=peid, rtype=rtype, role=role)
+
+    @jsonize
     def js_validate_form(self, action, names, values):
+        return self.validate_form(action, names, values)
+
+    def validate_form(self, action, names, values):
         # XXX this method (and correspoding js calls) should use the new
         #     `RemoteCallFailed` mechansim
         self.req.form = self._rebuild_posted_form(names, values, action)
@@ -308,56 +390,49 @@
             return (False, self.req._(str(err)))
         return (False, '???')
 
+    @jsonize
     def js_edit_field(self, action, names, values, rtype, eid):
-        success, args = self.js_validate_form(action, names, values)
+        success, args = self.validate_form(action, names, values)
         if success:
+            # Any X,N where we don't seem to use N is an optimisation
+            # printable_value won't need to query N again
             rset = self.req.execute('Any X,N WHERE X eid %%(x)s, X %s N' % rtype,
                                     {'x': eid}, 'x')
             entity = rset.get_entity(0, 0)
             return (success, args, entity.printable_value(rtype))
         else:
             return (success, args, None)
-            
-    def js_rql(self, rql):
-        rset = self._exec(rql)
-        return rset and rset.rows or []
-    
+
+    @jsonize
+    def js_edit_relation(self, action, names, values,
+                         rtype, eid, role='subject', vid='list'):
+        # XXX validate_form
+        success, args = self.validate_form(action, names, values)
+        if success:
+            entity = self.req.eid_rset(eid).get_entity(0, 0)
+            rset = entity.related('person_in_charge', role)
+            return (success, args, self.view(vid, rset))
+        else:
+            return (success, args, None)
+
+    @jsonize
     def js_i18n(self, msgids):
         """returns the translation of `msgid`"""
         return [self.req._(msgid) for msgid in msgids]
 
+    @jsonize
     def js_format_date(self, strdate):
         """returns the formatted date for `msgid`"""
-        date = DateFromString(strdate)
+        date = strptime(strdate, '%Y-%m-%d %H:%M:%S')
         return self.format_date(date)
 
+    @jsonize
     def js_external_resource(self, resource):
         """returns the URL of the external resource named `resource`"""
         return self.req.external_resource(resource)
 
-    def js_prop_widget(self, propkey, varname, tabindex=None):
-        """specific method for EProperty handling"""
-        w = self.vreg.property_value_widget(propkey, req=self.req)
-        entity = self.vreg.etype_class('EProperty')(self.req, None, None)
-        entity.eid = varname
-        self.req.form['value'] = self.vreg.property_info(propkey)['default']
-        return w.edit_render(entity, tabindex, includehelp=True)
-
-    def js_component(self, compid, rql, registry='components', extraargs=None):
-        if rql:
-            rset = self._exec(rql)
-        else:
-            rset = None
-        comp = self.vreg.select_object(registry, compid, self.req, rset)
-        if extraargs is None:
-            extraargs = {}
-        else: # we receive unicode keys which is not supported by the **syntax
-            extraargs = dict((str(key), value)
-                             for key, value in extraargs.items())
-        extraargs = extraargs or {}
-        return self._set_content_type(comp, comp.dispatch(**extraargs))
-
     @check_pageid
+    @jsonize
     def js_user_callback(self, cbname):
         page_data = self.req.get_session_data(self.req.pageid, {})
         try:
@@ -365,54 +440,16 @@
         except KeyError:
             return None
         return cb(self.req)
-    
-    def js_unregister_user_callback(self, cbname):
-        self.req.unregister_callback(self.req.pageid, cbname)
-
-    def js_unload_page_data(self):
-        self.req.del_session_data(self.req.pageid)
-        
-    def js_cancel_edition(self, errorurl):
-        """cancelling edition from javascript
-
-        We need to clear associated req's data :
-          - errorurl
-          - pending insertions / deletions
-        """
-        self.req.cancel_edition(errorurl)
-    
-    @check_pageid
-    def js_inline_creation_form(self, peid, ptype, ttype, rtype, role):
-        view = self.vreg.select_view('inline-creation', self.req, None,
-                                     etype=ttype, ptype=ptype, peid=peid,
-                                     rtype=rtype, role=role)
-        source = view.dispatch(etype=ttype, ptype=ptype, peid=peid, rtype=rtype,
-                               role=role)
-        return self._set_content_type(view, source)
-
-    def js_remove_pending_insert(self, (eidfrom, rel, eidto)):
-        self._remove_pending(eidfrom, rel, eidto, 'insert')
-        
-    def js_add_pending_insert(self, (eidfrom, rel, eidto)):
-        self._add_pending(eidfrom, rel, eidto, 'insert')
-        
-    def js_add_pending_inserts(self, tripletlist):
-        for eidfrom, rel, eidto in tripletlist:
-            self._add_pending(eidfrom, rel, eidto, 'insert')
-        
-    def js_remove_pending_delete(self, (eidfrom, rel, eidto)):
-        self._remove_pending(eidfrom, rel, eidto, 'delete')
-    
-    def js_add_pending_delete(self, (eidfrom, rel, eidto)):
-        self._add_pending(eidfrom, rel, eidto, 'delete')
 
     if HAS_SEARCH_RESTRICTION:
+        @jsonize
         def js_filter_build_rql(self, names, values):
             form = self._rebuild_posted_form(names, values)
             self.req.form = form
             builder = FilterRQLBuilder(self.req)
             return builder.build_rql()
 
+        @jsonize
         def js_filter_select_content(self, facetids, rql):
             rqlst = self.vreg.parse(self.req, rql) # XXX Union unsupported yet
             mainvar = prepare_facets_rqlst(rqlst)[0]
@@ -422,13 +459,33 @@
                 update_map[facetid] = facet.possible_values()
             return update_map
 
+    def js_unregister_user_callback(self, cbname):
+        self.req.unregister_callback(self.req.pageid, cbname)
+
+    def js_unload_page_data(self):
+        self.req.del_session_data(self.req.pageid)
+
+    def js_cancel_edition(self, errorurl):
+        """cancelling edition from javascript
+
+        We need to clear associated req's data :
+          - errorurl
+          - pending insertions / deletions
+        """
+        self.req.cancel_edition(errorurl)
+
     def js_delete_bookmark(self, beid):
-        try:
-            rql = 'DELETE B bookmarked_by U WHERE B eid %(b)s, U eid %(u)s'
-            self.req.execute(rql, {'b': typed_eid(beid), 'u' : self.req.user.eid})
-        except Exception, ex:
-            self.exception(unicode(ex))
-            return self.req._('Problem occured')
+        rql = 'DELETE B bookmarked_by U WHERE B eid %(b)s, U eid %(u)s'
+        self.req.execute(rql, {'b': typed_eid(beid), 'u' : self.req.user.eid})
+
+    def js_set_cookie(self, cookiename, cookievalue):
+        # XXX we should consider jQuery.Cookie
+        cookiename, cookievalue = str(cookiename), str(cookievalue)
+        cookies = self.req.get_cookie()
+        cookies[cookiename] = cookievalue
+        self.req.set_cookie(cookies, cookiename)
+
+    # relations edition stuff ##################################################
 
     def _add_pending(self, eidfrom, rel, eidto, kind):
         key = 'pending_%s' % kind
@@ -437,7 +494,7 @@
         self.req.set_session_data(key, pendings)
 
     def _remove_pending(self, eidfrom, rel, eidto, kind):
-        key = 'pending_%s' % kind        
+        key = 'pending_%s' % kind
         try:
             pendings = self.req.get_session_data(key)
             pendings.remove( (typed_eid(eidfrom), rel, typed_eid(eidto)) )
@@ -446,6 +503,21 @@
         else:
             self.req.set_session_data(key, pendings)
 
+    def js_remove_pending_insert(self, (eidfrom, rel, eidto)):
+        self._remove_pending(eidfrom, rel, eidto, 'insert')
+
+    def js_add_pending_inserts(self, tripletlist):
+        for eidfrom, rel, eidto in tripletlist:
+            self._add_pending(eidfrom, rel, eidto, 'insert')
+
+    def js_remove_pending_delete(self, (eidfrom, rel, eidto)):
+        self._remove_pending(eidfrom, rel, eidto, 'delete')
+
+    def js_add_pending_delete(self, (eidfrom, rel, eidto)):
+        self._add_pending(eidfrom, rel, eidto, 'delete')
+
+    # XXX specific code. Kill me and my AddComboBox friend
+    @jsonize
     def js_add_and_link_new_entity(self, etype_to, rel, eid_to, etype_from, value_from):
         # create a new entity
         eid_from = self.req.execute('INSERT %s T : T name "%s"' % ( etype_from, value_from ))[0][0]
@@ -453,16 +525,10 @@
         rql = 'SET F %(rel)s T WHERE F eid %(eid_to)s, T eid %(eid_from)s' % {'rel' : rel, 'eid_to' : eid_to, 'eid_from' : eid_from}
         return eid_from
 
-    def js_set_cookie(self, cookiename, cookievalue):
-        # XXX we should consider jQuery.Cookie
-        cookiename, cookievalue = str(cookiename), str(cookievalue)
-        cookies = self.req.get_cookie()
-        cookies[cookiename] = cookievalue
-        self.req.set_cookie(cookies, cookiename)
 
 class SendMailController(Controller):
     id = 'sendmail'
-    require_groups = ('managers', 'users')
+    __select__ = match_user_groups('managers', 'users')
 
     def recipients(self):
         """returns an iterator on email's recipients as entities"""
@@ -494,12 +560,12 @@
         msg = format_mail({'email' : self.req.user.get_email(),
                            'name' : self.req.user.dc_title(),},
                           [recipient], body, subject)
-        self.smtp.sendmail(helo_addr, [recipient], msg.as_string())    
+        self.smtp.sendmail(helo_addr, [recipient], msg.as_string())
 
     def publish(self, rset=None):
         # XXX this allow anybody with access to an cubicweb application to use it as a mail relay
         body = self.req.form['mailbody']
-        subject = self.req.form['mailsubject']
+        subject = self.req.form['subject']
         for recipient in self.recipients():
             text = body % recipient.as_email_context()
             self.sendmail(recipient.get_email(), subject, text)
@@ -510,11 +576,11 @@
 
 class MailBugReportController(SendMailController):
     id = 'reportbug'
-    __selectors__ = (yes,)
+    __select__ = yes()
 
     def publish(self, rset=None):
         body = self.req.form['description']
         self.sendmail(self.config['submit-mail'], _('%s error report') % self.config.appid, body)
         url = self.build_url(__message=self.req._('bug report sent'))
         raise Redirect(url)
-    
+
--- a/web/views/baseforms.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/baseforms.py	Mon May 04 13:09:48 2009 +0200
@@ -14,205 +14,19 @@
 from logilab.mtconverter import html_escape
 from logilab.common.decorators import cached
 
-from cubicweb.interfaces import IWorkflowable
-from cubicweb.common.utils import make_uid
-from cubicweb.common.uilib import cut
-from cubicweb.common.selectors import (accept_etype, match_kwargs,
-                                    one_line_rset, implement_interface,
-                                    match_form_params, accept)
-from cubicweb.common.view import EntityView
-from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs, eid_param
+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 EntityForm, relation_id
+from cubicweb.web.form import FormMixIn
+from cubicweb.web.views.autoform import AutomaticEntityForm
 
 _ = unicode
-
-class DeleteConfForm(EntityForm):
-    id = 'deleteconf'
-    title = _('delete')
-    domid = 'deleteconf'
-    onsubmit = None
-    # don't use navigation, all entities asked to be deleted should be displayed
-    # else we will only delete the displayed page
-    need_navigation = False
+    
     
-    def call(self):
-        """ask for confirmation before real deletion"""
-        _ = self.req._
-        self.req.add_css('cubicweb.form.css')
-        self.req.add_js('cubicweb.edition.js')
-        self.w(u'<script type="text/javascript">updateMessage(\'%s\');</script>\n' % _('this action is not reversible!'))
-        # XXX above message should have style of a warning
-        self.w(u'<h4>%s</h4>\n' % _('Do you want to delete the following element(s) ?'))
-        if self.onsubmit:
-            self.w(u'<form id="deleteconf" action="%s" onsubmit="%s" method="post">'
-                   % (self.build_url(), self.onsubmit))
-        else:
-            self.w(u'<form id="deleteconf" action="%s" method="post">'
-                   % (self.build_url()))
-            
-        self.w(u'<fieldset>\n')
-        self.display_rset()
-        #self.w(u'<input type="hidden" name="rql" value="%s"/>' % self.req.form['rql'])
-        self.w(u'<input type="hidden" name="__form_id" value="%s"/>' % self.id)
-        self.w(self.button_delete(label=stdmsgs.YES))
-        self.w(self.button_cancel(label=stdmsgs.NO))
-        for param in NAV_FORM_PARAMETERS:
-            value = self.req.form.get(param)
-            if value:
-                self.w(u'<input type="hidden" name="%s" value="%s"/>' % (param, value))
-        self.w(u'</fieldset></form>\n')
-
-    def display_rset(self):
-        self.w(u'<ul>\n')
-        done = set()
-        for i in xrange(self.rset.rowcount):
-            if self.rset[i][0] in done:
-                continue
-            done.add(self.rset[i][0])
-            self.cell_call(i, 0)
-        self.w(u'</ul>\n')
-        
-    def cell_call(self, row, col):
-        entity = self.entity(row, col)
-        self.w(u'<li>')
-        self.w(u'<input type="hidden" name="eid" value="%s" />' % entity.eid)
-        self.w(u'<input type="hidden" name="%s" value="%s"/>\n'
-               % (eid_param('__type', entity.eid), self.rset.description[row][0]))
-        self.w(u'<a href="%s">' % html_escape(entity.absolute_url()))
-        # don't use outofcontext view or any other that may contain inline edition form
-        self.w(html_escape(entity.view('textoutofcontext')))
-        self.w(u'</a>')
-        self.w(u'</li>')
-
-
-class ChangeStateForm(EntityForm):
-    id = 'statuschange'
-    title = _('status change')
-
-    __selectors__ = (implement_interface, match_form_params)
-    accepts_interfaces = (IWorkflowable,)
-    form_params = ('treid',)
-
-    def cell_call(self, row, col, vid='secondary'):
-        entity = self.entity(row, col)
-        eid = entity.eid
-        state = entity.in_state[0]
-        transition = self.req.eid_rset(self.req.form['treid']).get_entity(0, 0)
-        dest = transition.destination()
-        self.req.add_js('cubicweb.edition.js')
-        self.req.add_css('cubicweb.form.css')
-        _ = self.req._
-        self.w(self.error_message())
-        self.w(u'<h4>%s %s</h4>\n' % (_(transition.name), entity.view('oneline')))
-        msg = _('status will change from %(st1)s to %(st2)s') % {
-            'st1': _(state.name),
-            'st2': _(dest.name)}
-        self.w(u'<p>%s</p>\n' % msg)
-        self.w(u'<form action="%s" onsubmit="return freezeFormButtons(\'entityForm\');" method="post" id="entityForm">\n'
-               % self.build_url('edit'))
-        self.w(u'<div id="progress">%s</div>' % _('validating...'))
-        self.w(u'<fieldset>\n')
-        #self.w(u'<input id="errorurl" type="hidden" name="__errorurl" value="%s"/>\n'
-        #       % html_escape(self.req.url()))
-        self.w(u'<input type="hidden" name="__form_id" value="%s"/>\n' % self.id)
-        self.w(u'<input type="hidden" name="eid" value="%s" />' % eid)
-        self.w(u'<input type="hidden" name="%s" value="%s"/>\n'
-               % (eid_param('__type', eid), entity.e_schema))
-        self.w(u'<input type="hidden" name="%s" value="%s"/>\n'
-               % (eid_param('state', eid), dest.eid))
-        self.w(u'<input type="hidden" name="__redirectpath" value="%s"/>\n'
-               % html_escape(self.redirectpath(entity)))
-        self.fill_form(entity, state, dest)
-        self.w(u'<input type="hidden" name="__method" value="set_state"/>\n')
-        self.w(self.button_ok(label=stdmsgs.YES, tabindex=self.req.next_tabindex()))
-        self.w(self.button_cancel(label=stdmsgs.NO, tabindex=self.req.next_tabindex()))
-        self.w(u'</fieldset>\n')
-        self.w(u'</form>')
-        
-    def fill_form(self, entity, state, dest):
-        # hack to use the widget for comment_format
-        trinfo = self.vreg.etype_class('TrInfo')(self.req, None)
-        # widget are cached, copy it since we want to modify its name attribute
-        wdg = trinfo.get_widget('comment_format')
-        wdg.name = 'trcommentformat'
-        # set a value in entity to avoid lookup for a non existant attribute...
-        trinfo['trcommentformat'] = u''
-        # comment format/content have to be grouped using the original entity eid
-        wdg.rname = eid_param('trcommentformat', entity.eid)
-        self.w(wdg.render_label(trinfo))
-        self.w(wdg._edit_render(trinfo))
-        self.w(u'<br/>\n')
-        cformname = eid_param('trcomment', entity.eid)
-        self.w(u'<label for="%s">%s</label>\n' % (cformname, self.req._('comment:')))
-        self.w(u'<textarea rows="10" cols="80" name="%s" tabindex="%s"></textarea><br/>\n'
-               % (cformname, self.req.next_tabindex()))
-
-    def redirectpath(self, entity):
-        return entity.rest_path()
-
-
-class ClickAndEditForm(EntityForm):
-    id = 'reledit'
-    __selectors__ = (match_kwargs, )
-    expected_kwargs = ('rtype',)
-
-    #FIXME editableField class could be toggleable from userprefs
-
-    EDITION_BODY = '''
-<div class="editableField" id="%(divid)s"
-      ondblclick="showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')">%(value)s</div>
-<form style="display: none;" onsubmit="return inlineValidateForm('%(divid)s-form', '%(rtype)s', '%(eid)s', '%(divid)s', %(reload)s);" id="%(divid)s-form" action="#">
-<fieldset>
-<input type="hidden" name="eid" value="%(eid)s" />
-<input type="hidden" name="__maineid" value="%(eid)s" />
-<input type="hidden" name="__type:%(eid)s" value="%(etype)s" />
-%(attrform)s
-</fieldset>
-<div class="buttonbar">
-%(ok)s
-%(cancel)s
-</div>
-</form>
-'''
-    def cell_call(self, row, col, rtype=None, role='subject', reload=False):
-        entity = self.entity(row, col)
-        if getattr(entity, rtype) is None:
-            value = self.req._('not specified')
-        else:
-            value = entity.printable_value(rtype)
-        if not entity.has_perm('update'):
-            self.w(value)
-            return
-        self.req.add_js( ('cubicweb.ajax.js', 'cubicweb.edition.js') )
-        eid = entity.eid
-        edit_key = make_uid('%s-%s' % (rtype, eid))
-        divid = 'd%s' % edit_key
-        widget = entity.get_widget(rtype, 'subject')
-        eschema = entity.e_schema
-        attrform = widget.edit_render(entity, useid='i%s' % edit_key)
-        ok = (u'<input class="validateButton" type="submit" name="__action_apply" value="%s" tabindex="%s" />'
-              % (self.req._(stdmsgs.BUTTON_OK), self.req.next_tabindex()))
-        cancel = (u'<input class="validateButton" type="button" '
-                  'value="%s" onclick="cancelInlineEdit(%s, \'%s\', \'%s\')"  tabindex="%s" />'
-                  % (self.req._(stdmsgs.BUTTON_CANCEL), eid, rtype, divid,
-                     self.req.next_tabindex()))
-        self.w(self.EDITION_BODY % {
-                'eid': eid,
-                'rtype': rtype,
-                'etype': entity.e_schema,
-                'attrform': attrform,
-                'action' : self.build_url('edit'), # NOTE: actually never gets called
-                'ok': ok,
-                'cancel': cancel,
-                'value': value,
-                'reload': dumps(reload),
-                'divid': divid,
-                })
-
-
-class EditionForm(EntityForm):
+class EditionForm(FormMixIn, EntityView):
     """primary entity edition form
 
     When generating a new attribute_input, the editor will look for a method
@@ -221,12 +35,12 @@
     dynamic default values such as the 'tomorrow' date or the user's login
     being connected
     """    
-    __selectors__ = (one_line_rset, accept)
+    id = 'edition'
+    __select__ = one_line_rset() & non_final_entity()
 
-    id = 'edition'
     title = _('edition')
     controller = 'edit'
-    skip_relations = EntityForm.skip_relations.copy()
+    skip_relations = set()
     
     EDITION_BODY = u'''\
  %(errormsg)s
@@ -256,8 +70,7 @@
 '''
 
     def cell_call(self, row, col, **kwargs):
-        self.req.add_js( ('cubicweb.ajax.js', 'cubicweb.edition.js') )
-        self.req.add_css('cubicweb.form.css')
+        self.req.add_js( ('cubicweb.ajax.js', ) )
         entity = self.complete_entity(row, col)
         self.edit_form(entity, kwargs)
 
@@ -507,7 +320,8 @@
     # should_* method extracted to allow overriding
     
     def should_inline_relation_form(self, entity, rschema, targettype, role):
-        return entity.rtags.is_inlined(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+'
@@ -527,20 +341,24 @@
 
     
 class CreationForm(EditionForm):
-    __selectors__ = (accept_etype, )
+    __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', 'cubicweb.edition.js') )
-        self.req.add_css('cubicweb.form.css')
+        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):
@@ -605,7 +423,8 @@
     def should_inline_relation_form(self, entity, rschema, targettype, role):
         if rschema == self.rschema:
             return False
-        return entity.rtags.is_inlined(rschema, targettype, role)
+        return AutomaticEntityForm.rinlined.etype_get(entity.id, rschema, role,
+                                                      targettype)
 
     @cached
     def keep_entity(self, entity):
@@ -636,102 +455,6 @@
                      }
         ctx.update(local_ctx)
         return ctx
-
-
-class InlineEntityCreationForm(InlineFormMixIn, CreationForm):
-    id = 'inline-creation'
-    __selectors__ = (match_kwargs, accept_etype)
-    expected_kwargs = ('ptype', 'peid', 'rtype')
-    
-    EDITION_BODY = u'''\
-<div id="div-%(parenteid)s-%(rtype)s-%(eid)s" class="inlinedform">
- <div class="iformBody">
- <div class="iformTitle"><span>%(title)s</span> #<span class="icounter">1</span> [<a href="javascript: removeInlineForm('%(parenteid)s', '%(rtype)s', '%(eid)s'); noop();">%(removemsg)s</a>]</div>
- <fieldset class="subentity">
- %(attrform)s
- %(relattrform)s
- </fieldset>
- </div>
- <fieldset class="hidden" id="fs-%(parenteid)s-%(rtype)s-%(eid)s">
-%(base)s
- <input type="hidden" value="%(novalue)s" name="edit%(so)s-%(rtype)s:%(parenteid)s" />
- <input id="rel-%(parenteid)s-%(rtype)s-%(eid)s" type="hidden" value="%(eid)s" name="%(rtype)s:%(parenteid)s" />
- </fieldset>
-</div>''' # do not insert trailing space or \n here !
-
-    def call(self, etype, ptype, peid, rtype, role='subject', **kwargs):
-        """
-        :param etype: the entity type being created in the inline form
-        :param parent: the parent entity hosting the inline form
-        :param rtype: the relation bridging `etype` and `parent`
-        :param role: the role played by the `parent` in the relation
-        """
-        self.req.add_css('cubicweb.form.css')
-        try:
-            entity = self.vreg.etype_class(etype)(self.req, None, None)
-        except:
-            self.w(self.req._('no such entity type %s') % etype)
-            return
-        self.edit_form(entity, ptype, peid, rtype, role, **kwargs)
-    
-    
-
-
-class InlineEntityEditionForm(InlineFormMixIn, EditionForm):
-    id = 'inline-edition'
-    __selectors__ = (accept, match_kwargs)
-    expected_kwargs = ('ptype', 'peid', 'rtype')
-    
-    EDITION_BODY = u'''\
-<div onclick="restoreInlinedEntity('%(parenteid)s', '%(rtype)s', '%(eid)s')" id="div-%(parenteid)s-%(rtype)s-%(eid)s" class="inlinedform">   
-<div id="notice-%(parenteid)s-%(rtype)s-%(eid)s" class="notice">%(notice)s</div>
-<div class="iformTitle"><span>%(title)s</span>  #<span class="icounter">%(count)s</span> [<a href="javascript: removeInlinedEntity('%(parenteid)s', '%(rtype)s', '%(eid)s'); noop();">%(removemsg)s</a>]</div>
- <div class="iformBody">
- <fieldset class="subentity">
- %(attrform)s
- </fieldset>
- %(relattrform)s
- </div>
- <fieldset id="fs-%(parenteid)s-%(rtype)s-%(eid)s">
-%(base)s
- <input type="hidden" value="%(eid)s" name="edit%(so)s-%(rtype)s:%(parenteid)s" />
- %(rinput)s
- </fieldset>
-</div>''' # do not insert trailing space or \n here !
-
-    rel_input = u'''<input id="rel-%(parenteid)s-%(rtype)s-%(eid)s" type="hidden" value="%(eid)s" name="%(rtype)s:%(parenteid)s" />'''
- 
-    def call(self, **kwargs):
-        """redefine default View.call() method to avoid automatic
-        insertions of <div class="section"> between each row of
-        the resultset
-        """
-        self.req.add_css('cubicweb.form.css')
-        rset = self.rset
-        for i in xrange(len(rset)):
-            self.wview(self.id, rset, row=i, **kwargs)
-
-    def cell_call(self, row, col, ptype, peid, rtype, role='subject', **kwargs):
-        """
-        :param parent: the parent entity hosting the inline form
-        :param rtype: the relation bridging `etype` and `parent`
-        :param role: the role played by the `parent` in the relation
-        """
-        entity = self.entity(row, col)
-        self.edit_form(entity, ptype, peid, rtype, role, **kwargs)
-
-
-    def form_context(self, entity, kwargs):
-        ctx = super(InlineEntityEditionForm, self).form_context(entity, kwargs)
-        if self.keep_entity(entity):
-            ctx['rinput'] = self.rel_input % ctx
-            ctx['todelete'] = u''
-        else:
-            ctx['rinput'] = u''
-            ctx['todelete'] = u'checked="checked"'
-        ctx['count'] = entity.row + 1
-        return ctx
-    
     
 
 class CopyEditionForm(EditionForm):
@@ -739,8 +462,7 @@
     title = _('copy edition')
 
     def cell_call(self, row, col, **kwargs):
-        self.req.add_js(('cubicweb.ajax.js', 'cubicweb.edition.js'))
-        self.req.add_css('cubicweb.form.css')
+        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. 
@@ -777,8 +499,7 @@
         return self.req._('element copied')
        
     
-
-class TableEditForm(EntityForm):
+class TableEditForm(FormMixIn, EntityView):
     id = 'muledit'
     title = _('multiple edit')
 
@@ -821,8 +542,6 @@
         """
         req = self.req
         form = req.form
-        req.add_js('cubicweb.edition.js')
-        req.add_css('cubicweb.form.css')
         _ = req._
         sampleentity = self.complete_entity(0)
         attrheaders = [u'<th>%s</th>' % rdef[0].display_name(req, rdef[-1])
@@ -878,128 +597,10 @@
                                   'widget': wobj.edit_render(entity)})
         w(u'</tr>')
         return '\n'.join(html)
-        
-
-class UnrelatedDivs(EntityView):
-    id = 'unrelateddivs'
-    __selectors__ = (match_form_params,)
-    form_params = ('relation',)
-
-    @property
-    def limit(self):
-        if self.req.form.get('__force_display'):
-            return None
-        return self.req.property_value('navigation.related-limit') + 1
-
-    def cell_call(self, row, col):
-        entity = self.entity(row, col)
-        relname, target = self.req.form.get('relation').rsplit('_', 1)
-        rschema = self.schema.rschema(relname)
-        hidden = 'hidden' in self.req.form
-        is_cell = 'is_cell' in self.req.form
-        self.w(self.build_unrelated_select_div(entity, rschema, target,
-                                               is_cell=is_cell, hidden=hidden))
-
-    def build_unrelated_select_div(self, entity, rschema, target,
-                                   is_cell=False, hidden=True):
-        options = []
-        divid = 'div%s_%s_%s' % (rschema.type, target, entity.eid)
-        selectid = 'select%s_%s_%s' % (rschema.type, target, entity.eid)
-        if rschema.symetric or target == 'subject':
-            targettypes = rschema.objects(entity.e_schema)
-            etypes = '/'.join(sorted(etype.display_name(self.req) for etype in targettypes))
-        else:
-            targettypes = rschema.subjects(entity.e_schema)
-            etypes = '/'.join(sorted(etype.display_name(self.req) for etype in targettypes))
-        etypes = cut(etypes, self.req.property_value('navigation.short-line-size'))
-        options.append('<option>%s %s</option>' % (self.req._('select a'), etypes))
-        options += self._get_select_options(entity, rschema, target)
-        options += self._get_search_options(entity, rschema, target, targettypes)
-        if 'Basket' in self.schema: # XXX
-            options += self._get_basket_options(entity, rschema, target, targettypes)
-        relname, target = self.req.form.get('relation').rsplit('_', 1)
-        return u"""\
-<div class="%s" id="%s">
-  <select id="%s" onchange="javascript: addPendingInsert(this.options[this.selectedIndex], %s, %s, '%s');">
-    %s
-  </select>
-</div>
-""" % (hidden and 'hidden' or '', divid, selectid, html_escape(dumps(entity.eid)),
-       is_cell and 'true' or 'null', relname, '\n'.join(options))
-
-    def _get_select_options(self, entity, rschema, target):
-        """add options to search among all entities of each possible type"""
-        options = []
-        eid = entity.eid
-        pending_inserts = self.req.get_pending_inserts(eid)
-        rtype = rschema.type
-        for eview, reid in entity.vocabulary(rschema, target, self.limit):
-            if reid is None:
-                options.append('<option class="separator">-- %s --</option>' % html_escape(eview))
-            else:
-                optionid = relation_id(eid, rtype, target, reid)
-                if optionid not in pending_inserts:
-                    # prefix option's id with letters to make valid XHTML wise
-                    options.append('<option id="id%s" value="%s">%s</option>' %
-                                   (optionid, reid, html_escape(eview)))
-        return options
-
-    def _get_search_options(self, entity, rschema, target, targettypes):
-        """add options to search among all entities of each possible type"""
-        options = []
-        _ = self.req._
-        for eschema in targettypes:
-            mode = '%s:%s:%s:%s' % (target, entity.eid, rschema.type, eschema)
-            url = self.build_url(entity.rest_path(), vid='search-associate',
-                                 __mode=mode)
-            options.append((eschema.display_name(self.req),
-                            '<option value="%s">%s %s</option>' % (
-                html_escape(url), _('Search for'), eschema.display_name(self.req))))
-        return [o for l, o in sorted(options)]
-
-    def _get_basket_options(self, entity, rschema, target, targettypes):
-        options = []
-        rtype = rschema.type
-        _ = self.req._
-        for basketeid, basketname in self._get_basket_links(self.req.user.eid,
-                                                            target, targettypes):
-            optionid = relation_id(entity.eid, rtype, target, basketeid)
-            options.append('<option id="%s" value="%s">%s %s</option>' % (
-                optionid, basketeid, _('link to each item in'), html_escape(basketname)))
-        return options
-
-    def _get_basket_links(self, ueid, target, targettypes):
-        targettypes = set(targettypes)
-        for basketeid, basketname, elements in self._get_basket_info(ueid):
-            baskettypes = elements.column_types(0)
-            # if every elements in the basket can be attached to the
-            # edited entity
-            if baskettypes & targettypes:
-                yield basketeid, basketname
-            
-    def _get_basket_info(self, ueid):
-        basketref = []
-        basketrql = 'Any B,N WHERE B is Basket, B owned_by U, U eid %(x)s, B name N'
-        basketresultset = self.req.execute(basketrql, {'x': ueid}, 'x')
-        for result in basketresultset:
-            basketitemsrql = 'Any X WHERE X in_basket B, B eid %(x)s'
-            rset = self.req.execute(basketitemsrql, {'x': result[0]}, 'x')
-            basketref.append((result[0], result[1], rset))
-        return basketref
 
 
-class ComboboxView(EntityView):
-    """the view used in combobox (unrelated entities)
+# XXX bw compat
 
-    THIS IS A TEXT VIEW. DO NOT HTML_ESCAPE
-    """
-    id = 'combobox'
-    accepts = ('Any',)
-    title = None
-    
-    def cell_call(self, row, col):
-        """the combo-box view for an entity: same as text out of context view
-        by default
-        """
-        self.wview('textoutofcontext', self.rset, row=row, col=col)
-
+from logilab.common.deprecation import class_moved
+from cubicweb.web.views import editviews
+ComboboxView = class_moved(editviews.ComboboxView)
--- a/web/views/basetemplates.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/basetemplates.py	Mon May 04 13:09:48 2009 +0200
@@ -10,23 +10,19 @@
 
 from logilab.mtconverter import html_escape
 
-from cubicweb import NoSelectableObject, ObjectNotFound
-from cubicweb.common.view import Template, MainTemplate,  NOINDEX, NOFOLLOW
-from cubicweb.common.utils import make_uid
-from cubicweb.common.utils import UStringIO
-
-from cubicweb.web.views.baseviews import vid_from_rset
+from cubicweb.vregistry import objectify_selector
+from cubicweb.selectors import match_kwargs
+from cubicweb.view import View, MainTemplate,  NOINDEX, NOFOLLOW, STRICT_DOCTYPE
+from cubicweb.utils import make_uid, UStringIO
 
 # main templates ##############################################################
 
+class LogInOutTemplate(MainTemplate):
 
-class LogInOutTemplate(MainTemplate):
-    
     def call(self):
         self.set_request_content_type()
         w = self.w
         self.write_doctype()
-        lang = self.req.lang
         self.template_header('text/html', self.req._('login_action'))
         w(u'<body>\n')
         self.content(w)
@@ -41,17 +37,17 @@
         w(NOINDEX)
         w(NOFOLLOW)
         w(u'\n'.join(additional_headers) + u'\n')
-        self.template('htmlheader', rset=self.rset)
+        self.wview('htmlheader', rset=self.rset)
         w(u'<title>%s</title>\n' % html_escape(page_title))
-        
+
 
 class LogInTemplate(LogInOutTemplate):
     id = 'login'
     title = 'log in'
 
     def content(self, w):
-        self.template('logform', rset=self.rset, id='loginBox', klass='')
-        
+        self.wview('logform', rset=self.rset, id='loginBox', klass='')
+
 
 class LoggedOutTemplate(LogInOutTemplate):
     id = 'loggedout'
@@ -67,110 +63,76 @@
                 html_escape(indexurl),
                 self.req._('go back to the index page')))
 
-        
+@objectify_selector
+def templatable_view(cls, req, rset, *args, **kwargs):
+    view = kwargs.pop('view', None)
+    if view is None:
+        return 1
+    if view.binary:
+        return 0
+    if req.form.has_key('__notemplate'):
+        return 0
+    return view.templatable
+
+
+class NonTemplatableViewTemplate(MainTemplate):
+    """main template for any non templatable views (xml, binaries, etc.)"""
+    id = 'main-template'
+    __select__ = ~templatable_view()
+
+    def call(self, view):
+        view.set_request_content_type()
+        view.set_stream()
+        xhtml_wrap = (self.req.form.has_key('__notemplate') and view.templatable
+                      and view.content_type == self.req.html_content_type())
+        if xhtml_wrap:
+            view.w(u'<?xml version="1.0"?>\n' + STRICT_DOCTYPE)
+            view.w(u'<div xmlns="http://www.w3.org/1999/xhtml" xmlns:cubicweb="http://www.logilab.org/2008/cubicweb">')
+        # have to replace our unicode stream using view's binary stream
+        view.dispatch()
+        if xhtml_wrap:
+            view.w(u'</div>')
+        self._stream = view._stream
+
+
 class TheMainTemplate(MainTemplate):
     """default main template :
-    
+
     - call header / footer templates
-    - build result set
-    - guess and call an appropriate view through the view manager
     """
-    id = 'main'
+    id = 'main-template'
+    __select__ = templatable_view()
 
-    def _select_view_and_rset(self):
-        req = self.req
-        if self.rset is None and not hasattr(req, '_rql_processed'):
-            req._rql_processed = True
-            rset = self.process_rql(req.form.get('rql'))
-        else:
-            rset = self.rset
-        # handle special "method" param when necessary
-        # XXX this should probably not be in the template (controller ?), however:
-        #     * we need to have the displayed rset
-        #     * we don't want to handle it in each view
-        if rset and rset.rowcount == 1 and '__method' in req.form:
-            entity = rset.get_entity(0, 0)
-            try:
-                method = getattr(entity, req.form.pop('__method'))
-                method()
-            except Exception, ex:
-                self.exception('while handling __method')
-                req.set_message(req._("error while handling __method: %s") % req._(ex))
-        vid = req.form.get('vid') or vid_from_rset(req, rset, self.schema)
-        try:
-            view = self.vreg.select_view(vid, req, rset)
-        except ObjectNotFound:
-            self.warning("the view %s could not be found", vid)
-            req.set_message(req._("The view %s could not be found") % vid)
-            vid = vid_from_rset(req, rset, self.schema)
-            view = self.vreg.select_view(vid, req, rset)
-        except NoSelectableObject:
-            if rset:
-                req.set_message(req._("The view %s can not be applied to this query") % vid)
-            else:
-                req.set_message(req._("You have no access to this view or it's not applyable to current data"))
-            self.warning("the view %s can not be applied to this query", vid)
-            vid = vid_from_rset(req, rset, self.schema)
-            view = self.vreg.select_view(vid, req, rset)
-        return view, rset
-    
-    def call(self):
-        view, rset = self._select_view_and_rset()
-        req = self.req
-        # update breadcrumps **before** validating cache, unless the view
-        # specifies explicitly it should not be added to breadcrumb or the
-        # view is a binary view
-        if view.add_to_breadcrumbs and not view.binary:
-            req.update_breadcrumbs()
-        view.set_http_cache_headers()
-        req.validate_cache()
-        with_templates = not view.binary and view.templatable and \
-                         not req.form.has_key('__notemplate')
-        if not with_templates:
-            view.set_request_content_type()
-            self.set_stream(templatable=False)
-        else:
-            self.set_request_content_type()
-            content_type = self.content_type
-            self.template_header(content_type, view)
-        if view.binary:
-            # have to replace our unicode stream using view's binary stream
-            view.dispatch()
-            assert self._stream, 'duh, template used as a sub-view ?? (%s)' % self._stream
-            self._stream = view._stream
-        else:
-            view.dispatch(w=self.w)
-        if with_templates:
-            self.template_footer(view)
-
-            
-    def process_rql(self, rql):
-        """execute rql if specified"""
-        if rql:
-            self.ensure_ro_rql(rql)
-            if not isinstance(rql, unicode):
-                rql = unicode(rql, self.req.encoding)
-            pp = self.vreg.select_component('magicsearch', self.req)
-            self.rset = pp.process_query(rql, self.req)
-            return self.rset
-        return None
+    def call(self, view):
+        self.set_request_content_type()
+        self.template_header(self.content_type, view)
+        w = self.w
+        w(u'<div id="pageContent">\n')
+        vtitle = self.req.form.get('vtitle')
+        if vtitle:
+            w(u'<h1 class="vtitle">%s</h1>\n' % html_escape(vtitle))
+        # display entity type restriction component
+        etypefilter = self.vreg.select_component('etypenavigation',
+                                                 self.req, self.rset)
+        if etypefilter and etypefilter.propval('visible'):
+            etypefilter.dispatch(w=w)
+        self.nav_html = UStringIO()
+        if view and view.need_navigation:
+            view.paginate(w=self.nav_html.write)
+        w(_(self.nav_html.getvalue()))
+        w(u'<div id="contentmain">\n')
+        view.dispatch(w=w)
+        w(u'</div>\n') # close id=contentmain
+        w(_(self.nav_html.getvalue()))
+        w(u'</div>\n') # closes id=pageContent
+        self.template_footer(view)
 
     def template_header(self, content_type, view=None, page_title='', additional_headers=()):
         page_title = page_title or view.page_title()
         additional_headers = additional_headers or view.html_headers()
         self.template_html_header(content_type, page_title, additional_headers)
         self.template_body_header(view)
-        # display entity type restriction component
-        etypefilter = self.vreg.select_component('etypenavigation',
-                                                 self.req, self.rset)
-        if etypefilter and etypefilter.propval('visible'):
-            etypefilter.dispatch(w=self.w)
-        self.nav_html = UStringIO()
-        self.pagination(self.req, self.rset, self.nav_html.write,
-                        not (view and view.need_navigation))
-        self.w(_(self.nav_html.getvalue()))
-        self.w(u'<div id="contentmain">\n')
-    
+
     def template_html_header(self, content_type, page_title, additional_headers=()):
         w = self.whead
         lang = self.req.lang
@@ -179,14 +141,14 @@
         w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
           % (content_type, self.req.encoding))
         w(u'\n'.join(additional_headers) + u'\n')
-        self.template('htmlheader', rset=self.rset)
+        self.wview('htmlheader', rset=self.rset)
         if page_title:
             w(u'<title>%s</title>\n' % html_escape(page_title))
 
     def template_body_header(self, view):
         w = self.w
         w(u'<body>\n')
-        self.template('header', rset=self.rset, view=view)
+        self.wview('header', rset=self.rset, view=view)
         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')
@@ -197,20 +159,13 @@
         if msgcomp:
             msgcomp.dispatch(w=self.w)
         self.content_header(view)
-        w(u'<div id="pageContent">\n')
-        vtitle = self.req.form.get('vtitle')
-        if vtitle:
-            w(u'<h1 class="vtitle">%s</h1>\n' % html_escape(vtitle))
-            
+
     def template_footer(self, view=None):
-        self.w(u'</div>\n') # close id=contentmain
-        self.w(_(self.nav_html.getvalue()))
-        self.w(u'</div>\n') # closes id=pageContent
         self.content_footer(view)
         self.w(u'</td>\n')
         self.nav_column(view, 'right')
         self.w(u'</tr></table></div>\n')
-        self.template('footer', rset=self.rset)
+        self.wview('footer', rset=self.rset)
         self.w(u'</body>')
 
     def nav_column(self, view, context):
@@ -224,10 +179,10 @@
 
     def content_header(self, view=None):
         """by default, display informal messages in content header"""
-        self.template('contentheader', rset=self.rset, view=view)
-            
+        self.wview('contentheader', rset=self.rset, view=view)
+
     def content_footer(self, view=None):
-        self.template('contentfooter', rset=self.rset, view=view)
+        self.wview('contentfooter', rset=self.rset, view=view)
 
 
 class ErrorTemplate(TheMainTemplate):
@@ -235,8 +190,8 @@
     main template. This template may be called for authentication error,
     which means that req.cnx and req.user may not be set.
     """
-    id = 'error'
-    
+    id = 'error-template'
+
     def call(self):
         """display an unexpected error"""
         self.set_request_content_type()
@@ -246,7 +201,7 @@
                              [NOINDEX, NOFOLLOW])
         view.dispatch(w=self.w)
         self.template_footer(view)
-    
+
     def template_header(self, content_type, view=None, page_title='', additional_headers=()):
         w = self.whead
         lang = self.req.lang
@@ -254,7 +209,7 @@
         w(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
           % (content_type, self.req.encoding))
         w(u'\n'.join(additional_headers))
-        self.template('htmlheader', rset=self.rset)
+        self.wview('htmlheader', rset=self.rset)
         w(u'<title>%s</title>\n' % html_escape(page_title))
         self.w(u'<body>\n')
 
@@ -265,7 +220,7 @@
 class SimpleMainTemplate(TheMainTemplate):
 
     id = 'main-no-top'
-    
+
     def template_header(self, content_type, view=None, page_title='', additional_headers=()):
         page_title = page_title or view.page_title()
         additional_headers = additional_headers or view.html_headers()
@@ -275,7 +230,7 @@
         whead(u'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
               % (content_type, self.req.encoding))
         whead(u'\n'.join(additional_headers) + u'\n')
-        self.template('htmlheader', rset=self.rset)
+        self.wview('htmlheader', rset=self.rset)
         w = self.w
         w(u'<title>%s</title>\n' % html_escape(page_title))
         w(u'<body>\n')
@@ -296,7 +251,7 @@
         vtitle = self.req.form.get('vtitle')
         if vtitle:
             w(u'<h1 class="vtitle">%s</h1>' % html_escape(vtitle))
-            
+
     def topleft_header(self):
         self.w(u'<table id="header"><tr>\n')
         self.w(u'<td>')
@@ -306,10 +261,10 @@
 
 # page parts templates ########################################################
 
-class HTMLHeader(Template):
+class HTMLHeader(View):
     """default html headers"""
     id = 'htmlheader'
-    
+
     def call(self, **kwargs):
         self.favicon()
         self.stylesheets()
@@ -321,7 +276,7 @@
         favicon = self.req.external_resource('FAVICON', None)
         if favicon:
             self.whead(u'<link rel="shortcut icon" href="%s"/>\n' % favicon)
-            
+
     def stylesheets(self):
         req = self.req
         add_css = req.add_css
@@ -331,11 +286,11 @@
             add_css(css, u'print', localfile=False)
         for css in req.external_resource('IE_STYLESHEETS'):
             add_css(css, localfile=False, ieonly=True)
-        
+
     def javascripts(self):
         for jscript in self.req.external_resource('JAVASCRIPTS'):
             self.req.add_js(jscript, localfile=False)
-            
+
     def alternates(self):
         urlgetter = self.vreg.select_component('rss_feed_url', self.req, self.rset)
         if urlgetter is not None:
@@ -347,13 +302,13 @@
         req = self.req
         pid = make_uid(id(req))
         req.pageid = pid
-        req.html_headers.define_var('pageid', pid);
+        req.html_headers.define_var('pageid', pid)
 
 
-class HTMLPageHeader(Template):
+class HTMLPageHeader(View):
     """default html page header"""
     id = 'header'
-    
+
     def call(self, view, **kwargs):
         self.main_header(view)
         self.w(u'''
@@ -362,7 +317,7 @@
         self.w(u'''
   </div>
   ''')
-        
+
     def main_header(self, view):
         """build the top menu with authentification info and the rql box"""
         self.w(u'<table id="header"><tr>\n')
@@ -391,9 +346,9 @@
         self.w(u'<td id="lastcolumn">')
         self.w(u'</td>\n')
         self.w(u'</tr></table>\n')
-        self.template('logform', rset=self.rset, id='popupLoginBox', klass='hidden',
-                      title=False, message=False)
-        
+        self.wview('logform', rset=self.rset, id='popupLoginBox', klass='hidden',
+                   title=False, message=False)
+
     def state_header(self):
         state = self.req.search_state
         if state[0] == 'normal':
@@ -403,18 +358,18 @@
         msg = ' '.join((_("searching for"),
                         display_name(self.req, state[1][3]),
                         _("to associate with"), value,
-                        _("by relation"), '"', 
+                        _("by relation"), '"',
                         display_name(self.req, state[1][2], state[1][0]),
                         '"'))
         return self.w(u'<div class="stateMessage">%s</div>' % msg)
 
 
 
-class HTMLPageFooter(Template):
+class HTMLPageFooter(View):
     """default html page footer: include logo if any, and close the HTML body
     """
     id = 'footer'
-    
+
     def call(self, **kwargs):
         req = self.req
         self.w(u'<div class="footer">')
@@ -429,13 +384,13 @@
         self.w(u'</div>')
 
 
-class HTMLContentHeader(Template):
+class HTMLContentHeader(View):
     """default html page content header:
     * include message component if selectable for this request
     * include selectable content navigation components
     """
     id = 'contentheader'
-    
+
     def call(self, view, **kwargs):
         """by default, display informal messages in content header"""
         components = self.vreg.possible_vobjects('contentnavigation',
@@ -448,12 +403,12 @@
             self.w(u'</div><div class="clear"></div>')
 
 
-class HTMLContentFooter(Template):
+class HTMLContentFooter(View):
     """default html page content footer: include selectable content navigation
     components
     """
     id = 'contentfooter'
-    
+
     def call(self, view, **kwargs):
         components = self.vreg.possible_vobjects('contentnavigation',
                                                  self.req, self.rset,
@@ -465,8 +420,10 @@
             self.w(u'</div>')
 
 
-class LogFormTemplate(Template):
+class LogFormTemplate(View):
     id = 'logform'
+    __select__ = match_kwargs('id', 'klass')
+
     title = 'log in'
 
     def call(self, id, klass, title=True, message=True):
@@ -475,7 +432,7 @@
         if title:
             self.w(u'<div id="loginTitle">%s</div>'
                    % self.req.property_value('ui.site-title'))
-        self.w(u'<div id="loginContent">\n')        
+        self.w(u'<div id="loginContent">\n')
 
         if message:
             self.display_message()
@@ -491,14 +448,14 @@
         message = self.req.message
         if message:
             self.w(u'<div class="simpleMessage">%s</div>\n' % message)
-                     
+
     def login_form(self, id):
         _ = self.req._
         self.w(u'<form method="post" action="%s" id="login_form">\n'
                % html_escape(login_form_url(self.config, self.req)))
         self.w(u'<table>\n')
         self.w(u'<tr>\n')
-        self.w(u'<td><label for="__login">%s</label></td>' % _('login'))
+        self.w(u'<td><label for="__login">%s</label></td>' % _('login or email'))
         self.w(u'<td><input name="__login" id="__login" class="data" type="text" /></td>')
         self.w(u'</tr><tr>\n')
         self.w(u'<td><label for="__password" >%s</label></td>' % _('password'))
@@ -510,7 +467,7 @@
         self.w(u'</form>\n')
         self.req.html_headers.add_onload('jQuery("#__login:visible").focus()')
 
-    
+
 def login_form_url(config, req):
     if req.https:
         return req.url()
@@ -518,3 +475,7 @@
         return req.url().replace(req.base_url(), config['https-url'])
     return req.url()
 
+
+## vregistry registration callback ############################################
+def registration_callback(vreg):
+    vreg.register_all(globals().values(), modname=__name__)
--- a/web/views/baseviews.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/baseviews.py	Mon May 04 13:09:48 2009 +0200
@@ -4,7 +4,6 @@
 * primary, sidebox
 * secondary, oneline, incontext, outofcontext, text
 * list
-* xml, rss
 
 
 :organization: Logilab
@@ -15,39 +14,31 @@
 
 __docformat__ = "restructuredtext en"
 
-from warnings import warn
-from time import timezone
-
 from rql import nodes
 
-from logilab.common.decorators import cached
-from logilab.mtconverter import TransformError, html_escape, xml_escape
+from logilab.mtconverter import TransformError, html_escape
 
-from cubicweb import Unauthorized, NoSelectableObject, typed_eid
-from cubicweb.common.selectors import (yes, nonempty_rset, accept,
-                                       one_line_rset, match_search_state, 
-                                       match_form_params, accept_rset)
-from cubicweb.common.uilib import (cut, printable_value,  UnicodeCSVWriter,
-                                   ajax_replace_url, rql_for_eid, simple_sgml_tag)
-from cubicweb.common.view import EntityView, AnyRsetView, EmptyRsetView
-from cubicweb.web.httpcache import MaxAgeHTTPCacheManager
-from cubicweb.web.views import vid_from_rset, linksearch_select_url, linksearch_match
+from cubicweb import NoSelectableObject
+from cubicweb.selectors import yes, empty_rset
+from cubicweb.view import EntityView, AnyRsetView, View
+from cubicweb.common.uilib import cut, printable_value
 
 _ = unicode
 
 class NullView(AnyRsetView):
     """default view when no result has been found"""
     id = 'null'
-    __select__ = classmethod(yes)
+    __select__ = yes()
     def call(self, **kwargs):
         pass
     cell_call = call
 
 
-class NoResultView(EmptyRsetView):
+class NoResultView(View):
     """default view when no result has been found"""
+    __select__ = empty_rset()
     id = 'noresult'
-    
+
     def call(self, **kwargs):
         self.w(u'<div class="searchMessage"><strong>%s</strong></div>\n'
                % self.req._('No result matching query'))
@@ -55,7 +46,7 @@
 
 class FinalView(AnyRsetView):
     """display values without any transformation (i.e. get a number for
-    entities) 
+    entities)
     """
     id = 'final'
     # record generated i18n catalog messages
@@ -73,7 +64,7 @@
     _('%d hours')
     _('%d minutes')
     _('%d seconds')
-            
+
     def cell_call(self, row, col, props=None, displaytime=False, format='text/html'):
         etype = self.rset.description[row][col]
         value = self.rset.rows[row][col]
@@ -84,7 +75,7 @@
                 self.w(entity.printable_value(rtype, value, format=format))
                 return
         if etype in ('Time', 'Interval'):
-            # value is DateTimeDelta but we have no idea about what is the 
+            # value is DateTimeDelta but we have no idea about what is the
             # reference date here, so we can only approximate years and months
             if format == 'text/html':
                 space = '&nbsp;'
@@ -108,266 +99,10 @@
         self.wdata(printable_value(self.req, etype, value, props, displaytime=displaytime))
 
 
-class EditableFinalView(FinalView):
-    """same as FinalView but enables inplace-edition when possible"""
-    id = 'editable-final'
-                
-    def cell_call(self, row, col, props=None, displaytime=False):
-        etype = self.rset.description[row][col]
-        value = self.rset.rows[row][col]
-        entity, rtype = self.rset.related_entity(row, col)
-        if entity is not None:
-            self.w(entity.view('reledit', rtype=rtype))
-        else:
-            super(EditableFinalView, self).cell_call(row, col, props, displaytime)
-        
-PRIMARY_SKIP_RELS = set(['is', 'is_instance_of', 'identity',
-                         'owned_by', 'created_by', 
-                         'in_state', 'wf_info_for', 'require_permission',
-                         'from_entity', 'to_entity',
-                         'see_also'])
-
-class PrimaryView(EntityView):
-    """the full view of an non final entity"""
-    id = 'primary'
-    title = _('primary')
-    show_attr_label = True
-    show_rel_label = True
-    skip_none = True
-    skip_attrs = ('eid', 'creation_date', 'modification_date')
-    skip_rels = ()
-    main_related_section = True
-
-    def html_headers(self):
-        """return a list of html headers (eg something to be inserted between
-        <head> and </head> of the returned page
-
-        by default primary views are indexed
-        """
-        return []
-    
-    def cell_call(self, row, col):
-        self.row = row
-        # XXX move render_entity implementation here
-        self.render_entity(self.complete_entity(row, col))
-    
-    def render_entity(self, entity):
-        """return html to display the given entity"""
-        siderelations = []
-        self.render_entity_title(entity)
-        self.render_entity_metadata(entity)
-        # entity's attributes and relations, excluding meta data
-        # if the entity isn't meta itself
-        boxes = self._preinit_side_related(entity, siderelations)
-        if boxes:
-            self.w(u'<table width="100%"><tr><td width="75%">')
-        self.w(u'<div>')
-        self.w(u'<div class="mainInfo">')
-        self.render_entity_attributes(entity, siderelations)
-        self.w(u'</div>')
-        self.content_navigation_components('navcontenttop')
-        if self.main_related_section:
-            self.render_entity_relations(entity, siderelations)
-        self.w(u'</div>')
-        if boxes:
-            self.w(u'</td><td>')
-            # side boxes
-            self.w(u'<div class="primaryRight">')
-            self.render_side_related(entity, siderelations)
-            self.w(u'</div>')
-            self.w(u'</td></tr></table>')
-        self.content_navigation_components('navcontentbottom')
-
-        
-    def content_navigation_components(self, context):
-        self.w(u'<div class="%s">' % context)
-        for comp in self.vreg.possible_vobjects('contentnavigation',
-                                                self.req, self.rset, row=self.row,
-                                                view=self, context=context):
-            try:
-                comp.dispatch(w=self.w, row=self.row, view=self)
-            except NotImplementedError:
-                warn('component %s doesnt implement cell_call, please update'
-                     % comp.__class__, DeprecationWarning)
-                comp.dispatch(w=self.w, view=self)
-        self.w(u'</div>')
-        
-    def iter_attributes(self, entity):
-        for rschema, targetschema in entity.e_schema.attribute_definitions():
-            attr = rschema.type
-            if attr in self.skip_attrs:
-               continue
-            yield rschema, targetschema
-            
-    def iter_relations(self, entity):
-        skip = set(self.skip_rels)
-        skip.update(PRIMARY_SKIP_RELS)
-        for rschema, targetschemas, x in entity.e_schema.relation_definitions():
-            if rschema.type in skip:
-                continue
-            yield rschema, targetschemas, x
-
-    def render_entity_title(self, entity):
-        title = self.content_title(entity) # deprecate content_title?
-        if title:
-            self.w(u'<h1><span class="etype">%s</span> %s</h1>'
-                   % (entity.dc_type().capitalize(), title))
-    
-    def content_title(self, entity):
-        """default implementation return an empty string"""
-        return u''
-            
-    def render_entity_metadata(self, entity):
-        entity.view('metadata', w=self.w)
-        summary = self.summary(entity) # deprecate summary?
-        if summary:
-            self.w(u'<div class="summary">%s</div>' % summary)
-    
-    def summary(self, entity):
-        """default implementation return an empty string"""
-        return u''    
-               
-    def render_entity_attributes(self, entity, siderelations):
-        for rschema, targetschema in self.iter_attributes(entity):
-            attr = rschema.type
-            if targetschema.type in ('Password', 'Bytes'):
-                continue
-            try:
-                wdg = entity.get_widget(attr)
-            except Exception, ex:
-                value = entity.printable_value(attr, entity[attr], targetschema.type)
-            else:
-                value = wdg.render(entity)
-            if self.skip_none and (value is None or value == ''):
-                continue
-            if rschema.meta:
-                continue
-            self._render_related_entities(entity, rschema, value)
-
-    def _preinit_side_related(self, entity, siderelations):
-        self._sideboxes = None
-        self._related_entities = []
-        if hasattr(self, 'get_side_boxes_defs'):
-            self._sideboxes = [(label, rset) for label, rset in self.get_side_boxes_defs(entity)
-                               if rset]
-        else:
-            eschema = entity.e_schema
-            maxrelated = self.req.property_value('navigation.related-limit')
-            for rschema, targetschemas, x in self.iter_relations(entity):
-                try:
-                    related = entity.related(rschema.type, x, limit=maxrelated+1)
-                except Unauthorized:
-                    continue
-                if not related:
-                    continue
-                if self.is_side_related(rschema, eschema):
-                    siderelations.append((rschema, related, x))
-                    continue
-                self._related_entities.append((rschema, related, x)) 
-        self._boxes_in_context = list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
-                                                 row=self.row, view=self,
-                                                 context='incontext'))
-        return self._sideboxes or self._boxes_in_context or self._related_entities or siderelations
-        
-    def render_entity_relations(self, entity, siderelations):
-        if self._related_entities:
-            for rschema, related, x in self._related_entities:
-                self._render_related_entities(entity, rschema, related, x)
-
-              
-    def render_side_related(self, entity, siderelations):
-        """display side related relations:
-        non-meta in a first step, meta in a second step
-        """
-        if self._sideboxes:
-            for label, rset in self._sideboxes:
-                self.w(u'<div class="sideRelated">')
-                self.wview('sidebox', rset, title=label)
-                self.w(u'</div>')
-        elif siderelations:
-            self.w(u'<div class="sideRelated">')
-            for relatedinfos in siderelations:
-                # if not relatedinfos[0].meta:
-                #    continue
-                self._render_related_entities(entity, *relatedinfos)
-            self.w(u'</div>')
-
-        if self._boxes_in_context:
-            for box in self._boxes_in_context:
-                try:
-                    box.dispatch(w=self.w, row=self.row)
-                except NotImplementedError:
-                    # much probably a context insensitive box, which only implements
-                    # .call() and not cell_call()
-                    box.dispatch(w=self.w)               
-                
-    def is_side_related(self, rschema, eschema):
-        return rschema.meta and \
-               not rschema.schema_relation() == eschema.schema_entity()
-
-    def _render_related_entities(self, entity, rschema, related,
-                                 role='subject'):
-        if rschema.is_final():
-            value = related
-            show_label = self.show_attr_label
-        else:
-            if not related:
-                return
-            show_label = self.show_rel_label
-            # if not too many entities, show them all in a list
-            maxrelated = self.req.property_value('navigation.related-limit')
-            if related.rowcount <= maxrelated:
-                if related.rowcount == 1:
-                    value = self.view('incontext', related, row=0)
-                elif 1 < related.rowcount <= 5:
-                    value = self.view('csv', related)
-                else:
-                    value = '<div>' + self.view('simplelist', related) + '</div>'
-            # else show links to display related entities
-            else:
-                rql = related.printable_rql()
-                related.limit(maxrelated)
-                value = '<div>' + self.view('simplelist', related)
-                value += '[<a href="%s">%s</a>]' % (self.build_url(rql=rql),
-                                                    self.req._('see them all'))
-                value +=  '</div>'
-        label = display_name(self.req, rschema.type, role)
-        self.field(label, value, show_label=show_label, w=self.w, tr=False)
-        
-class SideBoxView(EntityView):
-    """side box usually displaying some related entities in a primary view"""
-    id = 'sidebox'
-    
-    def call(self, boxclass='sideBox', title=u''):
-        """display a list of entities by calling their <item_vid> view
-        """
-        if title:
-            self.w(u'<div class="sideBoxTitle"><span>%s</span></div>' % title)
-        self.w(u'<div class="%s"><div class="sideBoxBody">' % boxclass)
-        # if not too much entities, show them all in a list
-        maxrelated = self.req.property_value('navigation.related-limit')
-        if self.rset.rowcount <= maxrelated:
-            if len(self.rset) == 1:
-                self.wview('incontext', self.rset, row=0)
-            elif 1 < len(self.rset) < 5:
-                self.wview('csv', self.rset)
-            else:
-                self.wview('simplelist', self.rset)
-        # else show links to display related entities
-        else:
-            self.rset.limit(maxrelated)
-            rql = self.rset.printable_rql(encoded=False)
-            self.wview('simplelist', self.rset)
-            self.w(u'[<a href="%s">%s</a>]' % (self.build_url(rql=rql),
-                                               self.req._('see them all')))
-        self.w(u'</div>\n</div>\n')
-
-
- 
 class SecondaryView(EntityView):
     id = 'secondary'
     title = _('secondary')
-    
+
     def cell_call(self, row, col):
         """the secondary view for an entity
         secondary = icon + view(oneline)
@@ -376,9 +111,10 @@
         self.w(u'&nbsp;')
         self.wview('oneline', self.rset, row=row, col=col)
 
+
 class OneLineView(EntityView):
     id = 'oneline'
-    title = _('oneline') 
+    title = _('oneline')
 
     def cell_call(self, row, col):
         """the one line view for an entity: linked text view
@@ -388,12 +124,13 @@
         self.w(html_escape(self.view('text', self.rset, row=row, col=col)))
         self.w(u'</a>')
 
+
 class TextView(EntityView):
     """the simplest text view for an entity"""
     id = 'text'
     title = _('text')
     content_type = 'text/plain'
-    accepts = 'Any',
+
     def call(self, **kwargs):
         """the view is called for an entire result set, by default loop
         other rows of the result set and call the same view on the
@@ -408,7 +145,7 @@
             self.wview(self.id, rset, row=i, **kwargs)
             if len(rset) > 1:
                 self.w(u"\n")
-    
+
     def cell_call(self, row, col=0, **kwargs):
         entity = self.entity(row, col)
         self.w(cut(entity.dc_title(),
@@ -418,9 +155,8 @@
 class MetaDataView(EntityView):
     """paragraph view of some metadata"""
     id = 'metadata'
-    accepts = 'Any',
     show_eid = True
-    
+
     def cell_call(self, row, col):
         _ = self.req._
         entity = self.entity(row, col)
@@ -429,10 +165,10 @@
             self.w(u'#%s - ' % entity.eid)
         if entity.modification_date != entity.creation_date:
             self.w(u'<span>%s</span> ' % _('latest update on'))
-            self.w(u'<span class="value">%s</span>, ;'
+            self.w(u'<span class="value">%s</span>, '
                    % self.format_date(entity.modification_date))
         # entities from external source may not have a creation date (eg ldap)
-        if entity.creation_date: 
+        if entity.creation_date:
             self.w(u'<span>%s</span> ' % _('created on'))
             self.w(u'<span class="value">%s</span>'
                    % self.format_date(entity.creation_date))
@@ -451,7 +187,8 @@
     def cell_call(self, row, col):
         entity = self.entity(row, col)
         self.w(entity.dc_title())
-        
+
+
 class OutOfContextTextView(InContextTextView):
     id = 'textoutofcontext'
 
@@ -471,7 +208,7 @@
         self.w(html_escape(self.view('textincontext', self.rset, row=row, col=col)))
         self.w(u'</a>')
 
-        
+
 class OutOfContextView(EntityView):
     id = 'outofcontext'
 
@@ -480,29 +217,17 @@
         self.w(html_escape(self.view('textoutofcontext', self.rset, row=row, col=col)))
         self.w(u'</a>')
 
-class NotClickableInContextView(EntityView):
-    id = 'incontext'
-    accepts = ('State',)
-    def cell_call(self, row, col):
-        self.w(html_escape(self.view('textincontext', self.rset, row=row, col=col)))
 
-## class NotClickableOutOfContextView(EntityView):
-##     id = 'outofcontext'
-##     accepts = ('State',)
-##     def cell_call(self, row, col):
-##         self.w(html_escape(self.view('textoutofcontext', self.rset, row=row)))
+# list views ##################################################################
 
-            
-# list and table related views ################################################
-    
 class ListView(EntityView):
     id = 'list'
     title = _('list')
     item_vid = 'listitem'
-        
+
     def call(self, klass=None, title=None, subvid=None, listid=None, **kwargs):
         """display a list of entities by calling their <item_vid> view
-        
+
         :param listid: the DOM id to use for the root element
         """
         if subvid is None and 'subvid' in self.req.form:
@@ -549,16 +274,16 @@
             return self.build_url(entity.rest_path(), vid=self.id)
         return self.build_url(rql=self.rset.printable_rql(), vid=self.id)
 
- 
+
 class ListItemView(EntityView):
     id = 'listitem'
-    
+
     @property
     def redirect_vid(self):
         if self.req.search_state[0] == 'normal':
             return 'outofcontext'
         return 'outofcontext-search'
-        
+
     def cell_call(self, row, col, vid=None, **kwargs):
         if not vid:
             vid = self.redirect_vid
@@ -579,7 +304,7 @@
 class CSVView(SimpleListView):
     id = 'csv'
     redirect_vid = 'incontext'
-        
+
     def call(self, **kwargs):
         rset = self.rset
         for i in xrange(len(rset)):
@@ -589,394 +314,10 @@
 
 
 class TreeItemView(ListItemView):
-    accepts = ('Any',)
     id = 'treeitem'
-    
-    def cell_call(self, row, col):
-        self.wview('incontext', self.rset, row=row, col=col)
-
-
-# xml and xml/rss views #######################################################
-    
-class XmlView(EntityView):
-    id = 'xml'
-    title = _('xml')
-    templatable = False
-    content_type = 'text/xml'
-    xml_root = 'rset'
-    item_vid = 'xmlitem'
-    
-    def cell_call(self, row, col):
-        self.wview(self.item_vid, self.rset, row=row, col=col)
-        
-    def call(self):
-        """display a list of entities by calling their <item_vid> view"""
-        self.w(u'<?xml version="1.0" encoding="%s"?>\n' % self.req.encoding)
-        self.w(u'<%s size="%s">\n' % (self.xml_root, len(self.rset)))
-        for i in xrange(self.rset.rowcount):
-            self.cell_call(i, 0)
-        self.w(u'</%s>\n' % self.xml_root)
-
-
-class XmlItemView(EntityView):
-    id = 'xmlitem'
-
-    def cell_call(self, row, col):
-        """ element as an item for an xml feed """
-        entity = self.complete_entity(row, col)
-        self.w(u'<%s>\n' % (entity.e_schema))
-        for rschema, attrschema in entity.e_schema.attribute_definitions():
-            attr = rschema.type
-            try:
-                value = entity[attr]
-            except KeyError:
-                # Bytes
-                continue
-            if value is not None:
-                if attrschema == 'Bytes':
-                    from base64 import b64encode
-                    value = '<![CDATA[%s]]>' % b64encode(value.getvalue())
-                elif isinstance(value, basestring):
-                    value = xml_escape(value)
-                self.w(u'  <%s>%s</%s>\n' % (attr, value, attr))
-        self.w(u'</%s>\n' % (entity.e_schema))
-
-
-    
-class XMLRsetView(AnyRsetView):
-    """dumps xml in CSV"""
-    id = 'rsetxml'
-    title = _('xml export')
-    templatable = False
-    content_type = 'text/xml'
-    xml_root = 'rset'
-        
-    def call(self):
-        w = self.w
-        rset, descr = self.rset, self.rset.description
-        eschema = self.schema.eschema
-        labels = self.columns_labels(False)
-        w(u'<?xml version="1.0" encoding="%s"?>\n' % self.req.encoding)
-        w(u'<%s query="%s">\n' % (self.xml_root, html_escape(rset.printable_rql())))
-        for rowindex, row in enumerate(self.rset):
-            w(u' <row>\n')
-            for colindex, val in enumerate(row):
-                etype = descr[rowindex][colindex]
-                tag = labels[colindex]
-                attrs = {}
-                if '(' in tag:
-                    attrs['expr'] = tag
-                    tag = 'funccall'
-                if val is not None and not eschema(etype).is_final():
-                    attrs['eid'] = val
-                    # csvrow.append(val) # val is eid in that case
-                    val = self.view('textincontext', rset,
-                                    row=rowindex, col=colindex)
-                else:
-                    val = self.view('final', rset, displaytime=True,
-                                    row=rowindex, col=colindex, format='text/plain')
-                w(simple_sgml_tag(tag, val, **attrs))
-            w(u' </row>\n')
-        w(u'</%s>\n' % self.xml_root)
-    
-
-class RssView(XmlView):
-    id = 'rss'
-    title = _('rss')
-    templatable = False
-    content_type = 'text/xml'
-    http_cache_manager = MaxAgeHTTPCacheManager
-    cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
-
-    def _open(self):
-        req = self.req
-        self.w(u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding)
-        self.w(u'''<rss version="2.0"
- xmlns:dc="http://purl.org/dc/elements/1.1/"
->\n''')
-        self.w(u'  <channel>\n')
-        self.w(u'    <title>%s RSS Feed</title>\n' % html_escape(self.page_title()))
-        self.w(u'    <description>%s</description>\n' % html_escape(req.form.get('vtitle', '')))
-        params = req.form.copy()
-        params.pop('vid', None)
-        self.w(u'    <link>%s</link>\n' % html_escape(self.build_url(**params)))
-
-    def _close(self):
-        self.w(u'  </channel>\n')
-        self.w(u'</rss>')       
-        
-    def call(self):
-        """display a list of entities by calling their <item_vid> view"""
-        self._open()
-        for i in xrange(self.rset.rowcount):
-            self.cell_call(i, 0)
-        self._close()
-
-    def cell_call(self, row, col):
-        self.wview('rssitem', self.rset, row=row, col=col)
-
-class RssItemView(EntityView):
-    id = 'rssitem'
-    date_format = '%%Y-%%m-%%dT%%H:%%M%+03i:00' % (timezone / 3600)
-    add_div_section = False
 
     def cell_call(self, row, col):
-        entity = self.complete_entity(row, col)
-        self.w(u'<item>\n')
-        self.w(u'<guid isPermaLink="true">%s</guid>\n' % html_escape(entity.absolute_url()))
-        self.render_title_link(entity)
-        self._marker('description', html_escape(entity.dc_description()))
-        self._marker('dc:date', entity.dc_date(self.date_format))
-        self.render_entity_creator(entity)
-        self.w(u'</item>\n')
-
-    def render_title_link(self, entity):
-        self._marker('title', entity.dc_long_title())
-        self._marker('link', entity.absolute_url())
-           
-    def render_entity_creator(self, entity):
-        if entity.creator:
-            self._marker('dc:creator', entity.dc_creator())
-       
-                
-    def _marker(self, marker, value):
-        if value:
-            self.w(u'  <%s>%s</%s>\n' % (marker, html_escape(value), marker))
-
-class CSVMixIn(object):
-    """mixin class for CSV views"""
-    templatable = False
-    content_type = "text/comma-separated-values"    
-    binary = True # avoid unicode assertion
-    csv_params = {'dialect': 'excel',
-                  'quotechar': '"',
-                  'delimiter': ';',
-                  'lineterminator': '\n'}
-    
-    def set_request_content_type(self):
-        """overriden to set a .csv filename"""
-        self.req.set_content_type(self.content_type, filename='cubicwebexport.csv')
-            
-    def csvwriter(self, **kwargs):
-        params = self.csv_params.copy()
-        params.update(kwargs)
-        return UnicodeCSVWriter(self.w, self.req.encoding, **params)
-
-    
-class CSVRsetView(CSVMixIn, AnyRsetView):
-    """dumps rset in CSV"""
-    id = 'csvexport'
-    title = _('csv export')
-        
-    def call(self):
-        writer = self.csvwriter()
-        writer.writerow(self.columns_labels())
-        rset, descr = self.rset, self.rset.description
-        eschema = self.schema.eschema
-        for rowindex, row in enumerate(rset):
-            csvrow = []
-            for colindex, val in enumerate(row):
-                etype = descr[rowindex][colindex]
-                if val is not None and not eschema(etype).is_final():
-                    # csvrow.append(val) # val is eid in that case
-                    content = self.view('textincontext', rset, 
-                                        row=rowindex, col=colindex)
-                else:
-                    content = self.view('final', rset,
-                                        displaytime=True, format='text/plain',
-                                        row=rowindex, col=colindex)
-                csvrow.append(content)                    
-            writer.writerow(csvrow)
-    
-    
-class CSVEntityView(CSVMixIn, EntityView):
-    """dumps rset's entities (with full set of attributes) in CSV"""
-    id = 'ecsvexport'
-    title = _('csv entities export')
-
-    def call(self):
-        """
-        the generated CSV file will have a table per entity type
-        found in the resultset. ('table' here only means empty
-        lines separation between table contents)
-        """
-        req = self.req
-        rows_by_type = {}
-        writer = self.csvwriter()
-        rowdef_by_type = {}
-        for index in xrange(len(self.rset)):
-            entity = self.complete_entity(index)
-            if entity.e_schema not in rows_by_type:
-                rowdef_by_type[entity.e_schema] = [rs for rs, at in entity.e_schema.attribute_definitions()
-                                                   if at != 'Bytes']
-                rows_by_type[entity.e_schema] = [[display_name(req, rschema.type)
-                                                  for rschema in rowdef_by_type[entity.e_schema]]]
-            rows = rows_by_type[entity.e_schema]
-            rows.append([entity.printable_value(rs.type, format='text/plain')
-                         for rs in rowdef_by_type[entity.e_schema]])
-        for etype, rows in rows_by_type.items():
-            writer.writerows(rows)
-            # use two empty lines as separator
-            writer.writerows([[], []])        
-    
-
-## Work in progress ###########################################################
-
-class SearchForAssociationView(EntityView):
-    """view called by the edition view when the user asks
-    to search for something to link to the edited eid
-    """
-    id = 'search-associate'
-    title = _('search for association')
-    __selectors__ = (one_line_rset, match_search_state, accept)
-    accepts = ('Any',)
-    search_states = ('linksearch',)
-
-    def cell_call(self, row, col):
-        rset, vid, divid, paginate = self.filter_box_context_info()
-        self.w(u'<div id="%s">' % divid)
-        self.pagination(self.req, rset, w=self.w)
-        self.wview(vid, rset, 'noresult')
-        self.w(u'</div>')
-
-    @cached
-    def filter_box_context_info(self):
-        entity = self.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
-        # them. Use fetch_order and not fetch_unrelated_order as sort method
-        # since the latter is mainly there to select relevant items in the combo
-        # box, it doesn't give interesting result in this context
-        rql = entity.unrelated_rql(rtype, etype, role,
-                                   ordermethod='fetch_order',
-                                   vocabconstraints=False)
-        rset = self.req.execute(rql, {'x' : entity.eid}, 'x')
-        #vid = vid_from_rset(self.req, rset, self.schema)
-        return rset, 'list', "search-associate-content", True
-
-
-class OutOfContextSearch(EntityView):
-    id = 'outofcontext-search'
-    def cell_call(self, row, col):
-        entity = self.entity(row, col)
-        erset = entity.as_rset()
-        if linksearch_match(self.req, erset):
-            self.w(u'<a href="%s" title="%s">%s</a>&nbsp;<a href="%s" title="%s">[...]</a>' % (
-                html_escape(linksearch_select_url(self.req, erset)),
-                self.req._('select this entity'),
-                html_escape(entity.view('textoutofcontext')),
-                html_escape(entity.absolute_url(vid='primary')),
-                self.req._('view detail for this entity')))
-        else:
-            entity.view('outofcontext', w=self.w)
-            
-            
-class EditRelationView(EntityView):
-    """Note: This is work in progress
-
-    This view is part of the edition view refactoring.
-    It is still too big and cluttered with strange logic, but it's a start
-
-    The main idea is to be able to call an edition view for a specific
-    relation. For example :
-       self.wview('editrelation', person_rset, rtype='firstname')
-       self.wview('editrelation', person_rset, rtype='works_for')
-    """
-    id = 'editrelation'
-
-    __selectors__ = (match_form_params,)
-    form_params = ('rtype',)
-    
-    # TODO: inlineview, multiple edit, (widget view ?)
-    def cell_call(self, row, col, rtype=None, role='subject', targettype=None,
-                 showlabel=True):
-        self.req.add_js( ('cubicweb.ajax.js', 'cubicweb.edition.js') )
-        entity = self.entity(row, col)
-        rtype = self.req.form.get('rtype', rtype)
-        showlabel = self.req.form.get('showlabel', showlabel)
-        assert rtype is not None, "rtype is mandatory for 'edirelation' view"
-        targettype = self.req.form.get('targettype', targettype)
-        role = self.req.form.get('role', role)
-        category = entity.rtags.get_category(rtype, targettype, role)
-        if category in ('primary', 'secondary') or self.schema.rschema(rtype).is_final():
-            if hasattr(entity, '%s_format' % rtype):
-                formatwdg = entity.get_widget('%s_format' % rtype, role)
-                self.w(formatwdg.edit_render(entity))
-                self.w(u'<br/>')
-            wdg = entity.get_widget(rtype, role)
-            if showlabel:
-                self.w(u'%s' % wdg.render_label(entity))
-            self.w(u'%s %s %s' %
-                   (wdg.render_error(entity), wdg.edit_render(entity),
-                    wdg.render_help(entity),))
-        else:
-            self._render_generic_relation(entity, rtype, role)
-
-    def _render_generic_relation(self, entity, relname, role):
-        text = self.req.__('add %s %s %s' % (entity.e_schema, relname, role))
-        # pending operations
-        operations = self.req.get_pending_operations(entity, relname, role)
-        if operations['insert'] or operations['delete'] or 'unfold' in self.req.form:
-            self.w(u'<h3>%s</h3>' % text)
-            self._render_generic_relation_form(operations, entity, relname, role)
-        else:
-            divid = "%s%sreledit" % (relname, role)
-            url = ajax_replace_url(divid, rql_for_eid(entity.eid), 'editrelation',
-                                   {'unfold' : 1, 'relname' : relname, 'role' : role})
-            self.w(u'<a href="%s">%s</a>' % (url, text))
-            self.w(u'<div id="%s"></div>' % divid)
-        
-
-    def _build_opvalue(self, entity, relname, target, role):
-        if role == 'subject':
-            return '%s:%s:%s' % (entity.eid, relname, target)
-        else:
-            return '%s:%s:%s' % (target, relname, entity.eid)
-        
-    
-    def _render_generic_relation_form(self, operations, entity, relname, role):
-        rqlexec = self.req.execute
-        for optype, targets in operations.items():
-            for target in targets:
-                self._render_pending(optype, entity, relname, target, role)
-                opvalue = self._build_opvalue(entity, relname, target, role)
-                self.w(u'<a href="javascript: addPendingDelete(\'%s\', %s);">-</a> '
-                       % (opvalue, entity.eid))
-                rset = rqlexec('Any X WHERE X eid %(x)s', {'x': target}, 'x')
-                self.wview('oneline', rset)
-        # now, unrelated ones
-        self._render_unrelated_selection(entity, relname, role)
-
-    def _render_pending(self, optype, entity, relname, target, role):
-        opvalue = self._build_opvalue(entity, relname, target, role)
-        self.w(u'<input type="hidden" name="__%s" value="%s" />'
-               % (optype, opvalue))
-        if optype == 'insert':
-            checktext = '-'
-        else:
-            checktext = '+'
-        rset = self.req.execute('Any X WHERE X eid %(x)s', {'x': target}, 'x')
-        self.w(u"""[<a href="javascript: cancelPending%s('%s:%s:%s')">%s</a>"""
-               % (optype.capitalize(), relname, target, role,
-                  self.view('oneline', rset)))
-
-    def _render_unrelated_selection(self, entity, relname, role):
-        rschema = self.schema.rschema(relname)
-        if role == 'subject':
-            targettypes = rschema.objects(entity.e_schema)
-        else:
-            targettypes = rschema.subjects(entity.e_schema)
-        self.w(u'<select onselect="addPendingInsert(this.selected.value);">')
-        for targettype in targettypes:
-            unrelated = entity.unrelated(relname, targettype, role) # XXX limit
-            for rowindex, row in enumerate(unrelated):
-                teid = row[0]
-                opvalue = self._build_opvalue(entity, relname, teid, role)
-                self.w(u'<option name="__insert" value="%s>%s</option>'
-                       % (opvalue, self.view('text', unrelated, row=rowindex)))
-        self.w(u'</select>')
-
+        self.wview('incontext', self.rset, row=row, col=col)
 
 class TextSearchResultView(EntityView):
     """this view is used to display full-text search
@@ -987,7 +328,6 @@
     """
     id = 'tsearch'
 
-
     def cell_call(self, row, col, **kwargs):
         entity = self.complete_entity(row, col)
         self.w(entity.view('incontext'))
@@ -1011,36 +351,32 @@
                     else:
                         contexts.append(ctx)
                 value = u'\n' + highlighted.join(contexts)
-                self.w(value.replace('\n', '<br/>'))            
+                self.w(value.replace('\n', '<br/>'))
 
 
-class EntityRelationView(EntityView):
-    accepts = ()
-    vid = 'list'
-    
-    def cell_call(self, row, col):
-        if self.target == 'object':
-            role = 'subject'
-        else:
-            role = 'object'
-        rset = self.rset.get_entity(row, col).related(self.rtype, role)
-        if self.title:
-            self.w(u'<h1>%s</h1>' % self.req._(self.title).capitalize())
-        self.w(u'<div class="mainInfo">')
-        self.wview(self.vid, rset, 'noresult')
-        self.w(u'</div>')
-
-
-class TooltipView(OneLineView):
+class TooltipView(EntityView):
     """A entity view used in a tooltip"""
     id = 'tooltip'
-    title = None # don't display in possible views
     def cell_call(self, row, col):
         self.wview('oneline', self.rset, row=row, col=col)
 
+
+# XXX bw compat
+
+from logilab.common.deprecation import class_moved
+
 try:
     from cubicweb.web.views.tableview import TableView
-    from logilab.common.deprecation import class_moved
     TableView = class_moved(TableView)
 except ImportError:
     pass # gae has no tableview module (yet)
+
+from cubicweb.web.views import boxes, xmlrss, primary
+PrimaryView = class_moved(primary.PrimaryView)
+SideBoxView = class_moved(boxes.SideBoxView)
+XmlView = class_moved(xmlrss.XmlView)
+XmlItemView = class_moved(xmlrss.XmlItemView)
+XmlRsetView = class_moved(xmlrss.XmlRsetView)
+RssView = class_moved(xmlrss.RssView)
+RssItemView = class_moved(xmlrss.RssItemView)
+
--- a/web/views/bookmark.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/bookmark.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """Primary view for bookmarks + user's bookmarks box
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -9,14 +9,29 @@
 from logilab.mtconverter import html_escape
 
 from cubicweb import Unauthorized
+from cubicweb.selectors import implements
 from cubicweb.web.htmlwidgets import BoxWidget, BoxMenu, RawBoxItem
-from cubicweb.web.box import UserRQLBoxTemplate
-from cubicweb.web.views.baseviews import PrimaryView
+from cubicweb.web import uicfg, action, box, formwidgets
+from cubicweb.web.views import primary
+
+uicfg.rcategories.tag_relation('primary', ('Bookmark', 'path', '*'), 'subject')
+uicfg.rwidgets.tag_relation(formwidgets.TextInput, ('Bookmark', 'path', '*'), 'subject')
 
 
-class BookmarkPrimaryView(PrimaryView):
-    accepts = ('Bookmark',)
-        
+class FollowAction(action.Action):
+    id = 'follow'
+    __select__ = implements('Bookmark')
+
+    title = _('follow')
+    category = 'mainactions'
+
+    def url(self):
+        return self.rset.get_entity(self.row or 0, self.col or 0).actual_url()
+
+
+class BookmarkPrimaryView(primary.PrimaryView):
+    __select__ = implements('Bookmark')
+
     def cell_call(self, row, col):
         """the primary view for bookmark entity"""
         entity = self.complete_entity(row, col)
@@ -32,7 +47,7 @@
         self.w(u'</div>')
 
 
-class BookmarksBox(UserRQLBoxTemplate):
+class BookmarksBox(box.UserRQLBoxTemplate):
     """display a box containing all user's bookmarks"""
     id = 'bookmarks_box'
     order = 40
@@ -42,8 +57,8 @@
            'U eid %(x)s')
     etype = 'Bookmark'
     rtype = 'bookmarked_by'
-    
-    
+
+
     def call(self, **kwargs):
         req = self.req
         ueid = req.user.eid
--- a/web/views/boxes.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/boxes.py	Mon May 04 13:09:48 2009 +0200
@@ -3,7 +3,6 @@
 
 * actions box
 * possible views box
-* rss icon
 
 additional (disabled by default) boxes
 * schema box
@@ -16,20 +15,66 @@
 __docformat__ = "restructuredtext en"
 
 from logilab.mtconverter import html_escape
-from cubicweb.common.selectors import any_rset, appobject_selectable
+
+from cubicweb.rtags import RelationTags
+from cubicweb.selectors import match_user_groups, non_final_entity
 from cubicweb.web.htmlwidgets import BoxWidget, BoxMenu, BoxHtml, RawBoxItem
-from cubicweb.web.box import BoxTemplate, ExtResourcesBoxTemplate
+from cubicweb.view import EntityView
+from cubicweb.web import uicfg
+from cubicweb.web.box import BoxTemplate
 
 _ = unicode
-    
+
 class EditBox(BoxTemplate):
     """
     box with all actions impacting the entity displayed: edit, copy, delete
     change state, add related entities
     """
     id = 'edit_box'
+    __select__ = BoxTemplate.__select__ & non_final_entity()
+
     title = _('actions')
     order = 2
+    # class attributes below are actually stored in the uicfg module since we
+    # don't want them to be reloaded
+    rmode = uicfg.rmode
+
+    @classmethod
+    def vreg_initialization_completed(cls):
+        """set default category tags for relations where it's not yet defined in
+        the category relation tags
+        """
+        for eschema in cls.schema.entities():
+            for rschema, tschemas, role in eschema.relation_definitions(True):
+                for tschema in tschemas:
+                    if role == 'subject':
+                        X, Y = eschema, tschema
+                        card = rschema.rproperty(X, Y, 'cardinality')[0]
+                    else:
+                        X, Y = tschema, eschema
+                        card = rschema.rproperty(X, Y, 'cardinality')[1]
+                    if not cls.rmode.get(rschema, role, X, Y):
+                        if card in '?1':
+                            # by default, suppose link mode if cardinality doesn't allow
+                            # more than one relation
+                            mode = 'link'
+                        elif rschema.rproperty(X, Y, 'composite') == role:
+                            # if self is composed of the target type, create mode
+                            mode = 'create'
+                        else:
+                            # link mode by default
+                            mode = 'link'
+                        cls.rmode.tag_relation(mode, (X, rschema, Y), role)
+
+    @classmethod
+    def relation_mode(cls, rtype, etype, targettype, role='subject'):
+        """return a string telling if the given relation is usually created
+        to a new entity ('create' mode) or to an existant entity ('link' mode)
+        """
+        if role == 'subject':
+            return cls.rmode.get(rtype, role, etype, targettype)
+        return cls.rmode.get(rtype, role, targettype, etype)
+
 
     def call(self, view=None, **kwargs):
         _ = self.req._
@@ -74,7 +119,7 @@
         self.add_submenu(box, other_menu)
         if not box.is_empty():
             box.render(self.w)
-            
+
     def add_submenu(self, box, submenu, label_prefix=None):
         if len(submenu.items) == 1:
             boxlink = submenu.items[0]
@@ -83,13 +128,13 @@
             box.append(boxlink)
         elif submenu.items:
             box.append(submenu)
-        
+
     def schema_actions(self, entity):
         user = self.req.user
         actions = []
         _ = self.req._
         eschema = entity.e_schema
-        for rschema, teschema, x in entity.add_related_schemas():
+        for rschema, teschema, x in self.add_related_schemas(entity):
             if x == 'subject':
                 label = 'add %s %s %s %s' % (eschema, rschema, teschema, x)
                 url = self.linkto_url(entity, rschema, teschema, 'object')
@@ -99,6 +144,33 @@
             actions.append(self.mk_action(_(label), url))
         return actions
 
+    def add_related_schemas(self, entity):
+        """this is actually used ui method to generate 'addrelated' actions from
+        the schema.
+
+        If you're using explicit 'addrelated' actions for an entity types, you
+        should probably overrides this method to return an empty list else you
+        may get some unexpected actions.
+        """
+        req = self.req
+        eschema = entity.e_schema
+        for role, rschemas in (('subject', eschema.subject_relations()),
+                               ('object', eschema.object_relations())):
+            for rschema in rschemas:
+                if rschema.is_final():
+                    continue
+                # check the relation can be added as well
+                if role == 'subject'and not rschema.has_perm(req, 'add', fromeid=entity.eid):
+                    continue
+                if role == 'object'and not rschema.has_perm(req, 'add', toeid=entity.eid):
+                    continue
+                # check the target types can be added as well
+                for teschema in rschema.targets(eschema, role):
+                    if not self.relation_mode(rschema, eschema, teschema, role) == 'create':
+                        continue
+                    if teschema.has_local_role('add') or teschema.has_perm(req, 'add'):
+                        yield rschema, teschema, role
+
 
     def workflow_actions(self, entity, box):
         if 'in_state' in entity.e_schema.subject_relations() and entity.in_state:
@@ -130,10 +202,10 @@
 class SearchBox(BoxTemplate):
     """display a box with a simple search form"""
     id = 'search_box'
+
     visible = True # enabled by default
     title = _('search')
     order = 0
-    need_resources = 'SEARCH_GO'
     formdef = u"""<form action="%s">
 <table id="tsearch"><tr><td>
 <input id="norql" type="text" accesskey="q" tabindex="%s" title="search text" value="%s" name="rql" />
@@ -144,7 +216,6 @@
 </td></tr></table>
 </form>"""
 
-
     def call(self, view=None, **kwargs):
         req = self.req
         if req.form.pop('__fromsearchbox', None):
@@ -156,7 +227,7 @@
         title = u"""<span onclick="javascript: toggleVisibility('rqlinput')">%s</span>""" % req._(self.title)
         box = BoxWidget(title, self.id, _class="searchBoxFrame", islist=False, escape=False)
         box.append(BoxHtml(form))
-        box.render(self.w)            
+        box.render(self.w)
 
 
 # boxes disabled by default ###################################################
@@ -164,11 +235,11 @@
 class PossibleViewsBox(BoxTemplate):
     """display a box containing links to all possible views"""
     id = 'possible_views_box'
-    
+    __select__ = BoxTemplate.__select__ & match_user_groups('users', 'managers')
+
+    visible = False
     title = _('possible views')
     order = 10
-    require_groups = ('users', 'managers')
-    visible = False
 
     def call(self, **kwargs):
         box = BoxWidget(self.req._(self.title), self.id)
@@ -182,27 +253,11 @@
         if not box.is_empty():
             box.render(self.w)
 
-        
-class RSSIconBox(ExtResourcesBoxTemplate):
-    """just display the RSS icon on uniform result set"""
-    __selectors__ = ExtResourcesBoxTemplate.__selectors__ + (appobject_selectable('components', 'rss_feed_url'),)
-    
-    id = 'rss'
-    order = 999
-    need_resources = 'RSS_LOGO',
-    visible = False
-    
-    def call(self, **kwargs):
-        urlgetter = self.vreg.select_component('rss_feed_url', self.req, self.rset)
-        url = urlgetter.feed_url()
-        rss = self.req.external_resource('RSS_LOGO')
-        self.w(u'<a href="%s"><img src="%s" alt="rss"/></a>\n' % (html_escape(url), rss))
-
 
 class StartupViewsBox(BoxTemplate):
     """display a box containing links to all startup views"""
     id = 'startup_views_box'
-    visible = False# disabled by default
+    visible = False # disabled by default
     title = _('startup views')
     order = 70
 
@@ -211,7 +266,36 @@
         for view in self.vreg.possible_views(self.req, None):
             if view.category == 'startupview':
                 box.append(self.box_action(view))
-        
+
         if not box.is_empty():
             box.render(self.w)
 
+
+# helper classes ##############################################################
+
+class SideBoxView(EntityView):
+    """helper view class to display some entities in a sidebox"""
+    id = 'sidebox'
+
+    def call(self, boxclass='sideBox', title=u''):
+        """display a list of entities by calling their <item_vid> view"""
+        if title:
+            self.w(u'<div class="sideBoxTitle"><span>%s</span></div>' % title)
+        self.w(u'<div class="%s"><div class="sideBoxBody">' % boxclass)
+        # if not too much entities, show them all in a list
+        maxrelated = self.req.property_value('navigation.related-limit')
+        if self.rset.rowcount <= maxrelated:
+            if len(self.rset) == 1:
+                self.wview('incontext', self.rset, row=0)
+            elif 1 < len(self.rset) < 5:
+                self.wview('csv', self.rset)
+            else:
+                self.wview('simplelist', self.rset)
+        # else show links to display related entities
+        else:
+            self.rset.limit(maxrelated)
+            rql = self.rset.printable_rql(encoded=False)
+            self.wview('simplelist', self.rset)
+            self.w(u'[<a href="%s">%s</a>]' % (self.build_url(rql=rql),
+                                               self.req._('see them all')))
+        self.w(u'</div>\n</div>\n')
--- a/web/views/calendar.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/calendar.py	Mon May 04 13:09:48 2009 +0200
@@ -1,90 +1,44 @@
 """html calendar views
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
-from mx.DateTime import DateTime, RelativeDateTime, today, ISO
-from datetime import datetime
+from datetime import datetime, date, timedelta
 
-from vobject import iCalendar, icalendar
+from vobject import iCalendar
 
 from logilab.mtconverter import html_escape
 
 from cubicweb.interfaces import ICalendarable
-from cubicweb.common.utils import date_range
-from cubicweb.common.uilib import ajax_replace_url
-from cubicweb.common.selectors import implement_interface
-from cubicweb.common.registerers import priority_registerer
-from cubicweb.common.view import EntityView
-
-
-# For backward compatibility
-from cubicweb.interfaces import ICalendarViews, ITimetableViews
-try:
-    from cubicweb.web.views.old_calendar import _CalendarView, AMPMWeekCalendarView
-except ImportError:
-    import logging
-    logger = logging.getLogger('cubicweb.registry')
-    logger.info("old calendar views could not be found and won't be registered")
+from cubicweb.selectors import implements
+from cubicweb.utils import strptime, date_range, todate
+from cubicweb.view import EntityView
+from cubicweb.web import ajax_replace_url
 
 _ = unicode
 
-# useful constants & functions
-def mkdt(mxdate):
-    """
-    Build a stdlib datetime date from a mx.datetime 
-    """
-    d = mxdate
-    return datetime(d.year, d.month, d.day, d.hour, d.minute,
-                    tzinfo=icalendar.utc)
-def iso(mxdate):
-    """
-    Format a ms datetime in ISO 8601 string 
-    """
-    # XXX What about timezone?
-    return ISO.str(mxdate)
+# useful constants & functions ################################################
 
-# mx.DateTime and ustrftime could be used to build WEEKDAYS
+ONEDAY = timedelta(1)
+
 WEEKDAYS = (_("monday"), _("tuesday"), _("wednesday"), _("thursday"),
             _("friday"), _("saturday"), _("sunday"))
-
-# used by i18n tools
 MONTHNAMES = ( _('january'), _('february'), _('march'), _('april'), _('may'),
                _('june'), _('july'), _('august'), _('september'), _('october'),
                _('november'), _('december')
                )
 
-#################
-# In calendar views (views used as calendar cell item) 
-
-
-class CalendarItemView(EntityView):
-    id = 'calendaritem'
+# Calendar views ##############################################################
 
-    def cell_call(self, row, col, dates=False):
-        task = self.complete_entity(row)
-        task.view('oneline', w=self.w)
-        if dates:
-            if task.start and task.stop:
-                self.w('<br/>from %s'%self.format_date(task.start))
-                self.w('<br/>to %s'%self.format_date(task.stop))
-                
-class CalendarLargeItemView(CalendarItemView):
-    id = 'calendarlargeitem'
-        
-#################
-# Calendar views
 
 class iCalView(EntityView):
     """A calendar view that generates a iCalendar file (RFC 2445)
 
     Does apply to ICalendarable compatible entities
     """
-    __registerer__ = priority_registerer
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (ICalendarable,)
+    __select__ = implements(ICalendarable)
     need_navigation = False
     content_type = 'text/calendar'
     title = _('iCalendar')
@@ -99,9 +53,9 @@
             event.add('summary').value = task.dc_title()
             event.add('description').value = task.dc_description()
             if task.start:
-                event.add('dtstart').value = mkdt(task.start)
+                event.add('dtstart').value = task.start
             if task.stop:
-                event.add('dtend').value = mkdt(task.stop)
+                event.add('dtend').value = task.stop
 
         buff = ical.serialize()
         if not isinstance(buff, unicode):
@@ -113,13 +67,11 @@
 
     Does apply to ICalendarable compatible entities
     """
-    __registerer__ = priority_registerer
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (ICalendarable,)
+    id = 'hcal'
+    __select__ = implements(ICalendarable)
     need_navigation = False
     title = _('hCalendar')
     #templatable = False
-    id = 'hcal'
 
     def call(self):
         self.w(u'<div class="hcalendar">')
@@ -129,13 +81,29 @@
             self.w(u'<h3 class="summary">%s</h3>' % html_escape(task.dc_title()))
             self.w(u'<div class="description">%s</div>' % html_escape(task.dc_description()))
             if task.start:
-                self.w(u'<abbr class="dtstart" title="%s">%s</abbr>' % (iso(task.start), self.format_date(task.start)))
+                self.w(u'<abbr class="dtstart" title="%s">%s</abbr>' % (task.start.isoformat(), self.format_date(task.start)))
             if task.stop:
-                self.w(u'<abbr class="dtstop" title="%s">%s</abbr>' % (iso(task.stop), self.format_date(task.stop)))
+                self.w(u'<abbr class="dtstop" title="%s">%s</abbr>' % (task.stop.isoformat(), self.format_date(task.stop)))
             self.w(u'</div>')
         self.w(u'</div>')
 
-    
+
+class CalendarItemView(EntityView):
+    id = 'calendaritem'
+
+    def cell_call(self, row, col, dates=False):
+        task = self.complete_entity(row)
+        task.view('oneline', w=self.w)
+        if dates:
+            if task.start and task.stop:
+                self.w('<br/>' % self.req._('from %(date)s' % {'date': self.format_date(task.start)}))
+                self.w('<br/>' % self.req._('to %(date)s' % {'date': self.format_date(task.stop)}))
+                self.w('<br/>to %s'%self.format_date(task.stop))
+
+class CalendarLargeItemView(CalendarItemView):
+    id = 'calendarlargeitem'
+
+
 class _TaskEntry(object):
     def __init__(self, task, color, index=0):
         self.task = task
@@ -143,20 +111,29 @@
         self.index = index
         self.length = 1
 
+    def in_working_hours(self):
+        """predicate returning True is the task is in working hours"""
+        if self.task.start.hour > 7 and self.task.stop.hour < 20:
+            return True
+        return False
+
+    def is_one_day_task(self):
+        task = self.task
+        return task.start and task.stop and task.start.isocalendar() ==  task.stop.isocalendar()
+
+
 class OneMonthCal(EntityView):
     """At some point, this view will probably replace ampm calendars"""
-    __registerer__ = priority_registerer
-    __selectors__ = (implement_interface, )
-    accepts_interfaces = (ICalendarable,)
+    id = 'onemonthcal'
+    __select__ = implements(ICalendarable)
     need_navigation = False
-    id = 'onemonthcal'
     title = _('one month')
 
     def call(self):
         self.req.add_js('cubicweb.ajax.js')
         self.req.add_css('cubicweb.calendar.css')
         # XXX: restrict courses directy with RQL
-        _today =  today()
+        _today =  datetime.today()
 
         if 'year' in self.req.form:
             year = int(self.req.form['year'])
@@ -167,56 +144,60 @@
         else:
             month = _today.month
 
-        first_day_of_month = DateTime(year, month, 1)
-        lastday = first_day_of_month + RelativeDateTime(months=1,weekday=(6,1))
-        firstday= first_day_of_month + RelativeDateTime(months=-1,weekday=(0,-1))
+        first_day_of_month = date(year, month, 1)
+        firstday = first_day_of_month - timedelta(first_day_of_month.weekday())
+        if month >= 12:
+            last_day_of_month = date(year + 1, 1, 1) - timedelta(1)
+        else:
+            last_day_of_month = date(year, month + 1, 1) - timedelta(1)
+        lastday = last_day_of_month + timedelta(6 - last_day_of_month.weekday())
         month_dates = list(date_range(firstday, lastday))
         dates = {}
-        users = []
         task_max = 0
         for row in xrange(self.rset.rowcount):
-            task = self.rset.get_entity(row,0)
-            if len(self.rset[row]) > 1 and self.rset.description[row][1] == 'EUser':
-                user = self.rset.get_entity(row,1)
+            task = self.rset.get_entity(row, 0)
+            if len(self.rset[row]) > 1 and self.rset.description[row][1] == 'CWUser':
+                user = self.rset.get_entity(row, 1)
             else:
                 user = None
             the_dates = []
-            if task.start:
-                if task.start > lastday:
+            tstart = todate(task.start)
+            if tstart:
+                if tstart > lastday:
                     continue
-                the_dates = [task.start]
-            if task.stop:
-                if task.stop < firstday:
+                the_dates = [tstart]
+            tstop = todate(task.start)
+            if tstop:
+                if tstop < firstday:
                     continue
-                the_dates = [task.stop]
-            if task.start and task.stop:
-                if task.start.absdate == task.stop.absdate:
-                    date = task.start
-                    if firstday<= date <= lastday:
-                        the_dates = [date]
+                the_dates = [tstop]
+            if tstart and tstop:
+                if tstart.isocalendar() == tstop.isocalendar():
+                    if firstday <= tstart <= lastday:
+                        the_dates = [tstart]
                 else:
-                    the_dates = date_range(max(task.start,firstday),
-                                           min(task.stop,lastday))
+                    the_dates = date_range(max(tstart, firstday),
+                                           min(tstop, lastday))
             if not the_dates:
                 continue
-            
+
             for d in the_dates:
                 d_tasks = dates.setdefault((d.year, d.month, d.day), {})
-                t_users = d_tasks.setdefault(task,set())
+                t_users = d_tasks.setdefault(task, set())
                 t_users.add( user )
-                if len(d_tasks)>task_max:
+                if len(d_tasks) > task_max:
                     task_max = len(d_tasks)
 
         days = []
-        nrows = max(3,task_max)
+        nrows = max(3, task_max)
         # colors here are class names defined in cubicweb.css
-        colors = [ "col%x"%i for i in range(12) ]
+        colors = [ "col%x" % i for i in range(12) ]
         next_color_index = 0
 
         visited_tasks = {} # holds a description of a task
         task_colors = {}   # remember a color assigned to a task
-        for date in month_dates:
-            d_tasks = dates.get((date.year, date.month, date.day), {})
+        for mdate in month_dates:
+            d_tasks = dates.get((mdate.year, mdate.month, mdate.day), {})
             rows = [None] * nrows
             # every task that is "visited" for the first time
             # require a special treatment, so we put them in
@@ -232,7 +213,7 @@
                 # to every 'new' task we must affect a color
                 # (which must be the same for every user concerned
                 # by the task)
-                for i,t in enumerate(rows):
+                for i, t in enumerate(rows):
                     if t is None:
                         if task in task_colors:
                             color = task_colors[task]
@@ -262,43 +243,43 @@
         # output header
         self.w(u'<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>' %
                tuple(self.req._(day) for day in WEEKDAYS))
-        
+
         # build calendar
-        for date, task_rows in zip(month_dates, days):
-            if date.day_of_week == 0:
+        for mdate, task_rows in zip(month_dates, days):
+            if mdate.weekday() == 0:
                 self.w(u'<tr>')
-            self._build_calendar_cell(date, task_rows, curdate)
-            if date.day_of_week == 6:
+            self._build_calendar_cell(mdate, task_rows, curdate)
+            if mdate.weekday() == 6:
                 self.w(u'</tr>')
         self.w(u'</table></div>')
 
     def _prevnext_links(self, curdate):
-        prevdate = curdate - RelativeDateTime(months=1)
-        nextdate = curdate + RelativeDateTime(months=1)
-        rql = self.rset.rql
+        prevdate = curdate - timedelta(31)
+        nextdate = curdate + timedelta(31)
+        rql = self.rset.printable_rql()
         prevlink = ajax_replace_url('onemonthcalid', rql, 'onemonthcal',
                                     year=prevdate.year, month=prevdate.month)
         nextlink = ajax_replace_url('onemonthcalid', rql, 'onemonthcal',
                                     year=nextdate.year, month=nextdate.month)
         return prevlink, nextlink
 
-    def _build_calendar_cell(self, date, rows, curdate):
+    def _build_calendar_cell(self, celldate, rows, curdate):
         curmonth = curdate.month
         classes = ""
-        if date.month != curmonth:
+        if celldate.month != curmonth:
             classes += " outOfRange"
-        if date == today():
+        if celldate == date.today():
             classes += " today"
         self.w(u'<td class="cell%s">' % classes)
         self.w(u'<div class="calCellTitle%s">' % classes)
-        self.w(u'<div class="day">%s</div>' % date.day)
-        
+        self.w(u'<div class="day">%s</div>' % celldate.day)
+
         if len(self.rset.column_types(0)) == 1:
             etype = list(self.rset.column_types(0))[0]
             url = self.build_url(vid='creation', etype=etype,
                                  schedule=True,
-                                 start=self.format_date(date), stop=self.format_date(date),
-                                 __redirectrql=self.rset.rql,
+                                 start=self.format_date(celldate), stop=self.format_date(celldate),
+                                 __redirectrql=self.rset.printable_rql(),
                                  __redirectparams=self.req.build_url_params(year=curdate.year, month=curmonth),
                                  __redirectvid=self.id
                                  )
@@ -312,7 +293,7 @@
                 self.w(u'<div class="task %s">' % task_descr.color)
                 task.view('calendaritem', w=self.w )
                 url = task.absolute_url(vid='edition',
-                                        __redirectrql=self.rset.rql,
+                                        __redirectrql=self.rset.printable_rql(),
                                         __redirectparams=self.req.build_url_params(year=curdate.year, month=curmonth),
                                         __redirectvid=self.id
                                         )
@@ -330,19 +311,16 @@
 
 class OneWeekCal(EntityView):
     """At some point, this view will probably replace ampm calendars"""
-    __registerer__ = priority_registerer
-    __selectors__ = (implement_interface, )
-    accepts_interfaces = (ICalendarable,)
+    id = 'oneweekcal'
+    __select__ = implements(ICalendarable)
     need_navigation = False
-    id = 'oneweekcal'
     title = _('one week')
-    
+
     def call(self):
         self.req.add_js( ('cubicweb.ajax.js', 'cubicweb.calendar.js') )
         self.req.add_css('cubicweb.calendar.css')
-        # XXX: restrict courses directy with RQL
-        _today =  today()
-
+        # XXX: restrict directly with RQL
+        _today =  datetime.today()
         if 'year' in self.req.form:
             year = int(self.req.form['year'])
         else:
@@ -350,47 +328,48 @@
         if 'week' in self.req.form:
             week = int(self.req.form['week'])
         else:
-            week = _today.iso_week[1]        
-
-        first_day_of_week = ISO.ParseWeek("%s-W%s-1"%(year, week))
-        lastday = first_day_of_week + RelativeDateTime(days=6)
-        firstday= first_day_of_week
+            week = _today.isocalendar()[1]
+        # week - 1 since we get week number > 0 while we want it to start from 0
+        first_day_of_week = todate(strptime('%s-%s-1' % (year, week - 1), '%Y-%U-%w'))
+        lastday = first_day_of_week + timedelta(6)
+        firstday = first_day_of_week
         dates = [[] for i in range(7)]
-        task_max = 0
         task_colors = {}   # remember a color assigned to a task
         # colors here are class names defined in cubicweb.css
-        colors = [ "col%x"%i for i in range(12) ]
+        colors = [ "col%x" % i for i in range(12) ]
         next_color_index = 0
         done_tasks = []
         for row in xrange(self.rset.rowcount):
-            task = self.rset.get_entity(row,0)
+            task = self.rset.get_entity(row, 0)
             if task in done_tasks:
                 continue
             done_tasks.append(task)
             the_dates = []
-            if task.start:
-                if task.start > lastday:
+            tstart = todate(task.start)
+            tstop = todate(task.stop)
+            if tstart:
+                if tstart > lastday:
                     continue
-                the_dates = [task.start]
-            if task.stop:
-                if task.stop < firstday:
+                the_dates = [tstart]
+            if tstop:
+                if tstop < firstday:
                     continue
-                the_dates = [task.stop]
-            if task.start and task.stop:
-                the_dates = date_range(max(task.start,firstday),
-                                       min(task.stop,lastday))
+                the_dates = [tstop]
+            if tstart and tstop:
+                the_dates = date_range(max(tstart, firstday),
+                                       min(tstop, lastday))
             if not the_dates:
                 continue
-                
+
             if task not in task_colors:
                 task_colors[task] = colors[next_color_index]
-                next_color_index = (next_color_index+1)%len(colors)
-            
+                next_color_index = (next_color_index+1) % len(colors)
+
             for d in the_dates:
-                day = d.day_of_week
-                task_descr = _TaskEntry(task, task_colors[task])  
+                day = d.weekday()
+                task_descr = _TaskEntry(task, task_colors[task])
                 dates[day].append(task_descr)
-            
+
         self.w(u'<div id="oneweekcalid">')
         # build schedule
         self.w(u'<table class="omcalendar" id="week">')
@@ -399,22 +378,21 @@
         self.w(u'<th><a href="%s">&lt;&lt;</a></th><th colspan="5">%s %s %s</th>'
                u'<th><a href="%s">&gt;&gt;</a></th></tr>' %
                (html_escape(prevlink), first_day_of_week.year,
-                self.req._(u'week'), first_day_of_week.iso_week[1],
+                self.req._(u'week'), first_day_of_week.isocalendar()[1],
                 html_escape(nextlink)))
 
         # output header
         self.w(u'<tr>')
         self.w(u'<th class="transparent"></th>') # column for hours
-        _today = today()
+        _today = date.today()
         for i, day in enumerate(WEEKDAYS):
-            date = first_day_of_week + i
-            if date.absdate == _today.absdate:
-                self.w(u'<th class="today">%s<br/>%s</th>' % (self.req._(day), self.format_date(date)))
+            wdate = first_day_of_week + timedelta(i)
+            if wdate.isocalendar() == _today.isocalendar():
+                self.w(u'<th class="today">%s<br/>%s</th>' % (self.req._(day), self.format_date(wdate)))
             else:
-                self.w(u'<th>%s<br/>%s</th>' % (self.req._(day), self.format_date(date)))
+                self.w(u'<th>%s<br/>%s</th>' % (self.req._(day), self.format_date(wdate)))
         self.w(u'</tr>')
 
-        
         # build week calendar
         self.w(u'<tr>')
         self.w(u'<td style="width:5em;">') # column for hours
@@ -422,52 +400,43 @@
         for h in range(8, 20):
             self.w(u'<div class="hour" %s>'%extra)
             self.w(u'%02d:00'%h)
-            self.w(u'</div>')            
+            self.w(u'</div>')
         self.w(u'</td>')
-        
+
         for i, day in enumerate(WEEKDAYS):
-            date = first_day_of_week + i
+            wdate = first_day_of_week + timedelta(i)
             classes = ""
-            if date.absdate == _today.absdate:
+            if wdate.isocalendar() == _today.isocalendar():
                 classes = " today"
-            self.w(u'<td class="column %s" id="%s">'%(classes, day))
+            self.w(u'<td class="column %s" id="%s">' % (classes, day))
             if len(self.rset.column_types(0)) == 1:
                 etype = list(self.rset.column_types(0))[0]
                 url = self.build_url(vid='creation', etype=etype,
                                      schedule=True,
-                                     __redirectrql=self.rset.rql,
+                                     __redirectrql=self.rset.printable_rql(),
                                      __redirectparams=self.req.build_url_params(year=year, week=week),
                                      __redirectvid=self.id
                                      )
-                extra = ' ondblclick="addCalendarItem(event, hmin=%s, hmax=%s, year=%s, month=%s, day=%s, duration=%s, baseurl=\'%s\')"' % (8,20,date.year, date.month, date.day, 2, html_escape(url))
+                extra = ' ondblclick="addCalendarItem(event, hmin=8, hmax=20, year=%s, month=%s, day=%s, duration=2, baseurl=\'%s\')"' % (
+                    wdate.year, wdate.month, wdate.day, html_escape(url))
             else:
                 extra = ""
             self.w(u'<div class="columndiv"%s>'% extra)
             for h in range(8, 20):
                 self.w(u'<div class="hourline" style="top:%sex;">'%((h-7)*8))
-                self.w(u'</div>')            
+                self.w(u'</div>')
             if dates[i]:
-                self._build_calendar_cell(date, dates[i])
+                self._build_calendar_cell(wdate, dates[i])
             self.w(u'</div>')
             self.w(u'</td>')
         self.w(u'</tr>')
         self.w(u'</table></div>')
         self.w(u'<div id="coord"></div>')
         self.w(u'<div id="debug">&nbsp;</div>')
- 
-    def _one_day_task(self, task):
-        """
-        Return true if the task is a "one day" task; ie it have a start and a stop the same day
-        """
-        if task.start and task.stop:
-            if task.start.absdate ==  task.stop.absdate:
-                return True
-        return False
-        
+
     def _build_calendar_cell(self, date, task_descrs):
-        inday_tasks = [t for t in task_descrs if self._one_day_task(t.task) and  t.task.start.hour<20 and t.task.stop.hour>7]
-        wholeday_tasks = [t for t in task_descrs if not self._one_day_task(t.task)]
-
+        inday_tasks = [t for t in task_descrs if t.is_one_day_task() and  t.in_working_hours()]
+        wholeday_tasks = [t for t in task_descrs if not t.is_one_day_task()]
         inday_tasks.sort(key=lambda t:t.task.start)
         sorted_tasks = []
         for i, t in enumerate(wholeday_tasks):
@@ -498,15 +467,15 @@
             stop_hour = 20
             stop_min = 0
             if task.start:
-                if date < task.start < date + 1:
+                if date < todate(task.start) < date + ONEDAY:
                     start_hour = max(8, task.start.hour)
                     start_min = task.start.minute
             if task.stop:
-                if date < task.stop < date + 1:
+                if date < todate(task.stop) < date + ONEDAY:
                     stop_hour = min(20, task.stop.hour)
                     if stop_hour < 20:
                         stop_min = task.stop.minute
-                    
+
             height = 100.0*(stop_hour+stop_min/60.0-start_hour-start_min/60.0)/(20-8)
             top = 100.0*(start_hour+start_min/60.0-8)/(20-8)
             left = width*task_desc.index
@@ -516,8 +485,8 @@
                        (task_desc.color, style))
             task.view('calendaritem', dates=False, w=self.w)
             url = task.absolute_url(vid='edition',
-                                    __redirectrql=self.rset.rql,
-                                    __redirectparams=self.req.build_url_params(year=date.year, week=date.iso_week[1]),
+                                    __redirectrql=self.rset.printable_rql(),
+                                    __redirectparams=self.req.build_url_params(year=date.year, week=date.isocalendar()[1]),
                                     __redirectvid=self.id
                                  )
 
@@ -536,14 +505,14 @@
                 self.w(u'</div>')
             self.w(u'</div>')
 
-            
+
     def _prevnext_links(self, curdate):
-        prevdate = curdate - RelativeDateTime(days=7)
-        nextdate = curdate + RelativeDateTime(days=7)
-        rql = self.rset.rql
+        prevdate = curdate - timedelta(7)
+        nextdate = curdate + timedelta(7)
+        rql = self.rset.printable_rql()
         prevlink = ajax_replace_url('oneweekcalid', rql, 'oneweekcal',
-                                    year=prevdate.year, week=prevdate.iso_week[1])
+                                    year=prevdate.year, week=prevdate.isocalendar()[1])
         nextlink = ajax_replace_url('oneweekcalid', rql, 'oneweekcal',
-                                    year=nextdate.year, week=nextdate.iso_week[1])
+                                    year=nextdate.year, week=nextdate.isocalendar()[1])
         return prevlink, nextlink
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/csvexport.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,87 @@
+"""csv export views
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from cubicweb.common.uilib import UnicodeCSVWriter
+from cubicweb.view import EntityView, AnyRsetView
+
+class CSVMixIn(object):
+    """mixin class for CSV views"""
+    templatable = False
+    content_type = "text/comma-separated-values"
+    binary = True # avoid unicode assertion
+    csv_params = {'dialect': 'excel',
+                  'quotechar': '"',
+                  'delimiter': ';',
+                  'lineterminator': '\n'}
+
+    def set_request_content_type(self):
+        """overriden to set a .csv filename"""
+        self.req.set_content_type(self.content_type, filename='cubicwebexport.csv')
+
+    def csvwriter(self, **kwargs):
+        params = self.csv_params.copy()
+        params.update(kwargs)
+        return UnicodeCSVWriter(self.w, self.req.encoding, **params)
+
+
+class CSVRsetView(CSVMixIn, AnyRsetView):
+    """dumps raw result set in CSV"""
+    id = 'csvexport'
+    title = _('csv export')
+
+    def call(self):
+        writer = self.csvwriter()
+        writer.writerow(self.columns_labels())
+        rset, descr = self.rset, self.rset.description
+        eschema = self.schema.eschema
+        for rowindex, row in enumerate(rset):
+            csvrow = []
+            for colindex, val in enumerate(row):
+                etype = descr[rowindex][colindex]
+                if val is not None and not eschema(etype).is_final():
+                    # csvrow.append(val) # val is eid in that case
+                    content = self.view('textincontext', rset,
+                                        row=rowindex, col=colindex)
+                else:
+                    content = self.view('final', rset,
+                                        displaytime=True, format='text/plain',
+                                        row=rowindex, col=colindex)
+                csvrow.append(content)
+            writer.writerow(csvrow)
+
+
+class CSVEntityView(CSVMixIn, EntityView):
+    """dumps rset's entities (with full set of attributes) in CSV
+
+    the generated CSV file will have a table per entity type found in the
+    resultset. ('table' here only means empty lines separation between table
+    contents)
+    """
+    id = 'ecsvexport'
+    title = _('csv entities export')
+
+    def call(self):
+        req = self.req
+        rows_by_type = {}
+        writer = self.csvwriter()
+        rowdef_by_type = {}
+        for index in xrange(len(self.rset)):
+            entity = self.complete_entity(index)
+            if entity.e_schema not in rows_by_type:
+                rowdef_by_type[entity.e_schema] = [rs for rs, at in entity.e_schema.attribute_definitions()
+                                                   if at != 'Bytes']
+                rows_by_type[entity.e_schema] = [[display_name(req, rschema.type)
+                                                  for rschema in rowdef_by_type[entity.e_schema]]]
+            rows = rows_by_type[entity.e_schema]
+            rows.append([entity.printable_value(rs.type, format='text/plain')
+                         for rs in rowdef_by_type[entity.e_schema]])
+        for rows in rows_by_type.itervalues():
+            writer.writerows(rows)
+            # use two empty lines as separator
+            writer.writerows([[], []])
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/cwproperties.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,344 @@
+"""Specific views for CWProperty
+
+:organization: Logilab
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+_ = unicode
+
+from logilab.mtconverter import html_escape
+
+from logilab.common.decorators import cached
+
+from cubicweb import UnknownProperty
+from cubicweb.selectors import (one_line_rset, none_rset, implements,
+                                match_user_groups)
+from cubicweb.view import StartupView
+from cubicweb.web import uicfg
+from cubicweb.web.views import baseviews
+from cubicweb.web import stdmsgs
+from cubicweb.web.form import CompositeForm, EntityFieldsForm, FormViewMixIn
+from cubicweb.web.formfields import FIELDS, StringField
+from cubicweb.web.formwidgets import Select, Button, SubmitButton
+
+
+# some string we want to be internationalizable for nicer display of eproperty
+# groups
+_('navigation')
+_('ui')
+_('actions')
+_('boxes')
+_('components')
+_('contentnavigation')
+_('navigation.combobox-limit')
+_('navigation.page-size')
+_('navigation.related-limit')
+_('navigation.short-line-size')
+_('ui.date-format')
+_('ui.datetime-format')
+_('ui.default-text-format')
+_('ui.fckeditor')
+_('ui.float-format')
+_('ui.language')
+_('ui.time-format')
+_('open all')
+_('ui.main-template')
+_('ui.site-title')
+_('ui.encoding')
+_('category')
+
+
+def make_togglable_link(nodeid, label, cookiename):
+    """builds a HTML link that switches the visibility & remembers it"""
+    action = u"javascript: toggle_and_remember_visibility('%s', '%s')" % \
+        (nodeid, cookiename)
+    return u'<a href="%s">%s</a>' % (action, label)
+
+def css_class(someclass):
+    return someclass and 'class="%s"' % someclass or ''
+
+
+class CWPropertyPrimaryView(baseviews.PrimaryView):
+    __select__ = implements('CWProperty')
+    skip_none = False
+
+
+class SystemEPropertiesForm(FormViewMixIn, StartupView):
+    id = 'systemepropertiesform'
+    __select__ = none_rset() & match_user_groups('managers')
+
+    title = _('site configuration')
+    category = 'startupview'
+
+    def linkable(self):
+        return True
+
+    def url(self):
+        """return the url associated with this view. We can omit rql here"""
+        return self.build_url('view', vid=self.id)
+
+    def _cookie_name(self, somestr):
+        return str('%s_property_%s' % (self.config.appid, somestr))
+
+    def _group_status(self, group, default=u'hidden'):
+        """return css class name 'hidden' (collapsed), or '' (open)"""
+        cookies = self.req.get_cookie()
+        cookiename = self._cookie_name(group)
+        cookie = cookies.get(cookiename)
+        if cookie is None:
+            cookies[cookiename] = default
+            self.req.set_cookie(cookies, cookiename, maxage=None)
+            status = default
+        else:
+            status = cookie.value
+        return status
+
+    def call(self, **kwargs):
+        """The default view representing the application's index"""
+        self.req.add_js('cubicweb.preferences.js')
+        self.req.add_css('cubicweb.preferences.css')
+        vreg = self.vreg
+        values = self.defined_keys
+        groupedopts = {}
+        mainopts = {}
+        # "self.id=='systemepropertiesform'" to skip site wide properties on
+        # user's preference but not site's configuration
+        for key in vreg.user_property_keys(self.id=='systemepropertiesform'):
+            parts = key.split('.')
+            if parts[0] in vreg:
+                # appobject configuration
+                reg, oid, propid = parts
+                groupedopts.setdefault(reg, {}).setdefault(oid, []).append(key)
+            else:
+                mainopts.setdefault(parts[0], []).append(key)
+        # precompute form to consume error message
+        for group, keys in mainopts.items():
+            mainopts[group] = self.form(keys, True)
+        for group, objects in groupedopts.items():
+            for oid, keys in objects.items():
+                groupedopts[group][oid] = self.form(keys, True)
+        w = self.w
+        req = self.req
+        _ = req._
+        w(u'<h1>%s</h1>\n' % _(self.title))
+        # we don't want this in each sub-forms
+        w(u'<div id="progress">%s</div>' % self.req._('validating...'))
+        for label, group, form in sorted((_(g), g, f)
+                                         for g, f in mainopts.iteritems()):
+            status = css_class(self._group_status(group))
+            w(u'<h2 class="propertiesform">%s</h2>\n' %
+              (make_togglable_link('fieldset_' + group, label,
+                                   self._cookie_name(group))))
+            w(u'<div id="fieldset_%s" %s>' % (group, status))
+            w(form)
+            w(u'</div>')
+        for label, group, objects in sorted((_(g), g, o)
+                                            for g, o in groupedopts.iteritems()):
+            status = css_class(self._group_status(group))
+            w(u'<h2 class="propertiesform">%s</h2>\n' %
+              (make_togglable_link('fieldset_' + group, label,
+                                   self._cookie_name(group))))
+            w(u'<div id="fieldset_%s" %s>' % (group, status))
+            for label, oid, form in sorted((self.req.__('%s_%s' % (group, o)), o, f)
+                                           for o, f in objects.iteritems()):
+                w(u'<fieldset class="subentity">')
+                w(u'<legend class="componentTitle">%s</legend>\n' % label)
+                docmsgid = '%s_%s_description' % (group, oid)
+                doc = _(docmsgid)
+                if doc != docmsgid:
+                    w(u'<p class="description">%s</p>' % html_escape(doc))
+                w(form)
+                w(u'</fieldset>')
+            w(u'</div>')
+
+    @property
+    @cached
+    def eprops_rset(self):
+        return self.req.execute('Any P,K,V WHERE P is CWProperty, P pkey K, '
+                                'P value V, NOT P for_user U')
+
+    @property
+    def defined_keys(self):
+        values = {}
+        for i, entity in enumerate(self.eprops_rset.entities()):
+            values[entity.pkey] = i
+        return values
+
+    def entity_for_key(self, key):
+        values = self.defined_keys
+        if key in values:
+            entity = self.eprops_rset.get_entity(values[key], 0)
+        else:
+            entity = self.vreg.etype_class('CWProperty')(self.req, None, None)
+            entity.eid = self.req.varmaker.next()
+            entity['pkey'] = key
+            entity['value'] = self.vreg.property_value(key)
+        return entity
+
+    def form(self, keys, splitlabel=False):
+        buttons = [SubmitButton(),
+                   Button(stdmsgs.BUTTON_CANCEL, cwaction='cancel')]
+        form = CompositeForm(self.req, domid=None, action=self.build_url(),
+                             form_buttons=buttons,
+                             submitmsg=self.req._('changes applied'))
+        path = self.req.relative_path()
+        if '?' in path:
+            path, params = path.split('?', 1)
+            form.form_add_hidden('__redirectparams', params)
+        form.form_add_hidden('__redirectpath', path)
+        for key in keys:
+            self.form_row(form, key, splitlabel)
+        return form.form_render(display_progress_div=False)
+
+    def form_row(self, form, key, splitlabel):
+        entity = self.entity_for_key(key)
+        if splitlabel:
+            label = key.split('.')[-1]
+        else:
+            label = key
+        subform = EntityFieldsForm(self.req, entity=entity, set_error_url=False)
+        subform.append_field(PropertyValueField(name='value', label=label,
+                                                eidparam=True))
+        subform.vreg = self.vreg
+        subform.form_add_hidden('pkey', key, eidparam=True)
+        form.form_add_subform(subform)
+        return subform
+
+
+def is_user_prefs(cls, req, rset, row=None, col=0, **kwargs):
+    return req.user.eid == rset[row or 0][col]
+
+
+class EPropertiesForm(SystemEPropertiesForm):
+    id = 'epropertiesform'
+    __select__ = (
+        # we don't want guests to be able to come here
+        match_user_groups('users', 'managers') &
+        (none_rset() | ((one_line_rset() & is_user_prefs) &
+                        (one_line_rset() & match_user_groups('managers'))))
+        )
+
+    title = _('preferences')
+
+    @property
+    def user(self):
+        if self.rset is None:
+            return self.req.user
+        return self.rset.get_entity(self.row or 0, self.col or 0)
+
+    @property
+    @cached
+    def eprops_rset(self):
+        return self.req.execute('Any P,K,V WHERE P is CWProperty, P pkey K, P value V,'
+                                'P for_user U, U eid %(x)s', {'x': self.user.eid})
+
+    def form_row(self, form, key, splitlabel):
+        subform = super(EPropertiesForm, self).form_row(form, key, splitlabel)
+        # if user is in the managers group and the property is being created,
+        # we have to set for_user explicitly
+        if not subform.edited_entity.has_eid() and self.user.matching_groups('managers'):
+            subform.form_add_hidden('for_user', self.user.eid, eidparam=True)
+
+
+# eproperty form objects ######################################################
+
+class PlaceHolderWidget(object):
+
+    def render(self, form, field):
+        domid = form.context[field]['id']
+        # empty span as well else html validation fail (label is refering to
+        # this id)
+        return '<div id="div:%s"><span id="%s">%s</span></div>' % (
+            domid, domid, form.req._('select a key first'))
+
+
+class NotEditableWidget(object):
+    def __init__(self, value, msg=None):
+        self.value = value
+        self.msg = msg
+
+    def render(self, form, field):
+        domid = form.context[field]['id']
+        value = '<span class="value" id="%s">%s</span>' % (domid, self.value)
+        if self.msg:
+            value + '<div class="helper">%s</div>' % self.msg
+        return value
+
+
+class PropertyKeyField(StringField):
+    """specific field for CWProperty.pkey to set the value widget according to
+    the selected key
+    """
+    widget = Select
+
+    def render(self, form, renderer):
+        wdg = self.get_widget(form)
+        wdg.attrs['tabindex'] = form.req.next_tabindex()
+        wdg.attrs['onchange'] = "javascript:setPropValueWidget('%s', %s)" % (
+            form.edited_entity.eid, form.req.next_tabindex())
+        return wdg.render(form, self)
+
+    def vocabulary(self, form):
+        entity = form.edited_entity
+        _ = form.req._
+        if entity.has_eid():
+            return [(_(entity.pkey), entity.pkey)]
+        # key beginning with 'system.' should usually not be edited by hand
+        choices = entity.vreg.user_property_keys()
+        return [(u'', u'')] + sorted(zip((_(v) for v in choices), choices))
+
+
+class PropertyValueField(StringField):
+    """specific field for CWProperty.value  which will be different according to
+    the selected key type and vocabulary information
+    """
+    widget = PlaceHolderWidget
+
+    def render(self, form, renderer=None, tabindex=None):
+        wdg = self.get_widget(form)
+        if tabindex is not None:
+            wdg.attrs['tabindex'] = tabindex
+        return wdg.render(form, self)
+
+    def form_init(self, form):
+        entity = form.edited_entity
+        if not (entity.has_eid() or 'pkey' in entity):
+            # no key set yet, just include an empty div which will be filled
+            # on key selection
+            return
+        try:
+            pdef = form.vreg.property_info(entity.pkey)
+        except UnknownProperty, ex:
+            self.warning('%s (you should probably delete that property '
+                         'from the database)', ex)
+            msg = form.req._('you should probably delete that property')
+            self.widget = NotEditableWidget(entity.printable_value('value'),
+                                            '%s (%s)' % (msg, ex))
+        if entity.pkey.startswith('system.'):
+            msg = form.req._('value associated to this key is not editable '
+                             'manually')
+            self.widget = NotEditableWidget(entity.printable_value('value'), msg)
+        # XXX race condition when used from CWPropertyForm, should not rely on
+        # instance attributes
+        self.initial = pdef['default']
+        self.help = pdef['help']
+        vocab = pdef['vocabulary']
+        if vocab is not None:
+            if callable(vocab):
+                # list() just in case its a generator function
+                self.choices = list(vocab(form.req))
+            else:
+                self.choices = vocab
+            wdg = Select()
+        else:
+            wdg = FIELDS[pdef['type']].widget()
+            if pdef['type'] == 'Boolean':
+                self.choices = [(form.req._('yes'), '1'), (form.req._('no'), '')]
+            elif pdef['type'] in ('Float', 'Int'):
+                wdg.attrs.setdefault('size', 3)
+        self.widget = wdg
+
+
+uicfg.rfields.tag_relation(PropertyKeyField, ('CWProperty', 'pkey', '*'), 'subject')
+uicfg.rfields.tag_relation(PropertyValueField, ('CWProperty', 'value', '*'), 'subject')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/cwuser.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,89 @@
+"""Specific views for users
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from logilab.mtconverter import html_escape
+
+from cubicweb.selectors import one_line_rset, implements, match_user_groups
+from cubicweb.view import EntityView
+from cubicweb.web import uicfg, action
+from cubicweb.web.views import primary
+
+
+uicfg.rcategories.tag_relation('secondary', ('CWUser', 'firstname', '*'), 'subject')
+uicfg.rcategories.tag_relation('secondary', ('CWUser', 'surname', '*'), 'subject')
+uicfg.rcategories.tag_relation('metadata', ('CWUser', 'last_login_time', '*'), 'subject')
+uicfg.rcategories.tag_relation('primary', ('CWUser', 'in_group', '*'), 'subject')
+uicfg.rcategories.tag_relation('generated', ('*', 'owned_by', 'CWUser'), 'object')
+uicfg.rcategories.tag_relation('generated', ('*', 'created_by', 'CWUser'), 'object')
+uicfg.rcategories.tag_relation('metadata', ('*', 'bookmarked_by', 'CWUser'), 'object')
+uicfg.rmode.tag_relation('create', ('*', 'in_group', 'CWGroup'), 'object')
+uicfg.rmode.tag_relation('link', ('*', 'owned_by', 'CWUser'), 'object')
+uicfg.rmode.tag_relation('link', ('*', 'created_by', 'CWUser'), 'object')
+uicfg.rmode.tag_relation('create', ('*', 'bookmarked_by', 'CWUser'), 'object')
+uicfg.rdisplay.tag_attribute({}, 'CWUser', 'firstname')
+uicfg.rdisplay.tag_attribute({}, 'CWUser', 'surname')
+
+
+class UserPreferencesEntityAction(action.Action):
+    id = 'prefs'
+    __select__ = (one_line_rset() & implements('CWUser') &
+                  match_user_groups('owners', 'managers'))
+
+    title = _('preferences')
+    category = 'mainactions'
+
+    def url(self):
+        login = self.rset.get_entity(self.row or 0, self.col or 0).login
+        return self.build_url('cwuser/%s'%login, vid='epropertiesform')
+
+
+class CWUserPrimaryView(primary.PrimaryView):
+    __select__ = implements('CWUser')
+
+    def content_title(self, entity):
+        return entity.name()
+
+
+class FoafView(EntityView):
+    id = 'foaf'
+    __select__ = implements('CWUser')
+
+    title = _('foaf')
+    templatable = False
+    content_type = 'text/xml'
+
+    def call(self):
+        self.w(u'''<?xml version="1.0" encoding="%s"?>
+<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+         xmlns:rdfs="http://www.w3org/2000/01/rdf-schema#"
+         xmlns:foaf="http://xmlns.com/foaf/0.1/"> '''% self.req.encoding)
+        for i in xrange(self.rset.rowcount):
+            self.cell_call(i, 0)
+        self.w(u'</rdf:RDF>\n')
+
+    def cell_call(self, row, col):
+        entity = self.complete_entity(row, col)
+        self.w(u'''<foaf:PersonalProfileDocument rdf:about="">
+                      <foaf:maker rdf:resource="%s"/>
+                      <foaf:primaryTopic rdf:resource="%s"/>
+                   </foaf:PersonalProfileDocument>''' % (entity.absolute_url(), entity.absolute_url()))
+        self.w(u'<foaf:Person rdf:ID="%s">\n' % entity.eid)
+        self.w(u'<foaf:name>%s</foaf:name>\n' % html_escape(entity.dc_long_title()))
+        if entity.surname:
+            self.w(u'<foaf:family_name>%s</foaf:family_name>\n'
+                   % html_escape(entity.surname))
+        if entity.firstname:
+            self.w(u'<foaf:givenname>%s</foaf:givenname>\n'
+                   % html_escape(entity.firstname))
+        emailaddr = entity.get_email()
+        if emailaddr:
+            self.w(u'<foaf:mbox>%s</foaf:mbox>\n' % html_escape(emailaddr))
+        self.w(u'</foaf:Person>\n')
+
+from logilab.common.deprecation import class_renamed
+EUserPrimaryView = class_renamed('EUserPrimaryView', CWUserPrimaryView)
--- a/web/views/debug.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/debug.py	Mon May 04 13:09:48 2009 +0200
@@ -2,7 +2,7 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -11,7 +11,8 @@
 
 from logilab.mtconverter import html_escape
 
-from cubicweb.common.view import StartupView
+from cubicweb.selectors import none_rset, match_user_groups
+from cubicweb.view import StartupView
 
 def dict_to_html(w, dict):
     # XHTML doesn't allow emtpy <ul> nodes
@@ -21,11 +22,12 @@
             w(u'<li><span class="label">%s</span>: <span>%s</span></li>' % (
                 html_escape(str(key)), html_escape(repr(dict[key]))))
         w(u'</ul>')
+
     
 class DebugView(StartupView):
     id = 'debug'
+    __select__ = none_rset() & match_user_groups('managers')
     title = _('server debug information')
-    require_groups = ('managers',)
 
     def call(self, **kwargs):
         """display server information"""
--- a/web/views/dynimages.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,198 +0,0 @@
-"""dynamically generated image views
-
-:organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-import os
-from tempfile import mktemp
-from itertools import cycle
-
-from logilab.common.graph import escape, GraphGenerator, DotBackend
-from yams import schema2dot as s2d
-
-from cubicweb.common.view import EntityView, StartupView
-
-
-class RestrictedSchemaDotPropsHandler(s2d.SchemaDotPropsHandler):
-    def __init__(self, req):
-        # FIXME: colors are arbitrary
-        self.nextcolor = cycle( ('#aa0000', '#00aa00', '#0000aa',
-                                 '#000000', '#888888') ).next
-        self.req = req
-        
-    def display_attr(self, rschema):
-        return not rschema.meta and (rschema.has_local_role('read')
-                                     or rschema.has_perm(self.req, 'read'))
-    
-    # XXX remove this method once yams > 0.20 is out
-    def node_properties(self, eschema):
-        """return default DOT drawing options for an entity schema"""
-        label = ['{',eschema.type,'|']
-        label.append(r'\l'.join(rel.type for rel in eschema.subject_relations()
-                                if rel.final and self.display_attr(rel)))
-        label.append(r'\l}') # trailing \l ensure alignement of the last one
-        return {'label' : ''.join(label), 'shape' : "record",
-                'fontname' : "Courier", 'style' : "filled"}
-
-    def edge_properties(self, rschema, subjnode, objnode):
-        kwargs = super(RestrictedSchemaDotPropsHandler, self).edge_properties(rschema, subjnode, objnode)
-        # symetric rels are handled differently, let yams decide what's best
-        if not rschema.symetric:
-            kwargs['color'] = self.nextcolor()
-        kwargs['fontcolor'] = kwargs['color']
-        # dot label decoration is just awful (1 line underlining the label
-        # + 1 line going to the closest edge spline point)
-        kwargs['decorate'] = 'false'
-        return kwargs
-    
-
-class RestrictedSchemaVisitorMiIn:
-    def __init__(self, req, *args, **kwargs):
-        # hack hack hack
-        assert len(self.__class__.__bases__) == 2
-        self.__parent = self.__class__.__bases__[1]
-        self.__parent.__init__(self, *args, **kwargs)
-        self.req = req
-        
-    def nodes(self):
-        for etype, eschema in self.__parent.nodes(self):
-            if eschema.has_local_role('read') or eschema.has_perm(self.req, 'read'):
-                yield eschema.type, eschema
-            
-    def edges(self):
-        for setype, oetype, rschema in self.__parent.edges(self):
-            if rschema.has_local_role('read') or rschema.has_perm(self.req, 'read'):
-                yield setype, oetype, rschema
-
-class FullSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.FullSchemaVisitor):
-    pass
-
-class OneHopESchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopESchemaVisitor):
-    pass
-
-class OneHopRSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopRSchemaVisitor):
-    pass
-                
-        
-class TmpFileViewMixin(object):
-    binary = True
-    content_type = 'application/octet-stream'
-    cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
-    
-    def call(self):
-        self.cell_call()
-        
-    def cell_call(self, row=0, col=0):
-        self.row, self.col = row, col # in case one need it
-        tmpfile = mktemp('.png')
-        try:
-            self._generate(tmpfile)
-            self.w(open(tmpfile).read())
-        finally:
-            os.unlink(tmpfile)
-    
-class SchemaImageView(TmpFileViewMixin, StartupView):
-    id = 'schemagraph'
-    content_type = 'image/png'
-    skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
-    def _generate(self, tmpfile):
-        """display global schema information"""
-        skipmeta = not int(self.req.form.get('withmeta', 0))
-        visitor = FullSchemaVisitor(self.req, self.schema, skiprels=self.skip_rels, skipmeta=skipmeta)
-        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
-                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
-
-class EETypeSchemaImageView(TmpFileViewMixin, EntityView):
-    id = 'eschemagraph'
-    content_type = 'image/png'
-    accepts = ('EEType',)
-    skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
-    
-    def _generate(self, tmpfile):
-        """display schema information for an entity"""
-        entity = self.entity(self.row, self.col)
-        eschema = self.vreg.schema.eschema(entity.name)
-        visitor = OneHopESchemaVisitor(self.req, eschema, skiprels=self.skip_rels)
-        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
-                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
-
-class ERTypeSchemaImageView(EETypeSchemaImageView):
-    accepts = ('ERType',)
-    
-    def _generate(self, tmpfile):
-        """display schema information for an entity"""
-        entity = self.entity(self.row, self.col)
-        rschema = self.vreg.schema.rschema(entity.name)
-        visitor = OneHopRSchemaVisitor(self.req, rschema)
-        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
-                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
-
-
-
-class WorkflowDotPropsHandler(object):
-    def __init__(self, req):
-        self._ = req._
-        
-    def node_properties(self, stateortransition):
-        """return default DOT drawing options for a state or transition"""
-        props = {'label': stateortransition.name, 
-                 'fontname': 'Courier'}
-        if hasattr(stateortransition, 'state_of'):
-            props['shape'] = 'box'
-            props['style'] = 'filled'
-            if stateortransition.reverse_initial_state:
-                props['color'] = '#88CC88'
-        else:
-            props['shape'] = 'ellipse'
-            descr = []
-            tr = stateortransition
-            if tr.require_group:
-                descr.append('%s %s'% (
-                    self._('groups:'),
-                    ','.join(g.name for g in tr.require_group)))
-            if tr.condition:
-                descr.append('%s %s'% (self._('condition:'), tr.condition))
-            if descr:
-                props['label'] += escape('\n'.join(descr))
-        return props
-    
-    def edge_properties(self, transition, fromstate, tostate):
-        return {'label': '', 'dir': 'forward',
-                'color': 'black', 'style': 'filled'}
-
-class WorkflowVisitor:
-    def __init__(self, entity):
-        self.entity = entity
-
-    def nodes(self):
-        for state in self.entity.reverse_state_of:
-            state.complete()
-            yield state.eid, state
-            
-        for transition in self.entity.reverse_transition_of:
-            transition.complete()
-            yield transition.eid, transition
-            
-    def edges(self):
-        for transition in self.entity.reverse_transition_of:
-            for incomingstate in transition.reverse_allowed_transition:
-                yield incomingstate.eid, transition.eid, transition
-            yield transition.eid, transition.destination().eid, transition
-
-
-class EETypeWorkflowImageView(TmpFileViewMixin, EntityView):
-    id = 'ewfgraph'
-    content_type = 'image/png'
-    accepts = ('EEType',)
-    
-    def _generate(self, tmpfile):
-        """display schema information for an entity"""
-        entity = self.entity(self.row, self.col)
-        visitor = WorkflowVisitor(entity)
-        prophdlr = WorkflowDotPropsHandler(self.req)
-        generator = GraphGenerator(DotBackend('workflow', 'LR',
-                                              ratio='compress', size='30,12'))
-        return generator.generate(visitor, prophdlr, tmpfile)
--- a/web/views/edit_multiple.pt	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,38 +0,0 @@
-<!-- rows are precomputed first to consume error messages if necessary -->
-<form method="post" id="entityForm" onsubmit="return validateForm('entityForm', null);"
-      action="%(action)s"
-      tal:define="rows python:[self.edit_form(e) for e in rset.entities()]"
-      >
-  <div tal:replace="structure self/error_message"/>
-  <div id="progress" tal:content="progress">validating changes...</div>
-  <fieldset>
-  <input type="hidden" name="__errorurl" value="#"
-         tal:attributes="value req/url;" />
-  <input type="hidden" name="__form_id" value="#"
-	 tal:attributes="value python:self.id"/>
-  <input type="hidden" name="__redirectvid" value="primary"
-	 tal:attributes="value python:req.form.get('__redirectvid', 'list');"/>
-  <input type="hidden" name="__redirectrql" value="#"
-	 tal:attributes="value python:req.form.get('__redirectrql', rset.printable_rql());"/>
-  <table class="listing">
-    <tr class="header">
-      <th align="left"><input type="checkbox" onclick="setCheckboxesState('eid', this.checked)" value="" title="toggle check boxes" /></th>
-      <tal:th tal:iter="rdef python:sampleentity.relations_by_category('primary', 'add')">
-	<th tal:condition="python: rdef[0].type != 'eid'"
-            tal:content="python: rdef[0].display_name(req, rdef[-1])"/>
-      </tal:th>
-    </tr>
-    <tr tal:iter="row rows" tal:attributes="class python: repeat['row'].getOdd() and 'even' or 'odd'" tal:content="structure row"/>
-  </table>
-  <table width="100%%">
-    <tr>
-      <td align="left">
-	<input class="validateButton" type="submit"  value="#"
-	       tal:attributes="value okbuttonmsg; title okbuttontitle;"/>
-	<input class="validateButton" type="reset" name="__action_cancel" value="#"
-	       tal:attributes="value  cancelbuttonmsg; title cancelbuttontitle;"/>
-      </td>
-    </tr>
-  </table>
-  </fieldset>
-</form>
--- a/web/views/edit_relations.pt	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,51 +0,0 @@
-<fieldset class="subentity">
-<legend class="iformTitle" tal:content="python: label">relations</legend>
-<table id="relatedEntities"
-       tal:define="pendings python: list(self.restore_pending_inserts(entity))">
-  <span tal:iter="row python: self.relations_table(entity)" tal:omit-tag="python: True">
-    <tr tal:condition="python: row[2]">
-      <th class="labelCol" tal:content="python: display_name(req, row[0].type, row[1])">relation name</th>
-      <td>
-	<ul>
-	  <li tal:iter="viewparams python: row[2]" class="invisible">
-	    <span tal:replace="structure python:viewparams[1]">[del it if you can]</span>
-	    <div tal:attributes="id python: 'span'+viewparams[0]; class python: viewparams[2]"
-                 tal:content="structure python: viewparams[3]">related entity view</div>
-	  </li>
-	  <li class="invisible"
-	      tal:condition="python: not self.force_display and self.maxrelitems &lt; len(row[2])"
-	      tal:content="structure python:self.force_display_link()"/>
-	</ul>
-      </td>
-    </tr>
-  </span>
-  <tr tal:iter="row pendings"
-      tal:attributes="id python: 'tr' + row[1]">
-    <!-- row: (relname, nodeid, js, url, eview) -->
-    <th tal:content="python: row[3]">relation name</th>
-    <td>
-      <a class="handle" title="cancel this insert"
-	 tal:attributes="href python: row[2]">[x]</a>
-      <a class="editionPending"
-	 tal:attributes="href python: row[4]; id python: 'a' + row[1]"
-	 tal:content="python: row[5]">entity\'s text_view</a>
-    </td>
-  </tr>
-  <tr tal:condition="not:pendings"><th>&nbsp;</th><td>&nbsp;</td></tr>
-  <tr class="separator" tal:attributes="id string: relationSelectorRow_$eid;">
-    <th class="labelCol">
-      <span i18n:content="add relation"></span>
-      <select tal:attributes="id string: relationSelector_${eid};
-                              tabindex req/next_tabindex;
-			      onchange string: javascript:showMatchingSelect(this.options[this.selectedIndex].value,${eid});">
-	<option value="" i18n:content="select a relation">select a relation</option>
-	<option tal:iter="rel python: entity.srelations_by_category(('generic', 'metadata'), 'add')" 
-                tal:attributes="value python: '%s_%s' % (rel[1], rel[2])"
-		tal:content="python: rel[0]">rel</option>
-      </select>
-    </th>
-    <td tal:attributes="id string: unrelatedDivs_$eid">
-    </td>
-  </tr>
-</table>
-</fieldset>
--- a/web/views/editcontroller.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/editcontroller.py	Mon May 04 13:09:48 2009 +0200
@@ -26,17 +26,14 @@
     def publish(self, rset=None, fromjson=False):
         """edit / create / copy / delete entity / relations"""
         self.fromjson = fromjson
-        req = self.req
-        form = req.form
-        for key in form:
+        for key in self.req.form:
             # There should be 0 or 1 action
             if key.startswith('__action_'):
                 cbname = key[1:]
                 try:
                     callback = getattr(self, cbname)
                 except AttributeError:
-                    raise ValidationError(None,
-                                          {None: req._('invalid action %r' % key)})
+                    raise RequestError(self.req._('invalid action %r' % key))
                 else:
                     return callback()
         self._default_publish()
@@ -199,27 +196,33 @@
             value = Decimal(value)
         elif attrtype == 'Bytes':
             # if it is a file, transport it using a Binary (StringIO)
-            if formparams.has_key('__%s_detach' % attr):
+            # 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):
                 # drop current file value
                 value = None
-            # no need to check value when nor explicit detach nor new file submitted,
-            # since it will think the attribut is not modified
+            # no need to check value when nor explicit detach nor new file
+            # submitted, since it will think the attribute is not modified
             elif isinstance(value, unicode):
                 # file modified using a text widget
-                value = Binary(value.encode(entity.text_encoding(attr)))
+                encoding = entity.attr_metadata(attr, 'encoding')
+                value = Binary(value.encode(encoding))
             else:
-                # (filename, mimetype, stream)
+                # value is a  3-uple (filename, mimetype, stream)
                 val = Binary(value[2].read())
                 if not val.getvalue(): # usually an unexistant file
                     value = None
                 else:
+                    val.filename = value[0]
+                    # ignore browser submitted MIME type since it may be buggy
+                    # XXX add a config option to tell if we should consider it
+                    # or not?
+                    #if entity.e_schema.has_metadata(attr, 'format'):
+                    #    key = '%s_format' % attr
+                    #    formparams[key] = value[1]
+                    #    self.relations.append('X %s_format %%(%s)s'
+                    #                          % (attr, key))
                     # XXX suppose a File compatible schema
-                    val.filename = value[0]
-                    if entity.has_format(attr):
-                        key = '%s_format' % attr
-                        formparams[key] = value[1]
-                        self.relations.append('X %s_format %%(%s)s'
-                                              % (attr, key))
                     if entity.e_schema.has_subject_relation('name') \
                            and not formparams.get('name'):
                         formparams['name'] = value[0]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/editforms.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,361 @@
+"""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), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from copy import copy
+
+from simplejson import dumps
+
+from logilab.mtconverter import html_escape
+
+from cubicweb.selectors import (match_kwargs, one_line_rset, non_final_entity,
+                                specified_etype_implements, yes)
+from cubicweb.utils import make_uid
+from cubicweb.view import EntityView
+from cubicweb.common import tags
+from cubicweb.web import INTERNAL_FIELD_VALUE, stdmsgs
+from cubicweb.web.form import CompositeForm, EntityFieldsForm, FormViewMixIn
+from cubicweb.web.formwidgets import Button, SubmitButton, ResetButton
+from cubicweb.web.formrenderers import (FormRenderer, EntityFormRenderer,
+                                        EntityCompositeFormRenderer,
+                                        EntityInlinedFormRenderer)
+
+_ = unicode
+
+def relation_id(eid, rtype, role, reid):
+    """return an identifier for a relation between two entities"""
+    if role == 'subject':
+        return u'%s:%s:%s' % (eid, rtype, reid)
+    return u'%s:%s:%s' % (reid, rtype, eid)
+
+def toggleable_relation_link(eid, nodeid, label='x'):
+    """return javascript snippet to delete/undelete a relation between two
+    entities
+    """
+    js = u"javascript: togglePendingDelete('%s', %s);" % (
+        nodeid, html_escape(dumps(eid)))
+    return u'[<a class="handle" href="%s" id="handle%s">%s</a>]' % (
+        js, nodeid, label)
+
+
+class DeleteConfForm(FormViewMixIn, EntityView):
+    """form used to confirm deletion of some entities"""
+    id = 'deleteconf'
+    title = _('delete')
+    # don't use navigation, all entities asked to be deleted should be displayed
+    # else we will only delete the displayed page
+    need_navigation = False
+
+    def call(self):
+        """ask for confirmation before real deletion"""
+        req, w = self.req, self.w
+        _ = req._
+        w(u'<script type="text/javascript">updateMessage(\'%s\');</script>\n'
+          % _('this action is not reversible!'))
+        # XXX above message should have style of a warning
+        w(u'<h4>%s</h4>\n' % _('Do you want to delete the following element(s) ?'))
+        form = CompositeForm(req, domid='deleteconf', copy_nav_params=True,
+                             action=self.build_url('edit'), onsubmit=None,
+                             form_buttons=[Button(stdmsgs.YES, cwaction='delete'),
+                                           Button(stdmsgs.NO, cwaction='cancel')])
+        done = set()
+        w(u'<ul>\n')
+        for entity in self.rset.entities():
+            if entity.eid in done:
+                continue
+            done.add(entity.eid)
+            subform = EntityFieldsForm(req, entity=entity, set_error_url=False)
+            form.form_add_subform(subform)
+            # don't use outofcontext view or any other that may contain inline edition form
+            w(u'<li>%s</li>' % tags.a(entity.view('textoutofcontext'),
+                                      href=entity.absolute_url()))
+        w(u'</ul>\n')
+        w(form.form_render())
+
+
+class ClickAndEditFormView(FormViewMixIn, EntityView):
+    """form used to permit ajax edition of an attribute of an entity in a view
+
+    (double-click on the field to see an appropriate edition widget)
+    """
+    id = 'reledit'
+    __select__ = non_final_entity() & match_kwargs('rtype')
+
+    # FIXME editableField class could be toggleable from userprefs
+
+    onsubmit = ("return inlineValidateForm('%(divid)s-form', '%(rtype)s', "
+                "'%(eid)s', '%(divid)s', %(reload)s);")
+    ondblclick = "showInlineEditionForm(%(eid)s, '%(rtype)s', '%(divid)s')"
+
+    def cell_call(self, row, col, rtype=None, role='subject', reload=False,
+                  vid='autolimited'):
+        """display field to edit entity's `rtype` relation on double-click"""
+        rschema = self.schema.rschema(rtype)
+        entity = self.entity(row, col)
+        if rschema.is_final():
+            if getattr(entity, rtype) is None:
+                value = self.req._('not specified')
+            else:
+                value = entity.printable_value(rtype)
+        else:
+            rset = entity.related(rtype, role)
+            value = self.view(vid, rset, 'null')
+        if not entity.has_perm('update'):
+            self.w(value)
+            return
+        eid = entity.eid
+        divid = 'd%s' % make_uid('%s-%s' % (rtype, eid))
+        event_data = {'divid' : divid, 'eid' : eid, 'rtype' : rtype,
+                      'reload' : dumps(reload)}
+        buttons = [SubmitButton(stdmsgs.BUTTON_OK),
+                   Button(stdmsgs.BUTTON_CANCEL,
+                          onclick="cancelInlineEdit(%s,\'%s\',\'%s\')" % (
+                              eid, rtype, divid))]
+        form = self.vreg.select_object('forms', 'edition', self.req, self.rset,
+                                       row=row, col=col, form_buttons=buttons,
+                                       domid='%s-form' % divid, action='#',
+                                       cssstyle='display: none',
+                                       onsubmit=self.onsubmit % event_data)
+        form.form_add_hidden(u'__maineid', entity.eid)
+        renderer = FormRenderer(display_label=False, display_help=False,
+                                display_fields=[(rtype, role)],
+                                button_bar_class='buttonbar',
+                                display_progress_div=False)
+        self.w(tags.div(value, klass='editableField', id=divid,
+                        ondblclick=self.ondblclick % event_data))
+        self.w(form.form_render(renderer=renderer))
+
+
+class EditionFormView(FormViewMixIn, EntityView):
+    """display primary entity edition form"""
+    id = 'edition'
+    # add yes() so it takes precedence over deprecated views in baseforms,
+    # though not baseforms based customized view
+    __select__ = one_line_rset() & non_final_entity() & yes()
+
+    title = _('edition')
+    renderer = EntityFormRenderer()
+
+    def cell_call(self, row, col, **kwargs):
+        entity = self.complete_entity(row, col)
+        self.render_form(entity)
+
+    def render_form(self, entity):
+        """fetch and render the form"""
+        self.form_title(entity)
+        form = self.vreg.select_object('forms', 'edition', self.req, entity.rset,
+                                       row=entity.row, col=entity.col, entity=entity,
+                                       submitmsg=self.submited_message())
+        self.init_form(form, entity)
+        self.w(form.form_render(renderer=self.renderer, formvid=u'edition'))
+
+    def init_form(self, form, entity):
+        """customize your form before rendering here"""
+        form.form_add_hidden(u'__maineid', entity.eid)
+
+    def form_title(self, entity):
+        """the form view title"""
+        ptitle = self.req._(self.title)
+        self.w(u'<div class="formTitle"><span>%s %s</span></div>' % (
+            entity.dc_type(), ptitle and '(%s)' % ptitle))
+
+    def submited_message(self):
+        """return the message that will be displayed on successful edition"""
+        return self.req._('entity edited')
+
+
+class CreationFormView(EditionFormView):
+    """display primary entity creation form"""
+    id = 'creation'
+    __select__ = specified_etype_implements('Any') & yes()
+
+    title = _('creation')
+
+    def call(self, **kwargs):
+        """creation view for an entity"""
+        etype = kwargs.pop('etype', self.req.form.get('etype'))
+        try:
+            entity = self.vreg.etype_class(etype)(self.req)
+        except:
+            self.w(self.req._('no such entity type %s') % etype)
+        else:
+            self.initialize_varmaker()
+            entity.eid = self.varmaker.next()
+            self.render_form(entity)
+
+    def form_title(self, entity):
+        """the form view title"""
+        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)}
+            self.w(u'<div class="formTitle notransform"><span>%s</span></div>' % msg)
+        else:
+            super(CreationFormView, self).form_title(entity)
+
+    def url(self):
+        """return the url associated with this view"""
+        return self.create_url(self.req.form.get('etype'))
+
+    def submited_message(self):
+        """return the message that will be displayed on successful edition"""
+        return self.req._('entity created')
+
+
+class CopyFormView(EditionFormView):
+    """display primary entity creation form initialized with values from another
+    entity
+    """
+    id = 'copy'
+    def render_form(self, entity):
+        """fetch and render the form"""
+        # 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.w(u'<script type="text/javascript">updateMessage("%s");</script>\n'
+               % self.req._('Please note that this is only a shallow copy'))
+        super(CopyFormView, self).render_form(entity)
+        del self.newentity
+
+    def init_form(self, form, entity):
+        """customize your form before rendering here"""
+        super(CopyFormView, self).init_form(form, entity)
+        if entity.eid == self.newentity.eid:
+            form.form_add_hidden('__cloned_eid', self.copying, eidparam=True)
+
+    def submited_message(self):
+        """return the message that will be displayed on successful edition"""
+        return self.req._('entity copied')
+
+
+class TableEditForm(CompositeForm):
+    id = 'muledit'
+    onsubmit = "return validateForm('entityForm', null);"
+    form_buttons = [SubmitButton(_('validate modifications on selected items')),
+                    ResetButton(_('revert changes'))]
+
+    def __init__(self, *args, **kwargs):
+        super(TableEditForm, self).__init__(*args, **kwargs)
+        for row in xrange(len(self.rset)):
+            form = self.vreg.select_object('forms', 'edition', self.req, self.rset,
+                                           row=row, attrcategories=('primary',),
+                                           set_error_url=False)
+            # XXX rely on the EntityCompositeFormRenderer to put the eid input
+            form.remove_field(form.field_by_name('eid'))
+            self.form_add_subform(form)
+
+
+class TableEditFormView(FormViewMixIn, EntityView):
+    id = 'muledit'
+    __select__ = EntityView.__select__ & yes()
+    title = _('multiple edit')
+
+    def call(self, **kwargs):
+        """a view to edit multiple entities of the same type the first column
+        should be the eid
+        """
+        #self.form_title(entity)
+        form = self.vreg.select_object('forms', self.id, self.req, self.rset)
+        self.w(form.form_render(renderer=EntityCompositeFormRenderer()))
+
+
+class InlineEntityEditionFormView(FormViewMixIn, EntityView):
+    id = 'inline-edition'
+    __select__ = non_final_entity() & match_kwargs('peid', 'rtype')
+    removejs = "removeInlinedEntity('%s', '%s', '%s')"
+
+    def call(self, **kwargs):
+        """redefine default call() method to avoid automatic
+        insertions of <div class="section"> between each row of
+        the resultset
+        """
+        rset = self.rset
+        for i in xrange(len(rset)):
+            self.wview(self.id, rset, row=i, **kwargs)
+
+    def cell_call(self, row, col, peid, rtype, role='subject', **kwargs):
+        """
+        :param peid: the parent entity's eid hosting the inline form
+        :param rtype: the relation bridging `etype` and `peid`
+        :param role: the role played by the `peid` in the relation
+        """
+        entity = self.entity(row, col)
+        divonclick = "restoreInlinedEntity('%s', '%s', '%s')" % (peid, rtype,
+                                                                 entity.eid)
+        self.render_form(entity, peid, rtype, role, divonclick=divonclick)
+
+    def render_form(self, entity, peid, rtype, role, **kwargs):
+        """fetch and render the form"""
+        form = self.vreg.select_object('forms', 'edition', self.req, None,
+                                       entity=entity, set_error_url=False)
+        self.add_hiddens(form, entity, peid, rtype, role)
+        divid = '%s-%s-%s' % (peid, rtype, entity.eid)
+        title = self.schema.rschema(rtype).display_name(self.req, role)
+        removejs = self.removejs % (peid, rtype,entity.eid)
+        self.w(form.form_render(renderer=EntityInlinedFormRenderer(), divid=divid,
+                                title=title, removejs=removejs,**kwargs))
+
+    def add_hiddens(self, form, entity, peid, rtype, role):
+        # to ease overriding (see cubes.vcsfile.views.forms for instance)
+        if self.keep_entity(entity, peid, rtype):
+            if entity.has_eid():
+                rval = entity.eid
+            else:
+                rval = INTERNAL_FIELD_VALUE
+            form.form_add_hidden('edit%s-%s:%s' % (role[0], rtype, peid), rval)
+        form.form_add_hidden(name='%s:%s' % (rtype, peid), value=entity.eid,
+                             id='rel-%s-%s-%s'  % (peid, rtype, entity.eid))
+
+    def keep_entity(self, entity, peid, rtype):
+        if not entity.has_eid():
+            return True
+        # are we regenerating form because of a validation error ?
+        erroneous_post = self.req.data.get('formvalues')
+        if erroneous_post:
+            cdvalues = self.req.list_form_param('%s:%s' % (rtype, peid),
+                                                erroneous_post)
+            if unicode(entity.eid) not in cdvalues:
+                return False
+        return True
+
+
+class InlineEntityCreationFormView(InlineEntityEditionFormView):
+    id = 'inline-creation'
+    __select__ = (match_kwargs('peid', 'rtype')
+                  & specified_etype_implements('Any'))
+    removejs = "removeInlineForm('%s', '%s', '%s')"
+
+    def call(self, etype, peid, rtype, role='subject', **kwargs):
+        """
+        :param etype: the entity type being created in the inline form
+        :param peid: the parent entity's eid hosting the inline form
+        :param rtype: the relation bridging `etype` and `peid`
+        :param role: the role played by the `peid` in the relation
+        """
+        try:
+            entity = self.vreg.etype_class(etype)(self.req, None, None)
+        except:
+            self.w(self.req._('no such entity type %s') % etype)
+            return
+        self.initialize_varmaker()
+        entity.eid = self.varmaker.next()
+        self.render_form(entity, peid, rtype, role)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/editviews.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,206 @@
+"""Some views used to help to the edition process
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from simplejson import dumps
+
+from logilab.common.decorators import cached
+from logilab.mtconverter import html_escape
+
+from cubicweb import typed_eid
+from cubicweb.view import EntityView
+from cubicweb.selectors import (one_line_rset, non_final_entity,
+                                match_search_state, match_form_params)
+from cubicweb.common.uilib import cut
+from cubicweb.web.views import linksearch_select_url
+from cubicweb.web.views.editforms import relation_id
+from cubicweb.web.views.baseviews import FinalView
+
+_ = unicode
+
+class SearchForAssociationView(EntityView):
+    """view called by the edition view when the user asks to search for
+    something to link to the edited eid
+    """
+    id = 'search-associate'
+    __select__ = (one_line_rset() & match_search_state('linksearch')
+                  & non_final_entity())
+
+    title = _('search for association')
+
+    def cell_call(self, row, col):
+        rset, vid, divid, paginate = self.filter_box_context_info()
+        self.rset = rset
+        self.w(u'<div id="%s">' % divid)
+        self.paginate()
+        self.wview(vid, rset, 'noresult')
+        self.w(u'</div>')
+
+    @cached
+    def filter_box_context_info(self):
+        entity = self.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
+        # them. Use fetch_order and not fetch_unrelated_order as sort method
+        # since the latter is mainly there to select relevant items in the combo
+        # box, it doesn't give interesting result in this context
+        rql = entity.unrelated_rql(rtype, etype, role,
+                                   ordermethod='fetch_order',
+                                   vocabconstraints=False)
+        rset = self.req.execute(rql, {'x' : entity.eid}, 'x')
+        return rset, 'list', "search-associate-content", True
+
+
+class OutOfContextSearch(EntityView):
+    id = 'outofcontext-search'
+    def cell_call(self, row, col):
+        entity = self.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>' % (
+                html_escape(linksearch_select_url(self.req, erset)),
+                self.req._('select this entity'),
+                html_escape(entity.view('textoutofcontext')),
+                html_escape(entity.absolute_url(vid='primary')),
+                self.req._('view detail for this entity')))
+        else:
+            entity.view('outofcontext', w=self.w)
+
+
+class UnrelatedDivs(EntityView):
+    id = 'unrelateddivs'
+    __select__ = match_form_params('relation')
+
+    def cell_call(self, row, col):
+        entity = self.entity(row, col)
+        relname, target = self.req.form.get('relation').rsplit('_', 1)
+        rschema = self.schema.rschema(relname)
+        hidden = 'hidden' in self.req.form
+        is_cell = 'is_cell' in self.req.form
+        self.w(self.build_unrelated_select_div(entity, rschema, target,
+                                               is_cell=is_cell, hidden=hidden))
+
+    def build_unrelated_select_div(self, entity, rschema, target,
+                                   is_cell=False, hidden=True):
+        options = []
+        divid = 'div%s_%s_%s' % (rschema.type, target, entity.eid)
+        selectid = 'select%s_%s_%s' % (rschema.type, target, entity.eid)
+        if rschema.symetric or target == 'subject':
+            targettypes = rschema.objects(entity.e_schema)
+            etypes = '/'.join(sorted(etype.display_name(self.req) for etype in targettypes))
+        else:
+            targettypes = rschema.subjects(entity.e_schema)
+            etypes = '/'.join(sorted(etype.display_name(self.req) for etype in targettypes))
+        etypes = cut(etypes, self.req.property_value('navigation.short-line-size'))
+        options.append('<option>%s %s</option>' % (self.req._('select a'), etypes))
+        options += self._get_select_options(entity, rschema, target)
+        options += self._get_search_options(entity, rschema, target, targettypes)
+        if 'Basket' in self.schema: # XXX
+            options += self._get_basket_options(entity, rschema, target, targettypes)
+        relname, target = self.req.form.get('relation').rsplit('_', 1)
+        return u"""\
+<div class="%s" id="%s">
+  <select id="%s" onchange="javascript: addPendingInsert(this.options[this.selectedIndex], %s, %s, '%s');">
+    %s
+  </select>
+</div>
+""" % (hidden and 'hidden' or '', divid, selectid,
+       html_escape(dumps(entity.eid)), is_cell and 'true' or 'null', relname,
+       '\n'.join(options))
+
+    def _get_select_options(self, entity, rschema, target):
+        """add options to search among all entities of each possible type"""
+        options = []
+        eid = entity.eid
+        pending_inserts = self.req.get_pending_inserts(eid)
+        rtype = rschema.type
+        form = self.vreg.select_object('forms', 'edition', self.req,
+                                       self.rset, entity=entity)
+        field = form.field_by_name(rschema, target, entity.e_schema)
+        limit = self.req.property_value('navigation.combobox-limit')
+        for eview, reid in form.form_field_vocabulary(field, limit):
+            if reid is None:
+                options.append('<option class="separator">-- %s --</option>'
+                               % html_escape(eview))
+            else:
+                optionid = relation_id(eid, rtype, target, reid)
+                if optionid not in pending_inserts:
+                    # prefix option's id with letters to make valid XHTML wise
+                    options.append('<option id="id%s" value="%s">%s</option>' %
+                                   (optionid, reid, html_escape(eview)))
+        return options
+
+    def _get_search_options(self, entity, rschema, target, targettypes):
+        """add options to search among all entities of each possible type"""
+        options = []
+        _ = self.req._
+        for eschema in targettypes:
+            mode = '%s:%s:%s:%s' % (target, entity.eid, rschema.type, eschema)
+            url = self.build_url(entity.rest_path(), vid='search-associate',
+                                 __mode=mode)
+            options.append((eschema.display_name(self.req),
+                            '<option value="%s">%s %s</option>' % (
+                html_escape(url), _('Search for'), eschema.display_name(self.req))))
+        return [o for l, o in sorted(options)]
+
+    def _get_basket_options(self, entity, rschema, target, targettypes):
+        options = []
+        rtype = rschema.type
+        _ = self.req._
+        for basketeid, basketname in self._get_basket_links(self.req.user.eid,
+                                                            target, targettypes):
+            optionid = relation_id(entity.eid, rtype, target, basketeid)
+            options.append('<option id="%s" value="%s">%s %s</option>' % (
+                optionid, basketeid, _('link to each item in'), html_escape(basketname)))
+        return options
+
+    def _get_basket_links(self, ueid, target, targettypes):
+        targettypes = set(targettypes)
+        for basketeid, basketname, elements in self._get_basket_info(ueid):
+            baskettypes = elements.column_types(0)
+            # if every elements in the basket can be attached to the
+            # edited entity
+            if baskettypes & targettypes:
+                yield basketeid, basketname
+
+    def _get_basket_info(self, ueid):
+        basketref = []
+        basketrql = 'Any B,N WHERE B is Basket, B owned_by U, U eid %(x)s, B name N'
+        basketresultset = self.req.execute(basketrql, {'x': ueid}, 'x')
+        for result in basketresultset:
+            basketitemsrql = 'Any X WHERE X in_basket B, B eid %(x)s'
+            rset = self.req.execute(basketitemsrql, {'x': result[0]}, 'x')
+            basketref.append((result[0], result[1], rset))
+        return basketref
+
+
+class ComboboxView(EntityView):
+    """the view used in combobox (unrelated entities)
+
+    THIS IS A TEXT VIEW. DO NOT HTML_ESCAPE
+    """
+    id = 'combobox'
+    title = None
+
+    def cell_call(self, row, col):
+        """the combo-box view for an entity: same as text out of context view
+        by default
+        """
+        self.wview('textoutofcontext', self.rset, row=row, col=col)
+
+
+class EditableFinalView(FinalView):
+    """same as FinalView but enables inplace-edition when possible"""
+    id = 'editable-final'
+
+    def cell_call(self, row, col, props=None, displaytime=False):
+        entity, rtype = self.rset.related_entity(row, col)
+        if entity is not None:
+            self.w(entity.view('reledit', rtype=rtype))
+        else:
+            super(EditableFinalView, self).cell_call(row, col, props, displaytime)
--- a/web/views/emailaddress.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/emailaddress.py	Mon May 04 13:09:48 2009 +0200
@@ -1,24 +1,25 @@
 """Specific views for email addresses entities
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 from logilab.mtconverter import html_escape
 
+from cubicweb.selectors import implements
 from cubicweb.common import Unauthorized
-from cubicweb.web.views import baseviews
+from cubicweb.web.views import baseviews, primary
 
-class EmailAddressPrimaryView(baseviews.PrimaryView):
-    accepts = ('EmailAddress',)
-    
+class EmailAddressPrimaryView(primary.PrimaryView):
+    __select__ = implements('EmailAddress')
+
     def cell_call(self, row, col, skipeids=None):
         self.skipeids = skipeids
         super(EmailAddressPrimaryView, self).cell_call(row, col)
-        
-    def render_entity_attributes(self, entity, siderelations):
+
+    def render_entity_attributes(self, entity):
         self.w(u'<h3>')
         entity.view('oneline', w=self.w)
         if not entity.canonical:
@@ -51,7 +52,7 @@
             emailofstr = ', '.join(e.view('oneline') for e in emailof)
             self.field(display_name(self.req, 'use_email', 'object'), emailofstr)
 
-    def render_entity_relations(self, entity, siderelations):
+    def render_entity_relations(self, entity):
         for i, email in enumerate(entity.related_emails(self.skipeids)):
             self.w(u'<div class="%s">' % (i%2 and 'even' or 'odd'))
             email.view('oneline', w=self.w, contexteid=entity.eid)
@@ -59,18 +60,19 @@
 
 
 class EmailAddressShortPrimaryView(EmailAddressPrimaryView):
-    accepts = ('EmailAddress',)
+    __select__ = implements('EmailAddress')
     id = 'shortprimary'
     title = None # hidden view
-    def render_entity_attributes(self, entity, siderelations):
+
+    def render_entity_attributes(self, entity):
         self.w(u'<h5>')
         entity.view('oneline', w=self.w)
         self.w(u'</h5>')
 
-    
+
 class EmailAddressOneLineView(baseviews.OneLineView):
-    accepts = ('EmailAddress',)
-    
+    __select__ = implements('EmailAddress')
+
     def cell_call(self, row, col, **kwargs):
         entity = self.entity(row, col)
         if entity.reverse_primary_email:
@@ -89,8 +91,8 @@
     'mailto:'"""
 
     id = 'mailto'
-    accepts = ('EmailAddress',)
-    
+    __select__ = implements('EmailAddress')
+
     def cell_call(self, row, col, **kwargs):
         entity = self.entity(row, col)
         if entity.reverse_primary_email:
@@ -105,15 +107,15 @@
             mailto = "mailto:%s" % entity.display_address()
         self.w(u'<a href="%s">%s</a>' % (html_escape(mailto),
                                          html_escape(entity.display_address())))
-            
+
         if entity.alias:
             self.w(u'&gt;\n')
         if entity.reverse_primary_email:
             self.w(u'</b>')
 
-    
+
 class EmailAddressTextView(baseviews.TextView):
-    accepts = ('EmailAddress',)
-    
+    __select__ = implements('EmailAddress')
+
     def cell_call(self, row, col, **kwargs):
         self.w(self.entity(row, col).display_address())
--- a/web/views/embedding.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/embedding.py	Mon May 04 13:09:48 2009 +0200
@@ -3,7 +3,7 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -15,11 +15,11 @@
 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
+from cubicweb.view import NOINDEX, NOFOLLOW
 from cubicweb.common.uilib import soup2xhtml
-from cubicweb.common.selectors import (one_line_rset, score_entity_selector,
-                                    match_search_state, implement_interface)
-from cubicweb.common.view import NOINDEX, NOFOLLOW
 from cubicweb.web.controller import Controller
 from cubicweb.web.action import Action
 from cubicweb.web.views import basetemplates
@@ -33,7 +33,6 @@
     def call(self, body):
         # XXX fallback to HTML 4 mode when embeding ?
         self.set_request_content_type()
-        self.process_rql(self.req.form.get('rql'))
         self.req.search_state = ('normal',)
         self.template_header(self.content_type, None, self.req._('external page'),
                              [NOINDEX, NOFOLLOW])
@@ -72,7 +71,19 @@
             except HTTPError, err:
                 body = '<h2>%s</h2><h3>%s</h3>' % (
                     _('error while embedding page'), err)
-        return self.vreg.main_template(req, self.template, body=body)
+        self.process_rql(req.form.get('rql'))
+        return self.vreg.main_template(req, self.template, rset=self.rset, body=body)
+
+
+def entity_has_embedable_url(entity):
+    """return 1 if the entity provides an allowed embedable url"""
+    url = entity.embeded_url()
+    if not url or not url.strip():
+        return 0
+    allowed = entity.config['embed-allowed']
+    if allowed is None or not allowed.match(url):
+        return 0
+    return 1
 
 
 class EmbedAction(Action):
@@ -80,25 +91,12 @@
     if the returned url match embeding configuration
     """
     id = 'embed'
-    controller = 'embed'
-    __selectors__ = (one_line_rset, match_search_state,
-                     implement_interface, score_entity_selector)
-    accepts_interfaces = (IEmbedable,)
+    __select__ = (one_line_rset() & match_search_state('normal')
+                  & implements(IEmbedable) 
+                  & score_entity(entity_has_embedable_url))
     
     title = _('embed')
-        
-    @classmethod
-    def score_entity(cls, entity):
-        """return a score telling how well I can display the given 
-        entity instance (required by the value_selector)
-        """
-        url = entity.embeded_url()
-        if not url or not url.strip():
-            return 0
-        allowed = cls.config['embed-allowed']
-        if allowed is None or not allowed.match(url):
-            return 0
-        return 1
+    controller = 'embed'
     
     def url(self, row=0):
         entity = self.rset.get_entity(row, 0)
@@ -132,6 +130,7 @@
                 url = '%s?custom_css=%s' % (url, self.custom_css)
         return '<a href="%s"' % url
 
+
 class absolutize_links:
     def __init__(self, embedded_url, tag, custom_css=None):
         self.embedded_url = embedded_url
@@ -152,12 +151,13 @@
     for rgx, repl in filters:
         body = rgx.sub(repl, body)
     return body
-    
+
+
 def embed_external_page(url, prefix, headers=None, custom_css=None):
     req = Request(url, headers=(headers or {}))
     content = urlopen(req).read()
     page_source = unicode(content, guess_encoding(content), 'replace')
-    page_source =page_source
+    page_source = page_source
     match = BODY_RGX.search(page_source)
     if match is None:
         return page_source
--- a/web/views/eproperties.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,14 +0,0 @@
-"""Specific views for EProperty
-
-
-:organization: Logilab
-:copyright: 2007-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-from cubicweb.web.views import baseviews
-
-class EPropertyPrimaryView(baseviews.PrimaryView):
-    accepts = ('EProperty',)
-    skip_none = False
--- a/web/views/error.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/error.py	Mon May 04 13:09:48 2009 +0200
@@ -2,12 +2,12 @@
 as startup views and are used for standard error pages (404, 500, etc.)
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb.common.view import StartupView
+from cubicweb.view import StartupView
 
 class FourOhFour(StartupView):
     id = '404'
--- a/web/views/euser.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +0,0 @@
-"""Specific views for users
-
-:organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-from logilab.common.decorators import cached
-from logilab.mtconverter import html_escape
-
-from cubicweb.schema import display_name
-from cubicweb.web import INTERNAL_FIELD_VALUE
-from cubicweb.web.form import EntityForm
-from cubicweb.web.views.baseviews import PrimaryView, EntityView
-
-class EUserPrimaryView(PrimaryView):
-    accepts = ('EUser',)
-    skip_attrs = ('firstname', 'surname')
-    
-    def iter_relations(self, entity):
-        # don't want to display user's entities
-        for rschema, targetschemas, x in super(EUserPrimaryView, self).iter_relations(entity):
-            if x == 'object' and rschema.type in ('owned_by', 'for_user'):
-                continue
-            yield rschema, targetschemas, x
-
-    def content_title(self, entity):
-        return entity.name()
-
-    def is_side_related(self, rschema, eschema):
-        return  rschema.type in ['interested_in', 'tags', 
-                                 'todo_by', 'bookmarked_by',
-                                 ]
-class FoafView(EntityView):
-    id = 'foaf'
-    accepts = ('EUser',)
-    title = _('foaf')
-    templatable = False
-    content_type = 'text/xml'
-
-    def call(self):
-        self.w(u'''<?xml version="1.0" encoding="%s"?>
-<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-         xmlns:rdfs="http://www.w3org/2000/01/rdf-schema#"
-         xmlns:foaf="http://xmlns.com/foaf/0.1/"> '''% self.req.encoding)
-        for i in xrange(self.rset.rowcount):
-            self.cell_call(i, 0)
-        self.w(u'</rdf:RDF>\n')
-
-    def cell_call(self, row, col):
-        entity = self.complete_entity(row, col)
-        self.w(u'''<foaf:PersonalProfileDocument rdf:about="">
-                      <foaf:maker rdf:resource="%s"/>
-                      <foaf:primaryTopic rdf:resource="%s"/>
-                   </foaf:PersonalProfileDocument>''' % (entity.absolute_url(), entity.absolute_url()))
-                      
-        self.w(u'<foaf:Person rdf:ID="%s">\n' % entity.eid)
-        self.w(u'<foaf:name>%s</foaf:name>\n' % html_escape(entity.dc_long_title()))
-        if entity.surname:
-            self.w(u'<foaf:family_name>%s</foaf:family_name>\n'
-                   % html_escape(entity.surname))
-        if entity.firstname:
-            self.w(u'<foaf:givenname>%s</foaf:givenname>\n'
-                   % html_escape(entity.firstname))
-        emailaddr = entity.get_email()
-        if emailaddr:
-            self.w(u'<foaf:mbox>%s</foaf:mbox>\n' % html_escape(emailaddr))
-        self.w(u'</foaf:Person>\n')
-                   
-class EditGroups(EntityForm):
-    """displays a simple euser / egroups editable table"""
-    
-    id = 'editgroups'
-    accepts = ('EUser',)
-    
-    def call(self):
-        self.req.add_css('cubicweb.acl.css')            
-        _ = self.req._
-        self.w(u'<form id="editgroup" method="post" action="edit">')
-        self.w(u'<table id="groupedit">\n')
-        self.w(u'<tr>')
-        self.w(u'<th>%s</th>' % display_name(self.req, 'EUser'))
-        self.w(u''.join(u'<th>%s</th>' % _(gname) for geid, gname in self.egroups))
-        self.w(u'</tr>')
-        for row in xrange(len(self.rset)):
-            self.build_table_line(row)
-        self.w(u'</table>')
-        self.w(u'<fieldset>')
-        self.w(self.button_cancel())
-        self.w(self.button_ok())
-        self.w(u'</fieldset>')
-        self.w(u'</form>')
-
-
-    def build_table_line(self, row):
-        euser = self.entity(row)
-        euser_groups = [group.name for group in euser.in_group]
-        if euser_groups:
-            self.w(u'<tr>')
-        else:
-            self.w(u'<tr class="nogroup">')
-        self.w(u'<th><fieldset>')
-        self.w(u'<input type="hidden" name="eid" value="%s" />' % euser.eid)
-        self.w(u'<input type="hidden" name="__type:%s" value="EUser" />' % euser.eid)
-        # this should not occur (for now) since in_group relation is mandatory
-        if not euser_groups:
-            self.w(u'<input type="hidden" name="edits-in_group:%s" value="%s">' %
-                   (euser.eid, INTERNAL_FIELD_VALUE))
-        self.w(euser.dc_title())
-        self.w(u'</fieldset></th>')
-        for geid, gname in self.egroups:
-            self.w(u'<td><fieldset>')
-            if gname in euser_groups:
-                self.w(u'<input type="hidden" name="edits-in_group:%s" value="%s" />' %
-                       (euser.eid, geid))
-                self.w(u'<input type="checkbox" name="in_group:%s" value="%s" checked="checked" />' %
-                       (euser.eid, geid))
-            else:
-                self.w(u'<input type="checkbox" name="in_group:%s" value="%s" />' %
-                       (euser.eid, geid))
-            self.w(u'</fieldset></td>')
-        self.w(u'</tr>\n')
-
-        
-    @property
-    @cached
-    def egroups(self):
-        groups = self.req.execute('Any G, N ORDERBY N WHERE G is EGroup, G name N')
-        return [(geid, gname) for geid, gname in groups.rows if gname != 'owners']
-                
-        
--- a/web/views/facets.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/facets.py	Mon May 04 13:09:48 2009 +0200
@@ -10,13 +10,14 @@
 
 from logilab.mtconverter import html_escape
 
-from cubicweb.common.selectors import (chainfirst, chainall, non_final_entity,
-                                    two_lines_rset, match_context_prop,
-                                    yes, one_has_relation)
+from cubicweb.vregistry import objectify_selector
+from cubicweb.selectors import (non_final_entity, two_lines_rset,
+                                match_context_prop, yes, relation_possible)
 from cubicweb.web.box import BoxTemplate
-from cubicweb.web.facet import (AbstractFacet, VocabularyFacet, FacetStringWidget,
-                             RelationFacet, prepare_facets_rqlst, filter_hiddens)
+from cubicweb.web.facet import (AbstractFacet, FacetStringWidget, RelationFacet,
+                                prepare_facets_rqlst, filter_hiddens)
 
+@objectify_selector
 def contextview_selector(cls, req, rset, row=None, col=None, view=None,
                          **kwargs):
     if view and getattr(view, 'filter_box_context_info', lambda: None)():
@@ -27,9 +28,9 @@
 class FilterBox(BoxTemplate):
     """filter results of a query"""
     id = 'filter_box'
-    __selectors__ = (chainfirst(contextview_selector,
-                                chainall(non_final_entity, two_lines_rset)),
-                     match_context_prop)
+    __select__ = (((non_final_entity() & two_lines_rset())
+                   | contextview_selector()
+                   ) & match_context_prop())
     context = 'left'
     title = _('boxes_filter_box')
     visible = True # functionality provided by the search box by default
@@ -58,7 +59,7 @@
         req.add_css('cubicweb.facets.css')
         if self.roundcorners:
             req.html_headers.add_onload('jQuery(".facet").corner("tl br 10px");')
-        rset, vid, divid, paginate=self._get_context(view)
+        rset, vid, divid, paginate = self._get_context(view)
         if rset.rowcount < 2: # XXX done by selectors, though maybe necessary when rset has been hijacked
             return
         if vid is None:
@@ -129,7 +130,7 @@
 # inherit from RelationFacet to benefit from its possible_values implementation
 class ETypeFacet(RelationFacet):
     id = 'etype-facet'
-    __selectors__ = (yes,)
+    __select__ = yes()
     order = 1
     rtype = 'is'
     target_attr = 'name'
@@ -153,7 +154,7 @@
 
 
 class HasTextFacet(AbstractFacet):
-    __selectors__ = (one_has_relation, match_context_prop)
+    __select__ = relation_possible('has_text', 'subject') & match_context_prop()
     id = 'has_text-facet'
     rtype = 'has_text'
     role = 'subject'
--- a/web/views/ibreadcrumbs.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/ibreadcrumbs.py	Mon May 04 13:09:48 2009 +0200
@@ -1,20 +1,19 @@
 """navigation components definition for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
 from logilab.mtconverter import html_escape
 
+# don't use AnyEntity since this may cause bug with isinstance() due to reloading
 from cubicweb.interfaces import IBreadCrumbs
-from cubicweb.common.selectors import (match_context_prop, one_line_rset, 
-                                    implement_interface)
-from cubicweb.common.view import EntityView
+from cubicweb.selectors import match_context_prop, one_line_rset, implements
+from cubicweb.entity import Entity
+from cubicweb.view import EntityView
 from cubicweb.common.uilib import cut
-# don't use AnyEntity since this may cause bug with isinstance() due to reloading
-from cubicweb.common.entity import Entity
 from cubicweb.web.component import EntityVComponent
 
 _ = unicode
@@ -29,8 +28,7 @@
     # register msg not generated since no entity implements IPrevNext in cubicweb itself
     title = _('contentnavigation_breadcrumbs')
     help = _('contentnavigation_breadcrumbs_description')
-    __selectors__ = (one_line_rset, match_context_prop, implement_interface)
-    accepts_interfaces = (IBreadCrumbs,)
+    __select__ = (one_line_rset() & match_context_prop() & implements(IBreadCrumbs))
     context = 'navtop'
     order = 5
     visible = False
@@ -73,7 +71,7 @@
 
 class BreadCrumbComponent(BreadCrumbEntityVComponent):
     __registry__ = 'components'
-    __selectors__ = (one_line_rset, implement_interface)
+    __select__ = (one_line_rset() & implements(IBreadCrumbs))
     visible = True
 
 
--- a/web/views/idownloadable.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/idownloadable.py	Mon May 04 13:09:48 2009 +0200
@@ -1,21 +1,28 @@
 """Specific views for entities implementing IDownloadable
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from logilab.mtconverter import BINARY_ENCODINGS, TransformError, html_escape
 
+from cubicweb.view import EntityView
+from cubicweb.selectors import (one_line_rset, score_entity,
+                                implements, match_context_prop)
 from cubicweb.interfaces import IDownloadable
 from cubicweb.common.mttransforms import ENGINE
-from cubicweb.common.selectors import (one_line_rset, score_entity_selector,
-                                       implement_interface, match_context_prop)
 from cubicweb.web.box import EntityBoxTemplate
-from cubicweb.web.views import baseviews
+from cubicweb.web.views import primary, baseviews
+
 
-_ = unicode
+def is_image(entity):
+    mt = entity.download_content_type()
+    if not (mt and mt.startswith('image/')):
+        return 0
+    return 1
 
 def download_box(w, entity, title=None, label=None):
     req = entity.req
@@ -32,34 +39,26 @@
     w(u'</div>')
     w(u'</div>\n</div>\n')
 
-    
+
 class DownloadBox(EntityBoxTemplate):
     id = 'download_box'
+    # no download box for images
     # XXX primary_view selector ?
-    __selectors__ = (one_line_rset, implement_interface, match_context_prop, score_entity_selector)
-    accepts_interfaces = (IDownloadable,)
+    __select__ = (one_line_rset() & implements(IDownloadable) &
+                  match_context_prop() & ~score_entity(is_image))
     order = 10
 
-    @classmethod
-    def score_entity(cls, entity):
-        mt = entity.download_content_type()
-        # no download box for images
-        if mt and mt.startswith('image/'):
-            return 0
-        return 1
-    
     def cell_call(self, row, col, title=None, label=None, **kwargs):
         entity = self.entity(row, col)
         download_box(self.w, entity, title, label)
 
 
-class DownloadView(baseviews.EntityView):
-    """this view is replacing the deprecated 'download' controller and allow downloading
-    of entities providing the necessary interface
+class DownloadView(EntityView):
+    """this view is replacing the deprecated 'download' controller and allow
+    downloading of entities providing the necessary interface
     """
     id = 'download'
-    __selectors__ = (one_line_rset, implement_interface)
-    accepts_interfaces = (IDownloadable,)
+    __select__ = one_line_rset() & implements(IDownloadable)
 
     templatable = False
     content_type = 'application/octet-stream'
@@ -83,34 +82,24 @@
         self.w(self.complete_entity(0).download_data())
 
 
-class DownloadLinkView(baseviews.EntityView):
+class DownloadLinkView(EntityView):
     """view displaying a link to download the file"""
     id = 'downloadlink'
+    __select__ = implements(IDownloadable)
     title = None # should not be listed in possible views
-    __selectors__ = (implement_interface,)
 
-    accepts_interfaces = (IDownloadable,)
-    
+
     def cell_call(self, row, col, title=None, **kwargs):
         entity = self.entity(row, col)
         url = html_escape(entity.download_url())
         self.w(u'<a href="%s">%s</a>' % (url, html_escape(title or entity.dc_title())))
 
 
-                                                                                
-class IDownloadablePrimaryView(baseviews.PrimaryView):
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (IDownloadable,)
-    # XXX File/Image attributes but this is not specified in the IDownloadable interface
-    skip_attrs = baseviews.PrimaryView.skip_attrs + ('data', 'name')
+class IDownloadablePrimaryView(primary.PrimaryView):
+    __select__ = implements(IDownloadable)
 
-    def render_entity_title(self, entity):
-        self.w(u'<h1>%s %s</h1>'
-               % (entity.dc_type().capitalize(),
-                  html_escape(entity.dc_title())))
-    
-    def render_entity_attributes(self, entity, siderelations):
-        super(IDownloadablePrimaryView, self).render_entity_attributes(entity, siderelations)
+    def render_entity_attributes(self, entity):
+        super(IDownloadablePrimaryView, self).render_entity_attributes(entity)
         self.w(u'<div class="content">')
         contenttype = entity.download_content_type()
         if contenttype.startswith('image/'):
@@ -126,17 +115,10 @@
                 msg = self.req._("can't display data, unexpected error: %s") % ex
                 self.w('<div class="error">%s</div>' % msg)
         self.w(u'</div>')
-            
-    def is_side_related(self, rschema, eschema):
-        """display all relations as side related"""
-        return True
 
 
 class IDownloadableLineView(baseviews.OneLineView):
-    __selectors__ = (implement_interface,)
-    # don't kick default oneline view
-    accepts_interfaces = (IDownloadable,)
-    
+    __select__ = implements(IDownloadable)
 
     def cell_call(self, row, col, title=None, **kwargs):
         """the secondary view is a link to download the file"""
@@ -148,12 +130,12 @@
                (url, name, durl, self.req._('download')))
 
 
-class ImageView(baseviews.EntityView):
-    __selectors__ = (implement_interface, score_entity_selector)
+class ImageView(EntityView):
     id = 'image'
+    __select__ = implements(IDownloadable) & score_entity(is_image)
+
     title = _('image')
-    accepts_interfaces = (IDownloadable,)
-    
+
     def call(self):
         rset = self.rset
         for i in xrange(len(rset)):
@@ -161,13 +143,6 @@
             self.wview(self.id, rset, row=i, col=0)
             self.w(u'</div>')
 
-    @classmethod
-    def score_entity(cls, entity):
-        mt = entity.download_content_type()
-        if not (mt and mt.startswith('image/')):
-            return 0
-        return 1
-    
     def cell_call(self, row, col):
         entity = self.entity(row, col)
         #if entity.data_format.startswith('image/'):
--- a/web/views/igeocodable.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/igeocodable.py	Mon May 04 13:09:48 2009 +0200
@@ -1,10 +1,16 @@
-# -*- coding: utf-8 -*-
+"""Specific views for entities implementing IGeocodable
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
 
 import simplejson
 
 from cubicweb.interfaces import IGeocodable
-from cubicweb.common.view import EntityView
-from cubicweb.common.selectors import implement_interface
+from cubicweb.view import EntityView
+from cubicweb.selectors import implements
 
 class GeocodingJsonView(EntityView):
     id = 'geocoding-json'
@@ -12,8 +18,7 @@
     templatable = False
     content_type = 'application/json'
 
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (IGeocodable,)
+    __select__ = implements(IGeocodable)
 
     def call(self):
         zoomlevel = self.req.form.pop('zoomlevel', 8)
@@ -46,8 +51,7 @@
 class GoogleMapBubbleView(EntityView):
     id = 'gmap-bubble'
 
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (IGeocodable,)
+    __select__ = implements(IGeocodable)
 
     def cell_call(self, row, col):
         entity = self.entity(row, col)
@@ -58,13 +62,12 @@
 class GoogleMapsView(EntityView):
     id = 'gmap-view'
 
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (IGeocodable,)
+    __select__ = implements(IGeocodable)
     need_navigation = False
 
     def call(self, gmap_key, width=400, height=400, uselabel=True, urlparams=None):
         self.req.add_js('http://maps.google.com/maps?file=api&amp;v=2&amp;key=%s' % gmap_key,
-                        localfile=False);
+                        localfile=False)
         self.req.add_js( ('cubicweb.widgets.js', 'cubicweb.gmap.js', 'gmap.utility.labeledmarker.js') )
         rql = self.rset.printable_rql()
         if urlparams is None:
--- a/web/views/iprogress.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/iprogress.py	Mon May 04 13:09:48 2009 +0200
@@ -9,10 +9,10 @@
 
 from logilab.mtconverter import html_escape
 
+from cubicweb.selectors import implements
 from cubicweb.interfaces import IProgress, IMileStone
 from cubicweb.schema import display_name
-from cubicweb.common.view import EntityView
-from cubicweb.common.selectors import implement_interface, accept
+from cubicweb.view import EntityView
 from cubicweb.web.htmlwidgets import ProgressBarWidget
 
 
@@ -35,9 +35,7 @@
     
     id = 'progress_table_view'
     title = _('task progression')
-    __selectors__ = (accept, implement_interface)
-
-    accepts_interfaces = (IMileStone,)
+    __select__ = implements(IMileStone)
 
     # default columns of the table
     columns = (_('project'), _('milestone'), _('state'), _('eta_date'),
@@ -182,9 +180,7 @@
     """displays a progress bar"""
     id = 'progressbar'
     title = _('progress bar')
-    __selectors__ = (accept, implement_interface)
-
-    accepts_interfaces = (IProgress,)
+    __select__ = implements(IProgress)
 
     def cell_call(self, row, col):
         self.req.add_css('cubicweb.iprogress.css')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/isioc.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,82 @@
+"""Specific views for SIOC interfaces
+
+:organization: Logilab
+:copyright: 2003-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from logilab.mtconverter import html_escape
+
+from cubicweb.view import EntityView
+from cubicweb.selectors import implements
+from cubicweb.interfaces import ISiocItem, ISiocContainer
+
+class SIOCView(EntityView):
+    id = 'sioc'
+    __select__ = EntityView.__select__ & implements(ISiocItem, ISiocContainer)
+    title = _('sioc')
+    templatable = False
+    content_type = 'text/xml'
+
+    def call(self):
+        self.w(u'<?xml version="1.0" encoding="%s"?>\n' % self.req.encoding)
+        self.w(u'''<rdf:RDF
+             xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+             xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
+             xmlns:owl="http://www.w3.org/2002/07/owl#"
+             xmlns:foaf="http://xmlns.com/foaf/0.1/"
+             xmlns:sioc="http://rdfs.org/sioc/ns#"
+             xmlns:sioctype="http://rdfs.org/sioc/types#"
+             xmlns:dcterms="http://purl.org/dc/terms/">\n''')
+        for i in xrange(self.rset.rowcount):
+            self.cell_call(i, 0)
+        self.w(u'</rdf:RDF>\n')
+
+    def cell_call(self, row, col):
+        self.wview('sioc_element', self.rset, row=row, col=col)
+
+class SIOCContainerView(EntityView):
+    id = 'sioc_element'
+    __select__ = EntityView.__select__ & implements(ISiocContainer)
+    templatable = False
+    content_type = 'text/xml'
+
+    def cell_call(self, row, col):
+        entity = self.complete_entity(row, col)
+        self.w(u'<sioc:%s rdf:about="%s">\n' % (html_escape(entity.isioc_type()),
+                                                html_escape(entity.absolute_url())))
+        self.w(u'<dcterms:title>%s</dcterms:title>' % html_escape(entity.dc_title()))
+        self.w(u'<dcterms:created>%s</dcterms:created>' % entity.creation_date)
+        self.w(u'<dcterms:modified>%s</dcterms:modified>' % entity.modification_date)
+        self.w(u'<!-- FIXME : here be items -->')#entity.isioc_items()
+        self.w(u'</sioc:%s>\n' % entity.isioc_type())
+
+
+class SIOCItemView(EntityView):
+    id = 'sioc_element'
+    __select__ = EntityView.__select__ & implements(ISiocItem)
+    templatable = False
+    content_type = 'text/xml'
+
+    def cell_call(self, row, col):
+        entity = self.complete_entity(row, col)
+        self.w(u'<sioc:%s rdf:about="%s">\n' %  (html_escape(entity.isioc_type()),
+                                                 html_escape(entity.absolute_url())))
+        self.w(u'<dcterms:title>%s</dcterms:title>' % html_escape(entity.dc_title()))
+        self.w(u'<dcterms:created>%s</dcterms:created>' % entity.creation_date)
+        self.w(u'<dcterms:modified>%s</dcterms:modified>' % entity.modification_date)
+        if entity.content:
+            self.w(u'<sioc:content>%s</sioc:content>''' % html_escape(entity.isioc_content()))
+        if entity.related('entry_of'):
+            self.w(u'<sioc:has_container rdf:resource="%s"/>\n' % html_escape(entity.isioc_container().absolute_url()))
+        if entity.creator:
+            self.w(u'<sioc:has_creator>\n')
+            self.w(u'<sioc:User rdf:about="%s">\n' % html_escape(entity.creator.absolute_url()))
+            self.w(entity.creator.view('foaf'))
+            self.w(u'</sioc:User>\n')
+            self.w(u'</sioc:has_creator>\n')
+        self.w(u'<!-- FIXME : here be topics -->')#entity.isioc_topics()
+        self.w(u'<!-- FIXME : here be replies -->')#entity.isioc_replies()
+        self.w(u' </sioc:%s>\n' % 'Post')
+
--- a/web/views/magicsearch.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/magicsearch.py	Mon May 04 13:09:48 2009 +0200
@@ -2,7 +2,7 @@
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
@@ -15,7 +15,7 @@
 from rql.nodes import Relation
 
 from cubicweb import Unauthorized
-from cubicweb.common.appobject import Component, SingletonComponent
+from cubicweb.view import Component
 
 LOGGER = getLogger('cubicweb.magicsearch')
 
@@ -41,7 +41,7 @@
     :param translations: the reverted l10n dict
 
     :type schema: `cubicweb.schema.Schema`
-    :param schema: the application's schema    
+    :param schema: the application's schema
     """
     # var_types is used as a map : var_name / var_type
     vartypes = {}
@@ -107,7 +107,7 @@
         if rtype is None:
             continue
         relation.r_type = rtype
-    
+
 
 
 QUOTED_SRE = re.compile(r'(.*?)(["\'])(.+?)\2')
@@ -145,7 +145,7 @@
             return req.execute(*args)
         finally:
             # rollback necessary to avoid leaving the connection in a bad state
-            req.cnx.rollback() 
+            req.cnx.rollback()
 
     def preprocess_query(self, uquery, req):
         raise NotImplementedError()
@@ -161,10 +161,10 @@
     priority = 0
     def preprocess_query(self, uquery, req):
         return uquery,
-    
+
 
 class QueryTranslator(BaseQueryProcessor):
-    """ parses through rql and translates into schema language entity names 
+    """ parses through rql and translates into schema language entity names
     and attributes
     """
     priority = 2
@@ -185,15 +185,15 @@
     preprocessing query in shortcut form to their RQL form
     """
     priority = 4
-    
+
     def preprocess_query(self, uquery, req):
-        """"""
+        """try to get rql from an unicode query string"""
         args = None
         self.req = req
         try:
             # Process as if there was a quoted part
             args = self._quoted_words_query(uquery)
-        ## No quoted part  
+        ## No quoted part
         except BadRQLQuery:
             words = uquery.split()
             if len(words) == 1:
@@ -205,7 +205,7 @@
             else:
                 args = self._multiple_words_query(words)
         return args
-    
+
     def _get_entity_type(self, word):
         """check if the given word is matching an entity type, return it if
         it's the case or raise BadRQLQuery if not
@@ -214,7 +214,7 @@
         try:
             return trmap(self.config, self.vreg.schema, self.req.lang)[etype]
         except KeyError:
-            raise BadRQLQuery('%s is not a valid entity name' % etype)        
+            raise BadRQLQuery('%s is not a valid entity name' % etype)
 
     def _get_attribute_name(self, word, eschema):
         """check if the given word is matching an attribute of the given entity type,
@@ -261,7 +261,7 @@
         if var is None:
             var = etype[0]
         return '%s %s %s%%(text)s' % (var, searchattr, searchop)
-        
+
     def _two_words_query(self, word1, word2):
         """Specific process for two words query (case (2) of preprocess_rql)
         """
@@ -272,7 +272,7 @@
         # else, suppose it's a shortcut like : Person Smith
         rql = '%s %s WHERE %s' % (etype, etype[0], self._complete_rql(word2, etype))
         return rql, {'text': word2}
-           
+
     def _three_words_query(self, word1, word2, word3):
         """Specific process for three words query (case (3) of preprocess_rql)
         """
@@ -336,20 +336,20 @@
             return self._three_words_query(word1, word2, quoted_part)
             # return ori_rql
         raise BadRQLQuery("unable to handle request %r" % ori_rql)
-    
+
 
- 
+
 class FullTextTranslator(BaseQueryProcessor):
     priority = 10
     name = 'text'
-    
+
     def preprocess_query(self, uquery, req):
         """suppose it's a plain text query"""
         return 'Any X WHERE X has_text %(text)s', {'text': uquery}
 
 
 
-class MagicSearchComponent(SingletonComponent):
+class MagicSearchComponent(Component):
     id  = 'magicsearch'
     def __init__(self, req, rset=None):
         super(MagicSearchComponent, self).__init__(req, rset)
@@ -392,33 +392,3 @@
             # let exception propagate
             return proc.process_query(uquery, req)
         raise BadRQLQuery(req._('sorry, the server is unable to handle this query'))
-
-
-# Do not make a strong dependency on NlpTools
-try:
-    from NlpTools.rqltools.client import RQLClient
-except ImportError:
-    LOGGER.info('could not import RQLClient (NlpTools)')
-else:
-    try:
-        from Pyro.errors import NamingError
-    except ImportError:
-        LOGGER.warning("pyro is not installed, can't try to connect to nlp server")
-    else:
-        try:
-            class NLPProcessor(BaseQueryProcessor):
-                priority = 8
-                nlp_agent = RQLClient('ivan')
-                def preprocess_query(self, uquery, req):
-                    try:
-                        answer = self.nlp_agent.get_translation(uquery)
-                        if not answer:
-                            raise BadRQLQuery(uquery)
-                        return answer or uquery,
-                    except Exception, ex:
-                        LOGGER.exception(str(ex))
-                        return uquery,
-
-        except NamingError: # NlpTools available but no server registered
-            LOGGER.warning('could not find any RQLServer object named "ivan"')
-
--- a/web/views/management.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/management.py	Mon May 04 13:09:48 2009 +0200
@@ -1,40 +1,25 @@
-"""management and error screens
+"""security management and error screens
 
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from logilab.mtconverter import html_escape
 
-from logilab.common.decorators import cached
+from cubicweb.selectors import yes, none_rset, match_user_groups
+from cubicweb.view import AnyRsetView, StartupView, EntityView
+from cubicweb.common.uilib import html_traceback, rest_traceback
+from cubicweb.web import formwidgets
+from cubicweb.web.form import FieldsForm, EntityFieldsForm
+from cubicweb.web.formfields import guess_field
+from cubicweb.web.formrenderers import HTableFormRenderer
 
-from cubicweb.common.utils import UStringIO
-from cubicweb.common.view import AnyRsetView, StartupView, EntityView
-from cubicweb.common.uilib import html_traceback, rest_traceback, ajax_replace_url
-from cubicweb.common.selectors import (yes, one_line_rset,
-                                       accept_rset, none_rset,
-				       match_user_group,
-                                       chainfirst, chainall, )
-from cubicweb.web import INTERNAL_FIELD_VALUE, eid_param, stdmsgs
-from cubicweb.web.widgets import StaticComboBoxWidget
-from cubicweb.web.form import FormMixIn
-_ = unicode
-
-def begin_form(w, entity, redirectvid, redirectpath=None, msg=None):
-    w(u'<form method="post" action="%s">\n' % entity.req.build_url('edit'))
-    w(u'<fieldset>\n')
-    w(u'<input type="hidden" name="__redirectvid" value="%s"/>\n' % redirectvid)
-    w(u'<input type="hidden" name="__redirectpath" value="%s"/>\n' % (
-        html_escape(redirectpath or entity.rest_path())))
-    w(u'<input type="hidden" name="eid" value="%s"/>\n' % entity.eid)
-    w(u'<input type="hidden" name="%s" value="%s"/>\n' % (
-        eid_param('__type', entity.eid), entity.e_schema))
-    if msg:
-        w(u'<input type="hidden" name="__message" value="%s"/>\n'
-          % html_escape(msg))
+SUBMIT_MSGID = _('Submit bug report')
+MAIL_SUBMIT_MSGID = _('Submit bug report by mail')
 
 class SecurityViewMixIn(object):
     """display security information for a given schema """
@@ -44,7 +29,7 @@
         if not access_types:
             access_types = eschema.ACTIONS
         w(u'<table class="schemaInfo">')
-        w(u'<tr><th>%s</th><th>%s</th><th>%s</th></tr>' % ( 
+        w(u'<tr><th>%s</th><th>%s</th><th>%s</th></tr>' % (
             _("permission"), _('granted to groups'), _('rql expressions')))
         for access_type in access_types:
             w(u'<tr>')
@@ -76,10 +61,14 @@
                 return True
         return False
 
+
 class SecurityManagementView(EntityView, SecurityViewMixIn):
     """display security information for a given entity"""
     id = 'security'
     title = _('security')
+    def call(self):
+        self.w(u'<div id="progress">%s</div>' % self.req._('validating...'))
+        super(SecurityManagementView, self).call()
 
     def cell_call(self, row, col):
         self.req.add_js('cubicweb.edition.js')
@@ -101,7 +90,7 @@
             self.owned_by_edit_form(entity)
         else:
             self.owned_by_information(entity)
-        # epermissions
+        # cwpermissions
         if 'require_permission' in entity.e_schema.subject_relations():
             w('<h3>%s</h3>' % _('permissions for this entity'))
             reqpermschema = self.schema.rschema('require_permission')
@@ -111,15 +100,15 @@
 
     def owned_by_edit_form(self, entity):
         self.w('<h3>%s</h3>' % self.req._('ownership'))
-        begin_form(self.w, entity, 'security', msg= _('ownerships have been changed'))
-        self.w(u'<table border="0">\n')
-        self.w(u'<tr><td>\n')
-        wdg = entity.get_widget('owned_by')
-        self.w(wdg.edit_render(entity))
-        self.w(u'</td><td>\n')
-        self.w(self.button_ok())
-        self.w(u'</td></tr>\n</table>\n')
-        self.w(u'</fieldset></form>\n')
+        msg = self.req._('ownerships have been changed')
+        form = EntityFieldsForm(self.req, None, entity=entity, submitmsg=msg,
+                                form_buttons=[formwidgets.SubmitButton()],
+                                domid='ownership%s' % entity.eid,
+                                __redirectvid='security',
+                                __redirectpath=entity.rest_path())
+        field = guess_field(entity.e_schema, self.schema.rschema('owned_by'))
+        form.append_field(field)
+        self.w(form.form_render(display_progress_div=False))
 
     def owned_by_information(self, entity):
         ownersrset = entity.related('owned_by')
@@ -150,58 +139,52 @@
             w(u'<table class="schemaInfo">')
             w(u'<tr><th>%s</th><th>%s</th></tr>' % (_("permission"),
                                                     _('granted to groups')))
-            for eperm in entity.require_permission:
+            for cwperm in entity.require_permission:
                 w(u'<tr>')
                 if dellinktempl:
-                    w(u'<td>%s%s</td>' % (dellinktempl % eperm.eid,
-                                          eperm.view('oneline')))
+                    w(u'<td>%s%s</td>' % (dellinktempl % cwperm.eid,
+                                          cwperm.view('oneline')))
                 else:
-                    w(u'<td>%s</td>' % eperm.view('oneline'))
-                w(u'<td>%s</td>' % self.view('csv', eperm.related('require_group'), 'null'))
+                    w(u'<td>%s</td>' % cwperm.view('oneline'))
+                w(u'<td>%s</td>' % self.view('csv', cwperm.related('require_group'), 'null'))
                 w(u'</tr>\n')
             w(u'</table>')
         else:
-            self.w(self.req._('no associated epermissions'))
+            self.w(self.req._('no associated permissions'))
 
     def require_permission_edit_form(self, entity):
         w = self.w
         _ = self.req._
-        newperm = self.vreg.etype_class('EPermission')(self.req, None)
+        newperm = self.vreg.etype_class('CWPermission')(self.req, None)
         newperm.eid = self.req.varmaker.next()
         w(u'<p>%s</p>' % _('add a new permission'))
-        begin_form(w, newperm, 'security', entity.rest_path())
-        w(u'<input type="hidden" name="%s" value="__cubicweb_internal_field__"/>'
-          % eid_param('edito-require_permission', newperm.eid))
-        w(u'<input type="hidden" name="%s" value="%s"/>'
-          % (eid_param('require_permission', newperm.eid), entity.eid))
-        w(u'<table border="0">\n')
-        w(u'<tr><th>%s</th><th>%s</th><th>%s</th><th>&nbsp;</th></tr>\n'
-               % (_("name"), _("label"), _('granted to groups')))
-        if getattr(entity, '__permissions__', None):
-            # vocabfunc must be compliant with StaticVocabularyConstraint.vocabulary
-            # which takes only keyword parameters
-            wdg = StaticComboBoxWidget(self.vreg, self.schema['EPermission'],
-                                       self.schema['name'], self.schema['String'],
-                                       vocabfunc=lambda entity, x=entity: x.__permissions__)
+        form = EntityFieldsForm(self.req, None, entity=newperm,
+                                form_buttons=[formwidgets.SubmitButton()],
+                                domid='reqperm%s' % entity.eid,
+                                __redirectvid='security',
+                                __redirectpath=entity.rest_path())
+        form.form_add_hidden('require_permission', entity.eid, role='object',
+                             eidparam=True)
+        permnames = getattr(entity, '__permissions__', None)
+        cwpermschema = newperm.e_schema
+        if permnames is not None:
+            field = guess_field(cwpermschema, self.schema.rschema('name'),
+                                widget=formwidgets.Select({'size': 1}),
+                                choices=permnames)
         else:
-            wdg = newperm.get_widget('name')
-        w(u'<tr><td>%s</td>\n' % wdg.edit_render(newperm))
-        wdg = newperm.get_widget('label')
-        w(u'<td>%s</td>\n' % wdg.edit_render(newperm))
-        wdg = newperm.get_widget('require_group')
-        w(u'<td>%s</td>\n' % wdg.edit_render(newperm))
-        w(u'<td>%s</td></tr>\n' % self.button_ok())
-        w(u'</table>')
-        w(u'</fieldset></form>\n')
+            field = guess_field(cwpermschema, self.schema.rschema('name'))
+        form.append_field(field)
+        field = guess_field(cwpermschema, self.schema.rschema('label'))
+        form.append_field(field)
+        field = guess_field(cwpermschema, self.schema.rschema('require_group'))
+        form.append_field(field)
+        self.w(form.form_render(renderer=HTableFormRenderer(display_progress_div=False)))
 
-    def button_ok(self):
-        return (u'<input class="validateButton" type="submit" name="submit" value="%s"/>'
-                % self.req._(stdmsgs.BUTTON_OK))
 
 
 class ErrorView(AnyRsetView):
     """default view when no result has been found"""
-    __selectors__ = (yes,)
+    __select__ = yes()
     id = 'error'
 
     def page_title(self):
@@ -212,10 +195,10 @@
 
     def call(self):
         req = self.req.reset_headers()
-        _ = req._; w = self.w
+        w = self.w
         ex = req.data.get('ex')#_("unable to find exception information"))
         excinfo = req.data.get('excinfo')
-        title = _('an error occured')
+        title = self.req._('an error occured')
         w(u'<h2>%s</h2>' % title)
         if 'errmsg' in req.data:
             ex = req.data['errmsg']
@@ -239,39 +222,32 @@
             return
         vcconf = self.config.vc_config()
         w(u"<div>")
-        eversion = vcconf.get('cubicweb', _('no version information'))
+        eversion = vcconf.get('cubicweb', self.req._('no version information'))
         # NOTE: tuple wrapping needed since eversion is itself a tuple
         w(u"<b>CubicWeb version:</b> %s<br/>\n" % (eversion,))
-        for pkg in self.config.cubes():
-            pkgversion = vcconf.get(pkg, _('no version information'))
-            w(u"<b>Package %s version:</b> %s<br/>\n" % (pkg, pkgversion))
+        cversions = []
+        for cube in self.config.cubes():
+            cubeversion = vcconf.get(cube, self.req._('no version information'))
+            w(u"<b>Package %s version:</b> %s<br/>\n" % (cube, cubeversion))
+            cversions.append((cube, cubeversion))
         w(u"</div>")
         # creates a bug submission link if SUBMIT_URL is set
         submiturl = self.config['submit-url']
-        if submiturl:
-            binfo = text_error_description(ex, excinfo, req, eversion,
-                                           [(pkg, vcconf.get(pkg, _('no version information')))
-                                            for pkg in self.config.cubes()])
-            w(u'<form action="%s" method="post">\n' % html_escape(submiturl))
-            w(u'<fieldset>\n')
-            w(u'<textarea class="hidden" name="description">%s</textarea>' % html_escape(binfo))
-            w(u'<input type="hidden" name="description_format" value="text/rest"/>')
-            w(u'<input type="hidden" name="__bugreporting" value="1"/>')
-            w(u'<input type="submit" value="%s"/>' % _('Submit bug report'))
-            w(u'</fieldset>\n')
-            w(u'</form>\n')
         submitmail = self.config['submit-mail']
-        if submitmail:
-            binfo = text_error_description(ex, excinfo, req, eversion,
-                                           [(pkg, vcconf.get(pkg, _('no version information')))
-                                            for pkg in self.config.cubes()])
-            w(u'<form action="%s" method="post">\n' % req.build_url('reportbug'))
-            w(u'<fieldset>\n')
-            w(u'<input type="hidden" name="description" value="%s"/>' % html_escape(binfo))
-            w(u'<input type="hidden" name="__bugreporting" value="1"/>')
-            w(u'<input type="submit" value="%s"/>' % _('Submit bug report by mail'))
-            w(u'</fieldset>\n')
-            w(u'</form>\n')
+        if submiturl or submitmail:
+            form = FieldsForm(self.req, set_error_url=False)
+            binfo = text_error_description(ex, excinfo, req, eversion, cversions)
+            form.form_add_hidden('description', binfo)
+            form.form_add_hidden('__bugreporting', '1')
+            if submitmail:
+                form.form_buttons = [formwidgets.SubmitButton(MAIL_SUBMIT_MSGID)]
+                form.action = req.build_url('reportbug')
+                w(form.form_render())
+            if submiturl:
+                form.form_add_hidden('description_format', 'text/rest')
+                form.form_buttons = [formwidgets.SubmitButton(SUBMIT_MSGID)]
+                form.action = submiturl
+                w(form.form_render())
 
 
 def exc_message(ex, encoding):
@@ -295,271 +271,11 @@
     binfo += '\n'
     return binfo
 
-# some string we want to be internationalizable for nicer display of eproperty
-# groups
-_('navigation')
-_('ui')
-_('actions')
-_('boxes')
-_('components')
-_('contentnavigation')
-
-
-def make_togglable_link(nodeid, label):
-    """builds a HTML link that switches the visibility & remembers it"""
-    action = u"javascript: toggleVisibility('%s')" % nodeid
-    return u'<a href="%s">%s</a>' % (action, label)
-
-def css_class(someclass):
-    return someclass and 'class="%s"' % someclass or ''
-
-### translations for SystemEpropertiesForm
-_('navigation.combobox-limit')
-_('navigation.page-size')
-_('navigation.related-limit')
-_('navigation.short-line-size')
-_('ui.date-format')
-_('ui.datetime-format')
-_('ui.default-text-format')
-_('ui.fckeditor')
-_('ui.float-format')
-_('ui.language')
-_('ui.time-format')
-_('open all')
-_('ui.main-template')
-_('ui.site-title')
-_('ui.encoding')
-_('category')
-
-class SystemEpropertiesForm(FormMixIn, StartupView):
-    controller = 'edit'
-    id = 'systemepropertiesform'
-    title = _('site configuration')
-    require_groups = ('managers',)
-    category = 'startupview'
-
-    def linkable(self):
-        return True
-
-    def url(self):
-        """return the url associated with this view. We can omit rql here"""
-        return self.build_url('view', vid=self.id)
-
-    def _cookie_name(self, somestr):
-        return str('%s_property_%s' % (self.config.appid, somestr))
-
-    def _group_status(self, group, default=u'hidden'):
-        cookies = self.req.get_cookie()
-        cookiename = self._cookie_name(group)
-        cookie = cookies.get(cookiename)
-        if cookie is None:
-            cookies[cookiename] = default
-            self.req.set_cookie(cookies, cookiename, maxage=None)
-            status = default
-        else:
-            status = cookie.value
-        return status
-
-    def call(self, **kwargs):
-        """The default view representing the application's index"""
-        self.req.add_js(('cubicweb.edition.js', 'cubicweb.preferences.js', 'cubicweb.ajax.js'))
-        self.req.add_css('cubicweb.preferences.css')
-        vreg = self.vreg
-        values = self.defined_keys
-        groupedopts = {}
-        mainopts = {}
-        # "self.id=='systemepropertiesform'" to skip site wide properties on
-        # user's preference but not site's configuration
-        for key in vreg.user_property_keys(self.id=='systemepropertiesform'):
-            parts = key.split('.')
-            if parts[0] in vreg:
-                # appobject configuration
-                reg, oid, propid = parts
-                groupedopts.setdefault(reg, {}).setdefault(oid, []).append(key)
-            else:
-                mainopts.setdefault(parts[0], []).append(key)
-        # precompute form to consume error message
-        for group, keys in mainopts.items():
-            mainopts[group] = self.form(group, keys, False)
-
-        for group, objects in groupedopts.items():
-            for oid, keys in objects.items():
-                groupedopts[group][oid] = self.form(group + '-' + oid, keys, True)
-
-        w = self.w
-        req = self.req
-        _ = req._
-        w(u'<h1>%s</h1>\n' % _(self.title))
-        w(self.error_message())
-        for label, group, form in sorted((_(g), g, f)
-                                         for g, f in mainopts.iteritems()):
-            status = css_class(self._group_status(group)) #'hidden' (collapsed), or '' (open) ?
-            w(u'<h2 class="propertiesform">%s</h2>\n' %
-            (make_togglable_link('fieldset_' + group, label.capitalize())))
-            w(u'<div id="fieldset_%s" %s>' % (group, status))
-            w(u'<fieldset class="preferences">')
-            w(form)
-            w(u'</fieldset></div>')
-
-        for label, group, objects in sorted((_(g), g, o)
-                                            for g, o in groupedopts.iteritems()):
-            status = css_class(self._group_status(group))
-            w(u'<h2 class="propertiesform">%s</h2>\n' %
-              (make_togglable_link('fieldset_' + group, label.capitalize())))
-            w(u'<div id="fieldset_%s" %s>' % (group, status))
-	    
-	    # create selection
-	    sorted_objects =  sorted((self.req.__('%s_%s' % (group, o)), o, f)
-                                           for o, f in objects.iteritems())
-	    for label, oid, form in sorted_objects:
-                w(u'''<div class="componentLink"><a href="javascript:noop();" onclick="javascript:toggleVisibility('field_%(oid)s_%(group)s')" class="componentTitle">%(label)s</a>''' % {'label':label, 'oid':oid, 'group':group})
-                w(u''' (<div class="openlink"><a href="javascript:noop();" onclick="javascript:closeFieldset('fieldset_%(group)s')">%(label)s</a></div>)'''
-                  % {'label':_('close all'), 'group':group})
-                w(u'</div>')
-                docmsgid = '%s_%s_description' % (group, oid)
-                doc = _(docmsgid)
-                if doc != docmsgid:
-                    w(u'<p class="helper">%s</p>' % html_escape(doc).capitalize())
-		    
-		w(u'<fieldset id="field_%(oid)s_%(group)s" class="%(group)s preferences">'
-                  % {'oid':oid, 'group':group})
-		w(form)
-                w(u'</fieldset>')
-            w(u'</div>')
-
-    @property
-    @cached
-    def eprops_rset(self):
-        return self.req.execute('Any P,K,V WHERE P is EProperty, P pkey K, P value V, NOT P for_user U')
-
-    @property
-    def defined_keys(self):
-        values = {}
-        for i, entity in enumerate(self.eprops_rset.entities()):
-            values[entity.pkey] = i
-        return values
-
-    def entity_for_key(self, key):
-        values = self.defined_keys
-        if key in values:
-            entity = self.eprops_rset.get_entity(values[key], 0)
-        else:
-            entity = self.vreg.etype_class('EProperty')(self.req, None, None)
-            entity.eid = self.req.varmaker.next()
-            entity['value'] = self.vreg.property_value(key)
-        return entity
-
-    def form(self, formid, keys, splitlabel=False):
-        stream = UStringIO()
-        w = stream.write
-	w(u'''<form action="%(url)s" id="%(formid)s" method="post" onsubmit="return validatePrefsForm('%(formid)s')" >\n''' % {'url' : self.build_url(), 'formid':formid})
-        w(u'<fieldset>\n')
-	w(u'<div class="formsg"></div>')
-        w(u'<input type="hidden" name="__errorurl" value="%s"/>\n'
-          % html_escape(self.req.url()))
-        w(u'<input type="hidden" name="__form_id" value="%s"/>\n' % self.id)
-        path = self.req.relative_path()
-        if '?' in path:
-            path, params = path.split('?', 1)
-            w(u'<input type="hidden" name="__redirectparams" value="%s"/>\n'
-              % html_escape(params))
-        w(u'<input type="hidden" name="__redirectpath" value="%s"/>\n' % path)
-        w(u'<input type="hidden" name="__redirectrql" value=""/>\n')
-        w(u'<input type="hidden" name="__message" value="%s"/>\n'
-          % self.req._('changes applied'))
-	for key in keys:
-            self.form_row(w, key, splitlabel)
-        w(self.button_ok())
-        w(u'</fieldset>\n')
-        w(u'</form>\n')
-        return stream.getvalue()
-     
-    def form_row(self, w, key, splitlabel):
-	entity = self.entity_for_key(key)
-        eid = entity.eid
-	if splitlabel:
-            w(u'<label>%s</label>' % self.req._(key.split('.')[-1]).capitalize())
-        else:
-            w(u'<label>%s</label>' % self.req._(key).capitalize())
- 
-        wdg = self.vreg.property_value_widget(key, req=self.req)
-        error = wdg.render_error(entity)
-	w(u'<div class="preffield">\n')
-        w(u'%s' % wdg.render_help(entity))
-        w(u'<div class="prefinput">')
-        w(u'<span class="%s">%s</span>' % (error and 'error' or '', error))
-        self.form_row_hiddens(w, entity, key)
-	w(wdg.edit_render(entity))
-        w(u'<input type="hidden" id="current-value:%(eid)s" value="%(value)s"/>'
-          % {'eid':entity.eid, 'value':wdg.current_display_value(entity)})
-	w(u'</div>')
-	w(u'</div>')
-	return entity
-
-    def form_row_hiddens(self, w, entity, key):
-        eid = entity.eid
-        w(u'<input type="hidden" name="eid" value="%s"/>' % eid)
-        w(u'<input type="hidden" name="%s" value="EProperty"/>' % eid_param('__type', eid))
-        w(u'<input type="hidden" name="%s" value="%s"/>' % (eid_param('pkey', eid), key))
-        w(u'<input type="hidden" name="%s" value="%s"/>' % (eid_param('edits-pkey', eid), ''))
-
-        
-class EpropertiesForm(SystemEpropertiesForm):
-    id = 'epropertiesform'
-    title = _('preferences')
-    require_groups = ('managers',) # we don't want guests to be able to come here
-    __selectors__ = chainfirst(none_rset, 
-                               chainall( match_user_group, one_line_rset, accept_rset)),
-    accepts = ('EUser',)
-
-    @classmethod
-    def accept_rset(cls, req, rset, row, col):
-        if row is None:
-            row = 0
-        score = super(EpropertiesForm, cls).accept_rset(req, rset, row, col)
-        # check current user is the rset user or he is in the managers group
-        if score and (req.user.eid == rset[row][col or 0]
-                      or req.user.matching_groups('managers')):
-            return score
-        return 0
-
-    @property
-    def user(self):
-        if self.rset is None:
-            return self.req.user
-        return self.rset.get_entity(self.row or 0, self.col or 0)
-
-    @property
-    @cached
-    def eprops_rset(self):
-        return self.req.execute('Any P,K,V WHERE P is EProperty, P pkey K, P value V,'
-                                'P for_user U, U eid %(x)s', {'x': self.user.eid})
-class ManagerEpropertiesForm(EpropertiesForm):
-    title = _('preferences')
-    require_groups = ('users',) 
-    __selectors__ = chainfirst(none_rset, 
-                               chainall( match_user_group, one_line_rset, accept_rset)),
- 
-
-    def form_row_hiddens(self, w, entity, key):
-        super(ManagerEpropertiesForm, self).form_row_hiddens(w, entity, key)
-        # if user is in the managers group and the property is being created,
-        # we have to set for_user explicitly
-        if not entity.has_eid():
-            eid = entity.eid
-            w(u'<input type="hidden" name="%s" value="%s"/>'
-              % (eid_param('edits-for_user', eid), INTERNAL_FIELD_VALUE))
-            w(u'<input type="hidden" name="%s" value="%s"/>'
-              % (eid_param('for_user', eid), self.user.eid))
-
-#     def form_row(self, w, key, splitlabel):
-# 	print 'manager'
-
-
 class ProcessInformationView(StartupView):
     id = 'info'
+    __select__ = none_rset() & match_user_groups('managers')
+
     title = _('server information')
-    require_groups = ('managers',)
 
     def call(self, **kwargs):
         """display server information"""
--- a/web/views/massmailing.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/massmailing.py	Mon May 04 13:09:48 2009 +0200
@@ -1,29 +1,31 @@
 """Mass mailing form views
 
 :organization: Logilab
-:copyright: 2007-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
+__docformat__ = "restructuredtext en"
 
 import operator
 
-from logilab.mtconverter import html_escape
-
 from cubicweb.interfaces import IEmailable
-from cubicweb.common.view import EntityView
-from cubicweb.common.selectors import implement_interface, match_user_group
-from cubicweb.web.action import EntityAction
+from cubicweb.selectors import implements, match_user_groups
+from cubicweb.view import EntityView
 from cubicweb.web import stdmsgs
-
+from cubicweb.web.action import Action
+from cubicweb.web.form import FieldsForm, FormRenderer, FormViewMixIn
+from cubicweb.web.formfields import StringField
+from cubicweb.web.formwidgets import CheckBox, TextInput, AjaxWidget, ImgButton
 
-class SendEmailAction(EntityAction):
+_ = unicode
+
+class SendEmailAction(Action):
+    id = 'sendemail'
+    # XXX should check email is set as well
+    __select__ = implements(IEmailable) & match_user_groups('managers', 'users')
+
+    title = _('send email')
     category = 'mainactions'
-    __selectors__ = (implement_interface, match_user_group)
-    accepts_interfaces = (IEmailable,) # XXX should check email is set as well
-    require_groups = ('managers', 'users')
-
-    id = 'sendemail'
-    title = _('send email')
 
     def url(self):
         params = {'vid': 'massmailing', '__force_display': 1}
@@ -33,97 +35,92 @@
                               **params)
 
 
-class MassMailingForm(EntityView):
+class MassMailingForm(FieldsForm):
     id = 'massmailing'
-    __selectors__ = (implement_interface, match_user_group)
-    accepts_interfaces = (IEmailable,)
-    require_groups = ('managers', 'users')
-    
+
+    sender = StringField(widget=TextInput({'disabled': 'disabled'}), label=_('From:'))
+    recipient = StringField(widget=CheckBox(), label=_('Recipients:'))
+    subject = StringField(label=_('Subject:'))
+    mailbody = StringField(widget=AjaxWidget(wdgtype='TemplateTextField',
+                                             inputid='mailbody'))
+    form_buttons = [ImgButton('sendbutton', "javascript: $('#sendmail').submit()",
+                              _('send email'), 'SEND_EMAIL_ICON'),
+                    ImgButton('cancelbutton', "javascript: history.back()",
+                              stdmsgs.BUTTON_CANCEL, 'CANCEL_EMAIL_ICON')]
+
+    def form_field_vocabulary(self, field):
+        if field.name == 'recipient':
+            vocab = [(entity.get_email(), entity.eid) for entity in self.rset.entities()]
+            return [(label, value) for label, value in vocab if label]
+        return super(MassMailingForm, self).form_field_vocabulary(field)
+
+    def form_field_value(self, field, values):
+        if field.name == 'recipient':
+            return [entity.eid for entity in self.rset.entities() if entity.get_email()]
+        elif field.name == 'mailbody':
+            field.widget.attrs['cubicweb:variables'] = ','.join(self.get_allowed_substitutions())
+        return super(MassMailingForm, self).form_field_value(field, values)
+
+    def get_allowed_substitutions(self):
+        attrs = []
+        for coltype in self.rset.column_types(0):
+            eclass = self.vreg.etype_class(coltype)
+            attrs.append(eclass.allowed_massmail_keys())
+        return sorted(reduce(operator.and_, attrs))
 
-    form_template = u"""
-<div id="compose">
-<form id="sendemail" action="sendmail" method="post">
-<table class="headersform">
-<tr>
-  <td class="hlabel">%(from_header)s</td>
-  <td class="hvalue">%(from)s</td>
-</tr>
-<tr>
-  <td class="hlabel">%(recipients_header)s</td>
-  <td class="hvalue">%(recipients)s</td>
-</tr>
-<tr>
-  <td class="hlabel">%(subject)s</td>
-  <td class="hvalue"><input id="mailsubj" name="mailsubject" value="" /></td>
-</tr>
-</table>
-<div id="toolbar">
-<ul>
-<li><a id="sendbutton" href="javascript: $('sendemail').submit()">
-    <img src="%(sendimgpath)s" alt="%(send)s"/>%(send)s</a></li>
-<li><a id="cancelbutton" href="javascript: history.back()">
-    <img src="%(cancelimgpath)s" alt="%(cancel)s"/>%(cancel)s</a></li>
- </ul>
-</div>
-<table>
-<tr>
-  <td>
-    <div>
-      <div id="emailbody" class="widget" cubicweb:loadtype="auto" cubicweb:wdgtype="TemplateTextField"
-           cubicweb:inputid="emailarea" cubicweb:inputname="mailbody" cubicweb:variables="%(variables)s"/>
-    </div>
-  </td>
-  <td>%(substitutions)s</td>
-</tr>
-</table>
-</form>
-</div>
-    """    
+    def build_substitutions_help(self):
+        insertLink = u'<a href="javascript: insertText(\'%%(%s)s\', \'emailarea\');">%%(%s)s</a>'
+        substs = (u'<div class="substitution">%s</div>' % (insertLink % (subst, subst))
+                  for subst in self.get_allowed_substitutions())
+        helpmsg = self.req._('You can use any of the following substitutions in your text')
+        return u'<div id="substitutions"><span>%s</span>%s</div>' % (
+            helpmsg, u'\n'.join(substs))
+
+
+class MassMailingFormRenderer(FormRenderer):
+    button_bar_class = u'toolbar'
+
+    def _render_fields(self, fields, w, form):
+        w(u'<table class="headersform">')
+        for field in fields:
+            if field.name == 'mailbody':
+                w(u'</table>')
+                self._render_toolbar(w, form)
+                w(u'<table>')
+                w(u'<tr><td><div>')
+            else:
+                w(u'<tr>')
+                w(u'<td class="hlabel">%s</td>' % self.render_label(form, field))
+                w(u'<td class="hvalue">')
+            w(field.render(form, self))
+            if field.name == 'mailbody':
+                w(u'</div></td>')
+                w(u'<td>%s</td>' % form.build_substitutions_help())
+                w(u'</tr>')
+            else:
+                w(u'</td></tr>')
+        w(u'</table>')
+
+    def _render_toolbar(self, w, form):
+        w(u'<div id="toolbar">')
+        w(u'<ul>')
+        for button in form.form_buttons:
+            w(u'<li>%s</li>' % button.render(form))
+        w(u'</ul>')
+        w(u'</div>')
+
+    def render_buttons(self, w, form):
+        pass
+
+class MassMailingFormView(FormViewMixIn, EntityView):
+    id = 'massmailing'
+    __select__ = implements(IEmailable) & match_user_groups('managers', 'users')
 
     def call(self):
         req = self.req
         req.add_js('cubicweb.widgets.js')
         req.add_css('cubicweb.mailform.css')
         from_addr = '%s <%s>' % (req.user.dc_title(), req.user.get_email())
-        ctx = {
-            'from_header' : req._('From: '),
-            'from' : html_escape(from_addr),
-            'substitutions' : self._build_substitutions_help(),
-            'recipients_header' : req._('Recipients: '),
-            'subject' : req._('Subject: '),
-            'body' : req._('Email body: '),
-            'variables' : ','.join(self._get_allowed_substitutions()),
-            'recipients' : self._build_recipients_list(),
-            'cancel' : req._(stdmsgs.BUTTON_CANCEL),
-            'cancelimgpath' : req.external_resource('CANCEL_EMAIL_ICON'),
-            'send' : req._('send email'),
-            'sendimgpath' : req.external_resource('SEND_EMAIL_ICON'),
-            }
-        self.w(self.form_template % ctx)
-
-
-    def _get_allowed_substitutions(self):
-        coltypes = self.rset.column_types(0)
-        attrs = []
-        for coltype in coltypes:
-            eclass = self.vreg.etype_class(coltype)
-            attrs.append(eclass.allowed_massmail_keys())
-        return sorted(reduce(operator.and_, attrs))
-            
-    def _build_recipients_list(self):
-        emails = ((entity.eid, entity.get_email()) for entity in self.rset.entities())
-        checkboxes = (u'<input name="recipient" type="checkbox" value="%s" checked="checked" />%s'
-                      % (eid, html_escape(email)) for eid, email in emails if email)
-        boxes = (u'<div class="recipient">%s</div>' % cbox for cbox in checkboxes)
-        return u'<div id="recipients">%s</div>' % u'\n'.join(boxes)
-            
-
-    def _build_substitutions_help(self):
-        insertLink = u'<a href="javascript: insertText(\'%%(%s)s\', \'emailarea\');">%%(%s)s</a>'
-        substs = (u'<div class="substitution">%s</div>' % (insertLink % (subst, subst))
-                  for subst in self._get_allowed_substitutions())
-        helpmsg = self.req._('You can use any of the following substitutions in your text')
-        return u'<div id="substitutions"><span>%s</span>%s</div>' % (
-            helpmsg, u'\n'.join(substs))
-
-    
+        form = self.vreg.select_object('forms', 'massmailing', self.req, self.rset,
+                                       action='sendmail', domid='sendmail')
+        self.w(form.form_render(sender=from_addr, renderer=MassMailingFormRenderer()))
--- a/web/views/navigation.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/navigation.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """navigation components definition for CubicWeb web client
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -9,11 +9,12 @@
 from rql.nodes import VariableRef, Constant
 
 from logilab.mtconverter import html_escape
+from logilab.common.deprecation import obsolete
 
 from cubicweb.interfaces import IPrevNext
-from cubicweb.common.selectors import (paginated_rset, sorted_rset,
-                                       primary_view, match_context_prop,
-                                       one_line_rset, implement_interface)
+from cubicweb.selectors import (paginated_rset, sorted_rset,
+                                primary_view, match_context_prop,
+                                one_line_rset, implements)
 from cubicweb.common.uilib import cut
 from cubicweb.web.component import EntityVComponent, NavigationComponent
 
@@ -51,7 +52,7 @@
     """sorted navigation apply if navigation is needed (according to page size)
     and if the result set is sorted
     """
-    __selectors__ = (paginated_rset, sorted_rset)
+    __select__ = paginated_rset() & sorted_rset()
     
     # number of considered chars to build page links
     nb_chars = 5
@@ -145,7 +146,7 @@
 
 
 def limit_rset_using_paged_nav(self, req, rset, w, forcedisplay=False,
-                               show_all_option=True, page_size = None):
+                               show_all_option=True, page_size=None):
     showall = forcedisplay or req.form.get('__force_display') is not None
     nav = not showall and self.vreg.select_component('navigation', req, rset,
                                                      page_size=page_size)
@@ -165,9 +166,9 @@
 
 # monkey patch base View class to add a .pagination(req, rset, w, forcedisplay)
 # method to be called on view's result set and printing pages index in the view
-from cubicweb.common.view import View
+from cubicweb.view import View
 # XXX deprecated, use paginate
-View.pagination = limit_rset_using_paged_nav
+View.pagination = obsolete('.pagination is deprecated, use paginate')(limit_rset_using_paged_nav)
 
 def paginate(view, show_all_option=True, w=None):
     limit_rset_using_paged_nav(view, view.req, view.rset, w or view.w,
@@ -180,9 +181,8 @@
     # itself
     title = _('contentnavigation_prevnext')
     help = _('contentnavigation_prevnext_description')
-    __selectors__ = (one_line_rset, primary_view,
-                     match_context_prop, implement_interface)
-    accepts_interfaces = (IPrevNext,)
+    __select__ = (one_line_rset() & primary_view()
+                  & match_context_prop() & implements(IPrevNext))
     context = 'navbottom'
     order = 10
     def call(self, view=None):
--- a/web/views/old_calendar.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,544 +0,0 @@
-"""html calendar views
-
-:organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-
-from mx.DateTime import DateTime, RelativeDateTime, Date, Time, today, Sunday
-
-from logilab.mtconverter import html_escape
-
-from cubicweb.interfaces import ICalendarViews
-from cubicweb.common.utils import date_range
-from cubicweb.common.selectors import implement_interface
-from cubicweb.common.registerers import priority_registerer
-from cubicweb.common.view import EntityView
-
-# Define some useful constants
-ONE_MONTH = RelativeDateTime(months=1)
-TODAY = today()
-THIS_MONTH = TODAY.month
-THIS_YEAR = TODAY.year
-# mx.DateTime and ustrftime could be used to build WEEKDAYS
-WEEKDAYS = [_("monday"), _("tuesday"), _("wednesday"), _("thursday"),
-            _("friday"), _("saturday"), _("sunday")]
-
-# used by i18n tools
-MONTHNAMES = [ _('january'), _('february'), _('march'), _('april'), _('may'),
-               _('june'), _('july'), _('august'), _('september'), _('october'),
-               _('november'), _('december')
-               ]
-
-class _CalendarView(EntityView):
-    """base calendar view containing helpful methods to build calendar views"""
-    __registerer__ = priority_registerer
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (ICalendarViews,)
-    need_navigation = False
-
-    # Navigation building methods / views ####################################
-
-    PREV = u'<a href="%s">&lt;&lt;</a>&nbsp;&nbsp;<a href="%s">&lt;</a>'
-    NEXT = u'<a href="%s">&gt;</a>&nbsp;&nbsp;<a href="%s">&gt;&gt;</a>'
-    NAV_HEADER = u"""<table class="calendarPageHeader">
-<tr><td class="prev">%s</td><td class="next">%s</td></tr>
-</table>
-""" % (PREV, NEXT)
-    
-    def nav_header(self, date, smallshift=3, bigshift=9):
-        """prints shortcut links to go to previous/next steps (month|week)"""
-        prev1 = date - RelativeDateTime(months=smallshift)
-        prev2 = date - RelativeDateTime(months=bigshift)
-        next1 = date + RelativeDateTime(months=smallshift)
-        next2 = date + RelativeDateTime(months=bigshift)
-        rql, vid = self.rset.printable_rql(), self.id
-        return self.NAV_HEADER % (
-            html_escape(self.build_url(rql=rql, vid=vid, year=prev2.year, month=prev2.month)),
-            html_escape(self.build_url(rql=rql, vid=vid, year=prev1.year, month=prev1.month)),
-            html_escape(self.build_url(rql=rql, vid=vid, year=next1.year, month=next1.month)),
-            html_escape(self.build_url(rql=rql, vid=vid, year=next2.year, month=next2.month)))
-        
-    
-    # Calendar building methods ##############################################
-    
-    def build_calendars(self, schedule, begin, end):
-        """build several HTML calendars at once, one for each month
-        between begin and end
-        """
-        return [self.build_calendar(schedule, date)
-                for date in date_range(begin, end, incr=ONE_MONTH)]
-    
-    def build_calendar(self, schedule, first_day):
-        """method responsible for building *one* HTML calendar"""
-        # FIXME  iterates between [first_day-first_day.day_of_week ;
-        #                          last_day+6-last_day.day_of_week]
-        umonth = self.format_date(first_day, '%B %Y') # localized month name
-        rows = []
-        current_row = [NO_CELL] * first_day.day_of_week
-        for daynum in xrange(0, first_day.days_in_month):
-            # build cell day
-            day = first_day + daynum
-            events = schedule.get(day)
-            if events:
-                events = [u'\n'.join(event) for event in events.values()]
-                current_row.append(CELL % (daynum+1, '\n'.join(events)))
-            else:
-                current_row.append(EMPTY_CELL % (daynum+1))
-            # store & reset current row on Sundays
-            if day.day_of_week == Sunday:
-                rows.append(u'<tr>%s%s</tr>' % (WEEKNUM_CELL % day.iso_week[1], ''.join(current_row)))
-                current_row = []
-        current_row.extend([NO_CELL] * (Sunday-day.day_of_week))
-        rql = self.rset.printable_rql()
-        if day.day_of_week != Sunday:
-            rows.append(u'<tr>%s%s</tr>' % (WEEKNUM_CELL % day.iso_week[1], ''.join(current_row)))
-        url = self.build_url(rql=rql, vid='calendarmonth',
-                             year=first_day.year, month=first_day.month)
-        monthlink = u'<a href="%s">%s</a>' % (html_escape(url), umonth)
-        return CALENDAR(self.req) % (monthlink, '\n'.join(rows))
-
-    def _mk_schedule(self, begin, end, itemvid='calendaritem'):
-        """private method that gathers information from resultset
-        and builds calendars according to it
-
-        :param begin: begin of date range
-        :param end: end of date rangs
-        :param itemvid: which view to call to render elements in cells
-
-        returns { day1 : { hour : [views] },
-                  day2 : { hour : [views] } ... }
-        """
-        # put this here since all sub views are calling this method        
-        self.req.add_css('cubicweb.calendar.css') 
-        schedule = {}
-        for row in xrange(len(self.rset.rows)):
-            entity = self.entity(row)
-            infos = u'<div class="event">'
-            infos += self.view(itemvid, self.rset, row=row)
-            infos += u'</div>'
-            for date in entity.matching_dates(begin, end):
-                day = Date(date.year, date.month, date.day)
-                time = Time(date.hour, date.minute, date.second) 
-                schedule.setdefault(day, {})
-                schedule[day].setdefault(time, []).append(infos)
-        return schedule
-        
-
-    @staticmethod
-    def get_date_range(day=TODAY, shift=4):
-        """returns a couple (begin, end)
-
-        <begin> is the first day of current_month - shift
-        <end> is the last day of current_month + (shift+1)
-        """
-        first_day_in_month = DateTime(day.year, day.month, 1)
-        begin = first_day_in_month - RelativeDateTime(months=shift)
-        end = (first_day_in_month + RelativeDateTime(months=shift+1)) - 1
-        return begin, end
-
-
-    def _build_ampm_cells(self, daynum, events):
-        """create a view without any hourly details.
-
-        :param daynum: day of the built cell
-        :param events: dictionnary with all events classified by hours"""
-        # split events according am/pm
-        am_events = [event for e_time, e_list in events.iteritems()
-                     if 0 <= e_time.hour < 12
-                     for event in e_list]
-        pm_events = [event for e_time, e_list in events.iteritems()
-                     if 12 <= e_time.hour < 24
-                     for event in e_list]
-        # format each am/pm cell
-        if am_events:
-            am_content = AMPM_CONTENT % ("amCell", "am", '\n'.join(am_events))
-        else:
-            am_content = AMPM_EMPTY % ("amCell", "am")
-        if pm_events:
-            pm_content = AMPM_CONTENT % ("pmCell", "pm", '\n'.join(pm_events))
-        else:
-            pm_content = AMPM_EMPTY % ("pmCell", "pm")
-        return am_content, pm_content
-
-
-
-class YearCalendarView(_CalendarView):
-    id = 'calendaryear'
-    title = _('calendar (year)')
-
-    def call(self, year=THIS_YEAR, month=THIS_MONTH):
-        """this view renders a 3x3 calendars' table"""
-        year = int(self.req.form.get('year', year))
-        month = int(self.req.form.get('month', month))
-        center_date = DateTime(year, month)
-        begin, end = self.get_date_range(day=center_date)
-        schedule = self._mk_schedule(begin, end)
-        self.w(self.nav_header(center_date))
-        calendars = tuple(self.build_calendars(schedule, begin, end))
-        self.w(SMALL_CALENDARS_PAGE % calendars)
-
-
-class SemesterCalendarView(_CalendarView):
-    """this view renders three semesters as three rows of six columns,
-    one column per month
-    """
-    id = 'calendarsemester'
-    title = _('calendar (semester)')
-
-    def call(self, year=THIS_YEAR, month=THIS_MONTH):
-        year = int(self.req.form.get('year', year))
-        month = int(self.req.form.get('month', month))
-        begin = DateTime(year, month) - RelativeDateTime(months=2)
-        end = DateTime(year, month) + RelativeDateTime(months=3)
-        schedule = self._mk_schedule(begin, end)
-        self.w(self.nav_header(DateTime(year, month), 1, 6))
-        self.w(u'<table class="semesterCalendar">')
-        self.build_calendars(schedule, begin, end)
-        self.w(u'</table>')
-        self.w(self.nav_header(DateTime(year, month), 1, 6))
-
-    def build_calendars(self, schedule, begin, end):
-        self.w(u'<tr>')
-        rql = self.rset.printable_rql()
-        for cur_month in date_range(begin, end, incr=ONE_MONTH):
-            umonth = u'%s&nbsp;%s' % (self.format_date(cur_month, '%B'), cur_month.year)
-            url = self.build_url(rql=rql, vid=self.id,
-                                 year=cur_month.year, month=cur_month.month)
-            self.w(u'<th colspan="2"><a href="%s">%s</a></th>' % (html_escape(url),
-                                                                  umonth))
-        self.w(u'</tr>')
-        _ = self.req._
-        for day_num in xrange(31):
-            self.w(u'<tr>')
-            for cur_month in date_range(begin, end, incr=ONE_MONTH):
-                if day_num >= cur_month.days_in_month:
-                    self.w(u'%s%s' % (NO_CELL, NO_CELL))
-                else:
-                    day = DateTime(cur_month.year, cur_month.month, day_num+1)
-                    events = schedule.get(day)
-                    self.w(u'<td>%s&nbsp;%s</td>\n' % (_(WEEKDAYS[day.day_of_week])[0].upper(), day_num+1))
-                    self.format_day_events(day, events)
-            self.w(u'</tr>')
-            
-    def format_day_events(self, day, events):
-        if events:
-            events = ['\n'.join(event) for event in events.values()]
-            self.w(WEEK_CELL % '\n'.join(events))
-        else:
-            self.w(WEEK_EMPTY_CELL)
-        
-
-class MonthCalendarView(_CalendarView):
-    """this view renders a 3x1 calendars' table"""
-    id = 'calendarmonth'
-    title = _('calendar (month)')
-    
-    def call(self, year=THIS_YEAR, month=THIS_MONTH):
-        year = int(self.req.form.get('year', year))
-        month = int(self.req.form.get('month', month))
-        center_date = DateTime(year, month)
-        begin, end = self.get_date_range(day=center_date, shift=1)
-        schedule = self._mk_schedule(begin, end)
-        calendars = self.build_calendars(schedule, begin, end)
-        self.w(self.nav_header(center_date, 1, 3))
-        self.w(BIG_CALENDARS_PAGE % tuple(calendars))
-        self.w(self.nav_header(center_date, 1, 3))
-
-        
-class WeekCalendarView(_CalendarView):
-    """this view renders a calendar for week events"""
-    id = 'calendarweek'
-    title = _('calendar (week)')
-    
-    def call(self, year=THIS_YEAR, week=TODAY.iso_week[1]):
-        year = int(self.req.form.get('year', year))
-        week = int(self.req.form.get('week', week))
-        day0 = DateTime(year)
-        first_day_of_week = (day0-day0.day_of_week) + 7*week
-        begin, end = first_day_of_week-7, first_day_of_week+14
-        schedule = self._mk_schedule(begin, end, itemvid='calendarlargeitem')
-        self.w(self.nav_header(first_day_of_week))
-        self.w(u'<table class="weekCalendar">')
-        _weeks = [(first_day_of_week-7, first_day_of_week-1),
-                  (first_day_of_week, first_day_of_week+6),
-                  (first_day_of_week+7, first_day_of_week+13)]
-        self.build_calendar(schedule, _weeks)
-        self.w(u'</table>')
-        self.w(self.nav_header(first_day_of_week))
- 
-    def build_calendar(self, schedule, weeks):
-        rql = self.rset.printable_rql()
-        _ = self.req._
-        for monday, sunday in weeks:            
-            umonth = self.format_date(monday, '%B %Y')
-            url = self.build_url(rql=rql, vid='calendarmonth',
-                                 year=monday.year, month=monday.month)
-            monthlink = '<a href="%s">%s</a>' % (html_escape(url), umonth)
-            self.w(u'<tr><th colspan="3">%s %s (%s)</th></tr>' \
-                  % (_('week'), monday.iso_week[1], monthlink))
-            for day in date_range(monday, sunday):
-                self.w(u'<tr>')
-                self.w(u'<td>%s</td>' % _(WEEKDAYS[day.day_of_week]))
-                self.w(u'<td>%s</td>' % (day.strftime('%Y-%m-%d')))
-                events = schedule.get(day)
-                if events:
-                    events = ['\n'.join(event) for event in events.values()]
-                    self.w(WEEK_CELL % '\n'.join(events))
-                else:
-                    self.w(WEEK_EMPTY_CELL)
-                self.w(u'</tr>')
-        
-    def nav_header(self, date, smallshift=1, bigshift=3):
-        """prints shortcut links to go to previous/next steps (month|week)"""
-        prev1 = date - RelativeDateTime(weeks=smallshift)
-        prev2 = date - RelativeDateTime(weeks=bigshift)
-        next1 = date + RelativeDateTime(weeks=smallshift)
-        next2 = date + RelativeDateTime(weeks=bigshift)
-        rql, vid = self.rset.printable_rql(), self.id
-        return self.NAV_HEADER % (
-            html_escape(self.build_url(rql=rql, vid=vid, year=prev2.year, week=prev2.iso_week[1])),
-            html_escape(self.build_url(rql=rql, vid=vid, year=prev1.year, week=prev1.iso_week[1])),
-            html_escape(self.build_url(rql=rql, vid=vid, year=next1.year, week=next1.iso_week[1])),
-            html_escape(self.build_url(rql=rql, vid=vid, year=next2.year, week=next2.iso_week[1])))
-
-
-        
-class AMPMYearCalendarView(YearCalendarView):
-    id = 'ampmcalendaryear'
-    title = _('am/pm calendar (year)')
-    
-    def build_calendar(self, schedule, first_day):
-        """method responsible for building *one* HTML calendar"""
-        umonth = self.format_date(first_day, '%B %Y') # localized month name
-        rows = [] # each row is: (am,pm), (am,pm) ... week_title
-        current_row = [(NO_CELL, NO_CELL, NO_CELL)] * first_day.day_of_week
-        rql = self.rset.printable_rql()
-        for daynum in xrange(0, first_day.days_in_month):
-            # build cells day
-            day = first_day + daynum
-            events = schedule.get(day)
-            if events:
-                current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(daynum, events))
-            else:
-                current_row.append((AMPM_DAY % (daynum+1),
-                                    AMPM_EMPTY % ("amCell", "am"),
-                                    AMPM_EMPTY % ("pmCell", "pm")))
-            # store & reset current row on Sundays
-            if day.day_of_week == Sunday:
-                url = self.build_url(rql=rql, vid='ampmcalendarweek',
-                                     year=day.year, week=day.iso_week[1])
-                weeklink = '<a href="%s">%s</a>' % (html_escape(url),
-                                                    day.iso_week[1])
-                current_row.append(WEEKNUM_CELL % weeklink)
-                rows.append(current_row)
-                current_row = []
-        current_row.extend([(NO_CELL, NO_CELL, NO_CELL)] * (Sunday-day.day_of_week))
-        url = self.build_url(rql=rql, vid='ampmcalendarweek',
-                             year=day.year, week=day.iso_week[1])
-        weeklink = '<a href="%s">%s</a>' % (html_escape(url), day.iso_week[1])
-        current_row.append(WEEKNUM_CELL % weeklink)
-        rows.append(current_row)
-        # build two rows for each week: am & pm
-        formatted_rows = []
-        for row in rows:
-            week_title = row.pop()
-            day_row = [day for day, am, pm in row]
-            am_row = [am for day, am, pm in row]
-            pm_row = [pm for day, am, pm in row]
-            formatted_rows.append('<tr>%s%s</tr>'% (week_title, '\n'.join(day_row)))
-            formatted_rows.append('<tr class="amRow"><td>&nbsp;</td>%s</tr>'% '\n'.join(am_row))
-            formatted_rows.append('<tr class="pmRow"><td>&nbsp;</td>%s</tr>'% '\n'.join(pm_row))
-        # tigh everything together
-        url = self.build_url(rql=rql, vid='ampmcalendarmonth',
-                             year=first_day.year, month=first_day.month)
-        monthlink = '<a href="%s">%s</a>' % (html_escape(url), umonth)
-        return CALENDAR(self.req) % (monthlink, '\n'.join(formatted_rows))
-        
-
-
-class AMPMSemesterCalendarView(SemesterCalendarView):
-    """this view renders a 3x1 calendars' table"""
-    id = 'ampmcalendarsemester'
-    title = _('am/pm calendar (semester)')
-
-    def build_calendars(self, schedule, begin, end):
-        self.w(u'<tr>')
-        rql = self.rset.printable_rql()
-        for cur_month in date_range(begin, end, incr=ONE_MONTH):
-            umonth = u'%s&nbsp;%s' % (self.format_date(cur_month, '%B'), cur_month.year)
-            url = self.build_url(rql=rql, vid=self.id,
-                                 year=cur_month.year, month=cur_month.month)
-            self.w(u'<th colspan="3"><a href="%s">%s</a></th>' % (html_escape(url),
-                                                                  umonth))
-        self.w(u'</tr>')
-        _ = self.req._
-        for day_num in xrange(31):
-            self.w(u'<tr>')
-            for cur_month in date_range(begin, end, incr=ONE_MONTH):
-                if day_num >= cur_month.days_in_month:
-                    self.w(u'%s%s%s' % (NO_CELL, NO_CELL, NO_CELL))
-                else:
-                    day = DateTime(cur_month.year, cur_month.month, day_num+1)
-                    events = schedule.get(day)
-                    self.w(u'<td>%s&nbsp;%s</td>\n' % (_(WEEKDAYS[day.day_of_week])[0].upper(),
-                                                       day_num+1))
-                    self.format_day_events(day, events)
-            self.w(u'</tr>')
-    
-    def format_day_events(self, day, events):
-        if events:
-            self.w(u'\n'.join(self._build_ampm_cells(day, events)))
-        else:
-            self.w(u'%s %s'% (AMPM_EMPTY % ("amCell", "am"), 
-                              AMPM_EMPTY % ("pmCell", "pm")))
-
-
-class AMPMMonthCalendarView(MonthCalendarView):
-    """this view renders a 3x1 calendars' table"""
-    id = 'ampmcalendarmonth'
-    title = _('am/pm calendar (month)')
-
-    def build_calendar(self, schedule, first_day):
-        """method responsible for building *one* HTML calendar"""
-        umonth = self.format_date(first_day, '%B %Y') # localized month name
-        rows = [] # each row is: (am,pm), (am,pm) ... week_title
-        current_row = [(NO_CELL, NO_CELL, NO_CELL)] * first_day.day_of_week
-        rql = self.rset.printable_rql()
-        for daynum in xrange(0, first_day.days_in_month):
-            # build cells day
-            day = first_day + daynum
-            events = schedule.get(day)
-            if events:
-                current_row.append((AMPM_DAY % (daynum+1),) + self._build_ampm_cells(daynum, events))
-            else:
-                current_row.append((AMPM_DAY % (daynum+1),
-                                    AMPM_EMPTY % ("amCell", "am"),
-                                    AMPM_EMPTY % ("pmCell", "pm")))
-            # store & reset current row on Sundays
-            if day.day_of_week == Sunday:
-                url = self.build_url(rql=rql, vid='ampmcalendarweek',
-                                     year=day.year, week=day.iso_week[1])
-                weeklink = '<a href="%s">%s</a>' % (html_escape(url),
-                                                    day.iso_week[1])
-                current_row.append(WEEKNUM_CELL % weeklink)
-                rows.append(current_row)
-                current_row = []
-        current_row.extend([(NO_CELL, NO_CELL, NO_CELL)] * (Sunday-day.day_of_week))
-        url = self.build_url(rql=rql, vid='ampmcalendarweek',
-                             year=day.year, week=day.iso_week[1])
-        weeklink = '<a href="%s">%s</a>' % (html_escape(url),
-                                            day.iso_week[1])
-        current_row.append(WEEKNUM_CELL % weeklink)
-        rows.append(current_row)
-        # build two rows for each week: am & pm
-        formatted_rows = []
-        for row in rows:
-            week_title = row.pop()
-            day_row = [day for day, am, pm in row]
-            am_row = [am for day, am, pm in row]
-            pm_row = [pm for day, am, pm in row]
-            formatted_rows.append('<tr>%s%s</tr>'% (week_title, '\n'.join(day_row)))
-            formatted_rows.append('<tr class="amRow"><td>&nbsp;</td>%s</tr>'% '\n'.join(am_row))
-            formatted_rows.append('<tr class="pmRow"><td>&nbsp;</td>%s</tr>'% '\n'.join(pm_row))
-        # tigh everything together
-        url = self.build_url(rql=rql, vid='ampmcalendarmonth',
-                             year=first_day.year, month=first_day.month)
-        monthlink = '<a href="%s">%s</a>' % (html_escape(url),
-                                             umonth)
-        return CALENDAR(self.req) % (monthlink, '\n'.join(formatted_rows))      
-    
-
-    
-class AMPMWeekCalendarView(WeekCalendarView):
-    """this view renders a 3x1 calendars' table"""
-    id = 'ampmcalendarweek'
-    title = _('am/pm calendar (week)')
-
-    def build_calendar(self, schedule, weeks):
-        rql = self.rset.printable_rql()
-        w = self.w
-        _ = self.req._
-        for monday, sunday in weeks:
-            umonth = self.format_date(monday, '%B %Y')
-            url = self.build_url(rql=rql, vid='ampmcalendarmonth',
-                                 year=monday.year, month=monday.month)
-            monthlink = '<a href="%s">%s</a>' % (html_escape(url), umonth)
-            w(u'<tr>%s</tr>' % (
-                WEEK_TITLE % (_('week'), monday.iso_week[1], monthlink)))
-            w(u'<tr><th>%s</th><th>&nbsp;</th></tr>'% _(u'Date'))
-            for day in date_range(monday, sunday):
-                events = schedule.get(day)
-                style = day.day_of_week % 2 and "even" or "odd"
-                w(u'<tr class="%s">' % style)
-                if events:
-                    hours = events.keys()
-                    hours.sort()
-                    w(AMPM_DAYWEEK % (
-                        len(hours), _(WEEKDAYS[day.day_of_week]),
-                        self.format_date(day)))
-                    w(AMPM_WEEK_CELL % (
-                        hours[0].hour, hours[0].minute,
-                        '\n'.join(events[hours[0]])))
-                    w(u'</tr>')
-                    for hour in hours[1:]:
-                        w(u'<tr class="%s">%s</tr>'% (
-                            style, AMPM_WEEK_CELL % (hour.hour, hour.minute,
-                                                     '\n'.join(events[hour]))))
-                else:
-                    w(AMPM_DAYWEEK_EMPTY % (
-                        _(WEEKDAYS[day.day_of_week]),
-                        self.format_date(day)))
-                    w(WEEK_EMPTY_CELL)
-                    w(u'</tr>')
-
-
-SMALL_CALENDARS_PAGE = u"""<table class="smallCalendars">
-<tr><td class="calendar">%s</td><td class="calendar">%s</td><td class="calendar">%s</td></tr>
-<tr><td class="calendar">%s</td><td class="calendar">%s</td><td class="calendar">%s</td></tr>
-<tr><td class="calendar">%s</td><td class="calendar">%s</td><td class="calendar">%s</td></tr>
-</table>
-"""
-
-BIG_CALENDARS_PAGE = u"""<table class="bigCalendars">
-<tr><td class="calendar">%s</td></tr>
-<tr><td class="calendar">%s</td></tr>
-<tr><td class="calendar">%s</td></tr>
-</table>
-"""
-
-WEEKNUM_CELL = u'<td class="weeknum">%s</td>'
-
-def CALENDAR(req):
-    _ = req._
-    WEEKNUM_HEADER = u'<th class="weeknum">%s</th>' % _('week')
-    CAL_HEADER = WEEKNUM_HEADER + u' \n'.join([u'<th class="weekday">%s</th>' % _(day)[0].upper()
-                                               for day in WEEKDAYS])
-    return u"""<table>
-<tr><th class="month" colspan="8">%%s</th></tr>
-<tr>
-  %s
-</tr>
-%%s
-</table>
-""" % (CAL_HEADER,)
-
-
-DAY_TEMPLATE = """<tr><td class="weekday">%(daylabel)s</td><td>%(dmydate)s</td><td>%(dayschedule)s</td>
-"""
-
-NO_CELL = u'<td class="noday"></td>'
-EMPTY_CELL = u'<td class="cellEmpty"><span class="cellTitle">%s</span></td>'
-CELL = u'<td class="cell"><span class="cellTitle">%s</span><div class="cellContent">%s</div></td>'
-
-AMPM_DAY = u'<td class="cellDay">%d</td>'
-AMPM_EMPTY = u'<td class="%sEmpty"><span class="cellTitle">%s</span></td>'
-AMPM_CONTENT = u'<td class="%s"><span class="cellTitle">%s</span><div class="cellContent">%s</div></td>'
-
-WEEK_TITLE = u'<th class="weekTitle" colspan="2">%s %s (%s)</th>'
-WEEK_EMPTY_CELL = u'<td class="weekEmptyCell">&nbsp;</td>'
-WEEK_CELL = u'<td class="weekCell"><div class="cellContent">%s</div></td>'
-
-AMPM_DAYWEEK_EMPTY = u'<td>%s&nbsp;%s</td>'
-AMPM_DAYWEEK = u'<td rowspan="%d">%s&nbsp;%s</td>'
-AMPM_WEEK_CELL = u'<td class="ampmWeekCell"><div class="cellContent">%02d:%02d - %s</div></td>'
--- a/web/views/owl.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/owl.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,16 @@
+"""produces some Ontology Web Language schema and views
+
+:organization: Logilab
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
 from logilab.mtconverter import TransformError, xml_escape
 
-from cubicweb.common.view import StartupView
-from cubicweb.common.view import EntityView
+from cubicweb.view import StartupView, EntityView
+from cubicweb.web.action import Action
+from cubicweb.selectors import none_rset, match_view
 
 _ = unicode
 
@@ -155,7 +164,6 @@
     id = 'owlabox'
     title = _('owlabox')
     templatable = False
-    accepts = ('Any',)
     content_type = 'application/xml' # 'text/xml'
     
     def call(self):
@@ -169,11 +177,9 @@
 
         
 class OWLABOXItemView(EntityView):
-    '''This view represents a part of the ABOX for a given entity.'''
-    
+    '''This view represents a part of the ABOX for a given entity.'''    
     id = 'owlaboxitem'
     templatable = False
-    accepts = ('Any',)
     content_type = 'application/xml' # 'text/xml'
 
     def cell_call(self, row, col, skiprels=DEFAULT_SKIP_RELS):
@@ -209,3 +215,14 @@
                 self.w(u'<%s>%s %s</%s>' % (attr, x.id, x.eid, attr))
         self.w(u'</%s>'% eschema)
 
+
+class DownloadOWLSchemaAction(Action):
+    id = 'download_as_owl'
+    __select__ = none_rset() & match_view('schema')
+    
+    category = 'mainactions'
+    title = _('download schema as owl')
+   
+    def url(self):
+        return self.build_url('view', vid='owl')
+
--- a/web/views/plots.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/plots.py	Mon May 04 13:09:48 2009 +0200
@@ -2,8 +2,10 @@
 
 from logilab.common import flatten
 
+from cubicweb.vregistry import objectify_selector
 from cubicweb.web.views import baseviews
 
+@objectify_selector
 def plot_selector(cls, req, rset, *args, **kwargs):
     """accept result set with at least one line and two columns of result
     all columns after second must be of numerical types"""
@@ -23,8 +25,7 @@
     import sys
     if 'matplotlib.backends' not in sys.modules:
         matplotlib.use('Agg')
-    from matplotlib.ticker import FormatStrFormatter
-    from pylab import figure, show
+    from pylab import figure
 except ImportError:
     pass
 else:
@@ -34,7 +35,7 @@
         binary = True
         content_type = 'image/png'
         _plot_count = 0
-        __selectors__ = (plot_selector,)
+        __select__ = plot_selector()
 
         def call(self, width=None, height=None):
             # compute dimensions
@@ -55,7 +56,7 @@
             abscisses = [row[0] for row in self.rset]
             courbes = []
             nbcols = len(self.rset.rows[0])
-            for col in range(1,nbcols):
+            for col in xrange(1, nbcols):
                 courbe = [row[col] for row in self.rset]
                 courbes.append(courbe)
             if not courbes:
@@ -70,7 +71,7 @@
             except ValueError:
                 xlabels = abscisses
                 abscisses = range(len(xlabels))
-            for idx,courbe in enumerate(courbes):
+            for idx, courbe in enumerate(courbes):
                 ax.plot(abscisses, courbe, '%sv-' % colors[idx], label=self.rset.description[0][idx+1])
             ax.autoscale_view()
             alldata = flatten(courbes)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/primary.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,279 @@
+"""The default primary view
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from warnings import warn
+
+from logilab.mtconverter import html_escape
+
+from cubicweb import Unauthorized
+from cubicweb.view import EntityView
+from cubicweb.web.uicfg import rdisplay
+
+_ = unicode
+
+
+class PrimaryView(EntityView):
+    """the full view of an non final entity"""
+    id = 'primary'
+    title = _('primary')
+    show_attr_label = True
+    show_rel_label = True
+    skip_none = True
+    rdisplay = rdisplay
+    main_related_section = True
+
+    @classmethod
+    def vreg_initialization_completed(cls):
+        """set default category tags for relations where it's not yet defined in
+        the category relation tags
+        """
+        for eschema in cls.schema.entities():
+            for rschema, tschemas, role in eschema.relation_definitions(True):
+                for tschema in tschemas:
+                    if role == 'subject':
+                        X, Y = eschema, tschema
+                        card = rschema.rproperty(X, Y, 'cardinality')[0]
+                        composed = rschema.rproperty(X, Y, 'composite') == 'object'
+                    else:
+                        X, Y = tschema, eschema
+                        card = rschema.rproperty(X, Y, 'cardinality')[1]
+                        composed = rschema.rproperty(X, Y, 'composite') == 'subject'
+                    displayinfo = cls.rdisplay.get(rschema, role, X, Y)
+                    if displayinfo is None:
+                        if rschema.is_final():
+                            if rschema.meta or tschema.type in ('Password', 'Bytes'):
+                                where = None
+                            else:
+                                where = 'attributes'
+                        elif card in '1+':
+                            where = 'attributes'
+                        elif composed:
+                            where = 'relations'
+                        else:
+                            where = 'sideboxes'
+                        displayinfo = {'where': where,
+                                       'order': cls.rdisplay.get_timestamp()}
+                        cls.rdisplay.tag_relation(displayinfo, (X, rschema, Y),
+                                                  role)
+                    if role == 'subject':
+                        displayinfo.setdefault('label', rschema.type)
+                    else:
+                        displayinfo.setdefault('label', '%s_%s' % (rschema, role))
+
+    def html_headers(self):
+        """return a list of html headers (eg something to be inserted between
+        <head> and </head> of the returned page
+
+        by default primary views are indexed
+        """
+        return []
+
+    def cell_call(self, row, col):
+        self.row = row
+        # XXX move render_entity implementation here
+        self.render_entity(self.complete_entity(row, col))
+        self.maxrelated = self.req.property_value('navigation.related-limit')
+
+    def render_entity(self, entity):
+        """return html to display the given entity"""
+        self.render_entity_title(entity)
+        self.render_entity_metadata(entity)
+        # entity's attributes and relations, excluding meta data
+        # if the entity isn't meta itself
+        boxes = self._prepare_side_boxes(entity)
+        if boxes or hasattr(self, 'render_side_related'):
+            self.w(u'<table width="100%"><tr><td style="width: 75%">')
+        self.w(u'<div>')
+        self.w(u'<div class="mainInfo">')
+        try:
+            self.render_entity_attributes(entity)
+        except TypeError: # XXX bw compat
+            warn('siderelations argument of render_entity_attributes is '
+                 'deprecated (%s)' % self.__class__)
+            self.render_entity_attributes(entity, [])
+        self.w(u'</div>')
+        self.content_navigation_components('navcontenttop')
+        if self.main_related_section:
+            try:
+                self.render_entity_relations(entity)
+            except TypeError: # XXX bw compat
+                warn('siderelations argument of render_entity_relations is '
+                     'deprecated')
+                self.render_entity_relations(entity, [])
+        self.w(u'</div>')
+        if boxes or hasattr(self, 'render_side_related'):
+            self.w(u'</td><td>')
+            # side boxes
+            self.w(u'<div class="primaryRight">')
+            if hasattr(self, 'render_side_related'):
+                warn('render_side_related is deprecated')
+                self.render_side_related(entity, [])
+            self.render_side_boxes(boxes)
+            self.w(u'</div>')
+            self.w(u'</td></tr></table>')
+        self.content_navigation_components('navcontentbottom')
+
+
+    def content_navigation_components(self, context):
+        self.w(u'<div class="%s">' % context)
+        for comp in self.vreg.possible_vobjects('contentnavigation',
+                                                self.req, self.rset, row=self.row,
+                                                view=self, context=context):
+            try:
+                comp.dispatch(w=self.w, row=self.row, view=self)
+            except NotImplementedError:
+                warn('component %s doesnt implement cell_call, please update'
+                     % comp.__class__, DeprecationWarning)
+                comp.dispatch(w=self.w, view=self)
+        self.w(u'</div>')
+
+    def render_entity_title(self, entity):
+        title = self.content_title(entity) # deprecate content_title?
+        if title:
+            self.w(u'<h1><span class="etype">%s</span> %s</h1>'
+                   % (entity.dc_type().capitalize(), title))
+
+
+    def content_title(self, entity):
+        """default implementation return dc_title"""
+        return html_escape(entity.dc_title())
+
+    def render_entity_metadata(self, entity):
+        entity.view('metadata', w=self.w)
+        summary = self.summary(entity) # deprecate summary?
+        if summary:
+            self.w(u'<div class="summary">%s</div>' % summary)
+
+    def summary(self, entity):
+        """default implementation return an empty string"""
+        return u''
+
+    def render_entity_attributes(self, entity, siderelations=None):
+        for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'attributes'):
+            vid =  displayinfo.get('vid', 'reledit')
+            if rschema.is_final() or vid == 'reledit':
+                value = entity.view(vid, rtype=rschema.type, role=role)
+            else:
+                rset = self._relation_rset(entity, rschema, role, displayinfo)
+                if rset:
+                    value = self.view(vid, rset)
+                else:
+                    value = None
+            if self.skip_none and (value is None or value == ''):
+                continue
+            self._render_attribute(rschema, value)
+
+    def render_entity_relations(self, entity, siderelations=None):
+        for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'relations'):
+            rset = self._relation_rset(entity, rschema, role, displayinfo)
+            if rset:
+                self._render_relation(rset, displayinfo, 'autolimited',
+                                      self.show_rel_label)
+
+    def render_side_boxes(self, boxes):
+        """display side related relations:
+        non-meta in a first step, meta in a second step
+        """
+        for box in boxes:
+            if isinstance(box, tuple):
+                label, rset, vid, _  = box
+                self.w(u'<div class="sideRelated">')
+                self.wview(vid, rset, title=label)
+                self.w(u'</div>')
+            else:
+                try:
+                    box.dispatch(w=self.w, row=self.row)
+                except NotImplementedError:
+                    # much probably a context insensitive box, which only implements
+                    # .call() and not cell_call()
+                    box.dispatch(w=self.w)
+
+    def _prepare_side_boxes(self, entity):
+        sideboxes = []
+        for rschema, tschemas, role, displayinfo in self._iter_display(entity, 'sideboxes'):
+            rset = self._relation_rset(entity, rschema, role, displayinfo)
+            if not rset:
+                continue
+            label = display_name(self.req, rschema.type, role)
+            vid = displayinfo.get('vid', 'autolimited')
+            sideboxes.append((label, rset, vid, displayinfo.get('order')))
+        sideboxes = sorted(sideboxes, key=lambda x: x[-1])
+        sideboxes += list(self.vreg.possible_vobjects('boxes', self.req, self.rset,
+                                                      row=self.row, view=self,
+                                                      context='incontext'))
+        return sideboxes
+
+    def _iter_display(self, entity, where):
+        eschema = entity.e_schema
+        for rschema, tschemas, role in eschema.relation_definitions(True):
+            matchtschemas = []
+            for tschema in tschemas:
+                displayinfo = self.rdisplay.etype_get(eschema, rschema, role,
+                                                      tschema)
+                assert displayinfo is not None, (str(rschema), role,
+                                                 str(eschema), str(tschema))
+                if displayinfo.get('where') == where:
+                    matchtschemas.append(tschema)
+            if matchtschemas:
+                # XXX pick the latest displayinfo
+                yield rschema, matchtschemas, role, displayinfo
+
+    def _relation_rset(self, entity, rschema, role, displayinfo):
+        try:
+            if displayinfo.get('limit'):
+                rset = entity.related(rschema.type, role,
+                                      limit=self.maxrelated+1)
+            else:
+                rset = entity.related(rschema.type, role)
+        except Unauthorized:
+            return
+        if 'filter' in displayinfo:
+            rset = displayinfo['filter'](rset)
+        return rset
+
+    def _render_relation(self, rset, displayinfo, defaultvid, showlabel):
+        self.w(u'<div class="section">')
+        if showlabel:
+            self.w(u'<h4>%s</h4>' % self.req._(displayinfo['label']))
+        self.wview(displayinfo.get('vid', defaultvid), rset)
+        self.w(u'</div>')
+
+    def _render_attribute(self, rschema, value, role='subject'):
+        if rschema.is_final():
+            show_label = self.show_attr_label
+        else:
+            show_label = self.show_rel_label
+        label = display_name(self.req, rschema.type, role)
+        self.field(label, value, show_label=show_label, tr=False)
+
+
+class RelatedView(EntityView):
+    id = 'autolimited'
+    def call(self, title=None, **kwargs):
+        # if not too many entities, show them all in a list
+        maxrelated = self.req.property_value('navigation.related-limit')
+        if title:
+            self.w(u'<div class="title"><span>%s</span></div>' % title)
+        if self.rset.rowcount <= maxrelated:
+            if self.rset.rowcount == 1:
+                self.wview('incontext', self.rset, row=0)
+            elif 1 < self.rset.rowcount <= 5:
+                self.wview('csv', self.rset)
+            else:
+                self.w(u'<div>')
+                self.wview('simplelist', self.rset)
+                self.w(u'</div>')
+        # else show links to display related entities
+        else:
+            rql = self.rset.printable_rql()
+            self.rset.limit(maxrelated)
+            self.w(u'<div>')
+            self.wview('simplelist', self.rset)
+            self.w(u'[<a href="%s">%s</a>]' % (self.build_url(rql=rql),
+                                               self.req._('see them all')))
+            self.w(u'</div>')
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/schema.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,231 @@
+"""Specific views for schema related entities
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from itertools import cycle
+
+from logilab.mtconverter import html_escape
+from yams import schema2dot as s2d
+
+from cubicweb.selectors import implements, yes
+from cubicweb.schemaviewer import SchemaViewer
+from cubicweb.view import EntityView, StartupView
+from cubicweb.common import tags, uilib
+from cubicweb.web import uicfg, formwidgets, action
+from cubicweb.web.views import TmpFileViewMixin, primary, baseviews
+
+
+uicfg.rcategories.tag_relation('primary', ('CWPermission', 'require_group', '*'), 'subject')
+uicfg.rcategories.tag_attribute('generated', 'EEtype', 'final')
+uicfg.rcategories.tag_attribute('generated', 'ERtype', 'final')
+uicfg.rinlined.tag_relation(True, ('CWRelation', 'relation_type', '*'), 'subject')
+uicfg.rinlined.tag_relation(True, ('CWRelation', 'from_entity', '*'), 'subject')
+uicfg.rinlined.tag_relation(True, ('CWRelation', 'to_entity', '*'), 'subject')
+uicfg.rwidgets.tag_attribute(formwidgets.TextInput, 'RQLExpression', 'expression')
+
+uicfg.rmode.tag_relation('create', ('*', 'state_of', 'CWEType'), 'object')
+uicfg.rmode.tag_relation('create', ('*', 'transition_of', 'CWEType'), 'object')
+uicfg.rmode.tag_relation('create', ('*', 'relation_type', 'CWRType'), 'object')
+uicfg.rmode.tag_relation('link', ('*', 'from_entity', 'CWEType'), 'object')
+uicfg.rmode.tag_relation('link', ('*', 'to_entity', 'CWEType'), 'object')
+
+for attr in ('name', 'meta', 'final'):
+    uicfg.rdisplay.tag_attribute({}, 'CWRType', attr)
+for attr in ('name', 'meta', 'final', 'symetric', 'inlined'):
+    uicfg.rdisplay.tag_attribute({}, 'CWRType', attr)
+
+
+class ViewSchemaAction(action.Action):
+    id = 'schema'
+    __select__ = yes()
+
+    title = _("site schema")
+    category = 'siteactions'
+    order = 30
+
+    def url(self):
+        return self.build_url(self.id)
+
+
+# schema entity types views ###################################################
+
+class CWRDEFPrimaryView(primary.PrimaryView):
+    __select__ = implements('CWAttribute', 'CWRelation')
+    cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
+
+    def content_title(self, entity):
+        return html_escape(entity.dc_long_title())
+
+
+class CWETypeOneLineView(baseviews.OneLineView):
+    __select__ = implements('CWEType')
+
+    def cell_call(self, row, col, **kwargs):
+        entity = self.entity(row, col)
+        final = entity.final
+        if final:
+            self.w(u'<em class="finalentity">')
+        super(CWETypeOneLineView, self).cell_call(row, col, **kwargs)
+        if final:
+            self.w(u'</em>')
+
+
+# in memory schema views (yams class instances) ###############################
+SKIPPED_RELS = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by',
+                'has_text',)
+
+class CWETypeSchemaView(primary.PrimaryView):
+    id = 'eschema'
+    __select__ = implements('CWEType')
+    title = _('in memory entity schema')
+    main_related_section = False
+    skip_rels = SKIPPED_RELS
+
+    def render_entity_attributes(self, entity):
+        super(CWETypeSchemaView, self).render_entity_attributes(entity)
+        eschema = self.vreg.schema.eschema(entity.name)
+        viewer = SchemaViewer(self.req)
+        layout = viewer.visit_entityschema(eschema, skiprels=self.skip_rels)
+        self.w(uilib.ureport_as_html(layout))
+        if not eschema.is_final():
+            msg = self.req._('graphical schema for %s') % entity.name
+            self.w(tags.img(src=entity.absolute_url(vid='eschemagraph'),
+                            alt=msg))
+
+
+class CWRTypeSchemaView(primary.PrimaryView):
+    id = 'eschema'
+    __select__ = implements('CWRType')
+    title = _('in memory relation schema')
+    main_related_section = False
+
+    def render_entity_attributes(self, entity):
+        super(CWRTypeSchemaView, self).render_entity_attributes(entity)
+        rschema = self.vreg.schema.rschema(entity.name)
+        viewer = SchemaViewer(self.req)
+        layout = viewer.visit_relationschema(rschema)
+        self.w(uilib.ureport_as_html(layout))
+        if not rschema.is_final():
+            msg = self.req._('graphical schema for %s') % entity.name
+            self.w(tags.img(src=entity.absolute_url(vid='eschemagraph'),
+                            alt=msg))
+
+
+# schema images ###############################################################
+
+class ImageView(EntityView):
+    __select__ = implements('CWEType')
+    id = 'image'
+    title = _('image')
+
+    def cell_call(self, row, col):
+        entity = self.entity(row, col)
+        url = entity.absolute_url(vid='eschemagraph')
+        self.w(u'<img src="%s" alt="%s"/>' % (
+            html_escape(url),
+            html_escape(self.req._('graphical schema for %s') % entity.name)))
+
+
+class RestrictedSchemaDotPropsHandler(s2d.SchemaDotPropsHandler):
+    def __init__(self, req):
+        # FIXME: colors are arbitrary
+        self.nextcolor = cycle( ('#aa0000', '#00aa00', '#0000aa',
+                                 '#000000', '#888888') ).next
+        self.req = req
+
+    def display_attr(self, rschema):
+        return not rschema.meta and (rschema.has_local_role('read')
+                                     or rschema.has_perm(self.req, 'read'))
+
+    # XXX remove this method once yams > 0.20 is out
+    def node_properties(self, eschema):
+        """return default DOT drawing options for an entity schema"""
+        label = ['{', eschema.type, '|']
+        label.append(r'\l'.join(rel.type for rel in eschema.subject_relations()
+                                if rel.final and self.display_attr(rel)))
+        label.append(r'\l}') # trailing \l ensure alignement of the last one
+        return {'label' : ''.join(label), 'shape' : "record",
+                'fontname' : "Courier", 'style' : "filled"}
+
+    def edge_properties(self, rschema, subjnode, objnode):
+        kwargs = super(RestrictedSchemaDotPropsHandler, self).edge_properties(rschema, subjnode, objnode)
+        # symetric rels are handled differently, let yams decide what's best
+        if not rschema.symetric:
+            kwargs['color'] = self.nextcolor()
+        kwargs['fontcolor'] = kwargs['color']
+        # dot label decoration is just awful (1 line underlining the label
+        # + 1 line going to the closest edge spline point)
+        kwargs['decorate'] = 'false'
+        return kwargs
+
+
+class RestrictedSchemaVisitorMiIn:
+    def __init__(self, req, *args, **kwargs):
+        # hack hack hack
+        assert len(self.__class__.__bases__) == 2
+        self.__parent = self.__class__.__bases__[1]
+        self.__parent.__init__(self, *args, **kwargs)
+        self.req = req
+
+    def nodes(self):
+        for etype, eschema in self.__parent.nodes(self):
+            if eschema.has_local_role('read') or eschema.has_perm(self.req, 'read'):
+                yield eschema.type, eschema
+
+    def edges(self):
+        for setype, oetype, rschema in self.__parent.edges(self):
+            if rschema.has_local_role('read') or rschema.has_perm(self.req, 'read'):
+                yield setype, oetype, rschema
+
+
+class FullSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.FullSchemaVisitor):
+    pass
+
+class OneHopESchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopESchemaVisitor):
+    pass
+
+class OneHopRSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopRSchemaVisitor):
+    pass
+
+
+class SchemaImageView(TmpFileViewMixin, StartupView):
+    id = 'schemagraph'
+
+    content_type = 'image/png'
+    skip_rels = SKIPPED_RELS
+    def _generate(self, tmpfile):
+        """display global schema information"""
+        skipmeta = not int(self.req.form.get('withmeta', 0))
+        visitor = FullSchemaVisitor(self.req, self.schema, skiprels=self.skip_rels, skipmeta=skipmeta)
+        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
+                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
+
+class CWETypeSchemaImageView(TmpFileViewMixin, EntityView):
+    id = 'eschemagraph'
+    __select__ = implements('CWEType')
+
+    content_type = 'image/png'
+    skip_rels = SKIPPED_RELS
+
+    def _generate(self, tmpfile):
+        """display schema information for an entity"""
+        entity = self.entity(self.row, self.col)
+        eschema = self.vreg.schema.eschema(entity.name)
+        visitor = OneHopESchemaVisitor(self.req, eschema, skiprels=self.skip_rels)
+        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
+                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
+
+class CWRTypeSchemaImageView(CWETypeSchemaImageView):
+    __select__ = implements('CWRType')
+
+    def _generate(self, tmpfile):
+        """display schema information for an entity"""
+        entity = self.entity(self.row, self.col)
+        rschema = self.vreg.schema.rschema(entity.name)
+        visitor = OneHopRSchemaVisitor(self.req, rschema)
+        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
+                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
--- a/web/views/schemaentities.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-"""Specific views for schema related entities
-
-:organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-from logilab.mtconverter import html_escape
-
-from cubicweb.schemaviewer import SchemaViewer
-from cubicweb.common.uilib import ureport_as_html
-from cubicweb.common.view import EntityView
-from cubicweb.web.views import baseviews
-
-
-class ImageView(EntityView):
-    accepts = ('EEType',)
-    id = 'image'
-    title = _('image')
-
-    def cell_call(self, row, col):
-        entity = self.entity(row, col)
-        url = entity.absolute_url(vid='eschemagraph')
-        self.w(u'<img src="%s" alt="%s"/>' % (
-            html_escape(url),
-            html_escape(self.req._('graphical schema for %s') % entity.name)))
-
-
-class _SchemaEntityPrimaryView(baseviews.PrimaryView):
-    show_attr_label = False
-    cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
-    
-    def content_title(self, entity):
-        return html_escape(entity.dc_long_title())
-    
-class EETypePrimaryView(_SchemaEntityPrimaryView):
-    accepts = ('EEType',)
-    skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final')
-
-class ERTypePrimaryView(_SchemaEntityPrimaryView):
-    accepts = ('ERType',)
-    skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final',
-                                                        'symetric', 'inlined')
-
-class ErdefPrimaryView(_SchemaEntityPrimaryView):
-    accepts = ('EFRDef', 'ENFRDef')
-    show_attr_label = True
-
-class EETypeSchemaView(EETypePrimaryView):
-    id = 'eschema'
-    title = _('in memory entity schema')
-    main_related_section = False
-    skip_rels = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by',
-                 'has_text',)
-    
-    def render_entity_attributes(self, entity, siderelations):
-        super(EETypeSchemaView, self).render_entity_attributes(entity, siderelations)
-        eschema = self.vreg.schema.eschema(entity.name)
-        viewer = SchemaViewer(self.req)
-        layout = viewer.visit_entityschema(eschema, skiprels=self.skip_rels)
-        self.w(ureport_as_html(layout))
-        if not eschema.is_final():
-            self.w(u'<img src="%s" alt="%s"/>' % (
-                html_escape(entity.absolute_url(vid='eschemagraph')),
-                html_escape(self.req._('graphical schema for %s') % entity.name)))
-
-class ERTypeSchemaView(ERTypePrimaryView):
-    id = 'eschema'
-    title = _('in memory relation schema')
-    main_related_section = False
-
-    def render_entity_attributes(self, entity, siderelations):
-        super(ERTypeSchemaView, self).render_entity_attributes(entity, siderelations)
-        rschema = self.vreg.schema.rschema(entity.name)
-        viewer = SchemaViewer(self.req)
-        layout = viewer.visit_relationschema(rschema)
-        self.w(ureport_as_html(layout))
-        if not rschema.is_final():
-            self.w(u'<img src="%s" alt="%s"/>' % (
-                html_escape(entity.absolute_url(vid='eschemagraph')),
-                html_escape(self.req._('graphical schema for %s') % entity.name)))
-
-        
-class EETypeWorkflowView(EntityView):
-    id = 'workflow'
-    accepts = ('EEType',)
-    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)
-        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"/>' % (
-            html_escape(entity.absolute_url(vid='ewfgraph')),
-            html_escape(self.req._('graphical workflow for %s') % entity.name)))
-
-
-class EETypeOneLineView(baseviews.OneLineView):
-    accepts = ('EEType',)
-    
-    def cell_call(self, row, col, **kwargs):
-        entity = self.entity(row, col)
-        final = entity.final
-        if final:
-            self.w(u'<em class="finalentity">')
-        super(EETypeOneLineView, self).cell_call(row, col, **kwargs)
-        if final:
-            self.w(u'</em>')
-        
-
-from cubicweb.web.action import EntityAction
-
-class ViewWorkflowAction(EntityAction):
-    id = 'workflow'
-    category = 'mainactions'
-    title = _('view workflow')
-    accepts = ('EEType',)
-    condition = 'S state_of X' # must have at least one state associated
-    def url(self):
-        entity = self.rset.get_entity(self.row or 0, self.col or 0)
-        return entity.absolute_url(vid='workflow')
-        
--- a/web/views/sessions.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/sessions.py	Mon May 04 13:09:48 2009 +0200
@@ -2,12 +2,12 @@
 object :/
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb.web import ExplicitLogin, InvalidSession
+from cubicweb.web import InvalidSession
 from cubicweb.web.application import AbstractSessionManager
 
 
--- a/web/views/startup.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/startup.py	Mon May 04 13:09:48 2009 +0200
@@ -6,26 +6,38 @@
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
+_ = unicode
 
+from logilab.common.textutils import unormalize
 from logilab.mtconverter import html_escape
 
-from cubicweb.common.uilib import ureport_as_html, unormalize, ajax_replace_url
-from cubicweb.common.view import StartupView
-from cubicweb.common.selectors import match_user_group
-from cubicweb.web.httpcache import EtagHTTPCacheManager
+from cubicweb.view import StartupView
+from cubicweb.selectors import match_user_groups
+from cubicweb.common.uilib import ureport_as_html
+from cubicweb.web import ajax_replace_url, uicfg, httpcache
 from cubicweb.web.views.management import SecurityViewMixIn
-from copy import deepcopy
-_ = unicode
 
 
 class ManageView(StartupView):
     id = 'manage'
-    title = _('manage')    
-    http_cache_manager = EtagHTTPCacheManager
+    title = _('manage')
+    http_cache_manager = httpcache.EtagHTTPCacheManager
+
+    @classmethod
+    def vreg_initialization_completed(cls):
+        for eschema in cls.schema.entities():
+            if eschema.schema_entity():
+                uicfg.etypecat.setdefault(eschema, 'schema')
+            elif eschema.is_subobject(strict=True):
+                uicfg.etypecat.setdefault(eschema, 'subobject')
+            elif eschema.meta:
+                uicfg.etypecat.setdefault(eschema, 'system')
+            else:
+                uicfg.etypecat.setdefault(eschema, 'application')
 
     def display_folders(self):
         return False
-    
+
     def call(self, **kwargs):
         """The default view representing the application's management"""
         self.req.add_css('cubicweb.manageview.css')
@@ -36,7 +48,7 @@
             self.w(u'<table><tr>\n')
             self.w(u'<td style="width:40%">')
             self._main_index()
-            self.w(u'</td><td style="width:60%">')            
+            self.w(u'</td><td style="width:60%">')
             self.folders()
             self.w(u'</td>')
             self.w(u'</tr></table>\n')
@@ -64,22 +76,22 @@
                 href = req.build_url('view', vid='creation', etype='Card', wikiid='index')
                 label = self.req._('create an index page')
             self.w(u'<br/><a href="%s">%s</a>\n' % (html_escape(href), label))
-        
+
     def folders(self):
         self.w(u'<h4>%s</h4>\n' % self.req._('Browse by category'))
         self.vreg.select_view('tree', self.req, None).dispatch(w=self.w)
-        
+
     def startup_views(self):
         self.w(u'<h4>%s</h4>\n' % self.req._('Startup views'))
         self.startupviews_table()
-        
+
     def startupviews_table(self):
         for v in self.vreg.possible_views(self.req, None):
             if v.category != 'startupview' or v.id in ('index', 'tree', 'manage'):
                 continue
             self.w('<p><a href="%s">%s</a></p>' % (
                 html_escape(v.url()), html_escape(self.req._(v.title).capitalize())))
-        
+
     def entities(self):
         schema = self.schema
         self.w(u'<h4>%s</h4>\n' % self.req._('The repository holds the following entities'))
@@ -88,17 +100,17 @@
         if manager:
             self.w(u'<tr><th colspan="4">%s</th></tr>\n' % self.req._('application entities'))
         self.entity_types_table(eschema for eschema in schema.entities()
-                                if not eschema.meta and not eschema.is_subobject(strict=True))
-        if manager: 
+                                if uicfg.etypecat.get(eschema) == 'application')
+        if manager:
             self.w(u'<tr><th colspan="4">%s</th></tr>\n' % self.req._('system entities'))
             self.entity_types_table(eschema for eschema in schema.entities()
-                                    if eschema.meta and not eschema.schema_entity())
-            if 'EFRDef' in schema: # check schema support
+                                if uicfg.etypecat.get(eschema) == 'system')
+            if 'CWAttribute' in schema: # check schema support
                 self.w(u'<tr><th colspan="4">%s</th></tr>\n' % self.req._('schema entities'))
-                self.entity_types_table(schema.eschema(etype)
-                                        for etype in schema.schema_entity_types())
+                self.entity_types_table(eschema for eschema in schema.entities()
+                                        if uicfg.etypecat.get(eschema) == 'schema')
         self.w(u'</table>')
-        
+
     def entity_types_table(self, eschemas):
         newline = 0
         infos = sorted(self.entity_types(eschemas),
@@ -112,8 +124,8 @@
             self.w(u'<td class="addcol">%s</td><td>%s</td>\n' % (addlink,  etypelink))
             self.w(u'<td class="addcol">%s</td><td>%s</td>\n' % (addlink2, etypelink2))
             self.w(u'</tr>\n')
-        
-        
+
+
     def entity_types(self, eschemas):
         """return a list of formatted links to get a list of entities of
         a each entity's types
@@ -134,7 +146,7 @@
             etypelink = u'&nbsp;<a href="%s">%s</a> (%d)' % (
                 html_escape(url), label, nb)
             yield (label, etypelink, self.add_entity_link(eschema, req))
-    
+
     def add_entity_link(self, eschema, req):
         """creates a [+] link for adding an entity if user has permission to do so"""
         if not eschema.has_perm(req, 'add'):
@@ -143,14 +155,14 @@
             html_escape(self.create_url(eschema.type)),
             self.req.__('add a %s' % eschema))
 
-    
+
 class IndexView(ManageView):
     id = 'index'
     title = _('index')
-    
+
     def display_folders(self):
         return 'Folder' in self.schema and self.req.execute('Any COUNT(X) WHERE X is Folder')[0][0]
-    
+
 
 
 class SchemaView(StartupView):
@@ -188,10 +200,10 @@
             self.wview(section, None)
         self.w(u'</div>')
 
-    
+
 class ManagerSchemaPermissionsView(StartupView, SecurityViewMixIn):
     id = 'schema_security'
-    require_groups = ('managers',)
+    __select__ = StartupView.__select__ & match_user_groups('managers')
 
     def call(self, display_relations=True,
              skiprels=('is', 'is_instance_of', 'identity', 'owned_by', 'created_by')):
@@ -207,7 +219,7 @@
             entities = [eschema for eschema in entities
                         if not eschema.meta]
         # compute relations
-        relations = []    
+        relations = []
         if display_relations:
             relations = [rschema for rschema in schema.relations()
                          if not (rschema.is_final() or rschema.type in skiprels)]
@@ -287,7 +299,7 @@
             self.schema_definition(rschema, link=False)
             self.w(u'</div>')
 
-                
+
 class SchemaUreportsView(StartupView):
     id = 'schematext'
 
--- a/web/views/tableview.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/tableview.py	Mon May 04 13:09:48 2009 +0200
@@ -11,13 +11,13 @@
 
 from logilab.mtconverter import html_escape
 
-from cubicweb.common.utils import make_uid
-from cubicweb.common.uilib import toggle_action, limitsize, jsonize, htmlescape
-from cubicweb.common.view import EntityView, AnyRsetView
-from cubicweb.common.selectors import (nonempty_rset,  match_form_params,
-                                    accept_rset)
+from cubicweb.selectors import nonempty_rset, match_form_params
+from cubicweb.utils import make_uid
+from cubicweb.view import EntityView, AnyRsetView
+from cubicweb.common.uilib import toggle_action, limitsize, htmlescape
+from cubicweb.web import jsonize
 from cubicweb.web.htmlwidgets import (TableWidget, TableColumn, MenuWidget,
-                                   PopupBoxMenu, BoxLink)
+                                      PopupBoxMenu, BoxLink)
 from cubicweb.web.facet import prepare_facets_rqlst, filter_hiddens
 
 class TableView(AnyRsetView):
@@ -45,7 +45,7 @@
                                         'displayfilter': displayfilter})
             return self.show_hide_actions(divid, not hidden)
         return ()
-    
+
     def _generate_form(self, divid, baserql, fwidgets, hidden=True, vidargs={}):
         """display a form to filter table's content. This should only
         occurs when a context eid is given
@@ -89,7 +89,7 @@
             else:
                 displaycols = range(len(self.rset.syntax_tree().children[0].selection))
         return displaycols
-    
+
     def call(self, title=None, subvid=None, displayfilter=None, headers=None,
              displaycols=None, displayactions=None, actions=(), divid=None,
              cellvids=None, cellattrs=None):
@@ -170,7 +170,7 @@
         if currentlydisplayed:
             return [(showhide, showlabel, 'hidden', '%sShow' % divid),
                     (showhide, hidelabel, None, '%sHide' % divid)]
-        return [(showhide, showlabel, None, '%sShow' % divid), 
+        return [(showhide, showlabel, None, '%sShow' % divid),
                 (showhide, hidelabel, 'hidden', '%sHide' % divid)]
 
     def render_actions(self, divid, actions):
@@ -185,7 +185,7 @@
             menu.append(BoxLink(url, label, klass, ident=ident, escape=True))
         box.render(w=self.w)
         self.w(u'<div class="clear"/>')
-        
+
     def get_columns(self, rqlstdescr, displaycols, headers, subvid, cellvids,
                     cellattrs, mainindex):
         columns = []
@@ -215,15 +215,15 @@
 
             if cellattrs and colindex in cellattrs:
                 for name, value in cellattrs[colindex].iteritems():
-                    column.add_attr(name,value)
+                    column.add_attr(name, value)
             # add column
             columns.append(column)
         return columns
-        
+
 
     def render(self, cellvid, row, col, w):
         self.view('cell', self.rset, row=row, col=col, cellvid=cellvid, w=w)
-        
+
     def get_rows(self):
         return self.rset
 
@@ -246,18 +246,18 @@
         entity = self.rset.get_entity(row, col)
         return entity.sortvalue()
 
+
 class EditableTableView(TableView):
     id = 'editable-table'
     finalview = 'editable-final'
     title = _('editable-table')
 
-    
+
 class CellView(EntityView):
-    __selectors__ = (nonempty_rset, accept_rset)
-    
+    __select__ = nonempty_rset()
+
     id = 'cell'
-    accepts = ('Any',)
-    
+
     def cell_call(self, row, col, cellvid=None):
         """
         :param row, col: indexes locating the cell value in view's result set
@@ -278,23 +278,22 @@
 
 class InitialTableView(TableView):
     """same display as  table view but consider two rql queries :
-    
+
     * the default query (ie `rql` form parameter), which is only used to select
       this view and to build the filter form. This query should have the same
       structure as the actual without actual restriction (but link to
       restriction variables) and usually with a limit for efficiency (limit set
       to 2 is advised)
-      
+
     * the actual query (`actualrql` form parameter) whose results will be
       displayed with default restrictions set
     """
     id = 'initialtable'
-    __selectors__ = nonempty_rset, match_form_params
-    form_params = ('actualrql',)
+    __select__ = nonempty_rset() & match_form_params('actualrql')
     # should not be displayed in possible view since it expects some specific
     # parameters
     title = None
-    
+
     def call(self, title=None, subvid=None, headers=None, divid=None,
              displaycols=None, displayactions=None):
         """Dumps a table displaying a composite query"""
@@ -328,4 +327,3 @@
 class EditableInitialTableTableView(InitialTableView):
     id = 'editable-initialtable'
     finalview = 'editable-final'
-    
--- a/web/views/tabs.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/tabs.py	Mon May 04 13:09:48 2009 +0200
@@ -1,22 +1,18 @@
 """base classes to handle tabbed views
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
 __docformat__ = "restructuredtext en"
 
-from logilab.common.decorators import monkeypatch
 from logilab.mtconverter import html_escape
 
 from cubicweb import NoSelectableObject, role
-from cubicweb.common.view import EntityView 
-from cubicweb.common.selectors import has_related_entities
-from cubicweb.common.utils import HTMLHead
-from cubicweb.common.uilib import rql_for_eid
-
-from cubicweb.web.views.basecontrollers import JSonController
+from cubicweb.selectors import partial_has_related_entities
+from cubicweb.view import EntityView
+from cubicweb.common import tags, uilib
 
 
 class LazyViewMixin(object):
@@ -42,11 +38,11 @@
             'lazyview wants at least : rql, or an eid, or an rset -- or call it with static=True'
         w = w or self.w
         self.req.add_js('cubicweb.lazy.js')
-        urlparams = {'vid' : vid, 'mode' : 'html'}
+        urlparams = {'vid' : vid, 'fname' : 'view'}
         if rql:
             urlparams['rql'] = rql
         elif eid:
-            urlparams['rql'] = rql_for_eid(eid)
+            urlparams['rql'] = uilib.rql_for_eid(eid)
         elif rset:
             urlparams['rql'] = rset.printable_rql()
         w(u'<div id="lazy-%s" cubicweb:loadurl="%s">' % (
@@ -148,9 +144,8 @@
         'cookiename' : self.cookie_name})
 
 
-class EntityRelatedTab(EntityView):
-    """A view you should inherit from leftmost,
-    to wrap another actual view displaying entity related stuff.
+class EntityRelationView(EntityView):
+    """view displaying entity related stuff.
     Such a view _must_ provide the rtype, target and vid attributes :
 
     Example :
@@ -158,23 +153,22 @@
     class ProjectScreenshotsView(EntityRelationView):
         '''display project's screenshots'''
         id = title = _('projectscreenshots')
-        accepts = ('Project',)
+        __select__ = EntityRelationView.__select__ & implements('Project')
         rtype = 'screenshot'
-        target = 'object'
+        role = 'subject'
         vid = 'gallery'
-        __selectors__ = EntityRelationView.__selectors__ + (one_line_rset,)
 
-    This is the view we want to have in a tab, only if there is something to show.
-    Then, just define as below, and declare this being the tab content :
-
-    class ProjectScreenshotTab(EntityRelatedTab, ProjectScreenshotsView):
-        id = 'screenshots_tab'
+    in this example, entities related to project entity by the'screenshot'
+    relation (where the project is subject of the relation) will be displayed
+    using the 'gallery' view.
     """
-    __selectors__ = EntityView.__selectors__ + (has_related_entities,)
+    __select__ = EntityView.__select__ & partial_has_related_entities()
     vid = 'list'
-
+    
     def cell_call(self, row, col):
         rset = self.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)))
         self.wview(self.vid, rset, 'noresult')
         self.w(u'</div>')
--- a/web/views/timeline.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/timeline.py	Mon May 04 13:09:48 2009 +0200
@@ -3,7 +3,7 @@
 cf. http://code.google.com/p/simile-widgets/
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -13,11 +13,10 @@
 from logilab.mtconverter import html_escape
 
 from cubicweb.interfaces import ICalendarable
-from cubicweb.common.view import EntityView, StartupView
-from cubicweb.common.selectors import implement_interface
+from cubicweb.selectors import implements
+from cubicweb.view import EntityView, StartupView
 
 
-# 
 class TimelineJsonView(EntityView):
     """generates a json file to feed Timeline.loadJSON()
     NOTE: work in progress (image_url, bubbleUrl and so on
@@ -28,8 +27,7 @@
     templatable = False
     content_type = 'application/json'
 
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (ICalendarable,)
+    __select__ = implements(ICalendarable)
     date_fmt = '%Y/%m/%d'
     
     def call(self):
@@ -103,8 +101,7 @@
 class TimelineView(TimelineViewMixIn, EntityView):
     """builds a cubicweb timeline widget node"""
     id = 'timeline'
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (ICalendarable,)
+    __select__ = implements(ICalendarable)
     need_navigation = False
     def call(self, tlunit=None):
         self.req.html_headers.define_var('Timeline_urlPrefix', self.req.datadir_url)
@@ -120,5 +117,5 @@
     id = 'static-timeline'
     
     def call(self, loadurl, tlunit=None, wdgclass=None):
-        self.widget_class = wdgclass or self.widget_clas
+        self.widget_class = wdgclass or self.widget_class
         self.render(loadurl, tlunit)
--- a/web/views/timetable.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/timetable.py	Mon May 04 13:09:48 2009 +0200
@@ -1,16 +1,16 @@
 """html calendar views
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
 from logilab.mtconverter import html_escape
 
 from cubicweb.interfaces import ITimetableViews
-from cubicweb.common.utils import date_range
-from cubicweb.common.selectors import implement_interface
-from cubicweb.common.view import AnyRsetView
+from cubicweb.selectors import implements
+from cubicweb.utils import date_range
+from cubicweb.view import AnyRsetView
 
 
 class _TaskEntry(object):
@@ -25,8 +25,7 @@
 class TimeTableView(AnyRsetView):
     id = 'timetable'
     title = _('timetable')
-    __selectors__ = (implement_interface,)
-    accepts_interfaces = (ITimetableViews,)
+    __select__ = implements(ITimetableViews)
     need_navigation = False
 
     def call(self, title=None):
@@ -39,14 +38,14 @@
 
         # XXX: try refactoring with calendar.py:OneMonthCal
         for row in xrange(self.rset.rowcount):
-            task = self.rset.get_entity(row,0)
+            task = self.rset.get_entity(row, 0)
             if len(self.rset[row])>1:
-                user = self.rset.get_entity(row,1)
+                user = self.rset.get_entity(row, 1)
             else:
                 user = u"*"
             the_dates = []
             if task.start and task.stop:
-                if task.start.absdate == task.stop.absdate:
+                if task.start.toordinal() == task.stop.toordinal():
                     the_dates.append(task.start)
                 else:
                     the_dates += date_range( task.start, task.stop )
@@ -56,9 +55,9 @@
                 the_dates.append(task.stop)
             for d in the_dates:
                 d_users = dates.setdefault(d, {})
-                u_tasks = d_users.setdefault(user,set())
+                u_tasks = d_users.setdefault(user, set())
                 u_tasks.add( task )
-                task_max = users_max.setdefault(user,0)
+                task_max = users_max.setdefault(user, 0)
                 if len(u_tasks)>task_max:
                     users_max[user] = len(u_tasks)
             if user not in users:
@@ -72,7 +71,7 @@
 
         rows = []
         # colors here are class names defined in cubicweb.css
-        colors = [ "col%x"%i for i in range(12) ]
+        colors = ["col%x" % i for i in xrange(12)]
         next_color_index = 0
 
         visited_tasks = {} # holds a description of a task for a user
@@ -93,7 +92,7 @@
                     if key in visited_tasks:
                         task_descr = visited_tasks[ key ]
                         user_columns[task_descr.column] = task_descr, False
-                        task_descr.lines+=1
+                        task_descr.lines += 1
                     else:
                         postpone.append(key)
                 for key in postpone:
@@ -101,7 +100,7 @@
                     # (which must be the same for every user concerned
                     # by the task)
                     task, user = key
-                    for i,t in enumerate(user_columns):
+                    for i, t in enumerate(user_columns):
                         if t is None:
                             if task in task_colors:
                                 color = task_colors[task]
@@ -129,16 +128,16 @@
         self.w(u'</table>')
         self.w(u'</div>\n')
 
-    def render_col_headers(self,users,widths):
+    def render_col_headers(self, users, widths):
         """ render column headers """
         self.w(u'<tr class="header">\n')
 
         self.w(u'<th class="ttdate">&nbsp;</th>\n')
         columns = []
-        for user,width in zip(users,widths):
-            self.w(u'<th colspan="%s">' % max(MIN_COLS,width))
-            if user!=u"*":
-                user.view('secondary',w=self.w)
+        for user, width in zip(users, widths):
+            self.w(u'<th colspan="%s">' % max(MIN_COLS, width))
+            if user != u"*":
+                user.view('secondary', w=self.w)
             else:
                 self.w(user)
             self.w(u'</th>')
@@ -165,7 +164,7 @@
             previous_is_empty = False
 
             klass = "even"
-            if date.day_of_week in (5,6) and not empty_line:
+            if date.weekday() in (5, 6) and not empty_line:
                 klass = "odd"
             self.w(u'<tr class="%s">' % klass)
             odd = not odd
--- a/web/views/treeview.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/treeview.py	Mon May 04 13:09:48 2009 +0200
@@ -1,22 +1,24 @@
 """Set of tree-building widgets, based on jQuery treeview plugin
 
 :organization: Logilab
-:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
+
 from logilab.mtconverter import html_escape
+
 from cubicweb.interfaces import ITree
-from cubicweb.common.selectors import implement_interface, yes
-from cubicweb.common.utils import make_uid
-from cubicweb.common.view import EntityView
+from cubicweb.selectors import implements
+from cubicweb.view import EntityView
+from cubicweb.utils import make_uid
 
 def treecookiename(treeid):
     return str('treestate-%s' % treeid)
 
+
 class TreeView(EntityView):
     id = 'treeview'
-    accepts = ('Any',)
     itemvid = 'treeitemview'
     css_classes = 'treeview widget'
     title = _('tree view')
@@ -37,13 +39,13 @@
             self.req.add_css('jquery.treeview.css')
             self.req.add_js(('cubicweb.ajax.js', 'jquery.treeview.js'))
             self.req.html_headers.add_onload(u"""
-                 jQuery("#tree-%s").treeview({toggle: toggleTree,
-                                              prerendered: true});""" % treeid)
+jQuery("#tree-%s").treeview({toggle: toggleTree, prerendered: true});""" % treeid)
         self.w(u'<ul id="tree-%s" class="%s">' % (treeid, self.css_classes))
         for rowidx in xrange(len(self.rset)):
             self.wview(self.itemvid, self.rset, row=rowidx, col=0,
-                       vid=subvid, parentvid=self.id, treeid=treeid)
+                       vid=subvid, parentvid=self.id)
         self.w(u'</ul>')
+        
 
 class FileTreeView(TreeView):
     """specific version of the treeview to display file trees
@@ -55,6 +57,8 @@
     def call(self, subvid=None, treeid=None, initial_load=True):
         super(FileTreeView, self).call(treeid=treeid, subvid='filetree-oneline', initial_load=initial_load)
 
+
+
 class FileItemInnerView(EntityView):
     """inner view used by the TreeItemView instead of oneline view
 
@@ -66,20 +70,17 @@
     def cell_call(self, row, col):
         entity = self.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'))
+            self.w(u'<div class="folder">%s</div>' % entity.view('oneline'))
         else:
             # XXX define specific CSS classes according to mime types
-            self.w(u'<div class="file">%s</div>\n' % entity.view('oneline'))
+            self.w(u'<div class="file">%s</div>' % entity.view('oneline'))
 
 
 class DefaultTreeViewItemView(EntityView):
-    """default treeitem view for entities which don't implement ITree
-    """
+    """default treeitem view for entities which don't implement ITree"""
     id = 'treeitemview'
-    accepts = ('Any',)
-
-    def cell_call(self, row, col, vid='oneline', parentvid='treeview', treeid=None):
-        assert treeid is not None
+    
+    def cell_call(self, row, col, vid='oneline', parentvid='treeview'):
         entity = self.entity(row, col)
         itemview = self.view(vid, self.rset, row=row, col=col)
         if row == len(self.rset) - 1:
@@ -91,90 +92,36 @@
 class TreeViewItemView(EntityView):
     """specific treeitem view for entities which implement ITree
 
-    (each item should be exandable if it's not a tree leaf)
+    (each item should be expandable if it's not a tree leaf)
     """
     id = 'treeitemview'
-    # XXX append yes to make sure we get an higher score than
-    #     the default treeitem view
-    __selectors__ = (implement_interface, yes)
-    accepts_interfaces = (ITree,)
-
-    def open_state(self, eeid, treeid):
-        cookies = self.req.get_cookie()
-        treestate = cookies.get(treecookiename(treeid))
-        if treestate:
-            return str(eeid) in treestate.value.split(';')
-        return False
-
-    def cell_call(self, row, col, treeid, vid='oneline', parentvid='treeview'):
-        w = self.w
+    __select__ = implements(ITree)
+    
+    def cell_call(self, row, col, vid='oneline', parentvid='treeview'):
         entity = self.entity(row, col)
-        liclasses = []
+        cssclasses = []
         is_leaf = False
-        is_last = row == len(self.rset) - 1
-        is_open = self.open_state(entity.eid, treeid)
+        if row == len(self.rset) - 1:
+            is_leaf = True
         if not hasattr(entity, 'is_leaf') or entity.is_leaf():
-            if is_last:
-                liclasses.append('last')
-            w(u'<li class="%s">' % u' '.join(liclasses))
+            if is_leaf : cssclasses.append('last')
+            self.w(u'<li class="%s">' % u' '.join(cssclasses))
         else:
             rql = entity.children_rql() % {'x': entity.eid}
             url = html_escape(self.build_url('json', rql=rql, vid=parentvid,
                                              pageid=self.req.pageid,
-                                             treeid=treeid,
-                                             subvid=vid))
-            divclasses = ['hitarea']
-            if is_open:
-                liclasses.append('collapsable')
-                divclasses.append('collapsable-hitarea')
-            else:
-                liclasses.append('expandable')
-                divclasses.append('closed-hitarea expandable-hitarea')
-            if is_last:
-                if is_open:
-                    liclasses.append('lastCollapsable')
-                    divclasses.append('lastCollapsable-hitarea')
-                else:
-                    liclasses.append('lastExpandable')
-                    divclasses.append('lastExpandable-hitarea')
-            if is_open:
-                w(u'<li class="%s">' % u' '.join(liclasses))
-            else:
-                w(u'<li cubicweb:loadurl="%s" class="%s">' % (url, u' '.join(liclasses)))
-            if is_leaf:
-                divtail = ''
-            else:
-                divtail = ''' onclick="async_remote_exec('node_clicked', '%s', '%s')"''' % \
-                    (treeid, entity.eid)
-            w(u'<div class="%s"%s></div>' % (u' '.join(divclasses), divtail))
-
+                                             subvid=vid,
+                                             noautoload=True))
+            cssclasses.append('expandable')
+            divclasses = ['hitarea expandable-hitarea']
+            if is_leaf :
+                cssclasses.append('lastExpandable')
+                divclasses.append('lastExpandable-hitarea')
+            self.w(u'<li cubicweb:loadurl="%s" class="%s">' % (url, u' '.join(cssclasses)))
+            self.w(u'<div class="%s"> </div>' % u' '.join(divclasses))
             # add empty <ul> because jquery's treeview plugin checks for
             # sublists presence
-            if not is_open:
-                w(u'<ul class="placeholder"><li>place holder</li></ul>')
-        # the local node info
+            self.w(u'<ul class="placeholder"><li>place holder</li></ul>')
         self.wview(vid, self.rset, row=row, col=col)
-        if is_open: # recurse if needed
-            self.wview(parentvid, self.req.execute(rql), treeid=treeid, initial_load=False)
-        w(u'</li>')
-
-from logilab.common.decorators import monkeypatch
-from cubicweb.web.views.basecontrollers import JSonController
+        self.w(u'</li>')
 
-@monkeypatch(JSonController)
-def js_node_clicked(self, treeid, nodeeid):
-    """add/remove eid in treestate cookie"""
-    cookies = self.req.get_cookie()
-    statename = treecookiename(treeid)
-    treestate = cookies.get(statename)
-    if treestate is None:
-        cookies[statename] = nodeeid
-        self.req.set_cookie(cookies, statename)
-    else:
-        marked = set(filter(None, treestate.value.split(';')))
-        if nodeeid in marked:
-            marked.remove(nodeeid)
-        else:
-            marked.add(nodeeid)
-        cookies[statename] = ';'.join(marked)
-        self.req.set_cookie(cookies, statename)
--- a/web/views/urlpublishing.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/urlpublishing.py	Mon May 04 13:09:48 2009 +0200
@@ -18,7 +18,7 @@
 because of redirecting instead of direct traversal
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 
@@ -28,7 +28,7 @@
 
 from cubicweb import RegistryException, typed_eid
 from cubicweb.web import NotFound, Redirect
-from cubicweb.web.component import SingletonComponent, Component
+from cubicweb.web.component import Component, Component
 
 
 class PathDontMatch(Exception):
@@ -36,7 +36,7 @@
     a path
     """
     
-class URLPublisherComponent(SingletonComponent):
+class URLPublisherComponent(Component):
     """associate url's path to view identifier / rql queries,
     by applying a chain of urlpathevaluator components.
 
--- a/web/views/urlrewrite.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/urlrewrite.py	Mon May 04 13:09:48 2009 +0200
@@ -1,22 +1,19 @@
 """Rules based url rewriter component, to get configurable RESTful urls
 
 :organization: Logilab
-:copyright: 2007-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2007-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 import re
 
-from cubicweb.vregistry import autoselectors
-
-from cubicweb.common.registerers import accepts_registerer
-from cubicweb.common.appobject import AppObject
+from cubicweb.appobject import AppObject
 
 
 def rgx(pattern, flags=0):
     """this is just a convenient shortcout to add the $ sign"""
     return re.compile(pattern+'$', flags)
 
-class metarewriter(autoselectors):
+class metarewriter(type):
     """auto-extend rules dictionnary"""
     def __new__(mcs, name, bases, classdict):
         # collect baseclass' rules
@@ -55,11 +52,9 @@
     """
     __metaclass__ = metarewriter
     __registry__ = 'urlrewriting'
-    __registerer__ = accepts_registerer
     __abstract__ = True
 
     id = 'urlrewriting'
-    accepts = ('Any',)
     priority = 1
 
     def rewrite(self, req, uri):
@@ -83,19 +78,18 @@
         ('/manage', dict(vid='manage')),
         ('/notfound', dict(vid='404')),
         ('/error', dict(vid='error')),
-        (rgx('/schema/([^/]+?)/?'),  dict(vid='eschema', rql=r'Any X WHERE X is EEType, X name "\1"')),
+        (rgx('/schema/([^/]+?)/?'),  dict(vid='eschema', rql=r'Any X WHERE X is CWEType, X name "\1"')),
         (rgx('/add/([^/]+?)/?'), dict(vid='creation', etype=r'\1')),
         (rgx('/doc/images/(.+?)/?'), dict(vid='wdocimages', fid=r'\1')),
         (rgx('/doc/?'), dict(vid='wdoc', fid=r'main')),
         (rgx('/doc/(.+?)/?'), dict(vid='wdoc', fid=r'\1')),
         (rgx('/changelog/?'), dict(vid='changelog')),
         ]
-    
+
     def rewrite(self, req, uri):
         """for each `input`, `output `in rules, if `uri` matches `input`,
         req's form is updated with `output`
         """
-        rset = None
         for data in self.rules:
             try:
                 inputurl, infos, required_groups = data
@@ -112,7 +106,7 @@
                 # XXX what about i18n ? (vtitle for instance)
                 for param, value in infos.items():
                     if isinstance(value, basestring):
-                        req.form[param]= inputurl.sub(value, uri)
+                        req.form[param] = inputurl.sub(value, uri)
                     else:
                         req.form[param] = value
                 break
@@ -185,7 +179,7 @@
     rules = [
         # rgxp : callback
         (rgx('/search/(.+)'), build_rset(rql=r'Any X WHERE X has_text %(text)s',
-                                         rgxgroups=[('text', 1)])), 
+                                         rgxgroups=[('text', 1)])),
         ]
 
     def rewrite(self, req, uri):
--- a/web/views/vcard.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/vcard.py	Mon May 04 13:09:48 2009 +0200
@@ -1,25 +1,25 @@
 """vcard import / export
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from cubicweb.common.view import EntityView
+from cubicweb.selectors import implements
+from cubicweb.view import EntityView
 
 _ = unicode 
 
 VCARD_PHONE_TYPES = {'home': 'HOME', 'office': 'WORK', 'mobile': 'CELL', 'fax': 'FAX'}
 
-class VCardEUserView(EntityView):
+class VCardCWUserView(EntityView):
     """export a person information as a vcard"""
     id = 'vcard'
     title = _('vcard')
     templatable = False
     content_type = 'text/x-vcard'
-    accepts = ('EUser',)
-        
+    __select__ = implements('CWUser')        
 
     def set_request_content_type(self):
         """overriden to set a .vcf filename"""
@@ -49,4 +49,4 @@
             w(u'EMAIL;TYPE=INTERNET:%s\n' % email.address)
 
 from logilab.common.deprecation import class_renamed
-VCardEuserView = class_renamed('VCardEuserView', VCardEUserView)
+VCardEuserView = VCardEUserView = class_renamed('VCardEuserView', VCardCWUserView)
--- a/web/views/wdoc.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/wdoc.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """inline help system, using ReST file in products `wdoc` directory
 
 :organization: Logilab
-:copyright: 2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2008-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -9,14 +9,14 @@
 from itertools import chain
 from os.path import join
 from bisect import bisect_right
-
-from mx.DateTime import strptime, today
+from datetime import date
 
 from logilab.common.changelog import ChangeLog
 from logilab.mtconverter import CHARSET_DECL_RGX
 
-from cubicweb.common.selectors import match_form_params
-from cubicweb.common.view import StartupView
+from cubicweb.selectors import match_form_params
+from cubicweb.view import StartupView
+from cubicweb.utils import strptime, todate
 from cubicweb.common.uilib import rest_publish
 from cubicweb.web import NotFound
 
@@ -74,7 +74,7 @@
             build_toc_index(section, index)
     return index
     
-def title(node, lang):
+def title_for_lang(node, lang):
     for title in node.findall('title'):
         if title.attrib['{http://www.w3.org/XML/1998/namespace}lang'] == lang:
             return unicode(title.text)
@@ -85,8 +85,7 @@
 # help views ##################################################################
 
 class InlineHelpView(StartupView):
-    __selectors__ = (match_form_params,)
-    form_params = ('fid',)
+    __select__ = match_form_params('fid')
     id = 'wdoc'
     title = _('site documentation')
     
@@ -108,7 +107,7 @@
         else:
             self.navigation_links(node)
             self.w(u'<div class="hr"></div>')
-            self.w(u'<h1>%s</h1>' % (title(node, self.req.lang)))            
+            self.w(u'<h1>%s</h1>' % (title_for_lang(node, self.req.lang)))            
         data = open(join(resourcedir, rid)).read()
         self.w(rest_publish(self, data))
         if node is not None:
@@ -142,7 +141,7 @@
         self.w(u'%s : ' % self.req._(msgid))
         self.w(u'<a href="%s">%s</a>' % (
             self.req.build_url('doc/'+node.attrib['resource']),
-            title(node, self.req.lang)))
+            title_for_lang(node, self.req.lang)))
         self.w(u'</span>\n')
         
     def subsections_links(self, node, first=True):
@@ -155,7 +154,7 @@
         for child in sub:
             self.w(u'<li><a href="%s">%s</a>' % (
                 self.req.build_url('doc/'+child.attrib['resource']),
-                title(child, self.req.lang)))
+                title_for_lang(child, self.req.lang)))
             self.subsections_links(child, False)
             self.w(u'</li>')
         self.w(u'</ul>\n')
@@ -163,9 +162,8 @@
 
 
 class InlineHelpImageView(StartupView):
-    __selectors__ = (match_form_params,)
-    form_params = ('fid',)
     id = 'wdocimages'
+    __select__ = match_form_params('fid')
     binary = True
     templatable = False
     content_type = 'image/png'
@@ -194,6 +192,7 @@
         title = self.req._(self.title)
         restdata = ['.. -*- coding: utf-8 -*-', '', title, '='*len(title), '']
         w = restdata.append
+        today = date.today()
         for fpath in self.config.locate_all_files(rid):
             cl = ChangeLog(fpath)
             encoding = 'utf-8'
@@ -207,9 +206,9 @@
                     w(unicode(line, encoding))
             for entry in cl.entries:
                 if entry.date:
-                    date = strptime(entry.date, '%Y-%m-%d')
+                    edate = todate(strptime(entry.date, '%Y-%m-%d'))
                 else:
-                    date = today()
+                    edate = today
                 messages = []
                 for msglines, submsgs in entry.messages:
                     msgstr = unicode(' '.join(l.strip() for l in msglines), encoding)
@@ -218,16 +217,16 @@
                         msgstr += '     - ' + unicode(' '.join(l.strip() for l in submsglines), encoding)
                         msgstr += u'\n'
                     messages.append(msgstr)
-                entry = (date, messages)
+                entry = (edate, messages)
                 allentries.insert(bisect_right(allentries, entry), entry)
         latestdate = None
         i = 0
-        for date, messages in reversed(allentries):
-            if latestdate != date:
-                fdate = self.format_date(date)
+        for edate, messages in reversed(allentries):
+            if latestdate != edate:
+                fdate = self.format_date(edate)
                 w(u'\n%s' % fdate)
-                w('~'*len(fdate))
-                latestdate = date
+                w('~' * len(fdate))
+                latestdate = edate
             for msg in messages:
                 w(u'* %s' % msg)
                 i += 1
--- a/web/views/wfentities.py	Thu Apr 30 16:16:39 2009 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,16 +0,0 @@
-"""html view for workflow related entities
-
-:organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
-"""
-__docformat__ = "restructuredtext en"
-
-from cubicweb.common.view import EntityView
-
-class CellView(EntityView):
-    id = 'cell'
-    accepts = ('TrInfo',)
-    def cell_call(self, row, col, cellvid=None):
-        entity = self.entity(row, col)
-        self.w(entity.printable_value('comment'))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/workflow.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,222 @@
+"""workflow views:
+
+* IWorkflowable views and forms
+* workflow entities views (State, Transition, TrInfo)
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from logilab.mtconverter import html_escape
+from logilab.common.graph import escape, GraphGenerator, DotBackend
+
+from cubicweb import Unauthorized, view
+from cubicweb.selectors import (implements, has_related_entities,
+                                relation_possible, match_form_params)
+from cubicweb.interfaces import IWorkflowable
+from cubicweb.web import stdmsgs, action, component, form
+from cubicweb.web.form import FormViewMixIn
+from cubicweb.web.formfields import StringField,  RichTextField
+from cubicweb.web.formwidgets import HiddenInput, SubmitButton, Button
+from cubicweb.web.views import TmpFileViewMixin
+from cubicweb.web.views.boxes import EditBox
+
+_ = unicode
+
+EditBox.rmode.tag_relation('create', ('Transition', 'destination_state', '*'), 'subject')
+EditBox.rmode.tag_relation('create', ('*', 'allowed_transition', 'Transition'), 'object')
+EditBox.rmode.tag_relation('create', ('*', 'destination_state', 'State'), 'object')
+EditBox.rmode.tag_relation('create', ('State', 'allowed_transition', '*'), 'subject')
+
+
+# IWorkflowable views #########################################################
+
+class ChangeStateForm(form.EntityFieldsForm):
+    id = 'changestate'
+
+    __method = StringField(name='__method', initial='set_state',
+                           widget=HiddenInput)
+    state = StringField(eidparam=True, widget=HiddenInput)
+    trcomment = RichTextField(label=_('comment:'), eidparam=True)
+    form_buttons = [SubmitButton(stdmsgs.YES),
+                     Button(stdmsgs.NO, cwaction='cancel')]
+
+
+class ChangeStateFormView(FormViewMixIn, view.EntityView):
+    id = 'statuschange'
+    title = _('status change')
+    __select__ = implements(IWorkflowable) & match_form_params('treid')
+
+    def cell_call(self, row, col):
+        entity = self.entity(row, col)
+        state = entity.in_state[0]
+        transition = self.req.eid_rset(self.req.form['treid']).get_entity(0, 0)
+        dest = transition.destination()
+        _ = self.req._
+        form = self.vreg.select_object('forms', 'changestate', self.req,
+                                       self.rset, row=row, col=col,
+                                       entity=entity,
+                                       redirect_path=self.redirectpath(entity))
+        self.w(form.error_message())
+        self.w(u'<h4>%s %s</h4>\n' % (_(transition.name),
+                                      entity.view('oneline')))
+        msg = _('status will change from %(st1)s to %(st2)s') % {
+            'st1': _(state.name),
+            'st2': _(dest.name)}
+        self.w(u'<p>%s</p>\n' % msg)
+        self.w(form.form_render(state=dest.eid, trcomment=u''))
+
+    def redirectpath(self, entity):
+        return entity.rest_path()
+
+
+class WFHistoryVComponent(component.EntityVComponent):
+    """display the workflow history for entities supporting it"""
+    id = 'wfhistory'
+    __select__ = (component.EntityVComponent.__select__
+                  & relation_possible('wf_info_for', role='object'))
+    context = 'navcontentbottom'
+    title = _('Workflow history')
+
+    def cell_call(self, row, col, view=None):
+        _ = self.req._
+        eid = self.rset[row][col]
+        sel = 'Any FS,TS,WF,D'
+        rql = ' ORDERBY D DESC WHERE WF wf_info_for X,'\
+              'WF from_state FS, WF to_state TS, WF comment C,'\
+              'WF creation_date D'
+        if self.vreg.schema.eschema('CWUser').has_perm(self.req, 'read'):
+            sel += ',U,C'
+            rql += ', WF owned_by U?'
+            displaycols = range(5)
+            headers = (_('from_state'), _('to_state'), _('comment'), _('date'),
+                       _('CWUser'))
+        else:
+            sel += ',C'
+            displaycols = range(4)
+            headers = (_('from_state'), _('to_state'), _('comment'), _('date'))
+        rql = '%s %s, X eid %%(x)s' % (sel, rql)
+        try:
+            rset = self.req.execute(rql, {'x': eid}, 'x')
+        except Unauthorized:
+            return
+        if rset:
+            self.wview('table', rset, title=_(self.title), displayactions=False,
+                       displaycols=displaycols, headers=headers)
+
+
+# workflow entity types views #################################################
+
+class CellView(view.EntityView):
+    id = 'cell'
+    __select__ = implements('TrInfo')
+
+    def cell_call(self, row, col, cellvid=None):
+        self.w(self.entity(row, col).printable_value('comment'))
+
+
+class StateInContextView(view.EntityView):
+    """convenience trick, State's incontext view should not be clickable"""
+    id = 'incontext'
+    __select__ = implements('State')
+
+    def cell_call(self, row, col):
+        self.w(html_escape(self.view('textincontext', self.rset,
+                                     row=row, col=col)))
+
+
+# workflow images #############################################################
+
+class ViewWorkflowAction(action.Action):
+    id = 'workflow'
+    __select__ = implements('CWEType') & has_related_entities('state_of', 'object')
+
+    category = 'mainactions'
+    title = _('view workflow')
+    def url(self):
+        entity = self.rset.get_entity(self.row or 0, self.col or 0)
+        return entity.absolute_url(vid='workflow')
+
+
+class CWETypeWorkflowView(view.EntityView):
+    id = 'workflow'
+    __select__ = implements('CWEType')
+    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)
+        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"/>' % (
+            html_escape(entity.absolute_url(vid='ewfgraph')),
+            html_escape(self.req._('graphical workflow for %s') % entity.name)))
+
+
+class WorkflowDotPropsHandler(object):
+    def __init__(self, req):
+        self._ = req._
+
+    def node_properties(self, stateortransition):
+        """return default DOT drawing options for a state or transition"""
+        props = {'label': stateortransition.name,
+                 'fontname': 'Courier'}
+        if hasattr(stateortransition, 'state_of'):
+            props['shape'] = 'box'
+            props['style'] = 'filled'
+            if stateortransition.reverse_initial_state:
+                props['color'] = '#88CC88'
+        else:
+            props['shape'] = 'ellipse'
+            descr = []
+            tr = stateortransition
+            if tr.require_group:
+                descr.append('%s %s'% (
+                    self._('groups:'),
+                    ','.join(g.name for g in tr.require_group)))
+            if tr.condition:
+                descr.append('%s %s'% (self._('condition:'), tr.condition))
+            if descr:
+                props['label'] += escape('\n'.join(descr))
+        return props
+
+    def edge_properties(self, transition, fromstate, tostate):
+        return {'label': '', 'dir': 'forward',
+                'color': 'black', 'style': 'filled'}
+
+
+class WorkflowVisitor:
+    def __init__(self, entity):
+        self.entity = entity
+
+    def nodes(self):
+        for state in self.entity.reverse_state_of:
+            state.complete()
+            yield state.eid, state
+
+        for transition in self.entity.reverse_transition_of:
+            transition.complete()
+            yield transition.eid, transition
+
+    def edges(self):
+        for transition in self.entity.reverse_transition_of:
+            for incomingstate in transition.reverse_allowed_transition:
+                yield incomingstate.eid, transition.eid, transition
+            yield transition.eid, transition.destination().eid, transition
+
+
+class CWETypeWorkflowImageView(TmpFileViewMixin, view.EntityView):
+    id = 'ewfgraph'
+    content_type = 'image/png'
+    __select__ = implements('CWEType')
+
+    def _generate(self, tmpfile):
+        """display schema information for an entity"""
+        entity = self.entity(self.row, self.col)
+        visitor = WorkflowVisitor(entity)
+        prophdlr = WorkflowDotPropsHandler(self.req)
+        generator = GraphGenerator(DotBackend('workflow', 'LR',
+                                              ratio='compress', size='30,12'))
+        return generator.generate(visitor, prophdlr, tmpfile)
+
--- a/web/views/xbel.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/views/xbel.py	Mon May 04 13:09:48 2009 +0200
@@ -1,7 +1,7 @@
 """xbel views
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
@@ -9,7 +9,9 @@
 
 from logilab.mtconverter import html_escape
 
-from cubicweb.web.views.baseviews import XmlView, EntityView
+from cubicweb.selectors import implements
+from cubicweb.view import EntityView
+from cubicweb.web.views.xmlrss import XmlView
 
 
 class XbelView(XmlView):
@@ -45,9 +47,10 @@
 
     def url(self, entity):
         return entity.absolute_url()
+
         
 class XbelItemBookmarkView(XbelItemView):
-    accepts = ('Bookmark',)
+    __select__ = implements('Bookmark')
 
     def url(self, entity):
         return entity.actual_url()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/xmlrss.py	Mon May 04 13:09:48 2009 +0200
@@ -0,0 +1,205 @@
+"""base xml and rss views
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from time import timezone
+
+from logilab.mtconverter import xml_escape
+
+from cubicweb.selectors import non_final_entity, one_line_rset, appobject_selectable
+from cubicweb.view import EntityView, AnyRsetView
+from cubicweb.web.httpcache import MaxAgeHTTPCacheManager
+from cubicweb.web.component import Component
+from cubicweb.web.box import BoxTemplate
+from cubicweb.common.uilib import simple_sgml_tag
+
+_ = unicode
+
+
+# base xml views ##############################################################
+
+class XmlView(EntityView):
+    """xml view for entities"""
+    id = 'xml'
+    title = _('xml')
+    templatable = False
+    content_type = 'text/xml'
+    xml_root = 'rset'
+    item_vid = 'xmlitem'
+
+    def cell_call(self, row, col):
+        self.wview(self.item_vid, self.rset, row=row, col=col)
+
+    def call(self):
+        """display a list of entities by calling their <item_vid> view"""
+        self.w(u'<?xml version="1.0" encoding="%s"?>\n' % self.req.encoding)
+        self.w(u'<%s size="%s">\n' % (self.xml_root, len(self.rset)))
+        for i in xrange(self.rset.rowcount):
+            self.cell_call(i, 0)
+        self.w(u'</%s>\n' % self.xml_root)
+
+
+class XmlItemView(EntityView):
+    id = 'xmlitem'
+
+    def cell_call(self, row, col):
+        """ element as an item for an xml feed """
+        entity = self.complete_entity(row, col)
+        self.w(u'<%s>\n' % (entity.e_schema))
+        for rschema, attrschema in entity.e_schema.attribute_definitions():
+            attr = rschema.type
+            try:
+                value = entity[attr]
+            except KeyError:
+                # Bytes
+                continue
+            if value is not None:
+                if attrschema == 'Bytes':
+                    from base64 import b64encode
+                    value = '<![CDATA[%s]]>' % b64encode(value.getvalue())
+                elif isinstance(value, basestring):
+                    value = xml_escape(value)
+                self.w(u'  <%s>%s</%s>\n' % (attr, value, attr))
+        self.w(u'</%s>\n' % (entity.e_schema))
+
+
+class XmlRsetView(AnyRsetView):
+    """dumps raw rset as xml"""
+    id = 'rsetxml'
+    title = _('xml export')
+    templatable = False
+    content_type = 'text/xml'
+    xml_root = 'rset'
+
+    def call(self):
+        w = self.w
+        rset, descr = self.rset, self.rset.description
+        eschema = self.schema.eschema
+        labels = self.columns_labels(False)
+        w(u'<?xml version="1.0" encoding="%s"?>\n' % self.req.encoding)
+        w(u'<%s query="%s">\n' % (self.xml_root, xml_escape(rset.printable_rql())))
+        for rowindex, row in enumerate(self.rset):
+            w(u' <row>\n')
+            for colindex, val in enumerate(row):
+                etype = descr[rowindex][colindex]
+                tag = labels[colindex]
+                attrs = {}
+                if '(' in tag:
+                    attrs['expr'] = tag
+                    tag = 'funccall'
+                if val is not None and not eschema(etype).is_final():
+                    attrs['eid'] = val
+                    # csvrow.append(val) # val is eid in that case
+                    val = self.view('textincontext', rset,
+                                    row=rowindex, col=colindex)
+                else:
+                    val = self.view('final', rset, displaytime=True,
+                                    row=rowindex, col=colindex, format='text/plain')
+                w(simple_sgml_tag(tag, val, **attrs))
+            w(u' </row>\n')
+        w(u'</%s>\n' % self.xml_root)
+
+
+# RSS stuff ###################################################################
+
+class RSSFeedURL(Component):
+    id = 'rss_feed_url'
+    __select__ = non_final_entity()
+
+    def feed_url(self):
+        return self.build_url(rql=self.limited_rql(), vid='rss')
+
+
+class RSSEntityFeedURL(Component):
+    id = 'rss_feed_url'
+    __select__ = non_final_entity() & one_line_rset()
+
+    def feed_url(self):
+        return self.entity(0, 0).rss_feed_url()
+
+
+class RSSIconBox(BoxTemplate):
+    """just display the RSS icon on uniform result set"""
+    id = 'rss'
+    __select__ = (BoxTemplate.__select__
+                  & appobject_selectable('components', 'rss_feed_url'))
+
+    visible = False
+    order = 999
+
+    def call(self, **kwargs):
+        try:
+            rss = self.req.external_resource('RSS_LOGO')
+        except KeyError:
+            self.error('missing RSS_LOGO external resource')
+            return
+        urlgetter = self.vreg.select_component('rss_feed_url', self.req, self.rset)
+        url = urlgetter.feed_url()
+        self.w(u'<a href="%s"><img src="%s" alt="rss"/></a>\n' % (xml_escape(url), rss))
+
+
+class RssView(XmlView):
+    id = 'rss'
+    title = _('rss')
+    templatable = False
+    content_type = 'text/xml'
+    http_cache_manager = MaxAgeHTTPCacheManager
+    cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
+
+    def _open(self):
+        req = self.req
+        self.w(u'<?xml version="1.0" encoding="%s"?>\n' % req.encoding)
+        self.w(u'<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">\n')
+        self.w(u'  <channel>\n')
+        self.w(u'    <title>%s RSS Feed</title>\n' % xml_escape(self.page_title()))
+        self.w(u'    <description>%s</description>\n' % xml_escape(req.form.get('vtitle', '')))
+        params = req.form.copy()
+        params.pop('vid', None)
+        self.w(u'    <link>%s</link>\n' % xml_escape(self.build_url(**params)))
+
+    def _close(self):
+        self.w(u'  </channel>\n')
+        self.w(u'</rss>')
+
+    def call(self):
+        """display a list of entities by calling their <item_vid> view"""
+        self._open()
+        for i in xrange(self.rset.rowcount):
+            self.cell_call(i, 0)
+        self._close()
+
+    def cell_call(self, row, col):
+        self.wview('rssitem', self.rset, row=row, col=col)
+
+
+class RssItemView(EntityView):
+    id = 'rssitem'
+    date_format = '%%Y-%%m-%%dT%%H:%%M%+03i:00' % (timezone / 3600)
+    add_div_section = False
+
+    def cell_call(self, row, col):
+        entity = self.complete_entity(row, col)
+        self.w(u'<item>\n')
+        self.w(u'<guid isPermaLink="true">%s</guid>\n' % xml_escape(entity.absolute_url()))
+        self.render_title_link(entity)
+        self._marker('description', xml_escape(entity.dc_description()))
+        self._marker('dc:date', entity.dc_date(self.date_format))
+        self.render_entity_creator(entity)
+        self.w(u'</item>\n')
+
+    def render_title_link(self, entity):
+        self._marker('title', entity.dc_long_title())
+        self._marker('link', entity.absolute_url())
+
+    def render_entity_creator(self, entity):
+        if entity.creator:
+            self._marker('dc:creator', entity.dc_creator())
+
+
+    def _marker(self, marker, value):
+        if value:
+            self.w(u'  <%s>%s</%s>\n' % (marker, xml_escape(value), marker))
--- a/web/wdoc/custom_view_rss_fr.rst	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/wdoc/custom_view_rss_fr.rst	Mon May 04 13:09:48 2009 +0200
@@ -14,5 +14,5 @@
 
 :raw-html:`<p><a class="reference"
 href="view?vid=rss&amp;rql=Any+X%2CM+WHERE+X+modification_date+M+ORDERBY+M+DESC+LIMIT+30"><img
-alt="rss" src="data/rss.png"> latest changes</a></p>`
+alt="rss" src="data/rss.png"> latest changes</img></a></p>`
 
--- a/web/wdoc/tut_rql_en.rst	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/wdoc/tut_rql_en.rst	Mon May 04 13:09:48 2009 +0200
@@ -127,16 +127,16 @@
 * `creation_date (Datetime)`, date on which the entity has been created
 * `modification_date (Datetime)`, lastest date on which the entity has been modified
 
-* `created_by (EUser)`, relation to the user which has created this entity
+* `created_by (CWUser)`, relation to the user which has created this entity
 
-* `owned_by (EUser)`, relation to the user()s considered as owner of this
+* `owned_by (CWUser)`, relation to the user()s considered as owner of this
   entity, the entity's creator by default
 
 * `is (Eetype)`, special relation to specify a variable type.
 
 A user's entity has the following schema:
 
-:EUser:
+:CWUser:
   ::
 
 	login  	  (String) not null
--- a/web/wdoc/tut_rql_fr.rst	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/wdoc/tut_rql_fr.rst	Mon May 04 13:09:48 2009 +0200
@@ -134,9 +134,9 @@
 * `creation_date (Datetime)`, date de création de l'entité
 * `modification_date (Datetime)`, date de dernière modification de l'entité
 
-* `created_by (EUser)`, relation vers l'utilisateur ayant créé l'entité
+* `created_by (CWUser)`, relation vers l'utilisateur ayant créé l'entité
 
-* `owned_by (EUser)`, relation vers le où les utilisateurs considérés comme 
+* `owned_by (CWUser)`, relation vers le où les utilisateurs considérés comme 
   propriétaire de l'entité, par défaut le créateur de l'entité
 
 * `is (Eetype)`, relation spéciale permettant de spécifier le
@@ -144,7 +144,7 @@
 
 Enfin, le schéma standard d'un utilisateur est le suivant :
 
-:EUser:
+:CWUser:
   ::
 
 	login  	  (String, obligatoire)
--- a/web/webconfig.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/webconfig.py	Mon May 04 13:09:48 2009 +0200
@@ -7,8 +7,7 @@
 __docformat__ = "restructuredtext en"
 
 import os
-from os.path import join, dirname, exists, split
-from urlparse import urljoin
+from os.path import join, exists, split
 
 from logilab.common.configuration import Method
 from logilab.common.decorators import cached
@@ -26,7 +25,7 @@
       'sitewide': True, 'group': 'ui', 
       }),
     ('main-template',
-     {'type' : 'string', 'default': 'main',
+     {'type' : 'string', 'default': 'main-template',
       'help': _('id of main template used to render pages'),
       'sitewide': True, 'group': 'ui',
       }),
@@ -80,6 +79,12 @@
           'if anonymous-user is set',
           'group': 'main', 'inputlevel': 1,
           }),
+        ('allow-email-login',
+         {'type' : 'yn',
+          'default': False,
+          'help': 'allow users to login with their primary email if set',
+          'group': 'main', 'inputlevel': 2,
+          }),
         ('query-log-file',
          {'type' : 'string',
           'default': None,
@@ -193,12 +198,21 @@
         try:
             cube = self.cubes()[0]
             cubeeid = self.cube_pkginfo(cube).cube_eid
-        except Exception, ex:
+        except Exception:
             return None
         if cubeeid:
             return 'http://intranet.logilab.fr/jpl/view?__linkto=concerns:%s:subject&etype=Ticket&type=bug&vid=creation' % cubeeid
         return None
 
+    def fckeditor_installed(self):
+        return exists(self.ext_resources['FCKEDITOR_PATH'])
+    
+    def eproperty_definitions(self):
+        for key, pdef in super(WebConfiguration, self).eproperty_definitions():
+            if key == 'ui.fckeditor' and not self.fckeditor_installed():
+                continue
+            yield key, pdef
+                
     # method used to connect to the repository: 'inmemory' / 'pyro'
     # Pyro repository by default
     repo_method = 'pyro'
--- a/web/webctl.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/webctl.py	Mon May 04 13:09:48 2009 +0200
@@ -23,8 +23,8 @@
             print '-' * 72
             config.input_config('pyro-client', inputlevel)
         if confirm('allow anonymous access', False):
-           config.global_set_option('anonymous-user', 'anon') 
-           config.global_set_option('anonymous-password', 'anon') 
+            config.global_set_option('anonymous-user', 'anon') 
+            config.global_set_option('anonymous-password', 'anon') 
         
     def postcreate(self):
         """hooks called once application's initialization has been completed"""
--- a/web/widgets.py	Thu Apr 30 16:16:39 2009 +0200
+++ b/web/widgets.py	Mon May 04 13:09:48 2009 +0200
@@ -4,13 +4,12 @@
 serialization time
 
 :organization: Logilab
-:copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
 """
 __docformat__ = "restructuredtext en"
 
-from simplejson import dumps
-from mx.DateTime import now, today
+from datetime import datetime
 
 from logilab.mtconverter import html_escape
 
@@ -64,7 +63,7 @@
     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):
@@ -84,12 +83,12 @@
         because widget instances are cached)
         """
         # brute force copy (subclasses don't have the
-        # same __init__ prototype) 
+        # 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"""
@@ -106,7 +105,7 @@
             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)
@@ -117,7 +116,7 @@
             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)
@@ -131,7 +130,7 @@
         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
@@ -155,16 +154,16 @@
                         % (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"""
@@ -182,7 +181,7 @@
         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
@@ -198,7 +197,7 @@
         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))
 
@@ -215,13 +214,13 @@
         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
@@ -260,7 +259,7 @@
     def __init__(self, vreg, subjschema, rschema, objschema,
                  role='subject', **kwattrs):
         InputWidget.__init__(self, vreg, subjschema, rschema, objschema,
-                             role='subject', 
+                             role='subject',
                              **kwattrs)
         # disable access key
         del self.attrs['accesskey']
@@ -272,18 +271,18 @@
     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):
 
@@ -299,15 +298,15 @@
         """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
@@ -346,22 +345,22 @@
 
 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'    
+    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
@@ -376,10 +375,10 @@
             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"""
@@ -387,16 +386,12 @@
             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 add_fckeditor_info(self, req):
-        req.add_js('fckeditor.js')
-        req.fckeditor_config()
-    
+
     def _edit_render(self, entity, with_format=True):
         req = entity.req
         editor = self._edit_render_textarea(entity, with_format)
@@ -404,7 +399,7 @@
         if isinstance(value, basestring):
             value = html_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)
@@ -412,10 +407,10 @@
         if isinstance(dvalue, basestring):
             dvalue = html_escape(dvalue)
         if entity.use_fckeditor(self.name):
-            self.add_fckeditor_info(entity.req)
+            entity.req.fckeditor_config()
             if with_format:
                 if entity.has_eid():
-                    format = entity.format(self.name)
+                    format = entity.attr_metadata(self.name, 'format')
                 else:
                     format = ''
                 frname = eid_param(self.name + '_format', entity.eid)
@@ -424,7 +419,7 @@
                     frname, format, frname)
             return u'%s<textarea cubicweb:type="wysiwyg" onkeypress="autogrow(this)" name="%s" %s>%s</textarea>' % (
                 hidden, self.rname, self.format_attrs(), dvalue)
-        if with_format and entity.has_format(self.name):
+        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()
@@ -432,8 +427,8 @@
             fmtwdgstr = ''
         return u'%s<br/><textarea onkeypress="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):
@@ -466,13 +461,14 @@
                 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.has_format(self.name) or entity.has_text_encoding(self.name):
+        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>' %
                         (html_escape(toggle_action(divid)),
@@ -497,7 +493,7 @@
                 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)
 
@@ -515,17 +511,17 @@
                 '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.format(self.name) in ('text/plain', 'text/html', 'text/rest'):
+        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.text_encoding(self.name)
+                encoding = entity.attr_metadata(self.name, 'encoding')
                 try:
                     entity[self.name] = unicode(data.getvalue(), encoding)
                 except UnicodeError:
@@ -539,7 +535,7 @@
 
 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,
@@ -550,10 +546,10 @@
                 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"'
@@ -579,9 +575,9 @@
         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,
@@ -596,11 +592,11 @@
         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)]
@@ -608,13 +604,13 @@
         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
@@ -637,7 +633,7 @@
 
 
 class DynamicComboBoxWidget(RawDynamicComboBoxWidget):
-    
+
     def vocabulary(self, entity, limit=None):
         return sorted(super(DynamicComboBoxWidget, self).vocabulary(entity, limit))
 
@@ -668,17 +664,17 @@
         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
@@ -688,7 +684,7 @@
     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:
@@ -701,26 +697,35 @@
             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")
-    
+    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)
@@ -735,17 +740,7 @@
 
     def render_example(self, req):
         formatstr = req.property_value(self.format_key)
-        return now().strftime(formatstr)
-
-    @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)
+        return datetime.now().strftime(formatstr)
 
 
     def _edit_render(self, entity):
@@ -775,7 +770,7 @@
         req.add_css(('cubicweb.calendar_popup.css',))
         inputid = self.attrs.get('id', self.rname)
         helperid = "%shelper" % inputid
-        _today = today()
+        _today = datetime.now()
         year = int(req.form.get('year', _today.year))
         month = int(req.form.get('month', _today.month))
 
@@ -786,23 +781,20 @@
 
 class DateTimeWidget(DateWidget):
     format_key = 'ui.datetime-format'
-    
-    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': now().strftime(formatstr1),
-            'fmt2': now().strftime(formatstr2),
-            }
-
-
-
 
     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(formatstr1),
+            'fmt2': datetime.now().strftime(formatstr2),
+            }
+
 
 class TimeWidget(StringWidget):
     format_key = 'ui.time-format'
@@ -811,26 +803,26 @@
         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 = html_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:
@@ -840,59 +832,6 @@
 
 
 
-class PropertyKeyWidget(ComboBoxWidget):
-    """specific widget for EProperty.pkey field to set the value widget according to
-    the selected key
-    """
-    
-    def _edit_render(self, entity):
-        entity.req.add_js( ('cubicweb.ajax.js', 'cubicweb.edition.js') )
-        vtabindex = self.attrs.get('tabindex', 0) + 1
-        self.attrs['onchange'] = "javascript:setPropValueWidget('%s', %s)" % (
-            entity.eid, vtabindex)
-        # limit size
-        if not entity.has_eid():
-            self.attrs['size'] = 10
-        else:
-            self.attrs['size'] = 1
-        return super(PropertyKeyWidget, self)._edit_render(entity)
-    
-    def vocabulary(self, entity):
-        _ = entity.req._
-        if entity.has_eid():
-            return [(_(entity.pkey), entity.pkey)]
-        # key beginning with 'system.' should usually not be edited by hand
-        choices = entity.vreg.user_property_keys()
-        return sorted(zip((_(v) for v in choices), choices))
-
-
-class PropertyValueWidget(Widget):
-    """specific widget for EProperty.value field which will be different according to
-    the selected key type and vocabulary information
-    """
-    
-    def render_help(self, entity):
-        return u''
-        
-    def render(self, entity):
-        assert entity.has_eid()
-        w = self.vreg.property_value_widget(entity.pkey, req=entity.req, **self.attrs)
-        return w.render(entity)
-        
-    def _edit_render(self, entity):
-        if not entity.has_eid():
-            # no key set yet, just include an empty div which will be filled
-            # on key selection
-            # empty span as well else html validation fail (label is refering to this id)
-            return u'<div id="div:%s"><span id="%s"/></div>' % (self.rname, self.attrs.get('id'))
-        w = self.vreg.property_value_widget(entity.pkey, req=entity.req, **self.attrs)
-        if entity.pkey.startswith('system.'):
-            value = '<span class="value" id="%s">%s</span>' % (self.attrs.get('id'), w.render(entity))
-            msg = entity.req._('value associated to this key is not editable manually')
-            return value + '<div>%s</div>' % msg
-        return w.edit_render(entity, self.attrs.get('tabindex'), includehelp=True)
-    
-
 def widget_factory(vreg, subjschema, rschema, objschema, role='subject',
                    **kwargs):
     """return the most adapated widget to edit the relation
@@ -902,7 +841,7 @@
         eclass, subjschema = _eclass_eschema(subjschema)
     else:
         eclass, objschema = _eclass_eschema(objschema)
-    if eclass is not None and rschema in eclass.widgets:
+    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')
@@ -920,14 +859,14 @@
 
 
 # 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) 
+            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:
@@ -977,7 +916,7 @@
     'String' :  StringWidget,
     'Time':     TimeWidget,
     }
-    
+
 # widgets registry
 WIDGETS = {}
 def register(widget_list):