mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-21 02:52:04 +02:00
19.0 vanilla
This commit is contained in:
parent
d1963a3c3a
commit
2d3ee4855a
7430 changed files with 2687981 additions and 2965473 deletions
|
|
@ -1,49 +1,119 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
import re
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from odoo import models
|
||||
from odoo.http import request
|
||||
from odoo.addons.bus.websocket import wsrequest
|
||||
from odoo.fields import Domain
|
||||
from odoo.addons.mail.tools.discuss import add_guest_to_context
|
||||
from odoo.tools.misc import verify_limited_field_access_token
|
||||
|
||||
PRESENCE_CHANNEL_PREFIX = "odoo-presence-"
|
||||
PRESENCE_CHANNEL_REGEX = re.compile(
|
||||
rf"{PRESENCE_CHANNEL_PREFIX}"
|
||||
r"(?P<model>res\.partner|mail\.guest)_(?P<record_id>\d+)"
|
||||
r"(?:-(?P<token>[a-f0-9]{64}o0x[a-f0-9]+))?$"
|
||||
)
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IrWebsocket(models.AbstractModel):
|
||||
_inherit = 'ir.websocket'
|
||||
"""Override to handle mail specific features (presence in particular)."""
|
||||
|
||||
def _get_im_status(self, data):
|
||||
im_status = super()._get_im_status(data)
|
||||
if 'mail.guest' in data:
|
||||
im_status['guests'] = self.env['mail.guest'].sudo().with_context(active_test=False).search_read(
|
||||
[('id', 'in', data['mail.guest'])],
|
||||
['im_status']
|
||||
_inherit = "ir.websocket"
|
||||
|
||||
def _serve_ir_websocket(self, event_name, data):
|
||||
"""Override to process update_presence."""
|
||||
super()._serve_ir_websocket(event_name, data)
|
||||
if event_name == "update_presence":
|
||||
self._update_mail_presence(**data)
|
||||
|
||||
@add_guest_to_context
|
||||
def _subscribe(self, og_data):
|
||||
super()._subscribe(og_data)
|
||||
|
||||
@add_guest_to_context
|
||||
def _update_mail_presence(self, inactivity_period):
|
||||
partner, guest = self.env["res.partner"]._get_current_persona()
|
||||
if not partner and not guest:
|
||||
return
|
||||
self.env["mail.presence"]._try_update_presence(
|
||||
self.env.user if partner else guest, inactivity_period
|
||||
)
|
||||
|
||||
def _prepare_subscribe_data(self, channels, last):
|
||||
data = super()._prepare_subscribe_data(channels, last)
|
||||
model_ids_to_token = defaultdict(dict)
|
||||
for channel in channels:
|
||||
if not isinstance(channel, str) or not channel.startswith(PRESENCE_CHANNEL_PREFIX):
|
||||
continue
|
||||
data["channels"].discard(channel)
|
||||
if not (match := re.match(PRESENCE_CHANNEL_REGEX, channel)):
|
||||
_logger.warning("Malformed presence channel: %s", channel)
|
||||
continue
|
||||
model, record_id, token = match.groups()
|
||||
model_ids_to_token[model][int(record_id)] = token or ""
|
||||
# sudo - res.partner, mail.guest: can access presence targets to decide whether
|
||||
# the current user is allowed to read it or not.
|
||||
partner_ids = model_ids_to_token["res.partner"].keys()
|
||||
partners = (
|
||||
self.env["res.partner"]
|
||||
.with_context(active_test=False)
|
||||
.sudo()
|
||||
.search([("id", "in", partner_ids)])
|
||||
.sudo(False)
|
||||
)
|
||||
partner, guest = self.env["res.partner"]._get_current_persona()
|
||||
allowed_partners = (
|
||||
partners.filtered(
|
||||
lambda p: verify_limited_field_access_token(
|
||||
p, "im_status", model_ids_to_token["res.partner"][p.id], scope="mail.presence"
|
||||
)
|
||||
or p.has_access("read")
|
||||
)
|
||||
return im_status
|
||||
| partner
|
||||
)
|
||||
guest_ids = model_ids_to_token["mail.guest"].keys()
|
||||
guests = self.env["mail.guest"].sudo().search([("id", "in", guest_ids)]).sudo(False)
|
||||
allowed_guests = (
|
||||
guests.filtered(
|
||||
lambda g: verify_limited_field_access_token(
|
||||
g, "im_status", model_ids_to_token["mail.guest"][g.id], scope="mail.presence"
|
||||
)
|
||||
or g.has_access("read")
|
||||
)
|
||||
| guest
|
||||
)
|
||||
data["channels"].update((partner, "presence") for partner in allowed_partners)
|
||||
data["channels"].update((guest, "presence") for guest in allowed_guests)
|
||||
# There is a gap between a subscription client side (which is debounced)
|
||||
# and the actual subcription thus presences can be missed. Send a
|
||||
# notification to avoid missing presences during a subscription.
|
||||
presence_domain = Domain("last_poll", ">", datetime.now() - timedelta(seconds=2)) & (
|
||||
Domain(
|
||||
"user_id",
|
||||
"in",
|
||||
allowed_partners.with_context(active_test=False).sudo().user_ids.ids,
|
||||
)
|
||||
| Domain("guest_id", "in", allowed_guests.ids)
|
||||
)
|
||||
# sudo: mail.presence: access to presence was validated with access token.
|
||||
data["missed_presences"] = self.env["mail.presence"].sudo().search(presence_domain)
|
||||
return data
|
||||
|
||||
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()
|
||||
mail_channels = self.env['mail.channel']
|
||||
if req.session.uid:
|
||||
partner = self.env.user.partner_id
|
||||
mail_channels = partner.channel_ids
|
||||
channels.append(partner)
|
||||
elif guest_sudo:
|
||||
mail_channels = guest_sudo.channel_ids
|
||||
channels.append(guest_sudo)
|
||||
for mail_channel in mail_channels:
|
||||
channels.append(mail_channel)
|
||||
return super()._build_bus_channel_list(channels)
|
||||
def _after_subscribe_data(self, data):
|
||||
current_partner, current_guest = self.env["res.partner"]._get_current_persona()
|
||||
if current_partner or current_guest:
|
||||
data["missed_presences"]._send_presence(bus_target=current_partner or current_guest)
|
||||
|
||||
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()
|
||||
if not guest_sudo:
|
||||
return
|
||||
guest_sudo.env['bus.presence'].update(inactivity_period, identity_field='guest_id', identity_value=guest_sudo.id)
|
||||
def _on_websocket_closed(self, cookies):
|
||||
super()._on_websocket_closed(cookies)
|
||||
if self.env.user and not self.env.user._is_public():
|
||||
# sudo: mail.presence - user can update their own presence
|
||||
self.env.user.sudo().presence_ids.status = "offline"
|
||||
token = cookies.get(self.env["mail.guest"]._cookie_name, "")
|
||||
if guest := self.env["mail.guest"]._get_guest_from_token(token):
|
||||
# sudo: mail.presence - guest can update their own presence
|
||||
guest.sudo().presence_ids.status = "offline"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue