From 9efd9463eb90e8ab81042a4a6f231d0ef4db7be1 Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Wed, 22 Sep 2021 09:24:27 +0000 Subject: [PATCH] [IMP] mail: allow creating channels on first visit from token MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit allows anyone to create a new mail.channel by visiting the URL /chat/[/], providing that the mail.chat_from_token system parameter is set. This is useful in some situations where new channels need to be created quickly as needed by anyone without special permissions (eg, during the Odoo experience) task-2648580 closes odoo/odoo#77010 X-original-commit: 56ddc073213a148579dd72f4a9336bc4d6a11f22 Signed-off-by: Sébastien Theys (seb) Signed-off-by: Samuel Degueldre --- addons/mail/controllers/discuss.py | 26 ++++++++++++++++++++++++++ addons/mail/models/mail_channel.py | 7 ++++++- 2 files changed, 32 insertions(+), 1 deletion(-) 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')