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