Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[17.0][FW] fs_attachement: Oca port from 16.0 to 17.0 #401

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
8260d45
Create base_attachment_object_storage to extract common code to store…
TDu Aug 30, 2017
84ff5ad
Abstract object storage in attachment_s3
guewen Sep 20, 2017
471914d
Set addons uninstallable
guewen Nov 15, 2017
45b2ea9
Set addons installable
guewen Nov 15, 2017
b3150fc
Replace value.decode('base64') by base64.b64decode (py3)
guewen Nov 15, 2017
a0d9b7d
Ensure that migration of files is commited before deleting files
guewen Jun 13, 2018
5285c0f
Fix attachments stored in FS instead of object storage
guewen Jun 13, 2018
5ba0232
Document a weird domain which is there for a reason
guewen Jun 13, 2018
9f6728f
base_attachment_object_storage: bump 1.1.0
guewen Jun 13, 2018
841fdc7
Set all modules to uninstallable
jcoux Oct 24, 2018
cca211d
Migration to 12.0
jcoux Oct 24, 2018
b8405bd
fixup! Migration to 12.0
jcoux Nov 23, 2018
adf09b7
[IMP]: Allow to pass storage as a context key
grindtildeath Feb 26, 2019
7345c77
[IMP]: Allow to use context Key as storage key
grindtildeath Mar 1, 2019
41182c7
BSRD-286: Set the addons to uninstallable
Tonow-c2c Oct 7, 2019
19edbf3
[MIG] base_attachment_object_storage: Migration to 13.0
grindtildeath Oct 7, 2019
6af6d4b
[IMP] route file to db base on size and mimetype
vrenaville Dec 3, 2019
9545d43
Add method to force storage of special attachments to DB
guewen May 1, 2019
4a2e1ff
Rework and fix storage forced in database
guewen May 27, 2020
164bede
Set module for 14.0 uninstallable
leemannd Oct 6, 2020
2cb4690
[MIG] base_attachment_object_storage: Migration to 14.0
p-tombez Nov 3, 2020
70ab23f
remove base64 from base_attachment
dnplkndll Nov 4, 2020
f7d8edf
15.0 Modules migration
leemannd Oct 18, 2021
63d9133
Update manifest files to be consistent inbetween them
leemannd Oct 18, 2021
6ea15ae
Object Storage - inactive mode
StephaneMangin Apr 13, 2022
7f67ff2
Object storage inactivation: changes INACTIVE concept for DISABLE
StephaneMangin May 9, 2022
72665ff
feat: v16.0 : all modules uninstallable
vrenaville Sep 26, 2022
1c2967e
fix: modifition setup (#386)
vrenaville Sep 26, 2022
909c457
fix: dependencies and deprecated code (#390)
vrenaville Nov 4, 2022
2d380fa
feat: remove after method (#393)
vrenaville Nov 11, 2022
ba5ba75
[ADD] fs_attachment: Store attachment through fsspec
lmignon Apr 7, 2023
e5a77d3
[FIX] fs_attachment: respect controller parameters for /web/content/
hbrunn Apr 27, 2024
82cde62
[FIX] fs_attachment: Rename file in storage when name is written
hbrunn Apr 27, 2024
b027702
[IMP] fs_attachment: No crash on missing file
lmignon Apr 22, 2024
48ac610
[FIX] fs_attachment: prevent recompute_urls from skipping records wit…
PabloEForgeFlow Jul 17, 2024
3ef1492
[IMP] fs_attachment: add test for recompute_urls
PabloEForgeFlow Jul 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions base_attachment_object_storage/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
16 changes: 13 additions & 3 deletions fs_attachment/models/fs_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -478,8 +478,18 @@ def recompute_urls(self) -> None:
in staging are done in a different directory and will not impact the
production.
"""
attachments = self.env["ir.attachment"].search(
[("fs_storage_id", "in", self.ids)]
)
# The weird "res_field = False OR res_field != False" domain
# is required! It's because of an override of _search in ir.attachment
# which adds ('res_field', '=', False) when the domain does not
# contain 'res_field'.
# https://github.com/odoo/odoo/blob/9032617120138848c63b3cfa5d1913c5e5ad76db/
# odoo/addons/base/ir/ir_attachment.py#L344-L347
domain = [
("fs_storage_id", "in", self.ids),
"|",
("res_field", "=", False),
("res_field", "!=", False),
]
attachments = self.env["ir.attachment"].search(domain)
attachments._compute_fs_url()
attachments._compute_fs_url_path()
13 changes: 11 additions & 2 deletions fs_attachment/models/ir_attachment.py
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,9 @@ def write(self, vals):
),
).write(vals)

if "name" in vals:
self._enforce_meaningful_storage_filename()

return True

@api.model
Expand Down Expand Up @@ -365,8 +368,14 @@ def _set_attachment_data(self, asbytes) -> None: # pylint: disable=missing-retu
def _storage_file_read(self, fname: str) -> bytes | None:
"""Read the file from the filesystem storage"""
fs, _storage, fname = self._fs_parse_store_fname(fname)
with fs.open(fname, "rb") as f:
return f.read()
try:
with fs.open(fname, "rb") as f:
return f.read()
except IOError:
_logger.info(
"Error reading %s on storage %s", fname, _storage, exc_info=True
)
return b""

@api.model
def _storage_file_write(self, bin_data: bytes) -> str:
Expand Down
28 changes: 28 additions & 0 deletions fs_attachment/models/ir_binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,34 @@ def _record_to_stream(self, record, field_name):
return FsStream.from_fs_attachment(fs_attachment)
return super()._record_to_stream(record, field_name)

def _get_stream_from(
self,
record,
field_name="raw",
filename=None,
filename_field="name",
mimetype=None,
default_mimetype="application/octet-stream",
):
stream = super()._get_stream_from(
record,
field_name=field_name,
filename=filename,
filename_field=filename_field,
mimetype=mimetype,
default_mimetype=default_mimetype,
)

if stream.type == "fs":
if mimetype:
stream.mimetype = mimetype
if filename:
stream.download_name = filename
elif record and filename_field in record:
stream.download_name = record[filename_field] or stream.download_name

return stream

def _get_image_stream_from(
self,
record,
Expand Down
7 changes: 7 additions & 0 deletions fs_attachment/readme/newsfragments/361.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
No crash o missign file.

Prior to this change, Odoo was crashing as soon as access to a file stored into
an external filesytem was not possible. This can lead to a complete system block.
This change prevents this kind of blockage by ignoring access error to files
stored into external system on read operations. These kind of errors are logged
into the log files for traceability.
11 changes: 7 additions & 4 deletions fs_attachment/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@

/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.

Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.

See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
Expand Down Expand Up @@ -274,7 +275,7 @@
margin-left: 2em ;
margin-right: 2em }

pre.code .ln { color: grey; } /* line numbers */
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
Expand All @@ -300,7 +301,7 @@
span.pre {
white-space: pre }

span.problematic {
span.problematic, pre.problematic {
color: red }

span.section-subtitle {
Expand Down Expand Up @@ -777,7 +778,9 @@ <h2><a class="toc-backref" href="#toc-entry-16">Contributors</a></h2>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-17">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
Expand Down
11 changes: 11 additions & 0 deletions fs_attachment/tests/test_fs_attachment.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,3 +455,14 @@ def test_update_png_to_svg(self):
)

self.assertEqual(attachment.mimetype, "image/svg+xml")

def test_write_name(self):
self.temp_backend.use_as_default_for_attachments = True
attachment = self.ir_attachment_model.create(
{"name": "file.bin", "datas": b"aGVsbG8gd29ybGQK"}
)
self.assertTrue(attachment.fs_filename.startswith("file-"))
self.assertTrue(attachment.fs_filename.endswith(".bin"))
attachment.write({"name": "file2.txt"})
self.assertTrue(attachment.fs_filename.startswith("file2-"))
self.assertTrue(attachment.fs_filename.endswith(".txt"))
49 changes: 49 additions & 0 deletions fs_attachment/tests/test_fs_storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,52 @@ def test_force_field_and_model_create_attachment(self):
self.env.flush_all()

self.assertFalse(attachment.fs_storage_code)

def test_recompute_urls(self):
"""
Mark temp_backend as default and set its base_url.
Create one attachment in temp_backend that is linked to a field and one that is not.
* Check that after updating the base_url for the backend, executing recompute_urls
updates fs_url for both attachments, whether they are linked to a field or not
"""
self.temp_backend.base_url = "https://acsone.eu/media"
self.temp_backend.use_as_default_for_attachments = True
self.ir_attachment_model.create(
{
"name": "field.txt",
"raw": "Attachment linked to a field",
"res_model": "res.partner",
"res_field": "name",
}
)
self.ir_attachment_model.create(
{
"name": "no_field.txt",
"raw": "Attachment not linked to a field",
}
)
self.env.flush_all()

self.env.cr.execute(
f"""
SELECT COUNT(*)
FROM ir_attachment
WHERE fs_storage_id = {self.temp_backend.id}
AND fs_url LIKE '{self.temp_backend.base_url}%'
"""
)
self.assertEqual(self.env.cr.dictfetchall()[0].get("count"), 2)

self.temp_backend.base_url = "https://forgeflow.com/media"
self.temp_backend.recompute_urls()
self.env.flush_all()

self.env.cr.execute(
f"""
SELECT COUNT(*)
FROM ir_attachment
WHERE fs_storage_id = {self.temp_backend.id}
AND fs_url LIKE '{self.temp_backend.base_url}%'
"""
)
self.assertEqual(self.env.cr.dictfetchall()[0].get("count"), 2)
11 changes: 11 additions & 0 deletions fs_attachment/tests/test_stream.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,17 @@ def test_content_url(self):
},
assert_content=self.content,
)
url = f"/web/content/{self.attachment_binary.id}/?filename=test2.txt&mimetype=text/csv"
self.assertDownload(
url,
headers={},
assert_status_code=200,
assert_headers={
"Content-Type": "text/csv; charset=utf-8",
"Content-Disposition": "inline; filename=test2.txt",
},
assert_content=self.content,
)

def test_image_url(self):
self.authenticate("admin", "admin")
Expand Down
Loading