merge 3.19.6 into 3.20 branch
authorJulien Cristau <julien.cristau@logilab.fr>
Mon, 01 Dec 2014 11:13:10 +0100
changeset 10074 ab956b780d4e
parent 10068 1b0cb3c6c95e (current diff)
parent 10073 c53062c49d53 (diff)
child 10075 136b5f995f8e
merge 3.19.6 into 3.20 branch
__pkginfo__.py
cubicweb.spec
hooks/syncschema.py
migration.py
misc/migration/bootstrapmigration_repository.py
server/__init__.py
server/schemaserial.py
server/session.py
server/sources/native.py
web/data/cubicweb.ajax.js
web/data/cubicweb.css
web/data/cubicweb.facets.js
web/data/cubicweb.old.css
web/facet.py
web/test/unittest_viewselector.py
--- a/.hgtags	Wed Jul 30 22:26:18 2014 +0200
+++ b/.hgtags	Mon Dec 01 11:13:10 2014 +0100
@@ -276,8 +276,8 @@
 ee860c51f56bd65c4f6ea363462c02700d1dab5a cubicweb-debian-version-3.16.3-1
 ee860c51f56bd65c4f6ea363462c02700d1dab5a cubicweb-centos-version-3.16.3-1
 041804bc48e91e440a5b573ceb0df5bf22863b80 cubicweb-version-3.16.4
+041804bc48e91e440a5b573ceb0df5bf22863b80 cubicweb-centos-version-3.16.4-1
 041804bc48e91e440a5b573ceb0df5bf22863b80 cubicweb-debian-version-3.16.4-1
-041804bc48e91e440a5b573ceb0df5bf22863b80 cubicweb-centos-version-3.16.4-1
 810a05fba1a46ab893b6cadac109097a047f8355 cubicweb-version-3.16.5
 810a05fba1a46ab893b6cadac109097a047f8355 cubicweb-debiann-version-3.16.5-1
 810a05fba1a46ab893b6cadac109097a047f8355 cubicweb-centos-version-3.16.5-1
@@ -297,38 +297,38 @@
 32b4d5314fd90fe050c931886190f9a372686148 cubicweb-debian-version-3.17.3-1
 32b4d5314fd90fe050c931886190f9a372686148 cubicweb-centos-version-3.17.3-1
 c7ba8e5d2e45e3d1289c1403df40d7dcb5e62acb cubicweb-version-3.17.4
+c7ba8e5d2e45e3d1289c1403df40d7dcb5e62acb cubicweb-centos-version-3.17.4-1
 c7ba8e5d2e45e3d1289c1403df40d7dcb5e62acb cubicweb-debian-version-3.17.4-1
-c7ba8e5d2e45e3d1289c1403df40d7dcb5e62acb cubicweb-centos-version-3.17.4-1
+15dd5b37998b8ef5e8fab1ea0491e6bd8e9f3355 cubicweb-version-3.17.5
 15dd5b37998b8ef5e8fab1ea0491e6bd8e9f3355 cubicweb-centos-version-3.17.5-1
-15dd5b37998b8ef5e8fab1ea0491e6bd8e9f3355 cubicweb-version-3.17.5
 15dd5b37998b8ef5e8fab1ea0491e6bd8e9f3355 cubicweb-debian-version-3.17.5-1
+5b9fedf67a2912a80fe315a477df9e3ab104c734 cubicweb-version-3.17.6
 5b9fedf67a2912a80fe315a477df9e3ab104c734 cubicweb-centos-version-3.17.6-1
-5b9fedf67a2912a80fe315a477df9e3ab104c734 cubicweb-version-3.17.6
 5b9fedf67a2912a80fe315a477df9e3ab104c734 cubicweb-debian-version-3.17.6-1
+483181543899a762d068cfdc3ae751b54adc3f14 cubicweb-version-3.17.7
 483181543899a762d068cfdc3ae751b54adc3f14 cubicweb-centos-version-3.17.7-1
-483181543899a762d068cfdc3ae751b54adc3f14 cubicweb-version-3.17.7
 483181543899a762d068cfdc3ae751b54adc3f14 cubicweb-debian-version-3.17.7-1
