backport stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Wed, 30 Mar 2011 11:17:58 +0200
changeset 7141 ef29d3ea3909
parent 7109 611663348158 (current diff)
parent 7140 ba51dac1115d (diff)
child 7146 215275be4877
backport stable
--- a/.hgtags	Thu Mar 24 15:21:13 2011 +0100
+++ b/.hgtags	Wed Mar 30 11:17:58 2011 +0200
@@ -186,3 +186,5 @@
 8daabda9f571863e8754f8ab722744c417ba3abf cubicweb-debian-version-3.11.0-1
 d0410eb4d8bbf657d7f32b0c681db09b1f8119a0 cubicweb-version-3.11.1
 77318f1ec4aae3523d455e884daf3708c3c79af7 cubicweb-debian-version-3.11.1-1
+56ae3cd5f8553678a2b1d4121b61241598d0ca68 cubicweb-version-3.11.2
+954b5b51cd9278eb45d66be1967064d01ab08453 cubicweb-debian-version-3.11.2-1
--- a/__pkginfo__.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/__pkginfo__.py	Wed Mar 30 11:17:58 2011 +0200
@@ -22,7 +22,7 @@
 
 modname = distname = "cubicweb"
 
-numversion = (3, 11, 1)
+numversion = (3, 11, 2)
 version = '.'.join(str(num) for num in numversion)
 
 description = "a repository of entities / relations for knowledge management"
@@ -40,7 +40,7 @@
 ]
 
 __depends__ = {
-    'logilab-common': '>= 0.55.0',
+    'logilab-common': '>= 0.55.2',
     'logilab-mtconverter': '>= 0.8.0',
     'rql': '>= 0.28.0',
     'yams': '>= 0.30.4',
--- a/cwconfig.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/cwconfig.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -325,7 +325,8 @@
          {'type' : 'string',
           'default': '',
           'help': 'Pyro name server\'s host. If not set, will be detected by a \
-broadcast query. It may contains port information using <host>:<port> notation.',
+broadcast query. It may contains port information using <host>:<port> notation. \
+Use "NO_PYRONS" to create a Pyro server but not register to a pyro nameserver',
           'group': 'pyro', 'level': 1,
           }),
         ('pyro-ns-group',
@@ -843,9 +844,8 @@
         if not exists(_INSTANCES_DIR):
             os.makedirs(_INSTANCES_DIR)
 
-    # for some commands (creation...) we don't want to initialize gettext
-    set_language = True
-    # set this to true to allow somethings which would'nt be possible
+    # set to true during repair (shell, migration) to allow some things which
+    # wouldn't be possible otherwise
     repairing = False
 
     options = CubicWebNoAppConfiguration.options + (
@@ -900,13 +900,13 @@
         return mdir
 
     @classmethod
-    def config_for(cls, appid, config=None, debugmode=False):
+    def config_for(cls, appid, config=None, debugmode=False, creating=False):
         """return a configuration instance for the given instance identifier
         """
         cls.load_available_configs()
         config = config or guess_configuration(cls.instance_home(appid))
         configcls = configuration_cls(config)
-        return configcls(appid, debugmode)
+        return configcls(appid, debugmode, creating)
 
     @classmethod
     def possible_configurations(cls, appid):
@@ -966,8 +966,6 @@
             log_path = os.path.join(_INSTALL_PREFIX, 'var', 'log', 'cubicweb', '%s-%s.log')
             return log_path % (self.appid, self.name)
 
-
-
     def default_pid_file(self):
         """return default path to the pid file of the instance'server"""
         if self.mode == 'system':
@@ -985,8 +983,10 @@
 
     # instance methods used to get instance specific resources #############
 
-    def __init__(self, appid, debugmode=False):
+    def __init__(self, appid, debugmode=False, creating=False):
         self.appid = appid
+        # set to true while creating an instance
+        self.creating = creating
         super(CubicWebConfiguration, self).__init__(debugmode)
         fake_gettext = (unicode, lambda ctx, msgid: unicode(msgid))
         for lang in self.available_languages():
@@ -1077,7 +1077,7 @@
     def load_configuration(self):
         """load instance's configuration files"""
         super(CubicWebConfiguration, self).load_configuration()
-        if self.apphome and self.set_language:
+        if self.apphome and not self.creating:
             # init gettext
             self._gettext_init()
 
--- a/cwctl.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/cwctl.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -300,6 +300,11 @@
                     print '* cube %s version %s is installed, but version %s is required by %s' % (
                         cube, cfgpb.cubes[cube], version, src)
 
+def check_options_consistency(config):
+    if config.automatic and config.config_level > 0:
+        raise BadCommandUsage('--automatic and --config-level should not be '
+                              'used together')
+
 class CreateInstanceCommand(Command):
     """Create an instance from a cube. This is an unified
     command which can handle web / server / all-in-one installation
@@ -309,7 +314,7 @@
     <cube>
       the name of cube to use (list available cube names using
       the "list" command). You can use several cubes by separating
-      them using comma (e.g. 'jpl,eemail')
+      them using comma (e.g. 'jpl,email')
     <instance>
       an identifier for the instance to create
     """
@@ -317,34 +322,39 @@
     arguments = '<cube> <instance>'
     min_args = max_args = 2
     options = (
-        ("config-level",
+        ('automatic',
+         {'short': 'a', 'action' : 'store_true',
+          'default': False,
+          'help': 'automatic mode: never ask and use default answer to every '
+          'question. this may require that your login match a database super '
+          'user (allowed to create database & all).',
+          }),
+        ('config-level',
          {'short': 'l', 'type' : 'int', 'metavar': '<level>',
           'default': 0,
-          'help': 'configuration level (0..2): 0 will ask for essential \
-configuration parameters only while 2 will ask for all parameters',
-          }
-         ),
-        ("config",
+          'help': 'configuration level (0..2): 0 will ask for essential '
+          'configuration parameters only while 2 will ask for all parameters',
+          }),
+        ('config',
          {'short': 'c', 'type' : 'choice', 'metavar': '<install type>',
           'choices': ('all-in-one', 'repository', 'twisted'),
           'default': 'all-in-one',
-          'help': 'installation type, telling which part of an instance \
-should be installed. You can list available configurations using the "list" \
-command. Default to "all-in-one", e.g. an installation embedding both the RQL \
-repository and the web server.',
-          }
-         ),
+          'help': 'installation type, telling which part of an instance '
+          'should be installed. You can list available configurations using the'
+          ' "list" command. Default to "all-in-one", e.g. an installation '
+          'embedding both the RQL repository and the web server.',
+          }),
         )
 
     def run(self, args):
         """run the command with its specific arguments"""
         from logilab.common.textutils import splitstrip
+        check_options_consistency(self.config)
         configname = self.config.config
         cubes, appid = args
         cubes = splitstrip(cubes)
         # get the configuration and helper
-        config = cwcfg.config_for(appid, configname)
-        config.set_language = False
+        config = cwcfg.config_for(appid, configname, creating=True)
         cubes = config.expand_cubes(cubes)
         config.init_cubes(cubes)
         helper = self.config_helper(config)
@@ -361,15 +371,19 @@
         print '\n'+underline_title('Creating the instance %s' % appid)
         create_dir(config.apphome)
         # cubicweb-ctl configuration
-        print '\n'+underline_title('Configuring the instance (%s.conf)' % configname)
-        config.input_config('main', self.config.config_level)
+        if not self.config.automatic:
+            print '\n'+underline_title('Configuring the instance (%s.conf)'
+                                       % configname)
+            config.input_config('main', self.config.config_level)
         # configuration'specific stuff
         print
-        helper.bootstrap(cubes, self.config.config_level)
+        helper.bootstrap(cubes, self.config.automatic, self.config.config_level)
         # input for cubes specific options
-        for section in set(sect.lower() for sect, opt, optdict in config.all_options()
-                           if optdict.get('level') <= self.config.config_level):
-            if section not in ('main', 'email', 'pyro'):
+        sections = set(sect.lower() for sect, opt, odict in config.all_options()
+                       if 'type' in odict
+                       and odict.get('level') <= self.config.config_level)
+        for section in sections:
+            if section not in ('main', 'email', 'pyro', 'web'):
                 print '\n' + underline_title('%s options' % section)
                 config.input_config(section, self.config.config_level)
         # write down configuration
@@ -384,8 +398,9 @@
         errors = config.i18ncompile(langs)
         if errors:
             print '\n'.join(errors)
-            if not ASK.confirm('error while compiling message catalogs, '
-                               'continue anyway ?'):
+            if self.config.automatic \
+                   or not ASK.confirm('error while compiling message catalogs, '
+                                      'continue anyway ?'):
                 print 'creation not completed'
                 return
         # create the additional data directory for this instance
@@ -398,7 +413,7 @@
             print 'set %s as owner of the data directory' % config['uid']
             chown(config.appdatahome, config['uid'])
         print '\n-> creation done for %r.\n' % config.apphome
-        helper.postcreate()
+        helper.postcreate(self.config.automatic)
 
     def _handle_win32(self, config, appid):
         if sys.platform != 'win32':
--- a/dataimport.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/dataimport.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,5 +1,5 @@
 # -*- coding: utf-8 -*-
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -81,7 +81,7 @@
 from logilab.common.deprecation import deprecated
 
 from cubicweb.server.utils import eschema_eid
-from cubicweb.server.ssplanner import EditedEntity
+from cubicweb.server.edition import EditedEntity
 
 def count_lines(stream_or_filename):
     if isinstance(stream_or_filename, basestring):
@@ -473,6 +473,11 @@
         self.rql('SET X %s Y WHERE X eid %%(x)s, Y eid %%(y)s' % rtype,
                  {'x': int(eid_from), 'y': int(eid_to)})
 
+    def find_entities(self, *args, **kwargs):
+        return self.session.find_entities(*args, **kwargs)
+
+    def find_one_entity(self, *args, **kwargs):
+        return self.session.find_one_entity(*args, **kwargs)
 
 # the import controller ########################################################
 
--- a/dbapi.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/dbapi.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -102,11 +102,14 @@
         return Repository(config, vreg=vreg)
     else: # method == 'pyro'
         # resolve the Pyro object
-        from logilab.common.pyro_ext import ns_get_proxy
+        from logilab.common.pyro_ext import ns_get_proxy, get_proxy
         pyroid = database or config['pyro-instance-id'] or config.appid
         try:
