server/hook.py
changeset 4835 13b0b96d7982
parent 4834 b718626a0e60
child 4845 dc351b96f596
equal deleted inserted replaced
4834:b718626a0e60 4835:13b0b96d7982
    31 :organization: Logilab
    31 :organization: Logilab
    32 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
    32 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
    33 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
    33 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
    34 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
    34 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
    35 """
    35 """
       
    36 from __future__ import with_statement
       
    37 
    36 __docformat__ = "restructuredtext en"
    38 __docformat__ = "restructuredtext en"
    37 
    39 
    38 from warnings import warn
    40 from warnings import warn
    39 from logging import getLogger
    41 from logging import getLogger
    40 from itertools import chain
    42 from itertools import chain
    45 
    47 
    46 from cubicweb.cwvreg import CWRegistry, VRegistry
    48 from cubicweb.cwvreg import CWRegistry, VRegistry
    47 from cubicweb.selectors import (objectify_selector, lltrace, ExpectedValueSelector,
    49 from cubicweb.selectors import (objectify_selector, lltrace, ExpectedValueSelector,
    48                                 implements)
    50                                 implements)
    49 from cubicweb.appobject import AppObject
    51 from cubicweb.appobject import AppObject
    50 
    52 from cubicweb.server.session import security_enabled
    51 
    53 
    52 ENTITIES_HOOKS = set(('before_add_entity',    'after_add_entity',
    54 ENTITIES_HOOKS = set(('before_add_entity',    'after_add_entity',
    53                       'before_update_entity', 'after_update_entity',
    55                       'before_update_entity', 'after_update_entity',
    54                       'before_delete_entity', 'after_delete_entity'))
    56                       'before_delete_entity', 'after_delete_entity'))
    55 RELATIONS_HOOKS = set(('before_add_relation',   'after_add_relation' ,
    57 RELATIONS_HOOKS = set(('before_add_relation',   'after_add_relation' ,
    74             if event not in ALL_HOOKS:
    76             if event not in ALL_HOOKS:
    75                 raise Exception('bad event %s on %s.%s' % (
    77                 raise Exception('bad event %s on %s.%s' % (
    76                     event, obj.__module__, obj.__name__))
    78                     event, obj.__module__, obj.__name__))
    77         super(HooksRegistry, self).register(obj, **kwargs)
    79         super(HooksRegistry, self).register(obj, **kwargs)
    78 
    80 
    79     def call_hooks(self, event, req=None, **kwargs):
    81     def call_hooks(self, event, session=None, **kwargs):
    80         kwargs['event'] = event
    82         kwargs['event'] = event
    81         for hook in sorted(self.possible_objects(req, **kwargs), key=lambda x: x.order):
    83         if session is None:
    82             if hook.enabled:
    84             for hook in sorted(self.possible_objects(session, **kwargs),
       
    85                                key=lambda x: x.order):
    83                 hook()
    86                 hook()
    84             else:
    87         else:
    85                 warn('[3.6] %s: enabled is deprecated' % hook.__class__)
    88             # by default, hooks are executed with security turned off
       
    89             with security_enabled(session, read=False):
       
    90                 hooks = sorted(self.possible_objects(session, **kwargs),
       
    91                                key=lambda x: x.order)
       
    92                 with security_enabled(session, write=False):
       
    93                     for hook in hooks:
       
    94                         hook()
    86 
    95 
    87 VRegistry.REGISTRY_FACTORY['hooks'] = HooksRegistry
    96 VRegistry.REGISTRY_FACTORY['hooks'] = HooksRegistry
    88 
    97 
    89 
    98 
    90 def entity_oldnewvalue(entity, attr):
    99 def entity_oldnewvalue(entity, attr):
   102 
   111 
   103 # some hook specific selectors #################################################
   112 # some hook specific selectors #################################################
   104 
   113 
   105 @objectify_selector
   114 @objectify_selector
   106 @lltrace
   115 @lltrace
       
   116 def _bw_is_enabled(cls, req, **kwargs):
       
   117     if cls.enabled:
       
   118         return 1
       
   119     warn('[3.6] %s: enabled is deprecated' % cls)
       
   120     return 0
       
   121 
       
   122 @objectify_selector
       
   123 @lltrace
   107 def match_event(cls, req, **kwargs):
   124 def match_event(cls, req, **kwargs):
   108     if kwargs.get('event') in cls.events:
   125     if kwargs.get('event') in cls.events:
   109         return 1
   126         return 1
   110     return 0
   127     return 0
   111 
   128 
   116         return True # XXX how to deactivate server startup / shutdown event
   133         return True # XXX how to deactivate server startup / shutdown event
   117     return req.is_hook_activated(cls)
   134     return req.is_hook_activated(cls)
   118 
   135 
   119 @objectify_selector
   136 @objectify_selector
   120 @lltrace
   137 @lltrace
   121 def regular_session(cls, req, **kwargs):
   138 def from_dbapi_query(cls, req, **kwargs):
   122     if req is None or req.is_super_session:
   139     if req.running_dbapi_query:
   123         return 0
   140         return 1
   124     return 1
   141     return 0
   125 
       
   126 
   142 
   127 class rechain(object):
   143 class rechain(object):
   128     def __init__(self, *iterators):
   144     def __init__(self, *iterators):
   129         self.iterators = iterators
   145         self.iterators = iterators
   130     def __iter__(self):
   146     def __iter__(self):
   173 
   189 
   174 # base class for hook ##########################################################
   190 # base class for hook ##########################################################
   175 
   191 
   176 class Hook(AppObject):
   192 class Hook(AppObject):
   177     __registry__ = 'hooks'
   193     __registry__ = 'hooks'
   178     __select__ = match_event() & enabled_category()
   194     __select__ = match_event() & enabled_category() & _bw_is_enabled()
   179     # set this in derivated classes
   195     # set this in derivated classes
   180     events = None
   196     events = None
   181     category = None
   197     category = None
   182     order = 0
   198     order = 0
   183     # XXX deprecated
   199     # XXX deprecated
   258         if self.rtype in self.subject_relations:
   274         if self.rtype in self.subject_relations:
   259             meid, seid = self.eidfrom, self.eidto
   275             meid, seid = self.eidfrom, self.eidto
   260         else:
   276         else:
   261             assert self.rtype in self.object_relations
   277             assert self.rtype in self.object_relations
   262             meid, seid = self.eidto, self.eidfrom
   278             meid, seid = self.eidto, self.eidfrom
   263         self._cw.unsafe_execute(
   279         self._cw.execute(
   264             'SET E %s P WHERE X %s P, X eid %%(x)s, E eid %%(e)s, NOT E %s P'\
   280             'SET E %s P WHERE X %s P, X eid %%(x)s, E eid %%(e)s, NOT E %s P'\
   265             % (self.main_rtype, self.main_rtype, self.main_rtype),
   281             % (self.main_rtype, self.main_rtype, self.main_rtype),
   266             {'x': meid, 'e': seid}, ('x', 'e'))
   282             {'x': meid, 'e': seid}, ('x', 'e'))
   267 
   283 
   268 
   284 
   276     subject_relations = None
   292     subject_relations = None
   277     object_relations = None
   293     object_relations = None
   278 
   294 
   279     def __call__(self):
   295     def __call__(self):
   280         eschema = self._cw.vreg.schema.eschema(self._cw.describe(self.eidfrom)[0])
   296         eschema = self._cw.vreg.schema.eschema(self._cw.describe(self.eidfrom)[0])
   281         execute = self._cw.unsafe_execute
   297         execute = self._cw.execute
   282         for rel in self.subject_relations:
   298         for rel in self.subject_relations:
   283             if rel in eschema.subjrels:
   299             if rel in eschema.subjrels:
   284                 execute('SET R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
   300                 execute('SET R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
   285                         'X %s R, NOT R %s P' % (self.rtype, rel, self.rtype),
   301                         'X %s R, NOT R %s P' % (self.rtype, rel, self.rtype),
   286                         {'x': self.eidfrom, 'p': self.eidto}, 'x')
   302                         {'x': self.eidfrom, 'p': self.eidto}, 'x')
   301     subject_relations = None
   317     subject_relations = None
   302     object_relations = None
   318     object_relations = None
   303 
   319 
   304     def __call__(self):
   320     def __call__(self):
   305         eschema = self._cw.vreg.schema.eschema(self._cw.describe(self.eidfrom)[0])
   321         eschema = self._cw.vreg.schema.eschema(self._cw.describe(self.eidfrom)[0])
   306         execute = self._cw.unsafe_execute
   322         execute = self._cw.execute
   307         for rel in self.subject_relations:
   323         for rel in self.subject_relations:
   308             if rel in eschema.subjrels:
   324             if rel in eschema.subjrels:
   309                 execute('DELETE R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
   325                 execute('DELETE R %s P WHERE X eid %%(x)s, P eid %%(p)s, '
   310                         'X %s R' % (self.rtype, rel),
   326                         'X %s R' % (self.rtype, rel),
   311                         {'x': self.eidfrom, 'p': self.eidto}, 'x')
   327                         {'x': self.eidfrom, 'p': self.eidto}, 'x')
   505         self.session.vreg.config.sendmails(self.to_send)
   521         self.session.vreg.config.sendmails(self.to_send)
   506 
   522 
   507 
   523 
   508 class RQLPrecommitOperation(Operation):
   524 class RQLPrecommitOperation(Operation):
   509     def precommit_event(self):
   525     def precommit_event(self):
   510         execute = self.session.unsafe_execute
   526         execute = self.session.execute
   511         for rql in self.rqls:
   527         for rql in self.rqls:
   512             execute(*rql)
   528             execute(*rql)