-909eb8b584c437b3d2580beff1325c3d5b5dcfb5 cubicweb-centos-version-3.17.8-1
 909eb8b584c437b3d2580beff1325c3d5b5dcfb5 cubicweb-version-3.17.8
 909eb8b584c437b3d2580beff1325c3d5b5dcfb5 cubicweb-debian-version-3.17.8-1
+909eb8b584c437b3d2580beff1325c3d5b5dcfb5 cubicweb-centos-version-3.17.8-1
 5668d210e49c910180ff27712b6ae9ce8286e06c cubicweb-version-3.17.9
 5668d210e49c910180ff27712b6ae9ce8286e06c cubicweb-debian-version-3.17.9-1
-fe0e1863a13772836f40f743cc6fe4865f288ed3 cubicweb-centos-version-3.17.10-1
 fe0e1863a13772836f40f743cc6fe4865f288ed3 cubicweb-version-3.17.10
 fe0e1863a13772836f40f743cc6fe4865f288ed3 cubicweb-debian-version-3.17.10-1
+fe0e1863a13772836f40f743cc6fe4865f288ed3 cubicweb-centos-version-3.17.10-1
+7f67db7c848ec20152daf489d9e11f0fc8402e9b cubicweb-version-3.17.11
 7f67db7c848ec20152daf489d9e11f0fc8402e9b cubicweb-centos-version-3.17.11-1
-7f67db7c848ec20152daf489d9e11f0fc8402e9b cubicweb-version-3.17.11
 7f67db7c848ec20152daf489d9e11f0fc8402e9b cubicweb-debian-version-3.17.11-1
 b02e2912cad5d80395e488c55b548495e8320198 cubicweb-debian-version-3.17.11-2
 838d58a30f7efc6a8f83ac27ae8de7d79b84b2bb cubicweb-version-3.17.12
+838d58a30f7efc6a8f83ac27ae8de7d79b84b2bb cubicweb-centos-version-3.17.12-1
 838d58a30f7efc6a8f83ac27ae8de7d79b84b2bb cubicweb-debian-version-3.17.12-1
-838d58a30f7efc6a8f83ac27ae8de7d79b84b2bb cubicweb-centos-version-3.17.12-1
 09b4ebb9b0f179009491410c07cd013a60258fc6 cubicweb-version-3.17.13
+09b4ebb9b0f179009491410c07cd013a60258fc6 cubicweb-centos-version-3.17.13-1
 09b4ebb9b0f179009491410c07cd013a60258fc6 cubicweb-debian-version-3.17.13-1
-09b4ebb9b0f179009491410c07cd013a60258fc6 cubicweb-centos-version-3.17.13-1
-fa00fc251d57f61e619d9c905502745fae21c58c cubicweb-centos-version-3.17.14-1
 fa00fc251d57f61e619d9c905502745fae21c58c cubicweb-version-3.17.14
 fa00fc251d57f61e619d9c905502745fae21c58c cubicweb-debian-version-3.17.14-1
+fa00fc251d57f61e619d9c905502745fae21c58c cubicweb-centos-version-3.17.14-1
 ee413076752b3e606801ef55e48f7e7ccd1f7238 cubicweb-version-3.17.15
 ee413076752b3e606801ef55e48f7e7ccd1f7238 cubicweb-debian-version-3.17.15-1
 ee413076752b3e606801ef55e48f7e7ccd1f7238 cubicweb-centos-version-3.17.15-1
@@ -338,15 +338,18 @@
 57e9d1c70512d0f4e2c33d33db436a8274e10c1a cubicweb-version-3.17.17
 57e9d1c70512d0f4e2c33d33db436a8274e10c1a cubicweb-debian-version-3.17.17-1
 57e9d1c70512d0f4e2c33d33db436a8274e10c1a cubicweb-centos-version-3.17.17-1
+cda4b066933f216abe185786f5458176894bdaf0 cubicweb-version-3.17.18
+cda4b066933f216abe185786f5458176894bdaf0 cubicweb-centos-version-3.17.18-1
+cda4b066933f216abe185786f5458176894bdaf0 cubicweb-debian-version-3.17.18-1
 db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-version-3.18.0
 db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-debian-version-3.18.0-1
 db37bf35a1474843ded0a537f9cb4838f4a78cda cubicweb-centos-version-3.18.0-1
 60322cb8636c0402cdac025d3297626c41583023 cubicweb-version-3.18.1
