server/hook.py
changeset 2968 0e3460341023
parent 2855 1d9be3dffa94
child 3087 dbbaa628f0f9
equal deleted inserted replaced
2902:dd9f2dd02f85 2968:0e3460341023
    75         kwargs['event'] = event
    75         kwargs['event'] = event
    76         for hook in sorted(self.possible_objects(req, **kwargs), key=lambda x: x.order):
    76         for hook in sorted(self.possible_objects(req, **kwargs), key=lambda x: x.order):
    77             if hook.enabled:
    77             if hook.enabled:
    78                 hook()
    78                 hook()
    79             else:
    79             else:
    80                 warn('[3.5] %s: enabled is deprecated' % cls)
    80                 warn('[3.6] %s: enabled is deprecated' % cls)
    81 
    81 
    82 VRegistry.REGISTRY_FACTORY['hooks'] = HooksRegistry
    82 VRegistry.REGISTRY_FACTORY['hooks'] = HooksRegistry
    83 
    83 
    84 
    84 
    85 # some hook specific selectors #################################################
    85 # some hook specific selectors #################################################
   135     # XXX deprecated
   135     # XXX deprecated
   136     enabled = True
   136     enabled = True
   137 
   137 
   138     @classproperty
   138     @classproperty
   139     def __id__(cls):
   139     def __id__(cls):
   140         warn('[3.5] %s: please specify an id for your hook' % cls)
   140         warn('[3.6] %s: please specify an id for your hook' % cls)
   141         return str(id(cls))
   141         return str(id(cls))
   142 
   142 
   143     @classmethod
   143     @classmethod
   144     def __registered__(cls, vreg):
   144     def __registered__(cls, vreg):
   145         super(Hook, cls).__registered__(vreg)
   145         super(Hook, cls).__registered__(vreg)
   146         if getattr(cls, 'accepts', None):
   146         if getattr(cls, 'accepts', None):
   147             warn('[3.5] %s: accepts is deprecated, define proper __select__' % cls)
   147             warn('[3.6] %s: accepts is deprecated, define proper __select__' % cls)
   148             rtypes = []
   148             rtypes = []
   149             for ertype in cls.accepts:
   149             for ertype in cls.accepts:
   150                 if ertype.islower():
   150                 if ertype.islower():
   151                     rtypes.append(ertype)
   151                     rtypes.append(ertype)
   152                 else:
   152                 else:
   163         super(Hook, self).__init__(req, **kwargs)
   163         super(Hook, self).__init__(req, **kwargs)
   164         self.event = event
   164         self.event = event
   165 
   165 
   166     def __call__(self):
   166     def __call__(self):
   167         if hasattr(self, 'call'):
   167         if hasattr(self, 'call'):
   168             warn('[3.5] %s: call is deprecated, implements __call__' % self.__class__)
   168             warn('[3.6] %s: call is deprecated, implements __call__' % self.__class__)
   169             if self.event.endswith('_relation'):
   169             if self.event.endswith('_relation'):
   170                 self.call(self._cw, self.eidfrom, self.rtype, self.eidto)
   170                 self.call(self._cw, self.eidfrom, self.rtype, self.eidto)
   171             elif 'delete' in self.event:
   171             elif 'delete' in self.event:
   172                 self.call(self._cw, self.entity.eid)
   172                 self.call(self._cw, self.entity.eid)
   173             elif self.event.startswith('server_'):
   173             elif self.event.startswith('server_'):
   178                 self.call(self._cw, self.entity)
   178                 self.call(self._cw, self.entity)
   179 
   179 
   180 set_log_methods(Hook, getLogger('cubicweb.hook'))
   180 set_log_methods(Hook, getLogger('cubicweb.hook'))
   181 
   181 
   182 
   182 
       
   183 # base classes for relation propagation ########################################
       
   184 
       
   185 class PropagateSubjectRelationHook(Hook):
       
   186     """propagate permissions and nosy list when new entity are added"""
       
   187     events = ('after_add_relation',)
       
   188     # to set in concrete class
       
   189     rtype = None
       
   190     subject_relations = None
       
   191     object_relations = None
       
   192     accepts = None # subject_relations + object_relations
       
   193 
       
   194     def call(self, session, fromeid, rtype, toeid):
       
   195         for eid in (fromeid, toeid):
       
   196             etype = session.describe(eid)[0]
       
   197             if not self.schema.eschema(etype).has_subject_relation(self.rtype):
       
   198                 return
       
   199         if rtype in self.subject_relations:
       
   200             meid, seid = fromeid, toeid
       
   201         else:
       
   202             assert rtype in self.object_relations
       
   203             meid, seid = toeid, fromeid
       
   204         rql = 'SET E %s P WHERE X %s P, X eid %%(x)s, E eid %%(e)s, NOT E %s P'\
       
   205               % (self.rtype, self.rtype, self.rtype)
       
   206         rqls = [(rql, {'x': meid, 'e': seid}, ('x', 'e'))]
       
   207         RQLPrecommitOperation(session, rqls=rqls)
       
   208 
       
   209 
       
   210 class PropagateSubjectRelationAddHook(Hook):
       
   211     """propagate on existing entities when a permission or nosy list is added"""
       
   212     events = ('after_add_relation',)
       
   213     # to set in concrete class
       
   214     rtype = None
       
   215     subject_relations = None
       
   216     object_relations = None
       
   217     accepts = None # (self.rtype,)
       
   218 
       
   219     def call(self, session, fromeid, rtype, toeid):
       
   220         eschema = self.schema.eschema(session.describe(fromeid)[0])
       
   221         rqls = []
       
   222         for rel in self.subject_relations:
       
   223             if eschema.has_subject_relation(rel):
       
   224                 rqls.append(('SET R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
       
   225                              'X %s R, NOT R %s P' % (rtype, rel, rtype),
       
   226                              {'x': fromeid, 'p': toeid}, 'x'))
       
   227         for rel in self.object_relations:
       
   228             if eschema.has_object_relation(rel):
       
   229                 rqls.append(('SET R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
       
   230                              'R %s X, NOT R %s P' % (rtype, rel, rtype),
       
   231                              {'x': fromeid, 'p': toeid}, 'x'))
       
   232         if rqls:
       
   233             RQLPrecommitOperation(session, rqls=rqls)
       
   234 
       
   235 
       
   236 class PropagateSubjectRelationDelHook(Hook):
       
   237     """propagate on existing entities when a permission is deleted"""
       
   238     events = ('after_delete_relation',)
       
   239     # to set in concrete class
       
   240     rtype = None
       
   241     subject_relations = None
       
   242     object_relations = None
       
   243     accepts = None # (self.rtype,)
       
   244 
       
   245     def call(self, session, fromeid, rtype, toeid):
       
   246         eschema = self.schema.eschema(session.describe(fromeid)[0])
       
   247         rqls = []
       
   248         for rel in self.subject_relations:
       
   249             if eschema.has_subject_relation(rel):
       
   250                 rqls.append(('DELETE R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
       
   251                              'X %s R' % (rtype, rel),
       
   252                              {'x': fromeid, 'p': toeid}, 'x'))
       
   253         for rel in self.object_relations:
       
   254             if eschema.has_object_relation(rel):
       
   255                 rqls.append(('DELETE R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
       
   256                              'R %s X' % (rtype, rel),
       
   257                              {'x': fromeid, 'p': toeid}, 'x'))
       
   258         if rqls:
       
   259             RQLPrecommitOperation(session, rqls=rqls)
       
   260 
       
   261 
   183 # abstract classes for operation ###############################################
   262 # abstract classes for operation ###############################################
   184 
   263 
   185 class Operation(object):
   264 class Operation(object):
   186     """an operation is triggered on connections pool events related to
   265     """an operation is triggered on connections pool events related to
   187     commit / rollback transations. Possible events are:
   266     commit / rollback transations. Possible events are:
   263         do nothing by default, the operation will just be removed from the pool
   342         do nothing by default, the operation will just be removed from the pool
   264         operation list
   343         operation list
   265         """
   344         """
   266 
   345 
   267     @property
   346     @property
   268     @deprecated('[3.5] use self.session.user')
   347     @deprecated('[3.6] use self.session.user')
   269     def user(self):
   348     def user(self):
   270         return self.session.user
   349         return self.session.user
   271 
   350 
   272     @property
   351     @property
   273     @deprecated('[3.5] use self.session.repo')
   352     @deprecated('[3.6] use self.session.repo')
   274     def repo(self):
   353     def repo(self):
   275         return self.session.repo
   354         return self.session.repo
   276 
   355 
   277     @property
   356     @property
   278     @deprecated('[3.5] use self.session.vreg.schema')
   357     @deprecated('[3.6] use self.session.vreg.schema')
   279     def schema(self):
   358     def schema(self):
   280         return self.session.repo.schema
   359         return self.session.repo.schema
   281 
   360 
   282     @property
   361     @property
   283     @deprecated('[3.5] use self.session.vreg.config')
   362     @deprecated('[3.6] use self.session.vreg.config')
   284     def config(self):
   363     def config(self):
   285         return self.session.repo.config
   364         return self.session.repo.config
   286 
   365 
   287 set_log_methods(Operation, getLogger('cubicweb.session'))
   366 set_log_methods(Operation, getLogger('cubicweb.session'))
   288 
   367 
   353     def commit_event(self):
   432     def commit_event(self):
   354         self.repo.threaded_task(self.sendmails)
   433         self.repo.threaded_task(self.sendmails)
   355 
   434 
   356     def sendmails(self):
   435     def sendmails(self):
   357         self.config.sendmails(self.to_send)
   436         self.config.sendmails(self.to_send)
       
   437 
       
   438 
       
   439 class RQLPrecommitOperation(Operation):
       
   440     def precommit_event(self):
       
   441         execute = self.session.unsafe_execute
       
   442         for rql in self.rqls:
       
   443             execute(*rql)