-            return ns_get_proxy(pyroid, defaultnsgroup=config['pyro-ns-group'],
-                                nshost=config['pyro-ns-host'])
+            if config['pyro-ns-host'] == 'NO_PYRONS':
+                return get_proxy(pyroid)
+            else:
+                return ns_get_proxy(pyroid, defaultnsgroup=config['pyro-ns-group'],
+                                    nshost=config['pyro-ns-host'])
         except Exception, ex:
             raise ConnectionError(str(ex))
 
--- a/debian/changelog	Thu Mar 24 15:21:13 2011 +0100
+++ b/debian/changelog	Wed Mar 30 11:17:58 2011 +0200
@@ -1,3 +1,9 @@
+cubicweb (3.11.2-1) unstable; urgency=low
+
+  * new upstream release 
+
+ -- Nicolas Chauvat <nicolas.chauvat@logilab.fr>  Mon, 28 Mar 2011 19:18:54 +0200
+
 cubicweb (3.11.1-1) unstable; urgency=low
 
   * new upstream release
--- a/debian/control	Thu Mar 24 15:21:13 2011 +0100
+++ b/debian/control	Wed Mar 30 11:17:58 2011 +0200
@@ -97,7 +97,7 @@
 Package: cubicweb-common
 Architecture: all
 XB-Python-Version: ${python:Versions}
-Depends: ${misc:Depends}, ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.8.0), python-logilab-common (>= 0.55.0), python-yams (>= 0.30.4), python-rql (>= 0.28.0), python-lxml
+Depends: ${misc:Depends}, ${python:Depends}, graphviz, gettext, python-logilab-mtconverter (>= 0.8.0), python-logilab-common (>= 0.55.2), python-yams (>= 0.30.4), python-rql (>= 0.28.0), python-lxml
 Recommends: python-simpletal (>= 4.0), python-crypto
 Conflicts: cubicweb-core
 Replaces: cubicweb-core
--- a/devtools/__init__.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/devtools/__init__.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -127,7 +127,6 @@
 
 class TestServerConfiguration(ServerConfiguration):
     mode = 'test'
-    set_language = False
     read_instance_schema = False
     init_repository = True
 
@@ -497,7 +496,8 @@
     @cached
     def dbcnx(self):
         from cubicweb.server.serverctl import _db_sys_cnx
-        return  _db_sys_cnx(self.system_source, 'CREATE DATABASE and / or USER', verbose=0)
+        return  _db_sys_cnx(self.system_source, 'CREATE DATABASE and / or USER',
+                            interactive=False)
 
     @property
     @cached
@@ -514,7 +514,8 @@
 
             createdb(self.helper, self.system_source, self.dbcnx, self.cursor)
             self.dbcnx.commit()
-            cnx = system_source_cnx(self.system_source, special_privs='LANGUAGE C', verbose=0)
+            cnx = system_source_cnx(self.system_source, special_privs='LANGUAGE C',
+                                    interactive=False)
             templcursor = cnx.cursor()
             try:
                 # XXX factorize with db-create code
--- a/doc/book/en/devrepo/testing.rst	Thu Mar 24 15:21:13 2011 +0100
+++ b/doc/book/en/devrepo/testing.rst	Wed Mar 30 11:17:58 2011 +0200
@@ -59,10 +59,10 @@
 
         def setup_database(self):
             req = self.request()
-            group_etype = req.execute('Any X WHERE X name "CWGroup"').get_entity(0,0)
+            group_etype = req.find_one_entity('CWEType', name='CWGroup')
             c1 = req.create_entity('Classification', name=u'classif1',
                                    classifies=group_etype)
-            user_etype = req.execute('Any X WHERE X name "CWUser"').get_entity(0,0)
+            user_etype = req.find_one_entity('CWEType', name='CWUser')
             c2 = req.create_entity('Classification', name=u'classif2',
                                    classifies=user_etype)
             self.kw1 = req.create_entity('Keyword', name=u'kwgroup', included_in=c1)
@@ -228,7 +228,7 @@
 
         def test_admin(self):
             req = self.request()
