diff --git a/addons/bus/static/src/services/bus_service.js b/addons/bus/static/src/services/bus_service.js index 53b8e28c146..94dc92cebad 100644 --- a/addons/bus/static/src/services/bus_service.js +++ b/addons/bus/static/src/services/bus_service.js @@ -32,6 +32,7 @@ export const busService = { let isUsingSharedWorker = browser.SharedWorker && !isIosApp(); const startTs = new Date().getTime(); const connectionInitializedDeferred = new Deferred(); + let context = {}; /** * Send a message to the worker. @@ -170,6 +171,23 @@ export const busService = { send("start"); isActive = true; }, + get context() { + return context; + }, + /** + * Update the context to be sent with every websocket + * message. + * + * @param {object} newContext + */ + async updateContext(newContext) { + context = newContext; + if (!worker) { + startWorker(); + await connectionInitializedDeferred; + } + send("update_context", context); + }, deleteChannel: (channel) => send("delete_channel", channel), forceUpdateChannels: () => send("force_update_channels"), trigger: bus.trigger.bind(bus), diff --git a/addons/bus/static/src/workers/websocket_worker.js b/addons/bus/static/src/workers/websocket_worker.js index 5df6a4affc9..8ea2b4b212e 100644 --- a/addons/bus/static/src/workers/websocket_worker.js +++ b/addons/bus/static/src/workers/websocket_worker.js @@ -11,7 +11,7 @@ import { debounce } from "@bus/workers/websocket_worker_utils"; /** * Type of action that can be sent from the client to the worker. * - * @typedef {'add_channel' | 'delete_channel' | 'force_update_channels' | 'initialize_connection' | 'send' | 'leave' | 'stop' | 'start' } WorkerAction + * @typedef {'add_channel' | 'delete_channel' | 'force_update_channels' | 'initialize_connection' | 'send' | 'leave' | 'stop' | 'start' | 'update_context'} WorkerAction */ export const WEBSOCKET_CLOSE_CODES = Object.freeze({ @@ -34,7 +34,7 @@ export const WEBSOCKET_CLOSE_CODES = Object.freeze({ }); // Should be incremented on every worker update in order to force // update of the worker in browser cache. -export const WORKER_VERSION = "1.0.5"; +export const WORKER_VERSION = "1.0.6"; const INITIAL_RECONNECT_DELAY = 1000; const MAXIMUM_RECONNECT_DELAY = 60000; @@ -61,6 +61,8 @@ export class WebsocketWorker { this.lastChannelSubscription = null; this.lastNotificationId = 0; this.messageWaitQueue = []; + // Context to be attached to each websocket request + this._context = {}; this._forceUpdateChannels = debounce(this._forceUpdateChannels, 300, true); this._onWebsocketClose = this._onWebsocketClose.bind(this); @@ -143,6 +145,9 @@ export class WebsocketWorker { return this._forceUpdateChannels(); case "initialize_connection": return this._initializeConnection(client, data); + case "update_context": + this._context = data; + break; } } @@ -392,9 +397,12 @@ export class WebsocketWorker { * If the websocket is not open, enqueue the message and send it * upon the next reconnection. * - * @param {any} message Message to send to the server. + * @param {{event_name: string, data: any }} message Message to send to the server. */ _sendToServer(message) { + if (Object.keys(this._context).length > 0) { + message.context = this._context; + } const payload = JSON.stringify(message); if (!this._isWebsocketConnected()) { this.messageWaitQueue.push(payload); diff --git a/addons/bus/websocket.py b/addons/bus/websocket.py index d4a449aa46c..91b628f4a96 100644 --- a/addons/bus/websocket.py +++ b/addons/bus/websocket.py @@ -756,6 +756,7 @@ class WebsocketRequest: f'Invalid JSON data, {exc.args[0]}' ) from exc data = jsonrequest.get('data') + context = jsonrequest.get('context') self.session = self._get_session() try: @@ -770,11 +771,11 @@ class WebsocketRequest: self.env = api.Environment(cr, self.session.uid, self.session.context) threading.current_thread().uid = self.env.uid service_model.retrying( - functools.partial(self._serve_ir_websocket, event_name, data), + functools.partial(self._serve_ir_websocket, event_name, data, context), self.env, ) - def _serve_ir_websocket(self, event_name, data): + def _serve_ir_websocket(self, event_name, data, context=None): """ Delegate most of the processing to the ir.websocket model which is extensible by applications. Directly call the @@ -783,6 +784,8 @@ class WebsocketRequest: """ ir_websocket = self.env['ir.websocket'] ir_websocket._authenticate() + if context: + self.update_context(**context) if event_name == 'subscribe': ir_websocket._subscribe(data) if event_name == 'update_presence': @@ -800,6 +803,14 @@ class WebsocketRequest: """ Request.update_env(self, user, context, su) + def update_context(self, **overrides): + """ + Override the environment context of the current request with the + values of ``overrides``. To replace the entire context, please + use :meth:`~update_env` instead. + """ + self.update_env(context=dict(self.env.context, **overrides)) + class WebsocketConnectionHandler: SUPPORTED_VERSIONS = {'13'} diff --git a/addons/mail/controllers/attachment.py b/addons/mail/controllers/attachment.py index 4113c051041..b84c969baf1 100644 --- a/addons/mail/controllers/attachment.py +++ b/addons/mail/controllers/attachment.py @@ -6,10 +6,12 @@ from odoo import _, http from odoo.exceptions import AccessError from odoo.http import request from odoo.tools import consteq +from ..models.discuss.mail_guest import add_guest_to_context class AttachmentController(http.Controller): @http.route("/mail/attachment/upload", methods=["POST"], type="http", auth="public") + @add_guest_to_context def mail_attachment_upload(self, ufile, thread_id, thread_model, is_pending=False, **kwargs): env = request.env["ir.attachment"]._get_upload_env(thread_model, thread_id) vals = { @@ -41,6 +43,7 @@ class AttachmentController(http.Controller): return request.make_json_response(attachmentData) @http.route("/mail/attachment/delete", methods=["POST"], type="json", auth="public") + @add_guest_to_context def mail_attachment_delete(self, attachment_id, access_token=None): attachment_sudo = request.env["ir.attachment"].browse(int(attachment_id)).sudo().exists() guest = request.env["mail.guest"]._get_guest_from_context() diff --git a/addons/mail/controllers/discuss/binary.py b/addons/mail/controllers/discuss/binary.py index ae08286d88c..e9637d64c6c 100644 --- a/addons/mail/controllers/discuss/binary.py +++ b/addons/mail/controllers/discuss/binary.py @@ -4,6 +4,7 @@ from werkzeug.exceptions import NotFound from odoo import http from odoo.http import request +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context class BinaryController(http.Controller): @@ -13,6 +14,7 @@ class BinaryController(http.Controller): type="http", auth="public", ) + @add_guest_to_context def discuss_channel_partner_avatar_128(self, channel_id, partner_id, **kwargs): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context(channel_id=channel_id) partner_sudo = channel_member_sudo.env["res.partner"].browse(partner_id).exists() @@ -35,6 +37,7 @@ class BinaryController(http.Controller): @http.route( "/discuss/channel//guest//avatar_128", methods=["GET"], type="http", auth="public" ) + @add_guest_to_context def discuss_channel_guest_avatar_128(self, channel_id, guest_id, **kwargs): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context(channel_id=channel_id) guest_sudo = channel_member_sudo.env["mail.guest"].browse(guest_id).exists() @@ -57,6 +60,7 @@ class BinaryController(http.Controller): @http.route( "/discuss/channel//attachment/", methods=["GET"], type="http", auth="public" ) + @add_guest_to_context def discuss_channel_attachment(self, channel_id, attachment_id, download=None, **kwargs): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context_or_raise(channel_id=int(channel_id)) domain = [ @@ -75,6 +79,7 @@ class BinaryController(http.Controller): type="http", auth="public", ) + @add_guest_to_context def discuss_channel_avatar_128(self, channel_id, **kwargs): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context_or_raise(channel_id=channel_id) domain = [("id", "=", channel_id)] @@ -96,6 +101,7 @@ class BinaryController(http.Controller): type="http", auth="public", ) + @add_guest_to_context def fetch_image(self, channel_id, attachment_id, width=0, height=0, **kwargs): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context_or_raise(channel_id=channel_id) domain = [ diff --git a/addons/mail/controllers/discuss/channel.py b/addons/mail/controllers/discuss/channel.py index 17d5b69c9bf..17397c4a495 100644 --- a/addons/mail/controllers/discuss/channel.py +++ b/addons/mail/controllers/discuss/channel.py @@ -6,15 +6,18 @@ from odoo import http from odoo.exceptions import UserError from odoo.http import request from odoo.tools import consteq +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context class ChannelController(http.Controller): @http.route("/discuss/channel/members", methods=["POST"], type="json", auth="public") + @add_guest_to_context def discuss_channel_members(self, channel_id, known_member_ids): channel_member = request.env["discuss.channel.member"]._get_as_sudo_from_context_or_raise(channel_id=channel_id) return channel_member.channel_id.sudo().load_more_members(known_member_ids) @http.route("/discuss/channel/add_guest_as_member", methods=["POST"], type="json", auth="public") + @add_guest_to_context def discuss_channel_add_guest_as_member(self, channel_id, channel_uuid): channel_sudo = request.env["discuss.channel"].browse(int(channel_id)).sudo().exists() if not channel_sudo or not channel_sudo.uuid or not consteq(channel_sudo.uuid, channel_uuid): @@ -42,11 +45,13 @@ class ChannelController(http.Controller): channel.write({"image_128": data}) @http.route("/discuss/channel/info", methods=["POST"], type="json", auth="public") + @add_guest_to_context def discuss_channel_info(self, channel_id): member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context(channel_id=int(channel_id)) return member_sudo.channel_id._channel_info() @http.route("/discuss/channel/messages", methods=["POST"], type="json", auth="public") + @add_guest_to_context def discuss_channel_messages(self, channel_id, before=None, after=None, limit=30, around=None): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context_or_raise(channel_id=int(channel_id)) domain = [ @@ -60,16 +65,19 @@ class ChannelController(http.Controller): return messages.message_format() @http.route("/discuss/channel/pinned_messages", methods=["POST"], type="json", auth="public") + @add_guest_to_context def discuss_channel_pins(self, channel_id): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context_or_raise(channel_id=int(channel_id)) return channel_member_sudo.channel_id.pinned_message_ids.sorted(key="pinned_at", reverse=True).message_format() @http.route("/discuss/channel/set_last_seen_message", methods=["POST"], type="json", auth="public") + @add_guest_to_context def discuss_channel_mark_as_seen(self, channel_id, last_message_id, allow_older=False): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context_or_raise(channel_id=int(channel_id)) return channel_member_sudo.channel_id._channel_seen(last_message_id, allow_older=allow_older) @http.route("/discuss/channel/notify_typing", methods=["POST"], type="json", auth="public") + @add_guest_to_context def discuss_channel_notify_typing(self, channel_id, is_typing): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context_or_raise(channel_id=int(channel_id)) channel_member_sudo._notify_typing(is_typing) diff --git a/addons/mail/controllers/discuss/public_page.py b/addons/mail/controllers/discuss/public_page.py index d5429c90fda..1b10f667e12 100644 --- a/addons/mail/controllers/discuss/public_page.py +++ b/addons/mail/controllers/discuss/public_page.py @@ -10,6 +10,7 @@ from odoo.exceptions import UserError from odoo.http import request from odoo.tools import consteq from odoo.tools.misc import get_lang +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context class PublicPageController(http.Controller): @@ -22,6 +23,7 @@ class PublicPageController(http.Controller): type="http", auth="public", ) + @add_guest_to_context def discuss_channel_chat_from_token(self, create_token, channel_name=None): return self._response_discuss_channel_from_token(create_token=create_token, channel_name=channel_name) @@ -34,12 +36,14 @@ class PublicPageController(http.Controller): type="http", auth="public", ) + @add_guest_to_context def discuss_channel_meet_from_token(self, create_token, channel_name=None): return self._response_discuss_channel_from_token( create_token=create_token, channel_name=channel_name, default_display_mode="video_full_screen" ) @http.route("/chat//", methods=["GET"], type="http", auth="public") + @add_guest_to_context def discuss_channel_invitation(self, channel_id, invitation_token): channel_sudo = request.env["discuss.channel"].browse(channel_id).sudo().exists() if not channel_sudo or not channel_sudo.uuid or not consteq(channel_sudo.uuid, invitation_token): @@ -47,6 +51,7 @@ class PublicPageController(http.Controller): return self._response_discuss_channel_invitation(channel_sudo=channel_sudo) @http.route("/discuss/channel/", methods=["GET"], type="http", auth="public") + @add_guest_to_context def discuss_channel(self, channel_id): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context_or_raise(channel_id=int(channel_id)) return self._response_discuss_public_template(channel_sudo=channel_member_sudo.channel_id) diff --git a/addons/mail/controllers/discuss/rtc.py b/addons/mail/controllers/discuss/rtc.py index 6a5ff0517aa..ed4c4c2b868 100644 --- a/addons/mail/controllers/discuss/rtc.py +++ b/addons/mail/controllers/discuss/rtc.py @@ -5,10 +5,12 @@ from collections import defaultdict from odoo import http from odoo.http import request from odoo.tools import file_open +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context class RtcController(http.Controller): @http.route("/mail/rtc/session/notify_call_members", methods=["POST"], type="json", auth="public") + @add_guest_to_context def session_call_notify(self, peer_notifications): """Sends content to other session of the same channel, only works if the user is the user of that session. This is used to send peer to peer information between sessions. @@ -33,6 +35,7 @@ class RtcController(http.Controller): session_sudo._notify_peers(notifications) @http.route("/mail/rtc/session/update_and_broadcast", methods=["POST"], type="json", auth="public") + @add_guest_to_context def session_update_and_broadcast(self, session_id, values): """Update a RTC session and broadcasts the changes to the members of its channel, only works of the user is the user of that session. @@ -52,6 +55,7 @@ class RtcController(http.Controller): session._update_and_broadcast(values) @http.route("/mail/rtc/channel/join_call", methods=["POST"], type="json", auth="public") + @add_guest_to_context def channel_call_join(self, channel_id, check_rtc_session_ids=None): """Joins the RTC call of a channel if the user is a member of that channel :param int channel_id: id of the channel to join @@ -60,6 +64,7 @@ class RtcController(http.Controller): return channel_member_sudo._rtc_join_call(check_rtc_session_ids=check_rtc_session_ids) @http.route("/mail/rtc/channel/leave_call", methods=["POST"], type="json", auth="public") + @add_guest_to_context def channel_call_leave(self, channel_id): """Disconnects the current user from a rtc call and clears any invitation sent to that user on this channel :param int channel_id: id of the channel from which to disconnect @@ -68,6 +73,7 @@ class RtcController(http.Controller): return channel_member_sudo._rtc_leave_call() @http.route("/mail/rtc/channel/cancel_call_invitation", methods=["POST"], type="json", auth="public") + @add_guest_to_context def channel_call_cancel_invitation(self, channel_id, member_ids=None): """ :param member_ids: members whose invitation is to cancel @@ -91,6 +97,7 @@ class RtcController(http.Controller): ) @http.route("/discuss/channel/ping", methods=["POST"], type="json", auth="public") + @add_guest_to_context def channel_ping(self, channel_id, rtc_session_id=None, check_rtc_session_ids=None): channel_member_sudo = request.env["discuss.channel.member"]._get_as_sudo_from_context(channel_id=int(channel_id)) if rtc_session_id: diff --git a/addons/mail/controllers/guest.py b/addons/mail/controllers/guest.py index 449d573bb5a..b184d6e8c0d 100644 --- a/addons/mail/controllers/guest.py +++ b/addons/mail/controllers/guest.py @@ -4,10 +4,12 @@ from werkzeug.exceptions import NotFound from odoo import http from odoo.http import request +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context class GuestController(http.Controller): @http.route("/mail/guest/update_name", methods=["POST"], type="json", auth="public") + @add_guest_to_context def mail_guest_update_name(self, guest_id, name): guest = request.env["mail.guest"]._get_guest_from_context() guest_to_rename_sudo = guest.env["mail.guest"].browse(guest_id).sudo().exists() diff --git a/addons/mail/controllers/link_preview.py b/addons/mail/controllers/link_preview.py index 4ba8a1e354f..09e919e97f1 100644 --- a/addons/mail/controllers/link_preview.py +++ b/addons/mail/controllers/link_preview.py @@ -2,10 +2,12 @@ from odoo import http from odoo.http import request +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context class LinkPreviewController(http.Controller): @http.route("/mail/link_preview", methods=["POST"], type="json", auth="public") + @add_guest_to_context def mail_link_preview(self, message_id, clear=None): if not request.env["mail.link.preview"]._is_link_preview_enabled(): return @@ -20,6 +22,7 @@ class LinkPreviewController(http.Controller): guest.env["mail.link.preview"].sudo()._create_link_previews(message) @http.route("/mail/link_preview/delete", methods=["POST"], type="json", auth="public") + @add_guest_to_context def mail_link_preview_delete(self, link_preview_id): guest = request.env["mail.guest"]._get_guest_from_context() link_preview_sudo = guest.env["mail.link.preview"].sudo().search([("id", "=", int(link_preview_id))]) diff --git a/addons/mail/controllers/message_reaction.py b/addons/mail/controllers/message_reaction.py index e0e6e5bea1d..b1eb5e3ef87 100644 --- a/addons/mail/controllers/message_reaction.py +++ b/addons/mail/controllers/message_reaction.py @@ -4,10 +4,12 @@ from werkzeug.exceptions import NotFound from odoo import http from odoo.http import request +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context class MessageReactionController(http.Controller): @http.route("/mail/message/reaction", methods=["POST"], type="json", auth="public") + @add_guest_to_context def mail_message_add_reaction(self, message_id, content, action): guest_sudo = request.env["mail.guest"]._get_guest_from_context().sudo() message_sudo = guest_sudo.env["mail.message"].browse(int(message_id)).exists() diff --git a/addons/mail/controllers/thread.py b/addons/mail/controllers/thread.py index f2f887fb7df..ca0a293bf58 100644 --- a/addons/mail/controllers/thread.py +++ b/addons/mail/controllers/thread.py @@ -6,6 +6,7 @@ from werkzeug.exceptions import NotFound from odoo import http from odoo.http import request +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context class ThreadController(http.Controller): @@ -70,6 +71,7 @@ class ThreadController(http.Controller): return {"attachment_ids", "body", "message_type", "partner_ids", "subtype_xmlid", "parent_id"} @http.route("/mail/message/post", methods=["POST"], type="json", auth="public") + @add_guest_to_context def mail_message_post(self, thread_model, thread_id, post_data, context=None): guest = request.env["mail.guest"]._get_guest_from_context() guest.env["ir.attachment"].browse(post_data.get("attachment_ids", []))._check_attachments_access( @@ -109,6 +111,7 @@ class ThreadController(http.Controller): return message_data @http.route("/mail/message/update_content", methods=["POST"], type="json", auth="public") + @add_guest_to_context def mail_message_update_content(self, message_id, body, attachment_ids, attachment_tokens=None, partner_ids=None): guest = request.env["mail.guest"]._get_guest_from_context() guest.env["ir.attachment"].browse(attachment_ids)._check_attachments_access(attachment_tokens) diff --git a/addons/mail/controllers/webclient.py b/addons/mail/controllers/webclient.py index 058114f9100..d9fccaf088e 100644 --- a/addons/mail/controllers/webclient.py +++ b/addons/mail/controllers/webclient.py @@ -4,10 +4,12 @@ from werkzeug.exceptions import NotFound from odoo import http from odoo.http import request +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context class WebclientController(http.Controller): @http.route("/mail/init_messaging", methods=["POST"], type="json", auth="public") + @add_guest_to_context def mail_init_messaging(self): if not request.env.user.sudo()._is_public(): return request.env.user.sudo(request.env.user.has_group("base.group_portal"))._init_messaging() diff --git a/addons/mail/models/discuss/ir_websocket.py b/addons/mail/models/discuss/ir_websocket.py index 84ea5faf383..55130a42ac1 100644 --- a/addons/mail/models/discuss/ir_websocket.py +++ b/addons/mail/models/discuss/ir_websocket.py @@ -1,6 +1,5 @@ from odoo import models -from odoo.http import request -from odoo.addons.bus.websocket import wsrequest +from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context class IrWebsocket(models.AbstractModel): @@ -17,16 +16,12 @@ class IrWebsocket(models.AbstractModel): ) return im_status + @add_guest_to_context def _build_bus_channel_list(self, channels): - # This method can either be called due to an http or a - # websocket request. The request itself is necessary to - # retrieve the current guest. Let's retrieve the proper - # request. - req = request or wsrequest channels = list(channels) # do not alter original list - guest_sudo = self.env["mail.guest"]._get_guest_from_request(req).sudo() + guest_sudo = self.env["mail.guest"]._get_guest_from_context().sudo() discuss_channels = self.env["discuss.channel"] - if req.session.uid: + if self.env.uid and not self.env.user._is_public(): discuss_channels = self.env.user.partner_id.channel_ids elif guest_sudo: discuss_channels = guest_sudo.channel_ids @@ -35,15 +30,11 @@ class IrWebsocket(models.AbstractModel): channels.append(discuss_channel) return super()._build_bus_channel_list(channels) + @add_guest_to_context def _update_bus_presence(self, inactivity_period, im_status_ids_by_model): super()._update_bus_presence(inactivity_period, im_status_ids_by_model) if not self.env.user or self.env.user._is_public(): - # This method can either be called due to an http or a - # websocket request. The request itself is necessary to - # retrieve the current guest. Let's retrieve the proper - # request. - req = request or wsrequest - guest_sudo = self.env["mail.guest"]._get_guest_from_request(req).sudo() + guest_sudo = self.env["mail.guest"]._get_guest_from_context().sudo() if not guest_sudo: return guest_sudo.env["bus.presence"].update_presence( diff --git a/addons/mail/models/discuss/mail_guest.py b/addons/mail/models/discuss/mail_guest.py index 2feddd7e5e3..908caa0c2d5 100644 --- a/addons/mail/models/discuss/mail_guest.py +++ b/addons/mail/models/discuss/mail_guest.py @@ -2,12 +2,64 @@ import pytz import uuid +from functools import wraps +from inspect import Parameter, signature from odoo.tools import consteq from odoo import _, api, fields, models +from odoo.http import request from odoo.addons.base.models.res_partner import _tz_get from odoo.exceptions import UserError from odoo.addons.bus.models.bus_presence import AWAY_TIMER, DISCONNECTION_TIMER +from odoo.addons.bus.websocket import wsrequest + + +def add_guest_to_context(func): + """ Decorate a function to extract the guest from the request. + The guest is then available on the context of the current + request. + """ + @wraps(func) + def wrapper(self, *args, **kwargs): + req = request or wsrequest + guest = req.env["mail.guest"]._get_guest_from_context() + guest_token = kwargs.pop("guest_token", None) + if guest: + return func(self, *args, **kwargs) + token = ( + guest_token + or req.httprequest.cookies.get(req.env["mail.guest"]._cookie_name) + or req.env.context.get("guest_token", "") + ) + parts = token.split(req.env["mail.guest"]._cookie_separator) + if len(parts) == 2: + guest_id, guest_access_token = parts + guest = req.env["mail.guest"].browse(int(guest_id)).sudo().exists() + if not guest or not guest.access_token or not consteq(guest.access_token, guest_access_token): + guest = req.env["mail.guest"] + elif not guest.timezone: + timezone = req.env["mail.guest"]._get_timezone_from_request(req) + if timezone: + guest._update_timezone(timezone) + guest = guest.sudo(False) + req.update_context(guest=guest) + if hasattr(self, "env"): + self.env.context = {**self.env.context, "guest": guest} + return func(self, *args, **kwargs) + + # Add the guest_token parameter to the wrapper signature + # so that it is not marked as being ignored. It will be + # popped before calling the wrapped function. + old_sig = signature(wrapper) + params = list(old_sig.parameters.values()) + new_param_index = next(( + index for index, param in enumerate(params) + if param.kind in [Parameter.VAR_POSITIONAL, Parameter.VAR_KEYWORD] + ), len(params)) + new_param = Parameter("guest_token", Parameter.POSITIONAL_OR_KEYWORD, default=None) + params.insert(new_param_index, new_param) + wrapper.__signature__ = old_sig.replace(parameters=params) + return wrapper class MailGuest(models.Model): @@ -52,22 +104,6 @@ class MailGuest(models.Model): return guest.with_context(guest=guest) return self.env['mail.guest'] - def _get_guest_from_request(self, request): - parts = request.httprequest.cookies.get(self._cookie_name, '').split(self._cookie_separator) - if len(parts) != 2: - return self.env['mail.guest'] - guest_id, guest_access_token = parts - if not guest_id or not guest_access_token: - return self.env['mail.guest'] - guest = self.env['mail.guest'].browse(int(guest_id)).sudo().exists() - if not guest or not guest.access_token or not consteq(guest.access_token, guest_access_token): - return self.env['mail.guest'] - if not guest.timezone: - timezone = self._get_timezone_from_request(request) - if timezone: - guest._update_timezone(timezone) - return guest.sudo(False).with_context(guest=guest) - def _get_timezone_from_request(self, request): timezone = request.httprequest.cookies.get('tz') return timezone if timezone in pytz.all_timezones else False diff --git a/addons/mail/models/discuss/mail_message.py b/addons/mail/models/discuss/mail_message.py index ef88018b3b0..dad0d45c246 100644 --- a/addons/mail/models/discuss/mail_message.py +++ b/addons/mail/models/discuss/mail_message.py @@ -12,7 +12,7 @@ class MailMessage(models.Model): self.ensure_one() if self.env.user._is_public(): guest = self.env["mail.guest"]._get_guest_from_context() - return guest and self.model == "discuss.channel" and self.res_id in guest.channel_ids.ids + return guest and self.model == "discuss.channel" and self.res_id in guest.sudo().channel_ids.ids return super()._validate_access_for_current_persona(operation) def _message_format_extras(self, format_reply): diff --git a/addons/mail/models/ir_http.py b/addons/mail/models/ir_http.py index ae9c264e479..c43321c03b7 100644 --- a/addons/mail/models/ir_http.py +++ b/addons/mail/models/ir_http.py @@ -25,11 +25,3 @@ class IrHttp(models.AbstractModel): 'user_context': user_context, }) return result - - @classmethod - def _pre_dispatch(cls, rule, args): - """Overriden to add the guest to the context if any.""" - super()._pre_dispatch(rule, args) - guest = request.env["mail.guest"]._get_guest_from_request(request) - if guest: - request.update_context(guest=guest)