merge 3.20.6 into 3.21
authorRémi Cardona <remi.cardona@logilab.fr>
Wed, 22 Apr 2015 10:08:14 +0200
changeset 10301 729f36a1bcfa
parent 10276 ffb269e60348 (current diff)
parent 10298 e52efb73f9ee (diff)
child 10302 7725396eb3df
merge 3.20.6 into 3.21
__pkginfo__.py
debian/control
devtools/devctl.py
devtools/testlib.py
doc/book/en/devrepo/datamodel/definition.rst
entities/adapters.py
etwist/twconfig.py
misc/migration/3.14.4_Any.py
misc/migration/3.18.0_Any.py
misc/migration/bootstrapmigration_repository.py
server/migractions.py
server/repository.py
server/serverconfig.py
server/session.py
server/sources/native.py
server/test/unittest_repository.py
sobjects/notification.py
web/data/cubicweb.ajax.js
web/test/unittest_views_editforms.py
--- a/.hgtags	Wed Mar 25 10:10:24 2015 +0100
+++ b/.hgtags	Wed Apr 22 10:08:14 2015 +0200
@@ -398,6 +398,13 @@
 b7c373d74754f5ba9344575cb179b47282c413b6 cubicweb-version-3.19.9
 b7c373d74754f5ba9344575cb179b47282c413b6 cubicweb-debian-version-3.19.9-1
 b7c373d74754f5ba9344575cb179b47282c413b6 cubicweb-centos-version-3.19.9-1
+3bab0b9b0ee7355a6fea45c2adca88bffe130e5d cubicweb-version-3.19.10
+3bab0b9b0ee7355a6fea45c2adca88bffe130e5d cubicweb-debian-version-3.19.10-1
+3bab0b9b0ee7355a6fea45c2adca88bffe130e5d cubicweb-centos-version-3.19.10-1
+1ae64186af9448dffbeebdef910c8c7391c04313 cubicweb-version-3.19.11
+1ae64186af9448dffbeebdef910c8c7391c04313 cubicweb-debian-version-3.19.11-1
+1ae64186af9448dffbeebdef910c8c7391c04313 cubicweb-centos-version-3.19.11-1
+6d265ea7d56fe49e9dff261d3b2caf3c2b6f9409 cubicweb-debian-version-3.19.11-2
 7e6b7739afe6128589ad51b0318decb767cbae36 cubicweb-version-3.20.0
 7e6b7739afe6128589ad51b0318decb767cbae36 cubicweb-debian-version-3.20.0-1
 7e6b7739afe6128589ad51b0318decb767cbae36 cubicweb-centos-version-3.20.0-1
@@ -413,3 +420,9 @@
 49831fdc84dc7e7bed01d5e8110a46242b5ccda6 cubicweb-version-3.20.4
 49831fdc84dc7e7bed01d5e8110a46242b5ccda6 cubicweb-debian-version-3.20.4-1
 49831fdc84dc7e7bed01d5e8110a46242b5ccda6 cubicweb-centos-version-3.20.4-1
+51aa56e7d507958b3326abbb6a31d0e6dde6b47b cubicweb-version-3.20.5
+51aa56e7d507958b3326abbb6a31d0e6dde6b47b cubicweb-debian-version-3.20.5-1
+51aa56e7d507958b3326abbb6a31d0e6dde6b47b cubicweb-centos-version-3.20.5-1
+7f64859dcbcdc6394421b8a5175896ba2e5caeb5 cubicweb-version-3.20.6
+7f64859dcbcdc6394421b8a5175896ba2e5caeb5 cubicweb-debian-version-3.20.6-1
+7f64859dcbcdc6394421b8a5175896ba2e5caeb5 cubicweb-centos-version-3.20.6-1
--- a/__init__.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/__init__.py	Wed Apr 22 10:08:14 2015 +0200
@@ -69,12 +69,12 @@
 class Binary(StringIO):
     """customize StringIO to make sure we don't use unicode"""
     def __init__(self, buf=''):
-        assert isinstance(buf, (str, buffer)), \
+        assert isinstance(buf, (str, buffer, bytearray)), \
                "Binary objects must use raw strings, not %s" % buf.__class__
         StringIO.__init__(self, buf)
 
     def write(self, data):
-        assert isinstance(data, (str, buffer)), \
+        assert isinstance(data, (str, buffer, bytearray)), \
                "Binary objects must use raw strings, not %s" % data.__class__
         StringIO.write(self, data)
 
--- a/__pkginfo__.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/__pkginfo__.py	Wed Apr 22 10:08:14 2015 +0200
@@ -22,7 +22,7 @@
 
 modname = distname = "cubicweb"
 
-numversion = (3, 20, 4)
+numversion = (3, 20, 6)
 version = '.'.join(str(num) for num in numversion)
 
 description = "a repository of entities / relations for knowledge management"
--- a/_exceptions.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/_exceptions.py	Wed Apr 22 10:08:14 2015 +0200
@@ -23,7 +23,7 @@
 
 from logilab.common.decorators import cachedproperty
 
-from yams import ValidationError as ValidationError
+from yams import ValidationError
 
 # abstract exceptions #########################################################
 
--- a/cubicweb.spec	Wed Mar 25 10:10:24 2015 +0100
+++ b/cubicweb.spec	Wed Apr 22 10:08:14 2015 +0200
@@ -7,7 +7,7 @@
 %endif
 
 Name:           cubicweb
-Version:        3.20.4
+Version:        3.20.6
 Release:        logilab.1%{?dist}
 Summary:        CubicWeb is a semantic web application framework
 Source0:        http://download.logilab.org/pub/cubicweb/cubicweb-%{version}.tar.gz
--- a/dataimport.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/dataimport.py	Wed Apr 22 10:08:14 2015 +0200
@@ -69,6 +69,7 @@
 import cPickle
 import os.path as osp
 import inspect
+from base64 import b64encode
 from collections import defaultdict
 from copy import copy
 from datetime import date, datetime, time
@@ -788,11 +789,11 @@
         entity = copy(entity)
         entity.cw_edited = copy(entity.cw_edited)
         entity.cw_clear_relation_cache()
-        self.metagen.init_entity(entity)
         entity.cw_edited.update(kwargs, skipsec=False)
+        entity_source, extid = self.metagen.init_entity(entity)
         cnx = self._cnx
         self.source.add_entity(cnx, entity)
-        self.source.add_info(cnx, entity, self.source, None)
+        self.source.add_info(cnx, entity, entity_source, extid)
         kwargs = dict()
         if inspect.getargspec(self.add_relation).keywords:
             kwargs['subjtype'] = entity.cw_etype
@@ -835,16 +836,19 @@
                       - set(('eid', 'cwuri',
                              'is', 'is_instance_of', 'cw_source')))
 
-    def __init__(self, cnx, baseurl=None):
+    def __init__(self, cnx, baseurl=None, source=None):
         self._cnx = cnx
-        self.source = cnx.repo.system_source
-        self.time = datetime.now()
         if baseurl is None:
             config = cnx.vreg.config
             baseurl = config['base-url'] or config.default_base_url()
         if not baseurl[-1] == '/':
             baseurl += '/'
-        self.baseurl =  baseurl
+        self.baseurl = baseurl
+        if source is None:
+            source = cnx.repo.system_source
+        self.source = source
+        self.create_eid = cnx.repo.system_source.create_eid
+        self.time = datetime.now()
         # attributes/relations shared by all entities of the same type
         self.etype_attrs = []
         self.etype_rels = []
@@ -854,6 +858,9 @@
         schema = cnx.vreg.schema
         rschema = schema.rschema
         for rtype in self.META_RELATIONS:
+            # skip owned_by / created_by if user is the internal manager
+            if cnx.user.eid == -1 and rtype in ('owned_by', 'created_by'):
+                continue
             if rschema(rtype).final:
                 self.etype_attrs.append(rtype)
             else:
@@ -877,16 +884,24 @@
         return entity, rels
 
     def init_entity(self, entity):
-        entity.eid = self.source.create_eid(self._cnx)
+        entity.eid = self.create_eid(self._cnx)
+        extid = entity.cw_edited.get('cwuri')
         for attr in self.entity_attrs:
+            if attr in entity.cw_edited:
+                # already set, skip this attribute
+                continue
             genfunc = self.generate(attr)
             if genfunc:
                 entity.cw_edited.edited_attribute(attr, genfunc(entity))
+        if isinstance(extid, unicode):
+            extid = extid.encode('utf-8')
+        return self.source, extid
 
     def generate(self, rtype):
         return getattr(self, 'gen_%s' % rtype, None)
 
     def gen_cwuri(self, entity):
+        assert self.baseurl, 'baseurl is None while generating cwuri'
         return u'%s%s' % (self.baseurl, entity.eid)
 
     def gen_creation_date(self, entity):
