diff --git a/fs_attachment/models/ir_binary.py b/fs_attachment/models/ir_binary.py index ef7e2e5bb9..57f6b2d5e4 100644 --- a/fs_attachment/models/ir_binary.py +++ b/fs_attachment/models/ir_binary.py @@ -17,16 +17,14 @@ class IrBinary(models.AbstractModel): _inherit = "ir.binary" - def _record_to_stream(self, record, field_name): - # Extend base implementation to support attachment stored into a - # filesystem storage - fs_attachment = None + def _get_fs_attachment_for_field(self, record, field_name): if record._name == "ir.attachment" and record.fs_filename: - fs_attachment = record + return record + record.check_field_access_rights("read", [field_name]) field_def = record._fields[field_name] - if field_def.attachment and not field_def.compute and not field_def.related: - field_attachment = ( + if field_def.attachment and field_def.store: + fs_attachment = ( self.env["ir.attachment"] .sudo() .search( @@ -38,8 +36,14 @@ def _record_to_stream(self, record, field_name): limit=1, ) ) - if field_attachment.fs_filename: - fs_attachment = field_attachment + if fs_attachment and fs_attachment.fs_filename: + return fs_attachment + return None + + def _record_to_stream(self, record, field_name): + # Extend base implementation to support attachment stored into a + # filesystem storage + fs_attachment = self._get_fs_attachment_for_field(record, field_name) if fs_attachment: return FsStream.from_fs_attachment(fs_attachment) return super()._record_to_stream(record, field_name) @@ -101,6 +105,11 @@ def _get_image_stream_from( if value: record = value.attachment field_name = "raw" + elif field_def.type in ("binary"): + fs_attachment = self._get_fs_attachment_for_field(record, field_name) + if fs_attachment: + record = fs_attachment + field_name = "raw" stream = super()._get_image_stream_from( record, field_name=field_name, diff --git a/fs_attachment/tests/test_stream.py b/fs_attachment/tests/test_stream.py index 1ce68a38c2..3bae74a626 100644 --- a/fs_attachment/tests/test_stream.py +++ b/fs_attachment/tests/test_stream.py @@ -1,5 +1,6 @@ # Copyright 2023 ACSONE SA/NV (http://acsone.eu). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import base64 import io import os import shutil @@ -160,3 +161,31 @@ def test_response_csp_header(self): "Content-Security-Policy": "default-src 'none'", }, ) + + def test_serving_field_image(self): + self.authenticate("admin", "admin") + demo_partner = self.env.ref("base.partner_demo") + demo_partner.with_context( + storage_location=self.temp_backend.code, + ).write({"image_128": base64.encodebytes(self._create_image(128, 128))}) + url = f"/web/image/{demo_partner._name}/{demo_partner.id}/image_128" + res = self.assertDownload( + url, + headers={}, + assert_status_code=200, + assert_headers={ + "Content-Type": "image/png", + }, + ) + self.assertEqual(Image.open(io.BytesIO(res.content)).size, (128, 128)) + + url = f"/web/image/{demo_partner._name}/{demo_partner.id}/avatar_128" + avatar_res = self.assertDownload( + url, + headers={}, + assert_status_code=200, + assert_headers={ + "Content-Type": "image/png", + }, + ) + self.assertEqual(Image.open(io.BytesIO(avatar_res.content)).size, (128, 128))