diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 830b82e4c6f..f79e8d2ada2 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -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 diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py index 055eb69de80..48f01f3cf88 100644 --- a/addons/mail/tests/common.py +++ b/addons/mail/tests/common.py @@ -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): diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py index 75f144db9ef..8c8ab81a3bc 100644 --- a/addons/test_mail/tests/test_mail_gateway.py +++ b/addons/test_mail/tests/test_mail_gateway.py @@ -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': '
test
', + '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) # --------------------------------------------------