[FIX] mail: notify alias owner when fetching

[1] introduces an issue when a response is sent to an aliased email.

In that case, it is intended that the message should be created
by the owner of the alias by [2]
Which breaks the assumption of made in the first commit, as the message
is being passively processed by fetchmail.

This causes the owner of the alias to never be notified on reponses
in cases where the message could be an alias update.
i.e. on any model where the alias applies.
Because the responses are assumed to have been authored by the owner.

As we do not actually care who creates the message records
and deciding the alias owner 'created the message' does not
actually make sense.
We make sure messages are created by odoobot even
if the message was sent to an alias address.

[1]: c676ed3ea906e99d27a6116cd77218c5ec95b416
[2]: af80c68ae5

task-3383275

X-original-commit: 96fe37d37c94c3d6f4642bd6c28d13fcd87075b7
Part-of: odoo/odoo#130798
This commit is contained in:
Renaud Thiry
2023-08-04 08:26:01 +02:00
committed by Thibault Delavallée
parent 9da5f6e0f6
commit 4dd17a201b
3 changed files with 71 additions and 12 deletions
+7 -4
View File
@@ -1143,6 +1143,9 @@ class MailThread(models.AbstractModel):
thread_id = thread.id
subtype_id = thread._creation_subtype().id
# switch to odoobot for all incoming message creation
# to have a priviledged archived user so real_author_id is correctly computed
thread_root = thread.with_user(self.env.ref('base.user_root'))
# replies to internal message are considered as notes, but parent message
# author is added in recipients to ensure they are notified of a private answer
parent_message = False
@@ -1162,13 +1165,13 @@ class MailThread(models.AbstractModel):
for x in ('from', 'to', 'cc', 'recipients', 'references', 'in_reply_to', 'is_bounce', 'bounced_email', 'bounced_message', 'bounced_msg_ids', 'bounced_partner'):
post_params.pop(x, None)
new_msg = False
if thread._name == 'mail.thread': # message with parent_id not linked to record
new_msg = thread.message_notify(**post_params)
if thread_root._name == 'mail.thread': # message with parent_id not linked to record
new_msg = thread_root.message_notify(**post_params)
else:
# parsing should find an author independently of user running mail gateway, and ensure it is not odoobot
partner_from_found = message_dict.get('author_id') and message_dict['author_id'] != self.env['ir.model.data']._xmlid_to_res_id('base.partner_root')
thread = thread.with_context(mail_create_nosubscribe=not partner_from_found)
new_msg = thread.message_post(**post_params)
thread_root = thread_root.with_context(mail_create_nosubscribe=not partner_from_found)
new_msg = thread_root.message_post(**post_params)
if new_msg and original_partner_ids:
# postponed after message_post, because this is an external message and we don't want to create
+2 -2
View File
@@ -132,7 +132,7 @@ class MockEmail(common.BaseCase, MockSmtplibCase):
def format_and_process(self, template, email_from, to, subject='Frogs', cc='',
return_path='', extra='', msg_id=False,
model=None, target_model='mail.test.gateway', target_field='name',
**kwargs):
with_user=None, **kwargs):
self.assertFalse(self.env[target_model].search([(target_field, '=', subject)]))
if not msg_id:
msg_id = "<%.7f-test@iron.sky>" % (time.time())
@@ -142,7 +142,7 @@ class MockEmail(common.BaseCase, MockSmtplibCase):
email_from=email_from, msg_id=msg_id,
**kwargs)
# In real use case, fetched mail processing is executed with administrative right.
self.env['mail.thread'].sudo().message_process(model, mail)
self.env['mail.thread'].with_user(with_user or self.env.user).sudo().message_process(model, mail)
return self.env[target_model].search([(target_field, '=', subject)])
def gateway_reply_wrecord(self, template, record, use_in_reply_to=True):
+62 -6
View File
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
import itertools
import socket
from datetime import datetime
@@ -354,6 +355,61 @@ class TestMailgateway(MailCommon):
self.assertFalse(record.message_ids[0].author_id)
self.assertEqual(record.message_ids[0].email_from, from_1.email_formatted)
@mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models')
def test_message_route_alias_owner_author_notify(self):
""" Make sure users are notified when a reply is sent to an alias address.
Alias owner should impact the message creator, but not notifications. """
test_record = self.env['mail.test.ticket'].create({})
author_partner = self.env['res.partner'].create({
'name': 'Author',
'email': 'author-partner@test.com',
})
message = self.env['mail.message'].create({
'body': '<p>test</p>',
'email_from': 'author-partner@test.com', # email sent by author who also has an alias with their email
'message_type': 'email',
'model': test_record._name,
'res_id': test_record.id,
})
self.env['mail.alias'].create({
'alias_model_id': self.env['ir.model']._get_id(test_record._name),
'alias_name': 'author-partner',
'alias_user_id': self.user_employee.id
})
test_record.message_subscribe((author_partner | self.user_employee.partner_id).ids)
messages = test_record.message_ids
self.assertFalse(self.user_root.active, 'notification logic relies on odoobot being archived')
test_users = [self.user_employee, self.user_root]
email_tos = ['author-partner@test.com', 'some_non_aliased_email@test.com']
for email_to, test_user in itertools.product(email_tos, test_users):
with self.subTest(test_user=test_user, email_to=email_to):
with self.mock_mail_gateway(), self.mock_mail_app():
self.format_and_process(
MAIL_TEMPLATE, self.email_from, email_to,
subject=message.message_id, extra=f'In-Reply-To:\r\n\t{message.message_id}\n',
model=None, with_user=test_user)
new_messages = test_record.message_ids - messages
self.assertEqual(len(new_messages), 1)
self.assertEqual(new_messages.create_uid, self.user_root,
'Odoobot should be creating the message')
# Make sure the alias owner is notified if they are a follower
self.assertNotified(new_messages, [{
'partner': self.user_employee.partner_id,
'is_read': False,
'type': 'inbox',
}])
# never notify the author of the incoming message
with self.assertRaises(Exception):
self.assertNotified(new_messages, [{'partner': author_partner}])
messages = test_record.message_ids
# --------------------------------------------------
# Alias configuration
# --------------------------------------------------
@@ -626,7 +682,7 @@ class TestMailgateway(MailCommon):
record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, f'groups@{self.alias_domain}', subject='NoEmployeeAllowed')
self.assertEqual(record.create_uid, self.user_employee)
self.assertEqual(record.message_ids[0].subject, 'NoEmployeeAllowed')
self.assertEqual(record.message_ids[0].create_uid, self.user_employee)
self.assertEqual(record.message_ids[0].create_uid, self.user_root, 'Message should be created by caller of message_process.')
self.assertEqual(record.message_ids[0].author_id, self.user_employee.partner_id)
@mute_logger('odoo.addons.mail.models.mail_thread')
@@ -634,19 +690,19 @@ class TestMailgateway(MailCommon):
record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, f'groups@{self.alias_domain}', subject='Email Found')
self.assertEqual(record.create_uid, self.user_employee)
self.assertEqual(record.message_ids[0].subject, 'Email Found')
self.assertEqual(record.message_ids[0].create_uid, self.user_employee)
self.assertEqual(record.message_ids[0].create_uid, self.user_root)
self.assertEqual(record.message_ids[0].author_id, self.user_employee.partner_id)
record = self.format_and_process(MAIL_TEMPLATE, 'Another name <%s>' % self.user_employee.email, f'groups@{self.alias_domain}', subject='Email OtherName')
self.assertEqual(record.create_uid, self.user_employee)
self.assertEqual(record.message_ids[0].subject, 'Email OtherName')
self.assertEqual(record.message_ids[0].create_uid, self.user_employee)
self.assertEqual(record.message_ids[0].create_uid, self.user_root)
self.assertEqual(record.message_ids[0].author_id, self.user_employee.partner_id)
record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_normalized, f'groups@{self.alias_domain}', subject='Email SimpleEmail')
self.assertEqual(record.create_uid, self.user_employee)
self.assertEqual(record.message_ids[0].subject, 'Email SimpleEmail')
self.assertEqual(record.message_ids[0].create_uid, self.user_employee)
self.assertEqual(record.message_ids[0].create_uid, self.user_root)
self.assertEqual(record.message_ids[0].author_id, self.user_employee.partner_id)
@mute_logger('odoo.addons.mail.models.mail_thread', 'odoo.models.unlink')
@@ -661,7 +717,7 @@ class TestMailgateway(MailCommon):
record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, f'groups@{self.alias_domain}', subject='FollowerWinner')
self.assertEqual(record.create_uid, follower_user)
self.assertEqual(record.message_ids[0].subject, 'FollowerWinner')
self.assertEqual(record.message_ids[0].create_uid, follower_user)
self.assertEqual(record.message_ids[0].create_uid, self.user_root)
self.assertEqual(record.message_ids[0].author_id, follower_user.partner_id)
# name order win
@@ -670,7 +726,7 @@ class TestMailgateway(MailCommon):
record = self.format_and_process(MAIL_TEMPLATE, self.user_employee.email_formatted, f'groups@{self.alias_domain}', subject='FirstFoundWinner')
self.assertEqual(record.create_uid, self.user_employee)
self.assertEqual(record.message_ids[0].subject, 'FirstFoundWinner')
self.assertEqual(record.message_ids[0].create_uid, self.user_employee)
self.assertEqual(record.message_ids[0].create_uid, self.user_root)
self.assertEqual(record.message_ids[0].author_id, self.user_employee.partner_id)
# --------------------------------------------------