enhance notification mecanism: recipients may return user entities, which will be used to create a fake session so one can check security during notification if necessary
--- a/common/mail.py Tue Sep 08 10:46:47 2009 +0200
+++ b/common/mail.py Tue Sep 08 15:37:46 2009 +0200
@@ -21,6 +21,7 @@
return 'XXX'
from cubicweb.view import EntityView
+from cubicweb.entity import Entity
def header(ustring):
return Header(ustring.encode('UTF-8'), 'UTF-8')
@@ -183,18 +184,12 @@
return construct_message_id(self.config.appid, eid, self.msgid_timestamp)
def render_emails(self, **kwargs):
- """generate and send an email message for this view"""
+ """generate and send emails for this view (one per recipient)"""
self._kwargs = kwargs
recipients = self.recipients()
if not recipients:
self.info('skipping %s notification, no recipients', self.id)
return
- if not isinstance(recipients[0], tuple):
- from warnings import warn
- warn('recipients should now return a list of 2-uple (email, language)',
- DeprecationWarning, stacklevel=1)
- lang = self.vreg.property_value('ui.language')
- recipients = zip(recipients, repeat(lang))
if self.rset is not None:
entity = self.entity(self.row or 0, self.col or 0)
# if the view is using timestamp in message ids, no way to reference
@@ -208,22 +203,32 @@
else:
refs = ()
msgid = None
- userdata = self.req.user_data()
- origlang = self.req.lang
- for emailaddr, lang in recipients:
- self.req.set_language(lang)
+ req = self.req
+ self.user_data = req.user_data()
+ origlang = req.lang
+ for something in recipients:
+ if isinstance(something, Entity):
+ # hi-jack self.req to get a session for the returned user
+ self.req = self.req.hijack_user(something)
+ emailaddr = something.get_email()
+ else:
+ emailaddr, lang = something
+ self.req.set_language(lang)
# since the same view (eg self) may be called multiple time and we
# need a fresh stream at each iteration, reset it explicitly
self.w = None
# XXX call render before subject to set .row/.col attributes on the
# view
- content = self.render(row=0, col=0, **kwargs)
- subject = self.subject()
- msg = format_mail(userdata, [emailaddr], content, subject,
+ try:
+ content = self.render(row=0, col=0, **kwargs)
+ subject = self.subject()
+ except SkipEmail:
+ continue
+ msg = format_mail(self.user_data, [emailaddr], content, subject,
config=self.config, msgid=msgid, references=refs)
yield [emailaddr], msg
# restore language
- self.req.set_language(origlang)
+ req.set_language(origlang)
def render_and_send(self, **kwargs):
"""generate and send an email message for this view"""
--- a/dbapi.py Tue Sep 08 10:46:47 2009 +0200
+++ b/dbapi.py Tue Sep 08 15:37:46 2009 +0200
@@ -284,6 +284,12 @@
# server session compat layer #############################################
+ def hijack_user(self, user):
+ """return a fake request/session using specified user"""
+ req = DBAPIRequest(self.vreg)
+ req.set_connection(self.cnx, user)
+ return req
+
@property
def user(self):
if self._user is None and self.cnx:
--- a/server/session.py Tue Sep 08 10:46:47 2009 +0200
+++ b/server/session.py Tue Sep 08 15:37:46 2009 +0200
@@ -78,16 +78,51 @@
def schema(self):
return self.repo.schema
- def add_relation(self, fromeid, rtype, toeid):
+ def hijack_user(self, user):
+ """return a fake request/session using specified user"""
+ session = Session(user, self.repo)
+ session._threaddata = self._threaddata
+ return session
+
+ def _change_relation(self, cb, fromeid, rtype, toeid):
if self.is_super_session:
- self.repo.glob_add_relation(self, fromeid, rtype, toeid)
+ cb(self, fromeid, rtype, toeid)
return
self.is_super_session = True
try:
- self.repo.glob_add_relation(self, fromeid, rtype, toeid)
+ cb(self, fromeid, rtype, toeid)
finally:
self.is_super_session = False
+ def add_relation(self, fromeid, rtype, toeid):
+ """provide direct access to the repository method to add a relation.
+
+ This is equivalent to the following rql query:
+
+ SET X rtype Y WHERE X eid fromeid, T eid toeid
+
+ without read security check but also all the burden of rql execution.
+ You may use this in hooks when you know both eids of the relation you
+ want to add.
+ """
+ self._change_relation(self.repo.glob_add_relation,
+ fromeid, rtype, toeid)
+ def delete_relation(self, fromeid, rtype, toeid):
+ """provide direct access to the repository method to delete a relation.
+
+ This is equivalent to the following rql query:
+
+ DELETE X rtype Y WHERE X eid fromeid, T eid toeid
+
+ without read security check but also all the burden of rql execution.
+ You may use this in hooks when you know both eids of the relation you
+ want to delete.
+ """
+ self._change_relation(self.repo.glob_delete_relation,
+ fromeid, rtype, toeid)
+
+ # relations cache handling #################################################
+
def update_rel_cache_add(self, subject, rtype, object, symetric=False):
self._update_entity_rel_cache_add(subject, rtype, 'subject', object)
if symetric:
--- a/sobjects/notification.py Tue Sep 08 10:46:47 2009 +0200
+++ b/sobjects/notification.py Tue Sep 08 15:37:46 2009 +0200
@@ -190,6 +190,6 @@
def subject(self):
entity = self.entity(self.row or 0, self.col or 0)
return u'%s #%s (%s)' % (self.req.__('New %s' % entity.e_schema),
- entity.eid, self.user_login())
+ entity.eid, self.user_data['login'])
NormalizedTextView = class_renamed('NormalizedTextView', ContentAddedView)