[merge] backport 3.17 fixes into the future 3.18
authorAurelien Campeas <aurelien.campeas@logilab.fr>
Mon, 30 Sep 2013 18:07:51 +0200
changeset 9246 593b29325f68
parent 9218 c027ed79f1ce (current diff)
parent 9244 483181543899 (diff)
child 9265 614762cdc357
[merge] backport 3.17 fixes into the future 3.18
schema.py
--- a/__pkginfo__.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/__pkginfo__.py	Mon Sep 30 18:07:51 2013 +0200
@@ -22,7 +22,7 @@
 
 modname = distname = "cubicweb"
 
-numversion = (3, 17, 6)
+numversion = (3, 17, 7)
 version = '.'.join(str(num) for num in numversion)
 
 description = "a repository of entities / relations for knowledge management"
--- a/cubicweb.spec	Fri Aug 09 10:00:40 2013 +0200
+++ b/cubicweb.spec	Mon Sep 30 18:07:51 2013 +0200
@@ -7,7 +7,7 @@
 %endif
 
 Name:           cubicweb
-Version:        3.17.6
+Version:        3.17.7
 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	Fri Aug 09 10:00:40 2013 +0200
+++ b/debian/changelog	Mon Sep 30 18:07:51 2013 +0200
@@ -1,3 +1,9 @@
+cubicweb (3.17.7-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- David Douard <david.douard@logilab.fr>  Thu, 26 Sep 2013 15:13:39 +0200
+
 cubicweb (3.17.6-1) unstable; urgency=low
 
   * new upstream release
--- a/debian/rules	Fri Aug 09 10:00:40 2013 +0200
+++ b/debian/rules	Mon Sep 30 18:07:51 2013 +0200
@@ -23,7 +23,6 @@
 
 clean:
 	dh_testdir
-	dh_testroot
 	rm -f build-stamp configure-stamp
 	rm -rf build
 	#rm -rf debian/cubicweb-*/
--- a/devtools/__init__.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/devtools/__init__.py	Mon Sep 30 18:07:51 2013 +0200
@@ -288,8 +288,11 @@
 
         The function create it if necessary"""
         backupdir = join(self.config.apphome, 'database')
-        if not isdir(backupdir):
+        try:
             os.makedirs(backupdir)
+        except:
+            if not isdir(backupdir):
+                raise
         return backupdir
 
     def config_path(self, db_id):
--- a/devtools/devctl.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/devtools/devctl.py	Mon Sep 30 18:07:51 2013 +0200
@@ -153,8 +153,8 @@
         libschema = {}
         for cstrtype in CONSTRAINTS:
             add_msg(w, cstrtype)
-
-        is_in_lib = lambda: False
+        libafss = libaiams = None
+        is_in_lib = lambda *args: False
     done = set()
     for eschema in sorted(schema.entities()):
         if eschema.type in libschema:
--- a/entities/test/unittest_wfobjs.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/entities/test/unittest_wfobjs.py	Mon Sep 30 18:07:51 2013 +0200
@@ -75,7 +75,7 @@
         wf.add_transition(u'baz', (bar,), foo)
         with self.assertRaises(ValidationError) as cm:
             self.commit()
-        self.assertEqual(cm.exception.errors, {'name-subject': 'workflow already have a transition of that name'})
+        self.assertEqual(cm.exception.errors, {'name-subject': 'workflow already has a transition of that name'})
         # no pb if not in the same workflow
         wf2 = add_wf(self, 'Company')
         foo = wf.add_state(u'foo', initial=True)
@@ -88,7 +88,7 @@
         biz.cw_set(name=u'baz')
         with self.assertRaises(ValidationError) as cm:
             self.commit()
-        self.assertEqual(cm.exception.errors, {'name-subject': 'workflow already have a transition of that name'})
+        self.assertEqual(cm.exception.errors, {'name-subject': 'workflow already has a transition of that name'})
 
 
 class WorkflowTC(CubicWebTC):
--- a/i18n/de.po	Fri Aug 09 10:00:40 2013 +0200
+++ b/i18n/de.po	Mon Sep 30 18:07:51 2013 +0200
@@ -118,10 +118,6 @@
 msgstr ""
 
 #, python-format
-msgid "%s not estimated"
-msgstr "%s unbekannt(e)"
-
-#, python-format
 msgid "%s relation should not be in mapped"
 msgstr ""
 
@@ -525,9 +521,6 @@
 msgid "FormatConstraint"
 msgstr "Format-Einschränkung"
 
-msgid "From:"
-msgstr "Von:"
-
 msgid "Garbage collection information"
 msgstr "Information zur Speicherbereinigung"
 
@@ -702,9 +695,6 @@
 msgid "RQLVocabularyConstraint"
 msgstr "RQL Wortschatz-Einschränkung"
 
-msgid "Recipients:"
-msgstr "Adressaten:"
-
 msgid "RegexpConstraint"
 msgstr "regulärer Ausdruck Einschränkung"
 
@@ -766,9 +756,6 @@
 msgid "SubWorkflowExitPoint_plural"
 msgstr "subworkflow Endpunkte"
 
-msgid "Subject:"
-msgstr "Subjekt :"
-
 msgid "Submit bug report"
 msgstr "Fehlerbericht senden"
 
@@ -936,9 +923,6 @@
 msgid "Web server"
 msgstr "Web-Server"
 
-msgid "What's new?"
-msgstr "Was ist neu?"
-
 msgid "Workflow"
 msgstr "Workflow"
 
@@ -972,9 +956,6 @@
 "\"Durchsuchen\" oberhalb eine neue Datei hochladen, oder den Datei-Inhalt "
 "mit dem Widget unterhalb editieren."
 
-msgid "You can use any of the following substitutions in your text"
-msgstr "Sie können die folgenden Ersetzungen in Ihrem Text verwenden:"
-
 msgid "You can't change this relation"
 msgstr ""
 
@@ -1030,6 +1011,9 @@
 msgid "abstract base class for transitions"
 msgstr "abstrakte Basisklasse für Übergänge"
 
+msgid "action menu"
+msgstr ""
+
 msgid "action(s) on this selection"
 msgstr "Aktionen(en) bei dieser Auswahl"
 
@@ -1192,6 +1176,9 @@
 "Die Relation %(rtype)s von %(frometype)s #%(eidfrom)s zu %(toetype)s #"
 "%(eidto)s wurde hinzugefügt."
 
+msgid "additional type specific properties"
+msgstr ""
+
 msgid "addrelated"
 msgstr "hinzufügen"
 
@@ -1679,9 +1666,6 @@
 "core relation indicating the types (including specialized types) of an entity"
 msgstr ""
 
-msgid "cost"
-msgstr "Kosten"
-
 msgid "could not connect to the SMTP server"
 msgstr "Keine Verbindung mit dem SMTP-Server"
 
@@ -2380,18 +2364,9 @@
 msgid "eid"
 msgstr ""
 
-msgid "emails successfully sent"
-msgstr "E-Mails erfolgreich versandt."
-
-msgid "embed"
-msgstr "einbetten"
-
 msgid "embedded html"
 msgstr "HTML-Inhalt"
 
-msgid "embedding this url is forbidden"
-msgstr "Einbettung dieses URLs ist nicht erlaubt."
-
 msgid "end_timestamp"
 msgstr ""
 
@@ -2444,9 +2419,6 @@
 msgid "error"
 msgstr ""
 
-msgid "error while embedding page"
-msgstr "Fehler beim Einbetten der Seite"
-
 msgid "error while publishing ReST text"
 msgstr "Fehler beim Übersetzen von reST"
 
@@ -2456,9 +2428,6 @@
 "Fehler beim Zugriff auf Quelle %s, möglicherweise sind die Daten "
 "unvollständig."
 
-msgid "eta_date"
-msgstr "Enddatum"
-
 msgid "exit state must be a subworkflow state"
 msgstr "Exit-Zustand muss ein Subworkflow-Zustand sein."
 
@@ -2472,9 +2441,6 @@
 msgid "exiting from subworkflow %s"
 msgstr "verlasse Subworkflow %s"
 
-msgid "expected:"
-msgstr "erwartet:"
-
 msgid "expression"
 msgstr "Ausdruck"
 
@@ -2489,8 +2455,12 @@
 msgid "exprtype"
 msgstr "Typ des Ausdrucks"
 
-msgid "external page"
-msgstr "externe Seite"
+msgid "extra_props"
+msgstr ""
+
+msgctxt "CWAttribute"
+msgid "extra_props"
+msgstr ""
 
 msgid "facet-loading-msg"
 msgstr ""
@@ -2908,10 +2878,6 @@
 msgid "info"
 msgstr "Information"
 
-#, python-format
-msgid "initial estimation %s"
-msgstr "Erste Schätzung %s"
-
 msgid "initial state for this workflow"
 msgstr "Anfangszustand für diesen Workflow"
 
@@ -3187,9 +3153,6 @@
 msgid "message"
 msgstr ""
 
-msgid "milestone"
-msgstr "Meilenstein"
-
 #, python-format
 msgid "missing parameters for entity %s"
 msgstr "Fehlende Parameter für Entität %s"
@@ -3336,9 +3299,6 @@
 msgid "no related entity"
 msgstr "keine verknüpfte Entität"
 
-msgid "no related project"
-msgstr "kein verknüpftes Projekt"
-
 msgid "no repository sessions found"
 msgstr "keine Datenbank-Sitzung gefunden"
 
@@ -3538,15 +3498,6 @@
 msgid "profile"
 msgstr "Profil"
 
-msgid "progress"
-msgstr "Fortschritt"
-
-msgid "progress bar"
-msgstr "Fortschrittsbalken"
-
-msgid "project"
-msgstr "Projekt"
-
 msgid "rdef-description"
 msgstr "Beschreibung"
 
@@ -3815,9 +3766,6 @@
 msgid "semantic description of this workflow"
 msgstr "Semantische Beschreibung dieses Workflows"
 
-msgid "send email"
-msgstr "E-Mail senden"
-
 msgid "september"
 msgstr "September"
 
@@ -3846,9 +3794,6 @@
 msgid "show filter form"
 msgstr "Filter zeigen"
 
-msgid "sioc"
-msgstr "sioc"
-
 msgid "site configuration"
 msgstr "Konfiguration der Website"
 
@@ -4067,9 +4012,6 @@
 msgid "tablefilter"
 msgstr "Tabellenfilter"
 
-msgid "task progression"
-msgstr "Fortschritt der Aufgabe"
-
 msgid "text"
 msgstr "Text"
 
@@ -4192,15 +4134,9 @@
 msgid "to_state_object"
 msgstr "Übergang zu diesem Zustand"
 
-msgid "todo_by"
-msgstr "zu erledigen bis"
-
 msgid "toggle check boxes"
 msgstr "Kontrollkästchen umkehren"
 
-msgid "toggle filter"
-msgstr "filter verbergen/zeigen"
-
 msgid "tr_count"
 msgstr ""
 
@@ -4491,6 +4427,14 @@
 msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
 msgstr ""
 
+#, python-format
+msgid "value %(KEY-value)s must be <= %(KEY-boundary)s"
+msgstr ""
+
+#, python-format
+msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
+msgstr ""
+
 msgid "value associated to this key is not editable manually"
 msgstr ""
 "Der mit diesem Schlüssele verbundene Wert kann n icht manuell geändert "
@@ -4534,10 +4478,6 @@
 msgid "view_index"
 msgstr "Index-Seite"
 
-#, python-format
-msgid "violates unique_together constraints (%s)"
-msgstr "Verletzung der unique_together-Einschränkung (%s)"
-
 msgid "visible"
 msgstr "sichtbar"
 
@@ -4547,6 +4487,9 @@
 msgid "we are not yet ready to handle this query"
 msgstr "Momentan können wir diese sparql-Anfrage noch nicht ausführen."
 
+msgid "web sessions without CNX"
+msgstr ""
+
 msgid "wednesday"
 msgstr "Mittwoch"
 
@@ -4577,11 +4520,11 @@
 msgid "workflow"
 msgstr "Workflow"
 
-msgid "workflow already have a state of that name"
-msgstr "Der Workflow hat bereits einen Zustand desselben Namens."
-
-msgid "workflow already have a transition of that name"
-msgstr "Der Workflow hat bereits einen Übergang desselben Namens."
+msgid "workflow already has a state of that name"
+msgstr ""
+
+msgid "workflow already has a transition of that name"
+msgstr ""
 
 #, python-format
 msgid "workflow changed to \"%s\""
@@ -4652,6 +4595,9 @@
 #~ msgid "%(value)r doesn't match the %(regexp)r regular expression"
 #~ msgstr "%(value)r entspricht nicht dem regulären Ausdruck %(regexp)r"
 
+#~ msgid "%s not estimated"
+#~ msgstr "%s unbekannt(e)"
+
 #~ msgid ""
 #~ "Can't restore relation %(rtype)s of entity %(eid)s, this relation does "
 #~ "not exists anymore in the schema."
@@ -4659,11 +4605,92 @@
 #~ "Kann die Relation %(rtype)s der Entität %(eid)s nicht wieder herstellen, "
 #~ "diese Relation existiert nicht mehr in dem Schema."
 
+#~ msgid "From:"
+#~ msgstr "Von:"
+
+#~ msgid "Recipients:"
+#~ msgstr "Adressaten:"
+
+#~ msgid "Subject:"
+#~ msgstr "Subjekt :"
+
+#~ msgid "What's new?"
+#~ msgstr "Was ist neu?"
+
+#~ msgid "You can use any of the following substitutions in your text"
+#~ msgstr "Sie können die folgenden Ersetzungen in Ihrem Text verwenden:"
+
 #~ msgid "can't change the %s attribute"
 #~ msgstr "Kann das Attribut %s nicht ändern."
 
+#~ msgid "cost"
+#~ msgstr "Kosten"
+
+#~ msgid "emails successfully sent"
+#~ msgstr "E-Mails erfolgreich versandt."
+
+#~ msgid "embed"
+#~ msgstr "einbetten"
+
+#~ msgid "embedding this url is forbidden"
+#~ msgstr "Einbettung dieses URLs ist nicht erlaubt."
+
+#~ msgid "error while embedding page"
+#~ msgstr "Fehler beim Einbetten der Seite"
+
+#~ msgid "eta_date"
+#~ msgstr "Enddatum"
+
+#~ msgid "expected:"
+#~ msgstr "erwartet:"
+
+#~ msgid "external page"
+#~ msgstr "externe Seite"
+
 #~ msgid "incorrect value (%(value)s) for type \"%(type)s\""
 #~ msgstr "Wert %(value)s ungültig für den Typ \"%(type)s\""
 
+#~ msgid "initial estimation %s"
+#~ msgstr "Erste Schätzung %s"
+
 #~ msgid "invalid value %(value)s, it must be one of %(choices)s"
 #~ msgstr "Wert %(value)s ungültig, er muss zwischen %(choices)s"
+
+#~ msgid "milestone"
+#~ msgstr "Meilenstein"
+
+#~ msgid "no related project"
+#~ msgstr "kein verknüpftes Projekt"
+
+#~ msgid "progress"
+#~ msgstr "Fortschritt"
+
+#~ msgid "progress bar"
+#~ msgstr "Fortschrittsbalken"
+
+#~ msgid "project"
+#~ msgstr "Projekt"
+
+#~ msgid "send email"
+#~ msgstr "E-Mail senden"
+
+#~ msgid "sioc"
+#~ msgstr "sioc"
+
+#~ msgid "task progression"
+#~ msgstr "Fortschritt der Aufgabe"
+
+#~ msgid "todo_by"
+#~ msgstr "zu erledigen bis"
+
+#~ msgid "toggle filter"
+#~ msgstr "filter verbergen/zeigen"
+
+#~ msgid "violates unique_together constraints (%s)"
+#~ msgstr "Verletzung der unique_together-Einschränkung (%s)"
+
+#~ msgid "workflow already have a state of that name"
+#~ msgstr "Der Workflow hat bereits einen Zustand desselben Namens."
+
+#~ msgid "workflow already have a transition of that name"
+#~ msgstr "Der Workflow hat bereits einen Übergang desselben Namens."
--- a/i18n/en.po	Fri Aug 09 10:00:40 2013 +0200
+++ b/i18n/en.po	Mon Sep 30 18:07:51 2013 +0200
@@ -110,10 +110,6 @@
 msgstr ""
 
 #, python-format
-msgid "%s not estimated"
-msgstr ""
-
-#, python-format
 msgid "%s relation should not be in mapped"
 msgstr ""
 
@@ -503,9 +499,6 @@
 msgid "FormatConstraint"
 msgstr "format constraint"
 
-msgid "From:"
-msgstr ""
-
 msgid "Garbage collection information"
 msgstr ""
 
@@ -678,9 +671,6 @@
 msgid "RQLVocabularyConstraint"
 msgstr "RQL vocabulary constraint"
 
-msgid "Recipients:"
-msgstr ""
-
 msgid "RegexpConstraint"
 msgstr "regular expression constrainte"
 
@@ -742,9 +732,6 @@
 msgid "SubWorkflowExitPoint_plural"
 msgstr "subworkflow exit-points"
 
-msgid "Subject:"
-msgstr ""
-
 msgid "Submit bug report"
 msgstr ""
 
@@ -912,9 +899,6 @@
 msgid "Web server"
 msgstr ""
 
-msgid "What's new?"
-msgstr ""
-
 msgid "Workflow"
 msgstr "Workflow"
 
@@ -941,9 +925,6 @@
 "content online with the widget below."
 msgstr ""
 
-msgid "You can use any of the following substitutions in your text"
-msgstr ""
-
 msgid "You can't change this relation"
 msgstr ""
 
@@ -992,6 +973,9 @@
 msgid "abstract base class for transitions"
 msgstr ""
 
+msgid "action menu"
+msgstr ""
+
 msgid "action(s) on this selection"
 msgstr ""
 
@@ -1152,6 +1136,9 @@
 "%(eidto)s"
 msgstr ""
 
+msgid "additional type specific properties"
+msgstr ""
+
 msgid "addrelated"
 msgstr "add"
 
@@ -1634,9 +1621,6 @@
 "core relation indicating the types (including specialized types) of an entity"
 msgstr ""
 
-msgid "cost"
-msgstr ""
-
 msgid "could not connect to the SMTP server"
 msgstr ""
 
@@ -2329,18 +2313,9 @@
 msgid "eid"
 msgstr ""
 
-msgid "emails successfully sent"
-msgstr ""
-
-msgid "embed"
-msgstr ""
-
 msgid "embedded html"
 msgstr ""
 
-msgid "embedding this url is forbidden"
-msgstr ""
-
 msgid "end_timestamp"
 msgstr "end timestamp"
 
@@ -2393,9 +2368,6 @@
 msgid "error"
 msgstr ""
 
-msgid "error while embedding page"
-msgstr ""
-
 msgid "error while publishing ReST text"
 msgstr ""
 
@@ -2403,9 +2375,6 @@
 msgid "error while querying source %s, some data may be missing"
 msgstr ""
 
-msgid "eta_date"
-msgstr "ETA date"
-
 msgid "exit state must be a subworkflow state"
 msgstr ""
 
@@ -2419,9 +2388,6 @@
 msgid "exiting from subworkflow %s"
 msgstr ""
 
-msgid "expected:"
-msgstr ""
-
 msgid "expression"
 msgstr ""
 
@@ -2436,7 +2402,11 @@
 msgid "exprtype"
 msgstr "expression type"
 
-msgid "external page"
+msgid "extra_props"
+msgstr ""
+
+msgctxt "CWAttribute"
+msgid "extra_props"
 msgstr ""
 
 msgid "facet-loading-msg"
@@ -2835,10 +2805,6 @@
 msgid "info"
 msgstr ""
 
-#, python-format
-msgid "initial estimation %s"
-msgstr ""
-
 msgid "initial state for this workflow"
 msgstr ""
 
@@ -3105,9 +3071,6 @@
 msgid "message"
 msgstr ""
 
-msgid "milestone"
-msgstr ""
-
 #, python-format
 msgid "missing parameters for entity %s"
 msgstr ""
@@ -3252,9 +3215,6 @@
 msgid "no related entity"
 msgstr ""
 
-msgid "no related project"
-msgstr ""
-
 msgid "no repository sessions found"
 msgstr ""
 
@@ -3453,15 +3413,6 @@
 msgid "profile"
 msgstr ""
 
-msgid "progress"
-msgstr ""
-
-msgid "progress bar"
-msgstr ""
-
-msgid "project"
-msgstr ""
-
 msgid "rdef-description"
 msgstr "description"
 
@@ -3727,9 +3678,6 @@
 msgid "semantic description of this workflow"
 msgstr ""
 
-msgid "send email"
-msgstr ""
-
 msgid "september"
 msgstr ""
 
@@ -3755,9 +3703,6 @@
 msgid "show filter form"
 msgstr ""
 
-msgid "sioc"
-msgstr ""
-
 msgid "site configuration"
 msgstr ""
 
@@ -3968,9 +3913,6 @@
 msgid "tablefilter"
 msgstr "table filter"
 
-msgid "task progression"
-msgstr ""
-
 msgid "text"
 msgstr ""
 
@@ -4092,15 +4034,9 @@
 msgid "to_state_object"
 msgstr "transitions to this state"
 
-msgid "todo_by"
-msgstr "to do by"
-
 msgid "toggle check boxes"
 msgstr ""
 
-msgid "toggle filter"
-msgstr ""
-
 msgid "tr_count"
 msgstr "transition number"
 
@@ -4382,6 +4318,14 @@
 msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
 msgstr ""
 
+#, python-format
+msgid "value %(KEY-value)s must be <= %(KEY-boundary)s"
+msgstr ""
+
+#, python-format
+msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
+msgstr ""
+
 msgid "value associated to this key is not editable manually"
 msgstr ""
 
@@ -4423,10 +4367,6 @@
 msgid "view_index"
 msgstr "index"
 
-#, python-format
-msgid "violates unique_together constraints (%s)"
-msgstr "violates unique_together constraints (%s)"
-
 msgid "visible"
 msgstr ""
 
@@ -4436,6 +4376,9 @@
 msgid "we are not yet ready to handle this query"
 msgstr ""
 
+msgid "web sessions without CNX"
+msgstr ""
+
 msgid "wednesday"
 msgstr ""
 
@@ -4464,10 +4407,10 @@
 msgid "workflow"
 msgstr ""
 
-msgid "workflow already have a state of that name"
-msgstr ""
-
-msgid "workflow already have a transition of that name"
+msgid "workflow already has a state of that name"
+msgstr ""
+
+msgid "workflow already has a transition of that name"
 msgstr ""
 
 #, python-format
@@ -4532,3 +4475,12 @@
 #, python-format
 msgid "you should un-inline relation %s which is supported and may be crossed "
 msgstr ""
+
+#~ msgid "eta_date"
+#~ msgstr "ETA date"
+
+#~ msgid "todo_by"
+#~ msgstr "to do by"
+
+#~ msgid "violates unique_together constraints (%s)"
+#~ msgstr "violates unique_together constraints (%s)"
--- a/i18n/es.po	Fri Aug 09 10:00:40 2013 +0200
+++ b/i18n/es.po	Mon Sep 30 18:07:51 2013 +0200
@@ -119,10 +119,6 @@
 msgstr ""
 
 #, python-format
-msgid "%s not estimated"
-msgstr "%s no estimado(s)"
-
-#, python-format
 msgid "%s relation should not be in mapped"
 msgstr "la relación %s no debería estar mapeada"
 
@@ -525,9 +521,6 @@
 msgid "FormatConstraint"
 msgstr "Restricción de Formato"
 
-msgid "From:"
-msgstr "De: "
-
 msgid "Garbage collection information"
 msgstr "Recolector de basura en memoria"
 
@@ -700,9 +693,6 @@
 msgid "RQLVocabularyConstraint"
 msgstr "Restricción RQL de Vocabulario"
 
-msgid "Recipients:"
-msgstr "Destinatarios :"
-
 msgid "RegexpConstraint"
 msgstr "restricción expresión regular"
 
@@ -767,9 +757,6 @@
 msgid "SubWorkflowExitPoint_plural"
 msgstr "Salidas de sub-workflow"
 
-msgid "Subject:"
-msgstr "Sujeto:"
-
 msgid "Submit bug report"
 msgstr "Enviar un reporte de error (bug)"
 
@@ -939,9 +926,6 @@
 msgid "Web server"
 msgstr "Servidor web"
 
-msgid "What's new?"
-msgstr "Lo más reciente"
-
 msgid "Workflow"
 msgstr "Workflow"
 
@@ -975,11 +959,6 @@
 "\"buscar\" en la parte superior, o editar el contenido del archivo en línea\n"
 "en el campo siguiente."
 
-msgid "You can use any of the following substitutions in your text"
-msgstr ""
-"Puede realizar cualquiera de las siguientes sustituciones en el contenido de "
-"su email."
-
 msgid "You can't change this relation"
 msgstr ""
 
@@ -1040,6 +1019,9 @@
 msgid "abstract base class for transitions"
 msgstr "Clase de base abstracta para la transiciones"
 
+msgid "action menu"
+msgstr ""
+
 msgid "action(s) on this selection"
 msgstr "Acción(es) en esta selección"
 
@@ -1202,6 +1184,9 @@
 "la relación %(rtype)s de %(frometype)s #%(eidfrom)s a %(toetype)s #%(eidto)s "
 "ha sido agregada"
 
+msgid "additional type specific properties"
+msgstr ""
+
 msgid "addrelated"
 msgstr "Agregar"
 
@@ -1699,9 +1684,6 @@
 "Relación sistema indicando los tipos (incluídos los tipos padres) de una "
 "entidad"
 
-msgid "cost"
-msgstr "Costo"
-
 msgid "could not connect to the SMTP server"
 msgstr "Imposible de conectarse al servidor SMTP"
 
@@ -2419,18 +2401,9 @@
 msgid "eid"
 msgstr "eid"
 
-msgid "emails successfully sent"
-msgstr "Mensajes enviados con éxito"
-
-msgid "embed"
-msgstr "Incrustado"
-
 msgid "embedded html"
 msgstr "Html incrustado"
 
-msgid "embedding this url is forbidden"
-msgstr "La inclusión de este url esta prohibida"
-
 msgid "end_timestamp"
 msgstr ""
 
@@ -2485,9 +2458,6 @@
 msgid "error"
 msgstr "error"
 
-msgid "error while embedding page"
-msgstr "Error durante la inclusión de la página"
-
 msgid "error while publishing ReST text"
 msgstr ""
 "Se ha producido un error durante la interpretación del texto en formato ReST"
@@ -2498,9 +2468,6 @@
 "Un error ha ocurrido al interrogar  %s, es posible que los \n"
 "datos visibles se encuentren incompletos"
 
-msgid "eta_date"
-msgstr "Fecha de fin"
-
 msgid "exit state must be a subworkflow state"
 msgstr "El estado de salida debe de ser un estado del Sub-Workflow"
 
@@ -2514,9 +2481,6 @@
 msgid "exiting from subworkflow %s"
 msgstr "Salida del subworkflow %s"
 
-msgid "expected:"
-msgstr "Previsto :"
-
 msgid "expression"
 msgstr "Expresión"
 
@@ -2531,8 +2495,12 @@
 msgid "exprtype"
 msgstr "Tipo"
 
-msgid "external page"
-msgstr "Página externa"
+msgid "extra_props"
+msgstr ""
+
+msgctxt "CWAttribute"
+msgid "extra_props"
+msgstr ""
 
 msgid "facet-loading-msg"
 msgstr ""
@@ -2949,10 +2917,6 @@
 msgid "info"
 msgstr "Información del Sistema"
 
-#, python-format
-msgid "initial estimation %s"
-msgstr "Estimación inicial %s"
-
 msgid "initial state for this workflow"
 msgstr "Estado inicial para este Workflow"
 
@@ -3228,9 +3192,6 @@
 msgid "message"
 msgstr ""
 
-msgid "milestone"
-msgstr "Milestone"
-
 #, python-format
 msgid "missing parameters for entity %s"
 msgstr "Parámetros faltantes a la entidad %s"
@@ -3377,9 +3338,6 @@
 msgid "no related entity"
 msgstr "No posee entidad asociada"
 
-msgid "no related project"
-msgstr "No tiene proyecto relacionado"
-
 msgid "no repository sessions found"
 msgstr "Ninguna sesión encontrada"
 
@@ -3579,15 +3537,6 @@
 msgid "profile"
 msgstr "perfil"
 
-msgid "progress"
-msgstr "Progreso"
-
-msgid "progress bar"
-msgstr "Barra de Progreso"
-
-msgid "project"
-msgstr "Proyecto"
-
 msgid "rdef-description"
 msgstr "Descripción"
 
@@ -3865,9 +3814,6 @@
 msgid "semantic description of this workflow"
 msgstr "Descripcion semántica de este Workflow"
 
-msgid "send email"
-msgstr "Enviar email"
-
 msgid "september"
 msgstr "Septiembre"
 
@@ -3897,9 +3843,6 @@
 msgid "show filter form"
 msgstr "Mostrar el Filtro"
 
-msgid "sioc"
-msgstr "SIOC"
-
 msgid "site configuration"
 msgstr "Configuración Sistema"
 
@@ -4117,9 +4060,6 @@
 msgid "tablefilter"
 msgstr "Tablero de Filtrado"
 
-msgid "task progression"
-msgstr "Progreso de la Acción"
-
 msgid "text"
 msgstr "Texto"
 
@@ -4242,15 +4182,9 @@
 msgid "to_state_object"
 msgstr "Transición hacia este Estado"
 
-msgid "todo_by"
-msgstr "Asignada a"
-
 msgid "toggle check boxes"
 msgstr "Cambiar valor"
 
-msgid "toggle filter"
-msgstr "esconder/mostrar el filtro"
-
 msgid "tr_count"
 msgstr "n° de transición"
 
@@ -4541,6 +4475,14 @@
 msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
 msgstr ""
 
+#, python-format
+msgid "value %(KEY-value)s must be <= %(KEY-boundary)s"
+msgstr ""
+
+#, python-format
+msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
+msgstr ""
+
 msgid "value associated to this key is not editable manually"
 msgstr "El valor asociado a este elemento no es editable manualmente"
 
@@ -4582,10 +4524,6 @@
 msgid "view_index"
 msgstr "Inicio"
 
-#, python-format
-msgid "violates unique_together constraints (%s)"
-msgstr "viola el principio (o restricción) de singularidad (%s)"
-
 msgid "visible"
 msgstr "Visible"
 
@@ -4595,6 +4533,9 @@
 msgid "we are not yet ready to handle this query"
 msgstr "Aún no podemos manejar este tipo de consulta Sparql"
 
+msgid "web sessions without CNX"
+msgstr ""
+
 msgid "wednesday"
 msgstr "Miércoles"
 
@@ -4626,11 +4567,11 @@
 msgid "workflow"
 msgstr "Workflow"
 
-msgid "workflow already have a state of that name"
-msgstr "El Workflow ya tiene un Estado con ese nombre"
-
-msgid "workflow already have a transition of that name"
-msgstr "El Workflow ya tiene una transición con ese nombre"
+msgid "workflow already has a state of that name"
+msgstr ""
+
+msgid "workflow already has a transition of that name"
+msgstr ""
 
 #, python-format
 msgid "workflow changed to \"%s\""
@@ -4703,6 +4644,9 @@
 #~ msgid "%(value)r doesn't match the %(regexp)r regular expression"
 #~ msgstr "%(value)r no corresponde a la expresión regular %(regexp)r"
 
+#~ msgid "%s not estimated"
+#~ msgstr "%s no estimado(s)"
+
 #~ msgid ""
 #~ "Can't restore relation %(rtype)s of entity %(eid)s, this relation does "
 #~ "not exists anymore in the schema."
@@ -4710,17 +4654,100 @@
 #~ "No puede restaurar la relación %(rtype)s de la entidad %(eid)s, esta "
 #~ "relación ya no existe en el esquema."
 
+#~ msgid "From:"
+#~ msgstr "De: "
+
+#~ msgid "Recipients:"
+#~ msgstr "Destinatarios :"
+
+#~ msgid "Subject:"
+#~ msgstr "Sujeto:"
+
+#~ msgid "What's new?"
+#~ msgstr "Lo más reciente"
+
+#~ msgid "You can use any of the following substitutions in your text"
+#~ msgstr ""
+#~ "Puede realizar cualquiera de las siguientes sustituciones en el contenido "
+#~ "de su email."
+
 #~ msgid "can't change the %s attribute"
 #~ msgstr "no puede modificar el atributo %s"
 
 #~ msgid "can't change this relation"
 #~ msgstr "no puede modificar esta relación"
 
+#~ msgid "cost"
+#~ msgstr "Costo"
+
+#~ msgid "emails successfully sent"
+#~ msgstr "Mensajes enviados con éxito"
+
+#~ msgid "embed"
+#~ msgstr "Incrustado"
+
+#~ msgid "embedding this url is forbidden"
+#~ msgstr "La inclusión de este url esta prohibida"
+
+#~ msgid "error while embedding page"
+#~ msgstr "Error durante la inclusión de la página"
+
+#~ msgid "eta_date"
+#~ msgstr "Fecha de fin"
+
+#~ msgid "expected:"
+#~ msgstr "Previsto :"
+
+#~ msgid "external page"
+#~ msgstr "Página externa"
+
 #~ msgid "incorrect value (%(value)s) for type \"%(type)s\""
 #~ msgstr "valor %(value)s incorrecto para el tipo \"%(type)s\""
 
+#~ msgid "initial estimation %s"
+#~ msgstr "Estimación inicial %s"
+
 #~ msgid "invalid value %(value)s, it must be one of %(choices)s"
 #~ msgstr "Valor %(value)s incorrecto, debe estar entre %(choices)s"
 
+#~ msgid "milestone"
+#~ msgstr "Milestone"
+
+#~ msgid "no related project"
+#~ msgstr "No tiene proyecto relacionado"
+
+#~ msgid "progress"
+#~ msgstr "Progreso"
+
+#~ msgid "progress bar"
+#~ msgstr "Barra de Progreso"
+
+#~ msgid "project"
+#~ msgstr "Proyecto"
+
+#~ msgid "send email"
+#~ msgstr "Enviar email"
+
+#~ msgid "sioc"
+#~ msgstr "SIOC"
+
+#~ msgid "task progression"
+#~ msgstr "Progreso de la Acción"
+
+#~ msgid "todo_by"
+#~ msgstr "Asignada a"
+
+#~ msgid "toggle filter"
+#~ msgstr "esconder/mostrar el filtro"
+
 #~ msgid "unknown source type"
 #~ msgstr "tipo de fuente desconocida"
+
+#~ msgid "violates unique_together constraints (%s)"
+#~ msgstr "viola el principio (o restricción) de singularidad (%s)"
+
+#~ msgid "workflow already have a state of that name"
+#~ msgstr "El Workflow ya tiene un Estado con ese nombre"
+
+#~ msgid "workflow already have a transition of that name"
+#~ msgstr "El Workflow ya tiene una transición con ese nombre"
--- a/i18n/fr.po	Fri Aug 09 10:00:40 2013 +0200
+++ b/i18n/fr.po	Mon Sep 30 18:07:51 2013 +0200
@@ -119,10 +119,6 @@
 msgstr "%s appartient à une contrainte d'unicité transgressée"
 
 #, python-format
-msgid "%s not estimated"
-msgstr "%s non estimé(s)"
-
-#, python-format
 msgid "%s relation should not be in mapped"
 msgstr "la relation %s ne devrait pas ếtre mappé"
 
@@ -439,6 +435,8 @@
 "Configuration of the system source goes to the 'sources' file, not in the "
 "database"
 msgstr ""
+"La configuration de la source système va dans le fichier 'sources' et non "
+"dans la base de données"
 
 #, python-format
 msgid "Created %(etype)s : %(entity)s"
@@ -526,9 +524,6 @@
 msgid "FormatConstraint"
 msgstr "contrainte de format"
 
-msgid "From:"
-msgstr "De :"
-
 msgid "Garbage collection information"
 msgstr "Information sur le ramasse-miette"
 
@@ -701,9 +696,6 @@
 msgid "RQLVocabularyConstraint"
 msgstr "contrainte rql de vocabulaire"
 
-msgid "Recipients:"
-msgstr "Destinataires :"
-
 msgid "RegexpConstraint"
 msgstr "contrainte expression régulière"
 
@@ -768,9 +760,6 @@
 msgid "SubWorkflowExitPoint_plural"
 msgstr "Sorties de sous-workflow"
 
-msgid "Subject:"
-msgstr "Sujet :"
-
 msgid "Submit bug report"
 msgstr "Soumettre un rapport de bug"
 
@@ -942,9 +931,6 @@
 msgid "Web server"
 msgstr "Serveur web"
 
-msgid "What's new?"
-msgstr "Nouveautés"
-
 msgid "Workflow"
 msgstr "Workflow"
 
@@ -978,11 +964,6 @@
 "\"parcourir\" ci-dessu, soit éditer le contenu du fichier en ligne\n"
 "avec le champ ci-dessous."
 
-msgid "You can use any of the following substitutions in your text"
-msgstr ""
-"Vous pouvez utiliser n'importe quelle substitution parmi la liste suivante "
-"dans le contenu de votre courriel."
-
 msgid "You can't change this relation"
 msgstr "Vous ne pouvez pas modifier cette relation"
 
@@ -1043,6 +1024,9 @@
 msgid "abstract base class for transitions"
 msgstr "classe de base abstraite pour les transitions"
 
+msgid "action menu"
+msgstr "actions"
+
 msgid "action(s) on this selection"
 msgstr "action(s) sur cette sélection"
 
@@ -1205,6 +1189,9 @@
 "la relation %(rtype)s de %(frometype)s #%(eidfrom)s vers %(toetype)s #"
 "%(eidto)s a été ajoutée"
 
+msgid "additional type specific properties"
+msgstr "propriétés supplémentaires spécifiques au type"
+
 msgid "addrelated"
 msgstr "ajouter"
 
@@ -1711,9 +1698,6 @@
 "relation système indiquant les types (y compris les types parents) d'une "
 "entité"
 
-msgid "cost"
-msgstr "coût"
-
 msgid "could not connect to the SMTP server"
 msgstr "impossible de se connecter au serveur SMTP"
 
@@ -2430,18 +2414,9 @@
 msgid "eid"
 msgstr "eid"
 
-msgid "emails successfully sent"
-msgstr "courriels envoyés avec succès"
-
-msgid "embed"
-msgstr "embarqué"
-
 msgid "embedded html"
 msgstr "HTML contenu"
 
-msgid "embedding this url is forbidden"
-msgstr "l'inclusion de cette url est interdite"
-
 msgid "end_timestamp"
 msgstr "horodate de fin"
 
@@ -2496,9 +2471,6 @@
 msgid "error"
 msgstr "erreur"
 
-msgid "error while embedding page"
-msgstr "erreur pendant l'inclusion de la page"
-
 msgid "error while publishing ReST text"
 msgstr ""
 "une erreur s'est produite lors de l'interprétation du texte au format ReST"
@@ -2509,9 +2481,6 @@
 "une erreur est survenue en interrogeant %s, il est possible que les\n"
 "données affichées soient incomplètes"
 
-msgid "eta_date"
-msgstr "date de fin"
-
 msgid "exit state must be a subworkflow state"
 msgstr "l'état de sortie doit être un état du sous-workflow"
 
@@ -2525,9 +2494,6 @@
 msgid "exiting from subworkflow %s"
 msgstr "sortie du sous-workflow %s"
 
-msgid "expected:"
-msgstr "attendu :"
-
 msgid "expression"
 msgstr "expression"
 
@@ -2542,8 +2508,12 @@
 msgid "exprtype"
 msgstr "type"
 
-msgid "external page"
-msgstr "page externe"
+msgid "extra_props"
+msgstr ""
+
+msgctxt "CWAttribute"
+msgid "extra_props"
+msgstr "propriétés additionnelles"
 
 msgid "facet-loading-msg"
 msgstr "en cours de traitement, merci de patienter"
@@ -2958,10 +2928,6 @@
 msgid "info"
 msgstr "information"
 
-#, python-format
-msgid "initial estimation %s"
-msgstr "estimation initiale %s"
-
 msgid "initial state for this workflow"
 msgstr "état initial pour ce workflow"
 
@@ -3239,9 +3205,6 @@
 msgid "message"
 msgstr "message"
 
-msgid "milestone"
-msgstr "jalon"
-
 #, python-format
 msgid "missing parameters for entity %s"
 msgstr "paramètres manquants pour l'entité %s"
@@ -3388,9 +3351,6 @@
 msgid "no related entity"
 msgstr "pas d'entité liée"
 
-msgid "no related project"
-msgstr "pas de projet rattaché"
-
 msgid "no repository sessions found"
 msgstr "aucune session trouvée"
 
@@ -3592,15 +3552,6 @@
 msgid "profile"
 msgstr "profil"
 
-msgid "progress"
-msgstr "avancement"
-
-msgid "progress bar"
-msgstr "barre d'avancement"
-
-msgid "project"
-msgstr "projet"
-
 msgid "rdef-description"
 msgstr "description"
 
@@ -3879,9 +3830,6 @@
 msgid "semantic description of this workflow"
 msgstr "description sémantique de ce workflow"
 
-msgid "send email"
-msgstr "envoyer un courriel"
-
 msgid "september"
 msgstr "septembre"
 
@@ -3910,9 +3858,6 @@
 msgid "show filter form"
 msgstr "afficher le filtre"
 
-msgid "sioc"
-msgstr "sioc"
-
 msgid "site configuration"
 msgstr "configuration du site"
 
@@ -4132,9 +4077,6 @@
 msgid "tablefilter"
 msgstr "filtre de tableau"
 
-msgid "task progression"
-msgstr "avancement de la tâche"
-
 msgid "text"
 msgstr "text"
 
@@ -4257,15 +4199,9 @@
 msgid "to_state_object"
 msgstr "transition vers cet état"
 
-msgid "todo_by"
-msgstr "à faire par"
-
 msgid "toggle check boxes"
 msgstr "afficher/masquer les cases à cocher"
 
-msgid "toggle filter"
-msgstr "afficher/masquer le filtre"
-
 msgid "tr_count"
 msgstr "n° de transition"
 
@@ -4554,6 +4490,16 @@
 msgid "value %(KEY-value)s must be %(KEY-op)s %(KEY-boundary)s"
 msgstr "la valeur %(KEY-value)s n'est pas %(KEY-op)s %(KEY-boundary)s"
 
+#, python-format
+msgid "value %(KEY-value)s must be <= %(KEY-boundary)s"
+msgstr ""
+"la valeur %(KEY-value)s n'est pas inférieure ou égale à %(KEY-boundary)s"
+
+#, python-format
+msgid "value %(KEY-value)s must be >= %(KEY-boundary)s"
+msgstr ""
+"la valeur %(KEY-value)s n'est pas supérieure ou égale à %(KEY-boundary)s"
+
 msgid "value associated to this key is not editable manually"
 msgstr "la valeur associée à cette clé n'est pas éditable manuellement"
 
@@ -4599,10 +4545,6 @@
 msgid "view_index"
 msgstr "accueil"
 
-#, python-format
-msgid "violates unique_together constraints (%s)"
-msgstr "violation de contrainte unique_together (%s)"
-
 msgid "visible"
 msgstr "visible"
 
@@ -4613,6 +4555,9 @@
 msgstr ""
 "nous ne sommes pas capable de gérer ce type de requête sparql pour le moment"
 
+msgid "web sessions without CNX"
+msgstr "sessions web sans connexion associée"
+
 msgid "wednesday"
 msgstr "mercredi"
 
@@ -4644,10 +4589,10 @@
 msgid "workflow"
 msgstr "workflow"
 
-msgid "workflow already have a state of that name"
+msgid "workflow already has a state of that name"
 msgstr "le workflow a déja un état du même nom"
 
-msgid "workflow already have a transition of that name"
+msgid "workflow already has a transition of that name"
 msgstr "le workflow a déja une transition du même nom"
 
 #, python-format
@@ -4715,26 +4660,106 @@
 "vous devriez enlevé la mise en ligne de la relation %s qui est supportée et "
 "peut-être croisée"
 
+#~ msgid "%s not estimated"
+#~ msgstr "%s non estimé(s)"
+
 #~ msgid "Action"
 #~ msgstr "Action"
 
+#~ msgid "From:"
+#~ msgstr "De :"
+
+#~ msgid "Recipients:"
+#~ msgstr "Destinataires :"
+
+#~ msgid "Subject:"
+#~ msgstr "Sujet :"
+
+#~ msgid "What's new?"
+#~ msgstr "Nouveautés"
+
+#~ msgid "You can use any of the following substitutions in your text"
+#~ msgstr ""
+#~ "Vous pouvez utiliser n'importe quelle substitution parmi la liste "
+#~ "suivante dans le contenu de votre courriel."
+
+#~ msgid "cost"
+#~ msgstr "coût"
+
 #~ msgid "day"
 #~ msgstr "jour"
 
+#~ msgid "emails successfully sent"
+#~ msgstr "courriels envoyés avec succès"
+
+#~ msgid "embed"
+#~ msgstr "embarqué"
+
+#~ msgid "embedding this url is forbidden"
+#~ msgstr "l'inclusion de cette url est interdite"
+
+#~ msgid "error while embedding page"
+#~ msgstr "erreur pendant l'inclusion de la page"
+
+#~ msgid "eta_date"
+#~ msgstr "date de fin"
+
+#~ msgid "expected:"
+#~ msgstr "attendu :"
+
+#~ msgid "external page"
+#~ msgstr "page externe"
+
+#~ msgid "initial estimation %s"
+#~ msgstr "estimation initiale %s"
+
 #~ msgid "jump to selection"
 #~ msgstr "afficher cette sélection"
 
 #~ msgid "log out first"
 #~ msgstr "déconnecter vous d'abord"
 
+#~ msgid "milestone"
+#~ msgstr "jalon"
+
 #~ msgid "month"
 #~ msgstr "mois"
 
+#~ msgid "no related project"
+#~ msgstr "pas de projet rattaché"
+
+#~ msgid "progress"
+#~ msgstr "avancement"
+
+#~ msgid "progress bar"
+#~ msgstr "barre d'avancement"
+
+#~ msgid "project"
+#~ msgstr "projet"
+
+#~ msgid "send email"
+#~ msgstr "envoyer un courriel"
+
+#~ msgid "sioc"
+#~ msgstr "sioc"
+
+#~ msgid "task progression"
+#~ msgstr "avancement de la tâche"
+
 #~ msgid "today"
 #~ msgstr "aujourd'hui"
 
+#~ msgid "todo_by"
+#~ msgstr "à faire par"
+
+#~ msgid "toggle filter"
+#~ msgstr "afficher/masquer le filtre"
+
 #~ msgid "undo last change"
 #~ msgstr "annuler dernier changement"
 
+#~ msgid "violates unique_together constraints (%s)"
+#~ msgstr "violation de contrainte unique_together (%s)"
+
 #~ msgid "week"
 #~ msgstr "semaine"
--- a/schema.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/schema.py	Mon Sep 30 18:07:51 2013 +0200
@@ -697,10 +697,11 @@
     info = warning = error = critical = exception = debug = lambda msg,*a,**kw: None
     # to be defined in concrete classes
     full_rql = None
+    predefined_variables = None
 
     def __init__(self, expression, mainvars, eid):
         """
-        :type mainvars: sequence of RQL variables' names. Can be provided as a 
+        :type mainvars: sequence of RQL variables' names. Can be provided as a
                         comma separated string.
         :param mainvars: names of the variables being selected.
 
@@ -718,7 +719,13 @@
         except RQLSyntaxError:
             raise RQLSyntaxError(expression)
         for mainvar in mainvars:
-            if len(self.rqlst.defined_vars[mainvar].references()) <= 2:
+            # if variable is predefined, an extra reference is inserted
+            # automatically (`VAR eid %(v)s`)
+            if mainvar in self.predefined_variables:
+                min_refs = 3
+            else:
+                min_refs = 2
+            if len(self.rqlst.defined_vars[mainvar].references()) < min_refs:
                 _LOGGER.warn('You did not use the %s variable in your RQL '
                              'expression %s', mainvar, self)
         # syntax tree used by read security (inserted in queries when necessary)
@@ -867,6 +874,8 @@
 # rql expressions for use in permission definition #############################
 
 class ERQLExpression(RQLExpression):
+    predefined_variables = 'UX'
+
     def __init__(self, expression, mainvars=None, eid=None):
         RQLExpression.__init__(self, expression, mainvars or 'X', eid)
 
@@ -922,6 +931,8 @@
 
 
 class RRQLExpression(RQLExpression):
+    predefined_variables = 'USO'
+
     def __init__(self, expression, mainvars=None, eid=None):
         if mainvars is None:
             mainvars = guess_rrqlexpr_mainvars(expression)
@@ -1102,7 +1113,7 @@
     """
     # XXX turns mainvars into a required argument in __init__
     distinct_query = True
- 
+
     def match_condition(self, session, eidfrom, eidto):
         return len(self.exec_query(session, eidfrom, eidto)) <= 1
 
--- a/schemas/workflow.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/schemas/workflow.py	Mon Sep 30 18:07:51 2013 +0200
@@ -89,14 +89,14 @@
     name = String(required=True, indexed=True, internationalizable=True,
                   maxsize=256,
                   constraints=[RQLUniqueConstraint('S name N, S transition_of WF, Y transition_of WF, Y name N', 'Y',
-                                                   _('workflow already have a transition of that name'))])
+                                                   _('workflow already has a transition of that name'))])
     type = String(vocabulary=(_('normal'), _('auto')), default='normal')
     description = RichString(description=_('semantic description of this transition'))
 
     transition_of = SubjectRelation('Workflow', cardinality='1*', composite='object',
                                     description=_('workflow to which this transition belongs'),
                                     constraints=[RQLUniqueConstraint('S name N, Y transition_of O, Y name N', 'Y',
-                                                                     _('workflow already have a transition of that name'))])
+                                                                     _('workflow already has a transition of that name'))])
 
 
 class require_group(RelationDefinition):
--- a/server/migractions.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/server/migractions.py	Mon Sep 30 18:07:51 2013 +0200
@@ -582,7 +582,7 @@
                         print 'dont add %s unique constraint on %s, missing %s' % (
                             ','.join(ut), eschema, name)
                         return False
-                    if not (rschema.final or rschema.final.inlined):
+                    if not (rschema.final or rschema.inlined):
                         (eschema, name)
                         print 'dont add %s unique constraint on %s, %s is neither final nor inlined' % (
                             ','.join(ut), eschema, name)
--- a/server/repository.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/server/repository.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1436,11 +1436,9 @@
                 source.update_entity(session, entity)
                 edited.saved = True
             except UniqueTogetherError as exc:
-                etype, rtypes = exc.args
-                problems = {}
-                for col in rtypes:
-                    problems[col] = session._('violates unique_together constraints (%s)') % (','.join(rtypes))
-                raise ValidationError(entity.eid, problems)
+                userhdlr = session.vreg['adapters'].select(
+                    'IUserFriendlyError', session, entity=entity, exc=exc)
+                userhdlr.raise_user_exception()
             self.system_source.update_info(session, entity, need_fti_update)
             if source.should_call_hooks:
                 if not only_inline_rels:
--- a/server/session.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/server/session.py	Mon Sep 30 18:07:51 2013 +0200
@@ -227,10 +227,10 @@
         self._record = {}
 
     def __enter__(self):
-        self._condition.__enter__()
+        return self._condition.__enter__()
 
     def __exit__(self, *args):
-        self._condition.__exit__(*args)
+        return self._condition.__exit__(*args)
 
     def record(self, txid, cnxset):
         """Inform the tracker that a txid have acquired a cnxset
@@ -1442,6 +1442,7 @@
         self.user._cw = self # XXX remove when "vreg = user._cw.vreg" hack in entity.py is gone
         if not safe:
             self.disable_hook_categories('integrity')
+            self._tx.ctx_count += 1
 
     def __enter__(self):
         return self
--- a/server/sources/datafeed.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/server/sources/datafeed.py	Mon Sep 30 18:07:51 2013 +0200
@@ -445,7 +445,7 @@
         if url.startswith('http'):
             url = self.normalize_url(url)
             self.source.info('GET %s', url)
-            stream = _OPENER.open(url, timeout=self.http_timeout)
+            stream = _OPENER.open(url, timeout=self.source.http_timeout)
         elif url.startswith('file://'):
             stream = open(url[7:])
         else:
@@ -462,7 +462,7 @@
         if extid.startswith('http'):
             try:
                 _OPENER.open(self.normalize_url(extid), # XXX HTTP HEAD request
-                             timeout=self.http_timeout)
+                             timeout=self.source.http_timeout)
             except urllib2.HTTPError as ex:
                 if ex.code == 404:
                     return True