-            rset = req.execute('Any C WHERE C is Conference')
+            rset = req.find_entities('Conference')
             self.assertListEqual(self.pactions(req, rset),
                                   [('workflow', workflow.WorkflowActions),
                                    ('edit', confactions.ModifyAction),
--- a/entities/authobjs.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/entities/authobjs.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -80,6 +80,20 @@
                          key, self.login)
         return self._cw.vreg.property_value(key)
 
+    def set_property(self, pkey, value):
+        value = unicode(value)
+        try:
+            prop = self._cw.execute(
+                'CWProperty X WHERE X pkey %(k)s, X for_user U, U eid %(u)s',
+                {'k': pkey, 'u': self.eid}).get_entity(0, 0)
+        except:
+            kwargs = dict(pkey=unicode(pkey), value=value)
+            if self.is_in_group('managers'):
+                kwargs['for_user'] = self
+            self._cw.create_entity('CWProperty', **kwargs)
+        else:
+            prop.set_attributes(value=value)
+
     def matching_groups(self, groups):
         """return the number of the given group(s) in which the user is
 
--- a/entity.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/entity.py	Wed Mar 30 11:17:58 2011 +0200
@@ -62,7 +62,6 @@
     return True
 
 
-
 class Entity(AppObject):
     """an entity instance has e_schema automagically set on
     the class and instances has access to their issuing cursor.
@@ -808,7 +807,11 @@
             else:
                 existant = None # instead of 'SO', improve perfs
             for select in rqlst.children:
-                rewriter.rewrite(select, [((searchedvar, searchedvar), rqlexprs)],
+                varmap = {}
+                for var in 'SO':
+                    if var in select.defined_vars:
+                        varmap[var] = var
+                rewriter.rewrite(select, [(varmap, rqlexprs)],
                                  select.solutions, args, existant)
             rql = rqlst.as_string()
         return rql, args
--- a/etwist/twctl.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/etwist/twctl.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -51,10 +51,10 @@
         """
         cfgname = 'all-in-one'
 
-        def bootstrap(self, cubes, inputlevel=0):
+        def bootstrap(self, cubes, automatic=False, inputlevel=0):
             """bootstrap this configuration"""
-            serverctl.RepositoryCreateHandler.bootstrap(self, cubes, inputlevel)
-            TWCreateHandler.bootstrap(self, cubes, inputlevel)
+            serverctl.RepositoryCreateHandler.bootstrap(self, cubes, automatic, inputlevel)
+            TWCreateHandler.bootstrap(self, cubes, automatic, inputlevel)
 
     class AllInOneStartHandler(TWStartHandler):
         cmdname = 'start'
--- a/hooks/syncsession.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/hooks/syncsession.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
--- a/req.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/req.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -35,6 +35,8 @@
 ONESECOND = timedelta(0, 1, 0)
 CACHE_REGISTRY = {}
 
+class FindEntityError(Exception):
+    """raised when find_one_entity() can not return one and only one entity"""
 
 def _check_cw_unsafe(kwargs):
     if kwargs.pop('_cw_unsafe', False):
@@ -140,6 +142,33 @@
         cls = self.vreg['etypes'].etype_class(etype)
         return cls.cw_instantiate(self.execute, **kwargs)
 
+    def find_entities(self, etype, **kwargs):
+        """find entities of the given type and attribute values.
+
+        >>> users = find_entities('CWGroup', name=u'users')
+        >>> groups = find_entities('CWGroup')
+        """
+        parts = ['Any X WHERE X is %s' % etype]
+        parts.extend('X %(attr)s %%(%(attr)s)s' % {'attr': attr} for attr in kwargs)
+        return self.execute(', '.join(parts), kwargs).entities()
+
+    def find_one_entity(self, etype, **kwargs):
+        """find one entity of the given type and attribute values.
+        raise :exc:`FindEntityError` if can not return one and only one entity.
+
+        >>> users = find_one_entity('CWGroup', name=u'users')
+        >>> groups = find_one_entity('CWGroup')
+        Exception()
+        """
+        parts = ['Any X WHERE X is %s' % etype]
+        parts.extend('X %(attr)s %%(%(attr)s)s' % {'attr': attr} for attr in kwargs)
+        rql = ', '.join(parts)
+        rset = self.execute(rql, kwargs)
+        if len(rset) != 1:
+            raise FindEntityError('Found %i entitie(s) when 1 was expected (rql=%s ; %s)'
+                                  % (len(rset), rql, repr(kwargs)))
+        return rset.get_entity(0,0)
+
     def ensure_ro_rql(self, rql):
         """raise an exception if the given rql is not a select query"""
         first = rql.split(None, 1)[0].lower()
--- a/rqlrewrite.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/rqlrewrite.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -24,6 +24,7 @@
 __docformat__ = "restructuredtext en"
 
 from rql import nodes as n, stmts, TypeResolverException
+from rql.utils import common_parent
 from yams import BadSchemaDefinition
 from logilab.common.graph import has_path
 
@@ -180,13 +181,23 @@
     def insert_snippets(self, snippets, varexistsmap=None):
         self.rewritten = {}
         for varmap, rqlexprs in snippets:
+            if isinstance(varmap, dict):
+                varmap = tuple(sorted(varmap.items()))
+            else:
+                assert isinstance(varmap, tuple), varmap
             if varexistsmap is not None and not varmap in varexistsmap:
                 continue
-            self.varmap = varmap
-            selectvar, snippetvar = varmap
+            self.insert_varmap_snippets(varmap, rqlexprs, varexistsmap)
+
+    def insert_varmap_snippets(self, varmap, rqlexprs, varexistsmap):
+        self.varmap = varmap
+        self.revvarmap = {}
+        self.varinfos = []
+        for i, (selectvar, snippetvar) in enumerate(varmap):
             assert snippetvar in 'SOX'
-            self.revvarmap = {snippetvar: selectvar}
-            self.varinfo = vi = {}
+            self.revvarmap[snippetvar] = (selectvar, i)
+            vi = {}
+            self.varinfos.append(vi)
             try:
                 vi['const'] = typed_eid(selectvar) # XXX gae
                 vi['rhs_rels'] = vi['lhs_rels'] = {}
@@ -194,42 +205,42 @@
                 try:
                     vi['stinfo'] = sti = self.select.defined_vars[selectvar].stinfo
                 except KeyError:
-                    # variable has been moved to a newly inserted subquery
+                    # variable may have been moved to a newly inserted subquery
                     # we should insert snippet in that subquery
                     subquery = self.select.aliases[selectvar].query
                     assert len(subquery.children) == 1
                     subselect = subquery.children[0]
                     RQLRewriter(self.session).rewrite(subselect, [(varmap, rqlexprs)],
                                                       subselect.solutions, self.kwargs)
-                    continue
+                    return
                 if varexistsmap is None:
                     vi['rhs_rels'] = dict( (r.r_type, r) for r in sti['rhsrelations'])
                     vi['lhs_rels'] = dict( (r.r_type, r) for r in sti['relations']
                                            if not r in sti['rhsrelations'])
                 else:
                     vi['rhs_rels'] = vi['lhs_rels'] = {}
-            parent = None
-            inserted = False
-            for rqlexpr in rqlexprs:
-                self.current_expr = rqlexpr
-                if varexistsmap is None:
-                    try:
-                        new = self.insert_snippet(varmap, rqlexpr.snippet_rqlst, parent)
-                    except Unsupported:
-                        continue
-                    inserted = True
-                    if new is not None:
-                        self.exists_snippet[rqlexpr] = new
-                    parent = parent or new
-                else:
-                    # called to reintroduce snippet due to ambiguity creation,
-                    # so skip snippets which are not introducing this ambiguity
-                    exists = varexistsmap[varmap]
-                    if self.exists_snippet[rqlexpr] is exists:
-                        self.insert_snippet(varmap, rqlexpr.snippet_rqlst, exists)
-            if varexistsmap is None and not inserted:
-                # no rql expression found matching rql solutions. User has no access right
-                raise Unauthorized()
+        parent = None
+        inserted = False
+        for rqlexpr in rqlexprs:
+            self.current_expr = rqlexpr
+            if varexistsmap is None:
+                try:
+                    new = self.insert_snippet(varmap, rqlexpr.snippet_rqlst, parent)
+                except Unsupported:
+                    continue
+                inserted = True
+                if new is not None:
+                    self.exists_snippet[rqlexpr] = new
+                parent = parent or new
+            else:
+                # called to reintroduce snippet due to ambiguity creation,
+                # so skip snippets which are not introducing this ambiguity
+                exists = varexistsmap[varmap]
+                if self.exists_snippet[rqlexpr] is exists:
+                    self.insert_snippet(varmap, rqlexpr.snippet_rqlst, exists)
+        if varexistsmap is None and not inserted:
+            # no rql expression found matching rql solutions. User has no access right
+            raise Unauthorized() # XXX bad constraint when inserting constraints
 
     def insert_snippet(self, varmap, snippetrqlst, parent=None):
         new = snippetrqlst.where.accept(self)
@@ -243,16 +254,23 @@
     def _insert_snippet(self, varmap, parent, new):
         if new is not None:
             if self._insert_scope is None:
-                insert_scope = self.varinfo.get('stinfo', {}).get('scope', self.select)
+                insert_scope = None
+                for vi in self.varinfos:
+                    scope = vi.get('stinfo', {}).get('scope', self.select)
+                    if insert_scope is None:
+                        insert_scope = scope
+                    else:
+                        insert_scope = common_parent(scope, insert_scope)
             else:
                 insert_scope = self._insert_scope
-            if self.varinfo.get('stinfo', {}).get('optrelations'):
+            if any(vi.get('stinfo', {}).get('optrelations') for vi in self.varinfos):
                 assert parent is None
                 self._insert_scope = self.snippet_subquery(varmap, new)
                 self.insert_pending()
                 self._insert_scope = None
                 return
-            new = n.Exists(new)
+            if not isinstance(new, (n.Exists, n.Not)):
+                new = n.Exists(new)
             if parent is None:
                 insert_scope.add_restriction(new)
             else:
@@ -291,7 +309,7 @@
                 varname = self.rewritten[key]
             except KeyError:
                 try:
-                    varname = self.revvarmap[key[-1]]
+                    varname = self.revvarmap[key[-1]][0]
                 except KeyError:
                     # variable isn't used anywhere else, we can't insert security
                     raise Unauthorized()
@@ -308,45 +326,51 @@
                 rqlexprs = eschema.get_rqlexprs(action)
                 if not rqlexprs:
                     raise Unauthorized()
-                self.insert_snippets([((varname, 'X'), rqlexprs)])
+                self.insert_snippets([({varname: 'X'}, rqlexprs)])
 
     def snippet_subquery(self, varmap, transformedsnippet):
         """introduce the given snippet in a subquery"""
         subselect = stmts.Select()
-        selectvar = varmap[0]
-        subselectvar = subselect.get_variable(selectvar)
-        subselect.append_selected(n.VariableRef(subselectvar))
         snippetrqlst = n.Exists(transformedsnippet.copy(subselect))
-        aliases = [selectvar]
-        stinfo = self.varinfo['stinfo']
-        need_null_test = False
-        for rel in stinfo['relations']:
-            rschema = self.schema.rschema(rel.r_type)
-            if rschema.final or (rschema.inlined and
-                                 not rel in stinfo['rhsrelations']):
-                rel.children[0].name = selectvar # XXX explain why
-                subselect.add_restriction(rel.copy(subselect))
-                for vref in rel.children[1].iget_nodes(n.VariableRef):
-                    if isinstance(vref.variable, n.ColumnAlias):
-                        # XXX could probably be handled by generating the subquery
-                        # into the detected subquery
-                        raise BadSchemaDefinition(
-                            "cant insert security because of usage two inlined "
-                            "relations in this query. You should probably at "
-                            "least uninline %s" % rel.r_type)
-                    subselect.append_selected(vref.copy(subselect))
-                    aliases.append(vref.name)
-                self.select.remove_node(rel)
-                # when some inlined relation has to be copied in the subquery,
-                # we need to test that either value is NULL or that the snippet
-                # condition is satisfied
-                if rschema.inlined and rel.optional:
-                    need_null_test = True
-        if need_null_test:
-            snippetrqlst = n.Or(
-                n.make_relation(subselectvar, 'is', (None, None), n.Constant,
-                                operator='='),
-                snippetrqlst)
+        aliases = []
+        rels_done = set()
+        for i, (selectvar, snippetvar) in enumerate(varmap):
+            subselectvar = subselect.get_variable(selectvar)
+            subselect.append_selected(n.VariableRef(subselectvar))
+            aliases.append(selectvar)
+            vi = self.varinfos[i]
+            need_null_test = False
+            stinfo = vi['stinfo']
+            for rel in stinfo['relations']:
+                if rel in rels_done:
+                    continue
+                rels_done.add(rel)
+                rschema = self.schema.rschema(rel.r_type)
+                if rschema.final or (rschema.inlined and
+                                     not rel in stinfo['rhsrelations']):
+                    rel.children[0].name = selectvar # XXX explain why
+                    subselect.add_restriction(rel.copy(subselect))
+                    for vref in rel.children[1].iget_nodes(n.VariableRef):
+                        if isinstance(vref.variable, n.ColumnAlias):
+                            # XXX could probably be handled by generating the
+                            # subquery into the detected subquery
+                            raise BadSchemaDefinition(
+                                "cant insert security because of usage two inlined "
+                                "relations in this query. You should probably at "
+                                "least uninline %s" % rel.r_type)
+                        subselect.append_selected(vref.copy(subselect))
+                        aliases.append(vref.name)
+                    self.select.remove_node(rel)
+                    # when some inlined relation has to be copied in the
+                    # subquery, we need to test that either value is NULL or
+                    # that the snippet condition is satisfied
+                    if rschema.inlined and rel.optional:
+                        need_null_test = True
+            if need_null_test:
+                snippetrqlst = n.Or(
+                    n.make_relation(subselectvar, 'is', (None, None), n.Constant,
+                                    operator='='),
+                    snippetrqlst)
         subselect.add_restriction(snippetrqlst)
         if self.u_varname:
             # generate an identifier for the substitution
@@ -433,35 +457,37 @@
                 # no more references, undefine the variable
                 del self.select.defined_vars[vref.name]
 
-    def _may_be_shared_with(self, sniprel, target, searchedvarname):
+    def _may_be_shared_with(self, sniprel, target):
         """if the snippet relation can be skipped to use a relation from the
         original query, return that relation node
         """
         rschema = self.schema.rschema(sniprel.r_type)
-        try:
-            if target == 'object':
-                orel = self.varinfo['lhs_rels'][sniprel.r_type]
-                cardindex = 0
-                ttypes_func = rschema.objects
-                rdef = rschema.rdef
-            else: # target == 'subject':
-                orel = self.varinfo['rhs_rels'][sniprel.r_type]
-                cardindex = 1
-                ttypes_func = rschema.subjects
-                rdef = lambda x, y: rschema.rdef(y, x)
-        except KeyError:
-            # may be raised by self.varinfo['xhs_rels'][sniprel.r_type]
-            return None
-        # can't share neged relation or relations with different outer join
-        if (orel.neged(strict=True) or sniprel.neged(strict=True)
-            or (orel.optional and orel.optional != sniprel.optional)):
-            return None
-        # if cardinality is in '?1', we can ignore the snippet relation and use
-        # variable from the original query
-        for etype in self.varinfo['stinfo']['possibletypes']:
-            for ttype in ttypes_func(etype):
-                if rdef(etype, ttype).cardinality[cardindex] in '+*':
-                    return None
+        for vi in self.varinfos:
+            try:
+                if target == 'object':
+                    orel = vi['lhs_rels'][sniprel.r_type]
+                    cardindex = 0
+                    ttypes_func = rschema.objects
+                    rdef = rschema.rdef
+                else: # target == 'subject':
+                    orel = vi['rhs_rels'][sniprel.r_type]
+                    cardindex = 1
+                    ttypes_func = rschema.subjects
+                    rdef = lambda x, y: rschema.rdef(y, x)
+            except KeyError:
+                # may be raised by vi['xhs_rels'][sniprel.r_type]
+                return None
+            # can't share neged relation or relations with different outer join
+            if (orel.neged(strict=True) or sniprel.neged(strict=True)
+                or (orel.optional and orel.optional != sniprel.optional)):
+                return None
+            # if cardinality is in '?1', we can ignore the snippet relation and use
+            # variable from the original query
+            for etype in vi['stinfo']['possibletypes']:
+                for ttype in ttypes_func(etype):
+                    if rdef(etype, ttype).cardinality[cardindex] in '+*':
+                        return None
+            break
         return orel
 
     def _use_orig_term(self, snippet_varname, term):
@@ -560,12 +586,12 @@
             if self.existingvars and not self.keep_var(rhs.name):
                 return
             if lhs.name in self.revvarmap and rhs.name != 'U':
-                orel = self._may_be_shared_with(node, 'object', lhs.name)
+                orel = self._may_be_shared_with(node, 'object')
                 if orel is not None:
                     self._use_orig_term(rhs.name, orel.children[1].children[0])
                     return
             elif rhs.name in self.revvarmap and lhs.name != 'U':
-                orel = self._may_be_shared_with(node, 'subject', rhs.name)
+                orel = self._may_be_shared_with(node, 'subject')
                 if orel is not None:
                     self._use_orig_term(lhs.name, orel.children[0])
                     return
@@ -600,10 +626,11 @@
     def visit_variableref(self, node):
         """get the sql name for a variable reference"""
         if node.name in self.revvarmap:
-            if self.varinfo.get('const') is not None:
-                return n.Constant(self.varinfo['const'], 'Int') # XXX gae
-            return n.VariableRef(self.select.get_variable(
-                self.revvarmap[node.name]))
+            selectvar, index = self.revvarmap[node.name]
+            vi = self.varinfos[index]
+            if vi.get('const') is not None:
+                return n.Constant(vi['const'], 'Int') # XXX gae
+            return n.VariableRef(self.select.get_variable(selectvar))
         vname_or_term = self._get_varname_or_term(node.name)
         if isinstance(vname_or_term, basestring):
             return n.VariableRef(self.select.get_variable(vname_or_term))
--- a/server/__init__.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/__init__.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -129,7 +129,6 @@
     # on connection
     config.creating = True
     config.consider_user_state = False
-    config.set_language = False
     # only enable the system source at initialization time
     repo = Repository(config, vreg=vreg)
     schema = repo.schema
@@ -210,7 +209,6 @@
     # restore initial configuration
     config.creating = False
     config.consider_user_state = True
-    config.set_language = True
     print '-> database for instance %s initialized.' % config.appid
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/edition.py	Wed Mar 30 11:17:58 2011 +0200
@@ -0,0 +1,150 @@
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""helper classes to handle server-side edition of entities"""
+
+from __future__ import with_statement
+
+__docformat__ = "restructuredtext en"
+
+from copy import copy
+from yams import ValidationError
+
+
+_MARKER = object()
+
+class dict_protocol_catcher(object):
+    def __init__(self, entity):
+        self.__entity = entity
+    def __getitem__(self, attr):
+        return self.__entity.cw_edited[attr]
+    def __setitem__(self, attr, value):
+        self.__entity.cw_edited[attr] = value
+    def __getattr__(self, attr):
+        return getattr(self.__entity, attr)
+
+
+class EditedEntity(dict):
+    """encapsulate entities attributes being written by an RQL query"""
+    def __init__(self, entity, **kwargs):
+        dict.__init__(self, **kwargs)
+        self.entity = entity
+        self.skip_security = set()
+        self.querier_pending_relations = {}
+        self.saved = False
+
+    def __hash__(self):
+        # dict|set keyable
+        return hash(id(self))
+
+    def __cmp__(self, other):
+        # we don't want comparison by value inherited from dict
+        return cmp(id(self), id(other))
+
+    def __setitem__(self, attr, value):
+        assert attr != 'eid'
+        # don't add attribute into skip_security if already in edited
+        # attributes, else we may accidentaly skip a desired security check
+        if attr not in self:
+            self.skip_security.add(attr)
+        self.edited_attribute(attr, value)
+
+    def __delitem__(self, attr):
+        assert not self.saved, 'too late to modify edited attributes'
+        super(EditedEntity, self).__delitem__(attr)
+        self.entity.cw_attr_cache.pop(attr, None)
+
+    def pop(self, attr, *args):
+        # don't update skip_security by design (think to storage api)
+        assert not self.saved, 'too late to modify edited attributes'
+        value = super(EditedEntity, self).pop(attr, *args)
+        self.entity.cw_attr_cache.pop(attr, *args)
+        return value
+
+    def setdefault(self, attr, default):
+        assert attr != 'eid'
+        # don't add attribute into skip_security if already in edited
+        # attributes, else we may accidentaly skip a desired security check
+        if attr not in self:
+            self[attr] = default
+        return self[attr]
+
+    def update(self, values, skipsec=True):
+        if skipsec:
+            setitem = self.__setitem__
+        else:
+            setitem = self.edited_attribute
+        for attr, value in values.iteritems():
+            setitem(attr, value)
+
+    def edited_attribute(self, attr, value):
+        """attribute being edited by a rql query: should'nt be added to
+        skip_security
+        """
+        assert not self.saved, 'too late to modify edited attributes'
+        super(EditedEntity, self).__setitem__(attr, value)
+        self.entity.cw_attr_cache[attr] = value
+
+    def oldnewvalue(self, attr):
+        """returns the couple (old attr value, new attr value)
+
+        NOTE: will only work in a before_update_entity hook
+        """
+        assert not self.saved, 'too late to get the old value'
+        # get new value and remove from local dict to force a db query to
+        # fetch old value
+        newvalue = self.entity.cw_attr_cache.pop(attr, _MARKER)
+        oldvalue = getattr(self.entity, attr)
+        if newvalue is not _MARKER:
+            self.entity.cw_attr_cache[attr] = newvalue
+        else:
+            newvalue = oldvalue
+        return oldvalue, newvalue
+
+    def set_defaults(self):
+        """set default values according to the schema"""
+        for attr, value in self.entity.e_schema.defaults():
+            if not attr in self:
+                self[str(attr)] = value
+
+    def check(self, creation=False):
+        """check the entity edition against its schema. Only final relation
+        are checked here, constraint on actual relations are checked in hooks
+        """
+        entity = self.entity
+        if creation:
+            # on creations, we want to check all relations, especially
+            # required attributes
+            relations = [rschema for rschema in entity.e_schema.subject_relations()
+                         if rschema.final and rschema.type != 'eid']
+        else:
+            relations = [entity._cw.vreg.schema.rschema(rtype)
+                         for rtype in self]
+        try:
+            entity.e_schema.check(dict_protocol_catcher(entity),
+                                  creation=creation, _=entity._cw._,
+                                  relations=relations)
+        except ValidationError, ex:
+            ex.entity = self.entity
+            raise
+
+    def clone(self):
+        thecopy = EditedEntity(copy(self.entity))
+        thecopy.entity.cw_attr_cache = copy(self.entity.cw_attr_cache)
+        thecopy.entity._cw_related_cache = {}
+        thecopy.update(self, skipsec=False)
+        return thecopy
--- a/server/migractions.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/migractions.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -1307,20 +1307,32 @@
     # CWProperty handling ######################################################
 
     def cmd_property_value(self, pkey):
-        rql = 'Any V WHERE X is CWProperty, X pkey %(k)s, X value V'
-        rset = self.rqlexec(rql, {'k': pkey}, ask_confirm=False)
+        """retreive the site-wide persistent property value for the given key.
+
+        To get a user specific property value, use appropriate method on CWUser
+        instance.
+        """
+        rset = self.rqlexec(
+            'Any V WHERE X is CWProperty, X pkey %(k)s, X value V, NOT X for_user U',
+            {'k': pkey}, ask_confirm=False)
         return rset[0][0]
 
     def cmd_set_property(self, pkey, value):
+        """set the site-wide persistent property value for the given key to the
+        given value.
+
+        To set a user specific property value, use appropriate method on CWUser
+        instance.
+        """
         value = unicode(value)
         try:
-            prop = self.rqlexec('CWProperty X WHERE X pkey %(k)s', {'k': pkey},
-                                ask_confirm=False).get_entity(0, 0)
+            prop = self.rqlexec(
+                'CWProperty X WHERE X pkey %(k)s, NOT X for_user U',
+                {'k': pkey}, ask_confirm=False).get_entity(0, 0)
         except:
             self.cmd_create_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)
+            prop.set_attributes(value=value)
 
     # other data migration commands ###########################################
 
@@ -1360,6 +1372,18 @@
             self.commit()
         return entity
 
+    def cmd_find_entities(self, etype, **kwargs):
+        """find entities of the given type and attribute values"""
+        return self._cw.find_entities(etype, **kwargs)
+
+    def cmd_find_one_entity(self, etype, **kwargs):
+        """find one entity of the given type and attribute values.
+
+        raise :exc:`cubicweb.req.FindEntityError` if can not return one and only
+        one entity.
+        """
+        return self._cw.find_one_entity(etype, **kwargs)
+
     def cmd_update_etype_fti_weight(self, etype, weight):
         if self.repo.system_source.dbdriver == 'postgres':
             self.sqlexec('UPDATE appears SET weight=%(weight)s '
--- a/server/querier.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/querier.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -38,7 +38,8 @@
 
 from cubicweb.server.utils import cleanup_solutions
 from cubicweb.server.rqlannotation import SQLGenAnnotator, set_qdata
-from cubicweb.server.ssplanner import READ_ONLY_RTYPES, add_types_restriction, EditedEntity
+from cubicweb.server.ssplanner import READ_ONLY_RTYPES, add_types_restriction
+from cubicweb.server.edition import EditedEntity
 from cubicweb.server.session import security_enabled
 
 def empty_rset(rql, args, rqlst=None):
@@ -353,7 +354,7 @@
                     myrqlst = select.copy(solutions=lchecksolutions)
                     myunion.append(myrqlst)
                     # in-place rewrite + annotation / simplification
-                    lcheckdef = [((var, 'X'), rqlexprs) for var, rqlexprs in lcheckdef]
+                    lcheckdef = [({var: 'X'}, rqlexprs) for var, rqlexprs in lcheckdef]
                     rewrite(myrqlst, lcheckdef, lchecksolutions, self.args)
                     add_noinvariant(noinvariant, restricted, myrqlst, nbtrees)
                 if () in localchecks:
--- a/server/repository.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/repository.py	Wed Mar 30 11:17:58 2011 +0200
@@ -59,6 +59,7 @@
      security_enabled
 from cubicweb.server.ssplanner import EditedEntity
 
+
 def prefill_entity_caches(entity, relations):
     session = entity._cw
     # prefill entity relation caches
@@ -134,6 +135,7 @@
             vreg = cwvreg.CubicWebVRegistry(config)
         self.vreg = vreg
         self.pyro_registered = False
+        self.pyro_uri = None
         self.info('starting repository from %s', self.config.apphome)
         # dictionary of opened sessions
         self._sessions = {}
@@ -415,7 +417,9 @@
                 self.exception('error while closing %s' % pool)
                 continue
         if self.pyro_registered:
-            pyro_unregister(self.config)
+            if self._use_pyrons():
+                pyro_unregister(self.config)
+            self.pyro_uri = None
         hits, misses = self.querier.cache_hit, self.querier.cache_miss
         try:
             self.info('rql st cache hit/miss: %s/%s (%s%% hits)', hits, misses,
@@ -1419,20 +1423,32 @@
         config['pyro-instance-id'] = appid
         return appid
 
+    def _use_pyrons(self):
+        """return True if the pyro-ns-host is set to something else
+        than NO_PYRONS, meaning we want to go through a pyro
+        nameserver"""
+        return self.config['pyro-ns-host'] != 'NO_PYRONS'
+
     def pyro_register(self, host=''):
         """register the repository as a pyro object"""
         from logilab.common import pyro_ext as pyro
         daemon = pyro.register_object(self, self.pyro_appid,
                                       daemonhost=self.config['pyro-host'],
-                                      nshost=self.config['pyro-ns-host'])
+                                      nshost=self.config['pyro-ns-host'],
+                                      use_pyrons=self._use_pyrons())
         self.info('repository registered as a pyro object %s', self.pyro_appid)
+        self.pyro_uri =  pyro.get_object_uri(self.pyro_appid)
+        self.info('pyro uri is: %s', self.pyro_uri)
         self.pyro_registered = True
         # register a looping task to regularly ensure we're still registered
         # into the pyro name server
-        self.looping_task(60*10, self._ensure_pyro_ns)
+        if self._use_pyrons():
+            self.looping_task(60*10, self._ensure_pyro_ns)
         return daemon
 
     def _ensure_pyro_ns(self):
+        if not self._use_pyrons():
+            return
         from logilab.common import pyro_ext as pyro
         pyro.ns_reregister(self.pyro_appid, nshost=self.config['pyro-ns-host'])
         self.info('repository re-registered as a pyro object %s',
--- a/server/serverconfig.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/serverconfig.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -215,8 +215,6 @@
 
     # read the schema from the database
     read_instance_schema = True
-    # set to true while creating an instance
-    creating = False
     # set this to true to get a minimal repository, for instance to get cubes
     # information on commands such as i18ninstance, db-restore, etc...
     quick_start = False
--- a/server/serverctl.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/serverctl.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -27,11 +27,11 @@
 
 from logilab.common import nullobject
 from logilab.common.configuration import Configuration
-from logilab.common.shellutils import ASK
+from logilab.common.shellutils import ASK, generate_password
 
 from cubicweb import AuthenticationError, ExecutionError, ConfigurationError
 from cubicweb.toolsutils import Command, CommandHandler, underline_title
-from cubicweb.cwctl import CWCTL
+from cubicweb.cwctl import CWCTL, check_options_consistency
 from cubicweb.server import SOURCE_TYPES
 from cubicweb.server.serverconfig import (
     USER_OPTIONS, ServerConfiguration, SourceConfiguration,
@@ -39,7 +39,7 @@
 
 # utility functions ###########################################################
 
-def source_cnx(source, dbname=None, special_privs=False, verbose=True):
+def source_cnx(source, dbname=None, special_privs=False, interactive=True):
     """open and return a connection to the system database defined in the
     given server.serverconfig
     """
@@ -50,21 +50,20 @@
         dbname = source['db-name']
     driver = source['db-driver']
     dbhelper = get_db_helper(driver)
-    if verbose:
+    if interactive:
         print '-> connecting to %s database' % driver,
         if dbhost:
             print '%s@%s' % (dbname, dbhost),
         else:
             print dbname,
     if dbhelper.users_support:
-        if not special_privs and source.get('db-user'):
-            user = source['db-user']
-            if verbose:
+        if not interactive or (not special_privs and source.get('db-user')):
+            user = source.get('db-user', os.environ.get('USER', ''))
+            if interactive:
                 print 'as', user
             password = source.get('db-password')
         else:
-            if verbose:
-                print
+            print
             if special_privs:
                 print 'WARNING'
                 print ('the user will need the following special access rights '
@@ -95,7 +94,7 @@
     return cnx
 
 def system_source_cnx(source, dbms_system_base=False,
-                      special_privs='CREATE/DROP DATABASE', verbose=True):
+                      special_privs='CREATE/DROP DATABASE', interactive=True):
     """shortcut to get a connextion to the instance system database
     defined in the given config. If <dbms_system_base> is True,
     connect to the dbms system database instead (for task such as
@@ -104,10 +103,12 @@
     if dbms_system_base:
         from logilab.database import get_db_helper
         system_db = get_db_helper(source['db-driver']).system_database()
-        return source_cnx(source, system_db, special_privs=special_privs, verbose=verbose)
-    return source_cnx(source, special_privs=special_privs, verbose=verbose)
+        return source_cnx(source, system_db, special_privs=special_privs,
+                          interactive=interactive)
+    return source_cnx(source, special_privs=special_privs,
+                      interactive=interactive)
 
-def _db_sys_cnx(source, special_privs, verbose=True):
+def _db_sys_cnx(source, special_privs, interactive=True):
     """return a connection on the RDMS system table (to create/drop a user or a
     database)
     """
@@ -118,7 +119,7 @@
     helper = get_db_helper(driver)
     # connect on the dbms system base to create our base
     cnx = system_source_cnx(source, True, special_privs=special_privs,
-                            verbose=verbose)
+                            interactive=interactive)
     # disable autocommit (isolation_level(1)) because DROP and
     # CREATE DATABASE can't be executed in a transaction
     try:
@@ -153,38 +154,49 @@
     cmdname = 'create'
     cfgname = 'repository'
 
-    def bootstrap(self, cubes, inputlevel=0):
+    def bootstrap(self, cubes, automatic=False, inputlevel=0):
         """create an instance by copying files from the given cube and by asking
         information necessary to build required configuration files
         """
         config = self.config
-        print underline_title('Configuring the repository')
-        config.input_config('email', inputlevel)
-        # ask for pyro configuration if pyro is activated and we're not using a
-        # all-in-one config, in which case this is done by the web side command
-        # handler
-        if config.pyro_enabled() and config.name != 'all-in-one':
-            config.input_config('pyro', inputlevel)
-        print '\n'+underline_title('Configuring the sources')
+        if not automatic:
+            print underline_title('Configuring the repository')
+            config.input_config('email', inputlevel)
+            # ask for pyro configuration if pyro is activated and we're not
+            # using a all-in-one config, in which case this is done by the web
+            # side command handler
+            if config.pyro_enabled() and config.name != 'all-in-one':
+                config.input_config('pyro', inputlevel)
+            print '\n'+underline_title('Configuring the sources')
         sourcesfile = config.sources_file()
-        # XXX hack to make Method('default_instance_id') usable in db option
-        # defs (in native.py)
+        # hack to make Method('default_instance_id') usable in db option defs
+        # (in native.py)
         sconfig = SourceConfiguration(config,
                                       options=SOURCE_TYPES['native'].options)
-        sconfig.input_config(inputlevel=inputlevel)
+        if not automatic:
+            sconfig.input_config(inputlevel=inputlevel)
+            print
         sourcescfg = {'system': sconfig}
-        print
-        sconfig = Configuration(options=USER_OPTIONS)
-        sconfig.input_config(inputlevel=inputlevel)
+        if automatic:
+            # XXX modify a copy
+            password = generate_password()
+            print 'Administration account is admin / %s' % password
+            USER_OPTIONS[1][1]['default'] = password
+            sconfig = Configuration(options=USER_OPTIONS)
+        else:
+            sconfig = Configuration(options=USER_OPTIONS)
+            sconfig.input_config(inputlevel=inputlevel)
         sourcescfg['admin'] = sconfig
         config.write_sources_file(sourcescfg)
         # remember selected cubes for later initialization of the database
         config.write_bootstrap_cubes_file(cubes)
 
-    def postcreate(self):
-        if ASK.confirm('Run db-create to create the system database ?'):
-            verbosity = (self.config.mode == 'installed') and 'y' or 'n'
-            CWCTL.run(['db-create', self.config.appid, '--verbose=%s' % verbosity])
+    def postcreate(self, automatic=False, inputlevel=0):
+        if automatic:
+            CWCTL.run(['db-create', '--automatic', self.config.appid])
+        elif ASK.confirm('Run db-create to create the system database ?'):
+            CWCTL.run(['db-create', '--config-level', str(inputlevel),
+                       self.config.appid])
         else:
             print ('-> nevermind, you can do it later with '
                    '"cubicweb-ctl db-create %s".' % self.config.appid)
@@ -292,27 +304,30 @@
     arguments = '<instance>'
     min_args = max_args = 1
     options = (
+        ('automatic',
+         {'short': 'a', 'action' : 'store_true',
+          'default': False,
+          'help': 'automatic mode: never ask and use default answer to every '
+          'question. this may require that your login match a database super '
+          'user (allowed to create database & all).',
+          }),
+        ('config-level',
+         {'short': 'l', 'type' : 'int', 'metavar': '<level>',
+          'default': 0,
+          'help': 'configuration level (0..2): 0 will ask for essential '
+          'configuration parameters only while 2 will ask for all parameters',
+          }),
         ('create-db',
          {'short': 'c', 'type': 'yn', 'metavar': '<y or n>',
           'default': True,
-          'help': 'create the database (yes by default)'}),
-        ('verbose',
-         {'short': 'v', 'type' : 'yn', 'metavar': '<verbose>',
-          'default': 'n',
-          'help': 'verbose mode: will ask all possible configuration questions',
-          }
-         ),
-        ('automatic',
-         {'short': 'a', 'type' : 'yn', 'metavar': '<auto>',
-          'default': 'n',
-          'help': 'automatic mode: never ask and use default answer to every question',
-          }
-         ),
+          'help': 'create the database (yes by default)'
+          }),
         )
+
     def run(self, args):
         """run the command with its specific arguments"""
         from logilab.database import get_db_helper
-        verbose = self.get('verbose')
+        check_options_consistency(self.config)
         automatic = self.get('automatic')
         appid = args.pop()
         config = ServerConfiguration.config_for(appid)
@@ -329,7 +344,7 @@
             print '\n'+underline_title('Creating the system database')
             # connect on the dbms system base to create our base
             dbcnx = _db_sys_cnx(source, 'CREATE/DROP DATABASE and / or USER',
-                                verbose=verbose)
+                                interactive=not automatic)
             cursor = dbcnx.cursor()
             try:
                 if helper.users_support:
@@ -342,6 +357,8 @@
                     if automatic or ASK.confirm('Database %s already exists -- do you want to drop it ?' % dbname):
                         cursor.execute('DROP DATABASE %s' % dbname)
                     else:
+                        print ('you may want to run "cubicweb-ctl db-init '
+                               '--drop %s" manually to continue.' % config.appid)
                         return
                 createdb(helper, source, dbcnx, cursor)
                 dbcnx.commit()
@@ -350,7 +367,7 @@
                 dbcnx.rollback()
                 raise
         cnx = system_source_cnx(source, special_privs='CREATE LANGUAGE',
-                                verbose=verbose)
+                                interactive=not automatic)
         cursor = cnx.cursor()
         helper.init_fti_extensions(cursor)
         # postgres specific stuff
@@ -363,8 +380,12 @@
         cnx.commit()
         print '-> database for instance %s created and necessary extensions installed.' % appid
         print
-        if automatic or ASK.confirm('Run db-init to initialize the system database ?'):
-            CWCTL.run(['db-init', config.appid])
+        if automatic:
+            CWCTL.run(['db-init', '--automatic', '--config-level', '0',
+                       config.appid])
+        elif ASK.confirm('Run db-init to initialize the system database ?'):
+            CWCTL.run(['db-init', '--config-level',
+                       str(self.config.config_level), config.appid])
         else:
             print ('-> nevermind, you can do it later with '
                    '"cubicweb-ctl db-init %s".' % config.appid)
@@ -383,18 +404,27 @@
     arguments = '<instance>'
     min_args = max_args = 1
     options = (
+        ('automatic',
+         {'short': 'a', 'action' : 'store_true',
+          'default': False,
+          'help': 'automatic mode: never ask and use default answer to every '
+          'question.',
+          }),
+        ('config-level',
+         {'short': 'l', 'type': 'int', 'default': 1,
+          'help': 'level threshold for questions asked when configuring '
+          'another source'
+          }),
         ('drop',
          {'short': 'd', 'action': 'store_true',
           'default': False,
-          'help': 'insert drop statements to remove previously existant \
-tables, indexes... (no by default)'}),
-        ('config-level',
-         {'short': 'l', 'type': 'int', 'default': 1,
-          'help': 'level threshold for questions asked when configuring another source'
+          'help': 'insert drop statements to remove previously existant '
+          'tables, indexes... (no by default)'
           }),
         )
 
     def run(self, args):
+        check_options_consistency(self.config)
         print '\n'+underline_title('Initializing the system database')
         from cubicweb.server import init_repository
         from logilab.database import get_connection
@@ -415,8 +445,10 @@
                 'the %s file. Resolve this first (error: %s).'
                 % (config.sources_file(), str(ex).strip()))
         init_repository(config, drop=self.config.drop)
-        while ASK.confirm('Enter another source ?', default_is_yes=False):
-            CWCTL.run(['add-source', '--config-level', self.config.config_level, config.appid])
+        if not self.config.automatic:
+            while ASK.confirm('Enter another source ?', default_is_yes=False):
+                CWCTL.run(['add-source', '--config-level',
+                           str(self.config.config_level), config.appid])
 
 
 class AddSourceCommand(Command):
--- a/server/session.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/session.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -37,6 +37,7 @@
 from cubicweb.dbapi import ConnectionProperties
 from cubicweb.utils import make_uid, RepeatList
 from cubicweb.rqlrewrite import RQLRewriter
+from cubicweb.server.edition import EditedEntity
 
 ETYPE_PYOBJ_MAP[Binary] = 'Bytes'
 
@@ -215,8 +216,9 @@
         with security_enabled(self, False, False):
             if self.vreg.schema[rtype].inlined:
                 entity = self.entity_from_eid(fromeid)
-                entity[rtype] = toeid
-                self.repo.glob_update_entity(self, entity, set((rtype,)))
+                edited = EditedEntity(entity)
+                edited.edited_attribute(rtype, toeid)
+                self.repo.glob_update_entity(self, edited)
             else:
                 self.repo.glob_add_relation(self, fromeid, rtype, toeid)
 
@@ -234,7 +236,7 @@
         with security_enabled(self, False, False):
             if self.vreg.schema[rtype].inlined:
                 entity = self.entity_from_eid(fromeid)
-                entity[rtype] = None
+                entity.cw_attr_cache[rtype] = None
                 self.repo.glob_update_entity(self, entity, set((rtype,)))
             else:
                 self.repo.glob_delete_relation(self, fromeid, rtype, toeid)
--- a/server/sources/__init__.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/sources/__init__.py	Wed Mar 30 11:17:58 2011 +0200
@@ -31,7 +31,7 @@
 from cubicweb import ValidationError, set_log_methods, server
 from cubicweb.schema import VIRTUAL_RTYPES
 from cubicweb.server.sqlutils import SQL_PREFIX
-from cubicweb.server.ssplanner import EditedEntity
+from cubicweb.server.edition import EditedEntity
 
 
 def dbg_st_search(uri, union, varmap, args, cachekey=None, prefix='rql for'):
--- a/server/sources/ldapuser.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/sources/ldapuser.py	Wed Mar 30 11:17:58 2011 +0200
@@ -522,7 +522,8 @@
     def _search(self, session, base, scope,
                 searchstr='(objectClass=*)', attrs=()):
         """make an ldap query"""
-        self.debug('ldap search %s %s %s %s %s', self.uri, base, scope, searchstr, list(attrs))
+        self.debug('ldap search %s %s %s %s %s', self.uri, base, scope,
+                   searchstr, list(attrs))
         # XXX for now, we do not have connection pool support for LDAP, so
         # this is always self._conn
         cnx = session.pool.connection(self.uri).cnx
--- a/server/sources/native.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/sources/native.py	Wed Mar 30 11:17:58 2011 +0200
@@ -58,7 +58,7 @@
 from cubicweb.server.rqlannotation import set_qdata
 from cubicweb.server.hook import CleanupDeletedEidsCacheOp
 from cubicweb.server.session import hooks_control, security_enabled
-from cubicweb.server.ssplanner import EditedEntity
+from cubicweb.server.edition import EditedEntity
 from cubicweb.server.sources import AbstractSource, dbg_st_search, dbg_results
 from cubicweb.server.sources.rql2sql import SQLGenerator
 
--- a/server/sources/storages.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/sources/storages.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -24,7 +24,7 @@
 
 from cubicweb import Binary, ValidationError
 from cubicweb.server import hook
-from cubicweb.server.ssplanner import EditedEntity
+from cubicweb.server.edition import EditedEntity
 
 
 def set_attribute_storage(repo, etype, attr, storage):
--- a/server/ssplanner.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/ssplanner.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -21,8 +21,6 @@
 
 __docformat__ = "restructuredtext en"
 
-from copy import copy
-
 from rql.stmts import Union, Select
 from rql.nodes import Constant, Relation
 
@@ -31,6 +29,7 @@
 from cubicweb.rqlrewrite import add_types_restriction
 from cubicweb.server.session import security_enabled
 from cubicweb.server.hook import CleanupDeletedEidsCacheOp
+from cubicweb.server.edition import EditedEntity
 
 READ_ONLY_RTYPES = set(('eid', 'has_text', 'is', 'is_instance_of', 'identity'))
 
@@ -128,132 +127,6 @@
     return select
 
 
-_MARKER = object()
-
-class dict_protocol_catcher(object):
-    def __init__(self, entity):
-        self.__entity = entity
-    def __getitem__(self, attr):
-        return self.__entity.cw_edited[attr]
-    def __setitem__(self, attr, value):
-        self.__entity.cw_edited[attr] = value
-    def __getattr__(self, attr):
-        return getattr(self.__entity, attr)
-
-
-class EditedEntity(dict):
-    """encapsulate entities attributes being written by an RQL query"""
-    def __init__(self, entity, **kwargs):
-        dict.__init__(self, **kwargs)
-        self.entity = entity
-        self.skip_security = set()
-        self.querier_pending_relations = {}
-        self.saved = False
-
-    def __hash__(self):
-        # dict|set keyable
-        return hash(id(self))
-
-    def __cmp__(self, other):
-        # we don't want comparison by value inherited from dict
-        return cmp(id(self), id(other))
-
-    def __setitem__(self, attr, value):
-        assert attr != 'eid'
-        # don't add attribute into skip_security if already in edited
-        # attributes, else we may accidentaly skip a desired security check
-        if attr not in self:
-            self.skip_security.add(attr)
-        self.edited_attribute(attr, value)
-
-    def __delitem__(self, attr):
-        assert not self.saved, 'too late to modify edited attributes'
-        super(EditedEntity, self).__delitem__(attr)
-        self.entity.cw_attr_cache.pop(attr, None)
-
-    def pop(self, attr, *args):
-        # don't update skip_security by design (think to storage api)
-        assert not self.saved, 'too late to modify edited attributes'
-        value = super(EditedEntity, self).pop(attr, *args)
-        self.entity.cw_attr_cache.pop(attr, *args)
-        return value
-
-    def setdefault(self, attr, default):
-        assert attr != 'eid'
-        # don't add attribute into skip_security if already in edited
-        # attributes, else we may accidentaly skip a desired security check
-        if attr not in self:
-            self[attr] = default
-        return self[attr]
-
-    def update(self, values, skipsec=True):
-        if skipsec:
-            setitem = self.__setitem__
-        else:
-            setitem = self.edited_attribute
-        for attr, value in values.iteritems():
-            setitem(attr, value)
-
-    def edited_attribute(self, attr, value):
-        """attribute being edited by a rql query: should'nt be added to
-        skip_security
-        """
-        assert not self.saved, 'too late to modify edited attributes'
-        super(EditedEntity, self).__setitem__(attr, value)
-        self.entity.cw_attr_cache[attr] = value
-
-    def oldnewvalue(self, attr):
-        """returns the couple (old attr value, new attr value)
-
-        NOTE: will only work in a before_update_entity hook
-        """
-        assert not self.saved, 'too late to get the old value'
-        # get new value and remove from local dict to force a db query to
-        # fetch old value
-        newvalue = self.entity.cw_attr_cache.pop(attr, _MARKER)
-        oldvalue = getattr(self.entity, attr)
-        if newvalue is not _MARKER:
-            self.entity.cw_attr_cache[attr] = newvalue
-        else:
-            newvalue = oldvalue
-        return oldvalue, newvalue
-
-    def set_defaults(self):
-        """set default values according to the schema"""
-        for attr, value in self.entity.e_schema.defaults():
-            if not attr in self:
-                self[str(attr)] = value
-
-    def check(self, creation=False):
-        """check the entity edition against its schema. Only final relation
-        are checked here, constraint on actual relations are checked in hooks
-        """
-        entity = self.entity
-        if creation:
-            # on creations, we want to check all relations, especially
-            # required attributes
-            relations = [rschema for rschema in entity.e_schema.subject_relations()
-                         if rschema.final and rschema.type != 'eid']
-        else:
-            relations = [entity._cw.vreg.schema.rschema(rtype)
-                         for rtype in self]
-        from yams import ValidationError
-        try:
-            entity.e_schema.check(dict_protocol_catcher(entity),
-                                  creation=creation, _=entity._cw._,
-                                  relations=relations)
-        except ValidationError, ex:
-            ex.entity = self.entity
-            raise
-
-    def clone(self):
-        thecopy = EditedEntity(copy(self.entity))
-        thecopy.entity.cw_attr_cache = copy(self.entity.cw_attr_cache)
-        thecopy.entity._cw_related_cache = {}
-        thecopy.update(self, skipsec=False)
-        return thecopy
-
-
 class SSPlanner(object):
     """SingleSourcePlanner: build execution plan for rql queries
 
--- a/server/test/unittest_ldapuser.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/server/test/unittest_ldapuser.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
--- a/test/unittest_entity.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/test/unittest_entity.py	Wed Mar 30 11:17:58 2011 +0200
@@ -223,38 +223,48 @@
                           'Any X,AA ORDERBY AA DESC '
                           'WHERE E eid %(x)s, E tags X, X modification_date AA')
 
-    def test_unrelated_rql_security_1(self):
+    def test_unrelated_rql_security_1_manager(self):
         user = self.request().user
         rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
         self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC '
-                          'WHERE NOT S use_email O, S eid %(x)s, O is EmailAddress, O address AA, O alias AB, O modification_date AC')
+                         'WHERE NOT S use_email O, S eid %(x)s, '
+                         'O is EmailAddress, O address AA, O alias AB, O modification_date AC')
+
+    def test_unrelated_rql_security_1_user(self):
         self.create_user('toto')
         self.login('toto')
         user = self.request().user
         rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
         self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC '
-                          'WHERE NOT S use_email O, S eid %(x)s, O is EmailAddress, O address AA, O alias AB, O modification_date AC')
+                          'WHERE NOT S use_email O, S eid %(x)s, '
+                         'O is EmailAddress, O address AA, O alias AB, O modification_date AC')
         user = self.execute('Any X WHERE X login "admin"').get_entity(0, 0)
