Added tag 3.20.15, debian/3.20.15-1, centos/3.20.15-1 for changeset 636a83e65870
# 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 <http://www.gnu.org/licenses/>."""helper classes to handle server-side edition of entities"""__docformat__="restructuredtext en"fromcopyimportcopyfromyamsimportValidationError_MARKER=object()classdict_protocol_catcher(object):def__init__(self,entity):self.__entity=entitydef__getitem__(self,attr):returnself.__entity.cw_edited[attr]def__setitem__(self,attr,value):self.__entity.cw_edited[attr]=valuedef__getattr__(self,attr):returngetattr(self.__entity,attr)classEditedEntity(dict):"""encapsulate entities attributes being written by an RQL query"""def__init__(self,entity,**kwargs):dict.__init__(self,**kwargs)self.entity=entityself.skip_security=set()self.querier_pending_relations={}self.saved=Falsedef__hash__(self):# dict|set keyablereturnhash(id(self))def__lt__(self,other):# we don't want comparison by value inherited from dictreturnid(self)<id(other)def__eq__(self,other):returnid(self)==id(other)def__setitem__(self,attr,value):assertattr!='eid'# don't add attribute into skip_security if already in edited# attributes, else we may accidentaly skip a desired security checkifattrnotinself:self.skip_security.add(attr)self.edited_attribute(attr,value)def__delitem__(self,attr):assertnotself.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 pointreturnEditedEntity(self.entity,**self)defpop(self,attr,*args):# don't update skip_security by design (think to storage api)assertnotself.saved,'too late to modify edited attributes'value=super(EditedEntity,self).pop(attr,*args)self.entity.cw_attr_cache.pop(attr,*args)returnvaluedefsetdefault(self,attr,default):assertattr!='eid'# don't add attribute into skip_security if already in edited# attributes, else we may accidentaly skip a desired security checkifattrnotinself:self[attr]=defaultreturnself[attr]defupdate(self,values,skipsec=True):ifskipsec:setitem=self.__setitem__else:setitem=self.edited_attributeforattr,valueinvalues.iteritems():setitem(attr,value)defedited_attribute(self,attr,value):"""attribute being edited by a rql query: should'nt be added to skip_security """assertnotself.saved,'too late to modify edited attributes'super(EditedEntity,self).__setitem__(attr,value)self.entity.cw_attr_cache[attr]=value# mark attribute as needing purge by the clientself.entity._cw_dont_cache_attribute(attr)defoldnewvalue(self,attr):"""returns the couple (old attr value, new attr value) NOTE: will only work in a before_update_entity hook """assertnotself.saved,'too late to get the old value'# get new value and remove from local dict to force a db query to# fetch old valuenewvalue=self.entity.cw_attr_cache.pop(attr,_MARKER)oldvalue=getattr(self.entity,attr)ifnewvalueisnot_MARKER:self.entity.cw_attr_cache[attr]=newvalueelse:newvalue=oldvaluereturnoldvalue,newvaluedefset_defaults(self):"""set default values according to the schema"""forattr,valueinself.entity.e_schema.defaults():ifnotattrinself:self[str(attr)]=valuedefcheck(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.entityifcreation:# on creations, we want to check all relations, especially# required attributesrelations=[rschemaforrschemainentity.e_schema.subject_relations()ifrschema.finalandrschema.type!='eid']else:relations=[entity._cw.vreg.schema.rschema(rtype)forrtypeinself]try:entity.e_schema.check(dict_protocol_catcher(entity),creation=creation,relations=relations)exceptValidationErrorasex:ex.entity=self.entity.eidraisedefclone(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)returnthecopy