BaseMailTemplate.py 6.53 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11
# Copyright (c) 2005-2006 Simplistix Ltd
#
# This Software is released under the MIT License:
# http://www.opensource.org/licenses/mit-license.html
# See license.txt for more details.

import os
import rfc822

from AccessControl import ClassSecurityInfo
from DateTime import DateTime
12
from email.Header import Header
13 14
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
15
from email.Utils import make_msgid, formataddr, getaddresses
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

from App.class_init import default__class_init__ as InitializeClass
from App.Common import package_home
from MTMultipart import MTMultipart
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
from Products.PageTemplates.PageTemplateFile import PageTemplateFile

from ZPublisher import HTTPResponse

# Configured using zope.conf in Zope 2.7.8, Zope 2.8.2, and above
default_encoding = getattr(HTTPResponse,'default_encoding','iso-8859-15')

class BaseMailTemplate:

    security = ClassSecurityInfo()

    _properties = ()

    ZScriptHTML_tryForm = None

    content_type = 'text/plain'

    mailhost = None

    security.declarePrivate('_process')
    def _process(self,kw):
        # sort out what encoding we're going to use
        encoding = kw.get('encoding',
                          self.getProperty('encoding',
                                           default_encoding))
        text = self.__class__.__bases__[1].__call__(self,**kw)
47 48 49 50 51
        # ZPT adds newline at the end, but it breaks backward compatibility.
        # So I remove it.
        if text.endswith('\n'):
            text = text[:-1]
        if not self.html() and isinstance(text, unicode):
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
            text = text.encode(encoding,'replace')
        # now turn the result into a MIMEText object
        msg = MIMEText(
            text.replace('\r',''),
            self.content_type.split('/')[1],
            encoding
            )
        # sort out what headers and addresses we're going to use
        headers = {}
        values = {}
        # headers from the headers property
        for header in getattr(self,'headers',()):
            name,value = header.split(':',1)
            headers[name]=value
        # headers from the headers parameter
        headers_param = kw.get('headers',{})
        headers.update(headers_param)
        # values and some specific headers
        for key,header in (('mfrom','From'),
                           ('mto','To'),
                           ('mcc','Cc'),
                           ('mbcc','Bcc'),
                           ('subject','Subject')):
            value = kw.get(key,
                           headers_param.get(header,
                                             getattr(self,
                                                     key,
                                                     headers.get(header))))
            if value is not None:
                values[key]=value
82

83
                # turn some sequences in coma-seperated strings
84
                if isinstance(value, (tuple, list)):
85 86 87 88
                    value = ', '.join(value)
                # make sure we have no unicode headers
                if isinstance(value,unicode):
                    value = value.encode(encoding)
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107

                if key == 'subject':
                    try:
                        # Try to keep header non encoded
                        value = Header(value.encode("ascii"))
                    except UnicodeDecodeError:
                        value = Header(value, "UTF-8")

                else:
                    value_list = getaddresses([value])
                    dest_list = []
                    for name, email in value_list:
                        try:
                            name = Header(name.encode("ascii"))
                        except UnicodeDecodeError:
                            name = Header(name, "UTF-8")
                        dest_list.append(formataddr((name.encode(), email)))
                    value = ", ".join(dest_list)

108 109 110
                headers[header]=value
        # check required values have been supplied
        errors = []
111
        for param in ('mfrom','mto'):
112 113 114 115 116 117 118 119 120
            if not values.get(param):
                errors.append(param)
        if errors:
            raise TypeError(
                'The following parameters were required by not specified: '+(
                ', '.join(errors)
                ))
        # add date header
        headers['Date']=DateTime().rfc822()
121 122 123
        # do not let the MTA to generate the Message-ID:
        # we want to have it stored in ERP5, for mail threading
        headers['Message-ID'] = make_msgid()
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
        # turn headers into an ordered list for predictable header order
        keys = headers.keys()
        keys.sort()
        return msg,values,[(key,headers[key]) for key in keys]

    security.declarePrivate('_send')
    def _send(self,mfrom,mto,msg):

        mailhost = self.restrictedTraverse(self.mailhost,None)
        if not getattr(mailhost,'meta_type',None) in (
            'Mail Host','Maildrop Host'
            ):
            raise RuntimeError(
                'Could not traverse to MailHost %r' % self.mailhost
                )

        mailhost._send(mfrom,mto,msg.as_string())

    security.declareProtected('View', 'send')
    def send(self,**kw):

        msg,values,headers = self._process(kw)

        for header,value in headers:
            msg[header]=value

        to_addrs = ()
        for key in ('mto', 'mcc', 'mbcc'):
            v = values.get(key)
            if v:
                if isinstance(v, basestring):
                    v = [rfc822.dump_address_pair(addr) for addr \
                            in rfc822.AddressList(v)]
                to_addrs += tuple(v)

        self._send(values['mfrom'], to_addrs, msg)

    security.declareProtected('View', '__call__')
    __call__ = send

    security.declareProtected('View', 'as_message')
    def as_message(self,**kw):
        msg,values,headers = self._process(kw)
        multipart_kw = {}
        #subtype = kw.get('subtype')
        #if subtype:
        #    multipart_kw['_subtype'] = subtype
        #boundary = kw.get('boundary')
        #if boundary:
        #    multipart_kw['boundary'] = boundary

        multipart = MTMultipart(self,
                                values['mfrom'],
                                values['mto'],
                                **multipart_kw)

        # set the encoding for the container
        #multipart.set_charset(msg.get_charset())

        for header,value in headers:
            multipart[header]=value

        multipart.attach(msg)

        return multipart

InitializeClass(BaseMailTemplate)