diff -r 058bb3dc685f -r 0b59724cb3f2 server/edition.py --- a/server/edition.py Mon Jan 04 18:40:30 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,159 +0,0 @@ -# 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. -# -# 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 . -"""helper classes to handle server-side edition of entities""" -__docformat__ = "restructuredtext en" - -from copy import copy -from yams import ValidationError - - -_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): - super(EditedEntity, self).__init__(**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 __lt__(self, other): - # we don't want comparison by value inherited from dict - raise NotImplementedError - - def __eq__(self, other): - return self is other - - def __ne__(self, other): - return not (self == other) - - def __setitem__(self, attr, value): - assert attr != 'eid' - # don't add attribute into skip_security if already in edited - # attributes, else we may accidentally 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 __copy__(self): - # default copy protocol fails in EditedEntity.__setitem__ because - # copied entity has no skip_security attribute at this point - return EditedEntity(self.entity, **self) - - 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 accidentally 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.items(): - 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 - if self.entity._cw.vreg.schema.rschema(attr).final: - self.entity._cw_dont_cache_attribute(attr) - - 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] - try: - entity.e_schema.check(dict_protocol_catcher(entity), - creation=creation, relations=relations) - except ValidationError as ex: - ex.entity = self.entity.eid - 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