From 492235240fc6d84d2ce748c1958758b5f12842ea Mon Sep 17 00:00:00 2001
From: "Laurent Mignonn (ACSONE)"
Date: Sun, 10 Nov 2024 11:17:58 +0100
Subject: [PATCH 1/4] oca-port: store 'fs_attachment' data
---
.oca/oca-port/blacklist/fs_attachment.json | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 .oca/oca-port/blacklist/fs_attachment.json
diff --git a/.oca/oca-port/blacklist/fs_attachment.json b/.oca/oca-port/blacklist/fs_attachment.json
new file mode 100644
index 0000000000..b4b5a95c16
--- /dev/null
+++ b/.oca/oca-port/blacklist/fs_attachment.json
@@ -0,0 +1,5 @@
+{
+ "pull_requests": {
+ "OCA/storage#344": "migration PR"
+ }
+}
From e71f748bd57bfa86630d7a3e0aca8526a56cd15d Mon Sep 17 00:00:00 2001
From: Fai
Date: Tue, 5 Nov 2024 09:34:25 -0500
Subject: [PATCH 2/4] [17.0][FIX] fs_attachment: Add CSP header for fs stream
---
fs_attachment/fs_stream.py | 14 +++++++++++++-
fs_attachment/tests/test_stream.py | 13 +++++++++++++
2 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/fs_attachment/fs_stream.py b/fs_attachment/fs_stream.py
index f04b6c47a5..7bf50e4325 100644
--- a/fs_attachment/fs_stream.py
+++ b/fs_attachment/fs_stream.py
@@ -38,7 +38,13 @@ def read(self):
return f.read()
return super().read()
- def get_response(self, as_attachment=None, immutable=None, **send_file_kwargs):
+ def get_response(
+ self,
+ as_attachment=None,
+ immutable=None,
+ content_security_policy="default-src 'none'",
+ **send_file_kwargs,
+ ):
if self.type != "fs":
return super().get_response(
as_attachment=as_attachment, immutable=immutable, **send_file_kwargs
@@ -79,6 +85,12 @@ def get_response(self, as_attachment=None, immutable=None, **send_file_kwargs):
if immutable and res.cache_control:
res.cache_control["immutable"] = None
+
+ res.headers["X-Content-Type-Options"] = "nosniff"
+
+ if content_security_policy:
+ res.headers["Content-Security-Policy"] = content_security_policy
+
return res
@classmethod
diff --git a/fs_attachment/tests/test_stream.py b/fs_attachment/tests/test_stream.py
index 9d19df60b4..e9ceb918bc 100644
--- a/fs_attachment/tests/test_stream.py
+++ b/fs_attachment/tests/test_stream.py
@@ -150,3 +150,16 @@ def test_image_url_with_size(self):
},
)
self.assertEqual(Image.open(io.BytesIO(res.content)).size, (64, 64))
+
+ def test_response_csp_header(self):
+ self.authenticate("admin", "admin")
+ url = f"/web/content/{self.attachment_binary.id}"
+ self.assertDownload(
+ url,
+ headers={},
+ assert_status_code=200,
+ assert_headers={
+ "X-Content-Type-Options": "nosniff",
+ "Content-Security-Policy": "default-src 'none'",
+ },
+ )
From 81ef6610f3bdaee48a9231c420ad82282e18a928 Mon Sep 17 00:00:00 2001
From: kobros-tech
Date: Sat, 26 Oct 2024 00:54:30 +0300
Subject: [PATCH 3/4] [FIX] fs_attachment: remove unwanted args
---
fs_attachment/README.rst | 400 ++++++++++----------
fs_attachment/fs_stream.py | 1 -
fs_attachment/readme/CONTRIBUTORS.md | 28 +-
fs_attachment/static/description/index.html | 132 +++----
4 files changed, 284 insertions(+), 277 deletions(-)
diff --git a/fs_attachment/README.rst b/fs_attachment/README.rst
index bd048c7cff..17cc78b89d 100644
--- a/fs_attachment/README.rst
+++ b/fs_attachment/README.rst
@@ -7,7 +7,7 @@ Base Attachment Object Store
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- !! source digest: sha256:88c623934cf2676ac173c9beaa37d16d7f9e735d3bb149ea5527494a151224d6
+ !! source digest: sha256:6138d157ac41f158eee22db8c23bd6b5661c7b63855c3f2248566532c6052587
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
@@ -49,10 +49,10 @@ the filename is based on the pattern:
This addon also adds on the attachments 2 new fields to use to retrieve
the file content from a URL:
-- ``Internal URL``: URL to retrieve the file content from the Odoo's
- filestore.
-- ``Filesystem URL``: URL to retrieve the file content from the
- external storage.
+- ``Internal URL``: URL to retrieve the file content from the Odoo's
+ filestore.
+- ``Filesystem URL``: URL to retrieve the file content from the external
+ storage.
Note
@@ -94,144 +94,142 @@ In addition to the common fields available to configure a storage,
specifics fields are available under the section 'Attachment' to
configure the way attachments will be stored in the filesystem.
-- ``Optimizes Directory Path``: This option is useful if you need to
- prevent having too many files in a single directory. It will create a
- directory structure based on the attachment's checksum (with 2 levels
- of depth) For example, if the checksum is ``123456789``, the file
- will be stored in the directory
- ``/path/to/storage/12/34/my_file-1-0.txt``.
-
-- ``Autovacuum GC``: This is used to automatically remove files from
- the filesystem when it's no longer referenced in Odoo. Some storage
- backends (like S3) may charge you for the storage of files, so it's
- important to remove them when they're no longer needed. In some
- cases, this option is not desirable, for example if you're using a
- storage backend to store images shared with others systems (like your
- website) and you don't want to remove the files from the storage
- while they're still referenced into the others systems. This
- mechanism is based on a ``fs.file.gc`` model used to collect the
- files to remove. This model is automatically populated by the
- ``ir.attachment`` model when a file is removed from the database. If
- you disable this option, you'll have to manually take care of the
- records in the ``fs.file.gc`` for your filesystem storage.
-
-- ``Use As Default For Attachment``: This options allows you to declare
- the storage as the default one for attachments. If you have multiple
- filesystem storage configured, you can choose which one will be used
- by default for attachments. Once activated, attachments created
- without specifying a storage will be stored in this default storage.
-
-- ``Force DB For Default Attachment Rules``: This option is useful if
- you want to force the storage of some attachments in the database,
- even if you have a default filesystem storage configured. This is
- specially useful when you're using a storage backend like S3, where
- the latency of the network can be high. This option is a JSON field
- that allows you to define the mimetypes and the size limit below
- which the attachments will be stored in the database.
-
- Small images (128, 256) are used in Odoo in list / kanban views. We
- want them to be fast to read. They are generally < 50KB (default
- configuration) so they don't take that much space in database, but
- they'll be read much faster than from the object storage.
-
- The assets (application/javascript, text/css) are stored in database
- as well whatever their size is:
-
- - a database doesn't have thousands of them
- - of course better for performance
- - better portability of a database: when replicating a production
- instance for dev, the assets are included
-
- The default configuration is:
-
- {"image/": 51200, "application/javascript": 0, "text/css": 0}
-
- Where the key is the beginning of the mimetype to configure and
- the value is the limit in size below which attachments are kept in
- DB. 0 means no limit.
-
- Default configuration means:
-
- - images mimetypes (image/png, image/jpeg, ...) below 50KB are
- stored in database
- - application/javascript are stored in database whatever their size
- - text/css are stored in database whatever their size
-
- This option is only available on the filesystem storage that is used
- as default for attachments.
+- ``Optimizes Directory Path``: This option is useful if you need to
+ prevent having too many files in a single directory. It will create a
+ directory structure based on the attachment's checksum (with 2 levels
+ of depth) For example, if the checksum is ``123456789``, the file will
+ be stored in the directory ``/path/to/storage/12/34/my_file-1-0.txt``.
+
+- ``Autovacuum GC``: This is used to automatically remove files from the
+ filesystem when it's no longer referenced in Odoo. Some storage
+ backends (like S3) may charge you for the storage of files, so it's
+ important to remove them when they're no longer needed. In some cases,
+ this option is not desirable, for example if you're using a storage
+ backend to store images shared with others systems (like your website)
+ and you don't want to remove the files from the storage while they're
+ still referenced into the others systems. This mechanism is based on a
+ ``fs.file.gc`` model used to collect the files to remove. This model
+ is automatically populated by the ``ir.attachment`` model when a file
+ is removed from the database. If you disable this option, you'll have
+ to manually take care of the records in the ``fs.file.gc`` for your
+ filesystem storage.
+
+- ``Use As Default For Attachment``: This options allows you to declare
+ the storage as the default one for attachments. If you have multiple
+ filesystem storage configured, you can choose which one will be used
+ by default for attachments. Once activated, attachments created
+ without specifying a storage will be stored in this default storage.
+
+- ``Force DB For Default Attachment Rules``: This option is useful if
+ you want to force the storage of some attachments in the database,
+ even if you have a default filesystem storage configured. This is
+ specially useful when you're using a storage backend like S3, where
+ the latency of the network can be high. This option is a JSON field
+ that allows you to define the mimetypes and the size limit below which
+ the attachments will be stored in the database.
+
+ Small images (128, 256) are used in Odoo in list / kanban views. We
+ want them to be fast to read. They are generally < 50KB (default
+ configuration) so they don't take that much space in database, but
+ they'll be read much faster than from the object storage.
+
+ The assets (application/javascript, text/css) are stored in database
+ as well whatever their size is:
+
+ - a database doesn't have thousands of them
+ - of course better for performance
+ - better portability of a database: when replicating a production
+ instance for dev, the assets are included
+
+ The default configuration is:
+
+ {"image/": 51200, "application/javascript": 0, "text/css": 0}
+
+ Where the key is the beginning of the mimetype to configure and the
+ value is the limit in size below which attachments are kept in DB.
+ 0 means no limit.
+
+ Default configuration means:
+
+ - images mimetypes (image/png, image/jpeg, ...) below 50KB are stored
+ in database
+ - application/javascript are stored in database whatever their size
+ - text/css are stored in database whatever their size
+
+ This option is only available on the filesystem storage that is used
+ as default for attachments.
It is also possible to use different FS storages for attachments linked
to different resource fields/models. You can configure it either on the
``fs.storage`` directly, or in a server environment file:
-- From the ``fs.storage``: Fields model_ids and field_ids will encode
- for which models/fields use this storage as default storage for
- attachments having these resource model/field. Note that if an
- attachment has both resource model and field, it will first take the
- FS storage where the field is explicitely linked, then is not found,
- the one where the model is explicitely linked.
-- From a server environment file: In this case you just have to provide
- a comma-separated list of models (under the model_xmlids key) or
- fields (under the field_xmlids key). To do so, use the model/field
- XML ids provided by Odoo. See the Server Environment section for a
- concrete example.
+- From the ``fs.storage``: Fields model_ids and field_ids will encode
+ for which models/fields use this storage as default storage for
+ attachments having these resource model/field. Note that if an
+ attachment has both resource model and field, it will first take the
+ FS storage where the field is explicitely linked, then is not found,
+ the one where the model is explicitely linked.
+- From a server environment file: In this case you just have to provide
+ a comma-separated list of models (under the model_xmlids key) or
+ fields (under the field_xmlids key). To do so, use the model/field XML
+ ids provided by Odoo. See the Server Environment section for a
+ concrete example.
Another key feature of this module is the ability to get access to the
attachments from URLs.
-- ``Base URL``: This is the base URL used to access the attachments
- from the filesystem storage itself. If your storage doesn't provide a
- way to access the files from a URL, you can leave this field empty.
-
-- ``Is Directory Path In URL``: Normally the directory patch configured
- on the storage is not included in the URL. If you want to include it,
- you can activate this option.
-
-- ``Use X-Sendfile To Serve Internal Url``: If checked and odoo is
- behind a proxy that supports x-sendfile, the content served by the
- attachment's internal URL will be served by the proxy using the
- filesystem url path if defined (This field is available on the
- attachment if the storage is configured with a base URL) If not, the
- file will be served by odoo that will stream the content read from
- the filesystem storage. This option is useful to avoid to serve files
- from odoo and therefore to avoid to load the odoo process.
-
- To be fully functional, this option requires the proxy to support
- x-sendfile (apache) or x-accel-redirect (nginx). You must also
- configure your proxy by adding for each storage a rule to redirect
- the url rooted at the 'storagge code' to the server serving the
- files. For example, if you have a storage with the code 'my_storage'
- and a server serving the files at the url 'http://myserver.com', you
- must add the following rule in your proxy configuration:
-
- .. code:: nginx
-
- location /my_storage/ {
- internal;
- proxy_pass http://myserver.com;
- }
-
- With this configuration a call to
- '/web/content//" for a file stored
- in the 'my_storage' storage will generate a response by odoo with the
- URI
- ``/my_storage//--``
- in the headers ``X-Accel-Redirect`` and ``X-Sendfile`` and the proxy
- will redirect to
- ``http://myserver.com//--``.
-
- see
- https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/
- for more information.
-
-- ``Use Filename Obfuscation``: If checked, the filename used to store
- the content into the filesystem storage will be obfuscated. This is
- useful to avoid to expose the real filename of the attachments
- outside of the Odoo database. The filename will be obfuscated by
- using the checksum of the content. This option is to avoid when the
- content of your filestore is shared with other systems (like your
- website) and you want to keep a meaningful filename to ensure SEO.
- This option is disabled by default.
+- ``Base URL``: This is the base URL used to access the attachments from
+ the filesystem storage itself. If your storage doesn't provide a way
+ to access the files from a URL, you can leave this field empty.
+
+- ``Is Directory Path In URL``: Normally the directory patch configured
+ on the storage is not included in the URL. If you want to include it,
+ you can activate this option.
+
+- ``Use X-Sendfile To Serve Internal Url``: If checked and odoo is
+ behind a proxy that supports x-sendfile, the content served by the
+ attachment's internal URL will be served by the proxy using the
+ filesystem url path if defined (This field is available on the
+ attachment if the storage is configured with a base URL) If not, the
+ file will be served by odoo that will stream the content read from the
+ filesystem storage. This option is useful to avoid to serve files from
+ odoo and therefore to avoid to load the odoo process.
+
+ To be fully functional, this option requires the proxy to support
+ x-sendfile (apache) or x-accel-redirect (nginx). You must also
+ configure your proxy by adding for each storage a rule to redirect the
+ url rooted at the 'storagge code' to the server serving the files. For
+ example, if you have a storage with the code 'my_storage' and a server
+ serving the files at the url 'http://myserver.com', you must add the
+ following rule in your proxy configuration:
+
+ .. code:: nginx
+
+ location /my_storage/ {
+ internal;
+ proxy_pass http://myserver.com;
+ }
+
+ With this configuration a call to
+ '/web/content//" for a file stored in
+ the 'my_storage' storage will generate a response by odoo with the URI
+ ``/my_storage//--``
+ in the headers ``X-Accel-Redirect`` and ``X-Sendfile`` and the proxy
+ will redirect to
+ ``http://myserver.com//--``.
+
+ see
+ https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/
+ for more information.
+
+- ``Use Filename Obfuscation``: If checked, the filename used to store
+ the content into the filesystem storage will be obfuscated. This is
+ useful to avoid to expose the real filename of the attachments outside
+ of the Odoo database. The filename will be obfuscated by using the
+ checksum of the content. This option is to avoid when the content of
+ your filestore is shared with other systems (like your website) and
+ you want to keep a meaningful filename to ensure SEO. This option is
+ disabled by default.
Server Environment
------------------
@@ -239,16 +237,16 @@ Server Environment
When you configure a storage through the use of server environment file,
you can provide values for the following keys:
-- ``optimizes_directory_path``
-- ``autovacuum_gc``
-- ``base_url``
-- ``is_directory_path_in_url``
-- ``use_x_sendfile_to_serve_internal_url``
-- ``use_as_default_for_attachments``
-- ``force_db_for_default_attachment_rules``
-- ``use_filename_obfuscation``
-- ``model_xmlids``
-- ``field_xmlids``
+- ``optimizes_directory_path``
+- ``autovacuum_gc``
+- ``base_url``
+- ``is_directory_path_in_url``
+- ``use_x_sendfile_to_serve_internal_url``
+- ``use_as_default_for_attachments``
+- ``force_db_for_default_attachment_rules``
+- ``use_filename_obfuscation``
+- ``model_xmlids``
+- ``field_xmlids``
For example, the configuration of my storage with code fsprod used to
store the attachments by default could be:
@@ -314,20 +312,20 @@ new_version can be passed as False to avoid the creation of a new file.
Tips & Tricks
-------------
-- When working in multi staging environments, the management of the
- attachments can be tricky. For example, if you have a production
- instance and a staging instance based on a backup of the production
- environment, you may want to have the attachments shared between the
- two instances BUT you don't want to have one instance removing or
- modifying the attachments of the other instance.
-
- To do so, you can add on your staging instances a new storage and
- declare it as the default storage to use for attachments. This way,
- all the new attachments will be stored in this new storage but the
- attachments created on the production instance will still be read
- from the production storage. Be careful to adapt the configuration of
- your storage to the production environment to make it read only. (The
- use of server environment files is a good way to do so).
+- When working in multi staging environments, the management of the
+ attachments can be tricky. For example, if you have a production
+ instance and a staging instance based on a backup of the production
+ environment, you may want to have the attachments shared between the
+ two instances BUT you don't want to have one instance removing or
+ modifying the attachments of the other instance.
+
+ To do so, you can add on your staging instances a new storage and
+ declare it as the default storage to use for attachments. This way,
+ all the new attachments will be stored in this new storage but the
+ attachments created on the production instance will still be read from
+ the production storage. Be careful to adapt the configuration of your
+ storage to the production environment to make it read only. (The use
+ of server environment files is a good way to do so).
Changelog
=========
@@ -337,67 +335,67 @@ Changelog
**Bugfixes**
-- Fix the error retrieving attachment files when the storage is set to
- optimize directory paths.
- (`#312 `__)
+- Fix the error retrieving attachment files when the storage is set to
+ optimize directory paths.
+ (`#312 `__)
16.0.1.0.6 (2023-12-02)
-----------------------
**Bugfixes**
-- Improve performance at creation of an attachment or when the
- attachment is updated.
+- Improve performance at creation of an attachment or when the
+ attachment is updated.
- Before this change, when the fs_url was computed the computed value
- was always reassigned to the fs_url attribute even if the value was
- the same. In a lot of cases the value was the same and the
- reassignment was not necessary. Unfortunately this reassignment has
- as side effect to mark the record as dirty and generate a SQL update
- statement at the end of the transaction.
- (`#307 `__)
+ Before this change, when the fs_url was computed the computed value
+ was always reassigned to the fs_url attribute even if the value was
+ the same. In a lot of cases the value was the same and the
+ reassignment was not necessary. Unfortunately this reassignment has as
+ side effect to mark the record as dirty and generate a SQL update
+ statement at the end of the transaction.
+ (`#307 `__)
16.0.1.0.5 (2023-11-29)
-----------------------
**Bugfixes**
-- When manipulating the file system api through a local variable named
- *fs*, we observed some strange behavior when it was wrongly redefined
- in an enclosing scope as in the following example: *with fs.open(...)
- as fs*. This commit fixes this issue by renaming the local variable
- and therefore avoiding the name clash.
- (`#306 `__)
+- When manipulating the file system api through a local variable named
+ *fs*, we observed some strange behavior when it was wrongly redefined
+ in an enclosing scope as in the following example: *with fs.open(...)
+ as fs*. This commit fixes this issue by renaming the local variable
+ and therefore avoiding the name clash.
+ (`#306 `__)
16.0.1.0.4 (2023-11-22)
-----------------------
**Bugfixes**
-- Fix error when an url is computed for an attachment in a storage
- configure wihtout directory path.
- (`#302 `__)
+- Fix error when an url is computed for an attachment in a storage
+ configure wihtout directory path.
+ (`#302 `__)
16.0.1.0.3 (2023-10-17)
-----------------------
**Bugfixes**
-- Fix access to technical models to be able to upload attachments for
- users with basic access
- (`#289 `__)
+- Fix access to technical models to be able to upload attachments for
+ users with basic access
+ (`#289 `__)
16.0.1.0.2 (2023-10-09)
-----------------------
**Bugfixes**
-- Ensures python 3.9 compatibility.
- (`#285 `__)
-- If a storage is not used to store all the attachments by default, the
- call to the get_force_db_for_default_attachment_rules method must
- return an empty dictionary.
- (`#286 `__)
+- Ensures python 3.9 compatibility.
+ (`#285 `__)
+- If a storage is not used to store all the attachments by default, the
+ call to the get_force_db_for_default_attachment_rules method must
+ return an empty dictionary.
+ (`#286 `__)
Bug Tracker
===========
@@ -421,17 +419,21 @@ Authors
Contributors
------------
-Thierry Ducrest Guewen Baconnier
- Julien Coux
- Akim Juillerat
- Thomas Nowicki
- Vincent Renaville
- Denis Leemann
- Patrick Tombez
- Don Kendall
-Stephane Mangin Laurent Mignon
- Marie Lejeune
-Wolfgang Pichler Nans Lefebvre
+- Thierry Ducrest
+- Guewen Baconnier
+- Julien Coux
+- Akim Juillerat
+- Thomas Nowicki
+- Vincent Renaville
+- Denis Leemann
+- Patrick Tombez
+- Don Kendall
+- Stephane Mangi
+- Laurent Mignon
+- Marie Lejeune
+- Wolfgang Pichler
+- Nans Lefebvre
+- Mohamed Alkobrosli
Maintainers
-----------
diff --git a/fs_attachment/fs_stream.py b/fs_attachment/fs_stream.py
index 7bf50e4325..70173eb814 100644
--- a/fs_attachment/fs_stream.py
+++ b/fs_attachment/fs_stream.py
@@ -63,7 +63,6 @@ def get_response(
"max_age": STATIC_CACHE_LONG if immutable else self.max_age,
"environ": request.httprequest.environ,
"response_class": Response,
- **send_file_kwargs,
}
use_x_sendfile = self._fs_use_x_sendfile
# The file will be closed by werkzeug...
diff --git a/fs_attachment/readme/CONTRIBUTORS.md b/fs_attachment/readme/CONTRIBUTORS.md
index c3441031b8..071273090e 100644
--- a/fs_attachment/readme/CONTRIBUTORS.md
+++ b/fs_attachment/readme/CONTRIBUTORS.md
@@ -1,13 +1,15 @@
-Thierry Ducrest \<\> Guewen Baconnier
-\<\> Julien Coux
-\<\> Akim Juillerat
-\<\> Thomas Nowicki
-\<\> Vincent Renaville
-\<\> Denis Leemann
-\<\> Patrick Tombez
-\<\> Don Kendall
-\<\> Stephane Mangin
-\<\> Laurent Mignon
-\<\> Marie Lejeune
-\<\> Wolfgang Pichler \<\>
-Nans Lefebvre \<\>
+* Thierry Ducrest \<\>
+* Guewen Baconnier \<\>
+* Julien Coux \<\>
+* Akim Juillerat \<\>
+* Thomas Nowicki \<\>
+* Vincent Renaville \<\>
+* Denis Leemann \<\>
+* Patrick Tombez \<\>
+* Don Kendall \<\>
+* Stephane Mangi \<\>
+* Laurent Mignon \<\>
+* Marie Lejeune \<\>
+* Wolfgang Pichler \<\>
+* Nans Lefebvre \<\>
+* Mohamed Alkobrosli \<\>
diff --git a/fs_attachment/static/description/index.html b/fs_attachment/static/description/index.html
index 76aade8029..a7019ed642 100644
--- a/fs_attachment/static/description/index.html
+++ b/fs_attachment/static/description/index.html
@@ -367,7 +367,7 @@ Base Attachment Object Store
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-!! source digest: sha256:88c623934cf2676ac173c9beaa37d16d7f9e735d3bb149ea5527494a151224d6
+!! source digest: sha256:6138d157ac41f158eee22db8c23bd6b5661c7b63855c3f2248566532c6052587
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
In some cases, you need to store attachment in another system that the
@@ -390,8 +390,8 @@
Base Attachment Object Store
- Internal URL: URL to retrieve the file content from the Odoo’s
filestore.
-- Filesystem URL: URL to retrieve the file content from the
-external storage.
+- Filesystem URL: URL to retrieve the file content from the external
+storage.
Note
The internal URL is always available, but the filesystem URL is only
@@ -453,23 +453,22 @@
Optimizes Directory Path: This option is useful if you need to
prevent having too many files in a single directory. It will create a
directory structure based on the attachment’s checksum (with 2 levels
-of depth) For example, if the checksum is 123456789, the file
-will be stored in the directory
-/path/to/storage/12/34/my_file-1-0.txt.
+of depth) For example, if the checksum is 123456789, the file will
+be stored in the directory /path/to/storage/12/34/my_file-1-0.txt.
-Autovacuum GC: This is used to automatically remove files from
-the filesystem when it’s no longer referenced in Odoo. Some storage
+
Autovacuum GC: This is used to automatically remove files from the
+filesystem when it’s no longer referenced in Odoo. Some storage
backends (like S3) may charge you for the storage of files, so it’s
-important to remove them when they’re no longer needed. In some
-cases, this option is not desirable, for example if you’re using a
-storage backend to store images shared with others systems (like your
-website) and you don’t want to remove the files from the storage
-while they’re still referenced into the others systems. This
-mechanism is based on a fs.file.gc model used to collect the
-files to remove. This model is automatically populated by the
-ir.attachment model when a file is removed from the database. If
-you disable this option, you’ll have to manually take care of the
-records in the fs.file.gc for your filesystem storage.
+important to remove them when they’re no longer needed. In some cases,
+this option is not desirable, for example if you’re using a storage
+backend to store images shared with others systems (like your website)
+and you don’t want to remove the files from the storage while they’re
+still referenced into the others systems. This mechanism is based on a
+fs.file.gc model used to collect the files to remove. This model
+is automatically populated by the ir.attachment model when a file
+is removed from the database. If you disable this option, you’ll have
+to manually take care of the records in the fs.file.gc for your
+filesystem storage.
Use As Default For Attachment: This options allows you to declare
the storage as the default one for attachments. If you have multiple
@@ -482,8 +481,8 @@
even if you have a default filesystem storage configured. This is
specially useful when you’re using a storage backend like S3, where
the latency of the network can be high. This option is a JSON field
-that allows you to define the mimetypes and the size limit below
-which the attachments will be stored in the database.
+that allows you to define the mimetypes and the size limit below which
+the attachments will be stored in the database.
Small images (128, 256) are used in Odoo in list / kanban views. We
want them to be fast to read. They are generally < 50KB (default
configuration) so they don’t take that much space in database, but
@@ -499,14 +498,14 @@
The default configuration is:
{“image/”: 51200, “application/javascript”: 0, “text/css”: 0}
-Where the key is the beginning of the mimetype to configure and
-the value is the limit in size below which attachments are kept in
-DB. 0 means no limit.
+Where the key is the beginning of the mimetype to configure and the
+value is the limit in size below which attachments are kept in DB.
+0 means no limit.
Default configuration means:
-- images mimetypes (image/png, image/jpeg, …) below 50KB are
-stored in database
+- images mimetypes (image/png, image/jpeg, …) below 50KB are stored
+in database
- application/javascript are stored in database whatever their size
- text/css are stored in database whatever their size
@@ -526,16 +525,16 @@
the one where the model is explicitely linked.
From a server environment file: In this case you just have to provide
a comma-separated list of models (under the model_xmlids key) or
-fields (under the field_xmlids key). To do so, use the model/field
-XML ids provided by Odoo. See the Server Environment section for a
+fields (under the field_xmlids key). To do so, use the model/field XML
+ids provided by Odoo. See the Server Environment section for a
concrete example.
Another key feature of this module is the ability to get access to the
attachments from URLs.
-Base URL: This is the base URL used to access the attachments
-from the filesystem storage itself. If your storage doesn’t provide a
-way to access the files from a URL, you can leave this field empty.
+Base URL: This is the base URL used to access the attachments from
+the filesystem storage itself. If your storage doesn’t provide a way
+to access the files from a URL, you can leave this field empty.
Is Directory Path In URL: Normally the directory patch configured
on the storage is not included in the URL. If you want to include it,
@@ -546,16 +545,16 @@
attachment’s internal URL will be served by the proxy using the
filesystem url path if defined (This field is available on the
attachment if the storage is configured with a base URL) If not, the
-file will be served by odoo that will stream the content read from
-the filesystem storage. This option is useful to avoid to serve files
-from odoo and therefore to avoid to load the odoo process.
+file will be served by odoo that will stream the content read from the
+filesystem storage. This option is useful to avoid to serve files from
+odoo and therefore to avoid to load the odoo process.
To be fully functional, this option requires the proxy to support
x-sendfile (apache) or x-accel-redirect (nginx). You must also
-configure your proxy by adding for each storage a rule to redirect
-the url rooted at the ‘storagge code’ to the server serving the
-files. For example, if you have a storage with the code ‘my_storage’
-and a server serving the files at the url ‘http://myserver.com’, you
-must add the following rule in your proxy configuration:
+configure your proxy by adding for each storage a rule to redirect the
+url rooted at the ‘storagge code’ to the server serving the files. For
+example, if you have a storage with the code ‘my_storage’ and a server
+serving the files at the url ‘http://myserver.com’, you must add the
+following rule in your proxy configuration:
location /my_storage/ {
internal;
@@ -563,9 +562,8 @@
}
With this configuration a call to
-‘/web/content/<att.id>/<att.name><att.extension>” for a file stored
-in the ‘my_storage’ storage will generate a response by odoo with the
-URI
+‘/web/content/<att.id>/<att.name><att.extension>” for a file stored in
+the ‘my_storage’ storage will generate a response by odoo with the URI
/my_storage/<paht_in_storage>/<att.name>-<att.id>-<version><att.extension>
in the headers X-Accel-Redirect and X-Sendfile and the proxy
will redirect to
@@ -576,12 +574,12 @@
Use Filename Obfuscation: If checked, the filename used to store
the content into the filesystem storage will be obfuscated. This is
-useful to avoid to expose the real filename of the attachments
-outside of the Odoo database. The filename will be obfuscated by
-using the checksum of the content. This option is to avoid when the
-content of your filestore is shared with other systems (like your
-website) and you want to keep a meaningful filename to ensure SEO.
-This option is disabled by default.
+useful to avoid to expose the real filename of the attachments outside
+of the Odoo database. The filename will be obfuscated by using the
+checksum of the content. This option is to avoid when the content of
+your filestore is shared with other systems (like your website) and
+you want to keep a meaningful filename to ensure SEO. This option is
+disabled by default.
@@ -666,10 +664,10 @@
To do so, you can add on your staging instances a new storage and
declare it as the default storage to use for attachments. This way,
all the new attachments will be stored in this new storage but the
-attachments created on the production instance will still be read
-from the production storage. Be careful to adapt the configuration of
-your storage to the production environment to make it read only. (The
-use of server environment files is a good way to do so).
+attachments created on the production instance will still be read from
+the production storage. Be careful to adapt the configuration of your
+storage to the production environment to make it read only. (The use
+of server environment files is a good way to do so).
@@ -694,8 +692,8 @@
Before this change, when the fs_url was computed the computed value
was always reassigned to the fs_url attribute even if the value was
the same. In a lot of cases the value was the same and the
-reassignment was not necessary. Unfortunately this reassignment has
-as side effect to mark the record as dirty and generate a SQL update
+reassignment was not necessary. Unfortunately this reassignment has as
+side effect to mark the record as dirty and generate a SQL update
statement at the end of the transaction.
(#307)
@@ -763,17 +761,23 @@
From a223d281f7b3fa51480d86ddbcee7f169f607463 Mon Sep 17 00:00:00 2001
From: Fai
Date: Tue, 5 Nov 2024 09:35:38 -0500
Subject: [PATCH 4/4] [17.0][FIX] fs_attachment: allow serving field attachment
with fs stream
---
fs_attachment/models/ir_binary.py | 27 ++++++++++++++++++---------
fs_attachment/tests/test_stream.py | 29 +++++++++++++++++++++++++++++
2 files changed, 47 insertions(+), 9 deletions(-)
diff --git a/fs_attachment/models/ir_binary.py b/fs_attachment/models/ir_binary.py
index 1683dd213c..a784aec9df 100644
--- a/fs_attachment/models/ir_binary.py
+++ b/fs_attachment/models/ir_binary.py
@@ -16,16 +16,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(
@@ -37,8 +35,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)
@@ -100,6 +104,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 e9ceb918bc..f05bf61e30 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
@@ -163,3 +164,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))