--- a/debian/changelog	Wed Mar 25 10:10:24 2015 +0100
+++ b/debian/changelog	Wed Apr 22 10:08:14 2015 +0200
@@ -1,3 +1,21 @@
+cubicweb (3.20.6-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Julien Cristau <julien.cristau@logilab.fr>  Thu, 02 Apr 2015 10:58:16 +0200
+
+cubicweb (3.20.5-2) unstable; urgency=low
+
+  * Fix cubicweb-dev dependencies.
+
+ -- Julien Cristau <julien.cristau@logilab.fr>  Fri, 27 Mar 2015 17:22:53 +0100
+
+cubicweb (3.20.5-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Julien Cristau <julien.cristau@logilab.fr>  Fri, 27 Mar 2015 14:56:16 +0100
+
 cubicweb (3.20.4-1) unstable; urgency=low
 
   * new upstream release
@@ -28,6 +46,24 @@
 
  -- Julien Cristau <julien.cristau@logilab.fr>  Tue, 06 Jan 2015 18:11:03 +0100
 
+cubicweb (3.19.11-2) UNRELEASED; urgency=low
+
+  * Fix cubicweb-dev dependencies.
+
+ -- Julien Cristau <julien.cristau@logilab.fr>  Fri, 27 Mar 2015 16:53:20 +0100
+
+cubicweb (3.19.11-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Julien Cristau <julien.cristau@logilab.fr>  Wed, 18 Mar 2015 11:55:52 +0100
+
+cubicweb (3.19.10-1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Rémi Cardona <remi.cardona@logilab.fr>  Thu, 12 Mar 2015 11:52:01 +0100
+
 cubicweb (3.19.9-1) unstable; urgency=low
 
   * new upstream release
--- a/debian/control	Wed Mar 25 10:10:24 2015 +0100
+++ b/debian/control	Wed Apr 22 10:08:14 2015 +0200
@@ -202,6 +202,7 @@
  ${python:Depends},
  cubicweb-server (= ${source:Version}),
  cubicweb-web (= ${source:Version}),
+ cubicweb-twisted (= ${source:Version}),
  python-pysqlite2
 Suggests:
  w3c-dtd-xhtml,
--- a/debian/watch	Wed Mar 25 10:10:24 2015 +0100
+++ b/debian/watch	Wed Apr 22 10:08:14 2015 +0200
@@ -1,2 +1,3 @@
 version=3
-http://download.logilab.org/pub/cubicweb cubicweb-(.*)\.tar\.gz
+opts=uversionmangle=s/(rc|a|b|c)/~$1/ \
+http://pypi.debian.net/cubicweb/cubicweb-(.+)\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))
--- a/devtools/devctl.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/devtools/devctl.py	Wed Apr 22 10:08:14 2015 +0200
@@ -167,6 +167,7 @@
             if not eschema.final:
                 add_msg(w, 'This %s:' % etype)
                 add_msg(w, 'New %s' % etype)
+                add_msg(w, 'add a %s' % etype) # AddNewAction
                 if libconfig is not None:  # processing a cube
                     # As of 3.20.3 we no longer use it, but keeping this string
                     # allows developers to run i18ncube with new cubicweb and still
--- a/devtools/test/data/cubes/i18ntestcube/i18n/en.po.ref	Wed Mar 25 10:10:24 2015 +0100
+++ b/devtools/test/data/cubes/i18ntestcube/i18n/en.po.ref	Wed Apr 22 10:08:14 2015 +0200
@@ -41,6 +41,12 @@
 msgid "add ForumThread in_forum Forum object"
 msgstr ""
 
+msgid "add a Forum"
+msgstr ""
+
+msgid "add a ForumThread"
+msgstr ""
+
 msgid "creating ForumThread (ForumThread in_forum Forum %(linkto)s)"
 msgstr ""
 
--- a/devtools/test/unittest_testlib.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/devtools/test/unittest_testlib.py	Wed Apr 22 10:08:14 2015 +0200
@@ -28,6 +28,25 @@
 from cubicweb.devtools.testlib import CubicWebTC
 from cubicweb.pytestconf import clean_repo_test_cls
 
+class FakeFormTC(TestCase):
+    def test_fake_form(self):
+        class entity:
+            cw_etype = 'Entity'
+            eid = 0
+        sio = StringIO('hop\n')
+        form = CubicWebTC.fake_form('import',
+                                    {'file': ('filename.txt', sio),
+                                     'encoding': u'utf-8',
+                                    }, [(entity, {'field': 'value'})])
+        self.assertEqual(form, {'__form_id': 'import',
+                                '__maineid': 0,
+                                '__type:0': 'Entity',
+                                '_cw_entity_fields:0': '__type,field',
+                                '_cw_fields': 'file,encoding',
+                                'eid': [0],
+                                'encoding': u'utf-8',
+                                'field:0': 'value',
+                                'file': ('filename.txt', sio)})
 
 class WebTestTC(TestCase):
 
--- a/devtools/testlib.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/devtools/testlib.py	Wed Apr 22 10:08:14 2015 +0200
@@ -44,7 +44,7 @@
 from cubicweb import cwconfig, devtools, web, server, repoapi
 from cubicweb.utils import json
 from cubicweb.sobjects import notification
-from cubicweb.web import Redirect, application
+from cubicweb.web import Redirect, application, eid_param
 from cubicweb.server.hook import SendMailOp
 from cubicweb.server.session import Session
 from cubicweb.devtools import SYSTEM_ENTITIES, SYSTEM_RELATIONS, VIEW_VALIDATORS
@@ -886,6 +886,43 @@
             raise
         return result
 
+    @staticmethod
+    def fake_form(formid, field_dict=None, entity_field_dicts=()):
+        """Build _cw.form dictionnary to fake posting of some standard cubicweb form
+
+        * `formid`, the form id, usually form's __regid__
+
+        * `field_dict`, dictionary of name:value for fields that are not tied to an entity
+
+        * `entity_field_dicts`, list of (entity, dictionary) where dictionary contains name:value
+          for fields that are not tied to the given entity
+        """
+        assert field_dict or entity_field_dicts, \
+                'field_dict and entity_field_dicts arguments must not be both unspecified'
+        if field_dict is None:
+            field_dict = {}
+        form = {'__form_id': formid}
+        fields = []
+        for field, value in field_dict.items():
+            fields.append(field)
+            form[field] = value
+        def _add_entity_field(entity, field, value):
+            entity_fields.append(field)
+            form[eid_param(field, entity.eid)] = value
+        for entity, field_dict in entity_field_dicts:
+            if '__maineid' not in form:
+                form['__maineid'] = entity.eid
+            entity_fields = []
+            form.setdefault('eid', []).append(entity.eid)
+            _add_entity_field(entity, '__type', entity.cw_etype)
+            for field, value in field_dict.items():
+                _add_entity_field(entity, field, value)
+            if entity_fields:
+                form[eid_param('_cw_entity_fields', entity.eid)] = ','.join(entity_fields)
+        if fields:
+            form['_cw_fields'] = ','.join(fields)
+        return form
+
     @deprecated('[3.19] use .admin_request_from_url instead')
     def req_from_url(self, url):
         """parses `url` and builds the corresponding CW-web request
--- a/doc/book/en/devrepo/datamodel/definition.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/doc/book/en/devrepo/datamodel/definition.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -244,8 +244,14 @@
 
 .. sourcecode:: python
 
-   from yams.constraints import BoundaryConstraint, TODAY
-   BoundaryConstraint('<=', TODAY())
+   from yams.constraints import BoundaryConstraint, TODAY, NOW, Attribute
+
+   class DatedEntity(EntityType):
+      start = Date(constraints=[BoundaryConstraint('>=', TODAY())])
+      end = Date(constraints=[BoundaryConstraint('>=', Attribute('start'))])
+
+   class Before(EntityType);
+      last_time = DateTime(constraints=[BoundaryConstraint('<=', NOW())])
 
 * `IntervalBoundConstraint`: allows to specify an interval with
   included values
@@ -259,7 +265,12 @@
 
 * `StaticVocabularyConstraint`: identical to "vocabulary=(...)"
 
-.. XXX Attribute, NOW
+Constraints can be dependent on a fixed value (90, Date(2015,3,23)) or a variable.
+In this second case, yams can handle :
+
+* `Attribute`: compare to the value of another attribute.
+* `TODAY`: compare to the current Date.
+* `NOW`: compare to the current Datetime.
 
 RQL Based Constraints
 ......................
--- a/entities/adapters.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/entities/adapters.py	Wed Apr 22 10:08:14 2015 +0200
@@ -20,6 +20,7 @@
 """
 
 __docformat__ = "restructuredtext en"
+_ = unicode
 
 from itertools import chain
 from warnings import warn
@@ -361,11 +362,13 @@
     __select__ = match_exception(UniqueTogetherError)
 
     def raise_user_exception(self):
-        _ = self._cw._
         rtypes = self.exc.rtypes
-        rtypes_msg = {}
+        errors = {}
+        msgargs = {}
+        i18nvalues = []
         for rtype in rtypes:
-            rtypes_msg[rtype] = _('%s is part of violated unicity constraint') % rtype
-        globalmsg = _('some relations violate a unicity constraint')
-        rtypes_msg['unicity constraint'] = globalmsg
-        raise ValidationError(self.entity.eid, rtypes_msg)
+            errors[rtype] = _('%(KEY-rtype)s is part of violated unicity constraint')
+            msgargs[rtype + '-rtype'] = rtype
+            i18nvalues.append(rtype + '-rtype')
+        errors[''] = _('some relations violate a unicity constraint')
+        raise ValidationError(self.entity.eid, errors, msgargs=msgargs, i18nvalues=i18nvalues)
--- a/entities/test/unittest_wfobjs.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/entities/test/unittest_wfobjs.py	Wed Apr 22 10:08:14 2015 +0200
@@ -52,9 +52,9 @@
             shell.commit()
             with self.assertRaises(ValidationError) as cm:
                 wf.add_state(u'foo')
-            self.assertEqual({'name': u'name is part of violated unicity constraint',
-                              'state_of': u'state_of is part of violated unicity constraint',
-                              'unicity constraint': u'some relations violate a unicity constraint'},
+            self.assertEqual({'name': u'%(KEY-rtype)s is part of violated unicity constraint',
+                              'state_of': u'%(KEY-rtype)s is part of violated unicity constraint',
+                              '': u'some relations violate a unicity constraint'},
                              cm.exception.errors)
             shell.rollback()
             # no pb if not in the same workflow
@@ -67,9 +67,9 @@
             with self.assertRaises(ValidationError) as cm:
                 bar.cw_set(name=u'foo')
             shell.rollback()
-            self.assertEqual({'name': u'name is part of violated unicity constraint',
-                              'state_of': u'state_of is part of violated unicity constraint',
-                              'unicity constraint': u'some relations violate a unicity constraint'},
+            self.assertEqual({'name': u'%(KEY-rtype)s is part of violated unicity constraint',
+                              'state_of': u'%(KEY-rtype)s is part of violated unicity constraint',
+                              '': u'some relations violate a unicity constraint'},
                              cm.exception.errors)
 
     def test_duplicated_transition(self):
@@ -80,9 +80,9 @@
             wf.add_transition(u'baz', (foo,), bar, ('managers',))
             with self.assertRaises(ValidationError) as cm:
                 wf.add_transition(u'baz', (bar,), foo)
-            self.assertEqual({'name': u'name is part of violated unicity constraint',
-                              'transition_of': u'transition_of is part of violated unicity constraint',
-                              'unicity constraint': u'some relations violate a unicity constraint'},
+            self.assertEqual({'name': u'%(KEY-rtype)s is part of violated unicity constraint',
+                              'transition_of': u'%(KEY-rtype)s is part of violated unicity constraint',
+                              '': u'some relations violate a unicity constraint'},
                              cm.exception.errors)
             shell.rollback()
             # no pb if not in the same workflow
@@ -97,9 +97,9 @@
             with self.assertRaises(ValidationError) as cm:
                 biz.cw_set(name=u'baz')
             shell.rollback()
-            self.assertEqual({'name': u'name is part of violated unicity constraint',
-                              'transition_of': u'transition_of is part of violated unicity constraint',
-                              'unicity constraint': u'some relations violate a unicity constraint'},
+            self.assertEqual({'name': u'%(KEY-rtype)s is part of violated unicity constraint',
+                              'transition_of': u'%(KEY-rtype)s is part of violated unicity constraint',
+                              '': u'some relations violate a unicity constraint'},
                              cm.exception.errors)
 
 
--- a/etwist/twconfig.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/etwist/twconfig.py	Wed Apr 22 10:08:14 2015 +0200
@@ -93,7 +93,7 @@
 
     def default_base_url(self):
         from socket import getfqdn
-        return 'http://%s:%s/' % (self['host'] or getfqdn(), self['port'] or 8080)
+        return 'http://%s:%s/' % (self['host'] or getfqdn().lower(), self['port'] or 8080)
 
 
 try:
--- a/i18n/de.po	Wed Mar 25 10:10:24 2015 +0100
+++ b/i18n/de.po	Wed Apr 22 10:08:14 2015 +0200
@@ -54,6 +54,10 @@
 msgstr ""
 
 #, python-format
+msgid "%(KEY-rtype)s is part of violated unicity constraint"
+msgstr ""
+
+#, python-format
 msgid "%(KEY-value)r doesn't match the %(KEY-regexp)r regular expression"
 msgstr ""
 
@@ -114,10 +118,6 @@
 msgstr "%s Fehlerbericht"
 
 #, python-format
-msgid "%s is part of violated unicity constraint"
-msgstr ""
-
-#, python-format
 msgid "%s software version of the database"
 msgstr "Software-Version der Datenbank %s"
 
@@ -1120,6 +1120,30 @@
 msgid "add WorkflowTransition transition_of Workflow object"
 msgstr "Workflow-Übergang"
 
+msgid "add a BaseTransition"
+msgstr ""
+
+msgid "add a Bookmark"
+msgstr ""
+
+msgid "add a CWAttribute"
+msgstr ""
+
+msgid "add a CWCache"
+msgstr ""
+
+msgid "add a CWConstraint"
+msgstr ""
+
+msgid "add a CWConstraintType"
+msgstr ""
+
+msgid "add a CWDataImport"
+msgstr ""
+
+msgid "add a CWEType"
+msgstr ""
+
 msgctxt "inlined:CWRelation.from_entity.subject"
 msgid "add a CWEType"
 msgstr "einen Entitätstyp hinzufügen"
@@ -1128,14 +1152,68 @@
 msgid "add a CWEType"
 msgstr "einen Entitätstyp hinzufügen"
 
+msgid "add a CWGroup"
+msgstr ""
+
+msgid "add a CWProperty"
+msgstr ""
+
+msgid "add a CWRType"
+msgstr ""
+
 msgctxt "inlined:CWRelation.relation_type.subject"
 msgid "add a CWRType"
 msgstr "einen Relationstyp hinzufügen"
 
+msgid "add a CWRelation"
+msgstr ""
+
+msgid "add a CWSource"
+msgstr ""
+
+msgid "add a CWSourceHostConfig"
+msgstr ""
+
+msgid "add a CWSourceSchemaConfig"
+msgstr ""
+
+msgid "add a CWUniqueTogetherConstraint"
+msgstr ""
+
+msgid "add a CWUser"
+msgstr ""
+
+msgid "add a EmailAddress"
+msgstr ""
+
 msgctxt "inlined:CWUser.use_email.subject"
 msgid "add a EmailAddress"
 msgstr "Email-Adresse hinzufügen"
 
+msgid "add a ExternalUri"
+msgstr ""
+
+msgid "add a RQLExpression"
+msgstr ""
+
+msgid "add a State"
+msgstr ""
+
+msgid "add a SubWorkflowExitPoint"
+msgstr ""
+
+msgid "add a TrInfo"
+msgstr ""
+
+msgid "add a Transition"
+msgstr ""
+
+msgid "add a Workflow"
+msgstr ""
+
+msgid "add a WorkflowTransition"
+msgstr ""
+
 # subject and object forms for each relation type
 # (no object form for final relation types)
 msgid "add_permission"
--- a/i18n/en.po	Wed Mar 25 10:10:24 2015 +0100
+++ b/i18n/en.po	Wed Apr 22 10:08:14 2015 +0200
@@ -46,6 +46,10 @@
 msgstr ""
 
 #, python-format
+msgid "%(KEY-rtype)s is part of violated unicity constraint"
+msgstr ""
+
+#, python-format
 msgid "%(KEY-value)r doesn't match the %(KEY-regexp)r regular expression"
 msgstr ""
 
@@ -106,10 +110,6 @@
 msgstr ""
 
 #, python-format
-msgid "%s is part of violated unicity constraint"
-msgstr ""
-
-#, python-format
 msgid "%s software version of the database"
 msgstr ""
 
@@ -1082,6 +1082,30 @@
 msgid "add WorkflowTransition transition_of Workflow object"
 msgstr "workflow-transition"
 
+msgid "add a BaseTransition"
+msgstr ""
+
+msgid "add a Bookmark"
+msgstr ""
+
+msgid "add a CWAttribute"
+msgstr ""
+
+msgid "add a CWCache"
+msgstr ""
+
+msgid "add a CWConstraint"
+msgstr ""
+
+msgid "add a CWConstraintType"
+msgstr ""
+
+msgid "add a CWDataImport"
+msgstr ""
+
+msgid "add a CWEType"
+msgstr ""
+
 msgctxt "inlined:CWRelation.from_entity.subject"
 msgid "add a CWEType"
 msgstr "add an entity type"
@@ -1090,14 +1114,68 @@
 msgid "add a CWEType"
 msgstr "add an entity type"
 
+msgid "add a CWGroup"
+msgstr ""
+
+msgid "add a CWProperty"
+msgstr ""
+
+msgid "add a CWRType"
+msgstr ""
+
 msgctxt "inlined:CWRelation.relation_type.subject"
 msgid "add a CWRType"
 msgstr "add a relation type"
 
+msgid "add a CWRelation"
+msgstr ""
+
+msgid "add a CWSource"
+msgstr ""
+
+msgid "add a CWSourceHostConfig"
+msgstr ""
+
+msgid "add a CWSourceSchemaConfig"
+msgstr ""
+
+msgid "add a CWUniqueTogetherConstraint"
+msgstr ""
+
+msgid "add a CWUser"
+msgstr ""
+
+msgid "add a EmailAddress"
+msgstr ""
+
 msgctxt "inlined:CWUser.use_email.subject"
 msgid "add a EmailAddress"
 msgstr "add an email address"
 
+msgid "add a ExternalUri"
+msgstr ""
+
+msgid "add a RQLExpression"
+msgstr ""
+
+msgid "add a State"
+msgstr ""
+
+msgid "add a SubWorkflowExitPoint"
+msgstr ""
+
+msgid "add a TrInfo"
+msgstr ""
+
+msgid "add a Transition"
+msgstr ""
+
+msgid "add a Workflow"
+msgstr ""
+
+msgid "add a WorkflowTransition"
+msgstr ""
+
 # subject and object forms for each relation type
 # (no object form for final relation types)
 msgid "add_permission"
--- a/i18n/es.po	Wed Mar 25 10:10:24 2015 +0100
+++ b/i18n/es.po	Wed Apr 22 10:08:14 2015 +0200
@@ -60,6 +60,10 @@
 msgstr "%(KEY-cstr)s restricción errónea para el valor %(KEY-value)r"
 
 #, python-format
+msgid "%(KEY-rtype)s is part of violated unicity constraint"
+msgstr "%(KEY-rtype)s pertenece a una restricción de unidad no respectada"
+
+#, python-format
 msgid "%(KEY-value)r doesn't match the %(KEY-regexp)r regular expression"
 msgstr "%(KEY-value)r no corresponde a la expresión regular %(KEY-regexp)r"
 
@@ -120,10 +124,6 @@
 msgstr "%s reporte de errores"
 
 #, python-format
-msgid "%s is part of violated unicity constraint"
-msgstr "%s pertenece a una restricción de unidad no respectada"
-
-#, python-format
 msgid "%s software version of the database"
 msgstr "versión sistema de la base para %s"
 
@@ -1139,6 +1139,30 @@
 msgid "add WorkflowTransition transition_of Workflow object"
 msgstr "Transición Workflow"
 
+msgid "add a BaseTransition"
+msgstr ""
+
+msgid "add a Bookmark"
+msgstr ""
+
+msgid "add a CWAttribute"
+msgstr ""
+
+msgid "add a CWCache"
+msgstr ""
+
+msgid "add a CWConstraint"
+msgstr ""
+
+msgid "add a CWConstraintType"
+msgstr ""
+
+msgid "add a CWDataImport"
+msgstr ""
+
+msgid "add a CWEType"
+msgstr ""
+
 msgctxt "inlined:CWRelation.from_entity.subject"
 msgid "add a CWEType"
 msgstr "Agregar un tipo de entidad"
@@ -1147,14 +1171,68 @@
 msgid "add a CWEType"
 msgstr "Agregar un tipo de entidad"
 
+msgid "add a CWGroup"
+msgstr ""
+
+msgid "add a CWProperty"
+msgstr ""
+
+msgid "add a CWRType"
+msgstr ""
+
 msgctxt "inlined:CWRelation.relation_type.subject"
 msgid "add a CWRType"
 msgstr "Agregar un tipo de relación"
 
+msgid "add a CWRelation"
+msgstr ""
+
+msgid "add a CWSource"
+msgstr ""
+
+msgid "add a CWSourceHostConfig"
+msgstr ""
+
+msgid "add a CWSourceSchemaConfig"
+msgstr ""
+
+msgid "add a CWUniqueTogetherConstraint"
+msgstr ""
+
+msgid "add a CWUser"
+msgstr ""
+
+msgid "add a EmailAddress"
+msgstr ""
+
 msgctxt "inlined:CWUser.use_email.subject"
 msgid "add a EmailAddress"
 msgstr "Agregar correo electrónico"
 
+msgid "add a ExternalUri"
+msgstr ""
+
+msgid "add a RQLExpression"
+msgstr ""
+
+msgid "add a State"
+msgstr ""
+
+msgid "add a SubWorkflowExitPoint"
+msgstr ""
+
+msgid "add a TrInfo"
+msgstr ""
+
+msgid "add a Transition"
+msgstr ""
+
+msgid "add a Workflow"
+msgstr ""
+
+msgid "add a WorkflowTransition"
+msgstr ""
+
 # subject and object forms for each relation type
 # (no object form for final relation types)
 msgid "add_permission"
--- a/i18n/fr.po	Wed Mar 25 10:10:24 2015 +0100
+++ b/i18n/fr.po	Wed Apr 22 10:08:14 2015 +0200
@@ -54,6 +54,10 @@
 msgstr "la valeur %(KEY-value)r ne satisfait pas la contrainte %(KEY-cstr)s"
 
 #, python-format
+msgid "%(KEY-rtype)s is part of violated unicity constraint"
+msgstr "%(KEY-rtype)s appartient à une contrainte d'unicité transgressée"
+
+#, python-format
 msgid "%(KEY-value)r doesn't match the %(KEY-regexp)r regular expression"
 msgstr ""
 "%(KEY-value)r ne correspond pas à l'expression régulière %(KEY-regexp)r"
@@ -115,10 +119,6 @@
 msgstr "%s rapport d'erreur"
 
 #, python-format
-msgid "%s is part of violated unicity constraint"
-msgstr "%s appartient à une contrainte d'unicité transgressée"
-
-#, python-format
 msgid "%s software version of the database"
 msgstr "version logicielle de la base pour %s"
 
@@ -1135,6 +1135,30 @@
 msgid "add WorkflowTransition transition_of Workflow object"
 msgstr "transition workflow"
 
+msgid "add a BaseTransition"
+msgstr ""
+
+msgid "add a Bookmark"
+msgstr ""
+
+msgid "add a CWAttribute"
+msgstr ""
+
+msgid "add a CWCache"
+msgstr ""
+
+msgid "add a CWConstraint"
+msgstr ""
+
+msgid "add a CWConstraintType"
+msgstr ""
+
+msgid "add a CWDataImport"
+msgstr ""
+
+msgid "add a CWEType"
+msgstr ""
+
 msgctxt "inlined:CWRelation.from_entity.subject"
 msgid "add a CWEType"
 msgstr "ajouter un type d'entité sujet"
@@ -1143,14 +1167,68 @@
 msgid "add a CWEType"
 msgstr "ajouter un type d'entité objet"
 
+msgid "add a CWGroup"
+msgstr ""
+
+msgid "add a CWProperty"
+msgstr ""
+
+msgid "add a CWRType"
+msgstr ""
+
 msgctxt "inlined:CWRelation.relation_type.subject"
 msgid "add a CWRType"
 msgstr "ajouter un type de relation"
 
+msgid "add a CWRelation"
+msgstr ""
+
+msgid "add a CWSource"
+msgstr ""
+
+msgid "add a CWSourceHostConfig"
+msgstr ""
+
+msgid "add a CWSourceSchemaConfig"
+msgstr ""
+
+msgid "add a CWUniqueTogetherConstraint"
+msgstr ""
+
+msgid "add a CWUser"
+msgstr ""
+
+msgid "add a EmailAddress"
+msgstr ""
+
 msgctxt "inlined:CWUser.use_email.subject"
 msgid "add a EmailAddress"
 msgstr "ajouter une adresse électronique"
 
+msgid "add a ExternalUri"
+msgstr ""
+
+msgid "add a RQLExpression"
+msgstr ""
+
+msgid "add a State"
+msgstr ""
+
+msgid "add a SubWorkflowExitPoint"
+msgstr ""
+
+msgid "add a TrInfo"
+msgstr ""
+
+msgid "add a Transition"
+msgstr ""
+
+msgid "add a Workflow"
+msgstr ""
+
+msgid "add a WorkflowTransition"
+msgstr ""
+
 # subject and object forms for each relation type
 # (no object form for final relation types)
 msgid "add_permission"
--- a/misc/migration/3.14.4_Any.py	Wed Mar 25 10:10:24 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-from cubicweb.server import schema2sql as y2sql
-
-dbhelper = repo.system_source.dbhelper
-rdefdef = schema['CWSource'].rdef('name')
-attrtype = y2sql.type_from_constraints(dbhelper, rdefdef.object, rdefdef.constraints).split()[0]
-
-cursor = session.cnxset.cu
-sql('UPDATE entities SET asource = source WHERE asource is NULL')
-dbhelper.change_col_type(cursor, 'entities', 'asource', attrtype, False)
-dbhelper.change_col_type(cursor, 'entities', 'source', attrtype, False)
--- a/misc/migration/3.18.0_Any.py	Wed Mar 25 10:10:24 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,151 +0,0 @@
-driver = config.system_source_config['db-driver']
-if not (driver == 'postgres' or driver.startswith('sqlserver')):
-    import sys
-    print >>sys.stderr, 'This migration is not supported for backends other than sqlserver or postgres (yet).'
-    sys.exit(1)
-
-add_relation_definition('CWAttribute', 'add_permission', 'CWGroup')
-add_relation_definition('CWAttribute', 'add_permission', 'RQLExpression')
-
-# a bad defaultval in 3.13.8 schema was fixed in 3.13.9, but the migration was missed
-rql('SET ATTR defaultval NULL WHERE ATTR from_entity E, E name "CWSource", ATTR relation_type T, T name "in_synchronization"')
-
-# the migration gets confused when we change rdefs out from under it.  So
-# explicitly remove this size constraint so it doesn't stick around and break
-# things later.
-rdefeid = schema['defaultval'].rdefs.values()[0].eid
-rql('DELETE CWConstraint C WHERE C cstrtype T, T name "SizeConstraint", R constrained_by C, R eid %(eid)s', {'eid': rdefeid})
-
-sync_schema_props_perms('defaultval')
-
-def convert_defaultval(cwattr, default):
-    from decimal import Decimal
-    import yams
-    from cubicweb import Binary
-    if default is None:
-        return
-    if isinstance(default, Binary):
-        # partially migrated instance, try to be idempotent
-        return default
-    atype = cwattr.to_entity[0].name
-    if atype == 'Boolean':
-        # boolean attributes with default=False were stored as ''
-        assert default in ('True', 'False', ''), repr(default)
-        default = default == 'True'
-    elif atype in ('Int', 'BigInt'):
-        default = int(default)
-    elif atype == 'Float':
-        default = float(default)
-    elif atype == 'Decimal':
-        default = Decimal(default)
-    elif atype in ('Date', 'Datetime', 'TZDatetime', 'Time'):
-        try:
-            # handle NOW and TODAY, keep them stored as strings
-            yams.KEYWORD_MAP[atype][default.upper()]
-            default = default.upper()
-        except KeyError:
-            # otherwise get an actual date or datetime
-            default = yams.DATE_FACTORY_MAP[atype](default)
-    else:
-        assert atype == 'String', atype
-        default = unicode(default)
-    return Binary.zpickle(default)
-
-dbh = repo.system_source.dbhelper
-
-
-sql('ALTER TABLE cw_cwattribute ADD new_defaultval %s' % dbh.TYPE_MAPPING['Bytes'])
-
-for cwattr in rql('CWAttribute X').entities():
-    olddefault = cwattr.defaultval
-    if olddefault is not None:
-        req = "UPDATE cw_cwattribute SET new_defaultval = %(val)s WHERE cw_eid = %(eid)s"
-        args = {'val': dbh.binary_value(convert_defaultval(cwattr, olddefault).getvalue()), 'eid': cwattr.eid}
-        sql(req, args, ask_confirm=False)
-
-sql('ALTER TABLE cw_cwattribute DROP COLUMN cw_defaultval')
-if driver == 'postgres':
-    sql('ALTER TABLE cw_cwattribute RENAME COLUMN new_defaultval TO cw_defaultval')
-else: # sqlserver
-    sql("sp_rename 'cw_cwattribute.new_defaultval', 'cw_defaultval', 'COLUMN'")
-
-
-# Set object type to "Bytes" for CWAttribute's "defaultval" attribute
-rql('SET X to_entity B WHERE X is CWAttribute, X from_entity Y, Y name "CWAttribute", '
-    'X relation_type Z, Z name "defaultval", B name "Bytes", NOT X to_entity B')
-
-oldrdef = schema['CWAttribute'].rdef('defaultval')
-import yams.buildobjs as ybo
-newrdef = ybo.RelationDefinition('CWAttribute', 'defaultval', 'Bytes')
-newrdef.eid = oldrdef.eid
-schema.add_relation_def(newrdef)
-schema.del_relation_def('CWAttribute', 'defaultval', 'String')
-
-commit()
-
-sync_schema_props_perms('defaultval')
-
-for rschema in schema.relations():
-    if rschema.symmetric:
-        subjects = set(repr(e.type) for e in rschema.subjects())
-        objects = set(repr(e.type) for e in rschema.objects())
-        assert subjects == objects
-        martians = set(str(eid) for eid, in sql('SELECT eid_to FROM %s_relation, entities WHERE eid_to = eid AND type NOT IN (%s)' %
-                                           (rschema.type, ','.join(subjects))))
-        martians |= set(str(eid) for eid, in sql('SELECT eid_from FROM %s_relation, entities WHERE eid_from = eid AND type NOT IN (%s)' %
-                                            (rschema.type, ','.join(subjects))))
-        if martians:
-            martians = ','.join(martians)
-            print 'deleting broken relations %s for eids %s' % (rschema.type, martians)
-            sql('DELETE FROM %s_relation WHERE eid_from IN (%s) OR eid_to IN (%s)' % (rschema.type, martians, martians))
-        with session.deny_all_hooks_but():
-            rql('SET X %(r)s Y WHERE Y %(r)s X, NOT X %(r)s Y' % {'r': rschema.type})
-        commit()
-
-
-# multi columns unique constraints regeneration
-from cubicweb.server import schemaserial
-
-# syncschema hooks would try to remove indices but
-# 1) we already do that below
-# 2) the hook expects the CWUniqueTogetherConstraint.name attribute that hasn't
-#    yet been added
-with session.allow_all_hooks_but('syncschema'):
-    rql('DELETE CWUniqueTogetherConstraint C')
-commit()
-
-add_attribute('CWUniqueTogetherConstraint', 'name')
-
-# low-level wipe code for postgres & sqlserver, plain sql ...
-if driver == 'postgres':
-    for indexname, in sql('select indexname from pg_indexes'):
-        if indexname.startswith('unique_'):
-            print 'dropping index', indexname
-            sql('DROP INDEX %s' % indexname)
-    commit()
-elif driver.startswith('sqlserver'):
-    for viewname, in sql('select name from sys.views'):
-        if viewname.startswith('utv_'):
-            print 'dropping view (index should be cascade-deleted)', viewname
-            sql('DROP VIEW %s' % viewname)
-    commit()
-
-# recreate the constraints, hook will lead to low-level recreation
-for eschema in sorted(schema.entities()):
-    if eschema._unique_together:
-        print 'recreate unique indexes for', eschema
-        rql_args = schemaserial.uniquetogether2rqls(eschema)
-        for rql, args in rql_args:
-            args['x'] = eschema.eid
-            session.execute(rql, args)
-commit()
-
-# all attributes perms have to be refreshed ...
-for rschema in sorted(schema.relations()):
-    if rschema.final:
-        if rschema.type in fsschema:
-            print 'sync perms for', rschema.type
-            sync_schema_props_perms(rschema.type, syncprops=False, ask_confirm=False, commit=False)
-        else:
-            print 'WARNING: attribute %s missing from fs schema' % rschema.type
-commit()
--- a/misc/migration/bootstrapmigration_repository.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/misc/migration/bootstrapmigration_repository.py	Wed Apr 22 10:08:14 2015 +0200
@@ -49,6 +49,27 @@
     cursor.execute(dbh.sql_restart_numrange('entities_id_seq', initial_value=lasteid))
     session.commit()
 
+if applcubicwebversion <= (3, 13, 0) and cubicwebversion >= (3, 13, 1):
+    sql('ALTER TABLE entities ADD asource VARCHAR(64)')
+    sql('UPDATE entities SET asource=cw_name  '
+        'FROM cw_CWSource, cw_source_relation '
+        'WHERE entities.eid=cw_source_relation.eid_from AND cw_source_relation.eid_to=cw_CWSource.cw_eid')
+    commit()
+
+if applcubicwebversion <= (3, 14, 4) and cubicwebversion >= (3, 14, 4):
+    from cubicweb.server import schema2sql as y2sql
+    dbhelper = repo.system_source.dbhelper
+    rdefdef = schema['CWSource'].rdef('name')
+    attrtype = y2sql.type_from_constraints(dbhelper, rdefdef.object, rdefdef.constraints).split()[0]
+    cursor = session.cnxset.cu
+    sql('UPDATE entities SET asource = source WHERE asource is NULL')
+    dbhelper.change_col_type(cursor, 'entities', 'asource', attrtype, False)
+    dbhelper.change_col_type(cursor, 'entities', 'source', attrtype, False)
+
+    # we now have a functional asource column, start using the normal eid_type_source method
+    if repo.system_source.eid_type_source == repo.system_source.eid_type_source_pre_131:
+        del repo.system_source.eid_type_source
+
 if applcubicwebversion < (3, 19, 0) and cubicwebversion >= (3, 19, 0):
     try: 
         # need explicit drop of the indexes on some database systems (sqlserver)
@@ -71,6 +92,163 @@
     add_entity_type('CWComputedRType')
     commit()
 
+if schema['TZDatetime'].eid is None:
+    add_entity_type('TZDatetime', auto=False)
+if schema['TZTime'].eid is None:
+    add_entity_type('TZTime', auto=False)
+
+if applcubicwebversion < (3, 18, 0) and cubicwebversion >= (3, 18, 0):
+    driver = config.system_source_config['db-driver']
+    if not (driver == 'postgres' or driver.startswith('sqlserver')):
+        import sys
+        print >>sys.stderr, 'This migration is not supported for backends other than sqlserver or postgres (yet).'
+        sys.exit(1)
+
+    add_relation_definition('CWAttribute', 'add_permission', 'CWGroup')
+    add_relation_definition('CWAttribute', 'add_permission', 'RQLExpression')
+
+    # a bad defaultval in 3.13.8 schema was fixed in 3.13.9, but the migration was missed
+    rql('SET ATTR defaultval NULL WHERE ATTR from_entity E, E name "CWSource", ATTR relation_type T, T name "in_synchronization"')
+
+    # the migration gets confused when we change rdefs out from under it.  So
+    # explicitly remove this size constraint so it doesn't stick around and break
+    # things later.
+    rdefeid = schema['defaultval'].rdefs.values()[0].eid
+    rql('DELETE CWConstraint C WHERE C cstrtype T, T name "SizeConstraint", R constrained_by C, R eid %(eid)s', {'eid': rdefeid})
+
+    sync_schema_props_perms('defaultval')
+
+    def convert_defaultval(cwattr, default):
+        from decimal import Decimal
+        import yams
+        from cubicweb import Binary
+        if default is None:
+            return
+        if isinstance(default, Binary):
+            # partially migrated instance, try to be idempotent
+            return default
+        atype = cwattr.to_entity[0].name
+        if atype == 'Boolean':
+            # boolean attributes with default=False were stored as ''
+            assert default in ('True', 'False', ''), repr(default)
+            default = default == 'True'
+        elif atype in ('Int', 'BigInt'):
+            default = int(default)
+        elif atype == 'Float':
+            default = float(default)
+        elif atype == 'Decimal':
+            default = Decimal(default)
+        elif atype in ('Date', 'Datetime', 'TZDatetime', 'Time'):
+            try:
+                # handle NOW and TODAY, keep them stored as strings
+                yams.KEYWORD_MAP[atype][default.upper()]
+                default = default.upper()
+            except KeyError:
+                # otherwise get an actual date or datetime
+                default = yams.DATE_FACTORY_MAP[atype](default)
+        else:
+            assert atype == 'String', atype
+            default = unicode(default)
+        return Binary.zpickle(default)
+
+    dbh = repo.system_source.dbhelper
+
+
+    sql('ALTER TABLE cw_cwattribute ADD new_defaultval %s' % dbh.TYPE_MAPPING['Bytes'])
+
+    for cwattr in rql('CWAttribute X').entities():
+        olddefault = cwattr.defaultval
+        if olddefault is not None:
+            req = "UPDATE cw_cwattribute SET new_defaultval = %(val)s WHERE cw_eid = %(eid)s"
+            args = {'val': dbh.binary_value(convert_defaultval(cwattr, olddefault).getvalue()), 'eid': cwattr.eid}
+            sql(req, args, ask_confirm=False)
+
+    sql('ALTER TABLE cw_cwattribute DROP COLUMN cw_defaultval')
+    if driver == 'postgres':
+        sql('ALTER TABLE cw_cwattribute RENAME COLUMN new_defaultval TO cw_defaultval')
+    else: # sqlserver
+        sql("sp_rename 'cw_cwattribute.new_defaultval', 'cw_defaultval', 'COLUMN'")
+
+
+    # Set object type to "Bytes" for CWAttribute's "defaultval" attribute
+    rql('SET X to_entity B WHERE X is CWAttribute, X from_entity Y, Y name "CWAttribute", '
+        'X relation_type Z, Z name "defaultval", B name "Bytes", NOT X to_entity B')
+
+    oldrdef = schema['CWAttribute'].rdef('defaultval')
+    import yams.buildobjs as ybo
+    newrdef = ybo.RelationDefinition('CWAttribute', 'defaultval', 'Bytes')
+    newrdef.eid = oldrdef.eid
+    schema.add_relation_def(newrdef)
+    schema.del_relation_def('CWAttribute', 'defaultval', 'String')
+
+    commit()
+
+    sync_schema_props_perms('defaultval')
+
+    for rschema in schema.relations():
+        if rschema.symmetric:
+            subjects = set(repr(e.type) for e in rschema.subjects())
+            objects = set(repr(e.type) for e in rschema.objects())
+            assert subjects == objects
+            martians = set(str(eid) for eid, in sql('SELECT eid_to FROM %s_relation, entities WHERE eid_to = eid AND type NOT IN (%s)' %
+                                               (rschema.type, ','.join(subjects))))
+            martians |= set(str(eid) for eid, in sql('SELECT eid_from FROM %s_relation, entities WHERE eid_from = eid AND type NOT IN (%s)' %
+                                                (rschema.type, ','.join(subjects))))
+            if martians:
+                martians = ','.join(martians)
+                print 'deleting broken relations %s for eids %s' % (rschema.type, martians)
+                sql('DELETE FROM %s_relation WHERE eid_from IN (%s) OR eid_to IN (%s)' % (rschema.type, martians, martians))
+            with session.deny_all_hooks_but():
+                rql('SET X %(r)s Y WHERE Y %(r)s X, NOT X %(r)s Y' % {'r': rschema.type})
+            commit()
+
+
+    # multi columns unique constraints regeneration
+    from cubicweb.server import schemaserial
+
+    # syncschema hooks would try to remove indices but
+    # 1) we already do that below
+    # 2) the hook expects the CWUniqueTogetherConstraint.name attribute that hasn't
+    #    yet been added
+    with session.allow_all_hooks_but('syncschema'):
+        rql('DELETE CWUniqueTogetherConstraint C')
+    commit()
+    add_attribute('CWUniqueTogetherConstraint', 'name')
+
+    # low-level wipe code for postgres & sqlserver, plain sql ...
+    if driver == 'postgres':
+        for indexname, in sql('select indexname from pg_indexes'):
+            if indexname.startswith('unique_'):
+                print 'dropping index', indexname
+                sql('DROP INDEX %s' % indexname)
+        commit()
+    elif driver.startswith('sqlserver'):
+        for viewname, in sql('select name from sys.views'):
+            if viewname.startswith('utv_'):
+                print 'dropping view (index should be cascade-deleted)', viewname
+                sql('DROP VIEW %s' % viewname)
+        commit()
+
+    # recreate the constraints, hook will lead to low-level recreation
+    for eschema in sorted(schema.entities()):
+        if eschema._unique_together:
+            print 'recreate unique indexes for', eschema
+            rql_args = schemaserial.uniquetogether2rqls(eschema)
+            for rql, args in rql_args:
+                args['x'] = eschema.eid
+                session.execute(rql, args)
+    commit()
+
+    # all attributes perms have to be refreshed ...
+    for rschema in sorted(schema.relations()):
+        if rschema.final:
+            if rschema.type in fsschema:
+                print 'sync perms for', rschema.type
+                sync_schema_props_perms(rschema.type, syncprops=False, ask_confirm=False, commit=False)
+            else:
+                print 'WARNING: attribute %s missing from fs schema' % rschema.type
+    commit()
+
 if applcubicwebversion < (3, 17, 0) and cubicwebversion >= (3, 17, 0):
     try:
         add_cube('sioc', update_database=False)
@@ -91,18 +269,6 @@
                        'cube, which is not installed.  Continue anyway?'):
             raise
 
-if applcubicwebversion <= (3, 13, 0) and cubicwebversion >= (3, 13, 1):
-    sql('ALTER TABLE entities ADD asource VARCHAR(64)')
-    sql('UPDATE entities SET asource=cw_name  '
-        'FROM cw_CWSource, cw_source_relation '
-        'WHERE entities.eid=cw_source_relation.eid_from AND cw_source_relation.eid_to=cw_CWSource.cw_eid')
-    commit()
-
-if schema['TZDatetime'].eid is None:
-    add_entity_type('TZDatetime', auto=False)
-if schema['TZTime'].eid is None:
-    add_entity_type('TZTime', auto=False)
-
 
 if applcubicwebversion <= (3, 14, 0) and cubicwebversion >= (3, 14, 0):
     if 'require_permission' in schema and not 'localperms'in repo.config.cubes():
@@ -113,14 +279,13 @@
             raise ExecutionError('In cubicweb 3.14, CWPermission and related stuff '
                                  'has been moved to cube localperms. Install it first.')
 
+
 if applcubicwebversion == (3, 6, 0) and cubicwebversion >= (3, 6, 0):
     CSTRMAP = dict(rql('Any T, X WHERE X is CWConstraintType, X name T',
                        ask_confirm=False))
     _add_relation_definition_no_perms('CWAttribute', 'update_permission', 'CWGroup')
     _add_relation_definition_no_perms('CWAttribute', 'update_permission', 'RQLExpression')
     rql('SET X update_permission Y WHERE X is CWAttribute, X add_permission Y')
-    drop_relation_definition('CWAttribute', 'add_permission', 'CWGroup')
-    drop_relation_definition('CWAttribute', 'add_permission', 'RQLExpression')
     drop_relation_definition('CWAttribute', 'delete_permission', 'CWGroup')
     drop_relation_definition('CWAttribute', 'delete_permission', 'RQLExpression')
 
--- a/schema.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/schema.py	Wed Apr 22 10:08:14 2015 +0200
@@ -891,7 +891,7 @@
         return False
 
     def has_perm(self, _cw, action, **kwargs):
-        """return true if the action is granted globaly or localy"""
+        """return true if the action is granted globally or locally"""
         if self.final:
             assert not ('fromeid' in kwargs or 'toeid' in kwargs), kwargs
             assert action in ('read', 'update')
--- a/server/migractions.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/server/migractions.py	Wed Apr 22 10:08:14 2015 +0200
@@ -524,6 +524,9 @@
                     subjtypes, objtypes = targettypes, [etype]
                 self._synchronize_rschema(rschema, syncrdefs=False,
                                           syncprops=syncprops, syncperms=syncperms)
+                if rschema.rule: # rdef for computed rtype are infered hence should not be
+                                 # synchronized
+                    continue
                 reporschema = self.repo.schema.rschema(rschema)
                 for subj in subjtypes:
                     for obj in objtypes:
--- a/server/repository.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/server/repository.py	Wed Apr 22 10:08:14 2015 +0200
@@ -1059,6 +1059,8 @@
         with session.security_enabled(read=False, write=False):
             in_eids = ','.join([str(_e.eid) for _e in entities])
             for rschema, _, role in entities[0].e_schema.relation_definitions():
+                if rschema.rule:
+                    continue # computed relation
                 rtype = rschema.type
                 if rtype in schema.VIRTUAL_RTYPES or rtype in pendingrtypes:
                     continue
--- a/server/serverconfig.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/server/serverconfig.py	Wed Apr 22 10:08:14 2015 +0200
@@ -199,7 +199,7 @@
           }),
          ('zmq-address-sub',
           {'type' : 'csv',
-           'default' : None,
+           'default' : (),
            'help': ('List of ZMQ addresses to subscribe to (requires pyzmq) '
                     '(of the form `tcp://<ipaddr>:<port>`)'),
            'group': 'zmq', 'level': 1,
--- a/server/session.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/server/session.py	Wed Apr 22 10:08:14 2015 +0200
@@ -995,8 +995,8 @@
         etype, extid, source = self.repo.type_and_source_from_eid(eid, self)
         metas = {'type': etype, 'source': source, 'extid': extid}
         if asdict:
-            metas['asource'] = meta['source'] # XXX pre 3.19 client compat
-            return meta
+            metas['asource'] = metas['source'] # XXX pre 3.19 client compat
+            return metas
         return etype, source, extid
 
     @_with_cnx_set
--- a/server/sources/native.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/server/sources/native.py	Wed Apr 22 10:08:14 2015 +0200
@@ -862,7 +862,7 @@
             res = list(res)
         if res[-1] is not None:
             res[-1] = b64decode(res[-1])
-        res.append(res[1])
+        res.append("system")
         return res
 
     def extid2eid(self, cnx, extid):
@@ -1570,14 +1570,15 @@
     system database in a database independent format. The file is a
     Zip archive containing the following files:
 
-    * format.txt: the format of the archive. Currently '1.0'
+    * format.txt: the format of the archive. Currently '1.1'
     * tables.txt: list of filenames in the archive tables/ directory
     * sequences.txt: list of filenames in the archive sequences/ directory
+    * numranges.txt: list of filenames in the archive numrange/ directory
     * versions.txt: the list of cube versions from CWProperty
     * tables/<tablename>.<chunkno>: pickled data
     * sequences/<sequencename>: pickled data
 
-    The pickled data format for tables and sequences is a tuple of 3 elements:
+    The pickled data format for tables, numranges and sequences is a tuple of 3 elements:
     * the table name
     * a tuple of column names
     * a list of rows (as tuples with one element per column)
@@ -1615,6 +1616,9 @@
             for seq in self.get_sequences():
                 self.logger.info('processing sequence %s', seq)
                 self.write_sequence(archive, seq)
+            for numrange in self.get_numranges():
+                self.logger.info('processing numrange %s', numrange)
+                self.write_numrange(archive, numrange)
             for table in self.get_tables():
                 self.logger.info('processing table %s', table)
                 self.write_table(archive, table)
@@ -1645,12 +1649,16 @@
         return non_entity_tables + etype_tables + relation_tables
 
     def get_sequences(self):
+        return []
+
+    def get_numranges(self):
         return ['entities_id_seq']
 
     def write_metadata(self, archive):
-        archive.writestr('format.txt', '1.0')
+        archive.writestr('format.txt', '1.1')
         archive.writestr('tables.txt', '\n'.join(self.get_tables()))
         archive.writestr('sequences.txt', '\n'.join(self.get_sequences()))
+        archive.writestr('numranges.txt', '\n'.join(self.get_numranges()))
         versions = self._get_versions()
         versions_str = '\n'.join('%s %s' % (k, v)
                                  for k, v in versions)
@@ -1663,6 +1671,13 @@
         serialized = self._serialize(seq, columns, rows)
         archive.writestr('sequences/%s' % seq, serialized)
 
+    def write_numrange(self, archive, numrange):
+        sql = self.dbhelper.sql_numrange_current_state(numrange)
+        columns, rows_iterator = self._get_cols_and_rows(sql)
+        rows = list(rows_iterator)
+        serialized = self._serialize(numrange, columns, rows)
+        archive.writestr('numrange/%s' % numrange, serialized)
+
     def write_table(self, archive, table):
         nb_lines_sql = 'SELECT COUNT(*) FROM %s' % table
         self.cursor.execute(nb_lines_sql)
@@ -1699,10 +1714,13 @@
         archive = zipfile.ZipFile(backupfile, 'r', allowZip64=True)
         self.cnx = self.get_connection()
         self.cursor = self.cnx.cursor()
-        sequences, tables, table_chunks = self.read_metadata(archive, backupfile)
+        sequences, numranges, tables, table_chunks = self.read_metadata(archive, backupfile)
         for seq in sequences:
             self.logger.info('restoring sequence %s', seq)
             self.read_sequence(archive, seq)
+        for numrange in numranges:
+            self.logger.info('restoring numrange %s', seq)
+            self.read_numrange(archive, numrange)
         for table in tables:
             self.logger.info('restoring table %s', table)
             self.read_table(archive, table, sorted(table_chunks[table]))
@@ -1713,11 +1731,12 @@
     def read_metadata(self, archive, backupfile):
         formatinfo = archive.read('format.txt')
         self.logger.info('checking metadata')
-        if formatinfo.strip() != "1.0":
+        if formatinfo.strip() != "1.1":
             self.logger.critical('Unsupported format in archive: %s', formatinfo)
             raise ValueError('Unknown format in %s: %s' % (backupfile, formatinfo))
         tables = archive.read('tables.txt').splitlines()
         sequences = archive.read('sequences.txt').splitlines()
+        numranges = archive.read('numranges.txt').splitlines()
         file_versions = self._parse_versions(archive.read('versions.txt'))
         versions = set(self._get_versions())
         if file_versions != versions:
@@ -1734,7 +1753,7 @@
             filename = basename(name)
             tablename, _ext = filename.rsplit('.', 1)
             table_chunks.setdefault(tablename, []).append(name)
-        return sequences, tables, table_chunks
+        return sequences, numranges, tables, table_chunks
 
     def read_sequence(self, archive, seq):
         seqname, columns, rows = loads(archive.read('sequences/%s' % seq))
@@ -1746,6 +1765,16 @@
         self.cursor.execute(sql)
         self.cnx.commit()
 
+    def read_numrange(self, archive, numrange):
+        rangename, columns, rows = loads(archive.read('numrange/%s' % numrange))
+        assert rangename == numrange
+        assert len(rows) == 1
+        assert len(rows[0]) == 1
+        value = rows[0][0]
+        sql = self.dbhelper.sql_restart_numrange(numrange, value)
+        self.cursor.execute(sql)
+        self.cnx.commit()
+
     def read_table(self, archive, table, filenames):
         merge_args = self._source.merge_args
         self.cursor.execute('DELETE FROM %s' % table)
--- a/server/test/data/schema.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/server/test/data/schema.py	Wed Apr 22 10:08:14 2015 +0200
@@ -16,7 +16,7 @@
 # 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 yams.buildobjs import (EntityType, RelationType, RelationDefinition,
+from yams.buildobjs import (EntityType, RelationType, RelationDefinition, ComputedRelation,
                             SubjectRelation, RichString, String, Int, Float,
                             Boolean, Datetime, TZDatetime, Bytes)
 from yams.constraints import SizeConstraint
@@ -274,3 +274,7 @@
     object = 'CWUser'
     inlined = True
     cardinality = '?*'
+
+
+class user_login(ComputedRelation):
+    rule = 'O login_user S'
--- a/server/test/unittest_repository.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/server/test/unittest_repository.py	Wed Apr 22 10:08:14 2015 +0200
@@ -51,10 +51,10 @@
             with self.assertRaises(ValidationError) as wraperr:
                 cnx.execute('INSERT Societe S: S nom "Logilab", S type "SSLL", S cp "75013"')
             self.assertEqual(
-                {'cp': u'cp is part of violated unicity constraint',
-                 'nom': u'nom is part of violated unicity constraint',
-                 'type': u'type is part of violated unicity constraint',
-                 'unicity constraint': u'some relations violate a unicity constraint'},
+                {'cp': u'%(KEY-rtype)s is part of violated unicity constraint',
+                 'nom': u'%(KEY-rtype)s is part of violated unicity constraint',
+                 'type': u'%(KEY-rtype)s is part of violated unicity constraint',
+                 '': u'some relations violate a unicity constraint'},
                 wraperr.exception.args[1])
 
     def test_unique_together_schema(self):
@@ -393,6 +393,13 @@
             cnx.commit()
             self.assertEqual(len(c.reverse_fiche), 1)
 
+    def test_delete_computed_relation_nonregr(self):
+        with self.admin_access.repo_cnx() as cnx:
+            c = cnx.create_entity('Personne', nom=u'Adam', login_user=cnx.user.eid)
+            cnx.commit()
+            c.cw_delete()
+            cnx.commit()
+
     def test_cw_set_in_before_update(self):
         # local hook
         class DummyBeforeHook(Hook):
--- a/sobjects/notification.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/sobjects/notification.py	Wed Apr 22 10:08:14 2015 +0200
@@ -81,7 +81,11 @@
     # this is usually the method to call
     def render_and_send(self, **kwargs):
         """generate and send email messages for this view"""
-        self._cw.vreg.config.sendmails(self.render_emails(**kwargs))
+        # render_emails changes self._cw so cache it here so all mails are sent
+        # after we commit our transaction.
+        cnx = self._cw
+        for msg, recipients in self.render_emails(**kwargs):
+            SendMailOp(cnx, recipients=recipients, msg=msg)
 
     def cell_call(self, row, col=0, **kwargs):
         self.w(self._cw._(self.content) % self.context(**kwargs))
--- a/test/unittest_dataimport.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/test/unittest_dataimport.py	Wed Apr 22 10:08:14 2015 +0200
@@ -1,7 +1,10 @@
 # -*- coding: utf-8 -*-
+
 import datetime as DT
 from StringIO import StringIO
+
 from logilab.common.testlib import TestCase, unittest_main
+
 from cubicweb import dataimport
 from cubicweb.devtools.testlib import CubicWebTC
 
@@ -24,6 +27,7 @@
             self.assertEqual(1, len(users))
             self.assertEqual(group_eid, groups.one().eid)
 
+
 class CreateCopyFromBufferTC(TestCase):
 
     # test converters
@@ -134,5 +138,30 @@
                           [u'1', u'2', u'3', u'4', u'']])
 
 
+class MetaGeneratorTC(CubicWebTC):
+
+    def test_dont_generate_relation_to_internal_manager(self):
+        with self.admin_access.repo_cnx() as cnx:
+            metagen = dataimport.MetaGenerator(cnx)
+            self.assertIn('created_by', metagen.etype_rels)
+            self.assertIn('owned_by', metagen.etype_rels)
+        with self.repo.internal_cnx() as cnx:
+            metagen = dataimport.MetaGenerator(cnx)
+            self.assertNotIn('created_by', metagen.etype_rels)
+            self.assertNotIn('owned_by', metagen.etype_rels)
+
+    def test_dont_generate_specified_values(self):
+        with self.admin_access.repo_cnx() as cnx:
+            metagen = dataimport.MetaGenerator(cnx)
+            # hijack gen_modification_date to ensure we don't go through it
+            metagen.gen_modification_date = None
+            md = DT.datetime.now() - DT.timedelta(days=1)
+            entity, rels = metagen.base_etype_dicts('CWUser')
+            entity.cw_edited.update(dict(modification_date=md))
+            with cnx.ensure_cnx_set:
+                metagen.init_entity(entity)
+            self.assertEqual(entity.cw_edited['modification_date'], md)
+
+
 if __name__ == '__main__':
     unittest_main()
--- a/web/data/cubicweb.ajax.box.js	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/data/cubicweb.ajax.box.js	Wed Apr 22 10:08:14 2015 +0200
@@ -17,7 +17,7 @@
     d.addCallback(function() {
             $('#' + holderid).empty();
             var formparams = ajaxFuncArgs('render', null, 'ctxcomponents', boxid, eid);
-            $('#' + cw.utils.domid(boxid) + eid).loadxhtml(AJAX_BASE_URL, formparams);
+            $('#' + cw.utils.domid(boxid) + eid).loadxhtml(AJAX_BASE_URL, formparams, null, 'swap');
             if (msg) {
                 document.location.hash = '#header';
                 updateMessage(msg);
@@ -29,7 +29,7 @@
     var d = loadRemote(AJAX_BASE_URL, ajaxFuncArgs(delfname, null, eid, relatedeid));
     d.addCallback(function() {
             var formparams = ajaxFuncArgs('render', null, 'ctxcomponents', boxid, eid);
-            $('#' + cw.utils.domid(boxid) + eid).loadxhtml(AJAX_BASE_URL, formparams);
+            $('#' + cw.utils.domid(boxid) + eid).loadxhtml(AJAX_BASE_URL, formparams, null, 'swap');
             if (msg) {
                 document.location.hash = '#header';
                 updateMessage(msg);
--- a/web/data/cubicweb.ajax.js	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/data/cubicweb.ajax.js	Wed Apr 22 10:08:14 2015 +0200
@@ -511,7 +511,8 @@
     d.addCallback(function(boxcontent) {
         $('#bookmarks_box').loadxhtml(AJAX_BASE_URL,
                                       ajaxFuncArgs('render', null, 'ctxcomponents',
-                                                   'bookmarks_box'));
+                                                   'bookmarks_box'),
+                                      null, 'swap');
         document.location.hash = '#header';
         updateMessage(_("bookmark has been removed"));
     });
--- a/web/data/cubicweb.edition.js	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/data/cubicweb.edition.js	Wed Apr 22 10:08:14 2015 +0200
@@ -21,14 +21,14 @@
  */
 
 function setPropValueWidget(varname, tabindex) {
-    var key = firstSelected(document.getElementById('pkey:' + varname));
+    var key = firstSelected(document.getElementById('pkey-subject:' + varname));
     if (key) {
         var args = {
             fname: 'prop_widget',
             pageid: pageid,
-            arg: $.map([key, varname, tabindex], JSON.stringify)
+            arg: $.map([key.value, varname, tabindex], JSON.stringify)
         };
-        cw.jqNode('div:value:' + varname).loadxhtml(AJAX_BASE_URL, args, 'post');
+        cw.jqNode('div:value-subject:' + varname).loadxhtml(AJAX_BASE_URL, args, 'post');
     }
 }
 
--- a/web/data/cubicweb.facets.js	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/data/cubicweb.facets.js	Wed Apr 22 10:08:14 2015 +0200
@@ -116,7 +116,7 @@
                 $node.loadxhtml(AJAX_BASE_URL, ajaxFuncArgs('render', {
                     'rql': rql
                 },
-                'ctxcomponents', 'breadcrumbs'));
+                'ctxcomponents', 'breadcrumbs'), null, 'swap');
             }
         }
         var mainvar = null;
--- a/web/data/cubicweb.reledit.js	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/data/cubicweb.reledit.js	Wed Apr 22 10:08:14 2015 +0200
@@ -53,7 +53,7 @@
                 return;
             }
         }
-        jQuery('#'+params.divid+'-reledit').loadxhtml(AJAX_BASE_URL, params, 'post');
+        jQuery('#'+params.divid+'-reledit').loadxhtml(AJAX_BASE_URL, params, 'post', 'swap');
         jQuery(cw).trigger('reledit-reloaded', params);
     },
 
@@ -69,7 +69,7 @@
                     pageid: pageid, action: action,
                     eid: eid, divid: divid, formid: formid,
                     reload: reload, vid: vid};
-        var d = jQuery('#'+divid+'-reledit').loadxhtml(AJAX_BASE_URL, args, 'post');
+        var d = jQuery('#'+divid+'-reledit').loadxhtml(AJAX_BASE_URL, args, 'post', 'swap');
         d.addCallback(function () {cw.reledit.showInlineEditionForm(divid);});
     }
 });