--- a/server/sources/rql2sql.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/server/sources/rql2sql.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1248,6 +1248,8 @@
         except KeyError:
             if lhsalias is None:
                 lhssql = lhsconst.accept(self)
+            elif attr == 'eid':
+                lhssql = lhsvar.accept(self)
             else:
                 lhssql = '%s.%s%s' % (lhsalias, SQL_PREFIX, attr)
         condition = '%s=%s' % (lhssql, (rhsconst or rhsvar).accept(self))
--- a/server/test/unittest_repository.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/server/test/unittest_repository.py	Mon Sep 30 18:07:51 2013 +0200
@@ -558,6 +558,30 @@
             req.create_entity('Affaire', ref=u'AFF02')
             req.execute('SET A duration 10 WHERE A is Affaire')
 
+
+    def test_user_friendly_error(self):
+        from cubicweb.entities.adapters import IUserFriendlyUniqueTogether
+        class MyIUserFriendlyUniqueTogether(IUserFriendlyUniqueTogether):
+            __select__ = IUserFriendlyUniqueTogether.__select__ & is_instance('Societe')
+            def raise_user_exception(self):
+                raise ValidationError(self.entity.eid, {'hip': 'hop'})
+
+        with self.temporary_appobjects(MyIUserFriendlyUniqueTogether):
+            req = self.request()
+            s = req.create_entity('Societe', nom=u'Logilab', type=u'ssll', cp=u'75013')
+            self.commit()
+            with self.assertRaises(ValidationError) as cm:
+                req.create_entity('Societe', nom=u'Logilab', type=u'ssll', cp=u'75013')
+            self.assertEqual(cm.exception.errors, {'hip': 'hop'})
+            self.rollback()
+            req.create_entity('Societe', nom=u'Logilab', type=u'ssll', cp=u'31400')
+            with self.assertRaises(ValidationError) as cm:
+                s.cw_set(cp=u'31400')
+            self.assertEqual(cm.exception.entity, s.eid)
+            self.assertEqual(cm.exception.errors, {'hip': 'hop'})
+            self.rollback()
+
+
 class SchemaDeserialTC(CubicWebTC):
 
     appid = 'data-schemaserial'
