|
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 |