--- a/web/test/test_views.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/test/test_views.py	Wed Apr 22 10:08:14 2015 +0200
@@ -55,9 +55,11 @@
 
     def test_sortable_js_added(self):
         with self.admin_access.web_request() as req:
+            # sortable.js should not be included by default
             rset = req.execute('CWUser X')
-            # sortable.js should not be included by default
-            self.assertFalse('jquery.tablesorter.js' in self.view('oneline', rset, req=req))
+            self.assertNotIn('jquery.tablesorter.js', self.view('oneline', rset, req=req).source)
+
+        with self.admin_access.web_request() as req:
             # but should be included by the tableview
             rset = req.execute('Any P,F,S LIMIT 1 WHERE P is CWUser, P firstname F, P surname S')
             self.assertIn('jquery.tablesorter.js', self.view('table', rset, req=req).source)
--- a/web/test/unittest_views_editforms.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/test/unittest_views_editforms.py	Wed Apr 22 10:08:14 2015 +0200
@@ -148,6 +148,13 @@
             self.vreg['forms'].select('edition', req, entity=rset.get_entity(0, 0))
             self.assertFalse(any(f for f in form.fields if f is None))
 
+    def test_edition_form_with_action(self):
+        with self.admin_access.web_request() as req:
+            rset = req.execute('CWUser X LIMIT 1')
+            form = self.vreg['forms'].select('edition', req, rset=rset, row=0,
+                                             col=0, action='my_custom_action')
+            self.assertEqual(form.form_action(), 'my_custom_action')
+
     def test_attribute_add_permissions(self):
         # https://www.cubicweb.org/ticket/4342844
         with self.admin_access.repo_cnx() as cnx:
