--- /dev/null
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Business Applications
+# Copyright (C) 2012 OpenERP S.A. (<http://openerp.com>).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import fields, osv
+
+class mail_alias(osv.Model):
+ """A Mail Alias is a mapping of an email address with a given OpenERP Document
+ model. It is used by OpenERP's mail gateway when processing incoming emails
+ sent to the system. If the recipient address (To) of the message matches
+ a Mail Alias, the message will be either processed following the rules
+ of that alias. If the message is a reply it will be attached to the
+ existing discussion on the corresponding record, otherwise a new
+ record of the corresponding model will be created.
+
+ This is meant to be used in combination with a catch-all email configuration
+ on the company's mail server, so that as soon as a new mail.alias is
+ created, it becomes immediately usable and OpenERP will accept email for it.
+ """
+ _name = 'mail.alias'
+ _description = "Mail Alias"
+ _rec_name = 'alias_name'
+
+ _columns = {
+ 'alias_name': fields.char('Mailbox Alias', required=True,
+ help="The name of the mailbox alias, e.g. `jobs' "
+ "if you want to catch emails for <jobs@example.my.openerp.com>",),
+ 'alias_model_id': fields.many2one('ir.model', 'Aliased Model', required=True,
+ help="The model (OpenERP Document Kind) to which this alias "
+ "corresponds. Any incoming email that does not reply to an "
+ "existing record will cause the creation of a new record "
+ "of this model (e.g. a Project Task)",
+ # only allow selecting mail_thread models!
+ domain="[('field_ids', 'in', 'message_ids')]"),
+ 'alias_user_id': fields.many2one('res.users', 'Owner',
+ help="The owner of records created upon receiving emails on this alias. "
+ "If this field is kept empty the system will attempt to find the right owner "
+ "based on the sender (From) address, or will use the Administrator account "
+ "if no system user is found for that address."),
+ 'alias_defaults': fields.text('Default Values', required=True,
+ help="The representation of a Python dictionary that will be evaluated to provide "
+ "default values when creating new records."),
+ 'alias_force_thread_id': fields.integer('Record Thread ID',
+ help="Optional ID of the thread (record) to which all "
+ "messages will be attached, even if they did not reply to it. "
+ "If set, this will disable the creation of new records completely.")
+ }
+ _defaults = {
+ 'alias_defaults': '{}',
+ 'alias_user_id': lambda s,c,u,ctx: u
+ }
+ _sql_constraint = [
+ ('mailbox_uniq', 'unique (alias_name)', 'Unfortunately this mail alias is already used, please choose a unique one')
+ ]
+
+ def create_unique_alias(self, cr, uid, values, context=None):
+ # TODO: call create() with `values` after checking that `alias_name`
+ # is unique. If not unique, append a sequential number after it until
+ # a unique one if found.
+ # E.g if create_unique_alias is called with {'alias_name': 'abc'}
+ # and 'abc', 'abc1', 'abc2' alias exist, replace alias_name with 'abc3'.
+ return
+
+
+
\ No newline at end of file
_columns = {
'message_ids': fields.function(_get_message_ids, method=True,
type='one2many', obj='mail.message', string='Temp messages', _fields_id = 'res_id'),
+ 'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
}
#------------------------------------------------------
msgs = msg_obj.read(cr, uid, msg_ids, context=context)
return msgs
+
+ def message_catchall(self, cr, uid, message, context=None):
+ """TODO proper docstring, inspired by messsage_process()"""
+ # TODO
+ pass
+
+
#------------------------------------------------------
# Email specific
#------------------------------------------------------
def message_process(self, cr, uid, model, message, custom_values=None,
save_original=False, strip_attachments=False,
- context=None):
+ thread_id=None, context=None):
"""Process an incoming RFC2822 email message related to the
given thread model, relying on ``mail.message.parse()``
for the parsing operation, and then calling ``message_new``
email source attached to the message after it is imported.
:param bool strip_attachments: whether to strip all attachments
before processing the message, in order to save some space.
+ :param int thread_id: optional ID of the record/thread from ``model``
+ to which this mail should be attached. When provided, this
+ overrides the automatic detection based on the message
+ headers.
"""
# extract message bytes - we are forced to pass the message as binary because
# we don't know its encoding until we parse its headers and hence can't
if isinstance(message, xmlrpclib.Binary):
message = str(message.data)
+ if context is None: context = {}
+
+ mail_message = self.pool.get('mail.message')
model_pool = self.pool.get(model)
if self._name != model:
- if context is None: context = {}
context.update({'thread_model': model})
- mail_message = self.pool.get('mail.message')
- res_id = False
-
# Parse Message
# Warning: message_from_string doesn't always work correctly on unicode,
# we must use utf-8 strings here :-(
return model_pool.message_new(cr, uid, msg,
custom_values,
context=context)
- res_id = False
- if msg.get('references') or msg.get('in-reply-to'):
+ if not thread_id and (msg.get('references') or msg.get('in-reply-to')):
references = msg.get('references') or msg.get('in-reply-to')
if '\r\n' in references:
references = references.split('\r\n')
references = references.split(' ')
for ref in references:
ref = ref.strip()
- res_id = tools.reference_re.search(ref)
- if res_id:
- res_id = res_id.group(1)
- else:
- res_id = tools.res_re.search(msg['subject'])
- if res_id:
- res_id = res_id.group(1)
- if res_id:
- res_id = int(res_id)
- if model_pool.exists(cr, uid, res_id):
- if hasattr(model_pool, 'message_update'):
- model_pool.message_update(cr, uid, [res_id], msg, {}, context=context)
- else:
- # referenced thread was not found, we'll have to create a new one
- res_id = False
- if not res_id:
- res_id = create_record(msg)
+ thread_id = tools.reference_re.search(ref)
+ if not thread_id:
+ thread_id = tools.res_re.search(msg['subject'])
+ if thread_id:
+ thread_id = int(thread_id.group(1))
+ if not model_pool.exists(cr, uid, thread_id) or \
+ not hasattr(model_pool, 'message_update'):
+ # referenced thread not found or not updatable,
+ # -> create a new one
+ thread_id = False
+ if not thread_id:
+ thread_id = create_record(msg)
+ else:
+ model_pool.message_update(cr, uid, [thread_id], msg, {}, context=context)
#To forward the email to other followers
- self.message_forward(cr, uid, model, [res_id], msg_txt, context=context)
- return res_id
+ self.message_forward(cr, uid, model, [thread_id], msg_txt, context=context)
+ return thread_id
def message_new(self, cr, uid, msg_dict, custom_values=None, context=None):
"""Called by ``message_process`` when a new message is received