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,2 @@
from . import base_adapter
from . import filesystem_adapter

View file

@ -0,0 +1,69 @@
# 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).
# Copyright 2020 ACSONE SA/NV (<http://acsone.eu>)
# @author Simone Orsi <simahawk@gmail.com>
import os
import re
from odoo.addons.component.core import AbstractComponent
class BaseStorageAdapter(AbstractComponent):
_name = "base.storage.adapter"
_collection = "storage.backend"
def _fullpath(self, relative_path):
dp = self.collection.directory_path
if not dp or relative_path.startswith(dp):
return relative_path
return os.path.join(dp, relative_path)
def add(self, relative_path, data, **kwargs):
raise NotImplementedError
def get(self, relative_path, **kwargs):
raise NotImplementedError
def list(self, relative_path=""):
raise NotImplementedError
def find_files(self, pattern, relative_path="", **kwargs):
"""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
"""
regex = re.compile(pattern)
filelist = self.list(relative_path)
files_matching = [
regex.match(file_).group() for file_ in filelist if regex.match(file_)
]
filepaths = []
if files_matching:
filepaths = [
os.path.join(self._fullpath(relative_path) or "", filename)
for filename in files_matching
]
return filepaths
def move_files(self, files, destination_path, **kwargs):
"""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
"""
raise NotImplementedError
def delete(self, relative_path):
raise NotImplementedError
# You can define `validate_config` on your own adapter
# to make validation button available on UI.
# This method should simply pass smoothly when validation is ok,
# otherwise it should raise an exception.
# def validate_config(self):
# raise NotImplementedError

View file

@ -0,0 +1,76 @@
# 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).
import logging
import os
import shutil
from odoo import _
from odoo.exceptions import AccessError
from odoo.addons.component.core import Component
_logger = logging.getLogger(__name__)
def is_safe_path(basedir, path):
return os.path.realpath(path).startswith(basedir)
class FileSystemStorageBackend(Component):
_name = "filesystem.adapter"
_inherit = "base.storage.adapter"
_usage = "filesystem"
def _basedir(self):
return os.path.join(self.env["ir.attachment"]._filestore(), "storage")
def _fullpath(self, relative_path):
"""This will build the full path for the file, we force to
store the data inside the filestore in the directory 'storage".
Becarefull if you implement your own custom path, end user
should never be able to write or read unwanted filesystem file"""
full_path = super(FileSystemStorageBackend, self)._fullpath(relative_path)
base_dir = self._basedir()
full_path = os.path.join(base_dir, full_path)
if not is_safe_path(base_dir, full_path):
raise AccessError(_("Access to %s is forbidden") % full_path)
return full_path
def add(self, relative_path, data, **kwargs):
full_path = self._fullpath(relative_path)
dirname = os.path.dirname(full_path)
if not os.path.isdir(dirname):
os.makedirs(dirname)
with open(full_path, "wb") as my_file:
my_file.write(data)
def get(self, relative_path, **kwargs):
full_path = self._fullpath(relative_path)
with open(full_path, "rb") as my_file:
data = my_file.read()
return data
def list(self, relative_path=""):
full_path = self._fullpath(relative_path)
if os.path.isdir(full_path):
return os.listdir(full_path)
return []
def delete(self, relative_path):
full_path = self._fullpath(relative_path)
try:
os.remove(full_path)
except FileNotFoundError:
_logger.warning("File not found in %s", full_path)
def move_files(self, files, destination_path):
result = []
for file_path in files:
if not os.path.exists(destination_path):
os.makedirs(destination_path)
filename = os.path.basename(file_path)
destination_file = os.path.join(destination_path, filename)
result.append(shutil.move(file_path, destination_file))
return result