--- a/web/views/autoform.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/views/autoform.py	Wed Apr 22 10:08:14 2015 +0200
@@ -732,10 +732,7 @@
 
     @deprecated('[3.18] use form_action()')
     def get_action(self):
-        try:
-            return self._action
-        except AttributeError:
-            return self._cw.build_url(self._default_form_action_path)
+        return self._action
 
 
     @iclassmethod
--- a/web/views/cwproperties.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/views/cwproperties.py	Wed Apr 22 10:08:14 2015 +0200
@@ -302,7 +302,7 @@
         domid = field.dom_id(form)
         value = '<span class="value" id="%s">%s</span>' % (domid, self.value)
         if self.msg:
-            value + '<div class="helper">%s</div>' % self.msg
+            value += '<div class="helper">%s</div>' % self.msg
         return value
 
 
@@ -355,6 +355,7 @@
             msg = form._cw._('you should probably delete that property')
             self.widget = NotEditableWidget(entity.printable_value('value'),
                                             '%s (%s)' % (msg, ex))
+            return
         if entity.pkey.startswith('system.'):
             msg = form._cw._('value associated to this key is not editable '
                              'manually')
@@ -425,10 +426,10 @@
     """specific method for CWProperty handling"""
     entity = self._cw.vreg['etypes'].etype_class('CWProperty')(self._cw)
     entity.eid = varname
