When a URL is entered to add an attachment through the media dialog a URL attachment is created. If the source material is removed the attachment will not be reachable anymore. This commit fetches the data and adds it as an uploaded file when adding a URL image in website. If the image binary cannot be fetched by the client (e.g. CORS), the old approach is used instead. This commit also adapts test tours which relied on the image's `src` to verify if the entered URL had been taken into account by making them rely on the image's `data-original-src` instead. task-3129360 closes odoo/odoo#118877 Signed-off-by: Romain Derie (rde) <rde@odoo.com>
86 lines
3.5 KiB
Python
86 lines
3.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from werkzeug.urls import url_quote
|
|
|
|
from odoo import api, models, fields, tools
|
|
|
|
SUPPORTED_IMAGE_MIMETYPES = {
|
|
'image/gif': '.gif',
|
|
'image/jpe': '.jpe',
|
|
'image/jpeg': '.jpeg',
|
|
'image/jpg': '.jpg',
|
|
'image/png': '.png',
|
|
'image/svg+xml': '.svg',
|
|
}
|
|
|
|
|
|
class IrAttachment(models.Model):
|
|
|
|
_inherit = "ir.attachment"
|
|
|
|
local_url = fields.Char("Attachment URL", compute='_compute_local_url')
|
|
image_src = fields.Char(compute='_compute_image_src')
|
|
image_width = fields.Integer(compute='_compute_image_size')
|
|
image_height = fields.Integer(compute='_compute_image_size')
|
|
original_id = fields.Many2one('ir.attachment', string="Original (unoptimized, unresized) attachment")
|
|
|
|
def _compute_local_url(self):
|
|
for attachment in self:
|
|
if attachment.url:
|
|
attachment.local_url = attachment.url
|
|
else:
|
|
attachment.local_url = '/web/image/%s?unique=%s' % (attachment.id, attachment.checksum)
|
|
|
|
@api.depends('mimetype', 'url', 'name')
|
|
def _compute_image_src(self):
|
|
for attachment in self:
|
|
# Only add a src for supported images
|
|
if attachment.mimetype not in SUPPORTED_IMAGE_MIMETYPES:
|
|
attachment.image_src = False
|
|
continue
|
|
|
|
if attachment.type == 'url':
|
|
if attachment.url.startswith('/'):
|
|
# Local URL
|
|
attachment.image_src = attachment.url
|
|
else:
|
|
name = url_quote(attachment.name)
|
|
attachment.image_src = '/web/image/%s-redirect/%s' % (attachment.id, name)
|
|
else:
|
|
# Adding unique in URLs for cache-control
|
|
unique = attachment.checksum[:8]
|
|
if attachment.url:
|
|
# For attachments-by-url, unique is used as a cachebuster. They
|
|
# currently do not leverage max-age headers.
|
|
separator = '&' if '?' in attachment.url else '?'
|
|
attachment.image_src = '%s%sunique=%s' % (attachment.url, separator, unique)
|
|
else:
|
|
name = url_quote(attachment.name)
|
|
attachment.image_src = '/web/image/%s-%s/%s' % (attachment.id, unique, name)
|
|
|
|
@api.depends('datas')
|
|
def _compute_image_size(self):
|
|
for attachment in self:
|
|
try:
|
|
image = tools.base64_to_image(attachment.datas)
|
|
attachment.image_width = image.width
|
|
attachment.image_height = image.height
|
|
except Exception:
|
|
attachment.image_width = 0
|
|
attachment.image_height = 0
|
|
|
|
def _get_media_info(self):
|
|
"""Return a dict with the values that we need on the media dialog."""
|
|
self.ensure_one()
|
|
return self._read_format(['id', 'name', 'description', 'mimetype', 'checksum', 'url', 'type', 'res_id', 'res_model', 'public', 'access_token', 'image_src', 'image_width', 'image_height', 'original_id'])[0]
|
|
|
|
def _can_bypass_rights_on_media_dialog(self, **attachment_data):
|
|
""" This method is meant to be overridden, for instance to allow to
|
|
create image attachment despite the user not allowed to create
|
|
attachment, eg:
|
|
- Portal user uploading an image on the forum (bypass acl)
|
|
- Non admin user uploading an unsplash image (bypass binary/url check)
|
|
"""
|
|
return False
|