server/hooks.py
branchtls-sprint
changeset 1802 d628defebc17
parent 1398 5fe84a5f7035
child 1977 606923dff11b
equal deleted inserted replaced
1801:672acc730ce5 1802:d628defebc17
    16                                      get_user_sessions, rproperty)
    16                                      get_user_sessions, rproperty)
    17 from cubicweb.server.repository import FTIndexEntityOp
    17 from cubicweb.server.repository import FTIndexEntityOp
    18 
    18 
    19 def relation_deleted(session, eidfrom, rtype, eidto):
    19 def relation_deleted(session, eidfrom, rtype, eidto):
    20     session.add_query_data('pendingrelations', (eidfrom, rtype, eidto))
    20     session.add_query_data('pendingrelations', (eidfrom, rtype, eidto))
    21     
    21 
    22 
    22 
    23 # base meta-data handling #####################################################
    23 # base meta-data handling #####################################################
    24 
    24 
    25 def setctime_before_add_entity(session, entity):
    25 def setctime_before_add_entity(session, entity):
    26     """before create a new entity -> set creation and modification date
    26     """before create a new entity -> set creation and modification date
    27  
    27 
    28     this is a conveniency hook, you shouldn't have to disable it
    28     this is a conveniency hook, you shouldn't have to disable it
    29     """
    29     """
    30     if not 'creation_date' in entity:
    30     if not 'creation_date' in entity:
    31         entity['creation_date'] = datetime.now()
    31         entity['creation_date'] = datetime.now()
    32     if not 'modification_date' in entity:
    32     if not 'modification_date' in entity:
    34 
    34 
    35 def setmtime_before_update_entity(session, entity):
    35 def setmtime_before_update_entity(session, entity):
    36     """update an entity -> set modification date"""
    36     """update an entity -> set modification date"""
    37     if not 'modification_date' in entity:
    37     if not 'modification_date' in entity:
    38         entity['modification_date'] = datetime.now()
    38         entity['modification_date'] = datetime.now()
    39         
    39 
    40 class SetCreatorOp(PreCommitOperation):
    40 class SetCreatorOp(PreCommitOperation):
    41         
    41 
    42     def precommit_event(self):
    42     def precommit_event(self):
    43         if self.eid in self.session.query_data('pendingeids', ()):
    43         if self.eid in self.session.query_data('pendingeids', ()):
    44             # entity have been created and deleted in the same transaction
    44             # entity have been created and deleted in the same transaction
    45             return
    45             return
    46         ueid = self.session.user.eid
    46         ueid = self.session.user.eid
    47         execute = self.session.unsafe_execute
    47         execute = self.session.unsafe_execute
    48         if not execute('Any X WHERE X created_by U, X eid %(x)s',
    48         if not execute('Any X WHERE X created_by U, X eid %(x)s',
    49                        {'x': self.eid}, 'x'): 
    49                        {'x': self.eid}, 'x'):
    50             execute('SET X created_by U WHERE X eid %(x)s, U eid %(u)s',
    50             execute('SET X created_by U WHERE X eid %(x)s, U eid %(u)s',
    51                     {'x': self.eid, 'u': ueid}, 'x')
    51                     {'x': self.eid, 'u': ueid}, 'x')
    52 
    52 
    53 def setowner_after_add_entity(session, entity):
    53 def setowner_after_add_entity(session, entity):
    54     """create a new entity -> set owner and creator metadata"""
    54     """create a new entity -> set owner and creator metadata"""
    91     entities is necessary.
    91     entities is necessary.
    92     """
    92     """
    93     if session.repo.schema.rschema(rtype).fulltext_container:
    93     if session.repo.schema.rschema(rtype).fulltext_container:
    94         FTIndexEntityOp(session, entity=session.entity(eidto))
    94         FTIndexEntityOp(session, entity=session.entity(eidto))
    95         FTIndexEntityOp(session, entity=session.entity(eidfrom))
    95         FTIndexEntityOp(session, entity=session.entity(eidfrom))
    96     
    96 
    97 class SyncOwnersOp(PreCommitOperation):
    97 class SyncOwnersOp(PreCommitOperation):
    98         
    98 
    99     def precommit_event(self):
    99     def precommit_event(self):
   100         self.session.unsafe_execute('SET X owned_by U WHERE C owned_by U, C eid %(c)s,'
   100         self.session.unsafe_execute('SET X owned_by U WHERE C owned_by U, C eid %(c)s,'
   101                                     'NOT EXISTS(X owned_by U, X eid %(x)s)',
   101                                     'NOT EXISTS(X owned_by U, X eid %(x)s)',
   102                                     {'c': self.compositeeid, 'x': self.composedeid},
   102                                     {'c': self.compositeeid, 'x': self.composedeid},
   103                                     ('c', 'x'))
   103                                     ('c', 'x'))
   104         
   104 
   105 def sync_owner_after_add_composite_relation(session, eidfrom, rtype, eidto):
   105 def sync_owner_after_add_composite_relation(session, eidfrom, rtype, eidto):
   106     """when adding composite relation, the composed should have the same owners
   106     """when adding composite relation, the composed should have the same owners
   107     has the composite
   107     has the composite
   108     """
   108     """
   109     if rtype == 'wf_info_for':
   109     if rtype == 'wf_info_for':
   112     composite = rproperty(session, rtype, eidfrom, eidto, 'composite')
   112     composite = rproperty(session, rtype, eidfrom, eidto, 'composite')
   113     if composite == 'subject':
   113     if composite == 'subject':
   114         SyncOwnersOp(session, compositeeid=eidfrom, composedeid=eidto)
   114         SyncOwnersOp(session, compositeeid=eidfrom, composedeid=eidto)
   115     elif composite == 'object':
   115     elif composite == 'object':
   116         SyncOwnersOp(session, compositeeid=eidto, composedeid=eidfrom)
   116         SyncOwnersOp(session, compositeeid=eidto, composedeid=eidfrom)
   117     
   117 
   118 def _register_metadata_hooks(hm):
   118 def _register_metadata_hooks(hm):
   119     """register meta-data related hooks on the hooks manager"""
   119     """register meta-data related hooks on the hooks manager"""
   120     hm.register_hook(setctime_before_add_entity, 'before_add_entity', '')
   120     hm.register_hook(setctime_before_add_entity, 'before_add_entity', '')
   121     hm.register_hook(setmtime_before_update_entity, 'before_update_entity', '')
   121     hm.register_hook(setmtime_before_update_entity, 'before_update_entity', '')
   122     hm.register_hook(setowner_after_add_entity, 'after_add_entity', '')
   122     hm.register_hook(setowner_after_add_entity, 'after_add_entity', '')
   125     hm.register_hook(fti_update_after_delete_relation, 'after_delete_relation', '')
   125     hm.register_hook(fti_update_after_delete_relation, 'after_delete_relation', '')
   126     if 'is' in hm.schema:
   126     if 'is' in hm.schema:
   127         hm.register_hook(setis_after_add_entity, 'after_add_entity', '')
   127         hm.register_hook(setis_after_add_entity, 'after_add_entity', '')
   128     if 'CWUser' in hm.schema:
   128     if 'CWUser' in hm.schema:
   129         hm.register_hook(setowner_after_add_user, 'after_add_entity', 'CWUser')
   129         hm.register_hook(setowner_after_add_user, 'after_add_entity', 'CWUser')
   130             
   130 
   131 # core hooks ##################################################################
   131 # core hooks ##################################################################
   132     
   132 
   133 class DelayedDeleteOp(PreCommitOperation):
   133 class DelayedDeleteOp(PreCommitOperation):
   134     """delete the object of composite relation except if the relation
   134     """delete the object of composite relation except if the relation
   135     has actually been redirected to another composite
   135     has actually been redirected to another composite
   136     """
   136     """
   137         
   137 
   138     def precommit_event(self):
   138     def precommit_event(self):
   139         session = self.session
   139         session = self.session
   140         if not self.eid in session.query_data('pendingeids', ()):
   140         if not self.eid in session.query_data('pendingeids', ()):
   141             etype = session.describe(self.eid)[0]
   141             etype = session.describe(self.eid)[0]
   142             session.unsafe_execute('DELETE %s X WHERE X eid %%(x)s, NOT %s'
   142             session.unsafe_execute('DELETE %s X WHERE X eid %%(x)s, NOT %s'
   143                                    % (etype, self.relation),
   143                                    % (etype, self.relation),
   144                                    {'x': self.eid}, 'x')
   144                                    {'x': self.eid}, 'x')
   145     
   145 
   146 def handle_composite_before_del_relation(session, eidfrom, rtype, eidto):
   146 def handle_composite_before_del_relation(session, eidfrom, rtype, eidto):
   147     """delete the object of composite relation"""
   147     """delete the object of composite relation"""
   148     composite = rproperty(session, rtype, eidfrom, eidto, 'composite')
   148     composite = rproperty(session, rtype, eidfrom, eidto, 'composite')
   149     if composite == 'subject':
   149     if composite == 'subject':
   150         DelayedDeleteOp(session, eid=eidto, relation='Y %s X' % rtype)
   150         DelayedDeleteOp(session, eid=eidto, relation='Y %s X' % rtype)
   155     """check that we don't remove the owners group"""
   155     """check that we don't remove the owners group"""
   156     check_internal_entity(session, eid, ('owners',))
   156     check_internal_entity(session, eid, ('owners',))
   157 
   157 
   158 
   158 
   159 # schema validation hooks #####################################################
   159 # schema validation hooks #####################################################
   160         
   160 
   161 class CheckConstraintsOperation(LateOperation):
   161 class CheckConstraintsOperation(LateOperation):
   162     """check a new relation satisfy its constraints
   162     """check a new relation satisfy its constraints
   163     """
   163     """
   164     def precommit_event(self):
   164     def precommit_event(self):
   165         eidfrom, rtype, eidto = self.rdef
   165         eidfrom, rtype, eidto = self.rdef
   174             try:
   174             try:
   175                 constraint.repo_check(self.session, eidfrom, rtype, eidto)
   175                 constraint.repo_check(self.session, eidfrom, rtype, eidto)
   176             except NotImplementedError:
   176             except NotImplementedError:
   177                 self.critical('can\'t check constraint %s, not supported',
   177                 self.critical('can\'t check constraint %s, not supported',
   178                               constraint)
   178                               constraint)
   179     
   179 
   180     def commit_event(self):
   180     def commit_event(self):
   181         pass
   181         pass
   182     
   182 
   183 def cstrcheck_after_add_relation(session, eidfrom, rtype, eidto):
   183 def cstrcheck_after_add_relation(session, eidfrom, rtype, eidto):
   184     """check the relation satisfy its constraints
   184     """check the relation satisfy its constraints
   185 
   185 
   186     this is delayed to a precommit time operation since other relation which
   186     this is delayed to a precommit time operation since other relation which
   187     will make constraint satisfied may be added later.
   187     will make constraint satisfied may be added later.
   211 class CheckRequiredRelationOperation(LateOperation):
   211 class CheckRequiredRelationOperation(LateOperation):
   212     """checking relation cardinality has to be done after commit in
   212     """checking relation cardinality has to be done after commit in
   213     case the relation is being replaced
   213     case the relation is being replaced
   214     """
   214     """
   215     eid, rtype = None, None
   215     eid, rtype = None, None
   216     
   216 
   217     def precommit_event(self):
   217     def precommit_event(self):
   218         # recheck pending eids
   218         # recheck pending eids
   219         if self.eid in self.session.query_data('pendingeids', ()):
   219         if self.eid in self.session.query_data('pendingeids', ()):
   220             return
   220             return
   221         if self.session.unsafe_execute(*self._rql()).rowcount < 1:
   221         if self.session.unsafe_execute(*self._rql()).rowcount < 1:
   222             etype = self.session.describe(self.eid)[0]
   222             etype = self.session.describe(self.eid)[0]
   223             msg = self.session._('at least one relation %(rtype)s is required on %(etype)s (%(eid)s)')
   223             msg = self.session._('at least one relation %(rtype)s is required on %(etype)s (%(eid)s)')
   224             raise ValidationError(self.eid, {self.rtype: msg % {'rtype': self.rtype,
   224             raise ValidationError(self.eid, {self.rtype: msg % {'rtype': self.rtype,
   225                                                                 'etype': etype,
   225                                                                 'etype': etype,
   226                                                                 'eid': self.eid}})
   226                                                                 'eid': self.eid}})
   227     
   227 
   228     def commit_event(self):
   228     def commit_event(self):
   229         pass
   229         pass
   230         
   230 
   231     def _rql(self):
   231     def _rql(self):
   232         raise NotImplementedError()
   232         raise NotImplementedError()
   233     
   233 
   234 class CheckSRelationOp(CheckRequiredRelationOperation):
   234 class CheckSRelationOp(CheckRequiredRelationOperation):
   235     """check required subject relation"""
   235     """check required subject relation"""
   236     def _rql(self):
   236     def _rql(self):
   237         return 'Any O WHERE S eid %%(x)s, S %s O' % self.rtype, {'x': self.eid}, 'x'
   237         return 'Any O WHERE S eid %%(x)s, S %s O' % self.rtype, {'x': self.eid}, 'x'
   238     
   238 
   239 class CheckORelationOp(CheckRequiredRelationOperation):
   239 class CheckORelationOp(CheckRequiredRelationOperation):
   240     """check required object relation"""
   240     """check required object relation"""
   241     def _rql(self):
   241     def _rql(self):
   242         return 'Any S WHERE O eid %%(x)s, S %s O' % self.rtype, {'x': self.eid}, 'x'
   242         return 'Any S WHERE O eid %%(x)s, S %s O' % self.rtype, {'x': self.eid}, 'x'
   243 
   243 
   246     for op in session.pending_operations:
   246     for op in session.pending_operations:
   247         if isinstance(op, opcls) and op.rtype == rtype and op.eid == eid:
   247         if isinstance(op, opcls) and op.rtype == rtype and op.eid == eid:
   248             break
   248             break
   249     else:
   249     else:
   250         opcls(session, rtype=rtype, eid=eid)
   250         opcls(session, rtype=rtype, eid=eid)
   251     
   251 
   252 def cardinalitycheck_after_add_entity(session, entity):
   252 def cardinalitycheck_after_add_entity(session, entity):
   253     """check cardinalities are satisfied"""
   253     """check cardinalities are satisfied"""
   254     eid = entity.eid
   254     eid = entity.eid
   255     for rschema, targetschemas, x in entity.e_schema.relation_definitions():
   255     for rschema, targetschemas, x in entity.e_schema.relation_definitions():
   256         # skip automatically handled relations
   256         # skip automatically handled relations
   281 
   281 
   282 
   282 
   283 def _register_core_hooks(hm):
   283 def _register_core_hooks(hm):
   284     hm.register_hook(handle_composite_before_del_relation, 'before_delete_relation', '')
   284     hm.register_hook(handle_composite_before_del_relation, 'before_delete_relation', '')
   285     hm.register_hook(before_del_group, 'before_delete_entity', 'CWGroup')
   285     hm.register_hook(before_del_group, 'before_delete_entity', 'CWGroup')
   286     
   286 
   287     #hm.register_hook(cstrcheck_before_update_entity, 'before_update_entity', '')
   287     #hm.register_hook(cstrcheck_before_update_entity, 'before_update_entity', '')
   288     hm.register_hook(cardinalitycheck_after_add_entity, 'after_add_entity', '')
   288     hm.register_hook(cardinalitycheck_after_add_entity, 'after_add_entity', '')
   289     hm.register_hook(cardinalitycheck_before_del_relation, 'before_delete_relation', '')
   289     hm.register_hook(cardinalitycheck_before_del_relation, 'before_delete_relation', '')
   290     hm.register_hook(cstrcheck_after_add_relation, 'after_add_relation', '')
   290     hm.register_hook(cstrcheck_after_add_relation, 'after_add_relation', '')
   291     hm.register_hook(uniquecstrcheck_before_modification, 'before_add_entity', '')
   291     hm.register_hook(uniquecstrcheck_before_modification, 'before_add_entity', '')
   292     hm.register_hook(uniquecstrcheck_before_modification, 'before_update_entity', '')
   292     hm.register_hook(uniquecstrcheck_before_modification, 'before_update_entity', '')
   293 
   293 
   294 
   294 
   295 # user/groups synchronisation #################################################
   295 # user/groups synchronisation #################################################
   296             
   296 
   297 class GroupOperation(Operation):
   297 class GroupOperation(Operation):
   298     """base class for group operation"""
   298     """base class for group operation"""
   299     geid = None
   299     geid = None
   300     def __init__(self, session, *args, **kwargs):
   300     def __init__(self, session, *args, **kwargs):
   301         """override to get the group name before actual groups manipulation:
   301         """override to get the group name before actual groups manipulation:
   302         
   302 
   303         we may temporarily loose right access during a commit event, so
   303         we may temporarily loose right access during a commit event, so
   304         no query should be emitted while comitting
   304         no query should be emitted while comitting
   305         """
   305         """
   306         rql = 'Any N WHERE G eid %(x)s, G name N'
   306         rql = 'Any N WHERE G eid %(x)s, G name N'
   307         result = session.execute(rql, {'x': kwargs['geid']}, 'x', build_descr=False)
   307         result = session.execute(rql, {'x': kwargs['geid']}, 'x', build_descr=False)
   316         try:
   316         try:
   317             groups.remove(self.group)
   317             groups.remove(self.group)
   318         except KeyError:
   318         except KeyError:
   319             self.error('user %s not in group %s',  self.cnxuser, self.group)
   319             self.error('user %s not in group %s',  self.cnxuser, self.group)
   320             return
   320             return
   321     
   321 
   322 def after_del_in_group(session, fromeid, rtype, toeid):
   322 def after_del_in_group(session, fromeid, rtype, toeid):
   323     """modify user permission, need to update users"""
   323     """modify user permission, need to update users"""
   324     for session_ in get_user_sessions(session.repo, fromeid):
   324     for session_ in get_user_sessions(session.repo, fromeid):
   325         DeleteGroupOp(session, cnxuser=session_.user, geid=toeid)
   325         DeleteGroupOp(session, cnxuser=session_.user, geid=toeid)
   326 
   326 
   327         
   327 
   328 class AddGroupOp(GroupOperation):
   328 class AddGroupOp(GroupOperation):
   329     """synchronize user when a in_group relation has been added"""
   329     """synchronize user when a in_group relation has been added"""
   330     def commit_event(self):
   330     def commit_event(self):
   331         """the observed connections pool has been commited"""
   331         """the observed connections pool has been commited"""
   332         groups = self.cnxuser.groups
   332         groups = self.cnxuser.groups
   345 class DelUserOp(Operation):
   345 class DelUserOp(Operation):
   346     """synchronize user when a in_group relation has been added"""
   346     """synchronize user when a in_group relation has been added"""
   347     def __init__(self, session, cnxid):
   347     def __init__(self, session, cnxid):
   348         self.cnxid = cnxid
   348         self.cnxid = cnxid
   349         Operation.__init__(self, session)
   349         Operation.__init__(self, session)
   350         
   350 
   351     def commit_event(self):
   351     def commit_event(self):
   352         """the observed connections pool has been commited"""
   352         """the observed connections pool has been commited"""
   353         try:
   353         try:
   354             self.repo.close(self.cnxid)
   354             self.repo.close(self.cnxid)
   355         except BadConnectionId:
   355         except BadConnectionId:
   357 
   357 
   358 def after_del_user(session, eid):
   358 def after_del_user(session, eid):
   359     """modify user permission, need to update users"""
   359     """modify user permission, need to update users"""
   360     for session_ in get_user_sessions(session.repo, eid):
   360     for session_ in get_user_sessions(session.repo, eid):
   361         DelUserOp(session, session_.id)
   361         DelUserOp(session, session_.id)
   362     
   362 
   363 def _register_usergroup_hooks(hm):
   363 def _register_usergroup_hooks(hm):
   364     """register user/group related hooks on the hooks manager"""
   364     """register user/group related hooks on the hooks manager"""
   365     hm.register_hook(after_del_user, 'after_delete_entity', 'CWUser')
   365     hm.register_hook(after_del_user, 'after_delete_entity', 'CWUser')
   366     hm.register_hook(after_add_in_group, 'after_add_relation', 'in_group')
   366     hm.register_hook(after_add_in_group, 'after_add_relation', 'in_group')
   367     hm.register_hook(after_del_in_group, 'after_delete_relation', 'in_group')
   367     hm.register_hook(after_del_in_group, 'after_delete_relation', 'in_group')
   428                                    {'x' : entity.eid, 's' : rset[0][0]}, 'x')
   428                                    {'x' : entity.eid, 's' : rset[0][0]}, 'x')
   429 
   429 
   430 
   430 
   431 def set_initial_state_after_add(session, entity):
   431 def set_initial_state_after_add(session, entity):
   432     SetInitialStateOp(session, entity=entity)
   432     SetInitialStateOp(session, entity=entity)
   433     
   433 
   434 def _register_wf_hooks(hm):
   434 def _register_wf_hooks(hm):
   435     """register workflow related hooks on the hooks manager"""
   435     """register workflow related hooks on the hooks manager"""
   436     if 'in_state' in hm.schema:
   436     if 'in_state' in hm.schema:
   437         hm.register_hook(before_add_in_state, 'before_add_relation', 'in_state')
   437         hm.register_hook(before_add_in_state, 'before_add_relation', 'in_state')
   438         hm.register_hook(relation_deleted, 'before_delete_relation', 'in_state')
   438         hm.register_hook(relation_deleted, 'before_delete_relation', 'in_state')
   445 # CWProperty hooks #############################################################
   445 # CWProperty hooks #############################################################
   446 
   446 
   447 
   447 
   448 class DelCWPropertyOp(Operation):
   448 class DelCWPropertyOp(Operation):
   449     """a user's custom properties has been deleted"""
   449     """a user's custom properties has been deleted"""
   450     
   450 
   451     def commit_event(self):
   451     def commit_event(self):
   452         """the observed connections pool has been commited"""
   452         """the observed connections pool has been commited"""
   453         try:
   453         try:
   454             del self.epropdict[self.key]
   454             del self.epropdict[self.key]
   455         except KeyError:
   455         except KeyError:
   456             self.error('%s has no associated value', self.key)
   456             self.error('%s has no associated value', self.key)
   457 
   457 
   458 class ChangeCWPropertyOp(Operation):
   458 class ChangeCWPropertyOp(Operation):
   459     """a user's custom properties has been added/changed"""
   459     """a user's custom properties has been added/changed"""
   460         
   460 
   461     def commit_event(self):
   461     def commit_event(self):
   462         """the observed connections pool has been commited"""
   462         """the observed connections pool has been commited"""
   463         self.epropdict[self.key] = self.value
   463         self.epropdict[self.key] = self.value
   464 
   464 
   465 class AddCWPropertyOp(Operation):
   465 class AddCWPropertyOp(Operation):
   466     """a user's custom properties has been added/changed"""
   466     """a user's custom properties has been added/changed"""
   467         
   467 
   468     def commit_event(self):
   468     def commit_event(self):
   469         """the observed connections pool has been commited"""
   469         """the observed connections pool has been commited"""
   470         eprop = self.eprop
   470         eprop = self.eprop
   471         if not eprop.for_user:
   471         if not eprop.for_user:
   472             self.repo.vreg.eprop_values[eprop.pkey] = eprop.value
   472             self.repo.vreg.eprop_values[eprop.pkey] = eprop.value
   483     if not session.user.matching_groups('managers'):
   483     if not session.user.matching_groups('managers'):
   484         session.unsafe_execute('SET P for_user U WHERE P eid %(x)s,U eid %(u)s',
   484         session.unsafe_execute('SET P for_user U WHERE P eid %(x)s,U eid %(u)s',
   485                                {'x': entity.eid, 'u': session.user.eid}, 'x')
   485                                {'x': entity.eid, 'u': session.user.eid}, 'x')
   486     else:
   486     else:
   487         AddCWPropertyOp(session, eprop=entity)
   487         AddCWPropertyOp(session, eprop=entity)
   488         
   488 
   489 def after_update_eproperty(session, entity):
   489 def after_update_eproperty(session, entity):
   490     key, value = entity.pkey, entity.value
   490     key, value = entity.pkey, entity.value
   491     try:
   491     try:
   492         value = session.vreg.typed_value(key, value)
   492         value = session.vreg.typed_value(key, value)
   493     except UnknownProperty:
   493     except UnknownProperty:
   500                               key=key, value=value)
   500                               key=key, value=value)
   501     else:
   501     else:
   502         # site wide properties
   502         # site wide properties
   503         ChangeCWPropertyOp(session, epropdict=session.vreg.eprop_values,
   503         ChangeCWPropertyOp(session, epropdict=session.vreg.eprop_values,
   504                           key=key, value=value)
   504                           key=key, value=value)
   505         
   505 
   506 def before_del_eproperty(session, eid):
   506 def before_del_eproperty(session, eid):
   507     for eidfrom, rtype, eidto in session.query_data('pendingrelations', ()):
   507     for eidfrom, rtype, eidto in session.query_data('pendingrelations', ()):
   508         if rtype == 'for_user' and eidfrom == eid:
   508         if rtype == 'for_user' and eidfrom == eid:
   509             # if for_user was set, delete has already been handled
   509             # if for_user was set, delete has already been handled
   510             break
   510             break
   522         raise ValidationError(fromeid,
   522         raise ValidationError(fromeid,
   523                               {'for_user': session._("site-wide property can't be set for user")})
   523                               {'for_user': session._("site-wide property can't be set for user")})
   524     for session_ in get_user_sessions(session.repo, toeid):
   524     for session_ in get_user_sessions(session.repo, toeid):
   525         ChangeCWPropertyOp(session, epropdict=session_.user.properties,
   525         ChangeCWPropertyOp(session, epropdict=session_.user.properties,
   526                           key=key, value=value)
   526                           key=key, value=value)
   527         
   527 
   528 def before_del_for_user(session, fromeid, rtype, toeid):
   528 def before_del_for_user(session, fromeid, rtype, toeid):
   529     key = session.execute('Any K WHERE P eid %(x)s, P pkey K',
   529     key = session.execute('Any K WHERE P eid %(x)s, P pkey K',
   530                           {'x': fromeid}, 'x')[0][0]
   530                           {'x': fromeid}, 'x')[0][0]
   531     relation_deleted(session, fromeid, rtype, toeid)
   531     relation_deleted(session, fromeid, rtype, toeid)
   532     for session_ in get_user_sessions(session.repo, toeid):
   532     for session_ in get_user_sessions(session.repo, toeid):