-    entity['pkey'] = propkey
+    entity.pkey = propkey
     form = self._cw.vreg['forms'].select('edition', self._cw, entity=entity)
     form.build_context()
-    vfield = form.field_by_name('value')
+    vfield = form.field_by_name('value', 'subject')
     renderer = formrenderers.FormRenderer(self._cw)
     return vfield.render(form, renderer, tabindex=tabindex) \
            + renderer.render_help(form, vfield)
--- a/web/views/rdf.py	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/views/rdf.py	Wed Apr 22 10:08:14 2015 +0200
@@ -94,7 +94,7 @@
                                 add( (cwuri, CW[rtype], URIRef(related.cwuri)) )
                                 try:
                                     for item in xy.xeq('%s %s' % (entity.e_schema.type, rtype)):
-                                        add( (cwuri, urijoin(item), URIRef(related.cwuri)) )
+                                        add( (cwuri, urijoin(item[1]), URIRef(related.cwuri)) )
                                 except xy.UnsupportedVocabulary:
                                     pass
                             else:
--- a/web/wdoc/about_en.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/about_en.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -22,4 +22,4 @@
 .. _dublincore: http://dublincore.org
 
 .. _Logilab: http://www.logilab.fr/
-.. _schema: schema
+.. _schema: ../schema
--- a/web/wdoc/about_fr.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/about_fr.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -25,4 +25,4 @@
 .. _dublincore: http://dublincore.org
 
 .. _Logilab: http://www.logilab.fr/
-.. _schéma: schema
+.. _schéma: ../schema
--- a/web/wdoc/add_content_en.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/add_content_en.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -2,7 +2,7 @@
 -----------------
 As manager, you can access to entity creation forms by using the `site management`_ page.
 
-.. _`site management`: manage
+.. _`site management`: ../manage
 
 As regular user, the index page or the action box may propose some links to create entities according to the context.
 
--- a/web/wdoc/add_content_fr.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/add_content_fr.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -2,7 +2,7 @@
 -------------------
 Pour un administrateur, la création des objets est toujours possible directement dans la `page de gestion de site`_.
 
-.. _`page de gestion de site`: manage
+.. _`page de gestion de site`: ../manage
 
 Pour les utilisateurs, la page principale ou la boîte d'action des entités vous permettra la création de nouveaux contenus.
 L'intérêt de la dernière méthode est de faciliter l'édition de la relation entre les objets.
--- a/web/wdoc/advanced_usage_schema_en.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/advanced_usage_schema_en.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -6,4 +6,4 @@
 site is not a content management system with items placed in folders. It is an
 interface to a database which applies a view to retreived data.
 
-.. _schema: schema
+.. _schema: ../schema
--- a/web/wdoc/advanced_usage_schema_fr.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/advanced_usage_schema_fr.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -5,7 +5,7 @@
 
 Commencez d'abord par jeter un œil au schéma_ de l'application, puis essayez de vous rappeler que vous naviguez dans un ensemble de données en appliquant des vues aux résultats de requête. Ce site n'est pas un système de gestion de contenu avec des objets dans des répertoires. C'est une interface vers une base de données qui vous permet d'appliquer une vue aux données récupérées.
 
