[IMP] mail: add decorator to extract guest
This PR adds the `add_guest_to_context` decorator in order to provide a generic way to extract the guest from a request. It will be used to unified guest extraction from cookie/param based on its provenance (external livechat/public page). This is better than the `pre_dispatch` method since it can be applied to specific routes instead of adding this logic to every request. part of task-3332628 closes odoo/odoo#130052 Signed-off-by: Matthieu Stockbauer (tsm) <tsm@odoo.com>
This commit is contained in:
@@ -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),
|
||||
|
||||
@@ -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);
|
||||
|
||||
+13
-2
@@ -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'}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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/<int:channel_id>/guest/<int:guest_id>/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/<int:channel_id>/attachment/<int:attachment_id>", 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 = [
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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/<int:channel_id>/<string:invitation_token>", 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/<int:channel_id>", 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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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))])
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user