[views] allow to specify sender (closes #4457904)

both 'From' header and 'MAIL FROM' envelope, also some minor cleanups.

authorSylvain Thénault <sylvain.thenault@logilab.fr>
changeset14d7adf96696
branchdefault
phasepublic
hiddenno
parent revision#68fe5f70a445 enhanced testing
child revision#330c2b825de5 [test] use the new connection api
files modified by this revision
test/unittest_massmailing.py
views.py
# HG changeset patch
# User Sylvain Thénault <sylvain.thenault@logilab.fr>
# Date 1412672141 -7200
# Tue Oct 07 10:55:41 2014 +0200
# Node ID 14d7adf9669626388c0ff16c06cca71c992af8e2
# Parent 68fe5f70a445aab765e0ed78acca222ed1b12401
[views] allow to specify sender (closes #4457904)

both 'From' header and 'MAIL FROM' envelope,
also some minor cleanups.

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.fromaddr)
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
@@ -1,6 +1,6 @@
25 -# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
26 +# copyright 2003-2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
27  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
28  #
29  # This file is part of CubicWeb.
30  #
31  # CubicWeb is free software: you can redistribute it and/or modify it under the
@@ -13,24 +13,29 @@
32  # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
33  # details.
34  #
35  # You should have received a copy of the GNU Lesser General Public License along
36  # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
37 -"""Mass mailing handling: send mail to entities adaptable to IEmailable"""
38 +"""Mass mailing handling: send mail to entities adaptable to IEmailable
39 +
40 +XXX this cube allows users with authenticated access to a cubicweb instance to
41 +use it as a mail relay.
42 +"""
43 
44  __docformat__ = "restructuredtext en"
45  _ = unicode
46 
47  import operator
48 +import re
49  from functools import reduce
50 
51 -from cubicweb.predicates import (is_instance, authenticated_user,
52 -                                adaptable, match_form_params)
53 +from cubicweb.predicates import authenticated_user, adaptable, match_form_params
54  from cubicweb.view import EntityView
55 +from cubicweb.mail import format_mail
56  from cubicweb.web import (Redirect, stdmsgs, controller, action,
57                            form, formfields as ff)
58 -from cubicweb.web.formwidgets import CheckBox, TextInput, AjaxWidget, ImgButton
59 +from cubicweb.web.formwidgets import CheckBox, AjaxWidget, ImgButton
60  from cubicweb.web.views import forms, formrenderers
61 
62 
63  class SendEmailAction(action.Action):
64      __regid__ = 'sendemail'
@@ -59,25 +64,28 @@
65  class MassMailingForm(forms.FieldsForm):
66      __regid__ = 'massmailing'
67 
68      needs_js = ('cubicweb.edition.js', 'cubicweb.widgets.js',)
69      needs_css = ('cubicweb.mailform.css')
70 -    domid = 'sendmail'
71 -    action = 'sendmail'
72 +    domid = action = 'sendmail'
73 
74 -    sender = ff.StringField(widget=TextInput({'disabled': 'disabled'}),
75 -                            label=_('From:'),
76 -                            value=lambda form, field: '%s <%s>' % (
77 -                                form._cw.user.dc_title(),
78 -                                form._cw.user.cw_adapt_to('IEmailable').get_email()))
79 -    recipient = ff.StringField(widget=CheckBox(), label=_('Recipients:'),
80 -                               choices=recipient_vocabulary,
81 -                               value= lambda form, field: [entity.eid for entity in form.cw_rset.entities()
82 -                                                           if entity.cw_adapt_to('IEmailable').get_email()])
83 -    subject = ff.StringField(label=_('Subject:'), max_length=256)
84 -    mailbody = ff.StringField(widget=AjaxWidget(wdgtype='TemplateTextField',
85 -                                                inputid='mailbody'))
86 +    sender = ff.StringField(
87 +        label=_('From:'),
88 +        value=lambda form, field: '%s <%s>' % (
89 +            form._cw.user.name(),
90 +            form._cw.user.cw_adapt_to('IEmailable').get_email()),
91 +        max_length=256)
92 +    recipient = ff.StringField(
93 +        widget=CheckBox(), label=_('Recipients:'),
94 +        choices=recipient_vocabulary,
95 +        value=lambda form, field: [e.eid for e in form.cw_rset.entities()
96 +                                   if e.cw_adapt_to('IEmailable').get_email()])
97 +    subject = ff.StringField(
98 +        label=_('Subject:'), max_length=256)
99 +    mailbody = ff.StringField(
100 +        widget=AjaxWidget(wdgtype='TemplateTextField',
101 +                          inputid='mailbody'))
102 
103      form_buttons = [ImgButton('sendbutton', "javascript: $('#sendmail').submit()",
104                                _('send email'), 'SEND_EMAIL_ICON'),
105                      ImgButton('cancelbutton', "javascript: history.back()",
106                                _(stdmsgs.BUTTON_CANCEL[0]), stdmsgs.BUTTON_CANCEL[1])]
@@ -150,11 +158,12 @@
107          form.render(w=self.w)
108 
109 
110  class SendMailController(controller.Controller):
111      __regid__ = 'sendmail'
112 -    __select__ = authenticated_user() & match_form_params('recipient', 'mailbody', 'subject')
113 +    __select__ = authenticated_user() & match_form_params(
114 +        'sender', 'recipient', 'mailbody', 'subject')
115 
116      def recipients(self):
117          """returns an iterator on email's recipients as entities"""
118          eids = self._cw.form['recipient']
119          # eids may be a string if only one recipient was specified
@@ -163,15 +172,21 @@
120          else:
121              rset = self._cw.execute('Any X WHERE X eid in (%s)' % (','.join(eids)))
122          return rset.entities()
123 
124      def publish(self, rset=None):
125 -        # XXX this allows users with access to an cubicweb instance to use it as
126 -        # a mail relay
127 -        body = self._cw.form['mailbody']
128 -        subject = self._cw.form['subject']
129 +        req = self._cw
130 +        sender = req.form['sender']
131 +        text = req.form['mailbody']
132 +        subject = req.form['subject']
133          for recipient in self.recipients():
134              iemailable = recipient.cw_adapt_to('IEmailable')
135 -            text = body % iemailable.as_email_context()
136 -            self.sendmail(iemailable.get_email(), subject, text)
137 -        url = self._cw.build_url(__message=self._cw._('emails successfully sent'))
138 +            body = text % iemailable.as_email_context()
139 +            recipient = iemailable.get_email()
140 +            uinfo = re.match(ur'(?P<name>.+) <(?P<email>.+)>', sender, re.U).groupdict()
141 +            msg = format_mail(uinfo, [recipient], body, subject)
142 +            if not req.vreg.config.sendmails([(msg, [recipient])], sender):
143 +                msg = req._('could not connect to the SMTP server')
144 +                url = req.build_url(__message=msg)
145 +                raise Redirect(url)
146 +        url = req.build_url(__message=req._('emails successfully sent'))
147          raise Redirect(url)