-        self.assertRaises(Unauthorized, user.cw_unrelated_rql, 'use_email', 'EmailAddress', 'subject')
+        rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
+        self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
+                         'NOT EXISTS(S use_email O), S eid %(x)s, '
+                         'O is EmailAddress, O address AA, O alias AB, O modification_date AC, '
+                         'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
+
+    def test_unrelated_rql_security_1_anon(self):
         self.login('anon')
         user = self.request().user
-        self.assertRaises(Unauthorized, user.cw_unrelated_rql, 'use_email', 'EmailAddress', 'subject')
+        rql = user.cw_unrelated_rql('use_email', 'EmailAddress', 'subject')[0]
+        self.assertEqual(rql, 'Any O,AA,AB,AC ORDERBY AC DESC WHERE '
+                         'NOT EXISTS(S use_email O), S eid %(x)s, '
+                         'O is EmailAddress, O address AA, O alias AB, O modification_date AC, '
+                         'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
 
     def test_unrelated_rql_security_2(self):
         email = self.execute('INSERT EmailAddress X: X address "hop"').get_entity(0, 0)
         rql = email.cw_unrelated_rql('use_email', 'CWUser', 'object')[0]
         self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA ASC '
                           'WHERE NOT S use_email O, O eid %(x)s, S is CWUser, S login AA, S firstname AB, S surname AC, S modification_date AD')
