From c23696a590f0669cfca800be1fd2261918b017da Mon Sep 17 00:00:00 2001 From: medmunds Date: Thu, 15 Nov 2012 16:17:16 -0800 Subject: [PATCH] Allow html from any EmailMultiAlternatives (not just DjrillMessage) Includes type-checking on alternative message part, and switches to ValueError (rather than ImproperlyConfigured) for unacceptable alternatives. [Also fixes bug where fail_silently=True wasn't respected.] Cherry-picked from: medmunds/Djrill@faf53a1a0 (and parts of medmunds/Djrill@62d48c5f) --- djrill/mail/backends/djrill.py | 29 ++++++++++++++++++--------- djrill/tests.py | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/djrill/mail/backends/djrill.py b/djrill/mail/backends/djrill.py index dbb6ed5..3046921 100644 --- a/djrill/mail/backends/djrill.py +++ b/djrill/mail/backends/djrill.py @@ -26,7 +26,7 @@ class DjrillBackend(BaseEmailBackend): Mandrill API Email Backend """ - def __init__(self, fail_silently=False, **kwargs): + def __init__(self, **kwargs): """ Set the API key, API url and set the action url. """ @@ -71,8 +71,13 @@ class DjrillBackend(BaseEmailBackend): if getattr(message, "alternative_subtype", None): if message.alternative_subtype == "mandrill": self._build_advanced_message_dict(message) - if message.alternatives: - self._add_alternatives(message) + try: + if getattr(message, 'alternatives', None): + self._add_alternatives(message) + except ValueError: + if not self.fail_silently: + raise + return False djrill_it = requests.post(self.api_action, data=json.dumps({ "key": self.api_key, @@ -121,23 +126,29 @@ class DjrillBackend(BaseEmailBackend): "from_name": message.from_name, "tags": message.tags, "track_opens": message.track_opens, + "track_clicks": message.track_clicks }) def _add_alternatives(self, message): """ - There can be only one! ... alternative attachment. + There can be only one! ... alternative attachment, and it must be text/html. Since mandrill does not accept image attachments or anything other than HTML, the assumption is the only thing you are attaching is the HTML output for your email. """ if len(message.alternatives) > 1: - raise ImproperlyConfigured( - "Mandrill only accepts plain text and html emails. Please " - "check the alternatives you have attached to your message.") + raise ValueError( + "Too many alternatives attached to the message. " + "Mandrill only accepts plain text and html emails.") + + (content, mimetype) = message.alternatives[0] + if mimetype != 'text/html': + raise ValueError("Invalid alternative mimetype '%s'. " + "Mandrill only accepts plain text and html emails." + % mimetype) self.msg_dict.update({ - "html": message.alternatives[0][0], - "track_clicks": message.track_clicks + "html": message.alternatives[0][0] }) diff --git a/djrill/tests.py b/djrill/tests.py index 82c8b87..e3ee158 100644 --- a/djrill/tests.py +++ b/djrill/tests.py @@ -92,6 +92,42 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase): self.assertEqual(data['message']['to'][4]['email'], "bcc1@example.com") self.assertEqual(data['message']['to'][5]['email'], "bcc2@example.com") + def test_html_message(self): + text_content = 'This is an important message.' + html_content = '

This is an important message.

' + email = mail.EmailMultiAlternatives('Subject', text_content, + 'from@example.com', ['to@example.com']) + email.attach_alternative(html_content, "text/html") + email.send() + data = self.get_api_call_data() + self.assertEqual(data['message']['text'], text_content) + self.assertEqual(data['message']['html'], html_content) + + def test_alternative_errors(self): + # Multiple alternatives not allowed + email = mail.EmailMultiAlternatives('Subject', 'Body', + 'from@example.com', ['to@example.com']) + email.attach_alternative("

First html is OK

", "text/html") + email.attach_alternative("

But not second html

", "text/html") + with self.assertRaises(ValueError): + email.send() + + # Only html alternatives allowed + email = mail.EmailMultiAlternatives('Subject', 'Body', + 'from@example.com', ['to@example.com']) + email.attach_alternative("{'not': 'allowed'}", "application/json") + with self.assertRaises(ValueError): + email.send() + + # Make sure fail_silently is respected + email = mail.EmailMultiAlternatives('Subject', 'Body', + 'from@example.com', ['to@example.com']) + email.attach_alternative("{'not': 'allowed'}", "application/json") + sent = email.send(fail_silently=True) + self.assertFalse(self.mock_post.called, + msg="Mandrill API should not be called when send fails silently") + self.assertEqual(sent, 0) + class DjrillMessageTests(TestCase): def setUp(self):