'MailHost.secureSend' is now deprecated; use 'send' instead

by Plone Documentation Team last modified Aug 30, 2010 05:03 AM
The SecureMailHost product is no longer a part of Plone in 4.0. As a result, the 'secureSend' method which was generally used to send mail is now deprecated. The default 'send' method of MailHost should be used instead.

In Plone 2.1 - 3.x the standard method for sending mail looked like this:

mh = getToolByName(context, 'MailHost')
mh.secureSend(message, mto, mfrom, subject=None,
              mcc=None, mbcc=None, subtype=None, 
              charset=None, **kwargs)

Where the message parameter is either text with no headers or an email.Message.Message object, the mto, mfrom, mcc and mbcc parameters are lists of email addresses, subject is content of the email subject header, subtype is used to provide the message mime sub-type, charset is used for message and header encoding, and the kwargs are used to provide additional headers.

In Plone 4.x, this method is deprecated and the standard send method of the MailHost should be used instead. The following is an example of using send:

mh = getToolByName(context, 'MailHost')
mh.send(messageText, mto=None, mfrom=None, 
        subject=None, encode=None, 
        immediate=False, charset='utf8', msg_type=None)

Here, messageText is the message with or without headers or an email.Message.Message object, mto and mfrom are strings containing the to and from addresses, subject is the content of the email subject header, encode is used to specify the message payload encoding (and should almost never be used), immediate is used to override the default MailHost queuing behavior, and charset is used for message and header character encoding (in Plone you should generally pass 'utf8' as the value for charset unless you have a specific reason not to). If you need to set custom headers they will need to be set in the messageText itself.

Message Type

Instead of passing the MIME subtype as the subtype parameter to set the message content type, you pass the full MIME type as msg_type. So instead of subtype='plain' you would use msg_type='text/plain'.

Custom Headers

The secureSend method had provided the ability to set some specific headers, and to set custom headers as well. Unfortunately, send does not allow doing this directly; fortunately it is pretty simple to construct a message with custom headers to pass to send. Below is an example that assumes you have the MailHost object and have already defined message_body, mto, mfrom and subject:

from email import message_from_string
from email.Header import Header
my_message = message_from_string(message_body.encode('utf-8'))
my_message.set_charset('utf-8')
my_message['CC']= Header('someone@example.com')
my_message['BCC']= Header('secret@example.com')
my_message['X-Custom'] = Header(u'Some Custom Parameter', 'utf-8')
mailhost.send(my_message, mto, mfrom, subject)

Delayed Sending

By default send waits to send messages until the end of the request transaction.  This ensures that if a conflict error occurs and the transaction is retried, multiple emails will not be sent (which is what happens with secureSend and earlier versions of send). Unfortunately, this means that unless you explicitly request immediate=True when using send, you will not be able to catch any errors which might happen during sending, as they won't occur until the end of the transaction.

If you want to handle email errors to prevent them from aborting an otherwise successful transaction, you need to set immediate=True and enclose the send call in a try/except block. Alternatively, you can go the the MailHost configuration screen in the ZMI and enable SMTP Queuing. This will ensure the mail sending happens completely outside of the transaction, providing more reliability and increased performance while still avoiding transaction retry issues. Using the new MailHost queueing feature is highly recommended for production sites.

Writing Tests

Plone includes some helpers for writing tests that need to use email in the Products.CMFPlone.tests.utils and Products.CMFPlone.tests.test_mails modules. These include a MockMailHost and a MockMailHostTestCase that replaces the MailHost in the test Plone site with a MockMailHost object. For products that make use of Plone's MockMailHost in their own tests, there are a few more changes that need to be made.

The messages property of the mail host no longer includes the an email.Message object, but instead contains a string representation of message. This means that in order to test the message object you can either work directly with the message string, or convert it into a email message object using the message_from_string function used in the last example.

Summary

In most cases, all you need to do to use send instead of secureSend is convert your mto and mfrom parameters from lists to comma separated strings, and add any CC, BCC, or other headers directly to the messageText instead of passing them as parameters. If you are using secureSend to add custom headers or make other adjustments to the message, the changes are a little more involved, but still straightforward. Additionally, if you are using Plone's MockMailHost in your tests you will need to update your tests to work with the message string rather than an email.Message object.