-        #rql = email.cw_unrelated_rql('use_email', 'Person', 'object')[0]
-        #self.assertEqual(rql, '')
         self.login('anon')
         email = self.execute('Any X WHERE X eid %(x)s', {'x': email.eid}).get_entity(0, 0)
         rql = email.cw_unrelated_rql('use_email', 'CWUser', 'object')[0]
         self.assertEqual(rql, 'Any S,AA,AB,AC,AD ORDERBY AA '
                           'WHERE NOT EXISTS(S use_email O), O eid %(x)s, S is CWUser, S login AA, S firstname AB, S surname AC, S modification_date AD, '
                           'A eid %(B)s, EXISTS(S identity A, NOT A in_group C, C name "guests", C is CWGroup)')
-        #rql = email.cw_unrelated_rql('use_email', 'Person', 'object')[0]
-        #self.assertEqual(rql, '')
 
     def test_unrelated_rql_security_nonexistant(self):
         self.login('anon')
--- a/test/unittest_rqlrewrite.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/test/unittest_rqlrewrite.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -62,15 +62,17 @@
             def simplify(mainrqlst, needcopy=False):
                 rqlhelper.simplify(rqlst, needcopy)
     rewriter = RQLRewriter(mock_object(vreg=FakeVReg, user=(mock_object(eid=1))))
