Feature: Add envelope_sender

New EmailMessage attribute `envelope_sender` controls ESP's sender,
sending domain, or return path where supported:

* Mailgun: overrides SENDER_DOMAIN on individual message
  (domain portion only)
* Mailjet: becomes `Sender` API param
* Mandrill: becomes `return_path_domain` API param
  (domain portion only)
* SparkPost: becomes `return_path` API param
* Other ESPs: not believed to be supported

Also support undocumented Django SMTP backend behavior, where envelope
sender is given by `message.from_email` when
`message.extra_headers["From"]` is set. Fixes #91.
This commit is contained in:
medmunds
2018-02-26 18:42:19 -08:00
parent bd9d92f5a0
commit 07fbeac6bd
27 changed files with 246 additions and 28 deletions

View File

@@ -28,32 +28,33 @@ The table below summarizes the Anymail features supported for each ESP.
.. currentmodule:: anymail.message
============================================ ========== ========== ========== ========== ========== ============ ===========
Email Service Provider |Mailgun| |Mailjet| |Mandrill| |Postmark| |SendGrid| |SendinBlue| |SparkPost|
============================================ ========== ========== ========== ========== ========== ============ ===========
============================================ =========== ========== =========== ========== ========== ============ ===========
Email Service Provider |Mailgun| |Mailjet| |Mandrill| |Postmark| |SendGrid| |SendinBlue| |SparkPost|
============================================ =========== ========== =========== ========== ========== ============ ===========
.. rubric:: :ref:`Anymail send options <anymail-send-options>`
-----------------------------------------------------------------------------------------------------------------------------------
:attr:`~AnymailMessage.metadata` Yes Yes Yes No Yes Yes Yes
:attr:`~AnymailMessage.send_at` Yes No Yes No Yes No Yes
:attr:`~AnymailMessage.tags` Yes Max 1 tag Yes Max 1 tag Yes Max 1 tag Max 1 tag
:attr:`~AnymailMessage.track_clicks` Yes Yes Yes Yes Yes No Yes
:attr:`~AnymailMessage.track_opens` Yes Yes Yes Yes Yes No Yes
-------------------------------------------------------------------------------------------------------------------------------------
:attr:`~AnymailMessage.envelope_sender` Domain only Yes Domain only No No No Yes
:attr:`~AnymailMessage.metadata` Yes Yes Yes No Yes Yes Yes
:attr:`~AnymailMessage.send_at` Yes No Yes No Yes No Yes
:attr:`~AnymailMessage.tags` Yes Max 1 tag Yes Max 1 tag Yes Max 1 tag Max 1 tag
:attr:`~AnymailMessage.track_clicks` Yes Yes Yes Yes Yes No Yes
:attr:`~AnymailMessage.track_opens` Yes Yes Yes Yes Yes No Yes
.. rubric:: :ref:`templates-and-merge`
-----------------------------------------------------------------------------------------------------------------------------------
:attr:`~AnymailMessage.template_id` No Yes Yes Yes Yes Yes Yes
:attr:`~AnymailMessage.merge_data` Yes Yes Yes No Yes No Yes
:attr:`~AnymailMessage.merge_global_data` (emulated) Yes Yes Yes Yes Yes Yes
-------------------------------------------------------------------------------------------------------------------------------------
:attr:`~AnymailMessage.template_id` No Yes Yes Yes Yes Yes Yes
:attr:`~AnymailMessage.merge_data` Yes Yes Yes No Yes No Yes
:attr:`~AnymailMessage.merge_global_data` (emulated) Yes Yes Yes Yes Yes Yes
.. rubric:: :ref:`Status <esp-send-status>` and :ref:`event tracking <event-tracking>`
-----------------------------------------------------------------------------------------------------------------------------------
:attr:`~AnymailMessage.anymail_status` Yes Yes Yes Yes Yes Yes Yes
|AnymailTrackingEvent| from webhooks Yes Yes Yes Yes Yes No Yes
-------------------------------------------------------------------------------------------------------------------------------------
:attr:`~AnymailMessage.anymail_status` Yes Yes Yes Yes Yes Yes Yes
|AnymailTrackingEvent| from webhooks Yes Yes Yes Yes Yes No Yes
.. rubric:: :ref:`Inbound handling <inbound>`
-----------------------------------------------------------------------------------------------------------------------------------
|AnymailInboundEvent| from webhooks Yes Yes Yes Yes Yes No Yes
============================================ ========== ========== ========== ========== ========== ============ ===========
-------------------------------------------------------------------------------------------------------------------------------------
|AnymailInboundEvent| from webhooks Yes Yes Yes Yes Yes No Yes
============================================ =========== ========== =========== ========== ========== ============ ===========
Trying to choose an ESP? Please **don't** start with this table. It's far more

View File

@@ -94,15 +94,22 @@ Mailgun account is configured for "*mail1*.example.com", you should provide
If you need to override the sender domain for an individual message,
include `sender_domain` in Anymail's :attr:`~anymail.message.AnymailMessage.esp_extra`
for that message:
use Anymail's :attr:`~anymail.message.AnymailMessage.envelope_sender`
(only the domain is used; anything before the @ is ignored):
.. code-block:: python
message = EmailMessage(from_email="marketing@example.com", ...)
message.esp_extra = {"sender_domain": "mail2.example.com"}
message.envelope_sender = "anything@mail2.example.com" # the "anything@" is ignored
.. versionchanged:: 2.0
Earlier Anymail versions looked for a special `sender_domain` key in the message's
:attr:`~anymail.message.AnymailMessage.esp_extra` to override Mailgun's sender domain.
This is still supported, but may be deprecated in a future release. Using
:attr:`~anymail.message.AnymailMessage.envelope_sender` as shown above is now preferred.
.. _Mailgun sender domain:
https://help.mailgun.com/hc/en-us/articles/202256730-How-do-I-pick-a-domain-name-for-my-Mailgun-account-
@@ -139,6 +146,12 @@ Limitations and quirks
if you need to access that metadata from an opened, clicked, or unsubscribed
:ref:`tracking event <event-tracking>` handler.
**Envelope sender uses only domain**
Anymail's :attr:`~anymail.message.AnymailMessage.envelope_sender` is used to
select your Mailgun :ref:`sender domain <mailgun-sender-domain>`. For
obvious reasons, only the domain portion applies. You can use anything before
the @, and it will be ignored.
.. _mailgun-templates:

View File

@@ -126,6 +126,11 @@ Limitations and quirks
**No delayed sending**
Mailjet does not support :attr:`~anymail.message.AnymailMessage.send_at`.
**Envelope sender may require approval**
Anymail passes :attr:`~anymail.message.AnymailMessage.envelope_sender` to
Mailjet, but this may result in an API error if you have not received
special approval from Mailjet support to use custom senders.
**Commas in recipient names**
Mailjet's v3 API does not properly handle commas in recipient display-names
*if* your message also uses the ``cc`` or ``bcc`` fields.

View File

@@ -129,6 +129,18 @@ as Mandrill's more complex list of name/content dicts.
.. _messages/send API:
https://mandrillapp.com/api/docs/messages.JSON.html#method=send
.. _mandrill-quirks:
Limitations and quirks
----------------------
**Envelope sender uses only domain**
Anymail's :attr:`~anymail.message.AnymailMessage.envelope_sender` is used to
populate Mandrill's `'return_path_domain'`---but only the domain portion.
(Mandrill always generates its own encoded mailbox for the envelope sender.)
.. _mandrill-templates:
Batch sending/merge and ESP templates
@@ -363,6 +375,13 @@ Changes to EmailMessage attributes
With Anymail, set ``message.from_email = None`` or ``message.subject = None``
to use the values from the stored template.
``message.return_path_domain``
With Anymail, set :attr:`~anymail.message.AnymailMessage.envelope_sender`
instead. You'll need to pass a valid email address (not just a domain),
but Anymail will use only the domain, and will ignore anything before the @.
.. versionchanged:: 2.0
**Other Mandrill-specific attributes**
Djrill allowed nearly all Mandrill API parameters to be set
as attributes directly on an EmailMessage. With Anymail, you

View File

@@ -120,6 +120,11 @@ see :ref:`unsupported-features`.
.. _several link-tracking options:
http://developer.postmarkapp.com/developer-link-tracking.html
**No envelope sender overrides**
Postmark does not support overriding :attr:`~anymail.message.AnymailMessage.envelope_sender`
on individual messages. (You can configure custom return paths for each sending domain in
the Postmark control panel.)
.. _postmark-templates:

View File

@@ -195,6 +195,10 @@ Limitations and quirks
(Tested March, 2016)
**No envelope sender overrides**
SendGrid does not support overriding :attr:`~anymail.message.AnymailMessage.envelope_sender`
on individual messages.
.. _sendgrid-templates:

View File

@@ -1,7 +1,7 @@
.. _sendinblue-backend:
SendinBlue
========
==========
Anymail integrates with the `SendinBlue`_ email service, using their `Web API v3`_.
@@ -88,3 +88,7 @@ Limitations and quirks
If you use a template you will suffer some limitations:
you can't change the subject or/and the body, and all email's
display-names will be hidden.
**No envelope sender overrides**
SendinBlue does not support overriding :attr:`~anymail.message.AnymailMessage.envelope_sender`
on individual messages.

View File

@@ -127,6 +127,13 @@ Limitations and quirks
(SparkPost's "recipient tags" are not available for tagging *messages*.
They're associated with individual *addresses* in stored recipient lists.)
**Envelope sender may use domain only**
Anymail's :attr:`~anymail.message.AnymailMessage.envelope_sender` is used to
populate SparkPost's `'return_path'` parameter. Anymail supplies the full
email address, but depending on your SparkPost configuration, SparkPost may
use only the domain portion and substitute its own encoded mailbox before
the @.
.. _sparkpost-templates:

View File

@@ -68,6 +68,35 @@ ESP send options (AnymailMessage)
:class:`~django.core.mail.EmailMessage` you send.
(You don't have to use :class:`AnymailMessage`.)
.. attribute:: envelope_sender
.. versionadded:: 2.0
Set this to a `str` email address that should be used as the message's
envelope sender. If supported by your ESP, this will become the Return-Path
in the recipient's mailbox.
(Envelope sender is also known as bounce address, MAIL FROM, envelope from,
unixfrom, SMTP FROM command, return path, and `several other terms`_. Confused?
Here's some good info on `how envelope sender relates to return path`_.)
ESP support for envelope sender varies widely. Be sure to check Anymail's
docs for your :ref:`specific ESP <supported-esps>` before attempting to use it.
And note that those ESPs who do support it will often use only the domain
portion of the envelope sender address, overriding the part before the @ with
their own encoded bounce mailbox.
[The :attr:`!envelope_sender` attribute is unique to Anymail. If you also use Django's
SMTP EmailBackend, you can portably control envelope sender by *instead* setting
``message.extra_headers["From"]`` to the desired email *header* :mailheader:`From`,
and ``message.from_email`` to the *envelope sender*. Anymail also allows this approach,
for compatibility with the SMTP EmailBackend. See the notes `in Django's bug tracker`_.]
.. _several other terms: https://en.wikipedia.org/wiki/Bounce_address
.. _in Django's bug tracker: https://code.djangoproject.com/ticket/9214
.. _how envelope sender relates to return path:
https://www.postmastery.com/blog/about-the-return-path-header/
.. attribute:: metadata
Set this to a `dict` of metadata values the ESP should store