Merge pull request #7 from medmunds/core_message_objects

Support Django standard EmailMessage objects
This commit is contained in:
Kenneth Love
2012-11-23 09:51:27 -08:00
2 changed files with 95 additions and 19 deletions

View File

@@ -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,
@@ -96,46 +101,54 @@ class DjrillBackend(BaseEmailBackend):
use by default. Standard text email messages sent through Django will
still work through Mandrill.
"""
return {
msg_dict = {
"text": message.body,
"subject": message.subject,
"from_email": self.sender,
"to": self.recipients
}
if message.extra_headers:
accepted_headers = {}
for k in message.extra_headers.keys():
if k.startswith("X-") or k == "Reply-To":
accepted_headers.update(
{"%s" % k: message.extra_headers[k]})
msg_dict.update({"headers": accepted_headers})
return msg_dict
def _build_advanced_message_dict(self, message):
"""
Builds advanced message dict and attaches any accepted extra headers.
Builds advanced message dict
"""
self.msg_dict.update({
"from_name": message.from_name,
"tags": message.tags,
"track_opens": message.track_opens,
"track_clicks": message.track_clicks
})
if message.extra_headers:
accepted_headers = {}
for k in message.extra_headers.keys():
if k.startswith("X-") or k == "Reply-To":
accepted_headers.update(
{"%s" % k: message.extra_headers[k]})
self.msg_dict.update({"headers": accepted_headers})
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": content
})

View File

@@ -65,6 +65,69 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase):
mail.send_mail('Subject', 'Message', 'from@example.com',
['to@example.com'])
def test_email_message(self):
email = mail.EmailMessage('Subject', 'Body goes here',
'from@example.com',
['to1@example.com', 'Also To <to2@example.com>'],
bcc=['bcc1@example.com', 'Also BCC <bcc2@example.com>'],
cc=['cc1@example.com', 'Also CC <cc2@example.com>'],
headers={'Reply-To': 'another@example.com',
'X-MyHeader': 'my value',
'Errors-To': 'silently stripped'})
email.send()
data = self.get_api_call_data()
self.assertEqual(data['message']['subject'], "Subject")
self.assertEqual(data['message']['text'], "Body goes here")
self.assertEqual(data['message']['from_email'], "from@example.com")
self.assertEqual(data['message']['headers'],
{ 'Reply-To': 'another@example.com', 'X-MyHeader': 'my value' })
# Mandrill doesn't have a notion of cc, and only allows a single bcc.
# Djrill just treats cc and bcc as though they were "to" addresses,
# which may or may not be what you want.
self.assertEqual(len(data['message']['to']), 6)
self.assertEqual(data['message']['to'][0]['email'], "to1@example.com")
self.assertEqual(data['message']['to'][1]['email'], "to2@example.com")
self.assertEqual(data['message']['to'][2]['email'], "cc1@example.com")
self.assertEqual(data['message']['to'][3]['email'], "cc2@example.com")
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 = '<p>This is an <strong>important</strong> message.</p>'
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("<p>First html is OK</p>", "text/html")
email.attach_alternative("<p>But not second html</p>", "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):