-    for v, snippets in snippets_map.items():
-        snippets_map[v] = [isinstance(snippet, basestring)
-                           and mock_object(snippet_rqlst=parse('Any X WHERE '+snippet).children[0],
-                                           expression='Any X WHERE '+snippet)
-                           or snippet
-                           for snippet in snippets]
+    snippets = []
+    for v, exprs in snippets_map.items():
+        rqlexprs = [isinstance(snippet, basestring)
+                    and mock_object(snippet_rqlst=parse('Any X WHERE '+snippet).children[0],
+                                    expression='Any X WHERE '+snippet)
+                    or snippet
+                    for snippet in exprs]
+        snippets.append((dict([v]), rqlexprs))
     rqlhelper.compute_solutions(rqlst.children[0], {'eid': eid_func_map}, kwargs=kwargs)
     solutions = rqlst.children[0].solutions
-    rewriter.rewrite(rqlst.children[0], snippets_map.items(), solutions, kwargs,
+    rewriter.rewrite(rqlst.children[0], snippets, solutions, kwargs,
                      existingvars)
     test_vrefs(rqlst.children[0])
     return rewriter.rewritten
--- a/utils.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/utils.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
--- a/web/data/cubicweb.ajax.js	Thu Mar 24 15:21:13 2011 +0100
+++ b/web/data/cubicweb.ajax.js	Wed Mar 30 11:17:58 2011 +0200
@@ -283,7 +283,7 @@
  * dictionary, `reqtype` the HTTP request type (get 'GET' or 'POST').
  */
 function loadRemote(url, form, reqtype, sync) {
-    if (!url.toLowerCase().startswith(baseuri())) {
+    if (!url.toLowerCase().startswith(baseuri().toLowerCase())) {
         url = baseuri() + url;
     }
     if (!sync) {
--- a/web/data/cubicweb.old.css	Thu Mar 24 15:21:13 2011 +0100
+++ b/web/data/cubicweb.old.css	Wed Mar 30 11:17:58 2011 +0200
@@ -283,7 +283,7 @@
 }
 
 table#mainLayout{
- margin:0px 3px;
+ padding: 0px 3px;
 }
 
 table#mainLayout td#contentColumn {
--- a/web/data/jquery.ui.datepicker-es.js	Thu Mar 24 15:21:13 2011 +0100
+++ b/web/data/jquery.ui.datepicker-es.js	Wed Mar 30 11:17:58 2011 +0200
@@ -10,9 +10,9 @@
 		'Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'],
 		monthNamesShort: ['Ene','Feb','Mar','Abr','May','Jun',
 		'Jul','Ago','Sep','Oct','Nov','Dic'],
-		dayNames: ['Domingo','Lunes','Martes','Mi&eacute;rcoles','Jueves','Viernes','S&aacute;bado'],
-		dayNamesShort: ['Dom','Lun','Mar','Mi&eacute;','Juv','Vie','S&aacute;b'],
-		dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','S&aacute;'],
+		dayNames: ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado'],
+		dayNamesShort: ['Dom','Lun','Mar','Mié','Juv','Vie','Sáb'],
+		dayNamesMin: ['Do','Lu','Ma','Mi','Ju','Vi','Sá'],
 		weekHeader: 'Sm',
 		dateFormat: 'dd/mm/yy',
 		firstDay: 1,
--- a/web/facet.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/web/facet.py	Wed Mar 30 11:17:58 2011 +0200
@@ -53,7 +53,7 @@
 from logilab.mtconverter import xml_escape
 from logilab.common.graph import has_path
 from logilab.common.decorators import cached
-from logilab.common.date import datetime2ticks
+from logilab.common.date import datetime2ticks, ustrftime, ticks2datetime
 from logilab.common.compat import all
 
 from rql import parse, nodes, utils
@@ -981,7 +981,11 @@
 
     def formatvalue(self, value):
         """format `value` before in order to insert it in the RQL query"""
-        return '"%s"' % date.fromtimestamp(float(value) / 1000).strftime('%Y/%m/%d')
+        try:
+            date_value = ticks2datetime(float(value))
+        except (ValueError, OverflowError):
+            return u'"date out-of-range"'
+        return '"%s"' % ustrftime(date_value, '%Y/%m/%d')
 
 
 class HasRelationFacet(AbstractFacet):
--- a/web/formwidgets.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/web/formwidgets.py	Wed Mar 30 11:17:58 2011 +0200
@@ -594,8 +594,11 @@
             value = self.values(form, field)[0]
         else:
             value = self.datestr
+        attrs = {}
+        if self.settabindex:
+            attrs['tabindex'] = req.next_tabindex()
         return tags.input(id=domid, name=domid, value=value,
-                          type='text', size='10')
+                          type='text', size='10', **attrs)
 
 
 class JQueryTimePicker(FieldWidget):
