[REF] mail_thread: refactored message_route improvement, to be closer to the original code + call to a new method that checks the route, instead of doing everything in one method.
bzr revid: tde@openerp.com-20130624151827-hxwaelefs540pvca
This commit is contained in:
parent
f9bc6be1ac
commit
fd90140d7b
|
@ -25,7 +25,6 @@ import dateutil
|
|||
import email
|
||||
import logging
|
||||
import pytz
|
||||
import re
|
||||
import time
|
||||
import xmlrpclib
|
||||
from email.message import Message
|
||||
|
@ -258,7 +257,6 @@ class mail_thread(osv.AbstractModel):
|
|||
|
||||
def _search_is_follower(self, cr, uid, obj, name, args, context):
|
||||
"""Search function for message_is_follower"""
|
||||
fol_obj = self.pool.get('mail.followers')
|
||||
res = []
|
||||
for field, operator, value in args:
|
||||
assert field == name
|
||||
|
@ -462,9 +460,6 @@ class mail_thread(osv.AbstractModel):
|
|||
return [('message_unread', '=', True)]
|
||||
return []
|
||||
|
||||
# def _get_model(self, cr, uid, context=None):
|
||||
# return if self._name == 'mail.thread'
|
||||
|
||||
def _garbage_collect_attachments(self, cr, uid, context=None):
|
||||
""" Garbage collect lost mail attachments. Those are attachments
|
||||
- linked to res_model 'mail.compose.message', the composer wizard
|
||||
|
@ -597,6 +592,118 @@ class mail_thread(osv.AbstractModel):
|
|||
s = ', '.join([decode(message.get(h)) for h in header_fields if message.get(h)])
|
||||
return filter(lambda x: x, self._find_partner_from_emails(cr, uid, None, tools.email_split(s), context=context))
|
||||
|
||||
def message_route_verify(self, cr, uid, message, message_dict, route, update_author=True, assert_model=True, create_fallback=True, context=None):
|
||||
""" Verify route validity. Check and rules:
|
||||
1 - if thread_id -> check that document effectively exists; otherwise
|
||||
fallback on a message_new by resetting thread_id
|
||||
2 - check that message_update exists if thread_id is set; or at least
|
||||
that message_new exist
|
||||
[ - find author_id if udpate_author is set]
|
||||
3 - if there is an alias, check alias_contact:
|
||||
'followers' and thread_id:
|
||||
check on target document that the author is in the followers
|
||||
'followers' and alias_parent_thread_id:
|
||||
check on alias parent document that the author is in the
|
||||
followers
|
||||
'partners': check that author_id id set
|
||||
"""
|
||||
|
||||
assert isinstance(route, (list, tuple)), 'A route should be a list or a tuple'
|
||||
assert len(route) == 5, 'A route should contain 5 elements: model, thread_id, custom_values, uid, alias record'
|
||||
|
||||
message_id = message.get('Message-Id')
|
||||
email_from = decode_header(message, 'From')
|
||||
author_id = message_dict.get('author_id')
|
||||
model, thread_id, alias = route[0], route[1], route[4]
|
||||
model_pool = None
|
||||
|
||||
def _create_bounce_email():
|
||||
mail_mail = self.pool.get('mail.mail')
|
||||
mail_id = mail_mail.create(cr, uid, {
|
||||
'body_html': '<div><p>Hello,</p>'
|
||||
'<p>The following email sent to %s cannot be accepted because this is '
|
||||
'a private email address. Only allowed people can contact us at this address.</p></div>'
|
||||
'<blockquote>%s</blockquote>' % (message.get('to'), message_dict.get('body')),
|
||||
'subject': 'Re: %s' % message.get('subject'),
|
||||
'email_to': message.get('from'),
|
||||
'auto_delete': True,
|
||||
}, context=context)
|
||||
mail_mail.send(cr, uid, [mail_id], context=context)
|
||||
|
||||
def _warn(message):
|
||||
_logger.warning('Routing mail with Message-Id %s: route %s: %s',
|
||||
message_id, route, message)
|
||||
|
||||
# Wrong model
|
||||
if model and not model in self.pool:
|
||||
if assert_model:
|
||||
assert model in self.pool, 'Routing: unknown target model %s' % model
|
||||
_warn('unknown target model %s' % model)
|
||||
return ()
|
||||
elif model:
|
||||
model_pool = self.pool[model]
|
||||
|
||||
# Private message: should not contain any thread_id
|
||||
if not model and thread_id:
|
||||
if assert_model:
|
||||
assert thread_id == 0, 'Routing: posting a message without model should be with a null res_id (private message).'
|
||||
_warn('posting a message without model should be with a null res_id (private message), resetting thread_id')
|
||||
thread_id = 0
|
||||
|
||||
# Existing Document: check if exists; if not, fallback on create if allowed
|
||||
if thread_id and not model_pool.exists(cr, uid, thread_id):
|
||||
if create_fallback:
|
||||
_warn('reply to missing document (%s,%s), fall back on new document creation' % (model, thread_id))
|
||||
thread_id = None
|
||||
elif assert_model:
|
||||
assert model_pool.exists(cr, uid, thread_id), 'Routing: reply to missing document (%s,%s)' % (model, thread_id)
|
||||
else:
|
||||
_warn('reply to missing document (%s,%s), skipping' % (model, thread_id))
|
||||
return ()
|
||||
|
||||
# Existing Document: check model accepts the mailgateway
|
||||
if thread_id and not hasattr(model_pool, 'message_update'):
|
||||
if create_fallback:
|
||||
_warn('model %s does not accept document update, fall back on document creation' % model)
|
||||
thread_id = None
|
||||
elif assert_model:
|
||||
assert hasattr(model_pool, 'message_update'), 'Routing: model %s does not accept document update, crashing' % model
|
||||
else:
|
||||
_warn('model %s does not accept document update, skipping' % model)
|
||||
return ()
|
||||
|
||||
# New Document: check model accepts the mailgateway
|
||||
if not thread_id and not hasattr(model_pool, 'message_new'):
|
||||
if assert_model:
|
||||
assert hasattr(model_pool, 'message_new'), 'Model %s does not accept document creation, crashing' % model
|
||||
_warn('model %s does not accept document creation, skipping' % model)
|
||||
return ()
|
||||
|
||||
# Update message author if asked
|
||||
# We do it now because we need it for aliases (contact settings)
|
||||
if not author_id and update_author:
|
||||
author_ids = self._find_partner_from_emails(cr, uid, thread_id, [email_from], model=model, context=context)
|
||||
if author_ids:
|
||||
author_id = author_ids[0]
|
||||
message_dict['author_id'] = author_id
|
||||
|
||||
# Alias: check alias_contact settings
|
||||
if alias and alias.alias_contact == 'followers' and (thread_id or alias.alias_parent_thread_id):
|
||||
if thread_id:
|
||||
obj = self.pool[model].browse(cr, uid, thread_id, context=context)
|
||||
else:
|
||||
obj = self.pool[alias.alias_parent_model_id.model].browse(cr, uid, alias.alias_parent_thread_id, context=context)
|
||||
if not author_id or not author_id in [fol.id for fol in obj.message_follower_ids]:
|
||||
_warn('alias %s restricted to internal followers, skipping' % alias.alias_name)
|
||||
_create_bounce_email()
|
||||
return ()
|
||||
elif alias and alias.alias_contact == 'partners' and not author_id:
|
||||
_warn('alias %s does not accept unknown author, skipping' % alias.alias_name)
|
||||
_create_bounce_email()
|
||||
return ()
|
||||
|
||||
return (model, thread_id, route[2], route[3], route[4])
|
||||
|
||||
def message_route(self, cr, uid, message, message_dict, model=None, thread_id=None,
|
||||
custom_values=None, context=None):
|
||||
"""Attempt to figure out the correct target model, thread_id,
|
||||
|
@ -616,8 +723,8 @@ class mail_thread(osv.AbstractModel):
|
|||
provided.
|
||||
4. If all the above fails, raise an exception.
|
||||
|
||||
:param dict message_dict: dictionary holding message variables
|
||||
:param string message: an email.message instance
|
||||
:param dict message_dict: dictionary holding message variables
|
||||
:param string model: the fallback model to use if the message
|
||||
does not match any of the currently configured mail aliases
|
||||
(may be None if a matching alias is supposed to be present)
|
||||
|
@ -628,12 +735,10 @@ class mail_thread(osv.AbstractModel):
|
|||
:param int thread_id: optional ID of the record/thread from ``model``
|
||||
to which this mail should be attached. Only used if the message
|
||||
does not reply to an existing thread and does not match any mail alias.
|
||||
:return: list of [model, thread_id, custom_values, user_id]
|
||||
:return: list of [model, thread_id, custom_values, user_id, alias]
|
||||
"""
|
||||
assert isinstance(message, Message), 'message must be an email.message.Message at this point'
|
||||
found_routes = []
|
||||
fallback_model = model
|
||||
avoid_assert = False
|
||||
|
||||
# Get email.message.Message variables for future processing
|
||||
message_id = message.get('Message-Id')
|
||||
|
@ -642,26 +747,6 @@ class mail_thread(osv.AbstractModel):
|
|||
references = decode_header(message, 'References')
|
||||
in_reply_to = decode_header(message, 'In-Reply-To')
|
||||
thread_references = references or in_reply_to
|
||||
# Delivered-To is a safe bet in most modern MTAs, but we have to fallback on To + Cc values
|
||||
# for all the odd MTAs out there, as there is no standard header for the envelope's `rcpt_to` value.
|
||||
rcpt_tos = ','.join([decode_header(message, 'Delivered-To'),
|
||||
decode_header(message, 'To'),
|
||||
decode_header(message, 'Cc'),
|
||||
decode_header(message, 'Resent-To'),
|
||||
decode_header(message, 'Resent-Cc')])
|
||||
|
||||
def create_bounce_email():
|
||||
mail_mail = self.pool.get('mail.mail')
|
||||
mail_id = mail_mail.create(cr, uid, {
|
||||
'body_html': '<div><p>Hello,</p>'
|
||||
'<p>The following email sent to %s cannot be accepted because this is '
|
||||
'a private email address. Only allowed people can contact us at this address.</p></div>'
|
||||
'<blockquote>%s</blockquote>' % (message.get('to'), message_dict.get('body')),
|
||||
'subject': 'Re: %s' % message.get('subject'),
|
||||
'email_to': message.get('from'),
|
||||
'auto_delete': True,
|
||||
}, context=context)
|
||||
mail_mail.send(cr, uid, [mail_id], context=context)
|
||||
|
||||
# 1. Reply to an existing thread
|
||||
ref_match = thread_references and tools.reference_re.search(thread_references)
|
||||
|
@ -673,10 +758,13 @@ class mail_thread(osv.AbstractModel):
|
|||
if model_obj.exists(cr, uid, thread_id) and hasattr(model_obj, 'message_update'):
|
||||
_logger.info('Routing mail from %s to %s with Message-Id %s: direct reply to model: %s, thread_id: %s, custom_values: %s, uid: %s',
|
||||
email_from, email_to, message_id, model, thread_id, custom_values, uid)
|
||||
found_routes = [(model, thread_id, custom_values, uid, None)]
|
||||
route = self.message_route_verify(cr, uid, message, message_dict,
|
||||
(model, thread_id, custom_values, uid, None),
|
||||
update_author=True, assert_model=True, create_fallback=True, context=context)
|
||||
return route and [route] or []
|
||||
|
||||
# 2. Reply to a private message
|
||||
if not found_routes and in_reply_to:
|
||||
if in_reply_to:
|
||||
message_ids = self.pool.get('mail.message').search(cr, uid, [
|
||||
('message_id', '=', in_reply_to),
|
||||
'!', ('message_id', 'ilike', 'reply_to')
|
||||
|
@ -685,96 +773,67 @@ class mail_thread(osv.AbstractModel):
|
|||
message = self.pool.get('mail.message').browse(cr, uid, message_ids[0], context=context)
|
||||
_logger.info('Routing mail from %s to %s with Message-Id %s: direct reply to a private message: %s, custom_values: %s, uid: %s',
|
||||
email_from, email_to, message_id, message.id, custom_values, uid)
|
||||
found_routes = [(message.model, message.res_id, custom_values, uid, None)]
|
||||
route = self.message_route_verify(cr, uid, message, message_dict,
|
||||
(message.model, message.res_id, custom_values, uid, None),
|
||||
update_author=True, assert_model=True, create_fallback=True, context=context)
|
||||
return route and [route] or []
|
||||
|
||||
# 3. Look for a matching mail.alias entry
|
||||
# Delivered-To is a safe bet in most modern MTAs, but we have to fallback on To + Cc values
|
||||
# for all the odd MTAs out there, as there is no standard header for the envelope's `rcpt_to` value.
|
||||
rcpt_tos = ','.join([decode_header(message, 'Delivered-To'),
|
||||
decode_header(message, 'To'),
|
||||
decode_header(message, 'Cc'),
|
||||
decode_header(message, 'Resent-To'),
|
||||
decode_header(message, 'Resent-Cc')])
|
||||
local_parts = [e.split('@')[0] for e in tools.email_split(rcpt_tos)]
|
||||
if not found_routes and local_parts:
|
||||
if local_parts:
|
||||
mail_alias = self.pool.get('mail.alias')
|
||||
alias_ids = mail_alias.search(cr, uid, [('alias_name', 'in', local_parts)])
|
||||
for alias in mail_alias.browse(cr, uid, alias_ids, context=context):
|
||||
user_id = alias.alias_user_id.id
|
||||
if not user_id:
|
||||
# TDE note: this could cause crashes, because no clue that the user
|
||||
# that send the email has the right to create or modify a new document
|
||||
# Fallback on user_id = uid
|
||||
# Note: recognized partners will be added as followers anyway
|
||||
# user_id = self._message_find_user_id(cr, uid, message, context=context)
|
||||
user_id = uid
|
||||
_logger.info('No matching user_id for the alias %s', alias.alias_name)
|
||||
found_routes.append((alias.alias_model_id.model, alias.alias_force_thread_id, eval(alias.alias_defaults), user_id, alias))
|
||||
if found_routes:
|
||||
_logger.info('Routing mail from %s to %s with Message-Id %s: direct alias match: %r',
|
||||
email_from, email_to, message_id, found_routes)
|
||||
if alias_ids:
|
||||
routes = []
|
||||
for alias in mail_alias.browse(cr, uid, alias_ids, context=context):
|
||||
user_id = alias.alias_user_id.id
|
||||
if not user_id:
|
||||
# TDE note: this could cause crashes, because no clue that the user
|
||||
# that send the email has the right to create or modify a new document
|
||||
# Fallback on user_id = uid
|
||||
# Note: recognized partners will be added as followers anyway
|
||||
# user_id = self._message_find_user_id(cr, uid, message, context=context)
|
||||
user_id = uid
|
||||
_logger.info('No matching user_id for the alias %s', alias.alias_name)
|
||||
route = (alias.alias_model_id.model, alias.alias_force_thread_id, eval(alias.alias_defaults), user_id, alias)
|
||||
_logger.info('Routing mail from %s to %s with Message-Id %s: direct alias match: %r',
|
||||
email_from, email_to, message_id, route)
|
||||
route = self.message_route_verify(cr, uid, message, message_dict, route,
|
||||
update_author=True, assert_model=True, create_fallback=True, context=context)
|
||||
if route:
|
||||
routes.append(route)
|
||||
return routes
|
||||
|
||||
# 4. Fallback to the provided parameters, if they work
|
||||
if not found_routes:
|
||||
if not thread_id:
|
||||
# Legacy: fallback to matching [ID] in the Subject
|
||||
match = tools.res_re.search(decode_header(message, 'Subject'))
|
||||
thread_id = match and match.group(1)
|
||||
# Convert into int (bug spotted in 7.0 because of str)
|
||||
try:
|
||||
thread_id = int(thread_id)
|
||||
except:
|
||||
thread_id = False
|
||||
found_routes = [(fallback_model, thread_id, custom_values, uid, None)]
|
||||
_logger.info('Routing mail from %s to %s with Message-Id %s: fallback to model:%s, thread_id:%s, custom_values:%s, uid:%s',
|
||||
email_from, email_to, message_id, fallback_model, thread_id, custom_values, uid)
|
||||
|
||||
# Post-process routes
|
||||
valid_routes = []
|
||||
author_id = False
|
||||
for route in found_routes:
|
||||
model, thread_id, alias = route[0], route[1], route[4]
|
||||
model_pool = self.pool.get(model)
|
||||
|
||||
# check related document effectively exists
|
||||
if thread_id and not model_pool.exists(cr, uid, thread_id):
|
||||
_logger.warning('Routing mail with Message-Id %s: route %s: reply to missing document %s, ignoring and creating new document instead',
|
||||
message_id, route, thread_id)
|
||||
thread_id = None
|
||||
# check model accepts the mailgateway
|
||||
if not (thread_id and hasattr(model_pool, 'message_update')) and not (hasattr(model_pool, 'message_new')):
|
||||
_logger.warning('Routing mail with Message-Id %s: route %s: model %s does not accept unknown emails, skipping',
|
||||
message_id, route, model)
|
||||
continue
|
||||
|
||||
# check for author
|
||||
if not author_id:
|
||||
author_ids = model_pool._find_partner_from_emails(cr, uid, thread_id, [email_from], context=context)
|
||||
if author_ids:
|
||||
author_id = author_ids[0]
|
||||
message_dict['author_id'] = author_id
|
||||
|
||||
# route contact settings
|
||||
if alias and alias.alias_contact == 'followers' and (thread_id or alias.alias_parent_thread_id):
|
||||
if thread_id:
|
||||
obj = self.pool[model].browse(cr, uid, thread_id, context=context)
|
||||
else:
|
||||
obj = self.pool[alias.alias_parent_model_id.model].browse(cr, uid, alias.alias_parent_thread_id, context=context)
|
||||
if not author_id or not author_id in [fol.id for fol in obj.message_follower_ids]:
|
||||
_logger.warning('Routing mail with Message-Id %s: route %s: alias %s restricted to internal followers, skipping',
|
||||
message_id, route, alias.alias_name)
|
||||
create_bounce_email()
|
||||
avoid_assert = True
|
||||
continue
|
||||
elif alias and alias.alias_contact == 'partners' and not author_id:
|
||||
_logger.warning('Routing mail with Message-Id %s: route %s: alias %s does not accept unknown sender, skipping',
|
||||
message_id, route, alias.alias_name)
|
||||
create_bounce_email()
|
||||
avoid_assert = True
|
||||
continue
|
||||
|
||||
# update route
|
||||
valid_routes.append((model, thread_id, route[2], route[3]))
|
||||
if not thread_id:
|
||||
# Legacy: fallback to matching [ID] in the Subject
|
||||
match = tools.res_re.search(decode_header(message, 'Subject'))
|
||||
thread_id = match and match.group(1)
|
||||
# Convert into int (bug spotted in 7.0 because of str)
|
||||
try:
|
||||
thread_id = int(thread_id)
|
||||
except:
|
||||
thread_id = False
|
||||
_logger.info('Routing mail from %s to %s with Message-Id %s: fallback to model:%s, thread_id:%s, custom_values:%s, uid:%s',
|
||||
email_from, email_to, message_id, fallback_model, thread_id, custom_values, uid)
|
||||
route = self.message_route_verify(cr, uid, message, message_dict,
|
||||
(fallback_model, thread_id, custom_values, uid, None),
|
||||
update_author=True, assert_model=True, context=context)
|
||||
if route:
|
||||
return [route]
|
||||
|
||||
# AssertionError if no routes found and if no bounce occured
|
||||
assert valid_routes or avoid_assert, \
|
||||
assert False, \
|
||||
"Routing mail from %s to %s with Message-Id %s: no possible route found. " \
|
||||
"Create an appropriate mail.alias or force the destination model." % \
|
||||
(email_from, email_to, message_id)
|
||||
return valid_routes
|
||||
|
||||
def message_process(self, cr, uid, model, message, custom_values=None,
|
||||
save_original=False, strip_attachments=False,
|
||||
|
@ -840,9 +899,9 @@ class mail_thread(osv.AbstractModel):
|
|||
return False
|
||||
|
||||
# find possible routes for the message
|
||||
thread_id = False
|
||||
routes = self.message_route(cr, uid, msg_txt, msg, model, thread_id, custom_values, context=context)
|
||||
for model, thread_id, custom_values, user_id in routes:
|
||||
thread_id = False
|
||||
for model, thread_id, custom_values, user_id, alias in routes:
|
||||
if self._name == 'mail.thread':
|
||||
context.update({'thread_model': model})
|
||||
if model:
|
||||
|
@ -1097,8 +1156,7 @@ class mail_thread(osv.AbstractModel):
|
|||
self._message_add_suggested_recipient(cr, uid, result, obj, partner=obj.user_id.partner_id, reason=self._all_columns['user_id'].column.string, context=context)
|
||||
return result
|
||||
|
||||
def _find_partner_from_emails(self, cr, uid, id, emails, context=None,
|
||||
check_followers=True, only_users=False, take_all=False, full_info=False):
|
||||
def _find_partner_from_emails(self, cr, uid, id, emails, model=None, context=None, check_followers=True):
|
||||
""" Utility method to find partners
|
||||
|
||||
:param boolean check_followers: TODO
|
||||
|
@ -1108,8 +1166,11 @@ class mail_thread(osv.AbstractModel):
|
|||
partner_obj = self.pool['res.partner']
|
||||
partner_ids = []
|
||||
obj = None
|
||||
if id and self._name != 'mail.thread' and check_followers:
|
||||
obj = self.browse(cr, uid, id, context=context)
|
||||
if id and (model or self._name != 'mail.thread') and check_followers:
|
||||
if model:
|
||||
obj = self.pool[model].browse(cr, uid, id, context=context)
|
||||
else:
|
||||
obj = self.browse(cr, uid, id, context=context)
|
||||
for contact in emails:
|
||||
partner_id = False
|
||||
email_address = tools.email_split(contact)
|
||||
|
@ -1145,9 +1206,7 @@ class mail_thread(osv.AbstractModel):
|
|||
new_partner_ids. The return value is non conventional because
|
||||
it is meant to be used by the mail widget.
|
||||
|
||||
:return dict: partner_ids and new_partner_ids
|
||||
|
||||
TDE TODO: merge me with other partner finding methods in 8.0 """
|
||||
:return dict: partner_ids and new_partner_ids """
|
||||
mail_message_obj = self.pool.get('mail.message')
|
||||
partner_ids = self._find_partner_from_emails(cr, uid, id, emails, context=context)
|
||||
result = list()
|
||||
|
@ -1326,21 +1385,6 @@ class mail_thread(osv.AbstractModel):
|
|||
self.message_subscribe(cr, uid, [thread_id], [message.author_id.id], context=context)
|
||||
return msg_id
|
||||
|
||||
#------------------------------------------------------
|
||||
# Compatibility methods: do not use
|
||||
# TDE TODO: remove me in 8.0
|
||||
#------------------------------------------------------
|
||||
|
||||
def message_create_partners_from_emails(self, cr, uid, emails, context=None):
|
||||
return {'partner_ids': [], 'new_partner_ids': []}
|
||||
|
||||
def message_post_user_api(self, cr, uid, thread_id, body='', parent_id=False,
|
||||
attachment_ids=None, content_subtype='plaintext',
|
||||
context=None, **kwargs):
|
||||
return self.message_post(cr, uid, thread_id, body=body, parent_id=parent_id,
|
||||
attachment_ids=attachment_ids, content_subtype=content_subtype,
|
||||
context=context, **kwargs)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Followers API
|
||||
#------------------------------------------------------
|
||||
|
|
|
@ -189,6 +189,7 @@ class TestMailgateway(TestMailBase):
|
|||
|
||||
# Data: set catchall domain
|
||||
self.registry('ir.config_parameter').set_param(cr, uid, 'mail.catchall.domain', alias_domain)
|
||||
self.registry('ir.config_parameter').unlink(cr, uid, self.registry('ir.config_parameter').search(cr, uid, [('key', '=', 'mail.catchall.alias')]))
|
||||
|
||||
# Update message
|
||||
self.mail_message.write(cr, user_raoul_id, [msg_id], {'email_from': False, 'reply_to': False})
|
||||
|
@ -220,87 +221,6 @@ class TestMailgateway(TestMailBase):
|
|||
self.assertEqual(mail.reply_to, msg.email_from,
|
||||
'mail_mail: incorrect reply_to: should be message email_from')
|
||||
|
||||
def test_05_mail_message_mail_mail(self):
|
||||
""" Tests designed for testing email values based on mail.message, aliases, ... """
|
||||
cr, uid = self.cr, self.uid
|
||||
|
||||
# Data: clean catchall domain
|
||||
param_ids = self.registry('ir.config_parameter').search(cr, uid, [('key', '=', 'mail.catchall.domain')])
|
||||
self.registry('ir.config_parameter').unlink(cr, uid, param_ids)
|
||||
|
||||
# Do: create a mail_message with a reply_to, without message-id
|
||||
msg_id = self.mail_message.create(cr, uid, {'subject': 'Subject', 'body': 'Body', 'reply_to': 'custom@example.com'})
|
||||
msg = self.mail_message.browse(cr, uid, msg_id)
|
||||
# Test: message content
|
||||
self.assertIn('reply_to', msg.message_id,
|
||||
'mail_message: message_id should be specific to a mail_message with a given reply_to')
|
||||
self.assertEqual('custom@example.com', msg.reply_to,
|
||||
'mail_message: incorrect reply_to')
|
||||
# Do: create a mail_mail with the previous mail_message and specified reply_to
|
||||
mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'reply_to': 'other@example.com', 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, uid, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, 'other@example.com',
|
||||
'mail_mail: reply_to should be equal to the one coming from creation values')
|
||||
# Do: create a mail_mail with the previous mail_message
|
||||
self.mail_message.write(cr, uid, [msg_id], {'reply_to': 'custom@example.com'})
|
||||
msg.refresh()
|
||||
mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, uid, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, msg.reply_to,
|
||||
'mail_mail: reply_to should be equal to the one coming from the mail_message')
|
||||
|
||||
# Do: create a mail_message without a reply_to
|
||||
msg_id = self.mail_message.create(cr, uid, {'subject': 'Subject', 'body': 'Body', 'model': 'mail.group', 'res_id': self.group_pigs_id, 'email_from': False})
|
||||
msg = self.mail_message.browse(cr, uid, msg_id)
|
||||
# Test: message content
|
||||
self.assertIn('mail.group', msg.message_id,
|
||||
'mail_message: message_id should contain model')
|
||||
self.assertIn('%s' % self.group_pigs_id, msg.message_id,
|
||||
'mail_message: message_id should contain res_id')
|
||||
self.assertFalse(msg.reply_to,
|
||||
'mail_message: should not generate a reply_to address when not specified')
|
||||
# Do: create a mail_mail based on the previous mail_message
|
||||
mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, uid, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertFalse(mail.reply_to,
|
||||
'mail_mail: reply_to should not have been guessed')
|
||||
# Update message
|
||||
self.mail_message.write(cr, uid, [msg_id], {'email_from': 'someone@example.com'})
|
||||
msg.refresh()
|
||||
# Do: create a mail_mail based on the previous mail_message
|
||||
mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, uid, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(email_split(mail.reply_to), email_split(msg.email_from),
|
||||
'mail_mail: reply_to should be equal to mail_message.email_from when having no document or default alias')
|
||||
|
||||
# Data: set catchall domain
|
||||
self.registry('ir.config_parameter').set_param(cr, uid, 'mail.catchall.domain', 'schlouby.fr')
|
||||
self.registry('ir.config_parameter').unlink(cr, uid, self.registry('ir.config_parameter').search(cr, uid, [('key', '=', 'mail.catchall.alias')]))
|
||||
|
||||
# Update message
|
||||
self.mail_message.write(cr, uid, [msg_id], {'email_from': False, 'reply_to': False})
|
||||
msg.refresh()
|
||||
# Do: create a mail_mail based on the previous mail_message
|
||||
mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, uid, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, '"Followers of Pigs" <group+pigs@schlouby.fr>',
|
||||
'mail_mail: reply_to should equal the mail.group alias')
|
||||
|
||||
# Update message
|
||||
self.mail_message.write(cr, uid, [msg_id], {'res_id': False, 'email_from': 'someone@schlouby.fr', 'reply_to': False})
|
||||
msg.refresh()
|
||||
# Do: create a mail_mail based on the previous mail_message
|
||||
mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, uid, mail_id)
|
||||
# Test: mail_mail content
|
||||
self.assertEqual(mail.reply_to, msg.email_from,
|
||||
'mail_mail: reply_to should equal the mail_message email_from')
|
||||
|
||||
# Data: set catchall alias
|
||||
self.registry('ir.config_parameter').set_param(self.cr, self.uid, 'mail.catchall.alias', 'gateway')
|
||||
|
||||
|
@ -310,7 +230,7 @@ class TestMailgateway(TestMailBase):
|
|||
# Do: create a mail_mail based on the previous mail_message
|
||||
mail_id = self.mail_mail.create(cr, uid, {'mail_message_id': msg_id, 'state': 'cancel'})
|
||||
mail = self.mail_mail.browse(cr, uid, mail_id)
|
||||
# Test: mail_mail content
|
||||
# Test: mail_mail Content-Type
|
||||
self.assertEqual(mail.reply_to, 'gateway@schlouby.fr',
|
||||
'mail_mail: reply_to should equal the catchall email alias')
|
||||
|
||||
|
@ -395,7 +315,7 @@ class TestMailgateway(TestMailBase):
|
|||
# Data: unlink group
|
||||
frog_group.unlink()
|
||||
|
||||
# Do: incoming email from an unknown partner on an Authenticated only alias -> bounce
|
||||
# Do: incoming email from an unknown partner on a Partners only alias -> bounce
|
||||
self._init_mock_build_email()
|
||||
self.mail_alias.write(cr, uid, [alias_id], {'alias_contact': 'partners'})
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other2@gmail.com')
|
||||
|
@ -410,25 +330,10 @@ class TestMailgateway(TestMailBase):
|
|||
self.assertIn('test.sylvie.lelitre@agrolait.com', sent_emails[0].get('email_to'),
|
||||
'message_process: bounce email on Partners alias should have original email sender as recipient')
|
||||
|
||||
# Do: incoming email from an unknown partner on a Partners only alias -> bounce
|
||||
self._init_mock_build_email()
|
||||
self.mail_alias.write(cr, uid, [alias_id], {'alias_contact': 'partners'})
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other3@gmail.com')
|
||||
# Test: no group created
|
||||
self.assertTrue(len(frog_groups) == 0)
|
||||
# Test: email bounced
|
||||
sent_emails = self._build_email_kwargs_list
|
||||
self.assertEqual(len(sent_emails), 1,
|
||||
'message_process: incoming email on Partners alias should send a bounce email')
|
||||
self.assertIn('Frogs', sent_emails[0].get('subject'),
|
||||
'message_process: bounce email on Followers alias should contain the original subject')
|
||||
self.assertIn('test.sylvie.lelitre@agrolait.com', sent_emails[0].get('email_to'),
|
||||
'message_process: bounce email on Followers alias should have original email sender as recipient')
|
||||
|
||||
# Do: incoming email from an unknown partner on a Followers only alias -> bounce
|
||||
self._init_mock_build_email()
|
||||
self.mail_alias.write(cr, uid, [alias_id], {'alias_contact': 'followers'})
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other4@gmail.com')
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other3@gmail.com')
|
||||
# Test: no group created
|
||||
self.assertTrue(len(frog_groups) == 0)
|
||||
# Test: email bounced
|
||||
|
@ -440,12 +345,12 @@ class TestMailgateway(TestMailBase):
|
|||
self.assertIn('test.sylvie.lelitre@agrolait.com', sent_emails[0].get('email_to'),
|
||||
'message_process: bounce email on Followers alias should have original email sender as recipient')
|
||||
|
||||
# Do: incoming email from a known partner on an alias with known recipients, alias is owned by user that can create a group
|
||||
# Do: incoming email from a known partner on a Partners alias -> ok (+ test on alias.user_id)
|
||||
self.mail_alias.write(cr, uid, [alias_id], {'alias_user_id': self.user_raoul_id, 'alias_contact': 'partners'})
|
||||
p1id = self.res_partner.create(cr, uid, {'name': 'Sylvie Lelitre', 'email': 'test.sylvie.lelitre@agrolait.com'})
|
||||
p2id = self.res_partner.create(cr, uid, {'name': 'Other Poilvache', 'email': 'other@gmail.com'})
|
||||
p2id = self.res_partner.create(cr, uid, {'name': 'Other Poilvache', 'email': 'other4@gmail.com'})
|
||||
self._init_mock_build_email()
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other@gmail.com')
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other4@gmail.com')
|
||||
sent_emails = self._build_email_kwargs_list
|
||||
# Test: one group created by Raoul
|
||||
self.assertEqual(len(frog_groups), 1, 'message_process: a new mail.group should have been created')
|
||||
|
@ -462,7 +367,7 @@ class TestMailgateway(TestMailBase):
|
|||
'message_process: message on created group should have Sylvie as author_id')
|
||||
self.assertIn('Sylvie Lelitre <test.sylvie.lelitre@agrolait.com>', msg.email_from,
|
||||
'message_process: message on created group should have have an email_from')
|
||||
# Test: author (not recipient and not raoul (as alias owner)) added as follower
|
||||
# Test: author (not recipient and not Raoul (as alias owner)) added as follower
|
||||
frog_follower_ids = set([p.id for p in frog_group.message_follower_ids])
|
||||
self.assertEqual(frog_follower_ids, set([p1id]),
|
||||
'message_process: newly created group should have 1 follower (author, not creator, not recipients)')
|
||||
|
@ -473,9 +378,19 @@ class TestMailgateway(TestMailBase):
|
|||
# Data: unlink group
|
||||
frog_group.unlink()
|
||||
|
||||
# Do: incoming email from a not follower Partner on a Followers only alias -> ok
|
||||
# Do: incoming email from a not follower Partner on a Followers only alias -> bounce
|
||||
self._init_mock_build_email()
|
||||
self.mail_alias.write(cr, uid, [alias_id], {'alias_user_id': False, 'alias_contact': 'followers'})
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other5@gmail.com')
|
||||
# Test: no group created
|
||||
self.assertTrue(len(frog_groups) == 0)
|
||||
# Test: email bounced
|
||||
sent_emails = self._build_email_kwargs_list
|
||||
self.assertEqual(len(sent_emails), 1,
|
||||
'message_process: incoming email on Partners alias should send a bounce email')
|
||||
|
||||
# Do: incoming email from a parent document follower on a Followers only alias -> ok
|
||||
self._init_mock_build_email()
|
||||
self.mail_alias.write(cr, uid, [alias_id], {'alias_contact': 'followers'})
|
||||
self.mail_group.message_subscribe(cr, uid, [self.group_pigs_id], [p1id])
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, to='groups@example.com, other6@gmail.com')
|
||||
# Test: one group created by Raoul (or Sylvie maybe, if we implement it)
|
||||
|
@ -500,7 +415,7 @@ class TestMailgateway(TestMailBase):
|
|||
# Do: Pigs alias is restricted, should bounce
|
||||
self._init_mock_build_email()
|
||||
self.mail_group.write(cr, uid, [frog_group.id], {'alias_name': 'frogs', 'alias_contact': 'followers', 'alias_force_thread_id': frog_group.id})
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other@gmail.com',
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
|
||||
msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
|
||||
to='frogs@example.com>', subject='Re: news')
|
||||
# Test: no group 'Re: news' created, still only 1 Frogs group
|
||||
|
@ -520,8 +435,7 @@ class TestMailgateway(TestMailBase):
|
|||
# Do: Pigs alias is restricted, should accept Followers
|
||||
self._init_mock_build_email()
|
||||
self.mail_group.message_subscribe(cr, uid, [frog_group.id], [p2id])
|
||||
self.mail_group.write(cr, uid, [frog_group.id], {'alias_name': 'frogs', 'alias_contact': 'followers'})
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other@gmail.com',
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
|
||||
msg_id='<1198923581.41972151344608186799.JavaMail.diff1@agrolait.com>',
|
||||
to='frogs@example.com>', subject='Re: cats')
|
||||
# Test: no group 'Re: news' created, still only 1 Frogs group
|
||||
|
@ -546,7 +460,7 @@ class TestMailgateway(TestMailBase):
|
|||
# --------------------------------------------------
|
||||
|
||||
# Do: even with a wrong destination, a reply should end up in the correct thread
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other@gmail.com',
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
|
||||
msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
|
||||
to='erroneous@example.com>', subject='Re: news',
|
||||
extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
|
||||
|
@ -565,7 +479,7 @@ class TestMailgateway(TestMailBase):
|
|||
'message_process: after reply, group should have 2 followers')
|
||||
|
||||
# Do: due to some issue, same email goes back into the mailgateway
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other@gmail.com',
|
||||
frog_groups = format_and_process(MAIL_TEMPLATE, email_from='other4@gmail.com',
|
||||
msg_id='<1198923581.41972151344608186760.JavaMail.diff1@agrolait.com>',
|
||||
subject='Re: news', extra='In-Reply-To: <12321321-openerp-%d-mail.group@example.com>\n' % frog_group.id)
|
||||
# Test: no group 'Re: news' created, still only 1 Frogs group
|
||||
|
@ -666,7 +580,7 @@ class TestMailgateway(TestMailBase):
|
|||
frog_group = self.mail_group.browse(cr, uid, frog_groups[0])
|
||||
msg = frog_group.message_ids[0]
|
||||
# Test: plain text content should be wrapped and stored as html
|
||||
self.assertEqual(msg.body, '<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>',
|
||||
self.assertIn('<pre>\nPlease call me as soon as possible this afternoon!\n\n--\nSylvie\n</pre>', msg.body,
|
||||
'message_process: plaintext incoming email incorrectly parsed')
|
||||
|
||||
@mute_logger('openerp.addons.mail.mail_thread', 'openerp.osv.orm')
|
||||
|
|
Loading…
Reference in New Issue