+60322cb8636c0402cdac025d3297626c41583023 cubicweb-centos-version-3.18.1-1
 60322cb8636c0402cdac025d3297626c41583023 cubicweb-debian-version-3.18.1-1
-60322cb8636c0402cdac025d3297626c41583023 cubicweb-centos-version-3.18.1-1
 6880674c1a2669e3635abd688755116dda72e65e cubicweb-version-3.18.2
+6880674c1a2669e3635abd688755116dda72e65e cubicweb-centos-version-3.18.2-1
 6880674c1a2669e3635abd688755116dda72e65e cubicweb-debian-version-3.18.2-1
-6880674c1a2669e3635abd688755116dda72e65e cubicweb-centos-version-3.18.2-1
 afd21fea201a745051357b7aa6be3c7da1ae5bd2 cubicweb-version-3.18.3
 afd21fea201a745051357b7aa6be3c7da1ae5bd2 cubicweb-debian-version-3.18.3-1
 afd21fea201a745051357b7aa6be3c7da1ae5bd2 cubicweb-centos-version-3.18.3-1
@@ -354,11 +357,14 @@
 0176da9bc75293e200de4f7b934c5d4c7c805199 cubicweb-debian-version-3.18.4-1
 0176da9bc75293e200de4f7b934c5d4c7c805199 cubicweb-centos-version-3.18.4-1
 5071b69b6b0b0de937bb231404cbf652a103dbe0 cubicweb-version-3.18.5
+5071b69b6b0b0de937bb231404cbf652a103dbe0 cubicweb-centos-version-3.18.5-1
 5071b69b6b0b0de937bb231404cbf652a103dbe0 cubicweb-debian-version-3.18.5-1
-5071b69b6b0b0de937bb231404cbf652a103dbe0 cubicweb-centos-version-3.18.5-1
 d915013567429b481cb2c367071e36451c07a226 cubicweb-version-3.18.6
+d915013567429b481cb2c367071e36451c07a226 cubicweb-centos-version-3.18.6-1
 d915013567429b481cb2c367071e36451c07a226 cubicweb-debian-version-3.18.6-1
-d915013567429b481cb2c367071e36451c07a226 cubicweb-centos-version-3.18.6-1
+cb96f4403cf2837b595992ceb0dfef2070d55e70 cubicweb-version-3.18.7
+cb96f4403cf2837b595992ceb0dfef2070d55e70 cubicweb-debian-version-3.18.7-1
+cb96f4403cf2837b595992ceb0dfef2070d55e70 cubicweb-centos-version-3.18.7-1
 1141927b8494aabd16e31b0d0d9a50fe1fed5f2f cubicweb-version-3.19.0
 1141927b8494aabd16e31b0d0d9a50fe1fed5f2f cubicweb-debian-version-3.19.0-1
 1141927b8494aabd16e31b0d0d9a50fe1fed5f2f cubicweb-centos-version-3.19.0-1
@@ -377,3 +383,6 @@
 3ac86df519af2a1194cb3fc882d30d0e1bf44e3b cubicweb-version-3.19.5
 3ac86df519af2a1194cb3fc882d30d0e1bf44e3b cubicweb-debian-version-3.19.5-1
 3ac86df519af2a1194cb3fc882d30d0e1bf44e3b cubicweb-centos-version-3.19.5-1
+934341b848a6874688314d7c154183aca3aed530 cubicweb-version-3.19.6
+934341b848a6874688314d7c154183aca3aed530 cubicweb-debian-version-3.19.6-1
+934341b848a6874688314d7c154183aca3aed530 cubicweb-centos-version-3.19.6-1
--- a/__pkginfo__.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/__pkginfo__.py	Mon Dec 01 11:13:10 2014 +0100
@@ -22,7 +22,7 @@
 
 modname = distname = "cubicweb"
 
-numversion = (3, 19, 5)
+numversion = (3, 19, 6)
 version = '.'.join(str(num) for num in numversion)
 
 description = "a repository of entities / relations for knowledge management"
--- a/cubicweb.spec	Wed Jul 30 22:26:18 2014 +0200
+++ b/cubicweb.spec	Mon Dec 01 11:13:10 2014 +0100
@@ -7,7 +7,7 @@
 %endif
 
 Name:           cubicweb
