mirror of
https://github.com/pacnpal/django-anymail.git
synced 2025-12-20 11:51:05 -05:00
Support sending attachments
This commit is contained in:
@@ -122,7 +122,8 @@ Djrill supports most of the functionality of Django's `EmailMessage`_ and
|
|||||||
raise a ``ValueError`` exception when you attempt to send the message.
|
raise a ``ValueError`` exception when you attempt to send the message.
|
||||||
(Mandrill doesn't support sending multiple html alternative parts, or any
|
(Mandrill doesn't support sending multiple html alternative parts, or any
|
||||||
non-html alternatives.)
|
non-html alternatives.)
|
||||||
* Djrill (currently) silently ignores all attachments on a message.
|
* Djrill attempts to include a message's attachments, though Mandrill may place
|
||||||
|
some restrictions on allowable attachment types. (See the Mandrill docs.)
|
||||||
* Djrill treats all cc and bcc recipients as if they were additional "to"
|
* Djrill treats all cc and bcc recipients as if they were additional "to"
|
||||||
addresses. (Mandrill does not distinguish cc, and only allows a single bcc --
|
addresses. (Mandrill does not distinguish cc, and only allows a single bcc --
|
||||||
which Djrill doesn't use. *Caution:* depending on the ``preserve_recipients``
|
which Djrill doesn't use. *Caution:* depending on the ``preserve_recipients``
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.core.mail.backends.base import BaseEmailBackend
|
from django.core.mail.backends.base import BaseEmailBackend
|
||||||
from django.core.mail.message import sanitize_address
|
from django.core.mail.message import sanitize_address, DEFAULT_ATTACHMENT_MIME_TYPE
|
||||||
from django.utils import simplejson as json
|
from django.utils import simplejson as json
|
||||||
|
|
||||||
|
from base64 import b64encode
|
||||||
|
from email.mime.base import MIMEBase
|
||||||
from email.utils import parseaddr
|
from email.utils import parseaddr
|
||||||
|
import mimetypes
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
# This backend was developed against this API endpoint.
|
# This backend was developed against this API endpoint.
|
||||||
@@ -68,6 +71,7 @@ class DjrillBackend(BaseEmailBackend):
|
|||||||
self._add_mandrill_options(message, msg_dict)
|
self._add_mandrill_options(message, msg_dict)
|
||||||
if getattr(message, 'alternatives', None):
|
if getattr(message, 'alternatives', None):
|
||||||
self._add_alternatives(message, msg_dict)
|
self._add_alternatives(message, msg_dict)
|
||||||
|
self._add_attachments(message, msg_dict)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
if not self.fail_silently:
|
if not self.fail_silently:
|
||||||
raise
|
raise
|
||||||
@@ -186,3 +190,39 @@ class DjrillBackend(BaseEmailBackend):
|
|||||||
% mimetype)
|
% mimetype)
|
||||||
|
|
||||||
msg_dict['html'] = content
|
msg_dict['html'] = content
|
||||||
|
|
||||||
|
def _add_attachments(self, message, msg_dict):
|
||||||
|
"""Extend msg_dict to include any attachments in message"""
|
||||||
|
if message.attachments:
|
||||||
|
attachments = [
|
||||||
|
self._make_mandrill_attachment(attachment)
|
||||||
|
for attachment in message.attachments
|
||||||
|
]
|
||||||
|
if len(attachments) > 0:
|
||||||
|
msg_dict['attachments'] = attachments
|
||||||
|
|
||||||
|
def _make_mandrill_attachment(self, attachment):
|
||||||
|
"""Return a Mandrill dict for an EmailMessage.attachments item"""
|
||||||
|
# Note that an attachment can be either a tuple of (filename, content,
|
||||||
|
# mimetype) or a MIMEBase object. (Also, both filename and mimetype may
|
||||||
|
# be missing.)
|
||||||
|
if isinstance(attachment, MIMEBase):
|
||||||
|
filename = attachment.get_filename()
|
||||||
|
content = attachment.get_payload(decode=True)
|
||||||
|
mimetype = attachment.get_content_type()
|
||||||
|
else:
|
||||||
|
(filename, content, mimetype) = attachment
|
||||||
|
|
||||||
|
# Guess missing mimetype, borrowed from
|
||||||
|
# django.core.mail.EmailMessage._create_attachment()
|
||||||
|
if mimetype is None and filename is not None:
|
||||||
|
mimetype, _ = mimetypes.guess_type(filename)
|
||||||
|
if mimetype is None:
|
||||||
|
mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
|
||||||
|
|
||||||
|
return {
|
||||||
|
'type': mimetype,
|
||||||
|
'name': filename or "",
|
||||||
|
'content': b64encode(content),
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
from base64 import b64encode
|
||||||
|
from email.mime.base import MIMEBase
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
@@ -78,6 +81,40 @@ class DjrillBackendTests(DjrillBackendMockAPITestCase):
|
|||||||
data = self.get_api_call_data()
|
data = self.get_api_call_data()
|
||||||
self.assertEqual(data['message']['text'], text_content)
|
self.assertEqual(data['message']['text'], text_content)
|
||||||
self.assertEqual(data['message']['html'], html_content)
|
self.assertEqual(data['message']['html'], html_content)
|
||||||
|
# Don't accidentally send the html part as an attachment:
|
||||||
|
self.assertFalse('attachments' in data['message'])
|
||||||
|
|
||||||
|
def test_attachments(self):
|
||||||
|
email = mail.EmailMessage('Subject', 'Body goes here',
|
||||||
|
'from@example.com', ['to1@example.com'])
|
||||||
|
|
||||||
|
content1 = "* Item one\n* Item two\n* Item three"
|
||||||
|
email.attach(filename="test.txt", content=content1,
|
||||||
|
mimetype="text/plain")
|
||||||
|
|
||||||
|
# Should guess mimetype if not provided...
|
||||||
|
content2 = "PNG* pretend this is the contents of a png file"
|
||||||
|
email.attach(filename="test.png", content=content2)
|
||||||
|
|
||||||
|
# Should work with a MIMEBase object (also tests no filename)...
|
||||||
|
content3 = "PDF* pretend this is valid pdf data"
|
||||||
|
mimeattachment = MIMEBase('application', 'pdf')
|
||||||
|
mimeattachment.set_payload(content3)
|
||||||
|
email.attach(mimeattachment)
|
||||||
|
|
||||||
|
email.send()
|
||||||
|
data = self.get_api_call_data()
|
||||||
|
attachments = data['message']['attachments']
|
||||||
|
self.assertEqual(len(attachments), 3)
|
||||||
|
self.assertEqual(attachments[0]["type"], "text/plain")
|
||||||
|
self.assertEqual(attachments[0]["name"], "test.txt")
|
||||||
|
self.assertEqual(attachments[0]["content"], b64encode(content1))
|
||||||
|
self.assertEqual(attachments[1]["type"], "image/png") # inferred
|
||||||
|
self.assertEqual(attachments[1]["name"], "test.png")
|
||||||
|
self.assertEqual(attachments[1]["content"], b64encode(content2))
|
||||||
|
self.assertEqual(attachments[2]["type"], "application/pdf")
|
||||||
|
self.assertEqual(attachments[2]["name"], "") # none
|
||||||
|
self.assertEqual(attachments[2]["content"], b64encode(content3))
|
||||||
|
|
||||||
def test_extra_header_errors(self):
|
def test_extra_header_errors(self):
|
||||||
email = mail.EmailMessage('Subject', 'Body', 'from@example.com',
|
email = mail.EmailMessage('Subject', 'Body', 'from@example.com',
|
||||||
|
|||||||
Reference in New Issue
Block a user