diff --git a/addons/mail/controllers/discuss.py b/addons/mail/controllers/discuss.py index ddfc33fc59b..b6319eec329 100644 --- a/addons/mail/controllers/discuss.py +++ b/addons/mail/controllers/discuss.py @@ -4,6 +4,8 @@ import json from collections import defaultdict from datetime import datetime, timedelta +from psycopg2 import IntegrityError +from psycopg2.errorcodes import UNIQUE_VIOLATION from odoo import http from odoo.http import request @@ -19,6 +21,30 @@ class DiscussController(http.Controller): # Public Pages # -------------------------------------------------------------------------- + @http.route([ + '/chat/', + '/chat//', + ], methods=['GET'], type='http', auth='public') + def discuss_channel_from_token(self, create_token, channel_name=None, **kwargs): + if not request.env['ir.config_parameter'].sudo().get_param('mail.chat_from_token'): + raise NotFound() + channel_sudo = request.env['mail.channel'].sudo().search([('uuid', '=', create_token)]) + if not channel_sudo: + try: + channel_sudo = channel_sudo.create({ + 'uuid': create_token, + 'name': channel_name or create_token, + 'public': 'public', + }) + except IntegrityError as e: + if e.pgcode != UNIQUE_VIOLATION: + raise + # concurrent insert attempt: another request created the channel. + # commit the current transaction and get the channel. + request.env.cr.commit() + channel_sudo = channel_sudo.search([('uuid', '=', create_token)]) + return request.redirect(f'/chat/{channel_sudo.id}/{channel_sudo.uuid}') + @http.route('/chat//', methods=['GET'], type='http', auth='public') def discuss_channel_invitation(self, channel_id, invitation_token, **kwargs): channel_sudo = request.env['mail.channel'].browse(int(channel_id)).sudo().exists() diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index 2c6621ac356..0b094a9502d 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -79,7 +79,7 @@ class Channel(models.Model): "Note that they will be able to manage their subscription manually " "if necessary.") # access - uuid = fields.Char('UUID', size=50, index=True, default=_generate_random_token, copy=False) + uuid = fields.Char('UUID', size=50, default=_generate_random_token, copy=False) public = fields.Selection([ ('public', 'Everyone'), ('private', 'Invited people only'), @@ -88,6 +88,11 @@ class Channel(models.Model): help='This group is visible by non members. Invisible groups can add members through the invite button.') group_public_id = fields.Many2one('res.groups', string='Authorized Group', default=lambda self: self.env.ref('base.group_user')) + + _sql_constraints = [ + ('uuid_unique', 'UNIQUE(uuid)', 'The channel UUID must be unique'), + ] + # COMPUTE / INVERSE @api.depends('channel_type')