# HG changeset patch
# User Sylvain Thénault <sylvain.thenault@logilab.fr>
# Date 1412672141 -7200
# Tue Oct 07 10:55:41 2014 +0200
# Node ID 1ff2ebeaee8d9c0a82a3f17281c578695f81aa78
# Parent 1865d9c595a7c0fdc703ebb2922398eaeb0542f9
allow to specify sender (both 'From' header and 'MAIL FROM' envelope)
also some minor cleanups
# User Sylvain Thénault <sylvain.thenault@logilab.fr>
# Date 1412672141 -7200
# Tue Oct 07 10:55:41 2014 +0200
# Node ID 1ff2ebeaee8d9c0a82a3f17281c578695f81aa78
# Parent 1865d9c595a7c0fdc703ebb2922398eaeb0542f9
allow to specify sender (both 'From' header and 'MAIL FROM' envelope)
also some minor cleanups
@@ -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()
@@ -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)