From bcce093ed3ca89fb8dfc69dfcf4147bcf1f34bad Mon Sep 17 00:00:00 2001 From: Renaud Thiry Date: Fri, 2 Sep 2022 07:54:08 +0000 Subject: [PATCH] [IMP] mail: add message_partner inverse on threads This change allows for adding and removing followers from threads by directly setting the message_partner_ids field of threads. This enables the use of many2many_tags to add followers to a thread. Task-2804888 closes odoo/odoo#95645 Related: odoo/enterprise#29284 Signed-off-by: Thibault Delavallee (tde) --- addons/mail/models/mail_thread.py | 12 ++++ addons/test_mail/tests/test_mail_followers.py | 60 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index c5183a7fc53..a779fb03322 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -85,6 +85,7 @@ class MailThread(models.AbstractModel): message_partner_ids = fields.Many2many( comodel_name='res.partner', string='Followers (Partners)', compute='_compute_message_partner_ids', + inverse='_inverse_message_partner_ids', search='_search_message_partner_ids', groups='base.group_user') message_ids = fields.One2many( @@ -113,6 +114,17 @@ class MailThread(models.AbstractModel): for thread in self: thread.message_partner_ids = thread.message_follower_ids.mapped('partner_id') + def _inverse_message_partner_ids(self): + for thread in self: + new_partners_ids = thread.message_partner_ids + previous_partners_ids = thread.message_follower_ids.partner_id + removed_partners_ids = previous_partners_ids - new_partners_ids + added_patners_ids = new_partners_ids - previous_partners_ids + if added_patners_ids: + thread.message_subscribe(added_patners_ids.ids) + if removed_partners_ids: + thread.message_unsubscribe(removed_partners_ids.ids) + @api.model def _search_message_partner_ids(self, operator, operand): """Search function for message_follower_ids diff --git a/addons/test_mail/tests/test_mail_followers.py b/addons/test_mail/tests/test_mail_followers.py index 6d746564a05..ac64cd377fd 100644 --- a/addons/test_mail/tests/test_mail_followers.py +++ b/addons/test_mail/tests/test_mail_followers.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.addons.test_mail.tests.common import TestMailCommon +from odoo.exceptions import AccessError from odoo.tests import tagged from odoo.tests import users @@ -143,6 +144,65 @@ class BaseFollowersTest(TestMailCommon): self.assertEqual(document.message_partner_ids, self.partner_portal, 'No active test: customer not visible') self.assertEqual(document.message_follower_ids.partner_id, self.partner_portal | customer) + @users('employee') + def test_followers_inverse_message_partner(self): + test_record = self.test_record.with_env(self.env) + partner0, partner1, partner2, partner3 = self.env['res.partner'].create( + [{'email': f'partner.{n}@test.lan', 'name': f'partner{n}'} for n in range(4)] + ) + self.assertFalse(test_record.message_follower_ids) + self.assertFalse(test_record.message_partner_ids) + + # fillup with API + test_record.message_subscribe(partner_ids=partner3.ids) + self.assertEqual(test_record.message_follower_ids.partner_id, partner3) + # set empty + test_record.message_partner_ids = None + self.assertFalse(test_record.message_follower_ids.partner_id) + # set 1 + test_record.message_partner_ids = partner0 + self.assertEqual(test_record.message_follower_ids.partner_id, partner0) + # set multiple when non-empty + test_record.message_partner_ids = partner1 + partner2 + self.assertEqual(test_record.message_follower_ids.partner_id, partner1 + partner2) + # remove 1 + test_record.message_partner_ids -= partner1 + self.assertEqual(test_record.message_follower_ids.partner_id, partner2) + # add multiple with one already set + test_record.message_partner_ids += partner1 + partner2 + self.assertEqual(test_record.message_follower_ids.partner_id, partner1 + partner2) + # remove outside of existing + test_record.message_partner_ids -= partner3 + self.assertEqual(test_record.message_follower_ids.partner_id, partner1 + partner2) + # reset + test_record.message_partner_ids = False + self.assertFalse(test_record.message_follower_ids.partner_id) + + # test with inactive and commands + partner0.write({'active': False}) + test_record.write({'message_partner_ids': [(4, partner0.id), (4, partner1.id)]}) + self.assertEqual(test_record.message_follower_ids.partner_id, partner1) + + def test_followers_inverse_message_partner_access_rights(self): + """ Make sure we're not bypassing security checks by setting a partner + instead of a follower """ + test_record = self.test_record.with_user(self.user_portal) + partner0 = self.env['res.partner'].create({ + 'email': 'partner1@test.lan', + 'name': 'partner1', + }) + _name = test_record.name # check portal user can read + + # set empty + with self.assertRaises(AccessError): + test_record.message_partner_ids = None + # set 1 + with self.assertRaises(AccessError): + test_record.message_partner_ids = partner0 + # remove 1 + with self.assertRaises(AccessError): + test_record.message_partner_ids -= partner0 + @users('employee') def test_followers_private_address(self): """ Test standard API does not subscribe private addresses """