hooks/notification.py
changeset 2841 107ba1c45227
child 2847 c2ee28f4d4b1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hooks/notification.py	Fri Aug 14 11:14:10 2009 +0200
@@ -0,0 +1,133 @@
+"""some hooks to handle notification on entity's changes
+
+:organization: Logilab
+:copyright: 2001-2009 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"
+
+from logilab.common.textutils import normalize_text
+
+from cubicweb import RegistryException
+from cubicweb.selectors import entity_implements
+from cubicweb.server import hook
+
+
+class RenderAndSendNotificationView(hook.Operation):
+    """delay rendering of notification view until precommit"""
+    def precommit_event(self):
+        view = self.view
+        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]):
+            return # entity added and deleted in the same transaction
+        self.view.render_and_send(**getattr(self, 'viewargs', {}))
+
+
+class NotificationHook(hook.Hook):
+    __abstract__ = True
+    category = 'notification'
+
+    def select_view(self, vid, rset, row=0, col=0):
+        return self.cw_req.vreg['views'].select_object(vid, self.cw_req,
+                                                       rset=rset, row=0, col=0)
+
+
+class StatusChangeHook(NotificationHook):
+    """notify when a workflowable entity has its state modified"""
+    __id__ = 'notifystatuschange'
+    __select__ = NotificationHook.__select__ & entity_implements('TrInfo')
+    events = ('after_add_entity',)
+
+    def __call__(self):
+        entity = self.entity
+        if not entity.from_state: # not a transition
+            return
+        rset = entity.related('wf_info_for')
+        view = self.select_view('notif_status_change', rset=rset, row=0)
+        if view is None:
+            return
+        comment = entity.printable_value('comment', format='text/plain')
+        if comment:
+            comment = normalize_text(comment, 80,
+                                     rest=entity.comment_format=='text/rest')
+        RenderAndSendNotificationView(self.cw_req, view=view, viewargs={
+            'comment': comment, 'previous_state': entity.previous_state.name,
+            'current_state': entity.new_state.name})
+
+
+class RelationChangeHook(NotificationHook):
+    __id__ = 'notifyrelationchange'
+    events = ('before_add_relation', 'after_add_relation',
+              'before_delete_relation', 'after_delete_relation')
+
+    def __call__(self):
+        """if a notification view is defined for the event, send notification
+        email defined by the view
+        """
+        rset = self.cw_req.eid_rset(self.eidfrom)
+        view = self.select_view('notif_%s_%s' % (self.event,  self.rtype),
+                                rset=rset, row=0)
+        if view is None:
+            return
+        RenderAndSendNotificationView(self.cw_req, view=view)
+
+
+class EntityChangeHook(NotificationHook):
+    """if a notification view is defined for the event, send notification
+    email defined by the view
+    """
+    __id__ = 'notifyentitychange'
+    events = ('after_add_entity', 'after_update_entity')
+
+    def __call__(self):
+        rset = self.entity.as_rset()
+        view = self.select_view('notif_%s' % self.event, rset=rset, row=0)
+        if view is None:
+            return
+        RenderAndSendNotificationView(self.cw_req, view=view)
+
+
+# supervising ##################################################################
+
+class SomethingChangedHook(NotificationHook):
+    __id__ = 'supervising'
+    events = ('before_add_relation', 'before_delete_relation',
+              'after_add_entity', 'before_update_entity')
+
+    def __call__(self):
+        dest = self.cw_req.vreg.config['supervising-addrs']
+        if not dest: # no supervisors, don't do this for nothing...
+            return
+        if self._call():
+            SupervisionMailOp(self.cw_req)
+
+    def _call(self):
+        event = self.event.split('_', 1)[1]
+        if event == 'update_entity':
+            if self.cw_req.added_in_transaction(self.entity.eid):
+                return False
+            if self.entity.e_schema == 'CWUser':
+                if not (self.entity.edited_attributes - frozenset(('eid', 'modification_date',
+                                                                   'last_login_time'))):
+                    # don't record last_login_time update which are done
+                    # automatically at login time
+                    return False
+        self.cw_req.transaction_data.setdefault('pendingchanges', []).append(
+            (event, self))
+        return True
+
+
+class EntityDeleteHook(SomethingChangedHook):
+    __id__ = 'supervisingentitydel'
+    events = ('before_delete_entity',)
+
+    def _call(self):
+        try:
+            title = self.entity.dc_title()
+        except:
+            # may raise an error during deletion process, for instance due to
+            # missing required relation
+            title = '#%s' % eid
+        self.cw_req.transaction_data.setdefault('pendingchanges', []).append(
+            ('delete_entity', (self.eid, str(self.entity.e_schema), title)))
+        return True