-Version:        3.19.5
+Version:        3.19.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/debian/changelog	Wed Jul 30 22:26:18 2014 +0200
+++ b/debian/changelog	Mon Dec 01 11:13:10 2014 +0100
@@ -1,3 +1,9 @@
+cubicweb (3.19.6-1) unstable; urgency=low
+
+  * new upstream release 
+
+ -- David Douard <david.douard@logilab.fr>  Wed, 26 Nov 2014 16:22:39 +0100
+
 cubicweb (3.19.5-1) unstable; urgency=low
 
   * new upstream release
@@ -34,6 +40,12 @@
 
  -- Julien Cristau <julien.cristau@logilab.fr>  Mon, 28 Apr 2014 18:35:27 +0200
 
+cubicweb (3.18.7-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Aurelien Campeas <aurelien.campeas@logilab.fr>  Tue, 25 Nov 2014 12:14:36 +0100
+
 cubicweb (3.18.6-1) unstable; urgency=low
 
   * new upstream release
@@ -76,6 +88,12 @@
 
  -- Julien Cristau <julien.cristau@logilab.fr>  Fri, 10 Jan 2014 17:14:18 +0100
 
+cubicweb (3.17.18-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Aurelien Campeas <aurelien.campeas@logilab.fr>  Mon, 24 Nov 2014 17:41:24 +0100
+
 cubicweb (3.17.17-1) unstable; urgency=low
 
   * new upstream release
--- a/devtools/__init__.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/devtools/__init__.py	Mon Dec 01 11:13:10 2014 +0100
@@ -292,6 +292,7 @@
 
 class TestDataBaseHandler(object):
     DRIVER = None
+
     db_cache = {}
     explored_glob = set()
 
@@ -535,6 +536,10 @@
 class PostgresTestDataBaseHandler(TestDataBaseHandler):
     DRIVER = 'postgres'
 
+    # Separate db_cache for PG databases, to avoid collisions with sqlite dbs
+    db_cache = {}
+    explored_glob = set()
+
     __CTL = set()
 
     @classmethod
@@ -560,8 +565,12 @@
         env = os.environ.copy()
         env['PGPORT'] = str(port)
         env['PGHOST'] = str(directory)
+        options = '-h "" -k %s -p %s' % (directory, port)
+        options += ' -c fsync=off -c full_page_writes=off'
+        options += ' -c synchronous_commit=off'
         try:
-            subprocess.check_call(['pg_ctl', 'start', '-w', '-D', datadir, '-o', '-h "" -k %s -p %s' % (directory, port)],
+            subprocess.check_call(['pg_ctl', 'start', '-w', '-D', datadir,
+                                   '-o', options],
                                   env=env)
         except OSError, err:
             if err.errno == errno.ENOENT:
@@ -670,12 +679,14 @@
             backup_name = self._backup_name(db_id)
             self._drop(backup_name)
             self.system_source['db-name'] = backup_name
-            # during postgres database initialization, there is no repo set here.
-            assert self._repo is None
-            #self._repo.turn_repo_off()
-            createdb(self.helper, self.system_source, self.dbcnx, self.cursor, template=orig_name)
-            self.dbcnx.commit()
-            #self._repo.turn_repo_on()
+            if self._repo:
+                self._repo.turn_repo_off()
+            try:
+                createdb(self.helper, self.system_source, self.dbcnx, self.cursor, template=orig_name)
+                self.dbcnx.commit()
+            finally:
+                if self._repo:
+                    self._repo.turn_repo_on()
             return backup_name
         finally:
             self.system_source['db-name'] = orig_name
--- a/devtools/test/unittest_httptest.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/devtools/test/unittest_httptest.py	Mon Dec 01 11:13:10 2014 +0100
@@ -41,7 +41,7 @@
 
 
 class TwistedCWIdentTC(CubicWebServerTC):
-
+    test_db_id = 'httptest-cwident'
     anonymous_allowed = False
     tags = CubicWebServerTC.tags | Tags(('auth',))
 
--- a/devtools/test/unittest_webtest.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/devtools/test/unittest_webtest.py	Mon Dec 01 11:13:10 2014 +0100
@@ -15,6 +15,7 @@
 
 
 class CWTIdentTC(CubicWebTestTC):
+    test_db_id = 'webtest-ident'
     anonymous_allowed = False
     tags = CubicWebTestTC.tags | Tags(('auth',))
 
--- a/hooks/syncschema.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/hooks/syncschema.py	Mon Dec 01 11:13:10 2014 +0100
@@ -476,7 +476,7 @@
             cnx.system_sql(str('ALTER TABLE %s ADD %s %s'
                                % (table, column, attrtype)),
                            rollback_on_failure=False)
-            self.info('added column %s to table %s', table, column)
+            self.info('added column %s to table %s', column, table)
         except Exception as ex:
             # the column probably already exists. this occurs when
             # the entity's type has just been added or if the column
--- a/migration.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/migration.py	Mon Dec 01 11:13:10 2014 +0100
@@ -525,7 +525,7 @@
                 self.errors.append( ('add', cube, version, source) )
             elif versions:
                 lower_strict = version_strictly_lower(self.cubes[cube], version)
-                if oper in ('>=','='):
+                if oper in ('>=','=','=='):
                     if lower_strict:
                         self.errors.append( ('update', cube, version, source) )
                 elif oper is None:
--- a/misc/migration/3.18.0_Any.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/misc/migration/3.18.0_Any.py	Mon Dec 01 11:13:10 2014 +0100
@@ -133,17 +133,19 @@
 # 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()
-
+commit()
 
 # all attributes perms have to be refreshed ...
-for rschema in schema.relations():
+for rschema in sorted(schema.relations()):
     if rschema.final:
         if rschema.type in fsschema:
-            sync_schema_props_perms(rschema.type, syncprops=False, ask_confirm=False)
+            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 Jul 30 22:26:18 2014 +0200
+++ b/misc/migration/bootstrapmigration_repository.py	Mon Dec 01 11:13:10 2014 +0100
@@ -50,8 +50,14 @@
     session.commit()
 
 if applcubicwebversion < (3, 19, 0) and cubicwebversion >= (3, 19, 0):
-    sql('ALTER TABLE "entities" DROP COLUMN "mtime"')
-    sql('ALTER TABLE "entities" DROP COLUMN "source"')
+    try: 
+        # need explicit drop of the indexes on some database systems (sqlserver)
+        sql(repo.system_source.dbhelper.sql_drop_index('entities', 'mtime'))
+        sql('ALTER TABLE "entities" DROP COLUMN "mtime"')
+        sql('ALTER TABLE "entities" DROP COLUMN "source"')
+    except: # programming error, already migrated
+        print "Failed to drop mtime or source database columns"
+        print "'entities' table of the database has probably been already updated"
 
     commit()
 
--- a/server/__init__.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/server/__init__.py	Mon Dec 01 11:13:10 2014 +0100
@@ -31,6 +31,8 @@
 from logilab.common.modutils import LazyObject
 from logilab.common.textutils import splitstrip
 from logilab.common.registry import yes
+from logilab import database
+
 from yams import BASE_GROUPS
 
 from cubicweb import CW_SOFTWARE_ROOT
--- a/server/schemaserial.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/server/schemaserial.py	Mon Dec 01 11:13:10 2014 +0100
@@ -454,7 +454,14 @@
 
 def uniquetogether2rqls(eschema):
     rql_args = []
+    # robustness against duplicated CWUniqueTogetherConstraint (pre 3.18)
+    columnset = set()
     for columns in eschema._unique_together:
+        if columns in columnset:
+            print ('schemaserial: skipping duplicate unique together %r %r' %
+                   (eschema.type, columns))
+            continue
+        columnset.add(columns)
         rql, args = _uniquetogether2rql(eschema, columns)
         args['name'] = y2sql.unique_index_name(eschema, columns)
         rql_args.append((rql, args))
--- a/server/session.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/server/session.py	Mon Dec 01 11:13:10 2014 +0100
@@ -1074,8 +1074,13 @@
         cstate = self.commit_state
         if cstate == 'uncommitable':
             raise QueryError('transaction must be rolled back')
-        if cstate is not None:
+        if cstate == 'precommit':
+            self.warn('calling commit in precommit makes no sense; ignoring commit')
             return
+        if cstate == 'postcommit':
+            self.critical('postcommit phase is not allowed to write to the db; ignoring commit')
+            return
+        assert cstate is None
         # on rollback, an operation should have the following state
         # information:
         # - processed by the precommit/commit event or not
--- a/server/sources/native.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/server/sources/native.py	Mon Dec 01 11:13:10 2014 +0100
@@ -1505,11 +1505,12 @@
             args['pwd'] = Binary(crypt_password(password, pwd.getvalue()))
         # get eid from login and (crypted) password
         rset = self.source.syntax_tree_search(cnx, self._auth_rqlst, args)
+        pwd = args['pwd']
         try:
             user = rset[0][0]
             # If the stored hash uses a deprecated scheme (e.g. DES or MD5 used
             # before 3.14.7), update with a fresh one
-            if pwd.getvalue():
+            if pwd is not None and pwd.getvalue():
                 verify, newhash = verify_and_update(password, pwd.getvalue())
                 if not verify: # should not happen, but...
                     raise AuthenticationError('bad password')
@@ -1564,6 +1565,8 @@
     Tables are saved in chunks in different files in order to prevent
     a too high memory consumption.
     """
+    blocksize = 100
+
     def __init__(self, source):
         """
         :param: source an instance of the system source
@@ -1647,10 +1650,7 @@
         sql = 'SELECT * FROM %s' % table
         columns, rows_iterator = self._get_cols_and_rows(sql)
         self.logger.info('number of rows: %d', rowcount)
-        if table.startswith('cw_'): # entities
-            blocksize = 2000
-        else: # relations and metadata
-            blocksize = 10000
+        blocksize = self.blocksize
         if rowcount > 0:
             for i, start in enumerate(xrange(0, rowcount, blocksize)):
                 rows = list(itertools.islice(rows_iterator, blocksize))
--- a/sobjects/services.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/sobjects/services.py	Mon Dec 01 11:13:10 2014 +0100
@@ -30,7 +30,7 @@
     """
 
     __regid__  = 'repo_stats'
-    __select__ = match_user_groups('managers')
+    __select__ = match_user_groups('managers', 'users')
 
     def call(self):
         repo = self._cw.repo # Service are repo side only.
--- a/web/data/cubicweb.ajax.js	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/data/cubicweb.ajax.js	Mon Dec 01 11:13:10 2014 +0100
@@ -431,13 +431,21 @@
 }
 
 //============= higher level AJAX functions using remote calls ===============//
+
+var _i18ncache = {};
+
 /**
  * .. function:: _(message)
  *
  * emulation of gettext's _ shortcut
  */
 function _(message) {
-    return loadRemote(AJAX_BASE_URL, ajaxFuncArgs('i18n', null, [message]), 'GET', true)[0];
+    var form;
+    if (!(message in _i18ncache)) {
+        form = ajaxFuncArgs('i18n', null, [message]);
+        _i18ncache[message] = loadRemote(AJAX_BASE_URL, form, 'GET', true)[0];
+    }
+    return _i18ncache[message];
 }
 
 /**
--- a/web/data/cubicweb.css	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/data/cubicweb.css	Mon Dec 01 11:13:10 2014 +0100
@@ -52,6 +52,11 @@
   background: %(pageBgColor)s;
 }
 
+/* more specific selectors to override jQueryUI's braindamaged CSS rules */
+#pageContent .ui-tabs-panel a,
+#pageContent .ui-tabs-panel a:active,
+#pageContent .ui-tabs-panel a:visited,
+#pageContent .ui-tabs-panel a:link,
 a, a:active, a:visited, a:link {
   color: %(aColor)s;
   text-decoration: none;
--- a/web/data/cubicweb.facets.js	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/data/cubicweb.facets.js	Mon Dec 01 11:13:10 2014 +0100
@@ -35,7 +35,7 @@
     });
     // pick up hidden inputs (required metadata inputs such as 'facets'
     // but also RangeWidgets)
-    $form.find('input:hidden').each(function() {
+    $form.find('input[type="hidden"]').each(function() {
         names.push(this.name);
         values.push(this.value);
     });
--- a/web/data/cubicweb.old.css	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/data/cubicweb.old.css	Mon Dec 01 11:13:10 2014 +0100
@@ -55,6 +55,11 @@
   font-size:105%;
 }
 
+/* more specific selectors to override jQueryUI's braindamaged CSS rules */
+#pageContent .ui-tabs-panel a,
+#pageContent .ui-tabs-panel a:active,
+#pageContent .ui-tabs-panel a:visited,
+#pageContent .ui-tabs-panel a:link,
 a, a:active, a:visited, a:link {
   color: %(aColor)s;
   text-decoration: none;
--- a/web/data/cubicweb.preferences.js	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/data/cubicweb.preferences.js	Mon Dec 01 11:13:10 2014 +0100
@@ -25,7 +25,7 @@
 function _toggleFieldset(fieldsetid, closeaction, linklabel, linkhref) {
     jQuery('#' + fieldsetid).find('div.openlink').each(function() {
         var link = A({
-            'href': "javascript:noop();",
+            'href': "javascript:$.noop();",
             'onclick': linkhref
         },
         linklabel);
--- a/web/data/cubicweb.widgets.js	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/data/cubicweb.widgets.js	Mon Dec 01 11:13:10 2014 +0100
@@ -355,7 +355,7 @@
 Widgets.SuggestForm = defclass("SuggestForm", null, {
 
     __init__: function(inputid, initfunc, varargs, validatefunc, options) {
-        this.validatefunc = validatefunc || noop;
+        this.validatefunc = validatefunc || $.noop;
         this.sgfield = new Widgets.BaseSuggestField(inputid, initfunc, varargs, options);
         this.oklabel = options.oklabel || 'ok';
         this.cancellabel = options.cancellabel || 'cancel';
@@ -369,11 +369,11 @@
             'class': "sgformbuttons"
         },
         [A({
-            'href': "javascript: noop();",
+            'href': "javascript: $.noop();",
             'onclick': this.onValidateClicked
         },
         this.oklabel), ' / ', A({
-            'href': "javascript: noop();",
+            'href': "javascript: $.noop();",
             'onclick': this.destroy
         },
         escapeHTML(this.cancellabel))]);
--- a/web/facet.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/facet.py	Mon Dec 01 11:13:10 2014 +0100
@@ -1620,7 +1620,10 @@
             cssclass += ' hideFacetBody'
         w(u'<div class="%s" cubicweb:facetName="%s">%s</div>\n' %
                (cssclass, xml_escape(self.facet.__regid__), title))
-        w(u'<div class="facetBody">\n')
+        cssclass = 'facetBody'
+        if not self.facet.start_unfolded:
+            cssclass += ' hidden'
+        w(u'<div class="%s">\n' % cssclass)
         w(u'<input name="%s" type="text" value="%s" />\n' % (
                 xml_escape(self.facet.__regid__), self.value or u''))
         w(u'</div>\n')
--- a/web/test/unittest_http_headers.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/test/unittest_http_headers.py	Mon Dec 01 11:13:10 2014 +0100
@@ -12,3 +12,7 @@
 
         with self.assertRaises(ValueError):
             http_headers.generateTrueFalse('any value')
+
+if __name__ == '__main__':
+    from unittest import main
+    main()
--- a/web/test/unittest_viewselector.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/test/unittest_viewselector.py	Mon Dec 01 11:13:10 2014 +0100
@@ -218,7 +218,6 @@
             rset = req.execute('Any N, X WHERE X in_group Y, Y name N')
             self.assertListEqual(self.pviews(req, rset),
                                  [('csvexport', csvexport.CSVRsetView),
-                                  ('ecsvexport', csvexport.CSVEntityView),
                                   ('jsonexport', json.JsonRsetView),
                                   ('rsetxml', xmlrss.XMLRsetView),
                                   ('table', tableview.RsetTableView),
--- a/web/test/unittest_webconfig.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/test/unittest_webconfig.py	Mon Dec 01 11:13:10 2014 +0100
@@ -25,7 +25,9 @@
 
 class WebconfigTC(TestCase):
     def setUp(self):
-        self.config = ApptestConfiguration('data')
+        # need explicit None if dirname(__file__) is empty, see
+        # ApptestConfiguration.__init__
+        self.config = ApptestConfiguration('data', apphome=os.path.dirname(__file__) or None)
         self.config._cubes = ['file']
         self.config.load_configuration()
 
@@ -42,7 +44,11 @@
         rname = self.config.uiprops['FILE_ICON'].replace(self.config.datadir_url, '')
         self.assertIn('file', self.config.locate_resource(rname)[0].split(os.sep))
         cubicwebcsspath = self.config.locate_resource('cubicweb.css')[0].split(os.sep)
-        self.assertTrue('web' in cubicwebcsspath or 'shared' in cubicwebcsspath) # 'shared' if tests under apycot
+
+        # 'shared' if tests under apycot
+        self.assertTrue('web' in cubicwebcsspath or 'shared' in cubicwebcsspath,
+                        'neither "web" nor "shared" found in cubicwebcsspath (%s)'
+                        % cubicwebcsspath)
 
     def test_sign_text(self):
         signature = self.config.sign_text(u'hôp')
--- a/web/uihelper.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/uihelper.py	Mon Dec 01 11:13:10 2014 +0100
@@ -76,12 +76,9 @@
     def __init__(cls, name, bases, classdict):
         if cls.etype is None:
             return
-        if cls.uicfg_afs is None:
-            uicfg_afs = uicfg.autoform_section
-        if cls.uicfg_aff is None:
-            uicfg_aff = uicfg.autoform_field
-        if cls.uicfg_affk is None:
-            uicfg_affk = uicfg.autoform_field_kwargs
+        uicfg_afs = cls.uicfg_afs or uicfg.autoform_section
+        uicfg_aff = cls.uicfg_aff or uicfg.autoform_field
+        uicfg_affk = cls.uicfg_affk or uicfg.autoform_field_kwargs
         for attr_role in cls.hidden:
             uicfg_afs.hide_field(cls.etype, attr_role, formtype=cls.formtype)
         for attr_role in cls.rels_as_attrs:
--- a/web/views/csvexport.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/views/csvexport.py	Mon Dec 01 11:13:10 2014 +0100
@@ -21,7 +21,7 @@
 _ = unicode
 
 from cubicweb.schema import display_name
-from cubicweb.predicates import any_rset
+from cubicweb.predicates import any_rset, empty_rset
 from cubicweb.uilib import UnicodeCSVWriter
 from cubicweb.view import EntityView, AnyRsetView
 
@@ -80,7 +80,7 @@
     contents)
     """
     __regid__ = 'ecsvexport'
-    __select__ = any_rset()
+    __select__ = EntityView.__select__ | empty_rset()
     title = _('csv export (entities)')
 
     def call(self):
--- a/web/views/cwuser.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/views/cwuser.py	Mon Dec 01 11:13:10 2014 +0100
@@ -160,7 +160,8 @@
     def entity_call(self, entity, **kwargs):
         entity.complete()
         self.w(u'<a href="%s" class="%s">%s</a>' % (
-            entity.absolute_url(), entity.name, entity.printable_value('name')))
+            entity.absolute_url(), xml_escape(entity.name),
+            entity.printable_value('name')))
 
 
 # user / groups management views ###############################################
--- a/web/views/urlpublishing.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/views/urlpublishing.py	Mon Dec 01 11:13:10 2014 +0100
@@ -209,7 +209,10 @@
         if rset.rowcount == 1:
             req.form.setdefault('vid', 'primary')
         else: # rset.rowcount >= 1
-            req.form.setdefault('vid', 'sameetypelist')
+            if len(rset.column_types(0)) > 1:
+                req.form.setdefault('vid', 'list')
+            else:
+                req.form.setdefault('vid', 'sameetypelist')
 
     def handle_etype(self, req, cls):
         rset = req.execute(cls.fetch_rql(req.user))
--- a/web/webctl.py	Wed Jul 30 22:26:18 2014 +0200
+++ b/web/webctl.py	Mon Dec 01 11:13:10 2014 +0100
@@ -68,7 +68,8 @@
             dest = osp.join(config.appdatahome, 'data')
         if osp.exists(dest):
             if (not ask_clean or
-                not ASK.confirm('Remove existing data directory %s?' % dest)):
+                not (config.verbosity and
+                     ASK.confirm('Remove existing data directory %s?' % dest))):
                 raise ExecutionError('Directory %s already exists. '
                                      'Remove it first.' % dest)
             rmtree(dest)