# HG changeset patch # User Sylvain Thénault # Date 1350049138 -7200 # Node ID e20057a9ceea7740f65c6e3a92e6084bdd1f4e70 # Parent 40b3634eebe8d6f19a342f9c33a9bfdd46d5cd68# Parent 6e5d8512b07dca7d1d87121986e34671acd547e8 dummy merge diff -r 6e5d8512b07d -r e20057a9ceea __init__.py --- a/__init__.py Tue Sep 11 22:32:01 2012 +0200 +++ b/__init__.py Fri Oct 12 15:38:58 2012 +0200 @@ -199,3 +199,26 @@ CW_EVENT_MANAGER.bind(event, func, *args, **kwargs) return func return _decorator + + +from yams.schema import role_name as rname + +def validation_error(entity, errors, substitutions=None, i18nvalues=None): + """easy way to retrieve a :class:`cubicweb.ValidationError` for an entity or eid. + + You may also have 2-tuple as error keys, :func:`yams.role_name` will be + called automatically for them. + + Messages in errors **should not be translated yet**, though marked for + internationalization. You may give an additional substition dictionary that + will be used for interpolation after the translation. + """ + if substitutions is None: + # set empty dict else translation won't be done for backward + # compatibility reason (see ValidationError.tr method) + substitutions = {} + for key in errors.keys(): + if isinstance(key, tuple): + errors[rname(*key)] = errors.pop(key) + return ValidationError(getattr(entity, 'eid', entity), errors, + substitutions, i18nvalues) diff -r 6e5d8512b07d -r e20057a9ceea _exceptions.py --- a/_exceptions.py Tue Sep 11 22:32:01 2012 +0200 +++ b/_exceptions.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -19,7 +19,7 @@ __docformat__ = "restructuredtext en" -from yams import ValidationError +from yams import ValidationError as ValidationError # abstract exceptions ######################################################### diff -r 6e5d8512b07d -r e20057a9ceea devtools/fake.py --- a/devtools/fake.py Tue Sep 11 22:32:01 2012 +0200 +++ b/devtools/fake.py Fri Oct 12 15:38:58 2012 +0200 @@ -155,6 +155,12 @@ def set_entity_cache(self, entity): pass + def security_enabled(self, read=False, write=False): + class FakeCM(object): + def __enter__(self): pass + def __exit__(self, exctype, exc, traceback): pass + return FakeCM() + # for use with enabled_security context manager read_security = write_security = True def init_security(self, *args): diff -r 6e5d8512b07d -r e20057a9ceea devtools/testlib.py --- a/devtools/testlib.py Tue Sep 11 22:32:01 2012 +0200 +++ b/devtools/testlib.py Fri Oct 12 15:38:58 2012 +0200 @@ -47,7 +47,7 @@ from cubicweb import cwconfig, dbapi, devtools, web, server from cubicweb.sobjects import notification from cubicweb.web import Redirect, application -from cubicweb.server.session import Session, security_enabled +from cubicweb.server.session import Session from cubicweb.server.hook import SendMailOp from cubicweb.devtools import SYSTEM_ENTITIES, SYSTEM_RELATIONS, VIEW_VALIDATORS from cubicweb.devtools import BASE_URL, fake, htmlparser, DEFAULT_EMPTY_DB_ID @@ -1050,7 +1050,7 @@ """this method populates the database with `how_many` entities of each possible type. It also inserts random relations between them """ - with security_enabled(self.session, read=False, write=False): + with self.session.security_enabled(read=False, write=False): self._auto_populate(how_many) def _auto_populate(self, how_many): diff -r 6e5d8512b07d -r e20057a9ceea entities/test/unittest_wfobjs.py --- a/entities/test/unittest_wfobjs.py Tue Sep 11 22:32:01 2012 +0200 +++ b/entities/test/unittest_wfobjs.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 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 @@ from cubicweb import ValidationError from cubicweb.devtools.testlib import CubicWebTC -from cubicweb.server.session import security_enabled def add_wf(self, etype, name=None, default=False): @@ -126,8 +125,9 @@ self.assertEqual(trs[0].destination(None).name, u'deactivated') # test a std user get no possible transition cnx = self.login('member') + req = self.request() # fetch the entity using the new session - trs = list(cnx.user().cw_adapt_to('IWorkflowable').possible_transitions()) + trs = list(req.user.cw_adapt_to('IWorkflowable').possible_transitions()) self.assertEqual(len(trs), 0) cnx.close() @@ -154,7 +154,7 @@ wf = add_wf(self, 'CWUser') s = wf.add_state(u'foo', initial=True) self.commit() - with security_enabled(self.session, write=False): + with self.session.security_enabled(write=False): with self.assertRaises(ValidationError) as cm: self.session.execute('SET X in_state S WHERE X eid %(x)s, S eid %(s)s', {'x': self.user().eid, 's': s.eid}) @@ -173,7 +173,7 @@ def test_goback_transition(self): req = self.request() - wf = self.session.user.cw_adapt_to('IWorkflowable').current_workflow + wf = req.user.cw_adapt_to('IWorkflowable').current_workflow asleep = wf.add_state('asleep') wf.add_transition('rest', (wf.state_by_name('activated'), wf.state_by_name('deactivated')), @@ -212,7 +212,7 @@ req = self.request() iworkflowable = req.entity_from_eid(self.member.eid).cw_adapt_to('IWorkflowable') iworkflowable.fire_transition('deactivate') - cnx.commit() + req.cu.commit() with self.assertRaises(ValidationError) as cm: iworkflowable.fire_transition('activate') self.assertEqual(cm.exception.errors, {'by_transition-subject': "transition may not be fired"}) @@ -556,13 +556,12 @@ def setUp(self): CubicWebTC.setUp(self) - self.wf = self.session.user.cw_adapt_to('IWorkflowable').current_workflow - self.session.set_cnxset() + req = self.request() + self.wf = req.user.cw_adapt_to('IWorkflowable').current_workflow self.s_activated = self.wf.state_by_name('activated').eid self.s_deactivated = self.wf.state_by_name('deactivated').eid self.s_dummy = self.wf.add_state(u'dummy').eid self.wf.add_transition(u'dummy', (self.s_deactivated,), self.s_dummy) - req = self.request() ueid = self.create_user(req, 'stduser', commit=False).eid # test initial state is set rset = self.execute('Any N WHERE S name N, X in_state S, X eid %(x)s', diff -r 6e5d8512b07d -r e20057a9ceea hooks/integrity.py --- a/hooks/integrity.py Tue Sep 11 22:32:01 2012 +0200 +++ b/hooks/integrity.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -20,12 +20,11 @@ """ __docformat__ = "restructuredtext en" +_ = unicode from threading import Lock -from yams.schema import role_name - -from cubicweb import ValidationError +from cubicweb import validation_error from cubicweb.schema import (META_RTYPES, WORKFLOW_RTYPES, RQLConstraint, RQLUniqueConstraint) from cubicweb.predicates import is_instance @@ -87,11 +86,11 @@ continue if not session.execute(self.base_rql % rtype, {'x': eid}): etype = session.describe(eid)[0] - _ = session._ msg = _('at least one relation %(rtype)s is required on ' '%(etype)s (%(eid)s)') - msg %= {'rtype': _(rtype), 'etype': _(etype), 'eid': eid} - raise ValidationError(eid, {role_name(rtype, self.role): msg}) + raise validation_error(eid, {(rtype, self.role): msg}, + {'rtype': rtype, 'etype': etype, 'eid': eid}, + ['rtype', 'etype']) class _CheckSRelationOp(_CheckRequiredRelationOperation): @@ -231,9 +230,9 @@ rql = '%s X WHERE X %s %%(val)s' % (entity.e_schema, attr) rset = self._cw.execute(rql, {'val': val}) if rset and rset[0][0] != entity.eid: - msg = self._cw._('the value "%s" is already used, use another one') - qname = role_name(attr, 'subject') - raise ValidationError(entity.eid, {qname: msg % val}) + msg = _('the value "%s" is already used, use another one') + raise validation_error(entity, {(attr, 'subject'): msg}, + (val,)) class DontRemoveOwnersGroupHook(IntegrityHook): @@ -246,15 +245,12 @@ def __call__(self): entity = self.entity if self.event == 'before_delete_entity' and entity.name == 'owners': - msg = self._cw._('can\'t be deleted') - raise ValidationError(entity.eid, {None: msg}) + raise validation_error(entity, {None: _("can't be deleted")}) elif self.event == 'before_update_entity' \ and 'name' in entity.cw_edited: oldname, newname = entity.cw_edited.oldnewvalue('name') if oldname == 'owners' and newname != oldname: - qname = role_name('name', 'subject') - msg = self._cw._('can\'t be changed') - raise ValidationError(entity.eid, {qname: msg}) + raise validation_error(entity, {('name', 'subject'): _("can't be changed")}) class TidyHtmlFields(IntegrityHook): diff -r 6e5d8512b07d -r e20057a9ceea hooks/syncschema.py --- a/hooks/syncschema.py Tue Sep 11 22:32:01 2012 +0200 +++ b/hooks/syncschema.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -24,6 +24,7 @@ """ __docformat__ = "restructuredtext en" +_ = unicode from copy import copy from yams.schema import BASE_TYPES, RelationSchema, RelationDefinitionSchema @@ -31,7 +32,7 @@ from logilab.common.decorators import clear_cache -from cubicweb import ValidationError +from cubicweb import validation_error from cubicweb.predicates import is_instance from cubicweb.schema import (SCHEMA_TYPES, META_RTYPES, VIRTUAL_RTYPES, CONSTRAINTS, ETYPE_NAME_MAP, display_name) @@ -127,10 +128,9 @@ if attr in ro_attrs: origval, newval = entity.cw_edited.oldnewvalue(attr) if newval != origval: - errors[attr] = session._("can't change the %s attribute") % \ - display_name(session, attr) + errors[attr] = _("can't change this attribute") if errors: - raise ValidationError(entity.eid, errors) + raise validation_error(entity, errors) class _MockEntity(object): # XXX use a named tuple with python 2.6 @@ -913,7 +913,7 @@ # final entities can't be deleted, don't care about that name = self.entity.name if name in CORE_TYPES: - raise ValidationError(self.entity.eid, {None: self._cw._('can\'t be deleted')}) + raise validation_error(self.entity, {None: _("can't be deleted")}) # delete every entities of this type if name not in ETYPE_NAME_MAP: self._cw.execute('DELETE %s X' % name) @@ -983,7 +983,7 @@ def __call__(self): name = self.entity.name if name in CORE_TYPES: - raise ValidationError(self.entity.eid, {None: self._cw._('can\'t be deleted')}) + raise validation_error(self.entity, {None: _("can't be deleted")}) # delete relation definitions using this relation type self._cw.execute('DELETE CWAttribute X WHERE X relation_type Y, Y eid %(x)s', {'x': self.entity.eid}) diff -r 6e5d8512b07d -r e20057a9ceea hooks/syncsession.py --- a/hooks/syncsession.py Tue Sep 11 22:32:01 2012 +0200 +++ b/hooks/syncsession.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -18,9 +18,9 @@ """Core hooks: synchronize living session on persistent data changes""" __docformat__ = "restructuredtext en" +_ = unicode -from yams.schema import role_name -from cubicweb import UnknownProperty, ValidationError, BadConnectionId +from cubicweb import UnknownProperty, BadConnectionId, validation_error from cubicweb.predicates import is_instance from cubicweb.server import hook @@ -165,13 +165,11 @@ try: value = session.vreg.typed_value(key, value) except UnknownProperty: - qname = role_name('pkey', 'subject') - msg = session._('unknown property key %s') % key - raise ValidationError(self.entity.eid, {qname: msg}) + msg = _('unknown property key %s') + raise validation_error(self.entity, {('pkey', 'subject'): msg}, (key,)) except ValueError, ex: - qname = role_name('value', 'subject') - raise ValidationError(self.entity.eid, - {qname: session._(str(ex))}) + raise validation_error(self.entity, + {('value', 'subject'): str(ex)}) if not session.user.matching_groups('managers'): session.add_relation(self.entity.eid, 'for_user', session.user.eid) else: @@ -196,8 +194,7 @@ except UnknownProperty: return except ValueError, ex: - qname = role_name('value', 'subject') - raise ValidationError(entity.eid, {qname: session._(str(ex))}) + raise validation_error(entity, {('value', 'subject'): str(ex)}) if entity.for_user: for session_ in get_user_sessions(session.repo, entity.for_user[0].eid): _ChangeCWPropertyOp(session, cwpropdict=session_.user.properties, @@ -237,10 +234,8 @@ key, value = session.execute('Any K,V WHERE P eid %(x)s,P pkey K,P value V', {'x': eidfrom})[0] if session.vreg.property_info(key)['sitewide']: - qname = role_name('for_user', 'subject') - msg = session._("site-wide property can't be set for user") - raise ValidationError(eidfrom, - {qname: msg}) + msg = _("site-wide property can't be set for user") + raise validation_error(eidfrom, {('for_user', 'subject'): msg}) for session_ in get_user_sessions(session.repo, self.eidto): _ChangeCWPropertyOp(session, cwpropdict=session_.user.properties, key=key, value=value) diff -r 6e5d8512b07d -r e20057a9ceea hooks/syncsources.py --- a/hooks/syncsources.py Tue Sep 11 22:32:01 2012 +0200 +++ b/hooks/syncsources.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2010-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2010-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -17,12 +17,13 @@ # with CubicWeb. If not, see . """hooks for repository sources synchronization""" +_ = unicode + from socket import gethostname from logilab.common.decorators import clear_cache -from yams.schema import role_name -from cubicweb import ValidationError +from cubicweb import validation_error from cubicweb.predicates import is_instance from cubicweb.server import SOURCE_TYPES, hook @@ -46,12 +47,15 @@ try: sourcecls = SOURCE_TYPES[self.entity.type] except KeyError: - msg = self._cw._('unknown source type') - raise ValidationError(self.entity.eid, - {role_name('type', 'subject'): msg}) - sourcecls.check_conf_dict(self.entity.eid, self.entity.host_config, - fail_if_unknown=not self._cw.vreg.config.repairing) - SourceAddedOp(self._cw, entity=self.entity) + msg = _('Unknown source type') + raise validation_error(self.entity, {('type', 'subject'): msg}) + # ignore creation of the system source done during database + # initialisation, as config for this source is in a file and handling + # is done separatly (no need for the operation either) + if self.entity.name != 'system': + sourcecls.check_conf_dict(self.entity.eid, self.entity.host_config, + fail_if_unknown=not self._cw.vreg.config.repairing) + SourceAddedOp(self._cw, entity=self.entity) class SourceRemovedOp(hook.Operation): @@ -65,7 +69,8 @@ events = ('before_delete_entity',) def __call__(self): if self.entity.name == 'system': - raise ValidationError(self.entity.eid, {None: 'cant remove system source'}) + msg = _("You cannot remove the system source") + raise validation_error(self.entity, {None: msg}) SourceRemovedOp(self._cw, uri=self.entity.name) @@ -116,11 +121,18 @@ __select__ = SourceHook.__select__ & is_instance('CWSource') events = ('before_update_entity',) def __call__(self): - if 'config' in self.entity.cw_edited: - SourceConfigUpdatedOp.get_instance(self._cw).add_data(self.entity) if 'name' in self.entity.cw_edited: oldname, newname = self.entity.cw_edited.oldnewvalue('name') + if oldname == 'system': + msg = _("You cannot rename the system source") + raise validation_error(self.entity, {('name', 'subject'): msg}) SourceRenamedOp(self._cw, oldname=oldname, newname=newname) + if 'config' in self.entity.cw_edited: + if self.entity.name == 'system' and self.entity.config: + msg = _("Configuration of the system source goes to " + "the 'sources' file, not in the database") + raise validation_error(self.entity, {('config', 'subject'): msg}) + SourceConfigUpdatedOp.get_instance(self._cw).add_data(self.entity) class SourceHostConfigUpdatedHook(SourceHook): @@ -154,8 +166,8 @@ events = ('before_add_relation',) def __call__(self): if not self._cw.added_in_transaction(self.eidfrom): - msg = self._cw._("can't change this relation") - raise ValidationError(self.eidfrom, {self.rtype: msg}) + msg = _("You can't change this relation") + raise validation_error(self.eidfrom, {self.rtype: msg}) class SourceMappingChangedOp(hook.DataOperationMixIn, hook.Operation): diff -r 6e5d8512b07d -r e20057a9ceea hooks/test/unittest_hooks.py --- a/hooks/test/unittest_hooks.py Tue Sep 11 22:32:01 2012 +0200 +++ b/hooks/test/unittest_hooks.py Fri Oct 12 15:38:58 2012 +0200 @@ -170,6 +170,7 @@ try: self.execute('INSERT CWUser X: X login "admin"') except ValidationError, ex: + ex.tr(unicode) self.assertIsInstance(ex.entity, int) self.assertEqual(ex.errors, {'login-subject': 'the value "admin" is already used, use another one'}) diff -r 6e5d8512b07d -r e20057a9ceea hooks/test/unittest_syncsession.py --- a/hooks/test/unittest_syncsession.py Tue Sep 11 22:32:01 2012 +0200 +++ b/hooks/test/unittest_syncsession.py Fri Oct 12 15:38:58 2012 +0200 @@ -31,9 +31,11 @@ def test_unexistant_cwproperty(self): with self.assertRaises(ValidationError) as cm: self.execute('INSERT CWProperty X: X pkey "bla.bla", X value "hop", X for_user U') + cm.exception.tr(unicode) self.assertEqual(cm.exception.errors, {'pkey-subject': 'unknown property key bla.bla'}) with self.assertRaises(ValidationError) as cm: self.execute('INSERT CWProperty X: X pkey "bla.bla", X value "hop"') + cm.exception.tr(unicode) self.assertEqual(cm.exception.errors, {'pkey-subject': 'unknown property key bla.bla'}) def test_site_wide_cwproperty(self): diff -r 6e5d8512b07d -r e20057a9ceea hooks/workflow.py --- a/hooks/workflow.py Tue Sep 11 22:32:01 2012 +0200 +++ b/hooks/workflow.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -18,12 +18,12 @@ """Core hooks: workflow related hooks""" __docformat__ = "restructuredtext en" +_ = unicode from datetime import datetime -from yams.schema import role_name -from cubicweb import RepositoryError, ValidationError +from cubicweb import RepositoryError, validation_error from cubicweb.predicates import is_instance, adaptable from cubicweb.server import hook @@ -92,9 +92,8 @@ if mainwf.eid == self.wfeid: deststate = mainwf.initial if not deststate: - qname = role_name('custom_workflow', 'subject') - msg = session._('workflow has no initial state') - raise ValidationError(entity.eid, {qname: msg}) + msg = _('workflow has no initial state') + raise validation_error(entity, {('custom_workflow', 'subject'): msg}) if mainwf.state_by_eid(iworkflowable.current_state.eid): # nothing to do return @@ -119,9 +118,8 @@ outputs = set() for ep in tr.subworkflow_exit: if ep.subwf_state.eid in outputs: - qname = role_name('subworkflow_exit', 'subject') - msg = self.session._("can't have multiple exits on the same state") - raise ValidationError(self.treid, {qname: msg}) + msg = _("can't have multiple exits on the same state") + raise validation_error(self.treid, {('subworkflow_exit', 'subject'): msg}) outputs.add(ep.subwf_state.eid) @@ -137,13 +135,12 @@ wftr = iworkflowable.subworkflow_input_transition() if wftr is None: # inconsistency detected - qname = role_name('to_state', 'subject') - msg = session._("state doesn't belong to entity's current workflow") - raise ValidationError(self.trinfo.eid, {'to_state': msg}) + msg = _("state doesn't belong to entity's current workflow") + raise validation_error(self.trinfo, {('to_state', 'subject'): msg}) tostate = wftr.get_exit_point(forentity, trinfo.cw_attr_cache['to_state']) if tostate is not None: # reached an exit point - msg = session._('exiting from subworkflow %s') + msg = _('exiting from subworkflow %s') msg %= session._(iworkflowable.current_workflow.name) session.transaction_data[(forentity.eid, 'subwfentrytr')] = True iworkflowable.change_state(tostate, msg, u'text/plain', tr=wftr) @@ -186,9 +183,8 @@ try: foreid = entity.cw_attr_cache['wf_info_for'] except KeyError: - qname = role_name('wf_info_for', 'subject') - msg = session._('mandatory relation') - raise ValidationError(entity.eid, {qname: msg}) + msg = _('mandatory relation') + raise validation_error(entity, {('wf_info_for', 'subject'): msg}) forentity = session.entity_from_eid(foreid) # see comment in the TrInfo entity definition entity.cw_edited['tr_count']=len(forentity.reverse_wf_info_for) @@ -201,13 +197,13 @@ else: wf = iworkflowable.current_workflow if wf is None: - msg = session._('related entity has no workflow set') - raise ValidationError(entity.eid, {None: msg}) + msg = _('related entity has no workflow set') + raise validation_error(entity, {None: msg}) # then check it has a state set fromstate = iworkflowable.current_state if fromstate is None: - msg = session._('related entity has no state') - raise ValidationError(entity.eid, {None: msg}) + msg = _('related entity has no state') + raise validation_error(entity, {None: msg}) # True if we are coming back from subworkflow swtr = session.transaction_data.pop((forentity.eid, 'subwfentrytr'), None) cowpowers = (session.user.is_in_group('managers') @@ -219,47 +215,42 @@ # no transition set, check user is a manager and destination state # is specified (and valid) if not cowpowers: - qname = role_name('by_transition', 'subject') - msg = session._('mandatory relation') - raise ValidationError(entity.eid, {qname: msg}) + msg = _('mandatory relation') + raise validation_error(entity, {('by_transition', 'subject'): msg}) deststateeid = entity.cw_attr_cache.get('to_state') if not deststateeid: - qname = role_name('by_transition', 'subject') - msg = session._('mandatory relation') - raise ValidationError(entity.eid, {qname: msg}) + msg = _('mandatory relation') + raise validation_error(entity, {('by_transition', 'subject'): msg}) deststate = wf.state_by_eid(deststateeid) if deststate is None: - qname = role_name('to_state', 'subject') - msg = session._("state doesn't belong to entity's workflow") - raise ValidationError(entity.eid, {qname: msg}) + msg = _("state doesn't belong to entity's workflow") + raise validation_error(entity, {('to_state', 'subject'): msg}) else: # check transition is valid and allowed, unless we're coming back # from subworkflow tr = session.entity_from_eid(treid) if swtr is None: - qname = role_name('by_transition', 'subject') + qname = ('by_transition', 'subject') if tr is None: - msg = session._("transition doesn't belong to entity's workflow") - raise ValidationError(entity.eid, {qname: msg}) + msg = _("transition doesn't belong to entity's workflow") + raise validation_error(entity, {qname: msg}) if not tr.has_input_state(fromstate): - msg = session._("transition %(tr)s isn't allowed from %(st)s") % { - 'tr': session._(tr.name), 'st': session._(fromstate.name)} - raise ValidationError(entity.eid, {qname: msg}) + msg = _("transition %(tr)s isn't allowed from %(st)s") + raise validation_error(entity, {qname: msg}, { + 'tr': tr.name, 'st': fromstate.name}, ['tr', 'st']) if not tr.may_be_fired(foreid): - msg = session._("transition may not be fired") - raise ValidationError(entity.eid, {qname: msg}) + msg = _("transition may not be fired") + raise validation_error(entity, {qname: msg}) deststateeid = entity.cw_attr_cache.get('to_state') if deststateeid is not None: if not cowpowers and deststateeid != tr.destination(forentity).eid: - qname = role_name('by_transition', 'subject') - msg = session._("transition isn't allowed") - raise ValidationError(entity.eid, {qname: msg}) + msg = _("transition isn't allowed") + raise validation_error(entity, {('by_transition', 'subject'): msg}) if swtr is None: deststate = session.entity_from_eid(deststateeid) if not cowpowers and deststate is None: - qname = role_name('to_state', 'subject') - msg = session._("state doesn't belong to entity's workflow") - raise ValidationError(entity.eid, {qname: msg}) + msg = _("state doesn't belong to entity's workflow") + raise validation_error(entity, {('to_state', 'subject'): msg}) else: deststateeid = tr.destination(forentity).eid # everything is ok, add missing information on the trinfo entity @@ -307,20 +298,18 @@ iworkflowable = entity.cw_adapt_to('IWorkflowable') mainwf = iworkflowable.main_workflow if mainwf is None: - msg = session._('entity has no workflow set') - raise ValidationError(entity.eid, {None: msg}) + msg = _('entity has no workflow set') + raise validation_error(entity, {None: msg}) for wf in mainwf.iter_workflows(): if wf.state_by_eid(self.eidto): break else: - qname = role_name('in_state', 'subject') - msg = session._("state doesn't belong to entity's workflow. You may " - "want to set a custom workflow for this entity first.") - raise ValidationError(self.eidfrom, {qname: msg}) + msg = _("state doesn't belong to entity's workflow. You may " + "want to set a custom workflow for this entity first.") + raise validation_error(self.eidfrom, {('in_state', 'subject'): msg}) if iworkflowable.current_workflow and wf.eid != iworkflowable.current_workflow.eid: - qname = role_name('in_state', 'subject') - msg = session._("state doesn't belong to entity's current workflow") - raise ValidationError(self.eidfrom, {qname: msg}) + msg = _("state doesn't belong to entity's current workflow") + raise validation_error(self.eidfrom, {('in_state', 'subject'): msg}) class SetModificationDateOnStateChange(WorkflowHook): diff -r 6e5d8512b07d -r e20057a9ceea i18n/de.po --- a/i18n/de.po Tue Sep 11 22:32:01 2012 +0200 +++ b/i18n/de.po Fri Oct 12 15:38:58 2012 +0200 @@ -2561,6 +2561,9 @@ msgid "foaf" msgstr "FOAF" +msgid "focus on this selection" +msgstr "" + msgid "follow" msgstr "dem Link folgen" diff -r 6e5d8512b07d -r e20057a9ceea i18n/en.po --- a/i18n/en.po Tue Sep 11 22:32:01 2012 +0200 +++ b/i18n/en.po Fri Oct 12 15:38:58 2012 +0200 @@ -2508,6 +2508,9 @@ msgid "foaf" msgstr "" +msgid "focus on this selection" +msgstr "" + msgid "follow" msgstr "" diff -r 6e5d8512b07d -r e20057a9ceea i18n/es.po --- a/i18n/es.po Tue Sep 11 22:32:01 2012 +0200 +++ b/i18n/es.po Fri Oct 12 15:38:58 2012 +0200 @@ -2603,6 +2603,9 @@ msgid "foaf" msgstr "Amigo de un Amigo, FOAF" +msgid "focus on this selection" +msgstr "" + msgid "follow" msgstr "Seguir la liga" diff -r 6e5d8512b07d -r e20057a9ceea i18n/fr.po --- a/i18n/fr.po Tue Sep 11 22:32:01 2012 +0200 +++ b/i18n/fr.po Fri Oct 12 15:38:58 2012 +0200 @@ -2611,6 +2611,9 @@ msgid "foaf" msgstr "foaf" +msgid "focus on this selection" +msgstr "afficher cette sélection" + msgid "follow" msgstr "suivre le lien" @@ -4137,10 +4140,6 @@ msgid "there is no transaction #%s" msgstr "Il n'y a pas de transaction #%s" -#, python-format -msgid "there is no transaction #%s" -msgstr "" - msgid "this action is not reversible!" msgstr "" "Attention ! Cette opération va détruire les données de façon irréversible." @@ -4696,6 +4695,9 @@ #~ msgid "day" #~ msgstr "jour" +#~ msgid "jump to selection" +#~ msgstr "afficher cette sélection" + #~ msgid "log out first" #~ msgstr "déconnecter vous d'abord" diff -r 6e5d8512b07d -r e20057a9ceea server/__init__.py --- a/server/__init__.py Tue Sep 11 22:32:01 2012 +0200 +++ b/server/__init__.py Fri Oct 12 15:38:58 2012 +0200 @@ -247,15 +247,13 @@ def initialize_schema(config, schema, mhandler, event='create'): from cubicweb.server.schemaserial import serialize_schema - from cubicweb.server.session import hooks_control session = mhandler.session cubes = config.cubes() # deactivate every hooks but those responsible to set metadata # so, NO INTEGRITY CHECKS are done, to have quicker db creation. # Active integrity is kept else we may pb such as two default # workflows for one entity type. - with hooks_control(session, session.HOOKS_DENY_ALL, 'metadata', - 'activeintegrity'): + with session.deny_all_hooks_but('metadata', 'activeintegrity'): # execute cubicweb's pre script mhandler.cmd_exec_event_script('pre%s' % event) # execute cubes pre script if any diff -r 6e5d8512b07d -r e20057a9ceea server/checkintegrity.py --- a/server/checkintegrity.py Tue Sep 11 22:32:01 2012 +0200 +++ b/server/checkintegrity.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -32,7 +32,6 @@ from cubicweb.schema import PURE_VIRTUAL_RTYPES, VIRTUAL_RTYPES from cubicweb.server.sqlutils import SQL_PREFIX -from cubicweb.server.session import security_enabled def notify_fixed(fix): if fix: @@ -394,7 +393,7 @@ # yo, launch checks if checks: eids_cache = {} - with security_enabled(session, read=False, write=False): # ensure no read security + with session.security_enabled(read=False, write=False): # ensure no read security for check in checks: check_func = globals()['check_%s' % check] check_func(repo.schema, session, eids_cache, fix=fix) diff -r 6e5d8512b07d -r e20057a9ceea server/edition.py --- a/server/edition.py Tue Sep 11 22:32:01 2012 +0200 +++ b/server/edition.py Fri Oct 12 15:38:58 2012 +0200 @@ -143,8 +143,7 @@ for rtype in self] try: entity.e_schema.check(dict_protocol_catcher(entity), - creation=creation, _=entity._cw._, - relations=relations) + creation=creation, relations=relations) except ValidationError, ex: ex.entity = self.entity raise diff -r 6e5d8512b07d -r e20057a9ceea server/hook.py --- a/server/hook.py Tue Sep 11 22:32:01 2012 +0200 +++ b/server/hook.py Fri Oct 12 15:38:58 2012 +0200 @@ -197,14 +197,12 @@ ~~~~~~~~~~~~~ It is sometimes convenient to explicitly enable or disable some hooks. For -instance if you want to disable some integrity checking hook. This can be +instance if you want to disable some integrity checking hook. This can be controlled more finely through the `category` class attribute, which is a string giving a category name. One can then uses the -:class:`~cubicweb.server.session.hooks_control` context manager to explicitly -enable or disable some categories. - -.. autoclass:: cubicweb.server.session.hooks_control - +:meth:`~cubicweb.server.session.Session.deny_all_hooks_but` and +:meth:`~cubicweb.server.session.Session.allow_all_hooks_but` context managers to +explicitly enable or disable some categories. The existing categories are: @@ -230,10 +228,8 @@ * ``bookmark``, bookmark entities handling hooks -Nothing precludes one to invent new categories and use the -:class:`~cubicweb.server.session.hooks_control` context manager to -filter them in or out. Note that ending the transaction with commit() -or rollback() will restore the hooks. +Nothing precludes one to invent new categories and use existing mechanisms to +filter them in or out. Hooks specific predicates @@ -268,7 +264,6 @@ from cubicweb.cwvreg import CWRegistry, CWRegistryStore from cubicweb.predicates import ExpectedValuePredicate, is_instance from cubicweb.appobject import AppObject -from cubicweb.server.session import security_enabled ENTITIES_HOOKS = set(('before_add_entity', 'after_add_entity', 'before_update_entity', 'after_update_entity', @@ -326,11 +321,11 @@ pruned = self.get_pruned_hooks(session, event, entities, eids_from_to, kwargs) # by default, hooks are executed with security turned off - with security_enabled(session, read=False): + with session.security_enabled(read=False): for _kwargs in _iter_kwargs(entities, eids_from_to, kwargs): hooks = sorted(self.filtered_possible_objects(pruned, session, **_kwargs), key=lambda x: x.order) - with security_enabled(session, write=False): + with session.security_enabled(write=False): for hook in hooks: hook() diff -r 6e5d8512b07d -r e20057a9ceea server/querier.py --- a/server/querier.py Tue Sep 11 22:32:01 2012 +0200 +++ b/server/querier.py Fri Oct 12 15:38:58 2012 +0200 @@ -42,7 +42,6 @@ from cubicweb.server.rqlannotation import SQLGenAnnotator, set_qdata from cubicweb.server.ssplanner import READ_ONLY_RTYPES, add_types_restriction from cubicweb.server.edition import EditedEntity -from cubicweb.server.session import security_enabled ETYPE_PYOBJ_MAP[Binary] = 'Bytes' @@ -262,7 +261,7 @@ cached = True else: noinvariant = set() - with security_enabled(self.session, read=False): + with self.session.security_enabled(read=False): self._insert_security(union, noinvariant) if key is not None: self.session.transaction_data[key] = (union, self.args) diff -r 6e5d8512b07d -r e20057a9ceea server/repository.py --- a/server/repository.py Tue Sep 11 22:32:01 2012 +0200 +++ b/server/repository.py Fri Oct 12 15:38:58 2012 +0200 @@ -56,8 +56,7 @@ RepositoryError, UniqueTogetherError, typed_eid, onevent) from cubicweb import cwvreg, schema, server from cubicweb.server import ShuttingDown, utils, hook, pool, querier, sources -from cubicweb.server.session import Session, InternalSession, InternalManager, \ - security_enabled +from cubicweb.server.session import Session, InternalSession, InternalManager from cubicweb.server.ssplanner import EditedEntity NO_CACHE_RELATIONS = set( [('owned_by', 'object'), @@ -109,12 +108,12 @@ # * we don't want read permissions to be applied but we want delete # permission to be checked if card[0] in '1?': - with security_enabled(session, read=False): + with session.security_enabled(read=False): session.execute('DELETE X %s Y WHERE X eid %%(x)s, ' 'NOT Y eid %%(y)s' % rtype, {'x': eidfrom, 'y': eidto}) if card[1] in '1?': - with security_enabled(session, read=False): + with session.security_enabled(read=False): session.execute('DELETE X %s Y WHERE Y eid %%(y)s, ' 'NOT X eid %%(x)s' % rtype, {'x': eidfrom, 'y': eidto}) @@ -1200,7 +1199,7 @@ source = self.sources_by_eid[scleanup] # delete remaining relations: if user can delete the entity, he can # delete all its relations without security checking - with security_enabled(session, read=False, write=False): + with session.security_enabled(read=False, write=False): eid = entity.eid for rschema, _, role in entity.e_schema.relation_definitions(): rtype = rschema.type @@ -1242,7 +1241,7 @@ source = self.sources_by_eid[scleanup] # delete remaining relations: if user can delete the entity, he can # delete all its relations without security checking - with security_enabled(session, read=False, write=False): + with session.security_enabled(read=False, write=False): in_eids = ','.join([str(_e.eid) for _e in entities]) for rschema, _, role in entities[0].e_schema.relation_definitions(): rtype = rschema.type @@ -1355,7 +1354,7 @@ session.update_rel_cache_add(entity.eid, attr, value) rdef = session.rtype_eids_rdef(attr, entity.eid, value) if rdef.cardinality[1] in '1?' and activeintegrity: - with security_enabled(session, read=False): + with session.security_enabled(read=False): session.execute('DELETE X %s Y WHERE Y eid %%(y)s' % attr, {'x': entity.eid, 'y': value}) edited.set_defaults() @@ -1541,7 +1540,7 @@ rdef = session.rtype_eids_rdef(rtype, subjeid, objeid) card = rdef.cardinality if card[0] in '?1': - with security_enabled(session, read=False): + with session.security_enabled(read=False): session.execute('DELETE X %s Y WHERE X eid %%(x)s, ' 'NOT Y eid %%(y)s' % rtype, {'x': subjeid, 'y': objeid}) @@ -1552,7 +1551,7 @@ continue subjects[subjeid] = len(relations_by_rtype[rtype]) - 1 if card[1] in '?1': - with security_enabled(session, read=False): + with session.security_enabled(read=False): session.execute('DELETE X %s Y WHERE Y eid %%(y)s, ' 'NOT X eid %%(x)s' % rtype, {'x': subjeid, 'y': objeid}) diff -r 6e5d8512b07d -r e20057a9ceea server/session.py --- a/server/session.py Tue Sep 11 22:32:01 2012 +0200 +++ b/server/session.py Fri Oct 12 15:38:58 2012 +0200 @@ -108,6 +108,11 @@ with hooks_control(self.session, self.session.HOOKS_DENY_ALL, 'integrity'): # ... do stuff with none but 'integrity' hooks activated + + This is an internal api, you should rather use + :meth:`~cubicweb.server.session.Session.deny_all_hooks_but` or + :meth:`~cubicweb.server.session.Session.allow_all_hooks_but` session + methods. """ def __init__(self, session, mode, *categories): self.session = session @@ -217,6 +222,9 @@ :attr:`running_dbapi_query`, boolean flag telling if the executing query is coming from a dbapi connection or is a query from within the repository + + .. automethod:: cubicweb.server.session.deny_all_hooks_but + .. automethod:: cubicweb.server.session.all_all_hooks_but """ is_request = False is_internal_session = False @@ -462,7 +470,7 @@ DEFAULT_SECURITY = object() # evaluated to true by design - def security_enabled(self, read=False, write=False): + def security_enabled(self, read=None, write=None): return security_enabled(self, read=read, write=write) def init_security(self, read, write): @@ -1205,6 +1213,9 @@ return 'en' return None + def prefered_language(self, language=None): + # mock CWUser.prefered_language, mainly for testing purpose + return self.property_value('ui.language') from logging import getLogger from cubicweb import set_log_methods diff -r 6e5d8512b07d -r e20057a9ceea server/ssplanner.py --- a/server/ssplanner.py Tue Sep 11 22:32:01 2012 +0200 +++ b/server/ssplanner.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -27,7 +27,6 @@ from cubicweb import QueryError, typed_eid from cubicweb.schema import VIRTUAL_RTYPES from cubicweb.rqlrewrite import add_types_restriction -from cubicweb.server.session import security_enabled from cubicweb.server.edition import EditedEntity READ_ONLY_RTYPES = set(('eid', 'has_text', 'is', 'is_instance_of', 'identity')) @@ -87,7 +86,7 @@ # the generated select substep if not emited (eg nothing # to be selected) if checkread and eid not in neweids: - with security_enabled(session, read=False): + with session.security_enabled(read=False): eschema(session.describe(eid)[0]).check_perm( session, 'read', eid=eid) eidconsts[lhs.variable] = eid diff -r 6e5d8512b07d -r e20057a9ceea server/test/unittest_session.py --- a/server/test/unittest_session.py Tue Sep 11 22:32:01 2012 +0200 +++ b/server/test/unittest_session.py Fri Oct 12 15:38:58 2012 +0200 @@ -18,7 +18,6 @@ from __future__ import with_statement from cubicweb.devtools.testlib import CubicWebTC -from cubicweb.server.session import hooks_control class InternalSessionTC(CubicWebTC): @@ -36,7 +35,7 @@ self.assertEqual(session.disabled_hook_categories, set()) self.assertEqual(session.enabled_hook_categories, set()) self.assertEqual(len(session._tx_data), 1) - with hooks_control(session, session.HOOKS_DENY_ALL, 'metadata'): + with session.deny_all_hooks_but('metadata'): self.assertEqual(session.hooks_mode, session.HOOKS_DENY_ALL) self.assertEqual(session.disabled_hook_categories, set()) self.assertEqual(session.enabled_hook_categories, set(('metadata',))) @@ -48,7 +47,7 @@ self.assertEqual(session.hooks_mode, session.HOOKS_DENY_ALL) self.assertEqual(session.disabled_hook_categories, set()) self.assertEqual(session.enabled_hook_categories, set(('metadata',))) - with hooks_control(session, session.HOOKS_ALLOW_ALL, 'integrity'): + with session.allow_all_hooks_but('integrity'): self.assertEqual(session.hooks_mode, session.HOOKS_ALLOW_ALL) self.assertEqual(session.disabled_hook_categories, set(('integrity',))) self.assertEqual(session.enabled_hook_categories, set(('metadata',))) # not changed in such case @@ -65,4 +64,5 @@ if __name__ == '__main__': + from logilab.common.testlib import unittest_main unittest_main() diff -r 6e5d8512b07d -r e20057a9ceea server/test/unittest_undo.py --- a/server/test/unittest_undo.py Tue Sep 11 22:32:01 2012 +0200 +++ b/server/test/unittest_undo.py Fri Oct 12 15:38:58 2012 +0200 @@ -228,6 +228,7 @@ "%s doesn't exist anymore." % g.eid]) with self.assertRaises(ValidationError) as cm: self.commit() + cm.exception.tr(unicode) self.assertEqual(cm.exception.entity, self.toto.eid) self.assertEqual(cm.exception.errors, {'in_group-subject': u'at least one relation in_group is ' diff -r 6e5d8512b07d -r e20057a9ceea test/unittest_migration.py --- a/test/unittest_migration.py Tue Sep 11 22:32:01 2012 +0200 +++ b/test/unittest_migration.py Fri Oct 12 15:38:58 2012 +0200 @@ -108,7 +108,13 @@ self.assertEqual(source['db-driver'], 'sqlite') handler = get_test_db_handler(config) handler.init_test_database() - + handler.build_db_cache() + repo, cnx = handler.get_repo_and_cnx() + cu = cnx.cursor() + self.assertEqual(cu.execute('Any SN WHERE X is CWUser, X login "admin", X in_state S, S name SN').rows, + [['activated']]) + cnx.close() + repo.shutdown() if __name__ == '__main__': unittest_main() diff -r 6e5d8512b07d -r e20057a9ceea test/unittest_schema.py --- a/test/unittest_schema.py Tue Sep 11 22:32:01 2012 +0200 +++ b/test/unittest_schema.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -350,8 +350,8 @@ class WorkflowShemaTC(CubicWebTC): def test_trinfo_default_format(self): - tr = self.session.user.cw_adapt_to('IWorkflowable').fire_transition('deactivate') - self.assertEqual(tr.comment_format, 'text/plain') + tr = self.request().user.cw_adapt_to('IWorkflowable').fire_transition('deactivate') + self.assertEqual(tr.comment_format, 'text/plain') if __name__ == '__main__': unittest_main() diff -r 6e5d8512b07d -r e20057a9ceea web/application.py --- a/web/application.py Tue Sep 11 22:32:01 2012 +0200 +++ b/web/application.py Fri Oct 12 15:38:58 2012 +0200 @@ -461,7 +461,6 @@ result = self.notfound_content(req) req.status_out = ex.status except ValidationError, ex: - req.status_out = httplib.CONFLICT result = self.validation_error_handler(req, ex) except RemoteCallFailed, ex: result = self.ajax_error_handler(req, ex) @@ -480,7 +479,7 @@ except (AuthenticationError, LogOut): # the rollback is handled in the finally raise - ### Last defence line + ### Last defense line except BaseException, ex: result = self.error_handler(req, ex, tb=True) finally: @@ -511,7 +510,7 @@ return '' def validation_error_handler(self, req, ex): - ex.errors = dict((k, v) for k, v in ex.errors.items()) + ex.tr(req._) # translate messages using ui language if '__errorurl' in req.form: forminfo = {'error': ex, 'values': req.form, @@ -526,6 +525,7 @@ req.headers_out.setHeader('location', str(location)) req.status_out = httplib.SEE_OTHER return '' + req.status_out = httplib.CONFLICT return self.error_handler(req, ex, tb=False) def error_handler(self, req, ex, tb=False): diff -r 6e5d8512b07d -r e20057a9ceea web/data/cubicweb.ajax.js --- a/web/data/cubicweb.ajax.js Tue Sep 11 22:32:01 2012 +0200 +++ b/web/data/cubicweb.ajax.js Fri Oct 12 15:38:58 2012 +0200 @@ -704,7 +704,7 @@ var ajaxArgs = ['render', formparams, registry, compid]; ajaxArgs = ajaxArgs.concat(cw.utils.sliceList(arguments, 4)); var params = ajaxFuncArgs.apply(null, ajaxArgs); - return $('#'+domid).loadxhtml(AJAX_BASE_URL, params, null, 'swap'); + return $('#'+domid).loadxhtml(AJAX_BASE_URL, params, null, 'swap', true); } /* ajax tabs ******************************************************************/ diff -r 6e5d8512b07d -r e20057a9ceea web/data/cubicweb.facets.js --- a/web/data/cubicweb.facets.js Tue Sep 11 22:32:01 2012 +0200 +++ b/web/data/cubicweb.facets.js Fri Oct 12 15:38:58 2012 +0200 @@ -68,6 +68,14 @@ var bkUrl = $bkLink.attr('cubicweb:target') + '&path=' + encodeURIComponent(bkPath); $bkLink.attr('href', bkUrl); } + var $focusLink = jQuery('#focusLink'); + if ($focusLink.length) { + var url = baseuri()+ 'view?rql=' + encodeURIComponent(rql); + if (vid) { + url += '&vid=' + encodeURIComponent(vid); + } + $focusLink.attr('href', url); + } var toupdate = result[1]; var extraparams = vidargs; if (paginate) { extraparams['paginate'] = '1'; } // XXX in vidargs diff -r 6e5d8512b07d -r e20057a9ceea web/test/unittest_views_basecontrollers.py --- a/web/test/unittest_views_basecontrollers.py Tue Sep 11 22:32:01 2012 +0200 +++ b/web/test/unittest_views_basecontrollers.py Fri Oct 12 15:38:58 2012 +0200 @@ -77,6 +77,7 @@ } with self.assertRaises(ValidationError) as cm: self.ctrl_publish(req) + cm.exception.tr(unicode) self.assertEqual(cm.exception.errors, {'login-subject': 'the value "admin" is already used, use another one'}) def test_user_editing_itself(self): @@ -249,6 +250,7 @@ } with self.assertRaises(ValidationError) as cm: self.ctrl_publish(req) + cm.exception.tr(unicode) self.assertEqual(cm.exception.errors, {'amount-subject': 'value -10 must be >= 0'}) req = self.request(rollbackfirst=True) req.form = {'eid': ['X'], @@ -259,6 +261,7 @@ } with self.assertRaises(ValidationError) as cm: self.ctrl_publish(req) + cm.exception.tr(unicode) self.assertEqual(cm.exception.errors, {'amount-subject': 'value 110 must be <= 100'}) req = self.request(rollbackfirst=True) req.form = {'eid': ['X'], diff -r 6e5d8512b07d -r e20057a9ceea web/views/basecontrollers.py --- a/web/views/basecontrollers.py Tue Sep 11 22:32:01 2012 +0200 +++ b/web/views/basecontrollers.py Fri Oct 12 15:38:58 2012 +0200 @@ -190,6 +190,7 @@ def _validation_error(req, ex): req.cnx.rollback() + ex.tr(req._) # translate messages using ui language # XXX necessary to remove existant validation error? # imo (syt), it's not necessary req.session.data.pop(req.form.get('__errorurl'), None) diff -r 6e5d8512b07d -r e20057a9ceea web/views/editcontroller.py --- a/web/views/editcontroller.py Tue Sep 11 22:32:01 2012 +0200 +++ b/web/views/editcontroller.py Fri Oct 12 15:38:58 2012 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. diff -r 6e5d8512b07d -r e20057a9ceea web/views/facets.py --- a/web/views/facets.py Tue Sep 11 22:32:01 2012 +0200 +++ b/web/views/facets.py Fri Oct 12 15:38:58 2012 +0200 @@ -26,6 +26,7 @@ from logilab.common.decorators import cachedproperty from logilab.common.registry import objectify_predicate, yes +from cubicweb import tags from cubicweb.predicates import (non_final_entity, multi_lines_rset, match_context_prop, relation_possible) from cubicweb.utils import json_dumps @@ -234,6 +235,7 @@ vid = req.form.get('vid') if self.bk_linkbox_template and req.vreg.schema['Bookmark'].has_perm(req, 'add'): w(self.bookmark_link(rset)) + w(self.focus_link(rset)) hiddens = {} for param in ('subvid', 'vtitle'): if param in req.form: @@ -269,6 +271,9 @@ req._('bookmark this search')) return self.bk_linkbox_template % bk_link + def focus_link(self, rset): + return self.bk_linkbox_template % tags.a(self._cw._('focus on this selection'), + href=self._cw.url(), id='focusLink') class FilterTable(FacetFilterMixIn, AnyRsetView): __regid__ = 'facet.filtertable'