diff --git a/.gitignore b/.gitignore index 77e536a..25a740f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,6 @@ ._* *.pyc *.egg-info +dist/ +TODO.txt local.py diff --git a/AUTHORS.txt b/AUTHORS.txt index 4d56db7..adf7072 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -6,3 +6,5 @@ Chris Jones Mike Edmunds ArnaudF Théo Crevon +Rafael E. Belliard +Jared Morse diff --git a/README.rst b/README.rst index 364d2dc..1a2553c 100644 --- a/README.rst +++ b/README.rst @@ -1,16 +1,16 @@ Djrill, for Mandrill ==================== -.. image:: https://secure.travis-ci.org/brack3t/Djrill.png - :target: https://secure.travis-ci.org/brack3t/Djrill +.. image:: https://secure.travis-ci.org/brack3t/Djrill.png?branch=master + :target: https://travis-ci.org/brack3t/Djrill -Djrill is an email backend and new message class for Django users that want to take advantage of the Mandrill_ transactional -email service from MailChimp_. +Djrill is an email backend for Django users who want to take advantage of the +Mandrill_ transactional email service from MailChimp_. An optional Django admin interface is included. The admin interface allows you to: * Check the status of your Mandrill API connection. -* See stats on email tags and urls. +* See stats on email senders, tags and urls. Djrill is made available under the BSD license. @@ -81,35 +81,38 @@ package, including ``send_mail``, ``send_mass_mail``, ``EmailMessage`` and ``EmailMultiAlternatives``. You can also take advantage of Mandrill-specific features like tags, metadata, -and tracking by creating a ``django.mail.EmailMessage`` (or for HTML, -``django.mail.EmailMultiAlternatives``) object and setting Mandrill-specific +and tracking by creating a Django EmailMessage_ (or for HTML, +EmailMultiAlternatives_) object and setting Mandrill-specific properties on it before calling its ``send`` method. -Example: +Example, sending HTML email with Mandrill tags and metadata: .. code:: python - from django.core.mail import EmailMultiAlternatives # or just EmailMessage if you don't need HTML + from django.core.mail import EmailMultiAlternatives - subject = "Djrill Message" - from_email = "Djrill Sender " # this has to be in your Mandrill account's sending domains - to = ["Djrill Receiver ", "djrill.two@example.com"] - reply_email = "Customer Service " # optional - text_content = "This is the text version of your email" - html_content = "

This is the HTML version of your email

" # optional, use with ``attach_alternative`` below + msg = EmailMultiAlternatives( + subject="Djrill Message", + body="This is the text version of your email", + from_email="Djrill Sender ", + to=["Djrill Receiver ", "another.person@example.com"], + headers={'Reply-To': "Service "} # optional extra headers + ) + msg.attach_alternative("

This is the HTML version of your email

", "text/html") - msg = EmailMultiAlternatives(subject, text_content, from_email, to, headers={'Reply-To': reply_email}) - msg.tags = ["one tag", "two tag", "red tag", "blue tag"] # optional, Mandrill-specific message extension - msg.metadata = {'user_id': "8675309"} # optional, Mandrill-specific message extension - msg.attach_alternative(html_content, "text/html") + # Optional Mandrill-specific extensions (see full list below): + msg.tags = ["one tag", "two tag", "red tag", "blue tag"] + msg.metadata = {'user_id': "8675309"} + + # Send it: msg.send() If the Mandrill API returns an error response for any reason, the send call will raise a ``djrill.mail.backends.djrill.DjrillBackendHTTPError`` exception (unless called with fail_silently=True). -Djrill supports most of the functionality of Django's ``EmailMessage`` and -``EmailMultiAlternatives``. Some limitations: +Djrill supports most of the functionality of Django's `EmailMessage`_ and +`EmailMultiAlternatives`_ classes. Some limitations: * Djrill accepts additional headers, but only ``Reply-To`` and ``X-*`` (since that is all that Mandrill accepts). Any other extra headers will raise a @@ -125,6 +128,10 @@ Djrill supports most of the functionality of Django's ``EmailMessage`` and which Djrill doesn't use. *Caution:* depending on the ``preserve_recipients`` setting, this could result in exposing bcc addresses to all recipients. It's probably best to just avoid bcc.) +* All email addresses (from, to, cc) can be simple ("email@example.com") or + can include a display name ("Real Name "). +* The ``from_email`` must be in one of the approved sending domains in your + Mandrill account. Many of the options from the Mandrill `messages/send.json API`_ ``message`` struct can be set directly on an ``EmailMessage`` (or subclass) object: @@ -160,12 +167,13 @@ see ``DjrillMandrillFeatureTests`` in tests.py for examples. Testing ------- -Djrill is tested against Django 1.3 and 1.4 on Python 2.6 and 2.7. +Djrill is tested against Django 1.3 and 1.4 on Python 2.6 and 2.7, and +Django 1.5beta on Python 2.7. (It may also work with Django 1.2 and Python 2.5, if you use an older version of requests compatible with that code.) -.. image:: https://secure.travis-ci.org/brack3t/Djrill.png - :target: https://secure.travis-ci.org/brack3t/Djrill +.. image:: https://secure.travis-ci.org/brack3t/Djrill.png?branch=master + :target: https://travis-ci.org/brack3t/Djrill The included tests verify that Djrill constructs the expected Mandrill API calls, without actually calling Mandrill or sending any email. So the tests @@ -179,6 +187,18 @@ or:: python runtests.py +Release Notes +------------- + +Version 0.2.0: + +* ``MANDRILL_API_URL`` is no longer required in settings.py +* Earlier versions of Djrill required use of a ``DjrillMessage`` class to + specify Mandrill-specific options. This is no longer needed -- Mandrill + options can now be set directly on a Django EmailMessage_ object or any + subclass. (Existing code can continue to use ``DjrillMessage``.) + + Thanks ------ @@ -193,5 +213,7 @@ the awesome ``requests`` library. .. _django-adminplus: https://github.com/jsocol/django-adminplus .. _mock: http://www.voidspace.org.uk/python/mock/index.html .. _django.core.mail: https://docs.djangoproject.com/en/dev/topics/email/ +.. _EmailMessage: https://docs.djangoproject.com/en/dev/topics/email/#django.core.mail.EmailMessage +.. _EmailMultiAlternatives: https://docs.djangoproject.com/en/dev/topics/email/#sending-alternative-content-types .. _messages/send.json API: https://mandrillapp.com/api/docs/messages.html#method=send diff --git a/djrill/__init__.py b/djrill/__init__.py index 97158cb..1e21173 100644 --- a/djrill/__init__.py +++ b/djrill/__init__.py @@ -1,7 +1,7 @@ from django.contrib.admin.sites import AdminSite from django.utils.text import capfirst -VERSION = (0, 1, 2) +VERSION = (0, 2, 0) __version__ = '.'.join([str(x) for x in VERSION]) diff --git a/djrill/mail/__init__.py b/djrill/mail/__init__.py index d406e90..021b149 100644 --- a/djrill/mail/__init__.py +++ b/djrill/mail/__init__.py @@ -1,30 +1,37 @@ -from django.core.exceptions import ImproperlyConfigured from django.core.mail import EmailMultiAlternatives +# DjrillMessage class is deprecated as of 0.2.0, but retained for +# compatibility with existing code. (New code can just set Mandrill-specific +# options directly on an EmailMessage or EmailMultiAlternatives object.) class DjrillMessage(EmailMultiAlternatives): alternative_subtype = "mandrill" def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, connection=None, attachments=None, headers=None, alternatives=None, cc=None, from_name=None, tags=None, track_opens=True, - track_clicks=True, preserve_recipients=False): + track_clicks=True, preserve_recipients=None): super(DjrillMessage, self).__init__(subject, body, from_email, to, bcc, connection, attachments, headers, alternatives, cc) - self.from_name = from_name - self.tags = self._set_mandrill_tags(tags) - self.track_opens = track_opens - self.track_clicks = track_clicks - self.preserve_recipients = preserve_recipients + if from_name: + self.from_name = from_name + if tags: + self.tags = self._set_mandrill_tags(tags) + if track_opens is not None: + self.track_opens = track_opens + if track_clicks is not None: + self.track_clicks = track_clicks + if preserve_recipients is not None: + self.preserve_recipients = preserve_recipients def _set_mandrill_tags(self, tags): """ Check that all tags are below 50 chars and that they do not start with an underscore. - Raise ImproperlyConfigured if an underscore tag is passed in to + Raise ValueError if an underscore tag is passed in to alert the user. Any tag over 50 chars is left out of the list. """ tag_list = [] @@ -33,8 +40,8 @@ class DjrillMessage(EmailMultiAlternatives): if len(tag) <= 50 and not tag.startswith("_"): tag_list.append(tag) elif tag.startswith("_"): - raise ImproperlyConfigured( + raise ValueError( "Tags starting with an underscore are reserved for " - "internal use and will cause errors with Mandill's API") + "internal use and will cause errors with Mandrill's API") return tag_list diff --git a/djrill/templates/djrill/status.html b/djrill/templates/djrill/status.html index 63f7731..33dac6f 100644 --- a/djrill/templates/djrill/status.html +++ b/djrill/templates/djrill/status.html @@ -61,6 +61,7 @@
{{ term|capfirst }}
{{ value }}
{% endfor %} + {% endblock %} diff --git a/djrill/tests.py b/djrill/tests.py index 7c9e175..3ecef10 100644 --- a/djrill/tests.py +++ b/djrill/tests.py @@ -392,7 +392,7 @@ class DjrillMessageTests(TestCase): self.assertEqual(msg.alternatives[0][0], self.html_content) def test_djrill_message_tag_failure(self): - with self.assertRaises(ImproperlyConfigured): + with self.assertRaises(ValueError): DjrillMessage(self.subject, self.text_content, self.from_email, self.to, tags=["_fail"]) @@ -409,3 +409,15 @@ class DjrillMessageTests(TestCase): self.assertIn(tags[0], msg.tags) self.assertIn(tags[1], msg.tags) self.assertNotIn(tags[2], msg.tags) + + def test_djrill_message_no_options(self): + """DjrillMessage with only basic EmailMessage options should work""" + msg = DjrillMessage(self.subject, self.text_content, + self.from_email, self.to) # no Mandrill-specific options + + self.assertIsInstance(msg, DjrillMessage) + self.assertEqual(msg.body, self.text_content) + self.assertEqual(msg.recipients(), self.to) + self.assertFalse(hasattr(msg, 'tags')) + self.assertFalse(hasattr(msg, 'from_name')) + self.assertFalse(hasattr(msg, 'preserve_recipients')) diff --git a/setup.py b/setup.py index 22ea8ab..da91696 100644 --- a/setup.py +++ b/setup.py @@ -2,9 +2,8 @@ from setuptools import setup setup( name="djrill", - version="0.1.4.1", + version="0.2.0", description='Django email backend for Mandrill.', - long_description='Email backend and new message class to send emails through the Mandrill email service.', keywords="django, mailchimp, mandrill, email, email backend", author="Kenneth Love , Chris Jones ", author_email="kenneth@brack3t.com", @@ -23,4 +22,14 @@ setup( "Framework :: Django", "Environment :: Web Environment", ], + long_description="""\ +Djrill is an email backend for Django users who want to take advantage of the +`Mandrill `_ transactional email service from MailChimp. + +In general, Djrill "just works" with Django's built-in ``django.core.mail`` +package. You can also take advantage of Mandrill-specific features like tags, +metadata, and tracking. An optional Django admin interface is included. + +Full details are on the `project page `_. +""", )