--- 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()