Files
odoo_source/addons/mail/models/mail_channel_partner.py
T
thcl-odoo 3ce63a0041 [FIX] mail: members can be added through channel settings' form view
Current behavior :
When we try to add a member through channel's settings by clicking on "Add a line", a blank line is correctly added but can't be edited.

Steps to reproduce :
- Install Discuss
- Go to channel settings
- Try to add a member by clicking on "Add a line"

Reason :
Since commit 89eef4cdd419cc23cfb8d3e0af7a7115119549c2 , 'partner_id' field as been set to readonly because it doesn't make sense to edit the partner_id of a channel. However, this prevents the possibility of adding a member.
( diff : https://github.com/odoo/odoo/pull/75496/files#diff-d02eca5ffaa6b9765330a905661cd6cb917b92d851b473d459ee011bb2c5be3cR17 ).

OPW-2677329

closes odoo/odoo#80326

X-original-commit: 9536f8ec556670f67939fdee825630e06d5b4808
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
2021-11-24 14:04:32 +00:00

199 lines
11 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from werkzeug.exceptions import NotFound
from odoo import api, fields, models, _
from odoo.exceptions import AccessError
from odoo.osv import expression
class ChannelPartner(models.Model):
_name = 'mail.channel.partner'
_description = 'Listeners of a Channel'
_table = 'mail_channel_partner'
# identity
partner_id = fields.Many2one('res.partner', string='Recipient', ondelete='cascade', index=True)
guest_id = fields.Many2one(string="Guest", comodel_name='mail.guest', ondelete='cascade', readonly=True, index=True)
partner_email = fields.Char('Email', related='partner_id.email', readonly=False)
# channel
channel_id = fields.Many2one('mail.channel', string='Channel', ondelete='cascade', readonly=True, required=True)
# state
custom_channel_name = fields.Char('Custom channel name')
fetched_message_id = fields.Many2one('mail.message', string='Last Fetched')
seen_message_id = fields.Many2one('mail.message', string='Last Seen')
fold_state = fields.Selection([('open', 'Open'), ('folded', 'Folded'), ('closed', 'Closed')], string='Conversation Fold State', default='open')
is_minimized = fields.Boolean("Conversation is minimized")
is_pinned = fields.Boolean("Is pinned on the interface", default=True)
last_interest_dt = fields.Datetime("Last Interest", default=fields.Datetime.now, help="Contains the date and time of the last interesting event that happened in this channel for this partner. This includes: creating, joining, pinning, and new message posted.")
# RTC
rtc_session_ids = fields.One2many(string="RTC Sessions", comodel_name='mail.channel.rtc.session', inverse_name='channel_partner_id')
rtc_inviting_session_id = fields.Many2one('mail.channel.rtc.session', string='Ringing session')
def name_get(self):
return [(record.id, record.partner_id.name or record.guest_id.name) for record in self]
def _name_search(self, name='', args=None, operator='ilike', limit=100, name_get_uid=None):
domain = [[('partner_id', operator, name)], [('guest_id', operator, name)]]
if '!' in operator or 'not' in operator:
domain = expression.AND(domain)
else:
domain = expression.OR(domain)
return self._search(expression.AND([domain, args]), limit=limit, access_rights_uid=name_get_uid)
def init(self):
self.env.cr.execute("CREATE UNIQUE INDEX IF NOT EXISTS mail_channel_partner_partner_unique ON %s (channel_id, partner_id) WHERE partner_id IS NOT NULL" % self._table)
self.env.cr.execute("CREATE UNIQUE INDEX IF NOT EXISTS mail_channel_partner_guest_unique ON %s (channel_id, guest_id) WHERE guest_id IS NOT NULL" % self._table)
_sql_constraints = [
("partner_or_guest_exists", "CHECK((partner_id IS NOT NULL AND guest_id IS NULL) OR (partner_id IS NULL AND guest_id IS NOT NULL))", "A channel member must be a partner or a guest."),
]
@api.model_create_multi
def create(self, vals_list):
"""Similar access rule as the access rule of the mail channel.
It can not be implemented in XML, because when the record will be created, the
partner will be added in the channel and the security rule will always authorize
the creation.
"""
if not self.env.is_admin():
for vals in vals_list:
if 'channel_id' in vals:
channel_id = self.env['mail.channel'].browse(vals['channel_id'])
if not channel_id._can_invite(vals.get('partner_id')):
raise AccessError(_('This user can not be added in this channel'))
return super(ChannelPartner, self).create(vals_list)
def write(self, vals):
for channel_partner in self:
for field_name in {'channel_id', 'partner_id', 'guest_id'}:
if field_name in vals and vals[field_name] != channel_partner[field_name].id:
raise AccessError(_('You can not write on %(field_name)s.', field_name=field_name))
return super(ChannelPartner, self).write(vals)
def unlink(self):
self.sudo().rtc_session_ids.unlink()
return super().unlink()
@api.model
def _get_as_sudo_from_request_or_raise(self, request, channel_id):
channel_partner = self._get_as_sudo_from_request(request=request, channel_id=channel_id)
if not channel_partner:
raise NotFound()
return channel_partner
@api.model
def _get_as_sudo_from_request(self, request, channel_id):
""" Seeks a channel partner matching the provided `channel_id` and the
current user or guest.
:param channel_id: The id of the channel of which the user/guest is
expected to be member.
:type channel_id: int
:return: A record set containing the channel partner if found, or an
empty record set otherwise. In case of guest, the record is returned
with the 'guest' record in the context.
:rtype: mail.channel.partner
"""
if request.session.uid:
return self.env['mail.channel.partner'].sudo().search([('channel_id', '=', channel_id), ('partner_id', '=', self.env.user.partner_id.id)], limit=1)
guest = self.env['mail.guest']._get_guest_from_request(request)
if guest:
return guest.env['mail.channel.partner'].sudo().search([('channel_id', '=', channel_id), ('guest_id', '=', guest.id)], limit=1)
return self.env['mail.channel.partner'].sudo()
# --------------------------------------------------------------------------
# RTC (voice/video)
# --------------------------------------------------------------------------
def _rtc_join_call(self, check_rtc_session_ids=None):
self.ensure_one()
check_rtc_session_ids = (check_rtc_session_ids or []) + self.rtc_session_ids.ids
self.channel_id._rtc_cancel_invitations(partner_ids=self.partner_id.ids, guest_ids=self.guest_id.ids)
self.rtc_session_ids.unlink()
rtc_session = self.env['mail.channel.rtc.session'].create({'channel_partner_id': self.id})
current_rtc_sessions, outdated_rtc_sessions = self._rtc_sync_sessions(check_rtc_session_ids=check_rtc_session_ids)
res = {
'iceServers': self.env['mail.ice.server']._get_ice_servers() or False,
'rtcSessions': [
('insert', [rtc_session_sudo._mail_rtc_session_format() for rtc_session_sudo in current_rtc_sessions]),
('insert-and-unlink', [{'id': missing_rtc_session_sudo.id} for missing_rtc_session_sudo in outdated_rtc_sessions]),
],
'sessionId': rtc_session.id,
}
if len(self.channel_id.rtc_session_ids) == 1 and self.channel_id.channel_type in {'chat', 'group'}:
self.channel_id.message_post(body=_("%s started a live conference", self.partner_id.name or self.guest_id.name), message_type='notification')
invited_partners, invited_guests = self._rtc_invite_members()
if invited_guests:
res['invitedGuests'] = [('insert', [{'id': guest.id, 'name': guest.name} for guest in invited_guests])]
if invited_partners:
res['invitedPartners'] = [('insert', [{'id': partner.id, 'name': partner.name} for partner in invited_partners])]
return res
def _rtc_leave_call(self):
self.ensure_one()
if self.rtc_session_ids:
self.rtc_session_ids.unlink()
else:
return self.channel_id._rtc_cancel_invitations(partner_ids=self.partner_id.ids, guest_ids=self.guest_id.ids)
def _rtc_sync_sessions(self, check_rtc_session_ids=None):
"""Synchronize the RTC sessions for self channel partner.
- Inactive sessions of the channel are deleted.
- Current sessions are returned.
- Sessions given in check_rtc_session_ids that no longer exists
are returned as non-existing.
:param list check_rtc_session_ids: list of the ids of the sessions to check
:returns tuple: (current_rtc_sessions, outdated_rtc_sessions)
"""
self.ensure_one()
self.channel_id.rtc_session_ids._delete_inactive_rtc_sessions()
check_rtc_sessions = self.env['mail.channel.rtc.session'].browse([int(check_rtc_session_id) for check_rtc_session_id in (check_rtc_session_ids or [])])
return self.channel_id.rtc_session_ids, check_rtc_sessions - self.channel_id.rtc_session_ids
def _rtc_invite_members(self, partner_ids=None, guest_ids=None):
""" Sends invitations to join the RTC call to all connected members of the thread who are not already invited.
:param list partner_ids: list of the partner ids to invite
:param list guest_ids: list of the guest ids to invite
if either partner_ids or guest_ids is set, only the specified ids will be invited.
"""
self.ensure_one()
channel_partner_domain = [
('channel_id', '=', self.channel_id.id),
('rtc_inviting_session_id', '=', False),
('rtc_session_ids', '=', False),
]
if partner_ids or guest_ids:
channel_partner_domain = expression.AND([channel_partner_domain, [
'|',
('partner_id', 'in', partner_ids or []),
('guest_id', 'in', guest_ids or []),
]])
invitation_notifications = []
invited_partners = self.env['res.partner']
invited_guests = self.env['mail.guest']
for member in self.env['mail.channel.partner'].search(channel_partner_domain):
member.rtc_inviting_session_id = self.rtc_session_ids.id
if member.partner_id:
invited_partners |= member.partner_id
target = member.partner_id
else:
invited_guests |= member.guest_id
target = member.guest_id
invitation_notifications.append((target, 'mail.channel/insert', {
'id': self.channel_id.id,
'rtcInvitingSession': [('insert', self.rtc_session_ids._mail_rtc_session_format())],
}))
self.env['bus.bus']._sendmany(invitation_notifications)
if invited_guests or invited_partners:
channel_data = {'id': self.channel_id.id}
if invited_guests:
channel_data['invitedGuests'] = [('insert', [{'id': guest.id, 'name': guest.name} for guest in invited_guests])]
if invited_partners:
channel_data['invitedPartners'] = [('insert', [{'id': partner.id, 'name': partner.name} for partner in invited_partners])]
self.env['bus.bus']._sendone(self.channel_id, 'mail.channel/insert', channel_data)
return invited_partners, invited_guests