--- a/sobjects/notification.py Tue Sep 29 15:10:56 2009 +0200
+++ b/sobjects/notification.py Tue Sep 29 15:10:12 2009 -0700
@@ -17,7 +17,7 @@
from cubicweb.selectors import implements, yes
from cubicweb.view import Component
from cubicweb.common.mail import NotificationView, parse_message_id
-from cubicweb.server.pool import PreCommitOperation
+from cubicweb.server.pool import PreCommitOperation, SingleLastOperation
from cubicweb.server.hookhelper import SendMailOp
from cubicweb.server.hooksmanager import Hook
@@ -53,6 +53,20 @@
# hooks #######################################################################
+class EntityUpdatedNotificationOp(SingleLastOperation):
+
+ def precommit_event(self):
+ session = self.session
+ for eid in session.transaction_data['changes']:
+ view = session.vreg['views'].select('notif_entity_updated', session,
+ rset=session.eid_rset(eid),
+ row=0)
+ RenderAndSendNotificationView(session, view=view)
+
+ def commit_event(self):
+ pass
+
+
class RenderAndSendNotificationView(PreCommitOperation):
"""delay rendering of notification view until precommit"""
def precommit_event(self):
@@ -121,6 +135,36 @@
return
RenderAndSendNotificationView(session, view=view)
+class EntityUpdateHook(Hook):
+ events = ('before_update_entity',)
+ accepts = ()
+ skip_attrs = set()
+
+ def call(self, session, entity):
+ if entity.eid in session.transaction_data.get('neweids', ()):
+ return # entity is being created
+ if session.is_super_session:
+ return # ignore changes triggered by hooks
+ # then compute changes
+ changes = session.transaction_data.setdefault('changes', {})
+ thisentitychanges = changes.setdefault(entity.eid, set())
+ attrs = [k for k in entity.edited_attributes if not k in self.skip_attrs]
+ if not attrs:
+ return
+ rqlsel, rqlrestr = [], ['X eid %(x)s']
+ for i, attr in enumerate(attrs):
+ var = chr(65+i)
+ rqlsel.append(var)
+ rqlrestr.append('X %s %s' % (attr, var))
+ rql = 'Any %s WHERE %s' % (','.join(rqlsel), ','.join(rqlrestr))
+ rset = session.execute(rql, {'x': entity.eid}, 'x')
+ for i, attr in enumerate(attrs):
+ oldvalue = rset[0][i]
+ newvalue = entity[attr]
+ if oldvalue != newvalue:
+ thisentitychanges.add((attr, oldvalue, newvalue))
+ if thisentitychanges:
+ EntityUpdatedNotificationOp(session)
# abstract or deactivated notification views and mixin ########################
@@ -193,3 +237,65 @@
entity.eid, self.user_data['login'])
NormalizedTextView = class_renamed('NormalizedTextView', ContentAddedView)
+
+def format_value(value):
+ if isinstance(value, unicode):
+ return u'"%s"' % value
+ return value
+
+class EntityUpdatedNotificationView(NotificationView):
+ """abstract class for notification on entity/relation
+
+ all you have to do by default is :
+ * set id and __select__ attributes to match desired events and entity types
+ * set a content attribute to define the content of the email (unless you
+ override call)
+ """
+ __abstract__ = True
+ id = 'notif_entity_updated'
+ msgid_timestamp = False
+ message = _('updated')
+ no_detailed_change_attrs = ()
+ content = """
+Properties have been updated by %(user)s:
+
+%(changes)s
+
+url: %(url)s
+"""
+
+ def context(self, **kwargs):
+ context = super(EntityUpdatedNotificationView, self).context(**kwargs)
+ changes = self.req.transaction_data['changes'][self.rset[0][0]]
+ _ = self.req._
+ formatted_changes = []
+ for attr, oldvalue, newvalue in sorted(changes):
+ # check current user has permission to see the attribute
+ rschema = self.vreg.schema[attr]
+ if rschema.is_final():
+ if not rschema.has_perm(self.req, 'read', eid=self.rset[0][0]):
+ continue
+ # XXX suppose it's a subject relation...
+ elif not rschema.has_perm(self.req, 'read', fromeid=self.rset[0][0]):
+ continue
+ if attr in self.no_detailed_change_attrs:
+ msg = _('%s updated') % _(attr)
+ elif oldvalue not in (None, ''):
+ msg = _('%(attr)s updated from %(oldvalue)s to %(newvalue)s') % {
+ 'attr': _(attr),
+ 'oldvalue': format_value(oldvalue),
+ 'newvalue': format_value(newvalue)}
+ else:
+ msg = _('%(attr)s set to %(newvalue)s') % {
+ 'attr': _(attr), 'newvalue': format_value(newvalue)}
+ formatted_changes.append('* ' + msg)
+ if not formatted_changes:
+ # current user isn't allowed to see changes, skip this notification
+ raise SkipEmail()
+ context['changes'] = '\n'.join(formatted_changes)
+ return context
+
+ def subject(self):
+ entity = self.entity(self.row or 0, self.col or 0)
+ return u'%s #%s (%s)' % (self.req.__('Updated %s' % entity.e_schema),
+ entity.eid, self.user_data['login'])