Commit 1f49870c authored by Yusei Tahara's avatar Yusei Tahara

refactoring. now portal_notifications is a central point to send messages.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@20865 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent bb96987c
......@@ -48,18 +48,11 @@ except ImportError:
A dummy exception class which is used when MimetypesRegistry product is
not installed yet.
"""
from email import message_from_string
from email.Header import decode_header
from email.Utils import parsedate
from email import Encoders
from email.Message import Message
from email.MIMEAudio import MIMEAudio
from email.MIMEBase import MIMEBase
from email.MIMEImage import MIMEImage
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
DEFAULT_TEXT_FORMAT = 'text/html'
COMMASPACE = ', '
......@@ -400,9 +393,6 @@ class EmailDocument(File, TextDocument):
download - if set to True returns, the message online
rather than sending it.
This method is based on the examples provided by
http://docs.python.org/lib/node162.html
TODO: support conversion to base format and use
base format rather than original format
......@@ -412,11 +402,17 @@ class EmailDocument(File, TextDocument):
if not _checkPermission(Permissions.View, self):
raise Unauthorized
#
# Prepare header data
#
if body is None:
body = self.asText()
# Subject
if subject is None:
subject = self.getTitle()
# From
if from_url is None:
sender = self.getSourceValue()
if sender.getTitle():
......@@ -424,101 +420,100 @@ class EmailDocument(File, TextDocument):
sender.getDefaultEmailText())
else:
from_url = sender.getDefaultEmailText()
# Return-Path
if reply_url is None:
reply_url = self.portal_preferences.getPreferredEventSenderEmail()
additional_headers = None
if reply_url:
additional_headers = {'Return-Path':reply_url}
# To (multiple)
to_url_list = []
if to_url is None:
to_url = []
for recipient in self.getDestinationValueList():
email = recipient.getDefaultEmailText()
if email:
if recipient.getTitle():
to_url.append('"%s" <%s>' % (recipient.getTitle(), email))
to_url_list.append('"%s" <%s>' % (recipient.getTitle(), email))
else:
to_url.append(email)
to_url_list.append(email)
else:
raise ValueError, 'Recipient %s has no defined email' % recipient
elif type(to_url) in types.StringTypes:
to_url = [to_url]
# Not efficient but clean
for recipient in to_url:
# Create the container (outer) email message.
message = MIMEMultipart()
message['Subject'] = subject
message['From'] = from_url
message['To'] = recipient
message['Return-Path'] = reply_url
message.preamble = 'You will not see this in a MIME-aware mail reader.\n'
# Add the body of the message
attached_message = MIMEText(str(body), _charset='UTF-8')
message.attach(attached_message)
# Attach files
document_type_list = self.getPortalDocumentTypeList()
for attachment in self.getAggregateValueList():
mime_type = None
attached_data = None
if attachment.getPortalType() in document_type_list:
# If this is a document, use
# WARNING - this could fail since getContentType
# is not (yet) part of Document API
if getattr(attachment, 'getContentType', None) is not None:
mime_type = attachment.getContentType()
elif getattr(attachment, 'getTextFormat', None) is not None:
mime_type = attachment.getTextFormat()
else:
raise ValueError, "Cannot find mimetype of the document."
if mime_type is not None:
try:
mime_type, attached_data = attachment.convert(mime_type)
except ConversionError:
mime_type = attachment.getBaseContentType()
attached_data = attachment.getBaseData()
except (NotImplementedError, MimeTypeException):
pass
if attached_data is None:
if getattr(attachment, 'getTextContent', None) is not None:
attached_data = attachment.getTextContent()
elif getattr(attachment, 'getData', None) is not None:
attached_data = attachment.getData()
elif getattr(attachment, 'getBaseData', None) is not None:
attached_data = attachment.getBaseData()
else:
mime_type = 'application/pdf'
attached_data = attachment.asPDF() # XXX - Not implemented yet
# should provide a default printout
if not isinstance(attached_data, str):
attached_data = str(attached_data)
if not mime_type:
mime_type = 'application/octet-stream'
# Use appropriate class based on mime_type
maintype, subtype = mime_type.split('/', 1)
if maintype == 'text':
attached_message = MIMEText(attached_data, _subtype=subtype)
elif maintype == 'image':
attached_message = MIMEImage(attached_data, _subtype=subtype)
elif maintype == 'audio':
attached_message = MIMEAudio(attached_data, _subtype=subtype)
to_url_list.append(to_url)
# Attachments
attachment_list = []
document_type_list = self.getPortalDocumentTypeList()
for attachment in self.getAggregateValueList():
mime_type = None
content = None
name = None
if not attachment.getPortalType() in document_type_list:
mime_type = 'application/pdf'
content = attachment.asPDF() # XXX - Not implemented yet
else:
#
# Document type attachment
#
# WARNING - this could fail since getContentType
# is not (yet) part of Document API
if getattr(attachment, 'getContentType', None) is not None:
mime_type = attachment.getContentType()
elif getattr(attachment, 'getTextFormat', None) is not None:
mime_type = attachment.getTextFormat()
else:
attached_message = MIMEBase(maintype, subtype)
attached_message.set_payload(attached_data)
Encoders.encode_base64(attached_message)
attached_message.add_header('Content-Disposition', 'attachment', filename=attachment.getReference())
message.attach(attached_message)
raise ValueError, "Cannot find mimetype of the document."
if mime_type is not None:
try:
mime_type, content = attachment.convert(mime_type)
except ConversionError:
mime_type = attachment.getBaseContentType()
content = attachment.getBaseData()
except (NotImplementedError, MimeTypeException):
pass
if content is None:
if getattr(attachment, 'getTextContent', None) is not None:
content = attachment.getTextContent()
elif getattr(attachment, 'getData', None) is not None:
content = attachment.getData()
elif getattr(attachment, 'getBaseData', None) is not None:
content = attachment.getBaseData()
if not isinstance(content, str):
content = str(content)
attachment_list.append({'mime_type':mime_type,
'content':content,
'name':attachment.getReference()}
)
portal_notifications = getToolByName(self, 'portal_notifications')
kw = {}
# Only for debugging purpose
if download:
kw = {'debug':True}
else:
portal_notifications = portal_notifications.activate(activity="SQLQueue")
# Send the message
if download:
return message.as_string() # Only for debugging purpose
for to_url in to_url_list:
result = portal_notifications.sendMessageLowLevel(
from_url=from_url, to_url=to_url, body=body, subject=subject,
attachment_list=attachment_list,
additional_headers=additional_headers,
**kw
)
# Use activities
self.activate(activity="SQLQueue").sendMailHostMessage(message.as_string())
# Send the message
if download:
return result # Only for debugging purpose
# XXX Obsolete method, Use portal_notifications instead.
security.declareProtected(Permissions.UseMailhostServices, 'sendMailHostMessage')
def sendMailHostMessage(self, message):
"""
......
......@@ -146,10 +146,6 @@ class Url(Coordinate, Base, UrlMixIn):
* extra_headers is a dictionnary of custom headers to add to the email.
"X-" prefix is automatically added to those headers.
"""
# get the mailhost object
mailhost = getattr(self.getPortalObject(), 'MailHost', None)
if mailhost is None:
raise AttributeError, "Cannot find a MailHost object"
if from_url is None:
from_url = self.getUrlString(None)
if to_url is None:
......@@ -157,9 +153,9 @@ class Url(Coordinate, Base, UrlMixIn):
if from_url is None or to_url is None:
raise AttributeError, "No mail defined"
message = buildEmailMessage(from_url, to_url, msg=msg,
subject=subject, attachment_list=attachment_list,
extra_headers=extra_headers)
portal_notifications = getToolByName(self, 'portal_notifications')
# send mail to user
mailhost.send(message.as_string(), to_url, from_url)
\ No newline at end of file
portal_notifications.sendMessageLowLevel(from_url=from_url, to_url=to_url,
body=msg, subject=subject,
attachment_list=attachment_list,
extra_headers=extra_headers)
......@@ -36,13 +36,16 @@ from mimetypes import guess_type
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEBase import MIMEBase
from email.MIMEAudio import MIMEAudio
from email.MIMEImage import MIMEImage
from email.Header import make_header
from email import Encoders
def buildEmailMessage(from_url, to_url, msg=None,
subject=None, attachment_list=None,
extra_headers=None):
extra_headers=None,
additional_headers=None):
"""
Builds a mail message which is ready to be
sent by Zope MailHost.
......@@ -53,6 +56,7 @@ def buildEmailMessage(from_url, to_url, msg=None,
- mime_type: mime-type corresponding to the attachment
* extra_headers is a dictionnary of custom headers to add to the email.
"X-" prefix is automatically added to those headers.
* additional_headers is similar to extra_headers, but no prefix is added.
"""
if attachment_list == None:
......@@ -67,8 +71,12 @@ def buildEmailMessage(from_url, to_url, msg=None,
message.attach(MIMEText(msg, _charset='utf-8'))
if extra_headers:
for k, v in extra_headers.items():
message.add_header('X-%s' % k, v)
for key, value in extra_headers.items():
message.add_header('X-%s' % key, value)
if additional_headers:
for key, value in additional_headers.items():
message.add_header(key, value)
message.add_header('Subject',
make_header([(subject, 'utf-8')]).encode())
......@@ -92,10 +100,18 @@ def buildEmailMessage(from_url, to_url, msg=None,
if attachment['mime_type'] == 'text/plain':
part = MIMEText(attachment['content'], _charset='utf-8')
else:
# encode non-plaintext attachment in base64
part = MIMEBase(*attachment['mime_type'].split('/', 1))
part.set_payload(attachment['content'])
Encoders.encode_base64(part)
major, minor = attachment['mime_type'].split('/', 1)
if major == 'text':
part = MIMEText(attachment['content'], _subtype=minor)
elif major == 'image':
part = MIMEImage(attachment['content'], _subtype=minor)
elif major == 'audio':
part = MIMEAudio(attachment['content'], _subtype=minor)
else:
# encode non-plaintext attachment in base64
part = MIMEBase(major, minor)
part.set_payload(attachment['content'])
Encoders.encode_base64(part)
part.add_header('Content-Disposition',
'attachment; filename=%s' % attachment_name)
......@@ -133,6 +149,24 @@ class NotificationTool(BaseTool):
security.declareProtected( Permissions.ManagePortal, 'manage_overview' )
manage_overview = DTMLFile( 'explainNotificationTool', _dtmldir )
# XXX Bad Name...Any Idea?
security.declareProtected(Permissions.UseMailhostServices, 'sendMessageLowLevel')
def sendMessageLowLevel(self, from_url, to_url, body=None, subject=None,
attachment_list=None, extra_headers=None, additional_headers=None,
debug=False):
portal = self.getPortalObject()
mailhost = getattr(portal, 'MailHost', None)
if mailhost is None:
raise ValueError, "Can't find MailHost."
message = buildEmailMessage(from_url, to_url, msg=body, subject=subject,
attachment_list=attachment_list, extra_headers=extra_headers,
additional_headers=additional_headers)
if debug:
return message.as_string()
mailhost.send(messageText=message.as_string(), mto=to_url, mfrom=from_url)
security.declareProtected(Permissions.UseMailhostServices, 'sendMessage')
def sendMessage(self, sender=None, recipient=None, subject=None,
message=None, attachment_list=None,
......@@ -171,9 +205,6 @@ class NotificationTool(BaseTool):
"""
portal = self.getPortalObject()
catalog_tool = getToolByName(self, 'portal_catalog')
mailhost = getattr(portal, 'MailHost', None)
if mailhost is None:
raise ValueError, "Can't find MailHost."
# Find Default Values
default_from_email = portal.email_from_address
......@@ -214,14 +245,12 @@ class NotificationTool(BaseTool):
# Build and Send Messages
for to_address in to_address_list:
mail_message = buildEmailMessage(from_url=from_address,
to_url=to_address,
msg=message,
subject=subject,
attachment_list=attachment_list
)
mailhost.send(mail_message.as_string(), to_address, from_address)
self.sendMessageLowLevel(from_url=from_address,
to_url=to_address,
body=message,
subject=subject,
attachment_list=attachment_list
)
return
# Future implemetation could consist in implementing
......
......@@ -28,6 +28,7 @@
import unittest
import os
import email
import email.Header
from Products.ERP5Type.tests.utils import DummyMailHost
from Products.ERP5Type.tests.ERP5TypeTestCase import ERP5TypeTestCase
......@@ -515,7 +516,8 @@ class TestCRMMailSend(ERP5TypeTestCase):
message = email.message_from_string(messageText)
self.assertEquals('A Mail', message['Subject'])
self.assertEquals('A Mail',
email.Header.decode_header(message['Subject'])[0][0])
part = None
for i in message.get_payload():
if i.get_content_type()=='text/plain':
......@@ -598,7 +600,8 @@ class TestCRMMailSend(ERP5TypeTestCase):
message = email.message_from_string(messageText)
self.assertEquals('Héhé', message['Subject'])
self.assertEquals('Héhé',
email.Header.decode_header(message['Subject'])[0][0])
part = None
for i in message.get_payload():
if i.get_content_type()=='text/plain':
......
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