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

134 lines
5.3 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
import requests
from datetime import timedelta
from lxml import html
from odoo import api, models, fields, tools
from odoo.tools.misc import OrderedSet
from odoo.addons.mail.tools.discuss import Store
from odoo.addons.mail.tools.link_preview import get_link_preview_from_url
class LinkPreview(models.Model):
_name = 'mail.link.preview'
_inherit = "bus.listener.mixin"
_description = "Store link preview data"
message_id = fields.Many2one('mail.message', string='Message', index=True, ondelete='cascade')
is_hidden = fields.Boolean()
source_url = fields.Char('URL', required=True)
og_type = fields.Char('Type')
og_title = fields.Char('Title')
og_site_name = fields.Char('Site name')
og_image = fields.Char('Image')
og_description = fields.Text('Description')
og_mimetype = fields.Char('MIME type')
image_mimetype = fields.Char('Image MIME type')
create_date = fields.Datetime(index=True)
def _bus_channel(self):
return self.message_id._bus_channel()
@api.model
def _create_from_message_and_notify(self, message, request_url=None):
if tools.is_html_empty(message.body):
return self
urls = OrderedSet(html.fromstring(message.body).xpath('//a[not(@data-oe-model)]/@href'))
link_previews = self.env['mail.link.preview']
requests_session = requests.Session()
link_preview_values = []
link_previews_by_url = {
preview.source_url: preview for preview in message.sudo().link_preview_ids
}
ignore_pattern = (
re.compile(f"{re.escape(request_url)}(odoo|web|chat)(/|$|#|\\?)") if request_url else None
)
for url in urls:
if ignore_pattern and ignore_pattern.match(url):
continue
if url in link_previews_by_url:
preview = link_previews_by_url.pop(url)
if not preview.is_hidden:
link_previews += preview
continue
if preview := get_link_preview_from_url(url, requests_session):
preview['message_id'] = message.id
link_preview_values.append(preview)
if len(link_preview_values) + len(link_previews) > 5:
break
for unused_preview in link_previews_by_url.values():
unused_preview._unlink_and_notify()
if link_preview_values:
link_previews += link_previews.create(link_preview_values)
if link_previews := link_previews.sorted(key=lambda p: list(urls).index(p.source_url)):
message._bus_send_store(message, {"linkPreviews": Store.many(link_previews)})
def _hide_and_notify(self):
if not self:
return True
for link_preview in self:
link_preview._bus_send_store(
link_preview.message_id,
{"linkPreviews": Store.many(link_preview, "DELETE", only_id=True)},
)
self.is_hidden = True
def _unlink_and_notify(self):
if not self:
return True
for link_preview in self:
link_preview._bus_send_store(
link_preview.message_id,
{"linkPreviews": Store.many(link_preview, "DELETE", only_id=True)},
)
self.unlink()
@api.model
def _is_link_preview_enabled(self):
link_preview_throttle = int(self.env['ir.config_parameter'].sudo().get_param('mail.link_preview_throttle', 99))
return link_preview_throttle > 0
@api.model
def _search_or_create_from_url(self, url):
"""Return the URL preview, first from the database if available otherwise make the request."""
lifetime = int(self.env['ir.config_parameter'].sudo().get_param('mail.mail_link_preview_lifetime_days', 3))
preview = self.env['mail.link.preview'].search([
('source_url', '=', url),
('create_date', '>=', fields.Datetime.now() - timedelta(days=lifetime)),
], order='create_date DESC', limit=1)
if not preview:
preview_values = get_link_preview_from_url(url)
if not preview_values:
return self.env["mail.link.preview"]
preview = self.env['mail.link.preview'].create(preview_values)
return preview
def _to_store(self, store: Store, /):
for preview in self:
data = preview._read_format(
[
"image_mimetype",
"og_description",
"og_image",
"og_mimetype",
"og_site_name",
"og_title",
"og_type",
"source_url",
],
load=False,
)[0]
data["message"] = Store.one(preview.message_id, only_id=True)
store.add(preview, data)
@api.autovacuum
def _gc_mail_link_preview(self):
lifetime = int(self.env['ir.config_parameter'].sudo().get_param('mail.mail_link_preview_lifetime_days', 3))
self.env['mail.link.preview'].search([
('message_id', '=', False),
('create_date', '<', fields.Datetime.now() - timedelta(days=lifetime)),
], order='create_date ASC', limit=1000).unlink()