Skip to content

Commit

Permalink
added support for email mimetypes
Browse files Browse the repository at this point in the history
  • Loading branch information
clickonchris committed Jun 9, 2017
1 parent ab7a5b5 commit 1d3d863
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 8 deletions.
1 change: 1 addition & 0 deletions AUTHORS.rst
Expand Up @@ -10,3 +10,4 @@ Contributors:
* Yuri Prezument (@yprez)
* Ștefan Daniel Mihăilă (@stefan-mihaila)
* Wojciech Banaś (@fizista)
* Maestro Health
6 changes: 6 additions & 0 deletions README.rst
Expand Up @@ -238,6 +238,7 @@ If you want to send an email with attachments:
attachments={
'attachment1.doc': '/path/to/file/file1.doc',
'attachment2.txt': ContentFile('file content'),
'attachment3.txt': { 'file': ContentFile('file content'), 'mimetype': 'text/plain'},
}
)
Expand Down Expand Up @@ -565,6 +566,11 @@ or::
Changelog
=========


Version 2.0.X
-------------
* Added support for mimetypes in email attachments

Version 2.0.8
-------------
* Django 1.10 compatibility fixes. Thanks @hockeybuggy!
Expand Down
20 changes: 20 additions & 0 deletions post_office/migrations/0006_attachment_mimetype.py
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('post_office', '0005_auto_20170515_0013'),
]

operations = [
migrations.AddField(
model_name='attachment',
name='mimetype',
field=models.CharField(max_length=255, null=True),
preserve_default=True,
),
]
11 changes: 6 additions & 5 deletions post_office/models.py
Expand Up @@ -60,10 +60,10 @@ class Email(models.Model):
blank=True, null=True)
created = models.DateTimeField(auto_now_add=True, db_index=True)
last_updated = models.DateTimeField(db_index=True, auto_now=True)
scheduled_time = models.DateTimeField(_('The scheduled sending time'),
scheduled_time = models.DateTimeField(_('The scheduled sending time'),
blank=True, null=True, db_index=True)
headers = JSONField(_('Headers'),blank=True, null=True)
template = models.ForeignKey('post_office.EmailTemplate', blank=True,
template = models.ForeignKey('post_office.EmailTemplate', blank=True,
null=True, verbose_name=_('Email template'))
context = context_field_class(_('Context'),blank=True, null=True)
backend_alias = models.CharField(_('Backend alias'), blank=True, default='',
Expand Down Expand Up @@ -108,7 +108,7 @@ def email_message(self, connection=None):
connection=connection, headers=self.headers)

for attachment in self.attachments.all():
msg.attach(attachment.name, attachment.file.read())
msg.attach(attachment.name, attachment.file.read(), mimetype=attachment.mimetype)
attachment.file.close()

return msg
Expand Down Expand Up @@ -164,7 +164,7 @@ class Log(models.Model):

STATUS_CHOICES = [(STATUS.sent, _("sent")), (STATUS.failed, _("failed"))]

email = models.ForeignKey(Email, editable=False, related_name='logs',
email = models.ForeignKey(Email, editable=False, related_name='logs',
verbose_name=_('Email address'))
date = models.DateTimeField(auto_now_add=True)
status = models.PositiveSmallIntegerField(_('Status'),choices=STATUS_CHOICES)
Expand Down Expand Up @@ -196,7 +196,7 @@ class EmailTemplate(models.Model):
verbose_name=_("Content"), validators=[validate_template_syntax])
html_content = models.TextField(blank=True,
verbose_name=_("HTML content"), validators=[validate_template_syntax])
language = models.CharField(max_length=12,
language = models.CharField(max_length=12,
verbose_name=_("Language"),
help_text=_("Render template in alternative language"),
default='', blank=True)
Expand Down Expand Up @@ -242,6 +242,7 @@ class Attachment(models.Model):
name = models.CharField(_('Name'),max_length=255, help_text=_("The original filename"))
emails = models.ManyToManyField(Email, related_name='attachments',
verbose_name=_('Email addresses'))
mimetype = models.CharField(max_length=255, null=True)

class Meta:
app_label = 'post_office'
Expand Down
17 changes: 17 additions & 0 deletions post_office/tests/test_models.py
Expand Up @@ -273,6 +273,23 @@ def test_attachments_email_message(self):
self.assertEqual(message.attachments,
[('test.txt', b'test file content', None)])

def test_attachments_email_message_with_mimetype(self):
email = Email.objects.create(to=['to@example.com'],
from_email='from@example.com',
subject='Subject')

attachment = Attachment()
attachment.file.save(
'test.txt', content=ContentFile('test file content'), save=True
)
attachment.mimetype = 'text/plain'
attachment.save()
email.attachments.add(attachment)
message = email.email_message()

self.assertEqual(message.attachments,
[('test.txt', b'test file content', 'text/plain')])

def test_translated_template_uses_default_templates_name(self):
template = EmailTemplate.objects.create(name='name')
id_template = template.translated_templates.create(language='id')
Expand Down
23 changes: 22 additions & 1 deletion post_office/tests/test_utils.py
Expand Up @@ -143,6 +143,26 @@ def test_create_attachments(self):
self.assertTrue(attachments[0].pk)
self.assertEqual(attachments[0].file.read(), b'content')
self.assertTrue(attachments[0].name.startswith('attachment_file'))
self.assertEquals(attachments[0].mimetype, None)

def test_create_attachments_with_mimetype(self):
attachments = create_attachments({
'attachment_file1.txt': {
'file': ContentFile('content'),
'mimetype': 'text/plain'
},
'attachment_file2.jpg': {
'file': ContentFile('content'),
'mimetype': 'text/plain'
}
})

self.assertEqual(len(attachments), 2)
self.assertIsInstance(attachments[0], Attachment)
self.assertTrue(attachments[0].pk)
self.assertEquals(attachments[0].file.read(), b'content')
self.assertTrue(attachments[0].name.startswith('attachment_file'))
self.assertEquals(attachments[0].mimetype, 'text/plain')

def test_create_attachments_open_file(self):
attachments = create_attachments({
Expand All @@ -153,7 +173,8 @@ def test_create_attachments_open_file(self):
self.assertIsInstance(attachments[0], Attachment)
self.assertTrue(attachments[0].pk)
self.assertTrue(attachments[0].file.read())
self.assertEqual(attachments[0].name, 'attachment_file.py')
self.assertEquals(attachments[0].name, 'attachment_file.py')
self.assertEquals(attachments[0].mimetype, None)

def test_parse_priority(self):
self.assertEqual(parse_priority('now'), PRIORITY.now)
Expand Down
13 changes: 11 additions & 2 deletions post_office/utils.py
Expand Up @@ -69,12 +69,20 @@ def create_attachments(attachment_files):
attachment_files is a dict of:
* Key - the filename to be used for the attachment.
* Value - file-like object, or a filename to open.
* Value - file-like object, or a filename to open OR a dict of {'file': file-like-object, 'mimetype': string}
Returns a list of Attachment objects
"""
attachments = []
for filename, content in attachment_files.items():
for filename, filedata in attachment_files.items():

if isinstance(filedata, dict):
content = filedata.get('file', None)
mimetype = filedata.get('mimetype', None)
else:
content = filedata
mimetype = None

opened_file = None

if isinstance(content, string_types):
Expand All @@ -83,6 +91,7 @@ def create_attachments(attachment_files):
content = File(opened_file)

attachment = Attachment()
attachment.mimetype = mimetype
attachment.file.save(filename, content=content, save=True)

attachments.append(attachment)
Expand Down

0 comments on commit 1d3d863

Please sign in to comment.