# HG changeset patch # User Nicolas Chauvat # Date 1301333505 -7200 # Node ID a055a164972779956a2bcdb4b8d0c182f3e1f65e # Parent 9ebc24d85bb5ae3e2adeceedba519a85b36e5c19# Parent bb303290a6cb7a2f8a770ebf681c0e166c5df666 merge diff -r 9ebc24d85bb5 -r a055a1649727 dataimport.py --- a/dataimport.py Mon Mar 28 19:31:26 2011 +0200 +++ b/dataimport.py Mon Mar 28 19:31:45 2011 +0200 @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -81,7 +81,7 @@ from logilab.common.deprecation import deprecated from cubicweb.server.utils import eschema_eid -from cubicweb.server.ssplanner import EditedEntity +from cubicweb.server.edition import EditedEntity def count_lines(stream_or_filename): if isinstance(stream_or_filename, basestring): diff -r 9ebc24d85bb5 -r a055a1649727 entities/authobjs.py --- a/entities/authobjs.py Mon Mar 28 19:31:26 2011 +0200 +++ b/entities/authobjs.py Mon Mar 28 19:31:45 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -87,8 +87,10 @@ 'CWProperty X WHERE X pkey %(k)s, X for_user U, U eid %(u)s', {'k': pkey, 'u': self.eid}).get_entity(0, 0) except: - self._cw.create_entity('CWProperty', pkey=unicode(pkey), - value=value, for_user=self) + kwargs = dict(pkey=unicode(pkey), value=value) + if self.is_in_group('managers'): + kwargs['for_user'] = self + self._cw.create_entity('CWProperty', **kwargs) else: prop.set_attributes(value=value) diff -r 9ebc24d85bb5 -r a055a1649727 hooks/syncsession.py --- a/hooks/syncsession.py Mon Mar 28 19:31:26 2011 +0200 +++ b/hooks/syncsession.py Mon Mar 28 19:31:45 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 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 9ebc24d85bb5 -r a055a1649727 req.py --- a/req.py Mon Mar 28 19:31:26 2011 +0200 +++ b/req.py Mon Mar 28 19:31:45 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -154,7 +154,7 @@ def find_one_entity(self, etype, **kwargs): """find one entity of the given type and attribute values. - raise FindEntityError if can not return one and only one entity. + raise :exc:`FindEntityError` if can not return one and only one entity. >>> users = find_one_entity('CWGroup', name=u'users') >>> groups = find_one_entity('CWGroup') diff -r 9ebc24d85bb5 -r a055a1649727 server/edition.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/server/edition.py Mon Mar 28 19:31:45 2011 +0200 @@ -0,0 +1,128 @@ +from copy import copy + +_MARKER = object() + +class dict_protocol_catcher(object): + def __init__(self, entity): + self.__entity = entity + def __getitem__(self, attr): + return self.__entity.cw_edited[attr] + def __setitem__(self, attr, value): + self.__entity.cw_edited[attr] = value + def __getattr__(self, attr): + return getattr(self.__entity, attr) + + +class EditedEntity(dict): + """encapsulate entities attributes being written by an RQL query""" + def __init__(self, entity, **kwargs): + dict.__init__(self, **kwargs) + self.entity = entity + self.skip_security = set() + self.querier_pending_relations = {} + self.saved = False + + def __hash__(self): + # dict|set keyable + return hash(id(self)) + + def __cmp__(self, other): + # we don't want comparison by value inherited from dict + return cmp(id(self), id(other)) + + def __setitem__(self, attr, value): + assert attr != 'eid' + # don't add attribute into skip_security if already in edited + # attributes, else we may accidentaly skip a desired security check + if attr not in self: + self.skip_security.add(attr) + self.edited_attribute(attr, value) + + def __delitem__(self, attr): + assert not self.saved, 'too late to modify edited attributes' + super(EditedEntity, self).__delitem__(attr) + self.entity.cw_attr_cache.pop(attr, None) + + def pop(self, attr, *args): + # don't update skip_security by design (think to storage api) + assert not self.saved, 'too late to modify edited attributes' + value = super(EditedEntity, self).pop(attr, *args) + self.entity.cw_attr_cache.pop(attr, *args) + return value + + def setdefault(self, attr, default): + assert attr != 'eid' + # don't add attribute into skip_security if already in edited + # attributes, else we may accidentaly skip a desired security check + if attr not in self: + self[attr] = default + return self[attr] + + def update(self, values, skipsec=True): + if skipsec: + setitem = self.__setitem__ + else: + setitem = self.edited_attribute + for attr, value in values.iteritems(): + setitem(attr, value) + + def edited_attribute(self, attr, value): + """attribute being edited by a rql query: should'nt be added to + skip_security + """ + assert not self.saved, 'too late to modify edited attributes' + super(EditedEntity, self).__setitem__(attr, value) + self.entity.cw_attr_cache[attr] = value + + def oldnewvalue(self, attr): + """returns the couple (old attr value, new attr value) + + NOTE: will only work in a before_update_entity hook + """ + assert not self.saved, 'too late to get the old value' + # get new value and remove from local dict to force a db query to + # fetch old value + newvalue = self.entity.cw_attr_cache.pop(attr, _MARKER) + oldvalue = getattr(self.entity, attr) + if newvalue is not _MARKER: + self.entity.cw_attr_cache[attr] = newvalue + else: + newvalue = oldvalue + return oldvalue, newvalue + + def set_defaults(self): + """set default values according to the schema""" + for attr, value in self.entity.e_schema.defaults(): + if not attr in self: + self[str(attr)] = value + + def check(self, creation=False): + """check the entity edition against its schema. Only final relation + are checked here, constraint on actual relations are checked in hooks + """ + entity = self.entity + if creation: + # on creations, we want to check all relations, especially + # required attributes + relations = [rschema for rschema in entity.e_schema.subject_relations() + if rschema.final and rschema.type != 'eid'] + else: + relations = [entity._cw.vreg.schema.rschema(rtype) + for rtype in self] + from yams import ValidationError + try: + entity.e_schema.check(dict_protocol_catcher(entity), + creation=creation, _=entity._cw._, + relations=relations) + except ValidationError, ex: + ex.entity = self.entity + raise + + def clone(self): + thecopy = EditedEntity(copy(self.entity)) + thecopy.entity.cw_attr_cache = copy(self.entity.cw_attr_cache) + thecopy.entity._cw_related_cache = {} + thecopy.update(self, skipsec=False) + return thecopy + + diff -r 9ebc24d85bb5 -r a055a1649727 server/querier.py --- a/server/querier.py Mon Mar 28 19:31:26 2011 +0200 +++ b/server/querier.py Mon Mar 28 19:31:45 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -38,7 +38,8 @@ from cubicweb.server.utils import cleanup_solutions from cubicweb.server.rqlannotation import SQLGenAnnotator, set_qdata -from cubicweb.server.ssplanner import READ_ONLY_RTYPES, add_types_restriction, EditedEntity +from cubicweb.server.ssplanner import READ_ONLY_RTYPES, add_types_restriction +from cubicweb.server.edition import EditedEntity from cubicweb.server.session import security_enabled def empty_rset(rql, args, rqlst=None): diff -r 9ebc24d85bb5 -r a055a1649727 server/serverctl.py --- a/server/serverctl.py Mon Mar 28 19:31:26 2011 +0200 +++ b/server/serverctl.py Mon Mar 28 19:31:45 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 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 9ebc24d85bb5 -r a055a1649727 server/session.py --- a/server/session.py Mon Mar 28 19:31:26 2011 +0200 +++ b/server/session.py Mon Mar 28 19:31:45 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -37,6 +37,7 @@ from cubicweb.dbapi import ConnectionProperties from cubicweb.utils import make_uid, RepeatList from cubicweb.rqlrewrite import RQLRewriter +from cubicweb.server.edition import EditedEntity ETYPE_PYOBJ_MAP[Binary] = 'Bytes' @@ -215,8 +216,9 @@ with security_enabled(self, False, False): if self.vreg.schema[rtype].inlined: entity = self.entity_from_eid(fromeid) - entity[rtype] = toeid - self.repo.glob_update_entity(self, entity, set((rtype,))) + edited = EditedEntity(entity) + edited.edited_attribute(rtype, toeid) + self.repo.glob_update_entity(self, edited) else: self.repo.glob_add_relation(self, fromeid, rtype, toeid) @@ -234,7 +236,7 @@ with security_enabled(self, False, False): if self.vreg.schema[rtype].inlined: entity = self.entity_from_eid(fromeid) - entity[rtype] = None + entity.cw_attr_cache[rtype] = None self.repo.glob_update_entity(self, entity, set((rtype,))) else: self.repo.glob_delete_relation(self, fromeid, rtype, toeid) diff -r 9ebc24d85bb5 -r a055a1649727 server/sources/__init__.py --- a/server/sources/__init__.py Mon Mar 28 19:31:26 2011 +0200 +++ b/server/sources/__init__.py Mon Mar 28 19:31:45 2011 +0200 @@ -31,7 +31,7 @@ from cubicweb import ValidationError, set_log_methods, server from cubicweb.schema import VIRTUAL_RTYPES from cubicweb.server.sqlutils import SQL_PREFIX -from cubicweb.server.ssplanner import EditedEntity +from cubicweb.server.edition import EditedEntity def dbg_st_search(uri, union, varmap, args, cachekey=None, prefix='rql for'): diff -r 9ebc24d85bb5 -r a055a1649727 server/sources/ldapuser.py --- a/server/sources/ldapuser.py Mon Mar 28 19:31:26 2011 +0200 +++ b/server/sources/ldapuser.py Mon Mar 28 19:31:45 2011 +0200 @@ -522,7 +522,8 @@ def _search(self, session, base, scope, searchstr='(objectClass=*)', attrs=()): """make an ldap query""" - self.debug('ldap search %s %s %s %s %s', self.uri, base, scope, searchstr, list(attrs)) + self.debug('ldap search %s %s %s %s %s', self.uri, base, scope, + searchstr, list(attrs)) # XXX for now, we do not have connection pool support for LDAP, so # this is always self._conn cnx = session.pool.connection(self.uri).cnx diff -r 9ebc24d85bb5 -r a055a1649727 server/sources/native.py --- a/server/sources/native.py Mon Mar 28 19:31:26 2011 +0200 +++ b/server/sources/native.py Mon Mar 28 19:31:45 2011 +0200 @@ -58,7 +58,7 @@ from cubicweb.server.rqlannotation import set_qdata from cubicweb.server.hook import CleanupDeletedEidsCacheOp from cubicweb.server.session import hooks_control, security_enabled -from cubicweb.server.ssplanner import EditedEntity +from cubicweb.server.edition import EditedEntity from cubicweb.server.sources import AbstractSource, dbg_st_search, dbg_results from cubicweb.server.sources.rql2sql import SQLGenerator diff -r 9ebc24d85bb5 -r a055a1649727 server/sources/storages.py --- a/server/sources/storages.py Mon Mar 28 19:31:26 2011 +0200 +++ b/server/sources/storages.py Mon Mar 28 19:31:45 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -24,7 +24,7 @@ from cubicweb import Binary, ValidationError from cubicweb.server import hook -from cubicweb.server.ssplanner import EditedEntity +from cubicweb.server.edition import EditedEntity def set_attribute_storage(repo, etype, attr, storage): diff -r 9ebc24d85bb5 -r a055a1649727 server/ssplanner.py --- a/server/ssplanner.py Mon Mar 28 19:31:26 2011 +0200 +++ b/server/ssplanner.py Mon Mar 28 19:31:45 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -21,8 +21,6 @@ __docformat__ = "restructuredtext en" -from copy import copy - from rql.stmts import Union, Select from rql.nodes import Constant, Relation @@ -31,6 +29,7 @@ from cubicweb.rqlrewrite import add_types_restriction from cubicweb.server.session import security_enabled from cubicweb.server.hook import CleanupDeletedEidsCacheOp +from cubicweb.server.edition import EditedEntity READ_ONLY_RTYPES = set(('eid', 'has_text', 'is', 'is_instance_of', 'identity')) @@ -128,132 +127,6 @@ return select -_MARKER = object() - -class dict_protocol_catcher(object): - def __init__(self, entity): - self.__entity = entity - def __getitem__(self, attr): - return self.__entity.cw_edited[attr] - def __setitem__(self, attr, value): - self.__entity.cw_edited[attr] = value - def __getattr__(self, attr): - return getattr(self.__entity, attr) - - -class EditedEntity(dict): - """encapsulate entities attributes being written by an RQL query""" - def __init__(self, entity, **kwargs): - dict.__init__(self, **kwargs) - self.entity = entity - self.skip_security = set() - self.querier_pending_relations = {} - self.saved = False - - def __hash__(self): - # dict|set keyable - return hash(id(self)) - - def __cmp__(self, other): - # we don't want comparison by value inherited from dict - return cmp(id(self), id(other)) - - def __setitem__(self, attr, value): - assert attr != 'eid' - # don't add attribute into skip_security if already in edited - # attributes, else we may accidentaly skip a desired security check - if attr not in self: - self.skip_security.add(attr) - self.edited_attribute(attr, value) - - def __delitem__(self, attr): - assert not self.saved, 'too late to modify edited attributes' - super(EditedEntity, self).__delitem__(attr) - self.entity.cw_attr_cache.pop(attr, None) - - def pop(self, attr, *args): - # don't update skip_security by design (think to storage api) - assert not self.saved, 'too late to modify edited attributes' - value = super(EditedEntity, self).pop(attr, *args) - self.entity.cw_attr_cache.pop(attr, *args) - return value - - def setdefault(self, attr, default): - assert attr != 'eid' - # don't add attribute into skip_security if already in edited - # attributes, else we may accidentaly skip a desired security check - if attr not in self: - self[attr] = default - return self[attr] - - def update(self, values, skipsec=True): - if skipsec: - setitem = self.__setitem__ - else: - setitem = self.edited_attribute - for attr, value in values.iteritems(): - setitem(attr, value) - - def edited_attribute(self, attr, value): - """attribute being edited by a rql query: should'nt be added to - skip_security - """ - assert not self.saved, 'too late to modify edited attributes' - super(EditedEntity, self).__setitem__(attr, value) - self.entity.cw_attr_cache[attr] = value - - def oldnewvalue(self, attr): - """returns the couple (old attr value, new attr value) - - NOTE: will only work in a before_update_entity hook - """ - assert not self.saved, 'too late to get the old value' - # get new value and remove from local dict to force a db query to - # fetch old value - newvalue = self.entity.cw_attr_cache.pop(attr, _MARKER) - oldvalue = getattr(self.entity, attr) - if newvalue is not _MARKER: - self.entity.cw_attr_cache[attr] = newvalue - else: - newvalue = oldvalue - return oldvalue, newvalue - - def set_defaults(self): - """set default values according to the schema""" - for attr, value in self.entity.e_schema.defaults(): - if not attr in self: - self[str(attr)] = value - - def check(self, creation=False): - """check the entity edition against its schema. Only final relation - are checked here, constraint on actual relations are checked in hooks - """ - entity = self.entity - if creation: - # on creations, we want to check all relations, especially - # required attributes - relations = [rschema for rschema in entity.e_schema.subject_relations() - if rschema.final and rschema.type != 'eid'] - else: - relations = [entity._cw.vreg.schema.rschema(rtype) - for rtype in self] - from yams import ValidationError - try: - entity.e_schema.check(dict_protocol_catcher(entity), - creation=creation, _=entity._cw._, - relations=relations) - except ValidationError, ex: - ex.entity = self.entity - raise - - def clone(self): - thecopy = EditedEntity(copy(self.entity)) - thecopy.entity.cw_attr_cache = copy(self.entity.cw_attr_cache) - thecopy.entity._cw_related_cache = {} - thecopy.update(self, skipsec=False) - return thecopy - - class SSPlanner(object): """SingleSourcePlanner: build execution plan for rql queries diff -r 9ebc24d85bb5 -r a055a1649727 server/test/unittest_ldapuser.py --- a/server/test/unittest_ldapuser.py Mon Mar 28 19:31:26 2011 +0200 +++ b/server/test/unittest_ldapuser.py Mon Mar 28 19:31:45 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2011 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 9ebc24d85bb5 -r a055a1649727 web/data/cubicweb.ajax.js --- a/web/data/cubicweb.ajax.js Mon Mar 28 19:31:26 2011 +0200 +++ b/web/data/cubicweb.ajax.js Mon Mar 28 19:31:45 2011 +0200 @@ -283,7 +283,7 @@ * dictionary, `reqtype` the HTTP request type (get 'GET' or 'POST'). */ function loadRemote(url, form, reqtype, sync) { - if (!url.toLowerCase().startswith(baseuri())) { + if (!url.toLowerCase().startswith(baseuri().toLowerCase())) { url = baseuri() + url; } if (!sync) {