Initial commit: OCA Storage packages (17 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:06 +02:00
commit 7a380f05d3
659 changed files with 41828 additions and 0 deletions

View file

@ -0,0 +1,291 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
==========================
Filesystem Storage Backend
==========================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:b9f95387306ce78e4543bc0b90f958fa188ba244dd6df41af486078d2d358fdf
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstorage-lightgray.png?logo=github
:target: https://github.com/OCA/storage/tree/16.0/fs_storage
:alt: OCA/storage
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/storage-16-0/storage-16-0-fs_storage
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/storage&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This addon is a technical addon that allows you to define filesystem like
storage for your data. It's used by other addons to store their data in a
transparent way into different kind of storages.
Through the fs.storage record, you get access to an object that implements
the `fsspec.spec.AbstractFileSystem <https://filesystem-spec.readthedocs.io/en/
latest/api.html#fsspec.spec.AbstractFileSystem>`_ interface and therefore give
you an unified interface to access your data whatever the storage protocol you
decide to use.
The list of supported protocols depends on the installed fsspec implementations.
By default, the addon will install the following protocols:
* LocalFileSystem
* MemoryFileSystem
* ZipFileSystem
* TarFileSystem
* FTPFileSystem
* CachingFileSystem
* WholeFileSystem
* SimplCacheFileSystem
* ReferenceFileSystem
* GenericFileSystem
* DirFileSystem
* DatabricksFileSystem
* GitHubFileSystem
* JupiterFileSystem
* OdooFileSystem
The OdooFileSystem is the one that allows you to store your data into a directory
mounted into your Odoo's storage directory. This is the default FS Storage
when creating a new fs.storage record.
Others protocols are available through the installation of additional
python packages:
* DropboxDriveFileSystem -> `pip install fsspec[dropbox]`
* HTTPFileSystem -> `pip install fsspec[http]`
* HTTPSFileSystem -> `pip install fsspec[http]`
* GCSFileSystem -> `pip install fsspec[gcs]`
* GSFileSystem -> `pip install fsspec[gs]`
* GoogleDriveFileSystem -> `pip install gdrivefs`
* SFTPFileSystem -> `pip install fsspec[sftp]`
* HaddoopFileSystem -> `pip install fsspec[hdfs]`
* S3FileSystem -> `pip install fsspec[s3]`
* WandbFS -> `pip install wandbfs`
* OCIFileSystem -> `pip install fsspec[oci]`
* AsyncLocalFileSystem -> `pip install 'morefs[asynclocalfs]`
* AzureDatalakeFileSystem -> `pip install fsspec[adl]`
* AzureBlobFileSystem -> `pip install fsspec[abfs]`
* DaskWorkerFileSystem -> `pip install fsspec[dask]`
* GitFileSystem -> `pip install fsspec[git]`
* SMBFileSystem -> `pip install fsspec[smb]`
* LibArchiveFileSystem -> `pip install fsspec[libarchive]`
* OSSFileSystem -> `pip install ossfs`
* WebdavFileSystem -> `pip install webdav4`
* DVCFileSystem -> `pip install dvc`
* XRootDFileSystem -> `pip install fsspec-xrootd`
This list of supported protocols is not exhaustive or could change in the future
depending on the fsspec releases. You can find more information about the
supported protocols on the `fsspec documentation
<https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem>`_.
**Table of contents**
.. contents::
:local:
Usage
=====
Configuration
~~~~~~~~~~~~~
When you create a new backend, you must specify the following:
* The name of the backend. This is the name that will be used to
identify the backend into Odoo
* The code of the backend. This code will identify the backend into the store_fname
field of the ir.attachment model. This code must be unique. It will be used
as scheme. example of the store_fname field: ``odoofs://abs34Tg11``.
* The protocol used by the backend. The protocol refers to the supported
protocols of the fsspec python package.
* A directory path. This is a root directory from which the filesystem will
be mounted. This directory must exist.
* The protocol options. These are the options that will be passed to the
fsspec python package when creating the filesystem. These options depend
on the protocol used and are described in the fsspec documentation.
* Resolve env vars. This options resolves the protocol options values starting
with $ from environment variables
* Check Connection Method. If set, Odoo will always check the connection before using
a storage and it will remove the fs connection from the cache if the check fails.
* ``Create Marker file`` : create a hidden file on remote and then check it exists with
Use it if you have write access to the remote and if it is not an issue to leave
the marker file in the root directory.
* ``List file`` : list all files from the root directory. You can use it if the directory
path does not contain a big list of files (for performance reasons)
Some protocols defined in the fsspec package are wrappers around other
protocols. For example, the SimpleCacheFileSystem protocol is a wrapper
around any local filesystem protocol. In such cases, you must specify into the
protocol options the protocol to be wrapped and the options to be passed to
the wrapped protocol.
For example, if you want to create a backend that uses the SimpleCacheFileSystem
protocol, after selecting the SimpleCacheFileSystem protocol, you must specify
the protocol options as follows:
.. code-block:: python
{
"directory_path": "/tmp/my_backend",
"target_protocol": "odoofs",
"target_options": {...},
}
In this example, the SimpleCacheFileSystem protocol will be used as a wrapper
around the odoofs protocol.
Server Environment
~~~~~~~~~~~~~~~~~~
To ease the management of the filesystem storages configuration accross the different
environments, the configuration of the filesystem storages can be defined in
environment files or directly in the main configuration file. For example, the
configuration of a filesystem storage with the code `fsprod` can be provided in the
main configuration file as follows:
.. code-block:: ini
[fs_storage.fsprod]
protocol=s3
options={"endpoint_url": "https://my_s3_server/", "key": "KEY", "secret": "SECRET"}
directory_path=my_bucket
To work, a `storage.backend` record must exist with the code `fsprod` into the database.
In your configuration section, you can specify the value for the following fields:
* `protocol`
* `options`
* `directory_path`
Migration from storage_backend
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The fs_storage addon can be used to replace the storage_backend addon. (It has
been designed to be a drop-in replacement for the storage_backend addon). To
ease the migration, the `fs.storage` model defines the high-level methods
available in the storage_backend model. These methods are:
* `add`
* `get`
* `list_files`
* `find_files`
* `move_files`
* `delete`
These methods are wrappers around the methods of the `fsspec.AbstractFileSystem`
class (see https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem).
These methods are marked as deprecated and will be removed in a future version (V18)
of the addon. You should use the methods of the `fsspec.AbstractFileSystem` class
instead since they are more flexible and powerful. You can access the instance
of the `fsspec.AbstractFileSystem` class using the `fs` property of a `fs.storage`
record.
Known issues / Roadmap
======================
* Transactions: fsspec comes with a transactional mechanism that once started,
gathers all the files created during the transaction, and if the transaction
is committed, moves them to their final locations. It would be useful to
bridge this with the transactional mechanism of odoo. This would allow to
ensure that all the files created during a transaction are either all
moved to their final locations, or all deleted if the transaction is rolled
back. This mechanism is only valid for files created during the transaction
by a call to the `open` method of the file system. It is not valid for others
operations, such as `rm`, `mv_file`, ... .
Changelog
=========
16.0.1.2.0 (2024-02-06)
~~~~~~~~~~~~~~~~~~~~~~~
**Features**
- Invalidate FS filesystem object cache when the connection fails, forcing a reconnection. (`#320 <https://github.com/OCA/storage/issues/320>`_)
16.0.1.1.0 (2023-12-22)
~~~~~~~~~~~~~~~~~~~~~~~
**Features**
- Add parameter on storage backend to resolve protocol options values starting with $ from environment variables (`#303 <https://github.com/OCA/storage/issues/303>`_)
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 <https://github.com/OCA/storage/issues/289>`_)
16.0.1.0.2 (2023-10-09)
~~~~~~~~~~~~~~~~~~~~~~~
**Bugfixes**
- Avoid config error when using the webdav protocol. The auth option is expected
to be a tuple not a list. Since our config is loaded from a json file, we
cannot use tuples. The fix converts the list to a tuple when the config is
related to a webdav protocol and the auth option is into the confix. (`#285 <https://github.com/OCA/storage/issues/285>`_)
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/storage/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/storage/issues/new?body=module:%20fs_storage%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* ACSONE SA/NV
Contributors
~~~~~~~~~~~~
* Laurent Mignon <laurent.mignon@acsone.eu>
* Sébastien BEAU <sebastien.beau@akretion.com>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
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.
This module is part of the `OCA/storage <https://github.com/OCA/storage/tree/16.0/fs_storage>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1,7 @@
# register protocols first
from . import odoo_file_system
from . import rooted_dir_file_system
# then add normal imports
from . import models
from . import wizards

View file

@ -0,0 +1,23 @@
# Copyright 2017 Akretion (http://www.akretion.com).
# @author Sébastien BEAU <sebastien.beau@akretion.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
{
"name": "Filesystem Storage Backend",
"summary": "Implement the concept of Storage with amazon S3, sftp...",
"version": "16.0.1.3.5",
"category": "FS Storage",
"website": "https://github.com/OCA/storage",
"author": " ACSONE SA/NV, Odoo Community Association (OCA)",
"license": "LGPL-3",
"development_status": "Beta",
"installable": True,
"depends": ["base", "base_sparse_field", "server_environment"],
"data": [
"views/fs_storage_view.xml",
"security/ir.model.access.csv",
"wizards/fs_test_connection.xml",
],
"demo": ["demo/fs_storage.xml"],
"external_dependencies": {"python": ["fsspec>=2024.5.0"]},
}

View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="default_fs_storage" model="fs.storage">
<field name="name">Odoo Filesystem Backend</field>
<field name="protocol">odoofs</field>
<field name="code">odoofs</field>
</record>
</odoo>

View file

@ -0,0 +1,325 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fs_storage
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Available options"
msgstr "Dostupne opcije"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_properties
msgid "Available properties"
msgstr "Dostupna svojstva"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__check_connection_method
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__check_connection_method
msgid "Check Connection Method"
msgstr "Metoda provjere konekcije"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Close"
msgstr "Zatvori"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__code
msgid "Code"
msgstr "Šifra"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Failed!"
msgstr "Provjera povezivanja nije uspjela!"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Succeeded!"
msgstr "Provjera povezivanja uspješna!"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Create Marker file"
msgstr "Kreiraj marker datoteku"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_uid
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__create_uid
msgid "Created by"
msgstr "Kreirao"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_date
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__create_date
msgid "Created on"
msgstr "Kreirano"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_protocol
msgid "Describes Protocol"
msgstr "Opisuje protokol"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path
msgid "Directory Path"
msgstr "Putanja direktorija"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_default
msgid "Directory Path Env Default"
msgstr "Env zadana putanja direktorija"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_is_editable
msgid "Directory Path Env Is Editable"
msgstr "Env putanja direktorija je uređiva"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__display_name
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__display_name
msgid "Display Name"
msgstr "Prikazani naziv"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Enter you fsspec options here."
msgstr "Unesite vaše fsspec opcije ovdje."
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env_env_is_editable
msgid "Eval Options From Env Env Is Editable"
msgstr "Eval opcije iz Env Env je uređivo"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Everything seems properly set up!"
msgstr "Sve izgleda ispravno podešeno!"
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_storage_view
#: model:ir.model,name:fs_storage.model_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_search_view
msgid "FS Storage"
msgstr "FS skladište"
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_test_connection_view
msgid "FS Test Connection"
msgstr "FS testiranje konekcije"
#. module: fs_storage
#: model:ir.model,name:fs_storage.model_fs_test_connection
msgid "FS Test Connection Wizard"
msgstr "FS čarobnjak testiranja konekcije"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__id
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__id
msgid "ID"
msgstr "ID"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__json_options
msgid "Json Options"
msgstr "Json opcije"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage____last_update
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection____last_update
msgid "Last Modified on"
msgstr "Zadnje mijenjano"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_uid
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__write_uid
msgid "Last Updated by"
msgstr "Zadnji ažurirao"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_date
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__write_date
msgid "Last Updated on"
msgstr "Zadnje ažurirano"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "List File"
msgstr "Lista datoteka"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__name
msgid "Name"
msgstr "Naziv:"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options
msgid "Options"
msgstr "Opcije"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_env_default
msgid "Options Env Default"
msgstr "Env zadane opcije"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_env_is_editable
msgid "Options Env Is Editable"
msgstr "Env opcije je uređivo"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol
msgid "Protocol"
msgstr "Protokol"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_descr
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Protocol Descr"
msgstr "Opis protokola"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_env_default
msgid "Protocol Env Default"
msgstr "Zadana vrijednost protokola okruženja"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_env_is_editable
msgid "Protocol Env Is Editable"
msgstr "Protokol okruženja može se mijenjati"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path_env_default
msgid "Relative path to the directory to store the file"
msgstr "Relativni put do direktorija za čuvanje datoteka"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env
msgid "Resolve env vars"
msgstr "Riješiti env vars"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env_env_default
msgid "Resolve env vars Env Default"
msgstr "Riješiti env vars Env zadano"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__eval_options_from_env
#: model:ir.model.fields,help:fs_storage.field_fs_storage__eval_options_from_env_env_default
msgid ""
"Resolve options values starting with $ from environment variables. e.g\n"
" {\n"
" \"endpoint_url\": \"$AWS_ENDPOINT_URL\",\n"
" }\n"
" "
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__server_env_defaults
msgid "Server Env Defaults"
msgstr "Zadane vrijednosti serverskog okruženja"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__check_connection_method
msgid ""
"Set a method if you want the connection to remote to be checked every time the storage is used, in order to remove the obsolete connection from the cache.\n"
"* Create Marker file : Create a file on remote and check it exists\n"
"* List File : List all files from root directory"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__storage_id
msgid "Storage"
msgstr "Skladište"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__code
msgid ""
"Technical code used to identify the storage backend into the code.This code "
"must be unique. This code is used for example to define the storage backend "
"to store the attachments via the configuration parameter "
"'ir_attachment.storage.force.database' when the module 'fs_attachment' is "
"installed."
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Test Connection"
msgstr "Testiraj vezu"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Test connection"
msgstr "Testiraj konekciju"
#. module: fs_storage
#: model:ir.model.constraint,message:fs_storage.constraint_fs_storage_code_uniq
msgid "The code must be unique"
msgstr "Kod mora biti jedinstven"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "The options must be a valid JSON"
msgstr "Opcije moraju biti važeći JSON"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__json_options
msgid "The options used to initialize the filesystem.\n"
msgstr "Opcije korišćene za inicijalizaciju sistema datoteka.\\\n"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options_env_default
msgid ""
"The options used to initialize the filesystem.\n"
"This is a JSON field that depends on the protocol used.\n"
"For example, for the sftp protocol, you can provide the following:\n"
"{\n"
" 'host': 'my.sftp.server',\n"
" 'ssh_kwrags': {\n"
" 'username': 'myuser',\n"
" 'password': 'mypassword',\n"
" 'port': 22,\n"
" }\n"
"}\n"
"For more information, please refer to the fsspec documentation:\n"
"https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-implementations"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options_protocol
#: model:ir.model.fields,help:fs_storage.field_fs_storage__protocol
#: model:ir.model.fields,help:fs_storage.field_fs_storage__protocol_env_default
msgid ""
"The protocol used to access the content of filesystem.\n"
"This list is the one supported by the fsspec library (see https://filesystem-spec.readthedocs.io/en/latest). A filesystem protocolis added by default and refers to the odoo local filesystem.\n"
"Pay attention that according to the protocol, some options must beprovided through the options field."
msgstr ""

View file

@ -0,0 +1,326 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fs_storage
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Available options"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_properties
msgid "Available properties"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__check_connection_method
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__check_connection_method
msgid "Check Connection Method"
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Close"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__code
msgid "Code"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Failed!"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Succeeded!"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Create Marker file"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_uid
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__create_uid
msgid "Created by"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_date
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__create_date
msgid "Created on"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_protocol
msgid "Describes Protocol"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path
msgid "Directory Path"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_default
msgid "Directory Path Env Default"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_is_editable
msgid "Directory Path Env Is Editable"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__display_name
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__display_name
msgid "Display Name"
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Enter you fsspec options here."
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env_env_is_editable
msgid "Eval Options From Env Env Is Editable"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Everything seems properly set up!"
msgstr ""
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_storage_view
#: model:ir.model,name:fs_storage.model_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_search_view
msgid "FS Storage"
msgstr ""
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_test_connection_view
msgid "FS Test Connection"
msgstr ""
#. module: fs_storage
#: model:ir.model,name:fs_storage.model_fs_test_connection
msgid "FS Test Connection Wizard"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__id
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__id
msgid "ID"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__json_options
msgid "Json Options"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage____last_update
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection____last_update
msgid "Last Modified on"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_uid
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__write_uid
msgid "Last Updated by"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_date
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__write_date
msgid "Last Updated on"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "List File"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__name
msgid "Name"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options
msgid "Options"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_env_default
msgid "Options Env Default"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_env_is_editable
msgid "Options Env Is Editable"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol
msgid "Protocol"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_descr
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Protocol Descr"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_env_default
msgid "Protocol Env Default"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_env_is_editable
msgid "Protocol Env Is Editable"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path_env_default
msgid "Relative path to the directory to store the file"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env
msgid "Resolve env vars"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env_env_default
msgid "Resolve env vars Env Default"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__eval_options_from_env
#: model:ir.model.fields,help:fs_storage.field_fs_storage__eval_options_from_env_env_default
msgid ""
"Resolve options values starting with $ from environment variables. e.g\n"
" {\n"
" \"endpoint_url\": \"$AWS_ENDPOINT_URL\",\n"
" }\n"
" "
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__server_env_defaults
msgid "Server Env Defaults"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__check_connection_method
msgid ""
"Set a method if you want the connection to remote to be checked every time the storage is used, in order to remove the obsolete connection from the cache.\n"
"* Create Marker file : Create a file on remote and check it exists\n"
"* List File : List all files from root directory"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__storage_id
msgid "Storage"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__code
msgid ""
"Technical code used to identify the storage backend into the code.This code "
"must be unique. This code is used for example to define the storage backend "
"to store the attachments via the configuration parameter "
"'ir_attachment.storage.force.database' when the module 'fs_attachment' is "
"installed."
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Test Connection"
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Test connection"
msgstr ""
#. module: fs_storage
#: model:ir.model.constraint,message:fs_storage.constraint_fs_storage_code_uniq
msgid "The code must be unique"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "The options must be a valid JSON"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__json_options
msgid "The options used to initialize the filesystem.\n"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options_env_default
msgid ""
"The options used to initialize the filesystem.\n"
"This is a JSON field that depends on the protocol used.\n"
"For example, for the sftp protocol, you can provide the following:\n"
"{\n"
" 'host': 'my.sftp.server',\n"
" 'ssh_kwrags': {\n"
" 'username': 'myuser',\n"
" 'password': 'mypassword',\n"
" 'port': 22,\n"
" }\n"
"}\n"
"For more information, please refer to the fsspec documentation:\n"
"https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-implementations"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options_protocol
#: model:ir.model.fields,help:fs_storage.field_fs_storage__protocol
#: model:ir.model.fields,help:fs_storage.field_fs_storage__protocol_env_default
msgid ""
"The protocol used to access the content of filesystem.\n"
"This list is the one supported by the fsspec library (see https://filesystem-spec.readthedocs.io/en/latest). A filesystem protocolis added by default and refers to the odoo local filesystem.\n"
"Pay attention that according to the protocol, some options must beprovided through the options field."
msgstr ""

View file

@ -0,0 +1,364 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fs_storage
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-12-28 02:07+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.es>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Available options"
msgstr "Opciones disponibles"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_properties
msgid "Available properties"
msgstr "Propiedades disponibles"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__check_connection_method
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__check_connection_method
msgid "Check Connection Method"
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Close"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__code
msgid "Code"
msgstr "Código"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Failed!"
msgstr "¡Conexión de prueba fallida!"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Succeeded!"
msgstr "¡Conexión de prueba exitosa!"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Create Marker file"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_uid
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__create_uid
msgid "Created by"
msgstr "Creado por"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_date
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__create_date
msgid "Created on"
msgstr "Creado el"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_protocol
msgid "Describes Protocol"
msgstr "Descripción del Protocolo"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path
msgid "Directory Path"
msgstr "Ruta del Directorio"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_default
msgid "Directory Path Env Default"
msgstr "Ruta del Directorio Env Predet"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_is_editable
msgid "Directory Path Env Is Editable"
msgstr "La Ruta de Directorio Env es Editable"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__display_name
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__display_name
msgid "Display Name"
msgstr "Mostrar Nombre"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Enter you fsspec options here."
msgstr "Introduzca aquí sus opciones fsspec."
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env_env_is_editable
msgid "Eval Options From Env Env Is Editable"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Everything seems properly set up!"
msgstr "¡Todo parece correctamente configurado!"
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_storage_view
#: model:ir.model,name:fs_storage.model_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_search_view
msgid "FS Storage"
msgstr "Almacenamiento FS"
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_test_connection_view
msgid "FS Test Connection"
msgstr ""
#. module: fs_storage
#: model:ir.model,name:fs_storage.model_fs_test_connection
msgid "FS Test Connection Wizard"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__id
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__id
msgid "ID"
msgstr "ID (identificación)"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__json_options
msgid "Json Options"
msgstr "Opciones Json"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage____last_update
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection____last_update
msgid "Last Modified on"
msgstr "Última Modificación el"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_uid
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__write_uid
msgid "Last Updated by"
msgstr "Actualizado por Última vez por"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_date
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__write_date
msgid "Last Updated on"
msgstr "Última Actualización el"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "List File"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__name
msgid "Name"
msgstr "Nombre"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options
msgid "Options"
msgstr "Opciones"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_env_default
msgid "Options Env Default"
msgstr "Opciones Env Por Defecto"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_env_is_editable
msgid "Options Env Is Editable"
msgstr "Las Opciones Env son Editables"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol
msgid "Protocol"
msgstr "Protocolo"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_descr
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Protocol Descr"
msgstr "Descr. del Protocolo"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_env_default
msgid "Protocol Env Default"
msgstr "Protocolo Env Predeterminado"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_env_is_editable
msgid "Protocol Env Is Editable"
msgstr "El Protocolo Env es Editable"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path_env_default
msgid "Relative path to the directory to store the file"
msgstr "Ruta relativa al directorio para almacenar el archivo"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env
msgid "Resolve env vars"
msgstr "Resolver las variables de entorno"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env_env_default
msgid "Resolve env vars Env Default"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__eval_options_from_env
#: model:ir.model.fields,help:fs_storage.field_fs_storage__eval_options_from_env_env_default
msgid ""
"Resolve options values starting with $ from environment variables. e.g\n"
" {\n"
" \"endpoint_url\": \"$AWS_ENDPOINT_URL\",\n"
" }\n"
" "
msgstr ""
"Resuelva los valores de opciones que comienzan con $ a partir de variables "
"de entorno. p.ej\n"
" {\n"
" \"endpoint_url\": \"$AWS_ENDPOINT_URL\",\n"
" }\n"
" "
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__server_env_defaults
msgid "Server Env Defaults"
msgstr "Valores por defecto del Entorno de Servidor"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__check_connection_method
msgid ""
"Set a method if you want the connection to remote to be checked every time "
"the storage is used, in order to remove the obsolete connection from the "
"cache.\n"
"* Create Marker file : Create a file on remote and check it exists\n"
"* List File : List all files from root directory"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__storage_id
msgid "Storage"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__code
msgid ""
"Technical code used to identify the storage backend into the code.This code "
"must be unique. This code is used for example to define the storage backend "
"to store the attachments via the configuration parameter 'ir_attachment."
"storage.force.database' when the module 'fs_attachment' is installed."
msgstr ""
"Código técnico utilizado para identificar el servidor de almacenamiento en "
"el código. Este código debe ser único. Este código se utiliza, por ejemplo, "
"para definir el servidor de almacenamiento para guardar los archivos "
"adjuntos mediante el parámetro de configuración \"ir_attachment.storage."
"force.database\" cuando se instala el módulo \"fs_attachment\"."
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Test Connection"
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Test connection"
msgstr "Probar conexión"
#. module: fs_storage
#: model:ir.model.constraint,message:fs_storage.constraint_fs_storage_code_uniq
msgid "The code must be unique"
msgstr "El código tiene que ser único"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "The options must be a valid JSON"
msgstr "Las opciones tienen que estar definidas en un JSON válido"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__json_options
msgid "The options used to initialize the filesystem.\n"
msgstr "Las opciones utilizadas para inicializar el sistema de archivos.\n"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options_env_default
msgid ""
"The options used to initialize the filesystem.\n"
"This is a JSON field that depends on the protocol used.\n"
"For example, for the sftp protocol, you can provide the following:\n"
"{\n"
" 'host': 'my.sftp.server',\n"
" 'ssh_kwrags': {\n"
" 'username': 'myuser',\n"
" 'password': 'mypassword',\n"
" 'port': 22,\n"
" }\n"
"}\n"
"For more information, please refer to the fsspec documentation:\n"
"https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-"
"implementations"
msgstr ""
"Las opciones utilizadas para inicializar el sistema de ficheros.\n"
"Este es un campo JSON que depende del protocolo utilizado.\n"
"Por ejemplo, para el protocolo sftp, puede proporcionar lo siguiente:\n"
"{\n"
" 'host': 'mi.sftp.server',\n"
" 'ssh_kwrags': {\n"
" 'username': 'myuser',\n"
" 'password': 'mypassword',\n"
" ' port': 22,\n"
" }\n"
"}\n"
"Para más información, consulta la documentación de fsspec:\n"
"https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-"
"implementations"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options_protocol
#: model:ir.model.fields,help:fs_storage.field_fs_storage__protocol
#: model:ir.model.fields,help:fs_storage.field_fs_storage__protocol_env_default
msgid ""
"The protocol used to access the content of filesystem.\n"
"This list is the one supported by the fsspec library (see https://filesystem-"
"spec.readthedocs.io/en/latest). A filesystem protocolis added by default and "
"refers to the odoo local filesystem.\n"
"Pay attention that according to the protocol, some options must beprovided "
"through the options field."
msgstr ""
"El protocolo utilizado para acceder al contenido del sistema de ficheros.\n"
"Esta lista es la soportada por la librería fsspec (ver https://filesystem-"
"spec.readthedocs.io/en/latest). Un protocolo de sistema de archivos es "
"agregado por defecto y se refiere al sistema de archivos local de odoo.\n"
"Preste atención que de acuerdo al protocolo, algunas opciones deben ser "
"provistas a través del campo opciones."

View file

@ -0,0 +1,325 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fs_storage
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Available options"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_properties
msgid "Available properties"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__check_connection_method
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__check_connection_method
msgid "Check Connection Method"
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Close"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__code
msgid "Code"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Failed!"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Succeeded!"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Create Marker file"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_uid
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__create_uid
msgid "Created by"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_date
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__create_date
msgid "Created on"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_protocol
msgid "Describes Protocol"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path
msgid "Directory Path"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_default
msgid "Directory Path Env Default"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_is_editable
msgid "Directory Path Env Is Editable"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__display_name
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__display_name
msgid "Display Name"
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Enter you fsspec options here."
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env_env_is_editable
msgid "Eval Options From Env Env Is Editable"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Everything seems properly set up!"
msgstr ""
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_storage_view
#: model:ir.model,name:fs_storage.model_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_search_view
msgid "FS Storage"
msgstr ""
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_test_connection_view
msgid "FS Test Connection"
msgstr ""
#. module: fs_storage
#: model:ir.model,name:fs_storage.model_fs_test_connection
msgid "FS Test Connection Wizard"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__id
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__id
msgid "ID"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__json_options
msgid "Json Options"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage____last_update
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection____last_update
msgid "Last Modified on"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_uid
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__write_uid
msgid "Last Updated by"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_date
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__write_date
msgid "Last Updated on"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "List File"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__name
msgid "Name"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options
msgid "Options"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_env_default
msgid "Options Env Default"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_env_is_editable
msgid "Options Env Is Editable"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol
msgid "Protocol"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_descr
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Protocol Descr"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_env_default
msgid "Protocol Env Default"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_env_is_editable
msgid "Protocol Env Is Editable"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path_env_default
msgid "Relative path to the directory to store the file"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env
msgid "Resolve env vars"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env_env_default
msgid "Resolve env vars Env Default"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__eval_options_from_env
#: model:ir.model.fields,help:fs_storage.field_fs_storage__eval_options_from_env_env_default
msgid ""
"Resolve options values starting with $ from environment variables. e.g\n"
" {\n"
" \"endpoint_url\": \"$AWS_ENDPOINT_URL\",\n"
" }\n"
" "
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__server_env_defaults
msgid "Server Env Defaults"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__check_connection_method
msgid ""
"Set a method if you want the connection to remote to be checked every time the storage is used, in order to remove the obsolete connection from the cache.\n"
"* Create Marker file : Create a file on remote and check it exists\n"
"* List File : List all files from root directory"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__storage_id
msgid "Storage"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__code
msgid ""
"Technical code used to identify the storage backend into the code.This code "
"must be unique. This code is used for example to define the storage backend "
"to store the attachments via the configuration parameter "
"'ir_attachment.storage.force.database' when the module 'fs_attachment' is "
"installed."
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Test Connection"
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Test connection"
msgstr ""
#. module: fs_storage
#: model:ir.model.constraint,message:fs_storage.constraint_fs_storage_code_uniq
msgid "The code must be unique"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "The options must be a valid JSON"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__json_options
msgid "The options used to initialize the filesystem.\n"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options_env_default
msgid ""
"The options used to initialize the filesystem.\n"
"This is a JSON field that depends on the protocol used.\n"
"For example, for the sftp protocol, you can provide the following:\n"
"{\n"
" 'host': 'my.sftp.server',\n"
" 'ssh_kwrags': {\n"
" 'username': 'myuser',\n"
" 'password': 'mypassword',\n"
" 'port': 22,\n"
" }\n"
"}\n"
"For more information, please refer to the fsspec documentation:\n"
"https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-implementations"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options_protocol
#: model:ir.model.fields,help:fs_storage.field_fs_storage__protocol
#: model:ir.model.fields,help:fs_storage.field_fs_storage__protocol_env_default
msgid ""
"The protocol used to access the content of filesystem.\n"
"This list is the one supported by the fsspec library (see https://filesystem-spec.readthedocs.io/en/latest). A filesystem protocolis added by default and refers to the odoo local filesystem.\n"
"Pay attention that according to the protocol, some options must beprovided through the options field."
msgstr ""

View file

@ -0,0 +1,368 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fs_storage
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-05-16 06:39+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.4\n"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Available options"
msgstr "Opzioni disponibili"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_properties
msgid "Available properties"
msgstr "Proprietà disponibili"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__check_connection_method
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__check_connection_method
msgid "Check Connection Method"
msgstr "Controlla metodo di connessione"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Close"
msgstr "Chiudi"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__code
msgid "Code"
msgstr "Codice"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Failed!"
msgstr "Test connessione fallito!"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Succeeded!"
msgstr "Test connessione avvenuto con successo!"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Create Marker file"
msgstr "Crea file marcatore"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_uid
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__create_uid
msgid "Created by"
msgstr "Creato da"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_date
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__create_date
msgid "Created on"
msgstr "Creato il"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_protocol
msgid "Describes Protocol"
msgstr "Descrive protocollo"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path
msgid "Directory Path"
msgstr "Percorso cartella"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_default
msgid "Directory Path Env Default"
msgstr "Percorso cartella ambiente predefinito"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_is_editable
msgid "Directory Path Env Is Editable"
msgstr "Percorso cartella ambiente è modificabile"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__display_name
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__display_name
msgid "Display Name"
msgstr "Nome visualizzato"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Enter you fsspec options here."
msgstr "Inserire qui le opzioni FSspec."
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env_env_is_editable
msgid "Eval Options From Env Env Is Editable"
msgstr "La valutazione opzioni da ambiente è modificabile"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Everything seems properly set up!"
msgstr "Tutto sembra impostato correttamente!"
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_storage_view
#: model:ir.model,name:fs_storage.model_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_search_view
msgid "FS Storage"
msgstr "Deposito FS"
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_test_connection_view
msgid "FS Test Connection"
msgstr "Test connessione FS"
#. module: fs_storage
#: model:ir.model,name:fs_storage.model_fs_test_connection
msgid "FS Test Connection Wizard"
msgstr "Procedura guidata test connessione FS"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__id
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__id
msgid "ID"
msgstr "ID"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__json_options
msgid "Json Options"
msgstr "Opzioni JSON"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage____last_update
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection____last_update
msgid "Last Modified on"
msgstr "Ultima modifica il"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_uid
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__write_uid
msgid "Last Updated by"
msgstr "Ultimo aggiornamento di"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_date
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__write_date
msgid "Last Updated on"
msgstr "Ultimo aggiornamento il"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "List File"
msgstr "Elenco file"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__name
msgid "Name"
msgstr "Nome"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options
msgid "Options"
msgstr "Opzioni"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_env_default
msgid "Options Env Default"
msgstr "Opzioni ambiente predefinite"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__options_env_is_editable
msgid "Options Env Is Editable"
msgstr "Opzioni ambiente sono modificabili"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol
msgid "Protocol"
msgstr "Protcollo"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_descr
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
msgid "Protocol Descr"
msgstr "Descrizione protocollo"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_env_default
msgid "Protocol Env Default"
msgstr "Protocollo ambiene predefinito"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__protocol_env_is_editable
msgid "Protocol Env Is Editable"
msgstr "Protocollo ambiente è modificabile"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path_env_default
msgid "Relative path to the directory to store the file"
msgstr "Percorso relativo alla cartella per archiviare il file"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env
msgid "Resolve env vars"
msgstr "Risole variabili ambiente"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__eval_options_from_env_env_default
msgid "Resolve env vars Env Default"
msgstr "Risolvi le variabili ambiente predefinito"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__eval_options_from_env
#: model:ir.model.fields,help:fs_storage.field_fs_storage__eval_options_from_env_env_default
msgid ""
"Resolve options values starting with $ from environment variables. e.g\n"
" {\n"
" \"endpoint_url\": \"$AWS_ENDPOINT_URL\",\n"
" }\n"
" "
msgstr ""
"Risolve valori opzioni iniziando con $ dalle variabili ambiente, es.\n"
" {\n"
" \"endpoint_url\": \"$AWS_ENDPOINT_URL\",\n"
" }\n"
" "
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__server_env_defaults
msgid "Server Env Defaults"
msgstr "Predefiniti ambiente server"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__check_connection_method
msgid ""
"Set a method if you want the connection to remote to be checked every time "
"the storage is used, in order to remove the obsolete connection from the "
"cache.\n"
"* Create Marker file : Create a file on remote and check it exists\n"
"* List File : List all files from root directory"
msgstr ""
"Impostare un metodo se si vuole verificare la connessione remota ogni volta "
"che il deposito è utilizzato, per eliminare la connessione obsoleta dalla "
"cache.\n"
"* Crea file marcatore : crea un file in remoto e controlla se esiste\n"
"* Elenco file : elenca tutti i file dalla cartella radice"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_test_connection__storage_id
msgid "Storage"
msgstr "Deposito"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__code
msgid ""
"Technical code used to identify the storage backend into the code.This code "
"must be unique. This code is used for example to define the storage backend "
"to store the attachments via the configuration parameter 'ir_attachment."
"storage.force.database' when the module 'fs_attachment' is installed."
msgstr ""
"Codice tecnico usato per identificare il backend deposito nel codice. Questo "
"codice deve essere univoco. Questo codice è utilizzato per esempio per "
"definire il backend deposito dove depositare gli allegati attraverso il "
"parametro configurazione 'ir_attachment.storage.force.database' quando il "
"modulo 'fs_attachment' è installato."
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Test Connection"
msgstr "Prova connessione"
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_form_view
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_test_connection_form_view
msgid "Test connection"
msgstr "Prova connessione"
#. module: fs_storage
#: model:ir.model.constraint,message:fs_storage.constraint_fs_storage_code_uniq
msgid "The code must be unique"
msgstr "Il codice deve essere univoco"
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "The options must be a valid JSON"
msgstr "L'opzione deve essere un JSON valido"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__json_options
msgid "The options used to initialize the filesystem.\n"
msgstr "Le opzioni per inizializzare il filesystem.\n"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options_env_default
msgid ""
"The options used to initialize the filesystem.\n"
"This is a JSON field that depends on the protocol used.\n"
"For example, for the sftp protocol, you can provide the following:\n"
"{\n"
" 'host': 'my.sftp.server',\n"
" 'ssh_kwrags': {\n"
" 'username': 'myuser',\n"
" 'password': 'mypassword',\n"
" 'port': 22,\n"
" }\n"
"}\n"
"For more information, please refer to the fsspec documentation:\n"
"https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-"
"implementations"
msgstr ""
"Le opzioni utilizzate per inizializzare il filesystem.\n"
"Questo è uncampo JSON che dipende dal protocollo utilizzato.\n"
"Per esempio, per il protocollo SFTP, si può fornire il seguente:\n"
"{\n"
" 'host': 'my.sftp.server',\n"
" 'ssh_kwrags': {\n"
" 'username': 'myuser',\n"
" 'password': 'mypassword',\n"
" 'port': 22,\n"
" }\n"
"}\n"
"Per ulteriori informazioni, fare riferimento alla documentazione FSspec:\n"
"https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-"
"implementations"
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__options_protocol
#: model:ir.model.fields,help:fs_storage.field_fs_storage__protocol
#: model:ir.model.fields,help:fs_storage.field_fs_storage__protocol_env_default
msgid ""
"The protocol used to access the content of filesystem.\n"
"This list is the one supported by the fsspec library (see https://filesystem-"
"spec.readthedocs.io/en/latest). A filesystem protocolis added by default and "
"refers to the odoo local filesystem.\n"
"Pay attention that according to the protocol, some options must beprovided "
"through the options field."
msgstr ""
"Il protocollo è utilizzato per accedere al contenuto del filesystem.\n"
"Questo elenco è quello supportato dalla libreria FSspec (vedere https://"
"filesystem-spec.readthedocs.io/en/latest). Un protocollo filesystem è "
"aggiunto in modo predefinito e fa riferimento al filesystem locale Odoo.\n"
"Fare attenzione che in accordo con il protocollo, alcune opzioni devono "
"essere fonrite attraverso il campo opzioni."

View file

@ -0,0 +1,144 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fs_storage
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__backend_type_env_default
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_default
msgid " Env Default"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/components/filesystem_adapter.py:0
#, python-format
msgid "Access to %s is forbidden"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__backend_type
msgid "Backend Type"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__backend_type_env_is_editable
msgid "Backend Type Env Is Editable"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Failed!"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Connection Test Succeeded!"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_uid
msgid "Created by"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__create_date
msgid "Created on"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path
msgid "Directory Path"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__directory_path_env_is_editable
msgid "Directory Path Env Is Editable"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__display_name
msgid "Display Name"
msgstr ""
#. module: fs_storage
#. odoo-python
#: code:addons/fs_storage/models/fs_storage.py:0
#, python-format
msgid "Everything seems properly set up!"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields.selection,name:fs_storage.selection__fs_storage__backend_type__filesystem
msgid "Filesystem"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__has_validation
msgid "Has Validation"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__id
msgid "ID"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage____last_update
msgid "Last Modified on"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_uid
msgid "Last Updated by"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__write_date
msgid "Last Updated on"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__name
msgid "Name"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path
#: model:ir.model.fields,help:fs_storage.field_fs_storage__directory_path_env_default
msgid "Relative path to the directory to store the file"
msgstr ""
#. module: fs_storage
#: model:ir.model.fields,field_description:fs_storage.field_fs_storage__server_env_defaults
msgid "Server Env Defaults"
msgstr ""
#. module: fs_storage
#: model:ir.actions.act_window,name:fs_storage.act_open_fs_storage_view
#: model:ir.model,name:fs_storage.model_fs_storage
#: model:ir.ui.menu,name:fs_storage.menu_storage
#: model:ir.ui.menu,name:fs_storage.menu_fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_view_form
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_view_search
msgid "FS Storage"
msgstr ""
#. module: fs_storage
#: model_terms:ir.ui.view,arch_db:fs_storage.fs_storage_view_form
msgid "Test connection"
msgstr ""

View file

@ -0,0 +1 @@
from . import fs_storage

View file

@ -0,0 +1,522 @@
# Copyright 2023 ACSONE SA/NV (https://www.acsone.eu).
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from __future__ import annotations
import base64
import functools
import inspect
import json
import logging
import os.path
import re
import warnings
from typing import AnyStr
import fsspec
from odoo import _, api, fields, models, tools
from odoo.exceptions import ValidationError
from odoo.addons.base_sparse_field.models.fields import Serialized
_logger = logging.getLogger(__name__)
# TODO: useful for the whole OCA?
def deprecated(reason):
"""Mark functions or classes as deprecated.
Emit warning at execution.
The @deprecated is used with a 'reason'.
.. code-block:: python
@deprecated("please, use another function")
def old_function(x, y):
pass
"""
def decorator(func1):
if inspect.isclass(func1):
fmt1 = "Call to deprecated class {name} ({reason})."
else:
fmt1 = "Call to deprecated function {name} ({reason})."
@functools.wraps(func1)
def new_func1(*args, **kwargs):
warnings.simplefilter("always", DeprecationWarning)
warnings.warn(
fmt1.format(name=func1.__name__, reason=reason),
category=DeprecationWarning,
stacklevel=2,
)
warnings.simplefilter("default", DeprecationWarning)
return func1(*args, **kwargs)
return new_func1
return decorator
class FSStorage(models.Model):
_name = "fs.storage"
_inherit = "server.env.mixin"
_description = "FS Storage"
__slots__ = ("__fs", "__odoo_storage_path")
def __init__(self, env, ids=(), prefetch_ids=()):
super().__init__(env, ids=ids, prefetch_ids=prefetch_ids)
self.__fs = None
self.__odoo_storage_path = None
name = fields.Char(required=True)
code = fields.Char(
required=True,
help="Technical code used to identify the storage backend into the code."
"This code must be unique. This code is used for example to define the "
"storage backend to store the attachments via the configuration parameter "
"'ir_attachment.storage.force.database' when the module 'fs_attachment' "
"is installed.",
)
protocol = fields.Selection(
selection="_get_protocols",
required=True,
help="The protocol used to access the content of filesystem.\n"
"This list is the one supported by the fsspec library (see "
"https://filesystem-spec.readthedocs.io/en/latest). A filesystem protocol"
"is added by default and refers to the odoo local filesystem.\n"
"Pay attention that according to the protocol, some options must be"
"provided through the options field.",
)
protocol_descr = fields.Text(
compute="_compute_protocol_descr",
)
options = fields.Text(
help="The options used to initialize the filesystem.\n"
"This is a JSON field that depends on the protocol used.\n"
"For example, for the sftp protocol, you can provide the following:\n"
"{\n"
" 'host': 'my.sftp.server',\n"
" 'ssh_kwrags': {\n"
" 'username': 'myuser',\n"
" 'password': 'mypassword',\n"
" 'port': 22,\n"
" }\n"
"}\n"
"For more information, please refer to the fsspec documentation:\n"
"https://filesystem-spec.readthedocs.io/en/latest/api.html#built-in-implementations"
)
json_options = Serialized(
help="The options used to initialize the filesystem.\n",
compute="_compute_json_options",
inverse="_inverse_json_options",
)
eval_options_from_env = fields.Boolean(
string="Resolve env vars",
help="""Resolve options values starting with $ from environment variables. e.g
{
"endpoint_url": "$AWS_ENDPOINT_URL",
}
""",
)
directory_path = fields.Char(
help="Relative path to the directory to store the file"
)
# the next fields are used to display documentation to help the user
# to configure the backend
options_protocol = fields.Selection(
string="Describes Protocol",
selection="_get_options_protocol",
compute="_compute_protocol_descr",
help="The protocol used to access the content of filesystem.\n"
"This list is the one supported by the fsspec library (see "
"https://filesystem-spec.readthedocs.io/en/latest). A filesystem protocol"
"is added by default and refers to the odoo local filesystem.\n"
"Pay attention that according to the protocol, some options must be"
"provided through the options field.",
)
options_properties = fields.Text(
string="Available properties",
compute="_compute_options_properties",
store=False,
)
check_connection_method = fields.Selection(
selection="_get_check_connection_method_selection",
default="marker_file",
help="Set a method if you want the connection to remote to be checked every "
"time the storage is used, in order to remove the obsolete connection from"
" the cache.\n"
"* Create Marker file : Create a file on remote and check it exists\n"
"* List File : List all files from root directory",
)
_sql_constraints = [
(
"code_uniq",
"unique(code)",
"The code must be unique",
),
]
_server_env_section_name_field = "code"
@api.model
def _get_check_connection_method_selection(self):
return [
("marker_file", _("Create Marker file")),
("ls", _("List File")),
]
@property
def _server_env_fields(self):
return {
"protocol": {},
"options": {},
"directory_path": {},
"eval_options_from_env": {},
}
def write(self, vals):
self.__fs = None
self.clear_caches()
return super().write(vals)
@api.model
@tools.ormcache()
def get_id_by_code_map(self):
"""Return a dictionary with the code as key and the id as value."""
return {rec.code: rec.id for rec in self.sudo().search([])}
@api.model
def get_id_by_code(self, code):
"""Return the id of the filesystem associated to the given code."""
return self.get_id_by_code_map().get(code)
@api.model
def get_by_code(self, code) -> FSStorage:
"""Return the filesystem associated to the given code."""
res = self.browse()
res_id = self.get_id_by_code(code)
if res_id:
res = self.browse(res_id)
return res
@api.model
@tools.ormcache()
def get_storage_codes(self):
"""Return the list of codes of the existing filesystems."""
return [s.code for s in self.search([])]
@api.model
@tools.ormcache("code")
def get_fs_by_code(self, code):
"""Return the filesystem associated to the given code.
:param code: the code of the filesystem
"""
fs = None
fs_storage = self.get_by_code(code)
if fs_storage:
fs = fs_storage.fs
return fs
def copy(self, default=None):
default = default or {}
if "code" not in default:
default["code"] = "{}_copy".format(self.code)
return super().copy(default)
@api.model
def _get_protocols(self) -> list[tuple[str, str]]:
protocol = [("odoofs", "Odoo's FileSystem")]
for p in fsspec.available_protocols():
try:
cls = fsspec.get_filesystem_class(p)
protocol.append((p, f"{p} ({cls.__name__})"))
except Exception as e:
_logger.debug("Cannot load the protocol %s. Reason: %s", p, e)
return protocol
@api.constrains("options")
def _check_options(self) -> None:
for rec in self:
try:
json.loads(rec.options or "{}")
except Exception as e:
raise ValidationError(_("The options must be a valid JSON")) from e
@api.depends("options")
def _compute_json_options(self) -> None:
for rec in self:
rec.json_options = json.loads(rec.options or "{}")
def _inverse_json_options(self) -> None:
for rec in self:
rec.options = json.dumps(rec.json_options)
@api.depends("protocol")
def _compute_protocol_descr(self) -> None:
for rec in self:
rec.protocol_descr = fsspec.get_filesystem_class(rec.protocol).__doc__
rec.options_protocol = rec.protocol
@api.model
def _get_options_protocol(self) -> list[tuple[str, str]]:
protocol = [("odoofs", "Odoo's Filesystem")]
for p in fsspec.available_protocols():
try:
fsspec.get_filesystem_class(p)
protocol.append((p, p))
except Exception as e:
_logger.debug("Cannot load the protocol %s. Reason: %s", p, e)
return protocol
@api.depends("options_protocol")
def _compute_options_properties(self) -> None:
for rec in self:
cls = fsspec.get_filesystem_class(rec.options_protocol)
signature = inspect.signature(cls.__init__)
doc = inspect.getdoc(cls.__init__)
rec.options_properties = f"__init__{signature}\n{doc}"
def _get_marker_file_name(self):
return ".odoo_fs_storage_%s.marker" % self.id
def _marker_file_check_connection(self, fs):
marker_file_name = self._get_marker_file_name()
try:
fs.info(marker_file_name)
except FileNotFoundError:
fs.touch(marker_file_name)
def _ls_check_connection(self, fs):
fs.ls("", detail=False)
def _check_connection(self, fs, check_connection_method):
if check_connection_method == "marker_file":
self._marker_file_check_connection(fs)
elif check_connection_method == "ls":
self._ls_check_connection(fs)
return True
@property
def fs(self) -> fsspec.AbstractFileSystem:
"""Get the fsspec filesystem for this backend."""
self.ensure_one()
if not self.__fs:
self.__fs = self.sudo()._get_filesystem()
if not tools.config["test_enable"]:
# Check whether we need to invalidate FS cache or not.
# Use a marker file to limit the scope of the LS command for performance.
try:
self._check_connection(self.__fs, self.check_connection_method)
except Exception as e:
self.__fs.clear_instance_cache()
self.__fs = None
raise e
return self.__fs
def _get_filesystem_storage_path(self) -> str:
"""Get the path to the storage directory.
This path is relative to the odoo filestore.and is used as root path
when the protocol is filesystem.
"""
self.ensure_one()
path = os.path.join(self.env["ir.attachment"]._filestore(), "storage")
if not os.path.exists(path):
os.makedirs(path)
return path
@property
def _odoo_storage_path(self) -> str:
"""Get the path to the storage directory.
This path is relative to the odoo filestore.and is used as root path
when the protocol is filesystem.
"""
if not self.__odoo_storage_path:
self.__odoo_storage_path = self._get_filesystem_storage_path()
return self.__odoo_storage_path
def _recursive_add_odoo_storage_path(self, options: dict) -> dict:
"""Add the odoo storage path to the options.
This is a recursive function that will add the odoo_storage_path
option to the nested target_options if the target_protocol is
odoofs
"""
if "target_protocol" in options:
target_options = options.get("target_options", {})
if options["target_protocol"] == "odoofs":
target_options["odoo_storage_path"] = self._odoo_storage_path
options["target_options"] = target_options
self._recursive_add_odoo_storage_path(target_options)
return options
def _eval_options_from_env(self, options):
values = {}
for key, value in options.items():
if isinstance(value, dict):
values[key] = self._eval_options_from_env(value)
elif isinstance(value, str) and value.startswith("$"):
env_variable_name = value[1:]
env_variable_value = os.getenv(env_variable_name)
if env_variable_value is not None:
values[key] = env_variable_value
else:
values[key] = value
_logger.warning(
"Environment variable %s is not set for fs_storage %s.",
env_variable_name,
self.display_name,
)
else:
values[key] = value
return values
def _get_fs_options(self):
options = self.json_options
if not self.eval_options_from_env:
return options
return self._eval_options_from_env(self.json_options)
def _get_filesystem(self) -> fsspec.AbstractFileSystem:
"""Get the fsspec filesystem for this backend.
See https://filesystem-spec.readthedocs.io/en/latest/api.html
#fsspec.spec.AbstractFileSystem
:return: fsspec.AbstractFileSystem
"""
self.ensure_one()
options = self._get_fs_options()
if self.protocol == "odoofs":
options["odoo_storage_path"] = self._odoo_storage_path
# Webdav protocol handler does need the auth to be a tuple not a list !
if (
self.protocol == "webdav"
and "auth" in options
and isinstance(options["auth"], list)
):
options["auth"] = tuple(options["auth"])
options = self._recursive_add_odoo_storage_path(options)
fs = fsspec.filesystem(self.protocol, **options)
directory_path = self.directory_path
if directory_path:
fs = fsspec.filesystem("rooted_dir", path=directory_path, fs=fs)
return fs
# Deprecated methods used to ease the migration from the storage_backend addons
# to the fs_storage addons. These methods will be removed in the future (Odoo 18)
@deprecated("Please use _get_filesystem() instead and the fsspec API directly.")
def add(self, relative_path, data, binary=True, **kwargs) -> None:
if not binary:
data = base64.b64decode(data)
path = relative_path.split(self.fs.sep)[:-1]
if not self.fs.exists(self.fs.sep.join(path)):
self.fs.makedirs(self.fs.sep.join(path))
with self.fs.open(relative_path, "wb", **kwargs) as f:
f.write(data)
@deprecated("Please use _get_filesystem() instead and the fsspec API directly.")
def get(self, relative_path, binary=True, **kwargs) -> AnyStr:
data = self.fs.read_bytes(relative_path, **kwargs)
if not binary and data:
data = base64.b64encode(data)
return data
@deprecated("Please use _get_filesystem() instead and the fsspec API directly.")
def list_files(self, relative_path="", pattern=False) -> list[str]:
relative_path = relative_path or self.fs.root_marker
if not self.fs.exists(relative_path):
return []
if pattern:
relative_path = self.fs.sep.join([relative_path, pattern])
return self.fs.glob(relative_path)
return self.fs.ls(relative_path, detail=False)
@deprecated("Please use _get_filesystem() instead and the fsspec API directly.")
def find_files(self, pattern, relative_path="", **kw) -> list[str]:
"""Find files matching given pattern.
:param pattern: regex expression
:param relative_path: optional relative path containing files
:return: list of file paths as full paths from the root
"""
result = []
relative_path = relative_path or self.fs.root_marker
if not self.fs.exists(relative_path):
return []
regex = re.compile(pattern)
for file_path in self.fs.ls(relative_path, detail=False):
# fs.ls returns a relative path
if regex.match(os.path.basename(file_path)):
result.append(file_path)
return result
@deprecated("Please use _get_filesystem() instead and the fsspec API directly.")
def move_files(self, files, destination_path, **kw) -> None:
"""Move files to given destination.
:param files: list of file paths to be moved
:param destination_path: directory path where to move files
:return: None
"""
for file_path in files:
self.fs.move(
file_path,
self.fs.sep.join([destination_path, os.path.basename(file_path)]),
**kw,
)
@deprecated("Please use _get_filesystem() instead and the fsspec API directly.")
def delete(self, relative_path) -> None:
self.fs.rm_file(relative_path)
def action_test_config(self):
self.ensure_one()
if self.check_connection_method:
return self._test_config(self.check_connection_method)
else:
action = self.env["ir.actions.actions"]._for_xml_id(
"fs_storage.act_open_fs_test_connection_view"
)
action["context"] = {"active_model": "fs.storage", "active_id": self.id}
return action
def _test_config(self, connection_method):
try:
self._check_connection(self.fs, connection_method)
title = _("Connection Test Succeeded!")
message = _("Everything seems properly set up!")
msg_type = "success"
except Exception as err:
title = _("Connection Test Failed!")
message = str(err)
msg_type = "danger"
return {
"type": "ir.actions.client",
"tag": "display_notification",
"params": {
"title": title,
"message": message,
"type": msg_type,
"sticky": False,
},
}
def _get_root_filesystem(self, fs=None):
if not fs:
self.ensure_one()
fs = self.fs
while hasattr(fs, "fs"):
fs = fs.fs
return fs

View file

@ -0,0 +1,50 @@
# Copyright 2023 ACSONE SA/NV (https://www.acsone.eu).
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from fsspec.registry import register_implementation
from .rooted_dir_file_system import RootedDirFileSystem
class OdooFileSystem(RootedDirFileSystem):
"""A directory-based filesystem for Odoo.
This filesystem is mounted from a specific subdirectory of the Odoo
filestore directory.
It extends the RootedDirFileSystem to avoid going outside the
specific subdirectory nor the Odoo filestore directory.
Parameters:
odoo_storage_path: The path of the subdirectory of the Odoo filestore
directory to mount. This parameter is required and is always provided
by the Odoo FS Storage even if it is explicitly defined in the
storage options.
fs: AbstractFileSystem
An instantiated filesystem to wrap.
target_protocol, target_options:
if fs is none, construct it from these
"""
def __init__(
self,
*,
odoo_storage_path,
fs=None,
target_protocol=None,
target_options=None,
**storage_options
):
if not odoo_storage_path:
raise ValueError("odoo_storage_path is required")
super().__init__(
path=odoo_storage_path,
fs=fs,
target_protocol=target_protocol,
target_options=target_options,
**storage_options
)
register_implementation("odoofs", OdooFileSystem)

View file

@ -0,0 +1,2 @@
* Laurent Mignon <laurent.mignon@acsone.eu>
* Sébastien BEAU <sebastien.beau@akretion.com>

View file

@ -0,0 +1,63 @@
This addon is a technical addon that allows you to define filesystem like
storage for your data. It's used by other addons to store their data in a
transparent way into different kind of storages.
Through the fs.storage record, you get access to an object that implements
the `fsspec.spec.AbstractFileSystem <https://filesystem-spec.readthedocs.io/en/
latest/api.html#fsspec.spec.AbstractFileSystem>`_ interface and therefore give
you an unified interface to access your data whatever the storage protocol you
decide to use.
The list of supported protocols depends on the installed fsspec implementations.
By default, the addon will install the following protocols:
* LocalFileSystem
* MemoryFileSystem
* ZipFileSystem
* TarFileSystem
* FTPFileSystem
* CachingFileSystem
* WholeFileSystem
* SimplCacheFileSystem
* ReferenceFileSystem
* GenericFileSystem
* DirFileSystem
* DatabricksFileSystem
* GitHubFileSystem
* JupiterFileSystem
* OdooFileSystem
The OdooFileSystem is the one that allows you to store your data into a directory
mounted into your Odoo's storage directory. This is the default FS Storage
when creating a new fs.storage record.
Others protocols are available through the installation of additional
python packages:
* DropboxDriveFileSystem -> `pip install fsspec[dropbox]`
* HTTPFileSystem -> `pip install fsspec[http]`
* HTTPSFileSystem -> `pip install fsspec[http]`
* GCSFileSystem -> `pip install fsspec[gcs]`
* GSFileSystem -> `pip install fsspec[gs]`
* GoogleDriveFileSystem -> `pip install gdrivefs`
* SFTPFileSystem -> `pip install fsspec[sftp]`
* HaddoopFileSystem -> `pip install fsspec[hdfs]`
* S3FileSystem -> `pip install fsspec[s3]`
* WandbFS -> `pip install wandbfs`
* OCIFileSystem -> `pip install fsspec[oci]`
* AsyncLocalFileSystem -> `pip install 'morefs[asynclocalfs]`
* AzureDatalakeFileSystem -> `pip install fsspec[adl]`
* AzureBlobFileSystem -> `pip install fsspec[abfs]`
* DaskWorkerFileSystem -> `pip install fsspec[dask]`
* GitFileSystem -> `pip install fsspec[git]`
* SMBFileSystem -> `pip install fsspec[smb]`
* LibArchiveFileSystem -> `pip install fsspec[libarchive]`
* OSSFileSystem -> `pip install ossfs`
* WebdavFileSystem -> `pip install webdav4`
* DVCFileSystem -> `pip install dvc`
* XRootDFileSystem -> `pip install fsspec-xrootd`
This list of supported protocols is not exhaustive or could change in the future
depending on the fsspec releases. You can find more information about the
supported protocols on the `fsspec documentation
<https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem>`_.

View file

@ -0,0 +1,33 @@
16.0.1.2.0 (2024-02-06)
~~~~~~~~~~~~~~~~~~~~~~~
**Features**
- Invalidate FS filesystem object cache when the connection fails, forcing a reconnection. (`#320 <https://github.com/OCA/storage/issues/320>`_)
16.0.1.1.0 (2023-12-22)
~~~~~~~~~~~~~~~~~~~~~~~
**Features**
- Add parameter on storage backend to resolve protocol options values starting with $ from environment variables (`#303 <https://github.com/OCA/storage/issues/303>`_)
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 <https://github.com/OCA/storage/issues/289>`_)
16.0.1.0.2 (2023-10-09)
~~~~~~~~~~~~~~~~~~~~~~~
**Bugfixes**
- Avoid config error when using the webdav protocol. The auth option is expected
to be a tuple not a list. Since our config is loaded from a json file, we
cannot use tuples. The fix converts the list to a tuple when the config is
related to a webdav protocol and the auth option is into the confix. (`#285 <https://github.com/OCA/storage/issues/285>`_)

View file

@ -0,0 +1,9 @@
* Transactions: fsspec comes with a transactional mechanism that once started,
gathers all the files created during the transaction, and if the transaction
is committed, moves them to their final locations. It would be useful to
bridge this with the transactional mechanism of odoo. This would allow to
ensure that all the files created during a transaction are either all
moved to their final locations, or all deleted if the transaction is rolled
back. This mechanism is only valid for files created during the transaction
by a call to the `open` method of the file system. It is not valid for others
operations, such as `rm`, `mv_file`, ... .

View file

@ -0,0 +1,94 @@
Configuration
~~~~~~~~~~~~~
When you create a new backend, you must specify the following:
* The name of the backend. This is the name that will be used to
identify the backend into Odoo
* The code of the backend. This code will identify the backend into the store_fname
field of the ir.attachment model. This code must be unique. It will be used
as scheme. example of the store_fname field: ``odoofs://abs34Tg11``.
* The protocol used by the backend. The protocol refers to the supported
protocols of the fsspec python package.
* A directory path. This is a root directory from which the filesystem will
be mounted. This directory must exist.
* The protocol options. These are the options that will be passed to the
fsspec python package when creating the filesystem. These options depend
on the protocol used and are described in the fsspec documentation.
* Resolve env vars. This options resolves the protocol options values starting
with $ from environment variables
* Check Connection Method. If set, Odoo will always check the connection before using
a storage and it will remove the fs connection from the cache if the check fails.
* ``Create Marker file`` : create a hidden file on remote and then check it exists with
Use it if you have write access to the remote and if it is not an issue to leave
the marker file in the root directory.
* ``List file`` : list all files from the root directory. You can use it if the directory
path does not contain a big list of files (for performance reasons)
Some protocols defined in the fsspec package are wrappers around other
protocols. For example, the SimpleCacheFileSystem protocol is a wrapper
around any local filesystem protocol. In such cases, you must specify into the
protocol options the protocol to be wrapped and the options to be passed to
the wrapped protocol.
For example, if you want to create a backend that uses the SimpleCacheFileSystem
protocol, after selecting the SimpleCacheFileSystem protocol, you must specify
the protocol options as follows:
.. code-block:: python
{
"directory_path": "/tmp/my_backend",
"target_protocol": "odoofs",
"target_options": {...},
}
In this example, the SimpleCacheFileSystem protocol will be used as a wrapper
around the odoofs protocol.
Server Environment
~~~~~~~~~~~~~~~~~~
To ease the management of the filesystem storages configuration accross the different
environments, the configuration of the filesystem storages can be defined in
environment files or directly in the main configuration file. For example, the
configuration of a filesystem storage with the code `fsprod` can be provided in the
main configuration file as follows:
.. code-block:: ini
[fs_storage.fsprod]
protocol=s3
options={"endpoint_url": "https://my_s3_server/", "key": "KEY", "secret": "SECRET"}
directory_path=my_bucket
To work, a `storage.backend` record must exist with the code `fsprod` into the database.
In your configuration section, you can specify the value for the following fields:
* `protocol`
* `options`
* `directory_path`
Migration from storage_backend
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The fs_storage addon can be used to replace the storage_backend addon. (It has
been designed to be a drop-in replacement for the storage_backend addon). To
ease the migration, the `fs.storage` model defines the high-level methods
available in the storage_backend model. These methods are:
* `add`
* `get`
* `list_files`
* `find_files`
* `move_files`
* `delete`
These methods are wrappers around the methods of the `fsspec.AbstractFileSystem`
class (see https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem).
These methods are marked as deprecated and will be removed in a future version (V18)
of the addon. You should use the methods of the `fsspec.AbstractFileSystem` class
instead since they are more flexible and powerful. You can access the instance
of the `fsspec.AbstractFileSystem` class using the `fs` property of a `fs.storage`
record.

View file

@ -0,0 +1,37 @@
# Copyright 2023 ACSONE SA/NV (https://www.acsone.eu).
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import os
from fsspec.implementations.dirfs import DirFileSystem
from fsspec.implementations.local import make_path_posix
from fsspec.registry import register_implementation
class RootedDirFileSystem(DirFileSystem):
"""A directory-based filesystem that uses path as a root.
The main purpose of this filesystem is to ensure that paths are always
a sub path of the initial path. IOW, it is not possible to go outside
the initial path. That's the only difference with the DirFileSystem provided
by fsspec.
This one should be provided by fsspec itself. We should propose a PR.
"""
def _join(self, path):
path = super()._join(path)
# Ensure that the path is a subpath of the root path by resolving
# any relative paths.
# Since the path separator is not always the same on all systems,
# we need to normalize the path separator.
path_posix = os.path.normpath(make_path_posix(path))
root_posix = os.path.normpath(make_path_posix(self.path))
if not path_posix.startswith(root_posix):
raise PermissionError(
"Path %s is not a subpath of the root path %s" % (path, self.path)
)
return path
register_implementation("rooted_dir", RootedDirFileSystem)

View file

@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_fs_storage_edit,fs_storage edit,model_fs_storage,base.group_system,1,1,1,1
access_fs_test_connection,fs.test.connection.access,model_fs_test_connection,base.group_system,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_fs_storage_edit fs_storage edit model_fs_storage base.group_system 1 1 1 1
3 access_fs_test_connection fs.test.connection.access model_fs_test_connection base.group_system 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -0,0 +1,641 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
: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.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
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 }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="filesystem-storage-backend">
<h1>Filesystem Storage Backend</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:b9f95387306ce78e4543bc0b90f958fa188ba244dd6df41af486078d2d358fdf
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/license-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/storage/tree/16.0/fs_storage"><img alt="OCA/storage" src="https://img.shields.io/badge/github-OCA%2Fstorage-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/storage-16-0/storage-16-0-fs_storage"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/storage&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This addon is a technical addon that allows you to define filesystem like
storage for your data. Its used by other addons to store their data in a
transparent way into different kind of storages.</p>
<p>Through the fs.storage record, you get access to an object that implements
the <a class="reference external" href="https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem">fsspec.spec.AbstractFileSystem</a> interface and therefore give
you an unified interface to access your data whatever the storage protocol you
decide to use.</p>
<p>The list of supported protocols depends on the installed fsspec implementations.
By default, the addon will install the following protocols:</p>
<ul class="simple">
<li>LocalFileSystem</li>
<li>MemoryFileSystem</li>
<li>ZipFileSystem</li>
<li>TarFileSystem</li>
<li>FTPFileSystem</li>
<li>CachingFileSystem</li>
<li>WholeFileSystem</li>
<li>SimplCacheFileSystem</li>
<li>ReferenceFileSystem</li>
<li>GenericFileSystem</li>
<li>DirFileSystem</li>
<li>DatabricksFileSystem</li>
<li>GitHubFileSystem</li>
<li>JupiterFileSystem</li>
<li>OdooFileSystem</li>
</ul>
<p>The OdooFileSystem is the one that allows you to store your data into a directory
mounted into your Odoos storage directory. This is the default FS Storage
when creating a new fs.storage record.</p>
<p>Others protocols are available through the installation of additional
python packages:</p>
<ul class="simple">
<li>DropboxDriveFileSystem -&gt; <cite>pip install fsspec[dropbox]</cite></li>
<li>HTTPFileSystem -&gt; <cite>pip install fsspec[http]</cite></li>
<li>HTTPSFileSystem -&gt; <cite>pip install fsspec[http]</cite></li>
<li>GCSFileSystem -&gt; <cite>pip install fsspec[gcs]</cite></li>
<li>GSFileSystem -&gt; <cite>pip install fsspec[gs]</cite></li>
<li>GoogleDriveFileSystem -&gt; <cite>pip install gdrivefs</cite></li>
<li>SFTPFileSystem -&gt; <cite>pip install fsspec[sftp]</cite></li>
<li>HaddoopFileSystem -&gt; <cite>pip install fsspec[hdfs]</cite></li>
<li>S3FileSystem -&gt; <cite>pip install fsspec[s3]</cite></li>
<li>WandbFS -&gt; <cite>pip install wandbfs</cite></li>
<li>OCIFileSystem -&gt; <cite>pip install fsspec[oci]</cite></li>
<li>AsyncLocalFileSystem -&gt; <cite>pip install morefs[asynclocalfs]</cite></li>
<li>AzureDatalakeFileSystem -&gt; <cite>pip install fsspec[adl]</cite></li>
<li>AzureBlobFileSystem -&gt; <cite>pip install fsspec[abfs]</cite></li>
<li>DaskWorkerFileSystem -&gt; <cite>pip install fsspec[dask]</cite></li>
<li>GitFileSystem -&gt; <cite>pip install fsspec[git]</cite></li>
<li>SMBFileSystem -&gt; <cite>pip install fsspec[smb]</cite></li>
<li>LibArchiveFileSystem -&gt; <cite>pip install fsspec[libarchive]</cite></li>
<li>OSSFileSystem -&gt; <cite>pip install ossfs</cite></li>
<li>WebdavFileSystem -&gt; <cite>pip install webdav4</cite></li>
<li>DVCFileSystem -&gt; <cite>pip install dvc</cite></li>
<li>XRootDFileSystem -&gt; <cite>pip install fsspec-xrootd</cite></li>
</ul>
<p>This list of supported protocols is not exhaustive or could change in the future
depending on the fsspec releases. You can find more information about the
supported protocols on the <a class="reference external" href="https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem">fsspec documentation</a>.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="toc-entry-1">Usage</a><ul>
<li><a class="reference internal" href="#configuration" id="toc-entry-2">Configuration</a></li>
<li><a class="reference internal" href="#server-environment" id="toc-entry-3">Server Environment</a></li>
<li><a class="reference internal" href="#migration-from-storage-backend" id="toc-entry-4">Migration from storage_backend</a></li>
</ul>
</li>
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-5">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#changelog" id="toc-entry-6">Changelog</a><ul>
<li><a class="reference internal" href="#section-1" id="toc-entry-7">16.0.1.2.0 (2024-02-06)</a></li>
<li><a class="reference internal" href="#section-2" id="toc-entry-8">16.0.1.1.0 (2023-12-22)</a></li>
<li><a class="reference internal" href="#section-3" id="toc-entry-9">16.0.1.0.3 (2023-10-17)</a></li>
<li><a class="reference internal" href="#section-4" id="toc-entry-10">16.0.1.0.2 (2023-10-09)</a></li>
</ul>
</li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-11">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-12">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-13">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-14">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-15">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-1">Usage</a></h2>
<div class="section" id="configuration">
<h3><a class="toc-backref" href="#toc-entry-2">Configuration</a></h3>
<p>When you create a new backend, you must specify the following:</p>
<ul class="simple">
<li>The name of the backend. This is the name that will be used to
identify the backend into Odoo</li>
<li>The code of the backend. This code will identify the backend into the store_fname
field of the ir.attachment model. This code must be unique. It will be used
as scheme. example of the store_fname field: <tt class="docutils literal"><span class="pre">odoofs://abs34Tg11</span></tt>.</li>
<li>The protocol used by the backend. The protocol refers to the supported
protocols of the fsspec python package.</li>
<li>A directory path. This is a root directory from which the filesystem will
be mounted. This directory must exist.</li>
<li>The protocol options. These are the options that will be passed to the
fsspec python package when creating the filesystem. These options depend
on the protocol used and are described in the fsspec documentation.</li>
<li>Resolve env vars. This options resolves the protocol options values starting
with $ from environment variables</li>
<li>Check Connection Method. If set, Odoo will always check the connection before using
a storage and it will remove the fs connection from the cache if the check fails.<ul>
<li><tt class="docutils literal">Create Marker file</tt> : create a hidden file on remote and then check it exists with
Use it if you have write access to the remote and if it is not an issue to leave
the marker file in the root directory.</li>
<li><tt class="docutils literal">List file</tt> : list all files from the root directory. You can use it if the directory
path does not contain a big list of files (for performance reasons)</li>
</ul>
</li>
</ul>
<p>Some protocols defined in the fsspec package are wrappers around other
protocols. For example, the SimpleCacheFileSystem protocol is a wrapper
around any local filesystem protocol. In such cases, you must specify into the
protocol options the protocol to be wrapped and the options to be passed to
the wrapped protocol.</p>
<p>For example, if you want to create a backend that uses the SimpleCacheFileSystem
protocol, after selecting the SimpleCacheFileSystem protocol, you must specify
the protocol options as follows:</p>
<pre class="code python literal-block">
<span class="p">{</span><span class="w">
</span> <span class="s2">&quot;directory_path&quot;</span><span class="p">:</span> <span class="s2">&quot;/tmp/my_backend&quot;</span><span class="p">,</span><span class="w">
</span> <span class="s2">&quot;target_protocol&quot;</span><span class="p">:</span> <span class="s2">&quot;odoofs&quot;</span><span class="p">,</span><span class="w">
</span> <span class="s2">&quot;target_options&quot;</span><span class="p">:</span> <span class="p">{</span><span class="o">...</span><span class="p">},</span><span class="w">
</span><span class="p">}</span>
</pre>
<p>In this example, the SimpleCacheFileSystem protocol will be used as a wrapper
around the odoofs protocol.</p>
</div>
<div class="section" id="server-environment">
<h3><a class="toc-backref" href="#toc-entry-3">Server Environment</a></h3>
<p>To ease the management of the filesystem storages configuration accross the different
environments, the configuration of the filesystem storages can be defined in
environment files or directly in the main configuration file. For example, the
configuration of a filesystem storage with the code <cite>fsprod</cite> can be provided in the
main configuration file as follows:</p>
<pre class="code ini literal-block">
<span class="k">[fs_storage.fsprod]</span><span class="w">
</span><span class="na">protocol</span><span class="o">=</span><span class="s">s3</span><span class="w">
</span><span class="na">options={&quot;endpoint_url&quot;</span><span class="o">:</span><span class="w"> </span><span class="s">&quot;https://my_s3_server/&quot;</span><span class="na">, &quot;key&quot;</span><span class="o">:</span><span class="w"> </span><span class="s">&quot;KEY&quot;</span><span class="na">, &quot;secret&quot;</span><span class="o">:</span><span class="w"> </span><span class="s">&quot;SECRET&quot;</span><span class="na">}</span><span class="w">
</span><span class="na">directory_path</span><span class="o">=</span><span class="s">my_bucket</span>
</pre>
<p>To work, a <cite>storage.backend</cite> record must exist with the code <cite>fsprod</cite> into the database.
In your configuration section, you can specify the value for the following fields:</p>
<ul class="simple">
<li><cite>protocol</cite></li>
<li><cite>options</cite></li>
<li><cite>directory_path</cite></li>
</ul>
</div>
<div class="section" id="migration-from-storage-backend">
<h3><a class="toc-backref" href="#toc-entry-4">Migration from storage_backend</a></h3>
<p>The fs_storage addon can be used to replace the storage_backend addon. (It has
been designed to be a drop-in replacement for the storage_backend addon). To
ease the migration, the <cite>fs.storage</cite> model defines the high-level methods
available in the storage_backend model. These methods are:</p>
<ul class="simple">
<li><cite>add</cite></li>
<li><cite>get</cite></li>
<li><cite>list_files</cite></li>
<li><cite>find_files</cite></li>
<li><cite>move_files</cite></li>
<li><cite>delete</cite></li>
</ul>
<p>These methods are wrappers around the methods of the <cite>fsspec.AbstractFileSystem</cite>
class (see <a class="reference external" href="https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem">https://filesystem-spec.readthedocs.io/en/latest/api.html#fsspec.spec.AbstractFileSystem</a>).
These methods are marked as deprecated and will be removed in a future version (V18)
of the addon. You should use the methods of the <cite>fsspec.AbstractFileSystem</cite> class
instead since they are more flexible and powerful. You can access the instance
of the <cite>fsspec.AbstractFileSystem</cite> class using the <cite>fs</cite> property of a <cite>fs.storage</cite>
record.</p>
</div>
</div>
<div class="section" id="known-issues-roadmap">
<h2><a class="toc-backref" href="#toc-entry-5">Known issues / Roadmap</a></h2>
<ul class="simple">
<li>Transactions: fsspec comes with a transactional mechanism that once started,
gathers all the files created during the transaction, and if the transaction
is committed, moves them to their final locations. It would be useful to
bridge this with the transactional mechanism of odoo. This would allow to
ensure that all the files created during a transaction are either all
moved to their final locations, or all deleted if the transaction is rolled
back. This mechanism is only valid for files created during the transaction
by a call to the <cite>open</cite> method of the file system. It is not valid for others
operations, such as <cite>rm</cite>, <cite>mv_file</cite>, … .</li>
</ul>
</div>
<div class="section" id="changelog">
<h2><a class="toc-backref" href="#toc-entry-6">Changelog</a></h2>
<div class="section" id="section-1">
<h3><a class="toc-backref" href="#toc-entry-7">16.0.1.2.0 (2024-02-06)</a></h3>
<p><strong>Features</strong></p>
<ul class="simple">
<li>Invalidate FS filesystem object cache when the connection fails, forcing a reconnection. (<a class="reference external" href="https://github.com/OCA/storage/issues/320">#320</a>)</li>
</ul>
</div>
<div class="section" id="section-2">
<h3><a class="toc-backref" href="#toc-entry-8">16.0.1.1.0 (2023-12-22)</a></h3>
<p><strong>Features</strong></p>
<ul class="simple">
<li>Add parameter on storage backend to resolve protocol options values starting with $ from environment variables (<a class="reference external" href="https://github.com/OCA/storage/issues/303">#303</a>)</li>
</ul>
</div>
<div class="section" id="section-3">
<h3><a class="toc-backref" href="#toc-entry-9">16.0.1.0.3 (2023-10-17)</a></h3>
<p><strong>Bugfixes</strong></p>
<ul class="simple">
<li>Fix access to technical models to be able to upload attachments for users with basic access (<a class="reference external" href="https://github.com/OCA/storage/issues/289">#289</a>)</li>
</ul>
</div>
<div class="section" id="section-4">
<h3><a class="toc-backref" href="#toc-entry-10">16.0.1.0.2 (2023-10-09)</a></h3>
<p><strong>Bugfixes</strong></p>
<ul class="simple">
<li>Avoid config error when using the webdav protocol. The auth option is expected
to be a tuple not a list. Since our config is loaded from a json file, we
cannot use tuples. The fix converts the list to a tuple when the config is
related to a webdav protocol and the auth option is into the confix. (<a class="reference external" href="https://github.com/OCA/storage/issues/285">#285</a>)</li>
</ul>
</div>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-11">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/storage/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/storage/issues/new?body=module:%20fs_storage%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-12">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-13">Authors</a></h3>
<ul class="simple">
<li>ACSONE SA/NV</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-14">Contributors</a></h3>
<ul class="simple">
<li>Laurent Mignon &lt;<a class="reference external" href="mailto:laurent.mignon&#64;acsone.eu">laurent.mignon&#64;acsone.eu</a>&gt;</li>
<li>Sébastien BEAU &lt;<a class="reference external" href="mailto:sebastien.beau&#64;akretion.com">sebastien.beau&#64;akretion.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-15">Maintainers</a></h3>
<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>
<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>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/storage/tree/16.0/fs_storage">OCA/storage</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,2 @@
from . import common
from . import test_fs_storage

View file

@ -0,0 +1,46 @@
# Copyright 2023 ACSONE SA/NV (http://acsone.eu).
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import base64
import os
import shutil
import tempfile
from unittest import mock
from odoo.tests.common import TransactionCase
from ..models.fs_storage import FSStorage
class TestFSStorageCase(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.backend: FSStorage = cls.env.ref("fs_storage.default_fs_storage")
cls.backend.json_options = {"target_options": {"auto_mkdir": "True"}}
cls.filedata = base64.b64encode(b"This is a simple file")
cls.filename = "test_file.txt"
cls.case_with_subdirectory = "subdirectory/here"
cls.demo_user = cls.env.ref("base.user_demo")
cls.temp_dir = tempfile.mkdtemp()
def setUp(self):
super().setUp()
mocked_backend = mock.patch.object(
self.backend.__class__, "_get_filesystem_storage_path"
)
mocked_get_filesystem_storage_path = mocked_backend.start()
mocked_get_filesystem_storage_path.return_value = self.temp_dir
self.backend.write({"directory_path": self.temp_dir})
# pylint: disable=unused-variable
@self.addCleanup
def stop_mock():
mocked_backend.stop()
# recursively delete the tempdir
if os.path.exists(self.temp_dir):
shutil.rmtree(self.temp_dir)
def _create_file(self, backend: FSStorage, filename: str, filedata: str):
with backend.fs.open(filename, "wb") as f:
f.write(filedata)

View file

@ -0,0 +1,152 @@
# Copyright 2023 ACSONE SA/NV (http://acsone.eu).
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import warnings
from unittest import mock
from odoo.tests import Form
from odoo.tools import mute_logger
from .common import TestFSStorageCase
class TestFSStorage(TestFSStorageCase):
@mute_logger("py.warnings")
def _test_deprecated_setting_and_getting_data(self):
# Check that the directory is empty
warnings.filterwarnings("ignore")
files = self.backend.list_files()
self.assertNotIn(self.filename, files)
# Add a new file
self.backend.add(
self.filename, self.filedata, mimetype="text/plain", binary=False
)
# Check that the file exist
files = self.backend.list_files()
self.assertIn(self.filename, files)
# Retrieve the file added
data = self.backend.get(self.filename, binary=False)
self.assertEqual(data, self.filedata)
# Delete the file
self.backend.delete(self.filename)
files = self.backend.list_files()
self.assertNotIn(self.filename, files)
@mute_logger("py.warnings")
def _test_deprecated_find_files(self):
warnings.filterwarnings("ignore")
self.backend.add(
self.filename, self.filedata, mimetype="text/plain", binary=False
)
try:
res = self.backend.find_files(r".*\.txt")
self.assertListEqual([self.filename], res)
res = self.backend.find_files(r".*\.text")
self.assertListEqual([], res)
finally:
self.backend.delete(self.filename)
def test_deprecated_setting_and_getting_data_from_root(self):
self._test_deprecated_setting_and_getting_data()
def test_deprecated_setting_and_getting_data_from_dir(self):
self.backend.directory_path = self.case_with_subdirectory
self._test_deprecated_setting_and_getting_data()
def test_deprecated_find_files_from_root(self):
self._test_deprecated_find_files()
def test_deprecated_find_files_from_dir(self):
self.backend.directory_path = self.case_with_subdirectory
self._test_deprecated_find_files()
def test_ensure_one_fs_by_record(self):
# in this test we ensure that we've one fs by record
backend_ids = []
for i in range(4):
backend_ids.append(
self.backend.create(
{"name": f"name{i}", "directory_path": f"{i}", "code": f"code{i}"}
).id
)
records = self.backend.browse(backend_ids)
fs = None
for rec in records:
self.assertNotEqual(fs, rec.fs)
def test_relative_access(self):
self.backend.directory_path = self.case_with_subdirectory
self._create_file(self.backend, self.filename, self.filedata)
other_subdirectory = "other_subdirectory"
backend2 = self.backend.copy({"directory_path": other_subdirectory})
self._create_file(backend2, self.filename, self.filedata)
with self.assertRaises(PermissionError), self.env.cr.savepoint():
# check that we can't access outside the subdirectory
backend2.fs.ls("../")
with self.assertRaises(PermissionError), self.env.cr.savepoint():
# check that we can't access the file into another subdirectory
backend2.fs.ls(f"../{self.case_with_subdirectory}")
self.backend.fs.rm_file(self.filename)
backend2.fs.rm_file(self.filename)
def test_recursive_add_odoo_storage_path_to_options(self):
options = {
"directory_path": "/tmp/my_backend",
"target_protocol": "odoofs",
}
self.backend._recursive_add_odoo_storage_path(options)
self.assertEqual(
self.backend._odoo_storage_path,
options.get("target_options").get("odoo_storage_path"),
)
options = {
"directory_path": "/tmp/my_backend",
"target_protocol": "dir",
"target_options": {
"path": "/my_backend",
"target_protocol": "odoofs",
},
}
self.backend._recursive_add_odoo_storage_path(options)
self.assertEqual(
self.backend._odoo_storage_path,
options.get("target_options")
.get("target_options")
.get("odoo_storage_path"),
)
def test_interface_values(self):
protocol = "file" # should be available by default in the list of protocols
with Form(self.env["fs.storage"]) as new_storage:
new_storage.name = "Test storage"
new_storage.code = "code"
new_storage.protocol = protocol
self.assertEqual(new_storage.protocol, protocol)
# the options should follow the protocol
self.assertEqual(new_storage.options_protocol, protocol)
description = new_storage.protocol_descr
self.assertTrue("Interface to files on local storage" in description)
# this is still true after saving
self.assertEqual(new_storage.options_protocol, protocol)
def test_options_env(self):
self.backend.json_options = {"key": {"sub_key": "$KEY_VAR"}}
eval_json_options = {"key": {"sub_key": "TEST"}}
options = self.backend._get_fs_options()
self.assertDictEqual(options, self.backend.json_options)
self.backend.eval_options_from_env = True
with mock.patch.dict("os.environ", {"KEY_VAR": "TEST"}):
options = self.backend._get_fs_options()
self.assertDictEqual(options, eval_json_options)
with self.assertLogs(level="WARNING") as log:
options = self.backend._get_fs_options()
self.assertIn(
(
f"Environment variable KEY_VAR is not set for "
f"fs_storage {self.backend.display_name}."
),
log.output[0],
)

View file

@ -0,0 +1,119 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="fs_storage_tree_view" model="ir.ui.view">
<field name="name">fs.storage.tree (in fs_storage)</field>
<field name="model">fs.storage</field>
<field name="arch" type="xml">
<tree>
<field name="code" />
<field name="name" />
<field name="protocol" />
</tree>
</field>
</record>
<record id="fs_storage_form_view" model="ir.ui.view">
<field name="name">fs.storage.form (in fs_storage)</field>
<field name="model">fs.storage</field>
<field name="arch" type="xml">
<form string="FS Storage">
<header>
<button
type="object"
name="action_test_config"
string="Test connection"
/>
</header>
<sheet>
<div class="oe_title">
<label for="name" class="oe_edit_only" />
<h1>
<field name="name" />
</h1>
</div>
<group name="config">
<group>
<field name="code" />
<field name="protocol" />
<field name="directory_path" />
<field name="eval_options_from_env" />
<field
name="options"
widget="ace"
options="{'mode': 'python'}"
placeholder="Enter you fsspec options here."
/>
<field name="check_connection_method" />
</group>
<group>
<notebook colspan="2">
<page string="Protocol Descr" colspan="2">
<group colspan="2">
<field
name="protocol_descr"
nolabel="1"
colspan="2"
/>
</group>
</page>
<page string="Available options" colspan="2">
<group colspan="2">
<field name="options_protocol" />
<label for="options_properties" colspan="2" />
<field
name="options_properties"
nolabel="1"
colspan="2"
/>
</group>
</page>
</notebook>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="fs_storage_search_view" model="ir.ui.view">
<field name="name">fs.storage.search (in fs_storage)</field>
<field name="model">fs.storage</field>
<field name="arch" type="xml">
<search string="FS Storage">
<field name="name" />
<field name="code" />
</search>
</field>
</record>
<record model="ir.actions.act_window" id="act_open_fs_storage_view">
<field name="name">FS Storage</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">fs.storage</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="fs_storage_search_view" />
<field name="domain">[]</field>
<field name="context">{}</field>
</record>
<record model="ir.actions.act_window.view" id="act_open_fs_storage_view_form">
<field name="act_window_id" ref="act_open_fs_storage_view" />
<field name="sequence" eval="20" />
<field name="view_mode">form</field>
<field name="view_id" ref="fs_storage_form_view" />
</record>
<record model="ir.actions.act_window.view" id="act_open_fs_storage_view_tree">
<field name="act_window_id" ref="act_open_fs_storage_view" />
<field name="sequence" eval="10" />
<field name="view_mode">tree</field>
<field name="view_id" ref="fs_storage_tree_view" />
</record>
<menuitem
id="menu_storage"
parent="base.menu_custom"
sequence="100"
action="act_open_fs_storage_view"
/>
<menuitem
id="menu_fs_storage"
parent="menu_storage"
sequence="10"
action="act_open_fs_storage_view"
/>
</odoo>

View file

@ -0,0 +1 @@
from . import fs_test_connection

View file

@ -0,0 +1,26 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo import api, fields, models
class FSTestConnection(models.TransientModel):
_name = "fs.test.connection"
_description = "FS Test Connection Wizard"
def _get_check_connection_method_selection(self):
return self.env["fs.storage"]._get_check_connection_method_selection()
storage_id = fields.Many2one("fs.storage")
check_connection_method = fields.Selection(
selection="_get_check_connection_method_selection",
required=True,
)
@api.model
def default_get(self, field_list):
res = super().default_get(field_list)
res["storage_id"] = self.env.context.get("active_id", False)
return res
def action_test_config(self):
return self.storage_id._test_config(self.check_connection_method)

View file

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="fs_test_connection_form_view" model="ir.ui.view">
<field name="name">fs.test.connection.form</field>
<field name="model">fs.test.connection</field>
<field name="arch" type="xml">
<form string="Test Connection">
<group>
<field name="storage_id" readonly="1" />
<field name="check_connection_method" />
</group>
<footer>
<button
type="object"
name="action_test_config"
string="Test connection"
/>
<button string="Close" class="btn-secondary" special="cancel" />
</footer>
</form>
</field>
</record>
<record model="ir.actions.act_window" id="act_open_fs_test_connection_view">
<field name="name">FS Test Connection</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">fs.test.connection</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo>