merge with 3.21.2
authorRémi Cardona <remi.cardona@logilab.fr>
Mon, 12 Oct 2015 10:53:35 +0200
changeset 10651 9ca33768473c
parent 10622 3cc6154b94a3 (current diff)
parent 10649 4be98ec89fef (diff)
child 10652 e3e4a8c45695
merge with 3.21.2
__pkginfo__.py
cubicweb.spec
devtools/__init__.py
devtools/test/unittest_testlib.py
devtools/testlib.py
misc/migration/3.21.0_Any.py
predicates.py
server/checkintegrity.py
server/migractions.py
server/sources/__init__.py
server/sources/native.py
server/sources/rql2sql.py
server/sqlutils.py
server/test/unittest_ldapsource.py
server/test/unittest_querier.py
web/views/autoform.py
--- a/.hgtags	Mon Sep 14 11:15:47 2015 +0200
+++ b/.hgtags	Mon Oct 12 10:53:35 2015 +0200
@@ -469,6 +469,9 @@
 5932de3d50bf023544c8f54b47898e4db35eac7c 3.19.12
 5932de3d50bf023544c8f54b47898e4db35eac7c debian/3.19.12-1
 5932de3d50bf023544c8f54b47898e4db35eac7c centos/3.19.12-1
+f933a38d7ab5fc6f2ad593fe1cf9985ce9d7e873 3.19.13
+f933a38d7ab5fc6f2ad593fe1cf9985ce9d7e873 debian/3.19.13-1
+f933a38d7ab5fc6f2ad593fe1cf9985ce9d7e873 centos/3.19.13-1
 7e6b7739afe6128589ad51b0318decb767cbae36 3.20.0
 7e6b7739afe6128589ad51b0318decb767cbae36 debian/3.20.0-1
 7e6b7739afe6128589ad51b0318decb767cbae36 centos/3.20.0-1
@@ -499,9 +502,15 @@
 d477e64475821c21632878062bf68d142252ffc2 3.20.9
 d477e64475821c21632878062bf68d142252ffc2 debian/3.20.9-1
 d477e64475821c21632878062bf68d142252ffc2 centos/3.20.9-1
+8f82e95239625d153a9f1de6e79820d96d9efe8a 3.20.10
+8f82e95239625d153a9f1de6e79820d96d9efe8a debian/3.20.10-1
+8f82e95239625d153a9f1de6e79820d96d9efe8a centos/3.20.10-1
 887c6eef807781560adcd4ecd2dea9011f5a6681 3.21.0
 887c6eef807781560adcd4ecd2dea9011f5a6681 debian/3.21.0-1
 887c6eef807781560adcd4ecd2dea9011f5a6681 centos/3.21.0-1
 a8a0de0298a58306d63dbc998ad60c48bf18c80a 3.21.1
 a8a0de0298a58306d63dbc998ad60c48bf18c80a debian/3.21.1-1
 a8a0de0298a58306d63dbc998ad60c48bf18c80a centos/3.21.1-1
+a5428e1ab36491a8e6d66ce09d23b708b97e1337 3.21.2
+a5428e1ab36491a8e6d66ce09d23b708b97e1337 debian/3.21.2-1
+a5428e1ab36491a8e6d66ce09d23b708b97e1337 centos/3.21.2-1
--- a/__pkginfo__.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/__pkginfo__.py	Mon Oct 12 10:53:35 2015 +0200
@@ -22,7 +22,7 @@
 
 modname = distname = "cubicweb"
 
-numversion = (3, 21, 1)
+numversion = (3, 21, 2)
 version = '.'.join(str(num) for num in numversion)
 
 description = "a repository of entities / relations for knowledge management"