--- a/server/test/unittest_rql2sql.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/server/test/unittest_rql2sql.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1541,6 +1541,16 @@
 FROM (SELECT MAX(_A.cw_ordernum) AS C0
 FROM cw_CWAttribute AS _A) AS _T0 LEFT OUTER JOIN (SELECT MAX(_A.cw_ordernum) AS C0
 FROM cw_CWRelation AS _A) AS _T1 ON (_T0.C0=_T1.C0)'''),
+
+            ('''Any TT1,STD,STDD WHERE TT2 identity TT1?
+ WITH TT1,STDD BEING (Any T,SUM(TD) GROUPBY T WHERE T is Affaire, T duration TD, TAG? tags T, TAG name "t"),
+      TT2,STD BEING (Any T,SUM(TD) GROUPBY T WHERE T is Affaire, T duration TD)''',
+             '''SELECT _T0.C0, _T1.C1, _T0.C1
+FROM (SELECT _T.cw_eid AS C0, SUM(_T.cw_duration) AS C1
+FROM cw_Affaire AS _T
+GROUP BY _T.cw_eid) AS _T1 LEFT OUTER JOIN (SELECT _T.cw_eid AS C0, SUM(_T.cw_duration) AS C1
+FROM cw_Affaire AS _T LEFT OUTER JOIN tags_relation AS rel_tags0 ON (rel_tags0.eid_to=_T.cw_eid) LEFT OUTER JOIN cw_Tag AS _TAG ON (rel_tags0.eid_from=_TAG.cw_eid AND _TAG.cw_name=t)
+GROUP BY _T.cw_eid) AS _T0 ON (_T1.C0=_T0.C0)'''),
             )):
             yield t
 
--- a/server/test/unittest_session.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/server/test/unittest_session.py	Mon Sep 30 18:07:51 2013 +0200
@@ -25,6 +25,15 @@
         self.assertFalse(session.running_dbapi_query)
         session.close()
 
+    def test_integrity_hooks(self):
+        with self.repo.internal_session() as session:
+            self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
+            self.assertEqual(set(('integrity',)), session.disabled_hook_categories)
+            self.assertEqual(set(), session.enabled_hook_categories)
+            session.commit()
+            self.assertEqual(HOOKS_ALLOW_ALL, session.hooks_mode)
+            self.assertEqual(set(('integrity',)), session.disabled_hook_categories)
+            self.assertEqual(set(), session.enabled_hook_categories)
 
 class SessionTC(CubicWebTC):
 
--- a/skeleton/debian/control.tmpl	Fri Aug 09 10:00:40 2013 +0200
+++ b/skeleton/debian/control.tmpl	Mon Sep 30 18:07:51 2013 +0200
@@ -8,7 +8,10 @@
 
 Package: %(distname)s
 Architecture: all
-Depends: cubicweb-common (>= %(version)s), ${python:Depends}
+Depends:
+ cubicweb-common (>= %(version)s),
+ ${python:Depends},
+ ${misc:Depends},
 Description: %(shortdesc)s
  CubicWeb is a semantic web application framework.
  .
--- a/skeleton/debian/rules.tmpl	Fri Aug 09 10:00:40 2013 +0200
+++ b/skeleton/debian/rules.tmpl	Mon Sep 30 18:07:51 2013 +0200
@@ -15,10 +15,9 @@
 
 clean:
 	dh_testdir
-	dh_testroot
 	rm -f build-stamp configure-stamp
 	rm -rf build
-	find . -name "*.pyc" | xargs rm -f
+	find . -name "*.pyc" -delete
 	dh_clean
 
 install: build
--- a/sobjects/ldapparser.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/sobjects/ldapparser.py	Mon Sep 30 18:07:51 2013 +0200
@@ -71,7 +71,7 @@
         return {}
 
     def _process(self, etype, sdict):
-        self.warning('fetched %s %s', etype, sdict)
+        self.debug('fetched %s %s', etype, sdict)
         extid = sdict['dn']
         entity = self.extid2entity(extid, etype, **sdict)
         if entity is not None and not self.created_during_pull(entity):
@@ -105,7 +105,7 @@
             for etype, eids in byetype.iteritems():
                 if etype != 'CWUser':
                     continue
-                self.warning('deactivate %s %s entities', len(eids), etype)
+                self.info('deactivate %s %s entities', len(eids), etype)
                 for eid in eids:
                     wf = session.entity_from_eid(eid).cw_adapt_to('IWorkflowable')
                     wf.fire_transition_if_possible('deactivate')
@@ -119,7 +119,7 @@
             wf = entity.cw_adapt_to('IWorkflowable')
             if wf.state == 'deactivated':
                 wf.fire_transition('activate')
-                self.warning('user %s reactivated', entity.login)
+                self.info('user %s reactivated', entity.login)
         mdate = attrs.get('modification_date')
         if not mdate or mdate > entity.modification_date:
             attrs = dict( (k, v) for k, v in attrs.iteritems()
--- a/utils.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/utils.py	Mon Sep 30 18:07:51 2013 +0200
@@ -49,10 +49,10 @@
     """Return a unique identifier string.
 
     if specified, `key` is used to prefix the generated uid so it can be used
-    for instance as a DOM id or as sql table names.
+    for instance as a DOM id or as sql table name.
 
     See uuid.uuid4 documentation for the shape of the generated identifier, but
-    this is basicallly a 32 bits hexadecimal string.
+    this is basically a 32 bits hexadecimal string.
     """
     if key is None:
         return uuid4().hex
--- a/web/application.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/application.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -455,8 +455,8 @@
                 req.update_search_state()
                 result = controller.publish(rset=rset)
             except StatusResponse as ex:
-                warn('StatusResponse is deprecated use req.status_out',
-                     DeprecationWarning)
+                warn('[3.16] StatusResponse is deprecated use req.status_out',
+                     DeprecationWarning, stacklevel=2)
                 result = ex.content
                 req.status_out = ex.status
             except Redirect as ex:
--- a/web/controller.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/controller.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -21,6 +21,7 @@
 
 from logilab.mtconverter import xml_escape
 from logilab.common.registry import yes
+from logilab.common.deprecation import deprecated
 
 from cubicweb.appobject import AppObject
 from cubicweb.mail import format_mail
@@ -103,6 +104,8 @@
         if not self._edited_entity:
             self._edited_entity = entity
 
+    @deprecated('[3.18] call view.set_http_cache_headers then '
+                '.is_client_cache_valid() method and return instead')
     def validate_cache(self, view):
         view.set_http_cache_headers()
         self._cw.validate_cache()
--- a/web/data/cubicweb.facets.js	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/data/cubicweb.facets.js	Mon Sep 30 18:07:51 2013 +0200
@@ -109,7 +109,7 @@
                 $node.loadxhtml(AJAX_BASE_URL, ajaxFuncArgs('render', {
                     'rql': rql
                 },
-                'ctxcomponents', 'edit_box'));
+                'ctxcomponents', 'edit_box'), 'GET', 'swap');
             }
             $node = $('#breadcrumbs');
             if ($node.length) {
--- a/web/formwidgets.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/formwidgets.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -542,6 +542,7 @@
                  'removeinput': self.remove_button % jsnodes
                  })
 
+
 class BitSelect(Select):
     """Select widget for IntField using a vocabulary with bit masks as values.
 
@@ -687,20 +688,21 @@
         # XXX find a way to understand every format
         fmt = req.property_value('ui.date-format')
         fmt = fmt.replace('%Y', 'yy').replace('%m', 'mm').replace('%d', 'dd')
-        req.add_onload(u'cw.jqNode("%s").datepicker('
+        req.add_onload(u'$("#%s").datepicker('
                        '{buttonImage: "%s", dateFormat: "%s", firstDay: 1,'
                        ' showOn: "button", buttonImageOnly: true})' % (
                            domid, req.uiprops['CALENDAR_ICON'], fmt))
-        return self._render_input(form, field, domid)
+        return self._render_input(form, field)
 
-    def _render_input(self, form, field, domid):
+    def _render_input(self, form, field):
         if self.value is None:
             value = self.values(form, field)[0]
         else:
             value = self.value
         attrs = self.attributes(form, field)
         attrs.setdefault('size', unicode(self.default_size))
-        return tags.input(name=domid, value=value, type='text', **attrs)
+        return tags.input(name=field.input_name(form, self.suffix),
+                          value=value, type='text', **attrs)
 
 
 class JQueryTimePicker(JQueryDatePicker):
@@ -718,9 +720,9 @@
 
     def _render(self, form, field, renderer):
         domid = field.dom_id(form, self.suffix)
-        form._cw.add_onload(u'cw.jqNode("%s").timePicker({step: %s, separator: "%s"})' % (
+        form._cw.add_onload(u'$("#%s").timePicker({step: %s, separator: "%s"})' % (
                 domid, self.timesteps, self.separator))
-        return self._render_input(form, field, domid)
+        return self._render_input(form, field)
 
 
 class JQueryDateTimePicker(FieldWidget):
--- a/web/request.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/request.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -765,11 +765,13 @@
             return controller
         return 'view'
 
-    def validate_cache(self):
-        """raise a `StatusResponse` exception if a cached page along the way
-        exists and is still usable.
+    def is_client_cache_valid(self):
+        """check if a client cached page exists (as specified in request
+        headers) and is still usable.
 
-        calls the client-dependant implementation of `_validate_cache`
+        Return False if the page has to be calculated, else True.
+
+        Some response cache headers may be set by this method.
         """
         modified = True
         if self.get_header('Cache-Control') not in ('max-age=0', 'no-cache'):
@@ -784,15 +786,30 @@
                 # Expires header seems to be required by IE7 -- Are you sure ?
                 self.add_header('Expires', 'Sat, 01 Jan 2000 00:00:00 GMT')
             if self.http_method() == 'HEAD':
-                raise StatusResponse(200, '')
-            # /!\ no raise, the function returns and we keep processing the request)
+                self.status_out = 200
+                # XXX replace by True once validate_cache bw compat method is dropped
+                return 200
+            # /!\ no raise, the function returns and we keep processing the request
         else:
             # overwrite headers_out to forge a brand new not-modified response
             self.headers_out = self._forge_cached_headers()
             if self.http_method() in ('HEAD', 'GET'):
-                raise StatusResponse(httplib.NOT_MODIFIED)
+                self.status_out = httplib.NOT_MODIFIED
             else:
-                raise StatusResponse(httplib.PRECONDITION_FAILED)
+                self.status_out = httplib.PRECONDITION_FAILED
+            # XXX replace by True once validate_cache bw compat method is dropped
+            return self.status_out
+        # XXX replace by False once validate_cache bw compat method is dropped
+        return None
+
+    @deprecated('[3.18] use .is_client_cache_valid() method instead')
+    def validate_cache(self):
+        """raise a `StatusResponse` exception if a cached page along the way
+        exists and is still usable.
+        """
+        status_code = self.is_client_cache_valid()
+        if status_code is not None:
+            raise StatusResponse(status_code)
 
     # abstract methods to override according to the web front-end #############
 
--- a/web/test/unittest_http.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/test/unittest_http.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1,15 +1,30 @@
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
 from logilab.common.testlib import TestCase, unittest_main, tag, Tags
 
-from cubicweb.web import StatusResponse
 from cubicweb.devtools.fake import FakeRequest
 
 
 def _test_cache(hin, hout, method='GET'):
-    """forge and process a request
+    """forge and process an HTTP request using given headers in/out and method,
+    then return it once its .is_client_cache_valid() method has been called.
 
-    return status code and the request object
-
-    status is None is no cache is involved
+    req.status_out is None if the page should have been calculated.
     """
     # forge request
     req = FakeRequest(method=method)
@@ -18,12 +33,9 @@
     for key, value in hout:
         req.headers_out.addRawHeader(key, str(value))
     # process
-    status = None
-    try:
-        req.validate_cache()
-    except StatusResponse as ex:
-        status = ex.status
-    return status, req
+    req.status_out = None
+    req.is_client_cache_valid()
+    return req
 
 class HTTPCache(TestCase):
     """Check that the http cache logiac work as expected
@@ -48,42 +60,42 @@
     def test_IN_none_OUT_none(self):
         #: test that no caching is requested when not data is available
         #: on any side
-        status, req =_test_cache((),())
-        self.assertIsNone(status)
+        req =_test_cache((), ())
+        self.assertIsNone(req.status_out)
 
     def test_IN_Some_OUT_none(self):
         #: test that no caching is requested when no data is available
         #: server (origin) side
         hin = [('if-modified-since','Sat, 14 Apr 2012 14:39:32 GM'),
               ]
-        status, req = _test_cache(hin, ())
-        self.assertIsNone(status)
+        req = _test_cache(hin, ())
+        self.assertIsNone(req.status_out)
         hin = [('if-none-match','babar/huitre'),
               ]
-        status, req = _test_cache(hin, ())
-        self.assertIsNone(status)
+        req = _test_cache(hin, ())
+        self.assertIsNone(req.status_out)
         hin = [('if-modified-since','Sat, 14 Apr 2012 14:39:32 GM'),
                ('if-none-match','babar/huitre'),
               ]
-        status, req = _test_cache(hin, ())
-        self.assertIsNone(status)
+        req = _test_cache(hin, ())
+        self.assertIsNone(req.status_out)
 
     def test_IN_none_OUT_Some(self):
         #: test that no caching is requested when no data is provided
         #: by the client
         hout = [('last-modified','Sat, 14 Apr 2012 14:39:32 GM'),
                ]
-        status, req = _test_cache((), hout)
-        self.assertIsNone(status)
+        req = _test_cache((), hout)
+        self.assertIsNone(req.status_out)
         hout = [('etag','babar/huitre'),
                ]
-        status, req = _test_cache((), hout)
-        self.assertIsNone(status)
+        req = _test_cache((), hout)
+        self.assertIsNone(req.status_out)
         hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'),
                 ('etag','babar/huitre'),
                ]
-        status, req = _test_cache((), hout)
-        self.assertIsNone(status)
+        req = _test_cache((), hout)
+        self.assertIsNone(req.status_out)
 
     @tag('last_modified')
     def test_last_modified_newer(self):
@@ -93,8 +105,8 @@
                ]
         hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(None, status, 'origin is newer than client')
+        req = _test_cache(hin, hout)
+        self.assertCache(None, req.status_out, 'origin is newer than client')
 
     @tag('last_modified')
     def test_last_modified_older(self):
@@ -103,8 +115,8 @@
                ]
         hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(304, status, 'origin is older than client')
+        req = _test_cache(hin, hout)
+        self.assertCache(304, req.status_out, 'origin is older than client')
 
     @tag('last_modified')
     def test_last_modified_same(self):
@@ -113,8 +125,8 @@
                ]
         hout = [('last-modified', 'Sat, 14 Apr 2012 14:39:32 GM'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(304, status, 'origin is equal to client')
+        req = _test_cache(hin, hout)
+        self.assertCache(304, req.status_out, 'origin is equal to client')
 
     @tag('etag')
     def test_etag_mismatch(self):
@@ -124,8 +136,8 @@
                ]
         hout = [('etag', 'celestine'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(None, status, 'etag mismatch')
+        req = _test_cache(hin, hout)
+        self.assertCache(None, req.status_out, 'etag mismatch')
 
     @tag('etag')
     def test_etag_match(self):
@@ -134,23 +146,23 @@
                ]
         hout = [('etag', 'babar'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(304, status, 'etag match')
+        req = _test_cache(hin, hout)
+        self.assertCache(304, req.status_out, 'etag match')
         # etag match in multiple
         hin  = [('if-none-match', 'loutre'),
                 ('if-none-match', 'babar'),
                ]
         hout = [('etag', 'babar'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(304, status, 'etag match in multiple')
+        req = _test_cache(hin, hout)
+        self.assertCache(304, req.status_out, 'etag match in multiple')
         # client use "*" as etag
         hin  = [('if-none-match', '*'),
                ]
         hout = [('etag', 'babar'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(304, status, 'client use "*" as etag')
+        req = _test_cache(hin, hout)
+        self.assertCache(304, req.status_out, 'client use "*" as etag')
 
     @tag('etag', 'last_modified')
     def test_both(self):
@@ -162,8 +174,8 @@
         hout = [('etag', 'loutre'),
                 ('last-modified', 'Sat, 15 Apr 2012 14:39:32 GM'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(None, status, 'both wrong')
+        req = _test_cache(hin, hout)
+        self.assertCache(None, req.status_out, 'both wrong')
 
     @tag('etag', 'last_modified')
     def test_both_etag_mismatch(self):
@@ -174,8 +186,8 @@
         hout = [('etag', 'loutre'),
                 ('last-modified', 'Sat, 13 Apr 2012 14:39:32 GM'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(None, status, 'both  but etag mismatch')
+        req = _test_cache(hin, hout)
+        self.assertCache(None, req.status_out, 'both  but etag mismatch')
 
     @tag('etag', 'last_modified')
     def test_both_but_modified(self):
@@ -186,8 +198,8 @@
         hout = [('etag', 'babar'),
                 ('last-modified', 'Sat, 15 Apr 2012 14:39:32 GM'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(None, status, 'both  but modified')
+        req = _test_cache(hin, hout)
+        self.assertCache(None, req.status_out, 'both  but modified')
 
     @tag('etag', 'last_modified')
     def test_both_ok(self):
@@ -198,8 +210,8 @@
         hout = [('etag', 'babar'),
                 ('last-modified', 'Sat, 13 Apr 2012 14:39:32 GM'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(304, status, 'both ok')
+        req = _test_cache(hin, hout)
+        self.assertCache(304, req.status_out, 'both ok')
 
     @tag('etag', 'HEAD')
     def test_head_verb(self):
@@ -210,15 +222,15 @@
                ]
         hout = [('etag', 'rhino/really-not-babar'),
                ]
-        status, req = _test_cache(hin, hout, method='HEAD')
-        self.assertCache(200, status, 'modifier HEAD verb')
+        req = _test_cache(hin, hout, method='HEAD')
+        self.assertCache(200, req.status_out, 'modifier HEAD verb')
         # not modified
         hin  = [('if-none-match', 'babar'),
                ]
         hout = [('etag', 'babar'),
                ]
-        status, req = _test_cache(hin, hout, method='HEAD')
-        self.assertCache(304, status, 'not modifier HEAD verb')
+        req = _test_cache(hin, hout, method='HEAD')
+        self.assertCache(304, req.status_out, 'not modifier HEAD verb')
 
     @tag('etag', 'POST')
     def test_post_verb(self):
@@ -227,15 +239,15 @@
                ]
         hout = [('etag', 'rhino/really-not-babar'),
                ]
-        status, req = _test_cache(hin, hout, method='POST')
-        self.assertCache(None, status, 'modifier HEAD verb')
+        req = _test_cache(hin, hout, method='POST')
+        self.assertCache(None, req.status_out, 'modifier HEAD verb')
         # not modified
         hin  = [('if-none-match', 'babar'),
                ]
         hout = [('etag', 'babar'),
                ]
-        status, req = _test_cache(hin, hout, method='POST')
-        self.assertCache(412, status, 'not modifier HEAD verb')
+        req = _test_cache(hin, hout, method='POST')
+        self.assertCache(412, req.status_out, 'not modifier HEAD verb')
 
     @tag('expires')
     def test_expires_added(self):
@@ -246,8 +258,8 @@
                ]
         hout = [('etag', 'rhino/really-not-babar'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(None, status, 'modifier HEAD verb')
+        req = _test_cache(hin, hout)
+        self.assertCache(None, req.status_out, 'modifier HEAD verb')
         value = req.headers_out.getHeader('expires')
         self.assertIsNotNone(value)
 
@@ -258,8 +270,8 @@
                ]
         hout = [('etag', 'babar'),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(304, status, 'not modifier HEAD verb')
+        req = _test_cache(hin, hout)
+        self.assertCache(304, req.status_out, 'not modifier HEAD verb')
         value = req.headers_out.getHeader('expires')
         self.assertIsNone(value)
 
@@ -272,8 +284,8 @@
         hout = [('etag', 'rhino/really-not-babar'),
                 ('expires', DATE),
                ]
-        status, req = _test_cache(hin, hout)
-        self.assertCache(None, status, 'not modifier HEAD verb')
+        req = _test_cache(hin, hout)
+        self.assertCache(None, req.status_out, 'not modifier HEAD verb')
         value = req.headers_out.getRawHeaders('expires')
         self.assertEqual(value, [DATE])
 
--- a/web/test/unittest_views_staticcontrollers.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/test/unittest_views_staticcontrollers.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1,3 +1,4 @@
+from logilab.common import tempattr
 from logilab.common.testlib import tag, Tags
 from cubicweb.devtools.testlib import CubicWebTC
 
@@ -29,6 +30,46 @@
         self.assertEqual(304, req.status_out)
 
 
+
+class DataControllerTC(CubicWebTC):
+
+    tags = CubicWebTC.tags | Tags('static_controller', 'data', 'http')
+
+    def _publish_static_files(self, url, header={}):
+        req = self.request(headers=header)
+        req._url = url
+        return self.app_handle_request(req, url), req
+
+    def _check_datafile_ok(self, fname):
+        _, req = self._publish_static_files(fname)
+        self.assertEqual(200, req.status_out)
+        self.assertIn('last-modified', req.headers_out)
+        next_headers = {
+            'if-modified-since': req.get_response_header('last-modified', raw=True),
+        }
+        _, req = self._publish_static_files(fname, next_headers)
+        self.assertEqual(304, req.status_out)
+
+    def _check_no_datafile(self, fname):
+        _, req = self._publish_static_files(fname)
+        self.assertEqual(404, req.status_out)
+
+    def test_static_data_mode(self):
+        hash = self.vreg.config.instance_md5_version()
+        self.assertEqual(32, len(hash))
+
+        with tempattr(self.vreg.config, 'mode', 'test'):
+            self._check_datafile_ok('data/cubicweb.css')
+            self._check_no_datafile('data/does/not/exist')
+            self._check_no_datafile('data/%s/cubicweb.css' % ('0'*len(hash)))
+
+        with tempattr(self.vreg.config, 'mode', 'notest'):
+            self._check_datafile_ok('data/cubicweb.css')
+            self._check_datafile_ok('data/%s/cubicweb.css' % hash)
+            self._check_no_datafile('data/does/not/exist')
+            self._check_no_datafile('data/%s/cubicweb.css' % ('0'*len(hash)))
+
+
 class ConcatFilesTC(CubicWebTC):
 
     tags = CubicWebTC.tags | Tags('static_controller', 'concat')
--- a/web/views/ajaxcontroller.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/views/ajaxcontroller.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -371,7 +371,8 @@
         vid = req.form.get('fallbackvid', 'noresult')
         viewobj = self._cw.vreg['views'].select(vid, req, rset=rset)
     viewobj.set_http_cache_headers()
-    req.validate_cache()
+    if req.is_client_cache_valid():
+        return ''
     return self._call_view(viewobj, paginate=req.form.pop('paginate', False))
 
 
--- a/web/views/authentication.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/views/authentication.py	Mon Sep 30 18:07:51 2013 +0200
@@ -71,7 +71,10 @@
         """
         pass
 
-WebAuthInfoRetreiver = class_renamed('WebAuthInfoRetreiver', WebAuthInfoRetriever)
+WebAuthInfoRetreiver = class_renamed(
+    'WebAuthInfoRetreiver', WebAuthInfoRetriever,
+    '[3.17] WebAuthInfoRetreiver had been renamed into WebAuthInfoRetriever '
+    '("ie" instead of "ei")')
 
 
 class LoginPasswordRetriever(WebAuthInfoRetriever):
@@ -93,7 +96,10 @@
     def revalidate_login(self, req):
         return req.get_authorization()[0]
 
-LoginPasswordRetreiver = class_renamed('LoginPasswordRetreiver', LoginPasswordRetriever)
+LoginPasswordRetreiver = class_renamed(
+    'LoginPasswordRetreiver', LoginPasswordRetriever,
+    '[3.17] LoginPasswordRetreiver had been renamed into LoginPasswordRetriever '
+    '("ie" instead of "ei")')
 
 
 class RepositoryAuthenticationManager(AbstractAuthenticationManager):
--- a/web/views/basecontrollers.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/views/basecontrollers.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -128,7 +128,9 @@
         """publish a request, returning an encoded string"""
         view, rset = self._select_view_and_rset(rset)
         self.add_to_breadcrumbs(view)
-        self.validate_cache(view)
+        view.set_http_cache_headers()
+        if self._cw.is_client_cache_valid():
+            return ''
         template = self.appli.main_template_id(self._cw)
         return self._cw.vreg['views'].main_template(self._cw, template,
                                                     rset=rset, view=view)
--- a/web/views/primary.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/views/primary.py	Mon Sep 30 18:07:51 2013 +0200
@@ -109,7 +109,7 @@
         """
         return []
 
-    def entity_call(self, entity):
+    def entity_call(self, entity, **kwargs):
         entity.complete()
         uicfg_reg = self._cw.vreg['uicfg']
         if self.rsection is None:
--- a/web/views/staticcontrollers.py	Fri Aug 09 10:00:40 2013 +0200
+++ b/web/views/staticcontrollers.py	Mon Sep 30 18:07:51 2013 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -20,7 +20,6 @@
 - /data/...
 - /static/...
 - /fckeditor/...
-
 """
 
 import os
@@ -32,7 +31,7 @@
 from datetime import datetime, timedelta
 from logging import getLogger
 
-from cubicweb import Unauthorized
+from cubicweb import Forbidden
 from cubicweb.web import NotFound
 from cubicweb.web.http_headers import generateDateTime
 from cubicweb.web.controller import Controller
@@ -60,7 +59,7 @@
         if osp.isdir(path):
             if self.directory_listing_allowed:
                 return u''
-            raise Unauthorized(path)
+            raise Forbidden(path)
         if not osp.isfile(path):
             raise NotFound()
         if not debugmode:
@@ -78,7 +77,8 @@
         #
         # Real production environment should use dedicated static file serving.
         self._cw.set_header('last-modified', generateDateTime(os.stat(path).st_mtime))
-        self._cw.validate_cache()
+        if self._cw.is_client_cache_valid():
+            return ''
         # XXX elif uri.startswith('/https/'): uri = uri[6:]
         mimetype, encoding = mimetypes.guess_type(path)
         if mimetype is None:
@@ -199,7 +199,12 @@
             filepath = self.concat_files_registry.concat_cached_filepath(paths)
         else:
             # skip leading '/data/' and url params
-            relpath = relpath[len(self.base_datapath):].split('?', 1)[0]
+            if relpath.startswith(self.base_datapath):
+                prefix = self.base_datapath
+            else:
+                prefix = 'data/'
+            relpath = relpath[len(prefix):]
+            relpath = relpath.split('?', 1)[0]
             dirpath, rid = config.locate_resource(relpath)
             if dirpath is None:
                 raise NotFound()