-.. _schéma: schema
+.. _schéma: ../schema
 
 
 Relation entre les objets
--- a/web/wdoc/bookmarks_fr.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/bookmarks_fr.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -27,4 +27,4 @@
 ayez le droit de les modifier.
 
 
-.. _`préférences utilisateurs`: myprefs
+.. _`préférences utilisateurs`: ../myprefs
--- a/web/wdoc/custom_view_en.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/custom_view_en.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -6,10 +6,10 @@
 preferences`_). Once you're satified, add a `bookmark`_ if you want to reuse
 this view later.
 
-.. _`RQL tutorial`: doc/tut_rql
-.. _`schema`: schema
-.. _`user preferences`: myprefs
-.. _`bookmark`: doc/bookmarks
+.. _`RQL tutorial`: tut_rql
+.. _`schema`: ../schema
+.. _`user preferences`: ../myprefs
+.. _`bookmark`: bookmarks
 
 
 Below are some example of what can be acheived...
--- a/web/wdoc/custom_view_fr.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/custom_view_fr.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -9,10 +9,10 @@
 utilisateur`_). Une fois que vous êtes satisfait, ajoutez un `signet`_ si vous
 voulez réutiliser votre vue plus tard.
 
-.. _`tutoriel RQL`: doc/tut_rql
-.. _`schéma`: schema
-.. _`préférences utilisateur`: myprefs
-.. _`signet`: doc/bookmarks
+.. _`tutoriel RQL`: tut_rql
+.. _`schéma`: ../schema
+.. _`préférences utilisateur`: ../myprefs
+.. _`signet`: bookmarks
 
 
 Ci-dessous quelques exemples de ce que l'on peut faire...
--- a/web/wdoc/custom_view_last_update_en.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/custom_view_last_update_en.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -5,4 +5,4 @@
 
 Links below is providing useful RQL query example.
 
-.. _all latest changes: view?rql=Any+M%2CX+WHERE+X+modification_date+M+ORDERBY+M+DESC+LIMIT+30
+.. _all latest changes: ../view?rql=Any+M%2CX+WHERE+X+modification_date+M+ORDERBY+M+DESC+LIMIT+30
--- a/web/wdoc/custom_view_last_update_fr.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/custom_view_last_update_fr.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -6,5 +6,5 @@
 * la table des `derniers changements`_ fournit un exemple d'utilisation de RQL
   pour récupérer les derniers changements ayant eu lieu sur ce site.
 
-.. _`derniers changements`: view?rql=Any+M%2CX+WHERE+X+modification_date+M+ORDERBY+M+DESC+LIMIT+30
+.. _`derniers changements`: ../view?rql=Any+M%2CX+WHERE+X+modification_date+M+ORDERBY+M+DESC+LIMIT+30
 
--- a/web/wdoc/custom_view_rss_en.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/custom_view_rss_en.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -10,5 +10,5 @@
 an RSS reader into that and follow the site activity. For example :
 
 :raw-html:`<p><a class="reference"
-href="view?vid=rss&amp;rql=Any+X%2CM+WHERE+X+modification_date+M+ORDERBY+M+DESC+LIMIT+30"><img
-alt="rss" src="data/rss.png"> latest changes</a></p>`
+href="../view?vid=rss&amp;rql=Any+X%2CM+WHERE+X+modification_date+M+ORDERBY+M+DESC+LIMIT+30"><img
+alt="rss" src="../data/rss.png"> latest changes</a></p>`
--- a/web/wdoc/custom_view_rss_fr.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/custom_view_rss_fr.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -13,6 +13,6 @@
 derniers changements sous forme de flux RSS:
 
 :raw-html:`<p><a class="reference"
-href="view?vid=rss&amp;rql=Any+X%2CM+WHERE+X+modification_date+M+ORDERBY+M+DESC+LIMIT+30"><img
-alt="rss" src="data/rss.png"> latest changes</img></a></p>`
+href="../view?vid=rss&amp;rql=Any+X%2CM+WHERE+X+modification_date+M+ORDERBY+M+DESC+LIMIT+30"><img
+alt="rss" src="../data/rss.png"> latest changes</img></a></p>`
 
--- a/web/wdoc/search_en.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/search_en.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -10,4 +10,4 @@
 * `rql` : RQL query
 * `text` : full text search
 
-.. _RQL: doc/tut_rql
+.. _RQL: tut_rql
--- a/web/wdoc/search_fr.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/search_fr.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -11,5 +11,5 @@
 * `rql` : requête RQL
 * `text` : recherche plein texte
 
-.. _RQL: doc/tut_rql
+.. _RQL: tut_rql
 
--- a/web/wdoc/tut_rql_en.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/tut_rql_en.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -20,7 +20,7 @@
 knowledge of the application'schema. You can always view it using the "schema"
 link in user's dropdown menu (on the top-right of the screen) or by clicking here_.
 
-.. _here: schema
+.. _here: ../schema
 
 
 Some bits of theory
--- a/web/wdoc/tut_rql_fr.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/tut_rql_fr.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -24,7 +24,7 @@
 boîte affichée en cliquant sur le lien de l'utilisateur connectée (en haut à droite).
 Vous pouvez également le voir en cliquant ici_.
 
-.. _ici: schema
+.. _ici: ../schema
 
 
 Un peu de théorie
--- a/web/wdoc/userprefs_en.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/userprefs_en.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -7,4 +7,4 @@
 preferences" link in this menu. This will show you a form to configure which
 boxes are displayed, in which order, etc...
 
-.. image:: doc/images/userprefs
+.. image:: images/userprefs
--- a/web/wdoc/userprefs_fr.rst	Wed Mar 25 10:10:24 2015 +0100
+++ b/web/wdoc/userprefs_fr.rst	Wed Apr 22 10:08:14 2015 +0200
@@ -6,7 +6,7 @@
 personnelles (y compris le mot de passe d'accès à l'application).
 
 Chaque utilisateur peut également personaliser l'apparence du site via le lien
-"préférences utilisateur"_. Ce formulaire permet notamment de configurer les
+`préférences utilisateur`_. Ce formulaire permet notamment de configurer les
 boîtes qui seront affichées, leur ordre, etc...
 
 L'administrateur possède quant à lui un menu "configuration du site" qui reprend l'ensemble des préférences utilisateurs mais les applique par défaut au site.
@@ -42,6 +42,6 @@
 
 Il est alors possible d'éditer et de relancer toute requête
 
-.. _"préférences utilisateur: myprefs
-.. _RQL: doc/tut_rql
-.. image:: doc/images/userprefs
+.. _`préférences utilisateur`: ../myprefs
+.. _RQL: tut_rql
+.. image:: images/userprefs