hooks/notification.py
changeset 2841 107ba1c45227
child 2847 c2ee28f4d4b1
equal deleted inserted replaced
2840:06daf13195d4 2841:107ba1c45227
       
     1 """some hooks to handle notification on entity's changes
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
       
     7 """
       
     8 __docformat__ = "restructuredtext en"
       
     9 
       
    10 from logilab.common.textutils import normalize_text
       
    11 
       
    12 from cubicweb import RegistryException
       
    13 from cubicweb.selectors import entity_implements
       
    14 from cubicweb.server import hook
       
    15 
       
    16 
       
    17 class RenderAndSendNotificationView(hook.Operation):
       
    18     """delay rendering of notification view until precommit"""
       
    19     def precommit_event(self):
       
    20         view = self.view
       
    21         if view.cw_rset and self.session.deleted_in_transaction(view.cw_rset[cw_rset.cw_row or 0][cw_rset.cw_col or 0]):
       
    22             return # entity added and deleted in the same transaction
       
    23         self.view.render_and_send(**getattr(self, 'viewargs', {}))
       
    24 
       
    25 
       
    26 class NotificationHook(hook.Hook):
       
    27     __abstract__ = True
       
    28     category = 'notification'
       
    29 
       
    30     def select_view(self, vid, rset, row=0, col=0):
       
    31         return self.cw_req.vreg['views'].select_object(vid, self.cw_req,
       
    32                                                        rset=rset, row=0, col=0)
       
    33 
       
    34 
       
    35 class StatusChangeHook(NotificationHook):
       
    36     """notify when a workflowable entity has its state modified"""
       
    37     __id__ = 'notifystatuschange'
       
    38     __select__ = NotificationHook.__select__ & entity_implements('TrInfo')
       
    39     events = ('after_add_entity',)
       
    40 
       
    41     def __call__(self):
       
    42         entity = self.entity
       
    43         if not entity.from_state: # not a transition
       
    44             return
       
    45         rset = entity.related('wf_info_for')
       
    46         view = self.select_view('notif_status_change', rset=rset, row=0)
       
    47         if view is None:
       
    48             return
       
    49         comment = entity.printable_value('comment', format='text/plain')
       
    50         if comment:
       
    51             comment = normalize_text(comment, 80,
       
    52                                      rest=entity.comment_format=='text/rest')
       
    53         RenderAndSendNotificationView(self.cw_req, view=view, viewargs={
       
    54             'comment': comment, 'previous_state': entity.previous_state.name,
       
    55             'current_state': entity.new_state.name})
       
    56 
       
    57 
       
    58 class RelationChangeHook(NotificationHook):
       
    59     __id__ = 'notifyrelationchange'
       
    60     events = ('before_add_relation', 'after_add_relation',
       
    61               'before_delete_relation', 'after_delete_relation')
       
    62 
       
    63     def __call__(self):
       
    64         """if a notification view is defined for the event, send notification
       
    65         email defined by the view
       
    66         """
       
    67         rset = self.cw_req.eid_rset(self.eidfrom)
       
    68         view = self.select_view('notif_%s_%s' % (self.event,  self.rtype),
       
    69                                 rset=rset, row=0)
       
    70         if view is None:
       
    71             return
       
    72         RenderAndSendNotificationView(self.cw_req, view=view)
       
    73 
       
    74 
       
    75 class EntityChangeHook(NotificationHook):
       
    76     """if a notification view is defined for the event, send notification
       
    77     email defined by the view
       
    78     """
       
    79     __id__ = 'notifyentitychange'
       
    80     events = ('after_add_entity', 'after_update_entity')
       
    81 
       
    82     def __call__(self):
       
    83         rset = self.entity.as_rset()
       
    84         view = self.select_view('notif_%s' % self.event, rset=rset, row=0)
       
    85         if view is None:
       
    86             return
       
    87         RenderAndSendNotificationView(self.cw_req, view=view)
       
    88 
       
    89 
       
    90 # supervising ##################################################################
       
    91 
       
    92 class SomethingChangedHook(NotificationHook):
       
    93     __id__ = 'supervising'
       
    94     events = ('before_add_relation', 'before_delete_relation',
       
    95               'after_add_entity', 'before_update_entity')
       
    96 
       
    97     def __call__(self):
       
    98         dest = self.cw_req.vreg.config['supervising-addrs']
       
    99         if not dest: # no supervisors, don't do this for nothing...
       
   100             return
       
   101         if self._call():
       
   102             SupervisionMailOp(self.cw_req)
       
   103 
       
   104     def _call(self):
       
   105         event = self.event.split('_', 1)[1]
       
   106         if event == 'update_entity':
       
   107             if self.cw_req.added_in_transaction(self.entity.eid):
       
   108                 return False
       
   109             if self.entity.e_schema == 'CWUser':
       
   110                 if not (self.entity.edited_attributes - frozenset(('eid', 'modification_date',
       
   111                                                                    'last_login_time'))):
       
   112                     # don't record last_login_time update which are done
       
   113                     # automatically at login time
       
   114                     return False
       
   115         self.cw_req.transaction_data.setdefault('pendingchanges', []).append(
       
   116             (event, self))
       
   117         return True
       
   118 
       
   119 
       
   120 class EntityDeleteHook(SomethingChangedHook):
       
   121     __id__ = 'supervisingentitydel'
       
   122     events = ('before_delete_entity',)
       
   123 
       
   124     def _call(self):
       
   125         try:
       
   126             title = self.entity.dc_title()
       
   127         except:
       
   128             # may raise an error during deletion process, for instance due to
       
   129             # missing required relation
       
   130             title = '#%s' % eid
       
   131         self.cw_req.transaction_data.setdefault('pendingchanges', []).append(
       
   132             ('delete_entity', (self.eid, str(self.entity.e_schema), title)))
       
   133         return True