Odoo18-Base/addons/mail/controllers/thread.py
2025-01-06 10:57:38 +07:00

171 lines
8.2 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime
from markupsafe import Markup
from werkzeug.exceptions import NotFound
from odoo import http
from odoo.http import request
from odoo.tools import frozendict
from odoo.tools import email_normalize
from odoo.addons.mail.models.discuss.mail_guest import add_guest_to_context
from odoo.addons.mail.tools.discuss import Store
class ThreadController(http.Controller):
@http.route("/mail/thread/data", methods=["POST"], type="json", auth="public", readonly=True)
def mail_thread_data(self, thread_model, thread_id, request_list, **kwargs):
thread = request.env[thread_model]._get_thread_with_access(thread_id, **kwargs)
if not thread:
return Store(
request.env[thread_model].browse(thread_id),
{"hasReadAccess": False, "hasWriteAccess": False},
as_thread=True,
).get_result()
return Store(thread, as_thread=True, request_list=request_list).get_result()
@http.route("/mail/thread/messages", methods=["POST"], type="json", auth="user")
def mail_thread_messages(self, thread_model, thread_id, search_term=None, before=None, after=None, around=None, limit=30):
domain = [
("res_id", "=", int(thread_id)),
("model", "=", thread_model),
("message_type", "!=", "user_notification"),
]
res = request.env["mail.message"]._message_fetch(domain, search_term=search_term, before=before, after=after, around=around, limit=limit)
messages = res.pop("messages")
if not request.env.user._is_public():
messages.set_message_done()
return {
**res,
"data": Store(messages, for_current_user=True).get_result(),
"messages": Store.many_ids(messages),
}
@http.route("/mail/partner/from_email", methods=["POST"], type="json", auth="user")
def mail_thread_partner_from_email(self, emails, additional_values=None):
partners = [
{"id": partner.id, "name": partner.name, "email": partner.email}
for partner in request.env["res.partner"]._find_or_create_from_emails(emails, additional_values)
]
return partners
@http.route("/mail/read_subscription_data", methods=["POST"], type="json", auth="user")
def read_subscription_data(self, follower_id):
"""Computes:
- message_subtype_data: data about document subtypes: which are
available, which are followed if any"""
request.env["mail.followers"].check_access("read")
follower = request.env["mail.followers"].sudo().browse(follower_id)
follower.ensure_one()
record = request.env[follower.res_model].browse(follower.res_id)
record.check_access("read")
# find current model subtypes, add them to a dictionary
subtypes = record._mail_get_message_subtypes()
followed_subtypes_ids = set(follower.subtype_ids.ids)
subtypes_list = [
{
"name": subtype.name,
"res_model": subtype.res_model,
"sequence": subtype.sequence,
"default": subtype.default,
"internal": subtype.internal,
"followed": subtype.id in followed_subtypes_ids,
"parent_model": subtype.parent_id.res_model,
"id": subtype.id,
}
for subtype in subtypes
]
return sorted(
subtypes_list,
key=lambda it: (it["parent_model"] or "", it["res_model"] or "", it["internal"], it["sequence"]),
)
def _prepare_post_data(self, post_data, thread, **kwargs):
partners = request.env["res.partner"].browse(post_data.pop("partner_ids", []))
if "body" in post_data:
post_data["body"] = Markup(post_data["body"]) # contains HTML such as @mentions
if "partner_emails" in kwargs and request.env.user.has_group("base.group_partner_manager"):
additional_values = {
email_normalize(email, strict=False) or email: values
for email, values in kwargs.get("partner_additional_values", {}).items()
}
partners |= request.env["res.partner"].browse(
partner.id
for partner in request.env["res.partner"]._find_or_create_from_emails(
kwargs["partner_emails"], additional_values
)
)
if not request.env.user._is_internal():
partners = partners & self._filter_message_post_partners(thread, partners)
post_data["partner_ids"] = partners.ids
return post_data
def _filter_message_post_partners(self, thread, partners):
domain = [
("res_model", "=", thread._name),
("res_id", "=", thread.id),
("partner_id", "in", partners.ids),
]
# sudo: mail.followers - filtering partners that are followers is acceptable
return request.env["mail.followers"].sudo().search(domain).partner_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, **kwargs):
guest = request.env["mail.guest"]._get_guest_from_context()
guest.env["ir.attachment"].browse(post_data.get("attachment_ids", []))._check_attachments_access(
kwargs.get("attachment_tokens")
)
if context:
request.update_context(**context)
canned_response_ids = tuple(cid for cid in kwargs.get('canned_response_ids', []) if isinstance(cid, int))
if canned_response_ids:
# Avoid serialization errors since last used update is not
# essential and should not block message post.
request.env.cr.execute("""
UPDATE mail_canned_response SET last_used=%(last_used)s
WHERE id IN (
SELECT id from mail_canned_response WHERE id IN %(ids)s
FOR NO KEY UPDATE SKIP LOCKED
)
""", {
'last_used': datetime.now(),
'ids': canned_response_ids,
})
thread = request.env[thread_model]._get_thread_with_access(
thread_id, mode=request.env[thread_model]._mail_post_access, **kwargs
)
if not thread:
raise NotFound()
if not request.env[thread_model]._get_thread_with_access(thread_id, "write"):
thread.env.context = frozendict(
thread.env.context, mail_create_nosubscribe=True, mail_post_autofollow=False
)
post_data = {
key: value
for key, value in post_data.items()
if key in thread._get_allowed_message_post_params()
}
# sudo: mail.thread - users can post on accessible threads
message = thread.sudo().message_post(**self._prepare_post_data(post_data, thread, **kwargs))
return Store(message, for_current_user=True).get_result()
@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, **kwargs):
guest = request.env["mail.guest"]._get_guest_from_context()
guest.env["ir.attachment"].browse(attachment_ids)._check_attachments_access(attachment_tokens)
message = request.env["mail.message"]._get_with_access(message_id, "create", **kwargs)
if not message or not self._is_message_editable(message, **kwargs):
raise NotFound()
# sudo: mail.message - access is checked in _get_with_access and _is_message_editable
message = message.sudo()
body = Markup(body) if body else body # may contain HTML such as @mentions
guest.env[message.model].browse([message.res_id])._message_update_content(
message, body=body, attachment_ids=attachment_ids, partner_ids=partner_ids
)
return Store(message, for_current_user=True).get_result()
def _is_message_editable(self, message, **kwargs):
return message.sudo().is_current_user_or_guest_author or request.env.user._is_admin()