[uicfg] fix autoform_section rtags initialization
1. when a relation is marked as inlined in the 'main' form type, we want
its opposite (eg when one the other side of the relation) to be
marked as hidden in the 'inlined' form type
2. when no section is specified for the 'inlined' form type, use the same
as in the 'main' form type
to do this properly, we need two initialization stages. The first one
to handle 1., the second to handle what was done before and 2.
We can't do this in a single stage because we've to know the bare
value of the "opposite" tag.
"""Core hooks: check for data integrity according to the instance'schemavalidity:organization: Logilab:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"fromcubicwebimportValidationErrorfromcubicweb.schemaimportRQLConstraint,RQLUniqueConstraintfromcubicweb.selectorsimportimplementsfromcubicweb.uilibimportsoup2xhtmlfromcubicweb.serverimporthook# special relations that don't have to be checked for integrity, usually# because they are handled internally by hooks (so we trust ourselves)DONT_CHECK_RTYPES_ON_ADD=set(('owned_by','created_by','is','is_instance_of','wf_info_for','from_state','to_state'))DONT_CHECK_RTYPES_ON_DEL=set(('is','is_instance_of','wf_info_for','from_state','to_state'))class_CheckRequiredRelationOperation(hook.LateOperation):"""checking relation cardinality has to be done after commit in case the relation is being replaced """eid,rtype=None,Nonedefprecommit_event(self):# recheck pending eidsifself.session.deleted_in_transaction(self.eid):returnifself.rtypeinself.session.transaction_data.get('pendingrtypes',()):returnifself.session.unsafe_execute(*self._rql()).rowcount<1:etype=self.session.describe(self.eid)[0]_=self.session._msg=_('at least one relation %(rtype)s is required on %(etype)s (%(eid)s)')msg%={'rtype':_(self.rtype),'etype':_(etype),'eid':self.eid}raiseValidationError(self.eid,{self.rtype:msg})defcommit_event(self):passdef_rql(self):raiseNotImplementedError()class_CheckSRelationOp(_CheckRequiredRelationOperation):"""check required subject relation"""def_rql(self):return'Any O WHERE S eid %%(x)s, S %s O'%self.rtype,{'x':self.eid},'x'class_CheckORelationOp(_CheckRequiredRelationOperation):"""check required object relation"""def_rql(self):return'Any S WHERE O eid %%(x)s, S %s O'%self.rtype,{'x':self.eid},'x'classIntegrityHook(hook.Hook):__abstract__=Truecategory='integrity'classUserIntegrityHook(IntegrityHook):__abstract__=True__select__=IntegrityHook.__select__&hook.regular_session()classCheckCardinalityHook(UserIntegrityHook):"""check cardinalities are satisfied"""__regid__='checkcard'events=('after_add_entity','before_delete_relation')def__call__(self):getattr(self,self.event)()defcheckrel_if_necessary(self,opcls,rtype,eid):"""check an equivalent operation has not already been added"""foropinself._cw.pending_operations:ifisinstance(op,opcls)andop.rtype==rtypeandop.eid==eid:breakelse:opcls(self._cw,rtype=rtype,eid=eid)defafter_add_entity(self):eid=self.entity.eideschema=self.entity.e_schemaforrschema,targetschemas,roleineschema.relation_definitions():# skip automatically handled relationsifrschema.typeinDONT_CHECK_RTYPES_ON_ADD:continueopcls=role=='subject'and_CheckSRelationOpor_CheckORelationOprdef=rschema.role_rdef(eschema,targetschemas[0],role)ifrdef.role_cardinality(role)in'1+':self.checkrel_if_necessary(opcls,rschema.type,eid)defbefore_delete_relation(self):rtype=self.rtypeifrtypeinDONT_CHECK_RTYPES_ON_DEL:returnsession=self._cweidfrom,eidto=self.eidfrom,self.eidtocard=session.schema_rproperty(rtype,eidfrom,eidto,'cardinality')pendingrdefs=session.transaction_data.get('pendingrdefs',())if(session.describe(eidfrom)[0],rtype,session.describe(eidto)[0])inpendingrdefs:returnifcard[0]in'1+'andnotsession.deleted_in_transaction(eidfrom):self.checkrel_if_necessary(_CheckSRelationOp,rtype,eidfrom)ifcard[1]in'1+'andnotsession.deleted_in_transaction(eidto):self.checkrel_if_necessary(_CheckORelationOp,rtype,eidto)class_CheckConstraintsOp(hook.LateOperation):"""check a new relation satisfy its constraints """defprecommit_event(self):eidfrom,rtype,eidto=self.rdef# first check related entities have not been deleted in the same# transactionifself.session.deleted_in_transaction(eidfrom):returnifself.session.deleted_in_transaction(eidto):returnforconstraintinself.constraints:try:constraint.repo_check(self.session,eidfrom,rtype,eidto)exceptNotImplementedError:self.critical('can\'t check constraint %s, not supported',constraint)defcommit_event(self):passclassCheckConstraintHook(UserIntegrityHook):"""check the relation satisfy its constraints this is delayed to a precommit time operation since other relation which will make constraint satisfied (or unsatisfied) may be added later. """__regid__='checkconstraint'events=('after_add_relation',)def__call__(self):# XXX get only RQL[Unique]Constraints?constraints=self._cw.schema_rproperty(self.rtype,self.eidfrom,self.eidto,'constraints')ifconstraints:_CheckConstraintsOp(self._cw,constraints=constraints,rdef=(self.eidfrom,self.rtype,self.eidto))classCheckAttributeConstraintHook(UserIntegrityHook):"""check the attribute relation satisfy its constraints this is delayed to a precommit time operation since other relation which will make constraint satisfied (or unsatisfied) may be added later. """__regid__='checkattrconstraint'events=('after_add_entity','after_update_entity')def__call__(self):eschema=self.entity.e_schemaforattrinself.entity.edited_attributes:ifeschema.subjrels[attr].final:constraints=[cforcineschema.rdef(attr).constraintsifisinstance(c,(RQLUniqueConstraint,RQLConstraint))]ifconstraints:_CheckConstraintsOp(self._cw,constraints=constraints,rdef=(self.entity.eid,attr,None))classCheckUniqueHook(UserIntegrityHook):__regid__='checkunique'events=('before_add_entity','before_update_entity')def__call__(self):entity=self.entityeschema=entity.e_schemaforattrinentity.edited_attributes:ifeschema.subjrels[attr].finalandeschema.has_unique_values(attr):val=entity[attr]ifvalisNone:continuerql='%s X WHERE X %s%%(val)s'%(entity.e_schema,attr)rset=self._cw.unsafe_execute(rql,{'val':val})ifrsetandrset[0][0]!=entity.eid:msg=self._cw._('the value "%s" is already used, use another one')raiseValidationError(entity.eid,{attr:msg%val})class_DelayedDeleteOp(hook.Operation):"""delete the object of composite relation except if the relation has actually been redirected to another composite """defprecommit_event(self):session=self.session# don't do anything if the entity is being created or deletedifnot(session.deleted_in_transaction(self.eid)orsession.added_in_transaction(self.eid)):etype=session.describe(self.eid)[0]session.unsafe_execute('DELETE %s X WHERE X eid %%(x)s, NOT %s'%(etype,self.relation),{'x':self.eid},'x')classDeleteCompositeOrphanHook(IntegrityHook):"""delete the composed of a composite relation when this relation is deleted """__regid__='deletecomposite'events=('before_delete_relation',)def__call__(self):# if the relation is being delete, don't delete composite's components# automaticallypendingrdefs=self._cw.transaction_data.get('pendingrdefs',())if(self._cw.describe(self.eidfrom)[0],self.rtype,self._cw.describe(self.eidto)[0])inpendingrdefs:returncomposite=self._cw.schema_rproperty(self.rtype,self.eidfrom,self.eidto,'composite')ifcomposite=='subject':_DelayedDeleteOp(self._cw,eid=self.eidto,relation='Y %s X'%self.rtype)elifcomposite=='object':_DelayedDeleteOp(self._cw,eid=self.eidfrom,relation='X %s Y'%self.rtype)classDontRemoveOwnersGroupHook(IntegrityHook):"""delete the composed of a composite relation when this relation is deleted """__regid__='checkownersgroup'__select__=IntegrityHook.__select__&implements('CWGroup')events=('before_delete_entity','before_update_entity')def__call__(self):ifself.event=='before_delete_entity'andself.entity.name=='owners':raiseValidationError(self.entity.eid,{None:self._cw._('can\'t be deleted')})elifself.event=='before_update_entity'and'name'inself.entity.edited_attributes:newname=self.entity.pop('name')oldname=self.entity.nameifoldname=='owners'andnewname!=oldname:raiseValidationError(self.entity.eid,{'name':self._cw._('can\'t be changed')})self.entity['name']=newnameclassTidyHtmlFields(UserIntegrityHook):"""tidy HTML in rich text strings"""__regid__='htmltidy'events=('before_add_entity','before_update_entity')def__call__(self):entity=self.entitymetaattrs=entity.e_schema.meta_attributes()formetaattr,(metadata,attr)inmetaattrs.iteritems():ifmetadata=='format'andattrinentity.edited_attributes:try:value=entity[attr]exceptKeyError:continue# no text to tidyifisinstance(value,unicode):# filter out None and Binaryifgetattr(entity,str(metaattr))=='text/html':entity[attr]=soup2xhtml(value,self._cw.encoding)classStripCWUserLoginHook(IntegrityHook):"""ensure user logins are stripped"""__regid__='stripuserlogin'__select__=IntegrityHook.__select__&implements('CWUser')events=('before_add_entity','before_update_entity',)def__call__(self):user=self.entityif'login'inuser.edited_attributesanduser.login:user.login=user.login.strip()