[REF] mail: do not support listener channels when posting a message

RATIONALE

Channel model is a mail.thread enabled model behaving strangely with followers,
notifications and discuss. Its code should however be simplified to be more
self contained and avoid unwanted side effects on other models.

PURPOSE

Remove channel ability to follow records as it mainly adds noise without a lot
of added value. Simplify channel notification flow by using directly members
and not a delegation through a channel self-following trick. Remove followers
being channels and posting with added listeners being channels.

SPECIFICATIONS

In this commit we remove support of ``channel_ids`` parameter when posting
a message. This means we do not support notifying channels as side-part
of notification mechanism on a document. Either we post on a channel and
its members are notified, either we post on a business documents and its
followers are notified. There is no possibility to add channels as being
notified when posting a message.

This is done as a followup of removing followers being channel-based and
prepares ground to make channel use model / res_id as all other documents
inheriting form mail.thread.

LINKS

Task ID-2070632 (main task)
Task ID-2419762 (followup task)
COM PR odoo/odoo#62859
ENT PR odoo/enterprise#15172
UPG PR odoo/upgrade#2005
This commit is contained in:
Thibault Delavallée
2021-03-17 18:16:20 +00:00
parent 4cb1f56aba
commit df013e76bc
3 changed files with 9 additions and 102 deletions
+9 -62
View File
@@ -1732,7 +1732,7 @@ class MailThread(models.AbstractModel):
def message_post(self, *,
body='', subject=None, message_type='notification',
email_from=None, author_id=None, parent_id=False,
subtype_xmlid=None, subtype_id=False, partner_ids=None, channel_ids=None,
subtype_xmlid=None, subtype_id=False, partner_ids=None,
attachments=None, attachment_ids=None,
add_sign=True, record_name=False,
**kwargs):
@@ -1748,8 +1748,6 @@ class MailThread(models.AbstractModel):
followers notification mechanism;
:param list(int) partner_ids: partner_ids to notify in addition to partners
computed based on subtype / followers matching;
:param list(int) channel_ids: channel_ids to notify in addition to partners
computed based on subtype / followers matching;
:param list(tuple(str,str), tuple(str,str, dict) or int) attachments : list of attachment tuples in the form
``(name,content)`` or ``(name,content, info)``, where content is NOT base64 encoded
:param list id attachment_ids: list of existing attachement to link to this message
@@ -1767,15 +1765,16 @@ class MailThread(models.AbstractModel):
# preliminary value safety check
partner_ids = set(partner_ids or [])
channel_ids = set(channel_ids or [])
if self._name == 'mail.thread' or not self.id or message_type == 'user_notification':
raise ValueError(_('Posting a message should be done on a business document. Use message_notify to send a notification to an user.'))
if 'channel_ids' in kwargs:
raise ValueError(_("Posting a message with channels as listeners is not supported since Odoo 14.3+. Please update code accordingly."))
if 'model' in msg_kwargs or 'res_id' in msg_kwargs:
raise ValueError(_("message_post does not support model and res_id parameters anymore. Please call message_post on record."))
if 'subtype' in kwargs:
raise ValueError(_("message_post does not support subtype parameter anymore. Please give a valid subtype_id or subtype_xmlid value instead."))
if any(not isinstance(pc_id, int) for pc_id in partner_ids | channel_ids):
raise ValueError(_('message_post partner_ids and channel_ids must be integer list, not commands.'))
if any(not isinstance(pc_id, int) for pc_id in partner_ids):
raise ValueError(_('message_post partner_ids and must be integer list, not commands.'))
self = self._fallback_lang() # add lang to context imediatly since it will be usefull in various flows latter.
@@ -1826,7 +1825,6 @@ class MailThread(models.AbstractModel):
'parent_id': parent_id,
'subtype_id': subtype_id,
'partner_ids': partner_ids,
'channel_ids': channel_ids,
'add_sign': add_sign,
'record_name': record_name,
})
@@ -2051,7 +2049,6 @@ class MailThread(models.AbstractModel):
for x in ('from', 'to', 'cc', 'canned_response_ids'):
create_values.pop(x, None)
create_values['partner_ids'] = [Command.link(pid) for pid in create_values.get('partner_ids', [])]
create_values['channel_ids'] = [Command.link(cid) for cid in create_values.get('channel_ids', [])]
create_values_list.append(create_values)
if 'default_child_ids' in self._context:
ctx = {key: val for key, val in self._context.items() if key != 'default_child_ids'}
@@ -2088,11 +2085,6 @@ class MailThread(models.AbstractModel):
if not rdata:
return False
cids = msg_vals.get('channel_ids', []) if msg_vals else message.channel_ids.ids
channel_ids = [r['id'] for r in rdata['channels'] if r['id'] not in cids]
if channel_ids:
message.write({'channel_ids': [Command.set(channel_ids)]})
self._notify_record_by_inbox(message, rdata, msg_vals=msg_vals, **kwargs)
if notify_by_email:
self._notify_record_by_email(message, rdata, msg_vals=msg_vals, **kwargs)
@@ -2103,14 +2095,12 @@ class MailThread(models.AbstractModel):
""" Notification method: inbox. Do two main things
* create an inbox notification for users;
* create channel / message link (channel_ids field of mail.message);
* send bus notifications;
TDE/XDO TODO: flag rdata directly, with for example r['notif'] = 'ocn_client' and r['needaction']=False
and correctly override notify_recipients
"""
channel_ids = [r['id'] for r in recipients_data['channels']]
bus_notifications = []
inbox_pids = [r['id'] for r in recipients_data['partners'] if r['notif'] == 'inbox']
if inbox_pids:
notif_create_values = [{
@@ -2121,24 +2111,9 @@ class MailThread(models.AbstractModel):
} for pid in inbox_pids]
self.env['mail.notification'].sudo().create(notif_create_values)
bus_notifications = []
if inbox_pids or channel_ids:
message_format_values = False
if inbox_pids:
message_format_values = message.message_format()[0]
for partner_id in inbox_pids:
bus_notifications.append([(self._cr.dbname, 'ir.needaction', partner_id), dict(message_format_values)])
if channel_ids:
channels = self.env['mail.channel'].sudo().browse(channel_ids)
bus_notifications += channels._channel_message_notifications(message, message_format_values)
# Message from mailing channel should not make a notification in Odoo for users
# with notification "Handled by Email", but web client should receive the message.
# To do so, message is still sent from longpolling, but channel is marked as read
# in order to remove notification.
for channel in channels.filtered(lambda c: c.email_send):
users = channel.channel_partner_ids.mapped('user_ids')
for user in users.filtered(lambda u: u.notification_type == 'email'):
channel.with_user(user).channel_seen(message.id)
message_format_values = message.message_format()[0]
for partner_id in inbox_pids:
bus_notifications.append([(self._cr.dbname, 'ir.needaction', partner_id), dict(message_format_values)])
if bus_notifications:
self.env['bus.bus'].sudo().sendmany(bus_notifications)
@@ -2375,7 +2350,6 @@ class MailThread(models.AbstractModel):
msg_sudo = message.sudo()
# get values from msg_vals or from message if msg_vals doen't exists
pids = msg_vals.get('partner_ids', []) if msg_vals else msg_sudo.partner_ids.ids
cids = msg_vals.get('channel_ids', []) if msg_vals else msg_sudo.channel_ids.ids
message_type = msg_vals.get('message_type') if msg_vals else msg_sudo.message_type
subtype_id = msg_vals.get('subtype_id') if msg_vals else msg_sudo.subtype_id.id
# is it possible to have record but no subtype_id ?
@@ -2404,33 +2378,6 @@ class MailThread(models.AbstractModel):
else: # has no user, is therefore customer
recipient_data['partners'].append(dict(pdata, notif=notif if notif else 'email', type='customer'))
# add partner ids in email channels
email_cids = [r['id'] for r in recipient_data['channels'] if r['notif'] == 'email']
if email_cids:
# we are doing a similar search in ocn_client
# Could be interesting to make everything in a single query.
# ocn_client: (searching all partners linked to channels of type chat).
# here : (searching all partners linked to channels with notif email if email is not the author one)
# TDE FIXME: use email_sanitized
email_from = msg_vals.get('email_from') or message.email_from
email_from = self.env['res.partner']._parse_partner_name(email_from)[1]
exept_partner = [r['id'] for r in recipient_data['partners']]
if author_id:
exept_partner.append(author_id)
sql_query = """ select distinct on (p.id) p.id from res_partner p
left join mail_channel_partner mcp on p.id = mcp.partner_id
left join mail_channel c on c.id = mcp.channel_id
left join res_users u on p.id = u.partner_id
where (u.notification_type != 'inbox' or u.id is null)
and (p.email != ANY(%s) or p.email is null)
and c.id = ANY(%s)
and p.id != ANY(%s)"""
self.env.cr.execute(sql_query, (([email_from], ), (email_cids, ), (exept_partner, )))
for partner_id in self._cr.fetchall():
# ocn_client: will add partners to recipient recipient_data. more ocn notifications. We neeed to filter them maybe
recipient_data['partners'].append({'id': partner_id[0], 'share': True, 'active': True, 'notif': 'email', 'type': 'channel_email', 'groups': []})
return recipient_data
@api.model
@@ -969,15 +969,10 @@ MockServer.include({
moderation_status = 'pending_moderation';
}
}
let channel_ids = [];
if (moderation_status === 'accepted') {
channel_ids = [[4, channel.id]];
}
const messageId = this._mockMailThreadMessagePost(
'mail.channel',
[id],
Object.assign(kwargs, {
channel_ids,
message_type,
moderation_status,
}),
@@ -908,39 +908,6 @@ class TestMailHeavyPerformancePost(BaseMailPerformance):
'name': 'partner',
'email': 'partner@example.com',
})
# channels user/partner
self.partner_channel_inbox = self.env['res.partner'].with_context(self._quick_create_ctx).create({
'name': 'partner_channel_inbox',
'email': 'partner_channel_inbox@example.com',
})
self.partner_channel_email = self.env['res.partner'].with_context(self._quick_create_ctx).create({
'name': 'partner_channel_email',
'email': 'partner_channel_email@example.com',
})
self.user_channel_inbox = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'user_channel_inbox',
'login': 'user_channel_inbox',
'email': 'user_channel_inbox@example.com',
'notification_type': 'inbox',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
self.user_channel_email = self.env['res.users'].with_context(self._quick_create_ctx).create({
'name': 'user_channel_email',
'login': 'user_channel_email',
'email': 'user_channel_email@example.com',
'notification_type': 'inbox',
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])],
})
# channels
self.channel_inbox = self.env['mail.channel'].with_context(self._quick_create_ctx).create({
'name': 'channel_inbox',
'channel_partner_ids': [(4, self.partner_channel_inbox.id), (4, self.user_channel_inbox.partner_id.id)]
})
self.channel_email = self.env['mail.channel'].with_context(self._quick_create_ctx).create({
'name': 'channel_email',
'email_send': True,
'channel_partner_ids': [(4, self.partner_channel_email.id), (4, self.user_channel_email.partner_id.id)]
})
self.vals = [{
'datas': base64.b64encode(bytes("attachement content %s" % i, 'utf-8')),
'name': 'fileText_test%s.txt' % i,
@@ -957,7 +924,6 @@ class TestMailHeavyPerformancePost(BaseMailPerformance):
def test_complete_message_post(self):
# aims to cover as much features of message_post as possible
partner_ids = [self.user_inbox.partner_id.id, self.user_email.partner_id.id, self.partner.id]
channel_ids = [self.channel_inbox.id, self.channel_email.id]
record = self.record.with_user(self.env.user)
attachements = [ # not linear on number of attachements
('attach tuple 1', "attachement tupple content 1"),
@@ -974,7 +940,6 @@ class TestMailHeavyPerformancePost(BaseMailPerformance):
message_type='notification',
subtype_xmlid=None,
partner_ids=partner_ids,
channel_ids=channel_ids,
parent_id=False,
attachments=attachements,
attachment_ids=attachement_ids,