@@ -56,7 +56,6 @@
 
 __recommends__ = {
     'docutils': '>= 0.6',
-    'Pyro': '>= 3.9.1, < 4.0.0',
     'Pillow': '',               # for captcha
     'pycrypto': '',             # for crypto extensions
     'fyzz': '>= 0.1.0',         # for sparql
--- a/cubicweb.spec	Mon Sep 14 11:15:47 2015 +0200
+++ b/cubicweb.spec	Mon Oct 12 10:53:35 2015 +0200
@@ -7,7 +7,7 @@
 %endif
 
 Name:           cubicweb
-Version:        3.21.1
+Version:        3.21.2
 Release:        logilab.1%{?dist}
 Summary:        CubicWeb is a semantic web application framework
 Source0:        http://download.logilab.org/pub/cubicweb/cubicweb-%{version}.tar.gz
--- a/debian/changelog	Mon Sep 14 11:15:47 2015 +0200
+++ b/debian/changelog	Mon Oct 12 10:53:35 2015 +0200
@@ -1,3 +1,9 @@
+cubicweb (3.21.2-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Rémi Cardona <remi.cardona@logilab.fr>  Fri, 09 Oct 2015 18:00:39 +0200
+
 cubicweb (3.21.1-1) unstable; urgency=medium
 
   * new upstream release
@@ -10,6 +16,12 @@
 
  -- Julien Cristau <julien.cristau@logilab.fr>  Fri, 10 Jul 2015 17:04:11 +0200
 
+cubicweb (3.20.10-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Rémi Cardona <remi.cardona@logilab.fr>  Thu, 08 Oct 2015 18:47:24 +0200
+
 cubicweb (3.20.9-1) unstable; urgency=low
 
   * New upstream release.
@@ -76,6 +88,12 @@
 
  -- Julien Cristau <julien.cristau@logilab.fr>  Tue, 06 Jan 2015 18:11:03 +0100
 
+cubicweb (3.19.13-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Rémi Cardona <remi.cardona@logilab.fr>  Tue, 06 Oct 2015 18:31:33 +0200
+
 cubicweb (3.19.12-1) unstable; urgency=low
 
   * New upstream release
--- a/devtools/__init__.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/devtools/__init__.py	Mon Oct 12 10:53:35 2015 +0200
@@ -629,6 +629,11 @@
             return backup_name
         return None
 
+    def has_cache(self, db_id):
+        backup_name = self._backup_name(db_id)
+        return (super(PostgresTestDataBaseHandler, self).has_cache(db_id)
+                and backup_name in self.helper.list_databases(self.cursor))
+
     def init_test_database(self):
         """initialize a fresh postgresql database used for testing purpose"""
         from cubicweb.server import init_repository
--- a/devtools/test/unittest_testlib.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/devtools/test/unittest_testlib.py	Mon Oct 12 10:53:35 2015 +0200
@@ -197,6 +197,18 @@
             self.assertTrue(rdef.permissions['add'])
         self.assertTrue(rdef.permissions['read'], ())
 
+    def test_temporary_permissions_rdef_with_exception(self):
+        rdef = self.schema['CWUser'].rdef('in_group')
+        try:
+            with self.temporary_permissions((rdef, {'read': ()})):
+                self.assertEqual(rdef.permissions['read'], ())
+                self.assertTrue(rdef.permissions['add'])
+                raise ValueError('goto')
+        except ValueError:
+            self.assertTrue(rdef.permissions['read'], ())
+        else:
+            self.fail('exception was caught unexpectedly')
+
     def test_temporary_appobjects_registered(self):
 
         class AnAppobject(object):
--- a/devtools/testlib.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/devtools/testlib.py	Mon Oct 12 10:53:35 2015 +0200
@@ -528,16 +528,18 @@
                 origperms = erschema.permissions[action]
                 erschema.set_action_permissions(action, actionperms)
                 torestore.append([erschema, action, origperms])
-        yield
-        for erschema, action, permissions in torestore:
-            if action is None:
-                erschema.permissions = permissions
-            else:
-                erschema.set_action_permissions(action, permissions)
+        try:
+            yield
+        finally:
+            for erschema, action, permissions in torestore:
+                if action is None:
+                    erschema.permissions = permissions
+                else:
+                    erschema.set_action_permissions(action, permissions)
 
     def assertModificationDateGreater(self, entity, olddate):
         entity.cw_attr_cache.pop('modification_date', None)
-        self.assertTrue(entity.modification_date > olddate)
+        self.assertGreater(entity.modification_date, olddate)
 
     def assertMessageEqual(self, req, params, expected_msg):
         msg = req.session.data[params['_cwmsgid']]
--- a/hooks/security.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/hooks/security.py	Mon Oct 12 10:53:35 2015 +0200
@@ -143,10 +143,13 @@
     """
     assert rschema.inlined
     try:
-        entity = cnx.transaction_data['ecache'][eid]
+        entity = cnx.entity_cache(eid)
     except KeyError:
         return False
-    return rschema.type in entity.cw_edited.skip_security
+    edited = getattr(entity, 'cw_edited', None)
+    if edited is None:
+        return False
+    return rschema.type in edited.skip_security
 
 
 class BeforeAddRelationSecurityHook(SecurityHook):
--- a/hooks/syncschema.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/hooks/syncschema.py	Mon Oct 12 10:53:35 2015 +0200
@@ -701,6 +701,16 @@
         syssource = cnx.repo.system_source
         cstrtype = self.oldcstr.type()
         if cstrtype == 'SizeConstraint':
+            # if the size constraint is being replaced with a new max size, we'll
+            # call update_rdef_column in CWConstraintAddOp, skip it here
+            for cstr in cnx.transaction_data.get('newsizecstr', ()):
+                rdefentity = cstr.reverse_constrained_by[0]
+                cstrrdef = cnx.vreg.schema.schema_by_eid(rdefentity.eid)
+                if cstrrdef == rdef:
+                    return
+
+            # we found that the size constraint for this rdef is really gone,
+            # not just replaced by another
             syssource.update_rdef_column(cnx, rdef)
             self.size_cstr_changed = True
         elif cstrtype == 'UniqueConstraint':
@@ -794,6 +804,13 @@
     entity = cstrname = None # for pylint
     cols = () # for pylint
 
+    def insert_index(self):
+        # We need to run before CWConstraintDelOp: if a size constraint is
+        # removed and the column is part of a unique_together constraint, we
+        # remove the unique_together index before changing the column's type.
+        # SQL Server does not support unique indices on unlimited text columns.
+        return 0
+
     def precommit_event(self):
         cnx = self.cnx
         prefix = SQL_PREFIX
@@ -1224,6 +1241,11 @@
     events = ('after_add_entity', 'after_update_entity')
 
     def __call__(self):
+        if self.entity.cstrtype[0].name == 'SizeConstraint':
+            txdata = self._cw.transaction_data
+            if 'newsizecstr' not in txdata:
+                txdata['newsizecstr'] = set()
+            txdata['newsizecstr'].add(self.entity)
         CWConstraintAddOp(self._cw, entity=self.entity)
 
 
--- a/misc/migration/3.21.0_Any.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/misc/migration/3.21.0_Any.py	Mon Oct 12 10:53:35 2015 +0200
@@ -5,7 +5,7 @@
 
 
 def add_foreign_keys():
-    source = repo.sources_by_uri['system']
+    source = repo.system_source
     if not source.dbhelper.alter_column_support:
         return
     for rschema in schema.relations():
@@ -35,14 +35,25 @@
         sql('DELETE FROM %(r)s_relation '
             'WHERE eid_to IN (SELECT eid_to FROM %(r)s_relation EXCEPT SELECT eid FROM entities)' % args)
 
-    sql('ALTER TABLE %(r)s_relation DROP CONSTRAINT IF EXISTS %(r)s_relation_eid_from_fkey' % args,
-        ask_confirm=False)
-    sql('ALTER TABLE %(r)s_relation DROP CONSTRAINT IF EXISTS %(r)s_relation_eid_to_fkey' % args,
-        ask_confirm=False)
-    sql('ALTER TABLE %(r)s_relation ADD CONSTRAINT %(r)s_relation_eid_from_fkey '
+    args['from_fk'] = '%(r)s_relation_eid_from_fkey' % args
+    args['to_fk'] = '%(r)s_relation_eid_to_fkey' % args
+    args['table'] = '%(r)s_relation' % args
+    if repo.system_source.dbdriver == 'postgres':
+        sql('ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(from_fk)s' % args,
+            ask_confirm=False)
+        sql('ALTER TABLE %(table)s DROP CONSTRAINT IF EXISTS %(to_fk)s' % args,
+            ask_confirm=False)
+    elif repo.system_source.dbdriver.startswith('sqlserver'):
+        sql("IF OBJECT_ID('%(from_fk)s', 'F') IS NOT NULL "
+            "ALTER TABLE %(table)s DROP CONSTRAINT %(from_fk)s" % args,
+            ask_confirm=False)
+        sql("IF OBJECT_ID('%(to_fk)s', 'F') IS NOT NULL "
+            "ALTER TABLE %(table)s DROP CONSTRAINT %(to_fk)s" % args,
+            ask_confirm=False)
+    sql('ALTER TABLE %(table)s ADD CONSTRAINT %(from_fk)s '
         'FOREIGN KEY (eid_from) REFERENCES entities (eid)' % args,
         ask_confirm=False)
-    sql('ALTER TABLE %(r)s_relation ADD CONSTRAINT %(r)s_relation_eid_to_fkey '
+    sql('ALTER TABLE %(table)s ADD CONSTRAINT %(to_fk)s '
         'FOREIGN KEY (eid_to) REFERENCES entities (eid)' % args,
         ask_confirm=False)
 
@@ -75,8 +86,14 @@
                 print('%(e)s.%(r)s references unknown entities, deleting relation' % args)
                 sql('UPDATE cw_%(e)s SET cw_%(r)s = NULL WHERE cw_%(r)s IS NOT NULL AND cw_%(r)s IN '
                     '(SELECT cw_%(r)s FROM cw_%(e)s EXCEPT SELECT eid FROM entities)' % args)
-        sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS %(c)s' % args,
-            ask_confirm=False)
+
+        if repo.system_source.dbdriver == 'postgres':
+            sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS %(c)s' % args,
+                ask_confirm=False)
+        elif repo.system_source.dbdriver.startswith('sqlserver'):
+            sql("IF OBJECT_ID('%(c)s', 'F') IS NOT NULL "
+                "ALTER TABLE cw_%(e)s DROP CONSTRAINT %(c)s" % args,
+                ask_confirm=False)
         sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT %(c)s '
             'FOREIGN KEY (cw_%(r)s) references entities(eid)' % args,
             ask_confirm=False)
@@ -92,9 +109,15 @@
         print('%(e)s has nonexistent entities, deleting' % args)
         sql('DELETE FROM cw_%(e)s WHERE cw_eid IN '
             '(SELECT cw_eid FROM cw_%(e)s EXCEPT SELECT eid FROM entities)' % args)
-    sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS cw_%(e)s_cw_eid_fkey' % args,
-        ask_confirm=False)
-    sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT cw_%(e)s_cw_eid_fkey '
+    args['c'] = 'cw_%(e)s_cw_eid_fkey' % args
+    if repo.system_source.dbdriver == 'postgres':
+        sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS %(c)s' % args,
+            ask_confirm=False)
+    elif repo.system_source.dbdriver.startswith('sqlserver'):
+        sql("IF OBJECT_ID('%(c)s', 'F') IS NOT NULL "
+            "ALTER TABLE cw_%(e)s DROP CONSTRAINT %(c)s" % args,
+            ask_confirm=False)
+    sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT %(c)s '
         'FOREIGN KEY (cw_eid) REFERENCES entities (eid)' % args,
         ask_confirm=False)
 
@@ -105,7 +128,10 @@
 helper = repo.system_source.dbhelper
 
 helper.drop_index(cu, 'entities', 'extid', False)
-helper.create_index(cu, 'entities', 'extid', True)
+# don't use create_index because it doesn't work for columns that may be NULL
+# on sqlserver
+for query in helper.sqls_create_multicol_unique_index('entities', ['extid']):
+    cu.execute(query)
 
 if 'moved_entities' not in helper.list_tables(cu):
     sql('''
@@ -117,9 +143,10 @@
 
 moved_entities = sql('SELECT -eid, extid FROM entities WHERE eid < 0',
                      ask_confirm=False)
-cu.executemany('INSERT INTO moved_entities (eid, extid) VALUES (%s, %s)',
-               moved_entities)
-sql('DELETE FROM entities WHERE eid < 0')
+if moved_entities:
+    cu.executemany('INSERT INTO moved_entities (eid, extid) VALUES (%s, %s)',
+                   moved_entities)
+    sql('DELETE FROM entities WHERE eid < 0')
 
 commit()
 
@@ -137,6 +164,11 @@
         continue
     cstrname, check = check_constraint(rdef.subject, rdef.object, rdef.rtype.type,
             cstr, helper, prefix='cw_')
-    sql('ALTER TABLE %s%s DROP CONSTRAINT IF EXISTS %s' % ('cw_', rdef.subject.type, cstrname))
-    sql('ALTER TABLE %s%s ADD CONSTRAINT %s CHECK(%s)' % ('cw_', rdef.subject.type, cstrname, check))
+    args = {'e': rdef.subject.type, 'c': cstrname, 'v': check}
+    if repo.system_source.dbdriver == 'postgres':
+        sql('ALTER TABLE cw_%(e)s DROP CONSTRAINT IF EXISTS %(c)s' % args)
+    elif repo.system_source.dbdriver.startswith('sqlserver'):
+        sql("IF OBJECT_ID('%(c)s', 'C') IS NOT NULL "
+            "ALTER TABLE cw_%(e)s DROP CONSTRAINT %(c)s" % args)
+    sql('ALTER TABLE cw_%(e)s ADD CONSTRAINT %(c)s CHECK(%(v)s)' % args)
 commit()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/migration/3.21.2_Any.py	Mon Oct 12 10:53:35 2015 +0200
@@ -0,0 +1,7 @@
+sync_schema_props_perms('cwuri')
+
+helper = repo.system_source.dbhelper
+cu = session.cnxset.cu
+helper.set_null_allowed(cu, 'moved_entities', 'extid', 'VARCHAR(256)', False)
+
+commit()
--- a/predicates.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/predicates.py	Mon Oct 12 10:53:35 2015 +0200
@@ -1372,7 +1372,7 @@
         score = self.score_class(req.vreg['etypes'].etype_class(etype), req)
         if score:
             eschema = req.vreg.schema.eschema(etype)
-            if eschema.has_local_role('add') or eschema.has_perm(req, 'add'):
+            if eschema.may_have_permission('add', req):
                 return score
         return 0
 
--- a/schemas/base.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/schemas/base.py	Mon Oct 12 10:53:35 2015 +0200
@@ -165,7 +165,6 @@
     cardinality = '11'
     subject = '*'
     object = 'String'
-    constraints = [UniqueConstraint()]
 
 
 # XXX find a better relation name
--- a/server/checkintegrity.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/checkintegrity.py	Mon Oct 12 10:53:35 2015 +0200
@@ -209,7 +209,7 @@
                                 '  WHERE cs.eid_from=e.eid AND cs.eid_to=s.cw_eid) '
                                 'ORDER BY e.eid')
     for row in cursor.fetchall():
-        sys.stderr.write(msg % row)
+        sys.stderr.write(msg % tuple(row))
     if fix:
         cnx.system_sql('INSERT INTO is_relation (eid_from, eid_to) '
                            'SELECT e.eid, s.cw_eid FROM entities as e, cw_CWEType as s '
@@ -223,7 +223,7 @@
                                 '  WHERE cs.eid_from=e.eid AND cs.eid_to=s.cw_eid) '
                                 'ORDER BY e.eid')
     for row in cursor.fetchall():
-        sys.stderr.write(msg % row)
+        sys.stderr.write(msg % tuple(row))
     if fix:
         cnx.system_sql('INSERT INTO is_instance_of_relation (eid_from, eid_to) '
                            'SELECT e.eid, s.cw_eid FROM entities as e, cw_CWEType as s '
--- a/server/migractions.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/migractions.py	Mon Oct 12 10:53:35 2015 +0200
@@ -62,9 +62,6 @@
 from cubicweb.server.sqlutils import sqlexec, SQL_PREFIX
 
 
-def mock_object(**params):
-    return type('Mock', (), params)()
-
 class ClearGroupMap(hook.Hook):
     __regid__ = 'cw.migration.clear_group_mapping'
     __select__ = hook.Hook.__select__ & is_instance('CWGroup')
@@ -451,7 +448,8 @@
         rtype = str(rtype)
         if rtype in self._synchronized:
             return
-        self._synchronized.add(rtype)
+        if syncrdefs and syncperms and syncprops:
+            self._synchronized.add(rtype)
         rschema = self.fs_schema.rschema(rtype)
         reporschema = self.repo.schema.rschema(rtype)
         if syncprops:
@@ -482,7 +480,8 @@
         etype = str(etype)
         if etype in self._synchronized:
             return
-        self._synchronized.add(etype)
+        if syncrdefs and syncperms and syncprops:
+            self._synchronized.add(etype)
         repoeschema = self.repo.schema.eschema(etype)
         try:
             eschema = self.fs_schema.eschema(etype)
@@ -580,9 +579,10 @@
         reporschema = self.repo.schema.rschema(rschema)
         if (subjtype, rschema, objtype) in self._synchronized:
             return
-        self._synchronized.add((subjtype, rschema, objtype))
-        if rschema.symmetric:
-            self._synchronized.add((objtype, rschema, subjtype))
+        if syncperms and syncprops:
+            self._synchronized.add((subjtype, rschema, objtype))
+            if rschema.symmetric:
+                self._synchronized.add((objtype, rschema, subjtype))
         rdef = rschema.rdef(subjtype, objtype)
         if rdef.infered:
             return # don't try to synchronize infered relation defs
@@ -1076,10 +1076,11 @@
             if not self.confirm('Relation %s is still present in the filesystem schema,'
                                 ' do you really want to drop it?' % oldname,
                                 default='n'):
-                raise SystemExit(1)
+                return
         self.cmd_add_relation_type(newname, commit=True)
-        self.rqlexec('SET X %s Y WHERE X %s Y' % (newname, oldname),
-                     ask_confirm=self.verbosity>=2)
+        if not self.repo.schema[oldname].rule:
+            self.rqlexec('SET X %s Y WHERE X %s Y' % (newname, oldname),
+                         ask_confirm=self.verbosity>=2)
         self.cmd_drop_relation_type(oldname, commit=commit)
 
     def cmd_add_relation_definition(self, subjtype, rtype, objtype, commit=True):
@@ -1476,19 +1477,28 @@
         * the actual schema won't be updated until next startup
         """
         rschema = self.repo.schema.rschema(attr)
-        oldtype = rschema.objects(etype)[0]
-        rdefeid = rschema.rdef(etype, oldtype).eid
-        allownull = rschema.rdef(etype, oldtype).cardinality[0] != '1'
+        oldschema = rschema.objects(etype)[0]
+        rdef = rschema.rdef(etype, oldschema)
         sql = ("UPDATE cw_CWAttribute "
                "SET cw_to_entity=(SELECT cw_eid FROM cw_CWEType WHERE cw_name='%s')"
-               "WHERE cw_eid=%s") % (newtype, rdefeid)
+               "WHERE cw_eid=%s") % (newtype, rdef.eid)
         self.sqlexec(sql, ask_confirm=False)
         dbhelper = self.repo.system_source.dbhelper
         sqltype = dbhelper.TYPE_MAPPING[newtype]
         cursor = self.cnx.cnxset.cu
-        dbhelper.change_col_type(cursor, 'cw_%s'  % etype, 'cw_%s' % attr, sqltype, allownull)
+        allownull = rdef.cardinality[0] != '1'
+        dbhelper.change_col_type(cursor, 'cw_%s' % etype, 'cw_%s' % attr, sqltype, allownull)
         if commit:
             self.commit()
+            # manually update live schema
+            eschema = self.repo.schema[etype]
+            rschema._subj_schemas[eschema].remove(oldschema)
+            rschema._obj_schemas[oldschema].remove(eschema)
+            newschema = self.repo.schema[newtype]
+            rschema._update(eschema, newschema)
+            rdef.object = newschema
+            del rschema.rdefs[(eschema, oldschema)]
+            rschema.rdefs[(eschema, newschema)] = rdef
 
     def cmd_add_entity_type_table(self, etype, commit=True):
         """low level method to create the sql table for an existing entity.
--- a/server/sources/__init__.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/sources/__init__.py	Mon Oct 12 10:53:35 2015 +0200
@@ -132,6 +132,9 @@
     def __eq__(self, other):
         return self.uri == other.uri
 
+    def __ne__(self, other):
+        return not (self == other)
+
     def backup(self, backupfile, confirm, format='native'):
         """method called to create a backup of source's data"""
         pass
@@ -405,7 +408,7 @@
     # system source interface #################################################
 
     def eid_type_source(self, cnx, eid):
-        """return a tuple (type, source, extid) for the entity with id <eid>"""
+        """return a tuple (type, extid, source) for the entity with id <eid>"""
         raise NotImplementedError(self)
 
     def create_eid(self, cnx):
--- a/server/sources/native.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/sources/native.py	Mon Oct 12 10:53:35 2015 +0200
@@ -852,7 +852,7 @@
         raise UnknownEid(eid)
 
     def eid_type_source(self, cnx, eid): # pylint: disable=E0202
-        """return a tuple (type, source, extid) for the entity with id <eid>"""
+        """return a tuple (type, extid, source) for the entity with id <eid>"""
         sql = 'SELECT type, extid, asource FROM entities WHERE eid=%s' % eid
         res = self._eid_type_source(cnx, eid, sql)
         if not isinstance(res, list):
@@ -861,7 +861,7 @@
         return res
 
     def eid_type_source_pre_131(self, cnx, eid):
-        """return a tuple (type, source, extid) for the entity with id <eid>"""
+        """return a tuple (type, extid, source) for the entity with id <eid>"""
         sql = 'SELECT type, extid FROM entities WHERE eid=%s' % eid
         res = self._eid_type_source(cnx, eid, sql)
         if not isinstance(res, list):
@@ -933,12 +933,12 @@
             self._handle_is_relation_sql(cnx, 'INSERT INTO cw_source_relation(eid_from,eid_to) VALUES (%s,%s)',
                                          (entity.eid, source.eid))
         # now we can update the full text index
-        if self.do_fti and self.need_fti_indexation(entity.cw_etype):
+        if self.need_fti_indexation(entity.cw_etype):
             self.index_entity(cnx, entity=entity)
 
     def update_info(self, cnx, entity, need_fti_update):
         """mark entity as being modified, fulltext reindex if needed"""
-        if self.do_fti and need_fti_update:
+        if need_fti_update:
             # reindex the entity only if this query is updating at least
             # one indexable attribute
             self.index_entity(cnx, entity=entity)
@@ -1334,7 +1334,8 @@
         """create an operation to [re]index textual content of the given entity
         on commit
         """
-        FTIndexEntityOp.get_instance(cnx).add_data(entity.eid)
+        if self.do_fti:
+            FTIndexEntityOp.get_instance(cnx).add_data(entity.eid)
 
     def fti_unindex_entities(self, cnx, entities):
         """remove text content for entities from the full text index
@@ -1401,12 +1402,12 @@
   eid INTEGER PRIMARY KEY NOT NULL,
   type VARCHAR(64) NOT NULL,
   asource VARCHAR(128) NOT NULL,
-  extid VARCHAR(256) UNIQUE
+  extid VARCHAR(256)
 );;
 CREATE INDEX entities_type_idx ON entities(type);;
 CREATE TABLE moved_entities (
   eid INTEGER PRIMARY KEY NOT NULL,
-  extid VARCHAR(256) UNIQUE
+  extid VARCHAR(256) UNIQUE NOT NULL
 );;
 
 CREATE TABLE transactions (
@@ -1459,18 +1460,22 @@
     DELETE FROM tx_relation_actions WHERE tx_uuid=OLD.tx_uuid;
 END;;
 '''
+    schema += ';;'.join(helper.sqls_create_multicol_unique_index('entities', ['extid']))
+    schema += ';;\n'
     return schema
 
 
 def sql_drop_schema(driver):
     helper = get_db_helper(driver)
     return """
+%s;
 %s
 DROP TABLE entities;
 DROP TABLE tx_entity_actions;
 DROP TABLE tx_relation_actions;
 DROP TABLE transactions;
-""" % helper.sql_drop_numrange('entities_id_seq')
+""" % (';'.join(helper.sqls_drop_multicol_unique_index('entities', ['extid'])),
+       helper.sql_drop_numrange('entities_id_seq'))
 
 
 def grant_schema(user, set_owner=True):
@@ -1720,7 +1725,7 @@
             self.logger.info('restoring sequence %s', seq)
             self.read_sequence(archive, seq)
         for numrange in numranges:
-            self.logger.info('restoring numrange %s', seq)
+            self.logger.info('restoring numrange %s', numrange)
             self.read_numrange(archive, numrange)
         for table in tables:
             self.logger.info('restoring table %s', table)
--- a/server/sources/rql2sql.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/sources/rql2sql.py	Mon Oct 12 10:53:35 2015 +0200
@@ -50,11 +50,9 @@
 __docformat__ = "restructuredtext en"
 
 import threading
-from datetime import datetime, time
 
 from six.moves import range
 
-from logilab.common.date import utcdatetime, utctime
 from logilab.database import FunctionDescr, SQL_FUNCTIONS_REGISTRY
 
 from rql import BadRQLQuery, CoercionError
@@ -1518,14 +1516,6 @@
                 _id = value
                 if isinstance(_id, unicode):
                     _id = _id.encode()
-                # convert timestamp to utc.
-                # expect SET TiME ZONE to UTC at connection opening time.
-                # This shouldn't change anything for datetime without TZ.
-                value = self._args[_id]
-                if isinstance(value, datetime) and value.tzinfo is not None:
-                    self._query_attrs[_id] = utcdatetime(value)
-                elif isinstance(value, time) and value.tzinfo is not None:
-                    self._query_attrs[_id] = utctime(value)
         else:
             _id = str(id(constant)).replace('-', '', 1)
             self._query_attrs[_id] = value
--- a/server/sqlutils.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/sqlutils.py	Mon Oct 12 10:53:35 2015 +0200
@@ -21,11 +21,11 @@
 __docformat__ = "restructuredtext en"
 
 import sys
-import os
 import re
 import subprocess
 from os.path import abspath
 from logging import getLogger
+from datetime import time, datetime
 
 from six import string_types
 from six.moves import filter
@@ -34,6 +34,7 @@
 from logilab.common.shellutils import ProgressBar, DummyProgressBar
 from logilab.common.deprecation import deprecated
 from logilab.common.logging_ext import set_log_methods
+from logilab.common.date import utctime, utcdatetime
 from logilab.database.sqlgen import SQLGenerator
 
 from cubicweb import Binary, ConfigurationError
@@ -376,8 +377,17 @@
                 # convert cubicweb binary into db binary
                 if isinstance(val, Binary):
                     val = self._binary(val.getvalue())
+                # convert timestamp to utc.
+                # expect SET TiME ZONE to UTC at connection opening time.
+                # This shouldn't change anything for datetime without TZ.
+                elif isinstance(val, datetime) and val.tzinfo is not None:
+                    val = utcdatetime(val)
+                elif isinstance(val, time) and val.tzinfo is not None:
+                    val = utctime(val)
                 newargs[key] = val
             # should not collide
+            assert not (frozenset(newargs) & frozenset(query_args)), \
+                'unexpected collision: %s' % (frozenset(newargs) & frozenset(query_args))
             newargs.update(query_args)
             return newargs
         return query_args
--- a/server/test/data-migractions/migratedapp/schema.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/test/data-migractions/migratedapp/schema.py	Mon Oct 12 10:53:35 2015 +0200
@@ -19,7 +19,7 @@
 import datetime as dt
 from yams.buildobjs import (EntityType, RelationType, RelationDefinition,
                             SubjectRelation, Bytes,
-                            RichString, String, Int, Boolean, Datetime, Date)
+                            RichString, String, Int, Boolean, Datetime, Date, Float)
 from yams.constraints import SizeConstraint, UniqueConstraint
 from cubicweb.schema import (WorkflowableEntityType, RQLConstraint,
                              RQLVocabularyConstraint,
@@ -49,7 +49,7 @@
         }
     nom  = String(maxsize=64, fulltextindexed=True)
     web  = String(maxsize=128)
-    tel  = Int()
+    tel  = Float()
     fax  = Int()
     rncs = String(maxsize=128)
     ad1  = String(maxsize=128)
@@ -108,6 +108,12 @@
 
 
 class Personne(EntityType):
+    __permissions__ = {
+        'read':   ('managers', 'users'), # 'guests' was removed
+        'add':    ('managers', 'users'),
+        'update': ('managers', 'owners'),
+        'delete': ('managers', 'owners')
+    }
     __unique_together__ = [('nom', 'prenom', 'datenaiss')]
     nom    = String(fulltextindexed=True, required=True, maxsize=64)
     prenom = String(fulltextindexed=True, maxsize=64)
--- a/server/test/data/schema.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/test/data/schema.py	Mon Oct 12 10:53:35 2015 +0200
@@ -128,6 +128,12 @@
 
 
 class Personne(EntityType):
+    __permissions__ = {
+        'read':   ('managers', 'users', 'guests'), # 'guests' will be removed
+        'add':    ('managers', 'users'),
+        'update': ('managers', 'owners'),
+        'delete': ('managers', 'owners')
+    }
     __unique_together__ = [('nom', 'prenom', 'inline2')]
     nom    = String(fulltextindexed=True, required=True, maxsize=64)
     prenom = String(fulltextindexed=True, maxsize=64)
--- a/server/test/datacomputed/migratedapp/schema.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/test/datacomputed/migratedapp/schema.py	Mon Oct 12 10:53:35 2015 +0200
@@ -55,3 +55,7 @@
 
 class whatever(ComputedRelation):
     rule = 'S employees E, O associates E'
+
+
+class renamed(ComputedRelation):
+    rule = 'S employees E, O concerns E'
--- a/server/test/datacomputed/schema.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/test/datacomputed/schema.py	Mon Oct 12 10:53:35 2015 +0200
@@ -36,11 +36,13 @@
 class Company(EntityType):
     score100 = Float(formula='Any AVG(NN) WHERE X employees E, N concerns E, N note100 NN')
 
+
 class Note(EntityType):
     note = Int()
     note20 = Int(formula='Any N*20 WHERE X note N')
     note100 = Int(formula='Any N*20 WHERE X note N')
 
+
 class concerns(RelationDefinition):
     subject = 'Note'
     object = 'Employee'
@@ -52,3 +54,7 @@
 
 class whatever(ComputedRelation):
     rule = 'S employees E, O concerns E'
+
+
+class to_be_renamed(ComputedRelation):
+    rule = 'S employees E, O concerns E'
--- a/server/test/unittest_ldapsource.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/test/unittest_ldapsource.py	Mon Oct 12 10:53:35 2015 +0200
@@ -29,8 +29,6 @@
 from six import string_types
 from six.moves import range
 
-from logilab.common.testlib import TestCase, unittest_main, mock_object, Tags
-
 from cubicweb import AuthenticationError
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.devtools.repotest import RQLGeneratorTC
@@ -484,4 +482,5 @@
 
 
 if __name__ == '__main__':
+    from logilab.common.testlib import unittest_main
     unittest_main()
--- a/server/test/unittest_migractions.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/test/unittest_migractions.py	Mon Oct 12 10:53:35 2015 +0200
@@ -452,6 +452,9 @@
             delete_concerne_rqlexpr = self._rrqlexpr_rset(cnx, 'delete', 'concerne')
             add_concerne_rqlexpr = self._rrqlexpr_rset(cnx, 'add', 'concerne')
 
+            # make sure properties (e.g. etype descriptions) are synced by the
+            # second call to sync_schema
+            mh.cmd_sync_schema_props_perms(syncprops=False, commit=False)
             mh.cmd_sync_schema_props_perms(commit=False)
 
             self.assertEqual(cnx.execute('Any D WHERE X name "Personne", X description D')[0][0],
@@ -704,6 +707,18 @@
             mh.cmd_add_relation_type('same_as')
             self.assertTrue(self.table_sql(mh, 'same_as_relation'))
 
+    def test_change_attribute_type(self):
+        with self.mh() as (cnx, mh):
+            mh.cmd_create_entity('Societe', tel=1)
+            mh.commit()
+            mh.change_attribute_type('Societe', 'tel', 'Float')
+            self.assertNotIn(('Societe', 'Int'), self.schema['tel'].rdefs)
+            self.assertIn(('Societe', 'Float'), self.schema['tel'].rdefs)
+            self.assertEqual(self.schema['tel'].rdefs[('Societe', 'Float')].object, 'Float')
+            tel = mh.rqlexec('Any T WHERE X tel T')[0][0]
+            self.assertEqual(tel, 1.0)
+            self.assertIsInstance(tel, float)
+
 
 class MigrationCommandsComputedTC(MigrationTC):
     """ Unit tests for computed relations and attributes
@@ -720,8 +735,7 @@
         self.assertNotIn('works_for', self.schema)
         with self.mh() as (cnx, mh):
             with self.assertRaises(ExecutionError) as exc:
-                mh.cmd_add_relation_definition('Employee', 'works_for',
-                                                    'Company')
+                mh.cmd_add_relation_definition('Employee', 'works_for', 'Company')
         self.assertEqual(str(exc.exception),
                          'Cannot add a relation definition for a computed '
                          'relation (works_for)')
@@ -780,6 +794,12 @@
                          'Cannot synchronize a relation definition for a computed '
                          'relation (whatever)')
 
+    def test_computed_relation_rename_relation_type(self):
+        with self.mh() as (cnx, mh):
+            mh.cmd_rename_relation_type('to_be_renamed', 'renamed')
+        self.assertIn('renamed', self.schema)
+        self.assertNotIn('to_be_renamed', self.schema)
+
     # computed attributes migration ############################################
 
     def setup_add_score(self):
@@ -787,9 +807,9 @@
             assert not cnx.execute('Company X')
             c = cnx.create_entity('Company')
             e1 = cnx.create_entity('Employee', reverse_employees=c)
-            n1 = cnx.create_entity('Note', note=2, concerns=e1)
+            cnx.create_entity('Note', note=2, concerns=e1)
             e2 = cnx.create_entity('Employee', reverse_employees=c)
-            n2 = cnx.create_entity('Note', note=4, concerns=e2)
+            cnx.create_entity('Note', note=4, concerns=e2)
             cnx.commit()
 
     def assert_score_initialized(self, mh):
--- a/server/test/unittest_querier.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/server/test/unittest_querier.py	Mon Oct 12 10:53:35 2015 +0200
@@ -1399,6 +1399,13 @@
         self.assertEqual(datenaiss.tzinfo, None)
         self.assertEqual(datenaiss.utctimetuple()[:5], (1977, 6, 7, 1, 0))
 
+    def test_tz_datetime_cache_nonregr(self):
+        datenaiss = datetime(1977, 6, 7, 2, 0, tzinfo=FixedOffset(1))
+        self.qexecute("INSERT Personne X: X nom 'bob', X tzdatenaiss %(date)s",
+                     {'date': datenaiss})
+        self.assertTrue(self.qexecute("Any X WHERE X tzdatenaiss %(d)s", {'d': datenaiss}))
+        self.assertFalse(self.qexecute("Any X WHERE X tzdatenaiss %(d)s", {'d': datenaiss - timedelta(1)}))
+
     # non regression tests #####################################################
 
     def test_nonregr_1(self):
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/test/unittest_sources_native.py	Mon Oct 12 10:53:35 2015 +0200
@@ -0,0 +1,38 @@
+# copyright 2015 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/>.
+
+from logilab.common import tempattr
+
+from cubicweb.devtools.testlib import CubicWebTC
+from cubicweb.server.sources.native import FTIndexEntityOp
+
+class NativeSourceTC(CubicWebTC):
+
+    def test_index_entity_consider_do_fti(self):
+        source = self.repo.system_source
+        with tempattr(source, 'do_fti', False):
+            with self.admin_access.repo_cnx() as cnx:
+                # when do_fti is set to false, call to index_entity (as may be done from hooks)
+                # should have no effect
+                source.index_entity(cnx, cnx.user)
+                self.assertNotIn(cnx.user.eid, FTIndexEntityOp.get_instance(cnx).get_data())
+
+
+if __name__ == '__main__':
+    from logilab.common.testlib import unittest_main
+    unittest_main()
--- a/web/test/unittest_form.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/web/test/unittest_form.py	Mon Oct 12 10:53:35 2015 +0200
@@ -21,7 +21,7 @@
 from xml.etree.ElementTree import fromstring
 from lxml import html
 
-from logilab.common.testlib import unittest_main, mock_object
+from logilab.common.testlib import unittest_main
 
 from cubicweb import Binary, ValidationError
 from cubicweb.devtools.testlib import CubicWebTC
--- a/web/test/unittest_views_actions.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/web/test/unittest_views_actions.py	Mon Oct 12 10:53:35 2015 +0200
@@ -21,6 +21,7 @@
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.web.views import actions, uicfg
 
+
 class ActionsTC(CubicWebTC):
     def test_view_action(self):
         with self.admin_access.web_request(vid='rss', rql='CWUser X') as req:
@@ -33,12 +34,31 @@
         """ensure has_editable_relation predicate used by ModifyAction
         return positive score if there is only some inlined forms
         """
+        # The schema only allows the anonymous user to modify his/her own
+        # EmailAddress if it is set, not to create one. Since the 'anon' CWUser
+        # entity is created without any associated EmailAddress entities, there
+        # are no attributes nor relations that can be edited: the "modify"
+        # action should not appear.
+        with self.new_access('anon').web_request() as req:
+            predicate = actions.has_editable_relation()
+            self.assertEqual(predicate(None, req, rset=req.user.as_rset()),
+                             0)
+        # being allowed to 'add' the relation is not enough
         use_email = self.schema['use_email'].rdefs['CWUser', 'EmailAddress']
         with self.temporary_permissions((use_email, {'add': ('guests',)})):
             with self.new_access('anon').web_request() as req:
                 predicate = actions.has_editable_relation()
                 self.assertEqual(predicate(None, req, rset=req.user.as_rset()),
+                                 0)
+        # if we also allow creating the target etype, then the "modify" action
+        # should appear
+        with self.temporary_permissions((use_email, {'add': ('guests',)}),
+                                        EmailAddress={'add': ('guests',)}):
+            with self.new_access('anon').web_request() as req:
+                predicate = actions.has_editable_relation()
+                self.assertEqual(predicate(None, req, rset=req.user.as_rset()),
                                  1)
 
+
 if __name__ == '__main__':
     unittest_main()
--- a/web/test/unittest_views_editforms.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/web/test/unittest_views_editforms.py	Mon Oct 12 10:53:35 2015 +0200
@@ -20,6 +20,8 @@
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.web.views import uicfg
 from cubicweb.web.formwidgets import AutoCompletionWidget
+from cubicweb.schema import RRQLExpression
+
 
 AFFK = uicfg.autoform_field_kwargs
 AFS = uicfg.autoform_section
@@ -173,6 +175,35 @@
                              [rschema.type
                               for rschema, _ in mform.editable_attributes()])
 
+    def test_inlined_relations(self):
+        with self.admin_access.web_request() as req:
+            with self.temporary_permissions(EmailAddress={'add': ()}):
+                autoform = self.vreg['forms'].select('edition', req, entity=req.user)
+                self.assertEqual(list(autoform.inlined_form_views()), [])
+
+    def test_check_inlined_rdef_permissions(self):
+        # try to check permissions when creating an entity ('user' below is a
+        # fresh entity without an eid)
+        with self.admin_access.web_request() as req:
+            ttype = 'EmailAddress'
+            rschema = self.schema['use_email']
+            rdef =  rschema.rdefs[('CWUser', ttype)]
+            tschema = self.schema[ttype]
+            role = 'subject'
+            with self.temporary_permissions((rdef, {'add': ()})):
+                user = self.vreg['etypes'].etype_class('CWUser')(req)
+                autoform = self.vreg['forms'].select('edition', req, entity=user)
+                self.assertFalse(autoform.check_inlined_rdef_permissions(rschema, role,
+                                                                         tschema, ttype))
+            # we actually don't care about the actual expression,
+            # may_have_permission only checks the presence of such expressions
+            expr = RRQLExpression('S use_email O')
+            with self.temporary_permissions((rdef, {'add': (expr,)})):
+                user = self.vreg['etypes'].etype_class('CWUser')(req)
+                autoform = self.vreg['forms'].select('edition', req, entity=user)
+                self.assertTrue(autoform.check_inlined_rdef_permissions(rschema, role,
+                                                                        tschema, ttype))
+
 
 class FormViewsTC(CubicWebTC):
 
--- a/web/views/actions.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/web/views/actions.py	Mon Oct 12 10:53:35 2015 +0200
@@ -50,7 +50,7 @@
                                                entity=entity, mainform=False)
         for dummy in form.editable_relations():
             return 1
-        for dummy in form.inlined_relations():
+        for dummy in form.inlined_form_views():
             return 1
         for dummy in form.editable_attributes(strict=True):
             return 1
--- a/web/views/autoform.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/web/views/autoform.py	Mon Oct 12 10:53:35 2015 +0200
@@ -128,6 +128,7 @@
 from logilab.mtconverter import xml_escape
 from logilab.common.decorators import iclassmethod, cached
 from logilab.common.deprecation import deprecated
+from logilab.common.registry import NoSelectableObject
 
 from cubicweb import neg_role, uilib
 from cubicweb.schema import display_name
@@ -947,6 +948,8 @@
     def check_inlined_rdef_permissions(self, rschema, role, tschema, ttype):
         """return true if permissions are granted on the inlined object and
         relation"""
+        if not tschema.has_perm(self._cw, 'add'):
+            return False
         entity = self.edited_entity
         rdef = entity.e_schema.rdef(rschema, role, ttype)
         if entity.has_eid():
@@ -954,10 +957,8 @@
                 rdefkwargs = {'fromeid': entity.eid}
             else:
                 rdefkwargs = {'toeid': entity.eid}
-        else:
-            rdefkwargs = {}
-        return (tschema.has_perm(self._cw, 'add')
-                and rdef.has_perm(self._cw, 'add', **rdefkwargs))
+            return rdef.has_perm(self._cw, 'add', **rdefkwargs)
+        return rdef.may_have_permission('add', self._cw)
 
 
     def should_hide_add_new_relation_link(self, rschema, card):
@@ -988,11 +989,16 @@
         """yield inline form views to a newly related (hence created) entity
         through the given relation
         """
-        yield self._cw.vreg['views'].select('inline-creation', self._cw,
-                                            etype=ttype, rtype=rschema, role=role,
-                                            peid=self.edited_entity.eid,
-                                            petype=self.edited_entity.e_schema,
-                                            pform=self)
+        try:
+            yield self._cw.vreg['views'].select('inline-creation', self._cw,
+                                                etype=ttype, rtype=rschema, role=role,
+                                                peid=self.edited_entity.eid,
+                                                petype=self.edited_entity.e_schema,
+                                                pform=self)
+        except NoSelectableObject:
+            # may be raised if user doesn't have the permission to add ttype entities (no checked
+            # earlier) or if there is some custom selector on the view
+            pass
 
 
 ## default form ui configuration ##############################################
--- a/web/views/debug.py	Mon Sep 14 11:15:47 2015 +0200
+++ b/web/views/debug.py	Mon Oct 12 10:53:35 2015 +0200
@@ -132,8 +132,8 @@
         w(u'<tr><th align="left">%s</th><td>%s</td></tr>' % (
             _('data directory url'), req.datadir_url))
         w(u'</table>')
-        if req.user.is_in_group('managers'):
-            from cubicweb.web.application import SESSION_MANAGER
+        from cubicweb.web.application import SESSION_MANAGER
+        if SESSION_MANAGER is not None and req.user.is_in_group('managers'):
             sessions = SESSION_MANAGER.current_sessions()
             w(u'<h3>%s</h3>' % _('opened web sessions'))
             if sessions: