Django

Code

Changeset 5547

Show
Ignore:
Timestamp:
06/27/07 04:44:55 (1 year ago)
Author:
mtredinnick
Message:

Fixed #1541 -- Added ability to create multipart email messages. Thanks, Nick
Lane.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/core/mail.py

    r5146 r5547  
    44 
    55from django.conf import settings 
     6from email import Charset, Encoders 
    67from email.MIMEText import MIMEText 
     8from email.MIMEMultipart import MIMEMultipart 
     9from email.MIMEBase import MIMEBase 
    710from email.Header import Header 
    811from email.Utils import formatdate 
    9 from email import Charset 
     12import mimetypes 
    1013import os 
    1114import smtplib 
     
    1720# some spam filters. 
    1821Charset.add_charset('utf-8', Charset.SHORTEST, Charset.QP, 'utf-8') 
     22 
     23# Default MIME type to use on attachments (if it is not explicitly given 
     24# and cannot be guessed). 
     25DEFAULT_ATTACHMENT_MIME_TYPE = 'application/octet-stream' 
    1926 
    2027# Cache the hostname, but do it lazily: socket.getfqdn() can take a couple of 
     
    5663    pass 
    5764 
    58 class SafeMIMEText(MIMEText): 
     65class SafeHeaderMixin(object): 
    5966    def __setitem__(self, name, val): 
    6067        "Forbids multi-line headers, to prevent header injection." 
     
    6370        if name == "Subject": 
    6471            val = Header(val, settings.DEFAULT_CHARSET) 
    65         MIMEText.__setitem__(self, name, val) 
     72        # Note: using super() here is safe; any __setitem__ overrides must use 
     73        # the same argument signature. 
     74        super(SafeHeaderMixin, self).__setitem__(name, val) 
     75 
     76class SafeMIMEText(MIMEText, SafeHeaderMixin): 
     77    pass 
     78 
     79class SafeMIMEMultipart(MIMEMultipart, SafeHeaderMixin): 
     80    pass 
    6681 
    6782class SMTPConnection(object): 
     
    155170    A container for email information. 
    156171    """ 
    157     def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, connection=None): 
     172    def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, 
     173            connection=None, attachments=None): 
    158174        self.to = to or [] 
    159175        self.bcc = bcc or [] 
     
    161177        self.subject = subject 
    162178        self.body = body 
     179        self.attachments = attachments or [] 
    163180        self.connection = connection 
    164181 
     
    170187    def message(self): 
    171188        msg = SafeMIMEText(self.body, 'plain', settings.DEFAULT_CHARSET) 
     189        if self.attachments: 
     190            body_msg = msg 
     191            msg = SafeMIMEMultipart() 
     192            if self.body: 
     193                msg.attach(body_msg) 
     194            for attachment in self.attachments: 
     195                if isinstance(attachment, MIMEBase): 
     196                    msg.attach(attachment) 
     197                else: 
     198                    msg.attach(self._create_attachment(*attachment)) 
    172199        msg['Subject'] = self.subject 
    173200        msg['From'] = self.from_email 
     
    190217        return self.get_connection(fail_silently).send_messages([self]) 
    191218 
     219    def attach(self, filename, content=None, mimetype=None): 
     220        """ 
     221        Attaches a file with the given filename and content. 
     222 
     223        Alternatively, the first parameter can be a MIMEBase subclass, which 
     224        is inserted directly into the resulting message attachments. 
     225        """ 
     226        if isinstance(filename, MIMEBase): 
     227            self.attachements.append(filename) 
     228        else: 
     229            assert content is not None 
     230            self.attachments.append((filename, content, mimetype)) 
     231 
     232    def attach_file(self, path, mimetype=None): 
     233        """Attaches a file from the filesystem.""" 
     234        filename = os.path.basename(path) 
     235        content = open(path, 'rb').read() 
     236        self.attach(filename, content, mimetype) 
     237 
     238    def _create_attachment(self, filename, content, mimetype=None): 
     239        """ 
     240        Convert the filename, content, mimetype triple into a MIME attachment 
     241        object. 
     242        """ 
     243        if mimetype is None: 
     244            mimetype, _ = mimetypes.guess_type(filename) 
     245            if mimetype is None: 
     246                mimetype = DEFAULT_ATTACHMENT_MIME_TYPE 
     247        basetype, subtype = mimetype.split('/', 1) 
     248        if basetype == 'text': 
     249            attachment = SafeMIMEText(content, subtype, settings.DEFAULT_CHARSET) 
     250        else: 
     251            # Encode non-text attachments with base64. 
     252            attachment = MIMEBase(basetype, subtype) 
     253            attachment.set_payload(content) 
     254            Encoders.encode_base64(attachment) 
     255        attachment.add_header('Content-Disposition', 'attachment', filename=filename) 
     256        return attachment 
     257 
    192258def send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None): 
    193259    """ 
  • django/trunk/docs/email.txt

    r5155 r5547  
    2929 
    3030    The character set of e-mail sent with ``django.core.mail`` will be set to 
    31     the value of your `DEFAULT_CHARSET setting`_
    32  
    33 .. _DEFAULT_CHARSET setting: ../settings/#default-charset 
     31    the value of your `DEFAULT_CHARSET`_ setting
     32 
     33.. _DEFAULT_CHARSET: ../settings/#default-charset 
    3434.. _EMAIL_HOST: ../settings/#email-host 
    3535.. _EMAIL_PORT: ../settings/#email-port 
     
    199199    Not all features of the ``EmailMessage`` class are available through the 
    200200    ``send_mail()`` and related wrapper functions. If you wish to use advanced 
    201     features, such as BCC'ed recipients or multi-part e-mail, you'll need to 
    202     create ``EmailMessage`` instances directly. 
     201    features, such as BCC'ed recipients, file attachments, or multi-part 
     202    e-mail, you'll need to create ``EmailMessage`` instances directly. 
     203 
     204    This is a design feature. ``send_mail()`` and related functions were 
     205    originally the only interface Django provided. However, the list of 
     206    parameters they accepted was slowly growing over time.  It made sense to 
     207    move to a more object-oriented design for e-mail messages and retain the 
     208    original functions only for backwards compatibility. 
     209 
     210    If you need to add new functionality to the e-mail infrastrcture, 
     211    sub-classing the ``EmailMessage`` class should make this a simple task. 
    203212 
    204213In general, ``EmailMessage`` is responsible for creating the e-mail message 
     
    207216``SMTPConnection`` instance) for multiple messages. 
    208217 
     218E-mail messages 
     219---------------- 
     220 
    209221The ``EmailMessage`` class is initialized as follows:: 
    210222 
    211     email = EmailMessage(subject, body, from_email, to, bcc, connection) 
     223    email = EmailMessage(subject, body, from_email, to, 
     224                         bcc, connection, attachments) 
    212225 
    213226All of these parameters are optional. If ``from_email`` is omitted, the value 
    214227from ``settings.DEFAULT_FROM_EMAIL`` is used. Both the ``to`` and ``bcc`` 
    215 parameters are lists of addresses, as strings. 
     228parameters are lists of addresses, as strings. The ``attachments`` parameter is 
     229a list containing either ``(filename, content, mimetype)`` triples of 
     230``email.MIMEBase.MIMEBase`` instances. 
    216231 
    217232For example:: 
     
    228243 
    229244    * ``message()`` constructs a ``django.core.mail.SafeMIMEText`` object (a 
    230       sub-class of Python's ``email.MIMEText.MIMEText`` class) holding the 
     245      sub-class of Python's ``email.MIMEText.MIMEText`` class) or a 
     246      ``django.core.mail.SafeMIMEMultipart`` object holding the 
    231247      message to be sent. If you ever need to extend the `EmailMessage` class, 
    232248      you'll probably want to override this method to put the content you wish 
     
    240256      need to be returned from this method as well. 
    241257 
     258    * ``attach()`` creates a new file attachment and adds it to the message. 
     259      There are two ways to call ``attach()``: 
     260 
     261       * You can pass it a single argument which is an 
     262         ``email.MIMBase.MIMEBase`` instance. This will be inserted directly 
     263         into the resulting message. 
     264 
     265       * Alternatively, you can pass ``attach()`` three arguments: 
     266         ``filename``, ``content`` and ``mimetype``. ``filename`` is the name 
     267         of the file attachment as it will appear in the email, ``content`` is 
     268         the data that will be contained inside the attachment and 
     269         ``mimetype`` is the optional MIME type for the attachment. If you 
     270         omit ``mimetype``, the MIME content type will be guessed from the 
     271         filename of the attachment. 
     272 
     273         For example:: 
     274 
     275            message.attach('design.png', img_data, 'image/png') 
     276 
     277    * ``attach_file()`` creates a new attachment using a file from your 
     278      filesystem. Call it with the path of the file to attach and, optionally, 
     279      the MIME type to use for the attachment. If the MIME type is omitted, it 
     280      will be guessed from the filename. The simplest use would be:: 
     281 
     282        message.attach_file('/images/weather_map.png') 
     283 
     284SMTP network connections 
     285------------------------- 
     286 
    242287The ``SMTPConnection`` class is initialized with the host, port, username and 
    243288password for the SMTP server. If you don't specify one or more of those