Commit f72afbba authored by Arnaud Fontaine's avatar Arnaud Fontaine

WIP: NotificationTool

parent 55e1ba56
...@@ -30,7 +30,7 @@ from AccessControl import ClassSecurityInfo ...@@ -30,7 +30,7 @@ from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions, PropertySheet from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
from Products.ERP5.Document.EmailDocument import EmailDocumentProxyMixin from erp5.component.document.EmailDocument import EmailDocumentProxyMixin
from erp5.component.document.Event import Event from erp5.component.document.Event import Event
class Acknowledgement(EmailDocumentProxyMixin, Event): class Acknowledgement(EmailDocumentProxyMixin, Event):
......
...@@ -201,7 +201,7 @@ class File(Document, CMFFile): ...@@ -201,7 +201,7 @@ class File(Document, CMFFile):
security.declareProtected(Permissions.AccessContentsInformation, 'getMimeTypeAndContent') security.declareProtected(Permissions.AccessContentsInformation, 'getMimeTypeAndContent')
def getMimeTypeAndContent(self): def getMimeTypeAndContent(self):
"""This method returns a tuple which contains mimetype and content.""" """This method returns a tuple which contains mimetype and content."""
from Products.ERP5.Document.EmailDocument import MimeTypeException from erp5.component.document.EmailDocument import MimeTypeException
# return a tuple (mime_type, data) # return a tuple (mime_type, data)
content = None content = None
mime_type = self.getContentType() mime_type = self.getContentType()
......
from AccessControl.SecurityInfo import allow_module from AccessControl.SecurityInfo import allow_module
allow_module('Products.ERP5.Tool.NotificationTool') allow_module('erp5.component.tool.NotificationTool')
allow_module('erp5.component.tool.erp5_version.NotificationTool')
...@@ -43,7 +43,7 @@ product_path = package_home( globals() ) ...@@ -43,7 +43,7 @@ product_path = package_home( globals() )
# Define object classes and tools # Define object classes and tools
from Tool import CategoryTool,TemplateTool,\ from Tool import CategoryTool,TemplateTool,\
AlarmTool,\ AlarmTool,\
TrashTool, ContributionTool, NotificationTool,\ TrashTool, ContributionTool,\
SolverTool SolverTool
import ERP5Site import ERP5Site
from Document import PythonScript, SQLMethod from Document import PythonScript, SQLMethod
...@@ -56,7 +56,6 @@ portal_tools = ( CategoryTool.CategoryTool, ...@@ -56,7 +56,6 @@ portal_tools = ( CategoryTool.CategoryTool,
AlarmTool.AlarmTool, AlarmTool.AlarmTool,
TrashTool.TrashTool, TrashTool.TrashTool,
ContributionTool.ContributionTool, ContributionTool.ContributionTool,
NotificationTool.NotificationTool,
SolverTool.SolverTool, SolverTool.SolverTool,
) )
content_classes = () content_classes = ()
......
...@@ -27,25 +27,19 @@ ...@@ -27,25 +27,19 @@
# #
############################################################################## ##############################################################################
import re, types import re
from email.utils import formataddr
from DateTime import DateTime from DateTime import DateTime
from AccessControl import ClassSecurityInfo, Unauthorized from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
from Products.CMFCore.utils import _checkPermission
from Products.ERP5Type import Permissions, PropertySheet from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5.Document.TextDocument import TextDocument from Products.ERP5.Document.TextDocument import TextDocument
from Products.ERP5.Document.File import File from Products.ERP5.Document.File import File
from Products.ERP5.Document.Document import ConversionError
from Products.ERP5.mixin.mail_message import MailMessageMixin, testCharsetAndConvert from Products.ERP5.mixin.mail_message import MailMessageMixin, testCharsetAndConvert
from Products.ERP5.mixin.document_proxy import DocumentProxyMixin, DocumentProxyError from Products.ERP5.mixin.document_proxy import DocumentProxyMixin, DocumentProxyError
from Products.ERP5.Tool.NotificationTool import buildEmailMessage
from Products.ERP5Type.Utils import guessEncodingFromText
from MethodObject import Method from MethodObject import Method
from zLOG import LOG, INFO
try: try:
from Products.MimetypesRegistry.common import MimeTypeException from Products.MimetypesRegistry.common import MimeTypeException # pylint: disable=unused-import
except ImportError: except ImportError:
class MimeTypeException(Exception): class MimeTypeException(Exception):
""" """
...@@ -54,12 +48,11 @@ except ImportError: ...@@ -54,12 +48,11 @@ except ImportError:
""" """
from email import message_from_string from email import message_from_string
from email.header import decode_header, HeaderParseError
from email.utils import parsedate_tz, mktime_tz from email.utils import parsedate_tz, mktime_tz
DEFAULT_TEXT_FORMAT = 'text/html' DEFAULT_TEXT_FORMAT = 'text/html'
COMMASPACE = ', ' COMMASPACE = ', '
_MARKER = [] _MARKER = ()
filename_regexp = 'name="([^"]*)"' filename_regexp = 'name="([^"]*)"'
...@@ -255,11 +248,11 @@ class EmailDocument(TextDocument, MailMessageMixin): ...@@ -255,11 +248,11 @@ class EmailDocument(TextDocument, MailMessageMixin):
else: else:
part_encoding = part.get_content_charset() part_encoding = part.get_content_charset()
message_text = part.get_payload(decode=1) message_text = part.get_payload(decode=1)
text_result, encoding = testCharsetAndConvert(message_text, text_result, _ = testCharsetAndConvert(message_text,
part.get_content_type(), part.get_content_type(),
part_encoding) part_encoding)
if part.get_content_type() == 'text/html': if part.get_content_type() == 'text/html':
mime, text_result = self.convert(format='html', _, text_result = self.convert(format='html',
text_content=text_result, text_content=text_result,
charset=part_encoding) charset=part_encoding)
...@@ -287,7 +280,7 @@ class EmailDocument(TextDocument, MailMessageMixin): ...@@ -287,7 +280,7 @@ class EmailDocument(TextDocument, MailMessageMixin):
else: else:
return part.get_content_type() return part.get_content_type()
email_parser = re.compile('[ ;,<>\'"]*([^<> ;,\'"]+?\@[^<> ;,\'"]+)[ ;,<>\'"]*', re.IGNORECASE) email_parser = re.compile(r'[ ;,<>\'"]*([^<> ;,\'"]+?\@[^<> ;,\'"]+)[ ;,<>\'"]*', re.IGNORECASE)
security.declareProtected(Permissions.AccessContentsInformation, 'getContentURLList') security.declareProtected(Permissions.AccessContentsInformation, 'getContentURLList')
def getContentURLList(self): def getContentURLList(self):
""" """
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Document Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>EmailDocument</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Document.EmailDocument</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>document.erp5.EmailDocument</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Document Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -33,7 +33,7 @@ from Products.ERP5Type import Permissions, PropertySheet ...@@ -33,7 +33,7 @@ from Products.ERP5Type import Permissions, PropertySheet
from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter from Products.ERP5Type.Accessor.Constant import PropertyGetter as ConstantGetter
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
from erp5.component.document.Movement import Movement from erp5.component.document.Movement import Movement
from Products.ERP5.Document.EmailDocument import EmailDocument from erp5.component.document.EmailDocument import EmailDocument
class AcknowledgeableMixin: class AcknowledgeableMixin:
""" """
......
...@@ -27,13 +27,10 @@ ...@@ -27,13 +27,10 @@
# #
############################################################################## ##############################################################################
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
from Products.ERP5Type.Globals import DTMLFile
from Products.CMFCore.utils import getToolByName
from Products.ERP5Type.Tool.BaseTool import BaseTool from Products.ERP5Type.Tool.BaseTool import BaseTool
from Products.ERP5Type import Permissions from Products.ERP5Type import Permissions
from AccessControl import ModuleSecurityInfo from AccessControl import ModuleSecurityInfo
from zExceptions import Unauthorized from zExceptions import Unauthorized
from Products.ERP5 import _dtmldir
from mimetypes import guess_type from mimetypes import guess_type
from email.mime.multipart import MIMEMultipart from email.mime.multipart import MIMEMultipart
...@@ -48,7 +45,7 @@ from email import encoders ...@@ -48,7 +45,7 @@ from email import encoders
class ConversionError(Exception): pass class ConversionError(Exception): pass
class MimeTypeException(Exception): pass class MimeTypeException(Exception): pass
security = ModuleSecurityInfo('Products.ERP5.Tool.NotificationTool') security = ModuleSecurityInfo(__name__)
security.declarePublic('buildEmailMessage',) security.declarePublic('buildEmailMessage',)
def buildAttachmentDictList(document_list, document_type_list=()): def buildAttachmentDictList(document_list, document_type_list=()):
...@@ -57,7 +54,6 @@ def buildAttachmentDictList(document_list, document_type_list=()): ...@@ -57,7 +54,6 @@ def buildAttachmentDictList(document_list, document_type_list=()):
for attachment in document_list: for attachment in document_list:
mime_type = None mime_type = None
content = None content = None
name = None
if not attachment.getPortalType() in document_type_list: if not attachment.getPortalType() in document_type_list:
mime_type = 'application/pdf' mime_type = 'application/pdf'
content = attachment.asPDF() # XXX - Not implemented yet content = attachment.asPDF() # XXX - Not implemented yet
...@@ -149,7 +145,7 @@ def buildEmailMessage(from_url, to_url, msg=None, ...@@ -149,7 +145,7 @@ def buildEmailMessage(from_url, to_url, msg=None,
# try to guess the mime type # try to guess the mime type
if not attachment.has_key('mime_type'): if not attachment.has_key('mime_type'):
mime_type, encoding = guess_type( attachment_name ) mime_type, _ = guess_type( attachment_name )
if mime_type is not None: if mime_type is not None:
attachment['mime_type'] = mime_type attachment['mime_type'] = mime_type
else: else:
...@@ -208,9 +204,6 @@ class NotificationTool(BaseTool): ...@@ -208,9 +204,6 @@ class NotificationTool(BaseTool):
# Declarative Security # Declarative Security
security = ClassSecurityInfo() security = ClassSecurityInfo()
security.declareProtected( Permissions.ManagePortal, 'manage_overview' )
manage_overview = DTMLFile( 'explainNotificationTool', _dtmldir )
# low-level interface # low-level interface
def _sendEmailMessage(self, from_url, to_url, body=None, subject=None, def _sendEmailMessage(self, from_url, to_url, body=None, subject=None,
attachment_list=None, extra_headers=None, attachment_list=None, extra_headers=None,
...@@ -396,48 +389,48 @@ class NotificationTool(BaseTool): ...@@ -396,48 +389,48 @@ class NotificationTool(BaseTool):
# NOTE: this implementation should also make sure that the current # NOTE: this implementation should also make sure that the current
# buildEmailMessage method defined here and the Event.send method # buildEmailMessage method defined here and the Event.send method
# are merged once for all # are merged once for all
#
if self.getNotifierList(): #if self.getNotifierList():
# CRM is installed - so we can lookup notification preferences # # CRM is installed - so we can lookup notification preferences
if notifier_list is None: # if notifier_list is None:
# Find which notifier to use on preferences # # Find which notifier to use on preferences
if priority_level not in ('low', 'medium', 'high'): # XXX Better naming required here # if priority_level not in ('low', 'medium', 'high'): # XXX Better naming required here
priority_level = 'high' # priority_level = 'high'
notifier_list = self.preferences.getPreference( # notifier_list = self.preferences.getPreference(
'preferred_%s_priority_nofitier_list' % priority_level) # 'preferred_%s_priority_nofitier_list' % priority_level)
event_list = [] # event_list = []
for notifier in notifier_list: # for notifier in notifier_list:
event_module = self.getDefaultModule(notifier) # event_module = self.getDefaultModule(notifier)
new_event = event_module.newContent(portal_type=notifier, temp_object=store_as_event) # new_event = event_module.newContent(portal_type=notifier, temp_object=store_as_event)
event_list.append(new_event) # event_list.append(new_event)
else: #else:
# CRM is not installed - only notification by email is possible # # CRM is not installed - only notification by email is possible
# So create a temp object directly # # So create a temp object directly
new_event = self.newContent(temp_object=True, portal_type='Event', id='_') # new_event = self.newContent(temp_object=True, portal_type='Event', id='_')
event_list = [new_event] # event_list = [new_event]
#
if event in event_list: #if event in event_list:
# We try to build events using the same parameters as the one # # We try to build events using the same parameters as the one
# we were provided for notification. # # we were provided for notification.
# The handling of attachment is still an open question: # # The handling of attachment is still an open question:
# either use relation (to prevent duplication) or keep # # either use relation (to prevent duplication) or keep
# a copy inside. It is probably a good idea to # # a copy inside. It is probably a good idea to
# make attachment_list polymorphic in order to be able # # make attachment_list polymorphic in order to be able
# to provide different kinds of attachments can be provided # # to provide different kinds of attachments can be provided
# Either document references or binary data. # # Either document references or binary data.
event.build(sender=sender, recipient=recipient, subject=subject, # event.build(sender=sender, recipient=recipient, subject=subject,
message=message, attachment_list=attachment_list,) # Rename here and add whatever # message=message, attachment_list=attachment_list,) # Rename here and add whatever
# parameter makes sense such # # parameter makes sense such
# as text format # # as text format
event.send() # Make sure workflow transition is invoked if this is # event.send() # Make sure workflow transition is invoked if this is
# a persistent notification # # a persistent notification
#
# Aggregation could be handled by appending the notification # # Aggregation could be handled by appending the notification
# to an existing message rather than creating a new one. # # to an existing message rather than creating a new one.
# Sending the message should be handled by the alarm based # # Sending the message should be handled by the alarm based
# on a date value stored on the event. This probably required # # on a date value stored on the event. This probably required
# a new workflow state to represent events which are waiting # # a new workflow state to represent events which are waiting
# for being sent automatically. (ie. scheduled sending) # # for being sent automatically. (ie. scheduled sending)
security.declareProtected(Permissions.AccessContentsInformation, 'getNotifierList') security.declareProtected(Permissions.AccessContentsInformation, 'getNotifierList')
def getNotifierList(self): def getNotifierList(self):
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Tool Component" module="erp5.portal_type"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>default_reference</string> </key>
<value> <string>NotificationTool</string> </value>
</item>
<item>
<key> <string>default_source_reference</string> </key>
<value> <string>Products.ERP5.Tool.NotificationTool</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>tool.erp5.NotificationTool</string> </value>
</item>
<item>
<key> <string>portal_type</string> </key>
<value> <string>Tool Component</string> </value>
</item>
<item>
<key> <string>sid</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>text_content_error_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>text_content_warning_message</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>version</string> </key>
<value> <string>erp5</string> </value>
</item>
<item>
<key> <string>workflow_history</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="2" aka="AAAAAAAAAAI=">
<pickle>
<global name="PersistentMapping" module="Persistence.mapping"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>data</string> </key>
<value>
<dictionary>
<item>
<key> <string>component_validation_workflow</string> </key>
<value>
<persistent> <string encoding="base64">AAAAAAAAAAM=</string> </persistent>
</value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</pickle>
</record>
<record id="3" aka="AAAAAAAAAAM=">
<pickle>
<global name="WorkflowHistoryList" module="Products.ERP5Type.Workflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_log</string> </key>
<value>
<list>
<dictionary>
<item>
<key> <string>action</string> </key>
<value> <string>validate</string> </value>
</item>
<item>
<key> <string>validation_state</string> </key>
<value> <string>validated</string> </value>
</item>
</dictionary>
</list>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -3,6 +3,7 @@ document.erp5.AppliedRule ...@@ -3,6 +3,7 @@ document.erp5.AppliedRule
document.erp5.Delivery document.erp5.Delivery
document.erp5.DeliveryCell document.erp5.DeliveryCell
document.erp5.DeliveryLine document.erp5.DeliveryLine
document.erp5.EmailDocument
document.erp5.Event document.erp5.Event
document.erp5.ImmobilisableItem document.erp5.ImmobilisableItem
document.erp5.ImmobilisationDelivery document.erp5.ImmobilisationDelivery
......
...@@ -7,6 +7,7 @@ tool.erp5.DiffTool ...@@ -7,6 +7,7 @@ tool.erp5.DiffTool
tool.erp5.DomainTool tool.erp5.DomainTool
tool.erp5.IdTool tool.erp5.IdTool
tool.erp5.IntrospectionTool tool.erp5.IntrospectionTool
tool.erp5.NotificationTool
tool.erp5.OrderTool tool.erp5.OrderTool
tool.erp5.PasswordTool tool.erp5.PasswordTool
tool.erp5.RuleTool tool.erp5.RuleTool
......
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<p>Help on Notification Tool</p>
This tool is useful to manage notifications.
It is used as a central point to send messages from one
user to one or many users. The purpose of the tool
is to provide an API for sending messages which is
independent on how messages are actually going to be
sent and when.
This early implementation only provides asynchronous
email sending.
<dtml-var manage_page_footer>
...@@ -54,7 +54,7 @@ implements_tuple_list = [ ...@@ -54,7 +54,7 @@ implements_tuple_list = [
(('Products.ERP5.Document.File', 'File'), 'IDocument'), (('Products.ERP5.Document.File', 'File'), 'IDocument'),
(('erp5.component.document.OOoDocument', 'OOoDocument'), 'IDocument'), (('erp5.component.document.OOoDocument', 'OOoDocument'), 'IDocument'),
(('Products.ERP5.Document.TextDocument', 'TextDocument'), 'IDocument'), (('Products.ERP5.Document.TextDocument', 'TextDocument'), 'IDocument'),
(('Products.ERP5.Document.EmailDocument', 'EmailDocument'), 'IDocument'), (('erp5.component.document.EmailDocument', 'EmailDocument'), 'IDocument'),
(('erp5.component.document.Event', 'Event'), 'IDocument'), (('erp5.component.document.Event', 'Event'), 'IDocument'),
# IAmountList # IAmountList
(('Products.ERP5.GeneratedAmountList', 'GeneratedAmountList'), 'IAmountList'), (('Products.ERP5.GeneratedAmountList', 'GeneratedAmountList'), 'IAmountList'),
......
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