# HG changeset patch # User Aurelien Campeas # Date 1380557271 -7200 # Node ID 593b29325f6870e35a1f39eb82e221ed0e22d198 # Parent c027ed79f1cecd5fac202e8dc3b1f98fda7a17b7# Parent 483181543899a762d068cfdc3ae751b54adc3f14 [merge] backport 3.17 fixes into the future 3.18 diff -r c027ed79f1ce -r 593b29325f68 __pkginfo__.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" diff -r c027ed79f1ce -r 593b29325f68 cubicweb.spec --- 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 diff -r c027ed79f1ce -r 593b29325f68 debian/changelog --- 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 Thu, 26 Sep 2013 15:13:39 +0200 + cubicweb (3.17.6-1) unstable; urgency=low * new upstream release diff -r c027ed79f1ce -r 593b29325f68 debian/rules --- 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-*/ diff -r c027ed79f1ce -r 593b29325f68 devtools/__init__.py --- 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): diff -r c027ed79f1ce -r 593b29325f68 devtools/devctl.py --- 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: diff -r c027ed79f1ce -r 593b29325f68 entities/test/unittest_wfobjs.py --- 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): diff -r c027ed79f1ce -r 593b29325f68 i18n/de.po --- 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." diff -r c027ed79f1ce -r 593b29325f68 i18n/en.po --- 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)" diff -r c027ed79f1ce -r 593b29325f68 i18n/es.po --- 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" diff -r c027ed79f1ce -r 593b29325f68 i18n/fr.po --- 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" diff -r c027ed79f1ce -r 593b29325f68 schema.py --- 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 diff -r c027ed79f1ce -r 593b29325f68 schemas/workflow.py --- 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): diff -r c027ed79f1ce -r 593b29325f68 server/migractions.py --- 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) diff -r c027ed79f1ce -r 593b29325f68 server/repository.py --- 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: diff -r c027ed79f1ce -r 593b29325f68 server/session.py --- 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 diff -r c027ed79f1ce -r 593b29325f68 server/sources/datafeed.py --- 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 diff -r c027ed79f1ce -r 593b29325f68 server/sources/rql2sql.py --- 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)) diff -r c027ed79f1ce -r 593b29325f68 server/test/unittest_repository.py --- 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' diff -r c027ed79f1ce -r 593b29325f68 server/test/unittest_rql2sql.py --- 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 diff -r c027ed79f1ce -r 593b29325f68 server/test/unittest_session.py --- 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): diff -r c027ed79f1ce -r 593b29325f68 skeleton/debian/control.tmpl --- 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. . diff -r c027ed79f1ce -r 593b29325f68 skeleton/debian/rules.tmpl --- 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 diff -r c027ed79f1ce -r 593b29325f68 sobjects/ldapparser.py --- 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() diff -r c027ed79f1ce -r 593b29325f68 utils.py --- 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 diff -r c027ed79f1ce -r 593b29325f68 web/application.py --- 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: diff -r c027ed79f1ce -r 593b29325f68 web/controller.py --- 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() diff -r c027ed79f1ce -r 593b29325f68 web/data/cubicweb.facets.js --- 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) { diff -r c027ed79f1ce -r 593b29325f68 web/formwidgets.py --- 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): diff -r c027ed79f1ce -r 593b29325f68 web/request.py --- 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 ############# diff -r c027ed79f1ce -r 593b29325f68 web/test/unittest_http.py --- 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 . 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]) diff -r c027ed79f1ce -r 593b29325f68 web/test/unittest_views_staticcontrollers.py --- 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') diff -r c027ed79f1ce -r 593b29325f68 web/views/ajaxcontroller.py --- 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)) diff -r c027ed79f1ce -r 593b29325f68 web/views/authentication.py --- 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): diff -r c027ed79f1ce -r 593b29325f68 web/views/basecontrollers.py --- 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) diff -r c027ed79f1ce -r 593b29325f68 web/views/primary.py --- 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: diff -r c027ed79f1ce -r 593b29325f68 web/views/staticcontrollers.py --- 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()