Commit 0c273c09 authored by Yoshinori Okuji's avatar Yoshinori Okuji
Browse files

Initial import. This is the HEAD of the official repository + my patch

to address the problem that Cc: or Bcc: does not work. Since I don't see
any maintenance activity in them, I put a fork here. Once they fix
the problem officially, we can trash this fork.

FYI, here is my report:

https://secure.simplistix.co.uk/support/issue318


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@20876 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 849af0b1
# 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
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from Globals import InitializeClass, package_home
from MTMultipart import MTMultipart
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate, SUPPORTS_WEBDAV_LOCKS
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)
if not self.html():
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
# turn some sequences in coma-seperated strings
if isinstance(value,tuple) or isinstance(value,list):
value = ', '.join(value)
# make sure we have no unicode headers
if isinstance(value,unicode):
value = value.encode(encoding)
headers[header]=value
# check required values have been supplied
errors = []
for param in ('mfrom','mto','subject'):
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()
# 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)
# 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.
from AccessControl import ClassSecurityInfo
from AccessControl import getSecurityManager
from Globals import InitializeClass
from Products.CMFCore.FSPageTemplate import FSPageTemplate,expandpath
from Products.CMFCore.DirectoryView import registerFileExtension
from Products.CMFCore.DirectoryView import registerMetaType
from BaseMailTemplate import BaseMailTemplate
from MailTemplate import MailTemplate
class FSMailTemplate(BaseMailTemplate,FSPageTemplate):
"Wrapper for Mail Template"
security = ClassSecurityInfo()
meta_type = 'Filesystem Mail Template'
def __init__(self, id, filepath, fullname=None, properties=None):
FSPageTemplate.__init__(self,id,filepath,fullname,properties)
self._properties = properties
security.declarePrivate('_createZODBClone')
def _createZODBClone(self):
"""Create a ZODB (editable) equivalent of this object."""
obj = MailTemplate(self.getId(), self._text, self.content_type)
obj.expand = 0
obj.write(self.read())
obj._setPropValue('mailhost',self.mailhost)
obj.content_type = self.content_type
if self._properties:
keys = self._properties.keys()
keys.sort()
for id in keys:
if id not in ('mailhost','content_type'):
obj.manage_addProperty(id,self._properties[id],'string')
return obj
security.declarePrivate('_readFile')
def _readFile(self, reparse):
fp = expandpath(self._filepath)
file = open(fp, 'r') # not 'rb', as this is a text file!
try:
data = file.read()
finally:
file.close()
if reparse:
self.write(data)
def _exec(self, bound_names, args, kw):
"""Call a FSPageTemplate"""
try:
response = self.REQUEST.RESPONSE
except AttributeError:
response = None
# Read file first to get a correct content_type default value.
self._updateFromFS()
if not kw.has_key('args'):
kw['args'] = args
bound_names['options'] = kw
security=getSecurityManager()
bound_names['user'] = security.getUser()
# Retrieve the value from the cache.
keyset = None
if self.ZCacheable_isCachingEnabled():
# Prepare a cache key.
keyset = {
# Why oh why?
# All this code is cut and paste
# here to make sure that we
# dont call _getContext and hence can't cache
# Annoying huh?
'here': self.aq_parent.getPhysicalPath(),
'bound_names': bound_names}
result = self.ZCacheable_get(keywords=keyset)
if result is not None:
# Got a cached value.
return result
# Execute the template in a new security context.
security.addContext(self)
try:
result = self.pt_render(extra_context=bound_names)
if keyset is not None:
# Store the result in the cache.
self.ZCacheable_set(result, keywords=keyset)
return result
finally:
security.removeContext(self)
return result
InitializeClass(FSMailTemplate)
registerFileExtension('mt', FSMailTemplate)
registerMetaType('Mail Template', FSMailTemplate)
# 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.
from AccessControl import ClassSecurityInfo
from email import Encoders
from email.MIMEBase import MIMEBase
from email.MIMEMultipart import MIMEMultipart
from Globals import InitializeClass
from zope.app.content_types import guess_content_type
from OFS.Image import File
from ZPublisher.HTTPRequest import FileUpload
def cookId(filename):
return filename[max(filename.rfind('/'),
filename.rfind('\\'),
filename.rfind(':'),
)+1:]
class MTMultipart(MIMEMultipart):
security = ClassSecurityInfo()
security.setDefaultAccess('allow')
def __init__(self,mt,mfrom,mto,_subtype='mixed',boundary=None):
MIMEMultipart.__init__(self,_subtype,boundary)
self.mfrom = mfrom
self.mto = mto
self.mt = mt
security.declarePublic('send')
def send(self):
"send ourselves using our MailTemplate's send method"
return self.mt._send(self.mfrom,self.mto,self)
security.declarePublic('add_file')
def add_file(self,theFile=None,data=None,filename=None,content_type=None):
"add a Zope file or Image to ourselves as an attachment"
if theFile and data:
raise TypeError(
'A file-like object was passed as well as data to create a file'
)
if (data or filename) and not (data and filename):
raise TypeError(
'Both data and filename must be specified'
)
if data:
if content_type is None:
content_type, enc=guess_content_type(filename, data)
elif isinstance(theFile,File):
filename = theFile.getId()
data = str(theFile.data)
content_type = content_type or theFile.content_type
elif isinstance(theFile,file):
filename = cookId(theFile.name)
data = theFile.read()
if content_type is None:
content_type,enc = guess_content_type(filename, data)
elif isinstance(theFile,FileUpload):
filename = cookId(theFile.filename)
data=theFile.read()
headers=theFile.headers
if content_type is None:
if headers.has_key('content-type'):
content_type=headers['content-type']
else:
content_type, enc=guess_content_type(filename, data)
else:
raise TypeError('Unknown object type found: %r' % theFile)
msg = MIMEBase(*content_type.split('/'))
msg.set_payload(data)
Encoders.encode_base64(msg)
msg.add_header('Content-Disposition', 'attachment',
filename=filename)
self.attach(msg)
InitializeClass(MTMultipart)
# 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
from AccessControl import ClassSecurityInfo
from AccessControl import getSecurityManager
from Globals import InitializeClass, package_home
from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate, SUPPORTS_WEBDAV_LOCKS
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from BaseMailTemplate import BaseMailTemplate
class MailTemplate(BaseMailTemplate,ZopePageTemplate):
"A ZPT-like template for sending mails"
security = ClassSecurityInfo()
meta_type = 'Mail Template'
_properties = ()
manage_options = ZopePageTemplate.manage_options[0:1] + \
ZopePageTemplate.manage_options[2:]
_default_content_fn = os.path.join(package_home(globals()),
'www', 'default.txt')
security.declareProtected('View management screens','pt_editForm')
pt_editForm = PageTemplateFile('www/mtEdit', globals(),
__name__='pt_editForm')
manage = manage_main = pt_editForm
security.declareProtected('Change Page Templates','pt_editAction')
def pt_editAction(self, REQUEST, mailhost, text, content_type, expand):
"""Change the mailhost and document."""
if SUPPORTS_WEBDAV_LOCKS and self.wl_isLocked():
raise ResourceLockedError, "File is locked via WebDAV"
self.expand=expand
self._setPropValue('mailhost',mailhost)
self.pt_edit(text, content_type)
REQUEST.set('text', self.read()) # May not equal 'text'!
message = "Saved changes."
if getattr(self, '_v_warnings', None):
message = ("<strong>Warning:</strong> <i>%s</i>"
% '<br>'.join(self._v_warnings))
return self.pt_editForm(manage_tabs_message=message)
def om_icons(self):
"""Return a list of icon URLs to be displayed by an ObjectManager"""
icons = ({'path': 'misc_/MailTemplates/mt.gif',
'alt': self.meta_type, 'title': self.meta_type},)
if not self._v_cooked:
self._cook()
if self._v_errors:
icons = icons + ({'path': 'misc_/PageTemplates/exclamation.gif',
'alt': 'Error',
'title': 'This template has an error'},)
return icons
def _exec(self, bound_names, args, kw):
"""Call a Page Template"""
if not kw.has_key('args'):
kw['args'] = args
bound_names['options'] = kw
security=getSecurityManager()
bound_names['user'] = security.getUser()
# Retrieve the value from the cache.
keyset = None
if self.ZCacheable_isCachingEnabled():
# Prepare a cache key.
keyset = {'here': self._getContext(),
'bound_names': bound_names}
result = self.ZCacheable_get(keywords=keyset)
if result is not None:
# Got a cached value.
return result
# Execute the template in a new security context.
security.addContext(self)
try:
result = self.pt_render(extra_context=bound_names)
if keyset is not None:
# Store the result in the cache.
self.ZCacheable_set(result, keywords=keyset)
return result
finally:
security.removeContext(self)
InitializeClass(MailTemplate)
# 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.
from AccessControl import allow_module,allow_class
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from MailTemplate import MailTemplate
from types import ClassType
from urllib import quote
try:
import Products.CMFCore
except ImportError:
pass
else:
import FSMailTemplate
import Products.CMFCore.utils
Products.CMFCore.utils.registerIcon(FSMailTemplate.FSMailTemplate,
'www/fsmt.gif', globals())
def initialize( context ):
context.registerClass(
MailTemplate,
# we use the same permission as page templates
# in order to keep things simple.
permission='Add Page Templates',
constructors=(addMailTemplateForm,
addMailTemplate),
icon='www/mt.gif',
)
addMailTemplateForm = PageTemplateFile(
'www/mtAdd',
globals(),
__name__='addMailTemplateForm'
)
def addMailTemplate(self, id, mailhost=None, text=None,
REQUEST=None, submit=None):
"Add a Mail Template with optional file content."
id = str(id)
if REQUEST is None:
self._setObject(id, MailTemplate(id, text))
ob = getattr(self, id)
if mailhost:
ob._setPropValue('mailhost',mailhost)
return ob
else:
file = REQUEST.form.get('file')
headers = getattr(file, 'headers', None)
if headers is None or not file.filename:
mt = MailTemplate(id, text)
else:
mt = MailTemplate(id, file, headers.get('content_type'))
self._setObject(id, mt)
ob = getattr(self, id)
if mailhost:
ob._setPropValue('mailhost',mailhost)
if submit == " Add and Edit ":
u = ob.absolute_url()
else:
u = ob.aq_parent.absolute_url()
REQUEST.RESPONSE.redirect(u+'/manage_main')
# allow all the email module's public bits
import email
for name in email.__all__:
path = 'email.'+name
allow_module(path)
try:
mod = __import__(path)
except ImportError:
pass
else:
mod = getattr(mod,name)
for mod_name in dir(mod):
obj = getattr(mod,mod_name)
if isinstance(obj,ClassType):
allow_class(obj)
Copyright (c) 2005-2006 Simplistix Ltd
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
This diff is collapsed.
# 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.
<tal:body xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
>Dear <tal:x replace="options/mto"/>,
<tal:x replace="user/getId"/> would like to thank you for
your interest in:
<tal:x replace="root/absolute_url"/>
<tal:x replace="options/message"/>
cheers,
The Web Team
</tal:body>
container.my_mt(
mfrom='webmaster@example.com',
mto='user@example.com',
subject='This is a test!',
message='This is a test!'
)
return 'Mail Sent!'
Content-Type: text/plain; charset="iso-8859-15"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Date:
From: webmaster@example.com
Subject: This is a test!
To: user@example.com
Dear user@example.com,
Test User would like to thank you for
your interest in:
http://foo
This is a test!
cheers,
The Web Team
Content-Type: text/plain; charset="iso-8859-15"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Date:
From: webmaster@example.com
Subject: Thankyou for your interest!
To: member@example.com
Dear Test Member,
Thankyou for you interest in our portal!
cheers,
The Web Team
This is the file attachment
\ No newline at end of file
<tal:body xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
>Dear <tal:x replace="options/mto"/>,
Please find attached the file you requested.
cheers,
The Web Team
</tal:body>
msg = container.my_mt.as_message(
mfrom='from@example.com',
mto='to1@example.com',
subject='Your requested file',
boundary='111' # for testing only, so we get a consistent boundary
)
msg.add_file(container['myfile.bin'])
msg.send()
return 'Mail Sent!'
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="111"; charset="iso-8859-15"
Content-Transfer-Encoding: quoted-printable
Date:
From: from@example.com
Subject: Your requested file
To: to1@example.com
--111
Content-Type: text/plain; charset="iso-8859-15"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Dear to1@example.com,
=
Please find attached the file you requested.
=
cheers,
The Web Team
--111
Content-Type: application/octet-stream
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="myfile.bin"
VGhpcyBpcyB0aGUgZmlsZSBhdHRhY2htZW50
--111--
\ No newline at end of file
<tal:body xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
>Dear <tal:x replace="options/mto"/>,
Welcome to our site!
cheers,
The Web Team
</tal:body>
container.my_mt(
mto='user@example.com',
subject=container.my_mt.subject % container.absolute_url()
)
return 'Mail Sent!'
Content-Type: text/plain; charset="iso-8859-15"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
Date:
From: webmaster@example.com
Subject: Welcome to http://foo
To: user@example.com
Dear user@example.com,
Welcome to our site!
cheers,
The Web Team
<tal:body xmlns:tal="http://xml.zope.org/namespaces/tal"
xmlns:metal="http://xml.zope.org/namespaces/metal"
>Dear <tal:x replace="options/member/getUserName"/>,
Thankyou for you interest in our portal!
cheers,
The Web Team
</tal:body>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment