When users are archived / deleted, they are still listed under the members of a channel. This means that those users could potentially still receive internal communications. This commit fixes the issue by removing archived / deleted users from the members of the private channels they were part of. However, they are not automatically removed from the public channels. Public channels are kepts as in most common cases public channels are mailing list (e-mail based) and so user's partner should always receive updates from public channels until they manually un-subscribe themselves. Task ID-2276681 closes odoo/odoo#55496 X-original-commit: 1a3dc58f3d987054c643556dc4b7c3dbcd3d074c Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
397 lines
20 KiB
Python
397 lines
20 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo.tests import tagged
|
|
from odoo.addons.mail.tests.common import mail_new_test_user
|
|
from odoo.addons.test_mail.tests.common import TestMailCommon
|
|
from odoo.exceptions import AccessError, ValidationError, UserError
|
|
from odoo.tools import mute_logger, formataddr
|
|
|
|
|
|
class TestChannelAccessRights(TestMailCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestChannelAccessRights, cls).setUpClass()
|
|
Channel = cls.env['mail.channel'].with_context(cls._test_context)
|
|
|
|
cls.user_public = mail_new_test_user(cls.env, login='bert', groups='base.group_public', name='Bert Tartignole')
|
|
cls.user_portal = mail_new_test_user(cls.env, login='chell', groups='base.group_portal', name='Chell Gladys')
|
|
|
|
# Pigs: base group for tests
|
|
cls.group_pigs = Channel.create({
|
|
'name': 'Pigs',
|
|
'public': 'groups',
|
|
'group_public_id': cls.env.ref('base.group_user').id})
|
|
# Jobs: public group
|
|
cls.group_public = Channel.create({
|
|
'name': 'Jobs',
|
|
'description': 'NotFalse',
|
|
'public': 'public'})
|
|
# Private: private group
|
|
cls.group_private = Channel.create({
|
|
'name': 'Private',
|
|
'public': 'private'})
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
|
|
def test_access_rights_public(self):
|
|
# Read public group -> ok
|
|
self.group_public.with_user(self.user_public).read()
|
|
|
|
# Read Pigs -> ko, restricted to employees
|
|
with self.assertRaises(AccessError):
|
|
self.group_pigs.with_user(self.user_public).read()
|
|
|
|
# Read a private group when being a member: ok
|
|
self.group_private.write({'channel_partner_ids': [(4, self.user_public.partner_id.id)]})
|
|
self.group_private.with_user(self.user_public).read()
|
|
|
|
# Create group: ko, no access rights
|
|
with self.assertRaises(AccessError):
|
|
self.env['mail.channel'].with_user(self.user_public).create({'name': 'Test'})
|
|
|
|
# Update group: ko, no access rights
|
|
with self.assertRaises(AccessError):
|
|
self.group_public.with_user(self.user_public).write({'name': 'Broutouschnouk'})
|
|
|
|
# Unlink group: ko, no access rights
|
|
with self.assertRaises(AccessError):
|
|
self.group_public.with_user(self.user_public).unlink()
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models', 'odoo.models.unlink')
|
|
def test_access_rights_groups(self):
|
|
# Employee read employee-based group: ok
|
|
self.group_pigs.with_user(self.user_employee).read()
|
|
|
|
# Employee can create a group
|
|
self.env['mail.channel'].with_user(self.user_employee).create({'name': 'Test'})
|
|
|
|
# Employee update employee-based group: ok
|
|
self.group_pigs.with_user(self.user_employee).write({'name': 'modified'})
|
|
|
|
# Employee unlink employee-based group: ok
|
|
self.group_pigs.with_user(self.user_employee).unlink()
|
|
|
|
# Employee cannot read a private group
|
|
with self.assertRaises(AccessError):
|
|
self.group_private.with_user(self.user_employee).read()
|
|
|
|
# Employee cannot write on private
|
|
with self.assertRaises(AccessError):
|
|
self.group_private.with_user(self.user_employee).write({'name': 're-modified'})
|
|
|
|
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
|
|
def test_access_rights_followers_ko(self):
|
|
# self.group_private.name has been put in the cache during the setup as sudo
|
|
# It must therefore be removed from the cache in other to validate the fact user_portal can't read it.
|
|
self.group_private.invalidate_cache(['name'])
|
|
with self.assertRaises(AccessError):
|
|
self.group_private.with_user(self.user_portal).name
|
|
|
|
def test_access_rights_followers_portal(self):
|
|
# Do: Chell is added into Pigs members and browse it -> ok for messages, ko for partners (no read permission)
|
|
self.group_private.write({'channel_partner_ids': [(4, self.user_portal.partner_id.id)]})
|
|
chell_pigs = self.group_private.with_user(self.user_portal)
|
|
trigger_read = chell_pigs.name
|
|
for message in chell_pigs.message_ids:
|
|
trigger_read = message.subject
|
|
|
|
with self.assertRaises(AccessError):
|
|
chell_pigs.message_partner_ids
|
|
|
|
for partner in self.group_private.message_partner_ids:
|
|
if partner.id == self.user_portal.partner_id.id:
|
|
# Chell can read her own partner record
|
|
continue
|
|
with self.assertRaises(AccessError):
|
|
trigger_read = partner.with_user(self.user_portal).name
|
|
|
|
|
|
class TestChannelFeatures(TestMailCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestChannelFeatures, cls).setUpClass()
|
|
cls.test_channel = cls.env['mail.channel'].with_context(cls._test_context).create({
|
|
'name': 'Test',
|
|
'description': 'Description',
|
|
'alias_name': 'test',
|
|
'public': 'public',
|
|
})
|
|
cls.test_partner = cls.env['res.partner'].with_context(cls._test_context).create({
|
|
'name': 'Test Partner',
|
|
'email': 'test@example.com',
|
|
})
|
|
|
|
def _join_channel(self, channel, partners):
|
|
for partner in partners:
|
|
channel.write({'channel_last_seen_partner_ids': [(0, 0, {'partner_id': partner.id})]})
|
|
channel.invalidate_cache()
|
|
|
|
def _leave_channel(self, channel, partners):
|
|
for partner in partners:
|
|
channel._action_unfollow(partner)
|
|
|
|
def test_channel_listeners(self):
|
|
self.assertEqual(self.test_channel.message_channel_ids, self.test_channel)
|
|
self.assertEqual(self.test_channel.message_partner_ids, self.env['res.partner'])
|
|
self.assertEqual(self.test_channel.channel_partner_ids, self.env['res.partner'])
|
|
|
|
self._join_channel(self.test_channel, self.test_partner)
|
|
self.assertEqual(self.test_channel.message_channel_ids, self.test_channel)
|
|
self.assertEqual(self.test_channel.message_partner_ids, self.env['res.partner'])
|
|
self.assertEqual(self.test_channel.channel_partner_ids, self.test_partner)
|
|
|
|
self._leave_channel(self.test_channel, self.test_partner)
|
|
self.assertEqual(self.test_channel.message_channel_ids, self.test_channel)
|
|
self.assertEqual(self.test_channel.message_partner_ids, self.env['res.partner'])
|
|
self.assertEqual(self.test_channel.channel_partner_ids, self.env['res.partner'])
|
|
|
|
def test_channel_post_nofollow(self):
|
|
self.test_channel.message_post(body='Test', message_type='comment', subtype_xmlid='mail.mt_comment')
|
|
self.assertEqual(self.test_channel.message_channel_ids, self.test_channel)
|
|
self.assertEqual(self.test_channel.message_partner_ids, self.env['res.partner'])
|
|
|
|
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
|
|
def test_channel_mailing_list_recipients(self):
|
|
""" Posting a message on a mailing list should send one email to all recipients """
|
|
self.env['ir.config_parameter'].set_param('mail.catchall.domain', 'schlouby.fr')
|
|
self.test_channel.write({'email_send': True})
|
|
self.user_employee.write({'notification_type': 'email'})
|
|
|
|
# Subscribe an user without email. We shouldn't try to send email to them.
|
|
nomail = self.env['res.users'].create({
|
|
"login": "nomail",
|
|
"name": "No Mail",
|
|
"email": False,
|
|
"notification_type": "email",
|
|
})
|
|
self._join_channel(self.test_channel, self.user_employee.partner_id | self.test_partner | nomail.partner_id)
|
|
with self.mock_mail_gateway():
|
|
self.test_channel.message_post(body="Test", message_type='comment', subtype_xmlid='mail.mt_comment')
|
|
self.assertSentEmail(self.test_channel.env.user.partner_id, [self.partner_employee, self.test_partner])
|
|
|
|
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
|
|
def test_channel_chat_recipients(self):
|
|
""" Posting a message on a chat should not send emails """
|
|
self.env['ir.config_parameter'].set_param('mail.catchall.domain', 'schlouby.fr')
|
|
self.test_channel.write({'email_send': False})
|
|
self._join_channel(self.test_channel, self.user_employee.partner_id | self.test_partner)
|
|
with self.mock_mail_gateway():
|
|
self.test_channel.message_post(body="Test", message_type='comment', subtype_xmlid='mail.mt_comment')
|
|
self.assertNotSentEmail()
|
|
self.assertEqual(len(self._mails), 0)
|
|
|
|
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
|
|
def test_channel_classic_recipients(self):
|
|
""" Posting a message on a classic channel should work like classic post """
|
|
self.test_channel.write({'alias_name': False})
|
|
self.test_channel.message_subscribe([self.user_employee.partner_id.id, self.test_partner.id])
|
|
with self.mock_mail_gateway():
|
|
self.test_channel.message_post(body="Test", message_type='comment', subtype_xmlid='mail.mt_comment')
|
|
self.assertSentEmail(self.test_channel.env.user.partner_id, [self.test_partner])
|
|
|
|
@mute_logger('odoo.models.unlink')
|
|
def test_channel_auto_unsubscribe_archived_or_deleted_users(self):
|
|
"""Archiving / deleting a user should automatically unsubscribe related partner from private channels"""
|
|
test_channel_private = self.env['mail.channel'].with_context(self._test_context).create({
|
|
'name': 'Winden caves',
|
|
'description': 'Channel to travel through time',
|
|
'public': 'private',
|
|
})
|
|
test_channel_group = self.env['mail.channel'].with_context(self._test_context).create({
|
|
'name': 'Sic Mundus',
|
|
'public': 'groups',
|
|
'group_public_id': self.env.ref('base.group_user').id})
|
|
|
|
test_user = self.env['res.users'].create({
|
|
"login": "adam",
|
|
"name": "Jonas",
|
|
})
|
|
test_partner = test_user.partner_id
|
|
|
|
self._join_channel(self.test_channel, self.user_employee.partner_id | test_partner)
|
|
self._join_channel(test_channel_private, self.user_employee.partner_id | test_partner)
|
|
self._join_channel(test_channel_group, self.user_employee.partner_id | test_partner)
|
|
|
|
# Unsubscribe archived user from the private channels, but not from public channels
|
|
self.user_employee.active = False
|
|
self.assertEqual(test_channel_private.channel_partner_ids, test_partner)
|
|
self.assertEqual(test_channel_group.channel_partner_ids, test_partner)
|
|
self.assertEqual(self.test_channel.channel_partner_ids, self.user_employee.partner_id | test_partner)
|
|
|
|
# Unsubscribe deleted user from the private channels, but not from public channels
|
|
test_user.unlink()
|
|
self.assertEqual(test_channel_private.channel_partner_ids, self.env['res.partner'])
|
|
self.assertEqual(test_channel_group.channel_partner_ids, self.env['res.partner'])
|
|
self.assertEqual(self.test_channel.channel_partner_ids, self.user_employee.partner_id | test_partner)
|
|
|
|
|
|
@tagged('moderation')
|
|
class TestChannelModeration(TestMailCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super(TestChannelModeration, cls).setUpClass()
|
|
|
|
cls.channel_1 = cls.env['mail.channel'].create({
|
|
'name': 'Moderation_1',
|
|
'email_send': True,
|
|
'moderation': True,
|
|
'channel_partner_ids': [(4, cls.partner_employee.id)],
|
|
'moderator_ids': [(4, cls.user_employee.id)],
|
|
})
|
|
|
|
# ensure initial data
|
|
cls.user_employee_2 = mail_new_test_user(
|
|
cls.env, login='employee2', groups='base.group_user', company_id=cls.company_admin.id,
|
|
name='Enguerrand Employee2', notification_type='inbox', signature='--\nEnguerrand'
|
|
)
|
|
cls.partner_employee_2 = cls.user_employee_2.partner_id
|
|
|
|
cls.user_portal = cls._create_portal_user()
|
|
|
|
def test_moderator_consistency(self):
|
|
# moderators should be channel members
|
|
with self.assertRaises(ValidationError):
|
|
self.channel_1.write({'moderator_ids': [(4, self.user_admin.id)]})
|
|
|
|
# member -> moderator or
|
|
self.channel_1.write({'channel_partner_ids': [(4, self.partner_admin.id)]})
|
|
self.channel_1.write({'moderator_ids': [(4, self.user_admin.id)]})
|
|
|
|
# member -> moderator ko if no email
|
|
self.channel_1.write({'moderator_ids': [(3, self.partner_admin.id)]})
|
|
self.user_admin.write({'email': False})
|
|
with self.assertRaises(ValidationError):
|
|
self.channel_1.write({'moderator_ids': [(4, self.user_admin.id)]})
|
|
|
|
def test_moderation_consistency(self):
|
|
# moderation enabled channels are restricted to mailing lists
|
|
with self.assertRaises(ValidationError):
|
|
self.channel_1.write({'email_send': False})
|
|
|
|
# moderation enabled channels should always have moderators
|
|
with self.assertRaises(ValidationError):
|
|
self.channel_1.write({'moderator_ids': [(5, 0)]})
|
|
|
|
def test_moderation_count(self):
|
|
self.assertEqual(self.channel_1.moderation_count, 0)
|
|
self.channel_1.write({'moderation_ids': [
|
|
(0, 0, {'email': 'test0@example.com', 'status': 'allow'}),
|
|
(0, 0, {'email': 'test1@example.com', 'status': 'ban'})
|
|
]})
|
|
self.assertEqual(self.channel_1.moderation_count, 2)
|
|
|
|
@mute_logger('odoo.addons.mail.models.mail_channel', 'odoo.models.unlink')
|
|
def test_send_guidelines(self):
|
|
self.channel_1.write({'channel_partner_ids': [(4, self.partner_portal.id), (4, self.partner_admin.id)]})
|
|
self.channel_1._update_moderation_email([self.partner_admin.email], 'ban')
|
|
with self.mock_mail_gateway():
|
|
self.channel_1.with_user(self.user_employee).send_guidelines()
|
|
for mail in self._new_mails:
|
|
self.assertEqual(mail.author_id, self.partner_employee)
|
|
self.assertEqual(mail.subject, 'Guidelines of channel %s' % self.channel_1.name)
|
|
self.assertEqual(mail.state, 'outgoing')
|
|
self.assertEqual(mail.email_from, self.user_employee.company_id.catchall_formatted)
|
|
self.assertEqual(self._new_mails.mapped('recipient_ids'), self.partner_employee | self.partner_portal)
|
|
|
|
def test_send_guidelines_crash(self):
|
|
self.channel_1.write({
|
|
'channel_partner_ids': [(4, self.partner_admin.id)],
|
|
'moderator_ids': [(4, self.user_admin.id), (3, self.user_employee.id)]
|
|
})
|
|
with self.assertRaises(UserError):
|
|
self.channel_1.with_user(self.user_employee).send_guidelines()
|
|
|
|
def test_update_moderation_email(self):
|
|
self.channel_1.write({'moderation_ids': [
|
|
(0, 0, {'email': 'test0@example.com', 'status': 'allow'}),
|
|
(0, 0, {'email': 'test1@example.com', 'status': 'ban'})
|
|
]})
|
|
self.channel_1._update_moderation_email(['test0@example.com', 'test3@example.com'], 'ban')
|
|
self.assertEqual(len(self.channel_1.moderation_ids), 3)
|
|
self.assertTrue(all(status == 'ban' for status in self.channel_1.moderation_ids.mapped('status')))
|
|
|
|
def test_moderation_reset(self):
|
|
self.channel_2 = self.env['mail.channel'].create({
|
|
'name': 'Moderation_1',
|
|
'email_send': True,
|
|
'moderation': True,
|
|
'channel_partner_ids': [(4, self.partner_employee.id)],
|
|
'moderator_ids': [(4, self.user_employee.id)],
|
|
})
|
|
|
|
self.msg_c1_1 = self._add_messages(self.channel_1, 'Body11', author=self.partner_admin, moderation_status='accepted')
|
|
self.msg_c1_2 = self._add_messages(self.channel_1, 'Body12', author=self.partner_admin, moderation_status='pending_moderation')
|
|
self.msg_c2_1 = self._add_messages(self.channel_2, 'Body21', author=self.partner_admin, moderation_status='pending_moderation')
|
|
|
|
self.assertEqual(self.env['mail.message'].search_count([
|
|
('moderation_status', '=', 'pending_moderation'),
|
|
('model', '=', 'mail.channel'), ('res_id', '=', self.channel_1.id)
|
|
]), 1)
|
|
self.channel_1.write({'moderation': False})
|
|
self.assertEqual(self.env['mail.message'].search_count([
|
|
('moderation_status', '=', 'pending_moderation'),
|
|
('model', '=', 'mail.channel'), ('res_id', '=', self.channel_1.id)
|
|
]), 0)
|
|
self.assertEqual(self.env['mail.message'].search_count([
|
|
('moderation_status', '=', 'pending_moderation'),
|
|
('model', '=', 'mail.channel'), ('res_id', '=', self.channel_2.id)
|
|
]), 1)
|
|
self.channel_2.write({'moderation': False})
|
|
self.assertEqual(self.env['mail.message'].search_count([
|
|
('moderation_status', '=', 'pending_moderation'),
|
|
('model', '=', 'mail.channel'), ('res_id', '=', self.channel_2.id)
|
|
]), 0)
|
|
|
|
@mute_logger('odoo.models.unlink')
|
|
def test_message_post(self):
|
|
email1 = 'test0@example.com'
|
|
email2 = 'test1@example.com'
|
|
|
|
self.channel_1._update_moderation_email([email1], 'ban')
|
|
self.channel_1._update_moderation_email([email2], 'allow')
|
|
|
|
msg_admin = self.channel_1.message_post(message_type='email', subtype_xmlid='mail.mt_comment', author_id=self.partner_admin.id)
|
|
msg_moderator = self.channel_1.message_post(message_type='comment', subtype_xmlid='mail.mt_comment', author_id=self.partner_employee.id)
|
|
msg_email1 = self.channel_1.message_post(message_type='comment', subtype_xmlid='mail.mt_comment', email_from=formataddr(("MyName", email1)))
|
|
msg_email2 = self.channel_1.message_post(message_type='email', subtype_xmlid='mail.mt_comment', email_from=email2)
|
|
msg_notif = self.channel_1.message_post()
|
|
|
|
messages = self.env['mail.message'].search([('model', '=', 'mail.channel'), ('res_id', '=', self.channel_1.id)])
|
|
pending_messages = messages.filtered(lambda m: m.moderation_status == 'pending_moderation')
|
|
accepted_messages = messages.filtered(lambda m: m.moderation_status == 'accepted')
|
|
|
|
self.assertFalse(msg_email1)
|
|
self.assertEqual(msg_admin, pending_messages)
|
|
self.assertEqual(accepted_messages, msg_moderator | msg_email2 | msg_notif)
|
|
self.assertFalse(msg_admin.channel_ids)
|
|
self.assertEqual(msg_email2.channel_ids, self.channel_1)
|
|
|
|
def test_user_is_moderator(self):
|
|
self.assertTrue(self.user_employee.is_moderator)
|
|
self.assertFalse(self.user_employee_2.is_moderator)
|
|
self.channel_1.write({
|
|
'channel_partner_ids': [(4, self.partner_employee_2.id)],
|
|
'moderator_ids': [(4, self.user_employee_2.id)],
|
|
})
|
|
self.assertTrue(self.user_employee_2.is_moderator)
|
|
|
|
def test_user_moderation_counter(self):
|
|
self._add_messages(self.channel_1, 'B', moderation_status='pending_moderation', author=self.partner_employee_2)
|
|
self._add_messages(self.channel_1, 'B', moderation_status='accepted', author=self.partner_employee_2)
|
|
self._add_messages(self.channel_1, 'B', moderation_status='accepted', author=self.partner_employee)
|
|
self._add_messages(self.channel_1, 'B', moderation_status='pending_moderation', author=self.partner_employee)
|
|
self._add_messages(self.channel_1, 'B', moderation_status='accepted', author=self.partner_employee)
|
|
|
|
self.assertEqual(self.user_employee.moderation_counter, 2)
|
|
self.assertEqual(self.user_employee_2.moderation_counter, 0)
|
|
|
|
self.channel_1.write({
|
|
'channel_partner_ids': [(4, self.partner_employee_2.id)],
|
|
'moderator_ids': [(4, self.user_employee_2.id)]
|
|
})
|
|
self.assertEqual(self.user_employee.moderation_counter, 2)
|
|
self.assertEqual(self.user_employee_2.moderation_counter, 0)
|