allow to specify sender (both 'From' header and helo_addr)

authorSylvain Thénault <sylvain.thenault@logilab.fr>
changeset24c0a2fb624e
branchdefault
phasedraft
hiddenyes
parent revision#1865d9c595a7 enhanced testing
child revision#c8d39c474844 minor cleanups
files modified by this revision
test/unittest_massmailing.py
views.py
# HG changeset patch
# User Sylvain Thénault <sylvain.thenault@logilab.fr>
# Date 1384354573 -3600
# Wed Nov 13 15:56:13 2013 +0100
# Node ID 24c0a2fb624e7a38cc022164f2dcd1f7d2237e62
# Parent 1865d9c595a7c0fdc703ebb2922398eaeb0542f9
allow to specify sender (both 'From' header and helo_addr)

diff --git a/test/unittest_massmailing.py b/test/unittest_massmailing.py
@@ -63,11 +63,11 @@
1                            self.vreg['controllers'].select, 'sendmail', self.request())
2 
3      def test_controller_admin(self):
4          self.user().cw_set(firstname=u'Sÿt', surname=u'Thénault')
5          rset = self.execute('CWUser X')
6 -        req = self.request(subject='tôtô',
7 +        req = self.request(subject='tôtô', sender=u'S. Thénô <stheno@dactylo.fr>',
8                             recipient=[str(eid) for eid, in rset],
9                             mailbody='hôp %(firstname)s %(surname)s')
10          ctrl = self.vreg['controllers'].select('sendmail', req)
11          with self.assertRaises(Redirect) as cm:
12              ctrl.publish()
@@ -76,10 +76,12 @@
13          self.assertEqual(len(testlib.MAILBOX), len(rset))
14          email = [mail for mail in testlib.MAILBOX
15                   if mail.recipients == ['admin@cubicweb.org']][0]
16          self.assertEqual(email.content, u'hôp Sÿt Thénault')
17          self.assertEqual(u'tôtô', email.subject)
18 +        self.assertEqual(u'S. Thénô <stheno@dactylo.fr>', email.helo_addr)
19 +        self.assertEqual(u'S. Thénô <stheno@dactylo.fr>', email.message['From'])
20 
21 
22  if __name__ == '__main__':
23      from logilab.common.testlib import unittest_main
24      unittest_main()
diff --git a/views.py b/views.py
@@ -19,15 +19,17 @@
25 
26  __docformat__ = "restructuredtext en"
27  _ = unicode
28 
29  import operator
30 +import re
31  from functools import reduce
32 
33  from cubicweb.predicates import (is_instance, authenticated_user,
34                                  adaptable, match_form_params)
35  from cubicweb.view import EntityView
36 +from cubicweb.mail import format_mail
37  from cubicweb.web import (Redirect, stdmsgs, controller, action,
38                            form, formfields as ff)
39  from cubicweb.web.formwidgets import CheckBox, TextInput, AjaxWidget, ImgButton
40  from cubicweb.web.views import forms, formrenderers
41 
@@ -62,22 +64,26 @@
42      needs_js = ('cubicweb.edition.js', 'cubicweb.widgets.js',)
43      needs_css = ('cubicweb.mailform.css')
44      domid = 'sendmail'
45      action = 'sendmail'
46 
47 -    sender = ff.StringField(widget=TextInput({'disabled': 'disabled'}),
48 -                            label=_('From:'),
49 -                            value=lambda form, field: '%s <%s>' % (
50 -                                form._cw.user.dc_title(),
51 -                                form._cw.user.cw_adapt_to('IEmailable').get_email()))
52 -    recipient = ff.StringField(widget=CheckBox(), label=_('Recipients:'),
53 -                               choices=recipient_vocabulary,
54 -                               value= lambda form, field: [entity.eid for entity in form.cw_rset.entities()
55 -                                                           if entity.cw_adapt_to('IEmailable').get_email()])
56 -    subject = ff.StringField(label=_('Subject:'), max_length=256)
57 -    mailbody = ff.StringField(widget=AjaxWidget(wdgtype='TemplateTextField',
58 -                                                inputid='mailbody'))
59 +    sender = ff.StringField(
60 +        label=_('From:'),
61 +        value=lambda form, field: '%s <%s>' % (
62 +            form._cw.user.name(),
63 +            form._cw.user.cw_adapt_to('IEmailable').get_email()),
64 +        max_length=256)
65 +    recipient = ff.StringField(
66 +        widget=CheckBox(), label=_('Recipients:'),
67 +        choices=recipient_vocabulary,
68 +        value=lambda form, field: [e.eid for e in form.cw_rset.entities()
69 +                                   if e.cw_adapt_to('IEmailable').get_email()])
70 +    subject = ff.StringField(
71 +        label=_('Subject:'), max_length=256)
72 +    mailbody = ff.StringField(
73 +        widget=AjaxWidget(wdgtype='TemplateTextField',
74 +                          inputid='mailbody'))
75 
76      form_buttons = [ImgButton('sendbutton', "javascript: $('#sendmail').submit()",
77                                _('send email'), 'SEND_EMAIL_ICON'),
78                      ImgButton('cancelbutton', "javascript: history.back()",
79                                _(stdmsgs.BUTTON_CANCEL[0]), stdmsgs.BUTTON_CANCEL[1])]
@@ -150,11 +156,12 @@
80          form.render(w=self.w)
81 
82 
83  class SendMailController(controller.Controller):
84      __regid__ = 'sendmail'
85 -    __select__ = authenticated_user() & match_form_params('recipient', 'mailbody', 'subject')
86 +    __select__ = authenticated_user() & match_form_params(
87 +        'sender', 'recipient', 'mailbody', 'subject')
88 
89      def recipients(self):
90          """returns an iterator on email's recipients as entities"""
91          eids = self._cw.form['recipient']
92          # eids may be a string if only one recipient was specified
@@ -163,15 +170,21 @@
93          else:
94              rset = self._cw.execute('Any X WHERE X eid in (%s)' % (','.join(eids)))
95          return rset.entities()
96 
97      def publish(self, rset=None):
98 -        # XXX this allows users with access to an cubicweb instance to use it as
99 -        # a mail relay
100 -        body = self._cw.form['mailbody']
101 -        subject = self._cw.form['subject']
102 +        req = self._cw
103 +        sender = req.form['sender']
104 +        text = req.form['mailbody']
105 +        subject = req.form['subject']
106          for recipient in self.recipients():
107              iemailable = recipient.cw_adapt_to('IEmailable')
108 -            text = body % iemailable.as_email_context()
109 -            self.sendmail(iemailable.get_email(), subject, text)
110 -        url = self._cw.build_url(__message=self._cw._('emails successfully sent'))
111 +            body = text % iemailable.as_email_context()
112 +            recipient = iemailable.get_email()
113 +            uinfo = re.match(ur'(?P<name>.+) <(?P<email>.+)>', sender, re.U).groupdict()
114 +            msg = format_mail(uinfo, [recipient], body, subject)
115 +            if not req.vreg.config.sendmails([(msg, [recipient])], sender):
116 +                msg = req._('could not connect to the SMTP server')
117 +                url = req.build_url(__message=msg)
118 +                raise Redirect(url)
119 +        url = req.build_url(__message=req._('emails successfully sent'))
120          raise Redirect(url)