@@ -620,6 +623,9 @@
             value = self.values(form, field)[0]
         else:
             value = self.timestr
+        attrs = {}
+        if self.settabindex:
+            attrs['tabindex'] = req.next_tabindex()
         return tags.input(id=domid, name=domid, value=value,
                           type='text', size='5')
 
--- a/web/test/unittest_reledit.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/web/test/unittest_reledit.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -33,9 +33,9 @@
 class ClickAndEditFormTC(ReleditMixinTC, CubicWebTC):
 
     def test_default_config(self):
-        reledit = {'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;title&#39;, &#39;subject&#39;, &#39;title-subject-%(eid)s&#39;, false, &#39;&#39;);" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
-                   'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;edition&#39;, %(eid)s, &#39;long_desc&#39;, &#39;subject&#39;, &#39;long_desc-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to add a value"><img title="click to add a value" src="data/plus.png" alt="click to add a value"/></div></div></div>""",
-                   'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;manager&#39;, &#39;subject&#39;, &#39;manager-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+        reledit = {'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;title&#39;, &#39;subject&#39;, &#39;title-subject-%(eid)s&#39;, false, &#39;&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+                   'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;edition&#39;, %(eid)s, &#39;long_desc&#39;, &#39;subject&#39;, &#39;long_desc-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to add a value"><img title="click to add a value" src="http://testing.fr/cubicweb/data/plus.png" alt="click to add a value"/></div></div></div>""",
+                   'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;manager&#39;, &#39;subject&#39;, &#39;manager-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
                    'composite_card11_2ttypes': """&lt;not specified&gt;""",
                    'concerns': """&lt;not specified&gt;"""}
 
@@ -76,7 +76,7 @@
 <td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel(&#39;title-subject-%(eid)s&#39;)" tabindex="3" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
 </tr></table>
 </fieldset>
-</form><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;title&#39;, &#39;subject&#39;, &#39;title-subject-%(eid)s&#39;, false, &#39;&#39;);" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+</form><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;title&#39;, &#39;subject&#39;, &#39;title-subject-%(eid)s&#39;, false, &#39;&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
 
                      'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="long_desc-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;long_desc-subject-%(eid)s-form&#39;);" class="releditForm" cubicweb:target="eformframe">
 <fieldset>
@@ -120,7 +120,7 @@
 <td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel(&#39;long_desc-subject-%(eid)s&#39;)" tabindex="8" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
 </tr></table>
 </fieldset>
-</form><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;edition&#39;, %(eid)s, &#39;long_desc&#39;, &#39;subject&#39;, &#39;long_desc-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to add a value"><img title="click to add a value" src="data/plus.png" alt="click to add a value"/></div></div></div>""",
+</form><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-add" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;edition&#39;, %(eid)s, &#39;long_desc&#39;, &#39;subject&#39;, &#39;long_desc-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to add a value"><img title="click to add a value" src="http://testing.fr/cubicweb/data/plus.png" alt="click to add a value"/></div></div></div>""",
 
                      'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue">&lt;not specified&gt;</div><form action="http://testing.fr/cubicweb/validateform?__onsuccess=window.parent.cw.reledit.onSuccess" method="post" enctype="application/x-www-form-urlencoded" id="manager-subject-%(eid)s-form" onsubmit="return freezeFormButtons(&#39;manager-subject-%(eid)s-form&#39;);" class="releditForm" cubicweb:target="eformframe">
 <fieldset>
@@ -156,7 +156,7 @@
 <td><button class="validateButton" onclick="cw.reledit.cleanupAfterCancel(&#39;manager-subject-%(eid)s&#39;)" tabindex="11" type="button" value="button_cancel"><img alt="CANCEL_ICON" src="http://testing.fr/cubicweb/data/cancel.png" />button_cancel</button></td>
 </tr></table>
 </fieldset>
-</form><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;manager&#39;, &#39;subject&#39;, &#39;manager-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+</form><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;manager&#39;, &#39;subject&#39;, &#39;manager-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
                      'composite_card11_2ttypes': """&lt;not specified&gt;""",
                      'concerns': """&lt;not specified&gt;"""
             }
@@ -190,11 +190,11 @@
         reledit_ctrl.tag_object_of(('Ticket', 'concerns', 'Project'),
                                    {'edit_target': 'rtype'})
         reledit = {
-            'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;title&#39;, &#39;subject&#39;, &#39;title-subject-%(eid)s&#39;, true, &#39;&#39;);" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
-            'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue">&lt;long_desc is required&gt;</div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;long_desc&#39;, &#39;subject&#39;, &#39;long_desc-subject-%(eid)s&#39;, true, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
-            'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/personne/%(toto)s" title="">Toto</a></div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;edition&#39;, %(eid)s, &#39;manager&#39;, &#39;subject&#39;, &#39;manager-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div><div id="manager-subject-%(eid)s-delete" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;deleteconf&#39;, %(eid)s, &#39;manager&#39;, &#39;subject&#39;, &#39;manager-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to delete this value"><img title="click to delete this value" src="data/cancel.png" alt="click to delete this value"/></div></div></div>""",
+            'title': """<div id="title-subject-%(eid)s-reledit" onmouseout="jQuery('#title-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#title-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="title-subject-%(eid)s-value" class="editableFieldValue">cubicweb-world-domination</div><div id="title-subject-%(eid)s" class="editableField hidden"><div id="title-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;title&#39;, &#39;subject&#39;, &#39;title-subject-%(eid)s&#39;, true, &#39;&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+            'long_desc': """<div id="long_desc-subject-%(eid)s-reledit" onmouseout="jQuery('#long_desc-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#long_desc-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="long_desc-subject-%(eid)s-value" class="editableFieldValue">&lt;long_desc is required&gt;</div><div id="long_desc-subject-%(eid)s" class="editableField hidden"><div id="long_desc-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;long_desc&#39;, &#39;subject&#39;, &#39;long_desc-subject-%(eid)s&#39;, true, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>""",
+            'manager': """<div id="manager-subject-%(eid)s-reledit" onmouseout="jQuery('#manager-subject-%(eid)s').addClass('hidden')" onmouseover="jQuery('#manager-subject-%(eid)s').removeClass('hidden')" class="releditField"><div id="manager-subject-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/personne/%(toto)s" title="">Toto</a></div><div id="manager-subject-%(eid)s" class="editableField hidden"><div id="manager-subject-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;edition&#39;, %(eid)s, &#39;manager&#39;, &#39;subject&#39;, &#39;manager-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div><div id="manager-subject-%(eid)s-delete" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;deleteconf&#39;, %(eid)s, &#39;manager&#39;, &#39;subject&#39;, &#39;manager-subject-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to delete this value"><img title="click to delete this value" src="http://testing.fr/cubicweb/data/cancel.png" alt="click to delete this value"/></div></div></div>""",
             'composite_card11_2ttypes': """&lt;not specified&gt;""",
-            'concerns': """<div id="concerns-object-%(eid)s-reledit" onmouseout="jQuery('#concerns-object-%(eid)s').addClass('hidden')" onmouseover="jQuery('#concerns-object-%(eid)s').removeClass('hidden')" class="releditField"><div id="concerns-object-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/ticket/%(tick)s" title="">write the code</a></div><div id="concerns-object-%(eid)s" class="editableField hidden"><div id="concerns-object-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;concerns&#39;, &#39;object&#39;, &#39;concerns-object-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="data/pen_icon.png" alt="click to edit this field"/></div></div></div>"""
+            'concerns': """<div id="concerns-object-%(eid)s-reledit" onmouseout="jQuery('#concerns-object-%(eid)s').addClass('hidden')" onmouseover="jQuery('#concerns-object-%(eid)s').removeClass('hidden')" class="releditField"><div id="concerns-object-%(eid)s-value" class="editableFieldValue"><a href="http://testing.fr/cubicweb/ticket/%(tick)s" title="">write the code</a></div><div id="concerns-object-%(eid)s" class="editableField hidden"><div id="concerns-object-%(eid)s-update" class="editableField" onclick="cw.reledit.loadInlineEditionForm(&#39;base&#39;, %(eid)s, &#39;concerns&#39;, &#39;object&#39;, &#39;concerns-object-%(eid)s&#39;, false, &#39;autolimited&#39;);" title="click to edit this field"><img title="click to edit this field" src="http://testing.fr/cubicweb/data/pen_icon.png" alt="click to edit this field"/></div></div></div>"""
             }
         for rschema, ttypes, role in self.proj.e_schema.relation_definitions(includefinal=True):
             if rschema not in reledit:
--- a/web/webconfig.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/web/webconfig.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -297,7 +297,7 @@
         baseurl = self['base-url'] or self.default_base_url()
         if baseurl and baseurl[-1] != '/':
             baseurl += '/'
-        if not self.repairing:
+        if not (self.repairing or self.creating):
             self.global_set_option('base-url', baseurl)
         httpsurl = self['https-url']
         if httpsurl:
--- a/web/webctl.py	Thu Mar 24 15:21:13 2011 +0100
+++ b/web/webctl.py	Wed Mar 30 11:17:58 2011 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -17,26 +17,29 @@
 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 """cubicweb-ctl commands and command handlers common to twisted/modpython
 web configuration
+"""
 
-"""
 __docformat__ = "restructuredtext en"
 
+from logilab.common.shellutils import ASK
+
 from cubicweb.toolsutils import CommandHandler, underline_title
-from logilab.common.shellutils import ASK
 
 class WebCreateHandler(CommandHandler):
     cmdname = 'create'
 
-    def bootstrap(self, cubes, inputlevel=0):
+    def bootstrap(self, cubes, automatic=False, inputlevel=0):
         """bootstrap this configuration"""
-        print '\n' + underline_title('Generic web configuration')
-        config = self.config
-        if config.repo_method == 'pyro' or config.pyro_enabled():
-            print '\n' + underline_title('Pyro configuration')
-            config.input_config('pyro', inputlevel)
-        if ASK.confirm('Allow anonymous access ?', False):
-            config.global_set_option('anonymous-user', 'anon')
-            config.global_set_option('anonymous-password', 'anon')
+        if not automatic:
+            print '\n' + underline_title('Generic web configuration')
+            config = self.config
+            if config.repo_method == 'pyro' or config.pyro_enabled():
+                print '\n' + underline_title('Pyro configuration')
+                config.input_config('pyro', inputlevel)
+            config.input_config('web', inputlevel)
+            if ASK.confirm('Allow anonymous access ?', False):
+                config.global_set_option('anonymous-user', 'anon')
+                config.global_set_option('anonymous-password', 'anon')
 
-    def postcreate(self):
+    def postcreate(self, *args, **kwargs):
         """hooks called once instance's initialization has been completed"""