diff --git a/setup/storage_media/odoo/addons/storage_media b/setup/storage_media/odoo/addons/storage_media new file mode 120000 index 0000000000..addef57a07 --- /dev/null +++ b/setup/storage_media/odoo/addons/storage_media @@ -0,0 +1 @@ +../../../../storage_media \ No newline at end of file diff --git a/setup/storage_media/setup.py b/setup/storage_media/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/storage_media/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/storage_media/README.rst b/storage_media/README.rst new file mode 100644 index 0000000000..f90e1e43f6 --- /dev/null +++ b/storage_media/README.rst @@ -0,0 +1,55 @@ +.. image:: https://img.shields.io/badge/licence-LGPL--3-blue.svg + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 + +============== +Storage Media +============== + +This module add the possibilty to store media (with specific tag/typology) on a public +storage in order to make them accessible on the web for your customer + +Installation +============ + +Nothing special required + + +Configuration +============= + +Install the module and configure your storage (see storage module) + +Usage +===== + +Go on the storage form and add a media + +Known issues / Roadmap +====================== + +* Nothing + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Sebastien Beau + +Funders +------- + +The development of this module has been financially supported by: + +* Akretion R&D +* Adaptoo diff --git a/storage_media/__init__.py b/storage_media/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/storage_media/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/storage_media/__manifest__.py b/storage_media/__manifest__.py new file mode 100644 index 0000000000..c835c3a520 --- /dev/null +++ b/storage_media/__manifest__.py @@ -0,0 +1,26 @@ +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +{ + "name": "Storage Media", + "summary": "Give the posibility to store media data in Odoo", + "version": "15.0.1.0.0", + "category": "Uncategorized", + "website": "https://github.com/OCA/storage", + "author": " Akretion, Odoo Community Association (OCA)", + "license": "LGPL-3", + "application": False, + "installable": True, + "external_dependencies": {"python": [], "bin": []}, + "depends": ["storage_file", "storage_thumbnail"], + "data": [ + "views/storage_media_view.xml", + "data/ir_parameter.xml", + "security/res_group.xml", + "security/ir_rule.xml", + "security/ir.model.access.csv", + ], + "demo": [], + "qweb": [], +} diff --git a/storage_media/data/ir_parameter.xml b/storage_media/data/ir_parameter.xml new file mode 100644 index 0000000000..0cccb2e7e9 --- /dev/null +++ b/storage_media/data/ir_parameter.xml @@ -0,0 +1,9 @@ + + + + + storage.media.backend_id + + + + diff --git a/storage_media/i18n/storage_media.pot b/storage_media/i18n/storage_media.pot new file mode 100644 index 0000000000..1f37260aca --- /dev/null +++ b/storage_media/i18n/storage_media.pot @@ -0,0 +1,232 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * storage_media +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.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: storage_media +#: model:ir.model.fields,help:storage_media.field_storage_media__url_path +msgid "Accessible path, no base URL" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__active +msgid "Active" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__checksum +msgid "Checksum/SHA1" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media_type__code +msgid "Code" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__company_id +msgid "Company" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__create_uid +#: model:ir.model.fields,field_description:storage_media.field_storage_media_type__create_uid +msgid "Created by" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__create_date +#: model:ir.model.fields,field_description:storage_media.field_storage_media_type__create_date +msgid "Created on" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__data +msgid "Data" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,help:storage_media.field_storage_media__data +msgid "Datas" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_file__display_name +#: model:ir.model.fields,field_description:storage_media.field_storage_media__display_name +#: model:ir.model.fields,field_description:storage_media.field_storage_media_type__display_name +msgid "Display Name" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__extension +msgid "Extension" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__file_id +msgid "File" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__file_size +msgid "File Size" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_file__file_type +#: model:ir.model.fields,field_description:storage_media.field_storage_image__file_type +#: model:ir.model.fields,field_description:storage_media.field_storage_media__file_type +#: model:ir.model.fields,field_description:storage_media.field_storage_thumbnail__file_type +msgid "File Type" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__filename +msgid "Filename without extension" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,help:storage_media.field_storage_media__internal_url +msgid "HTTP URL to load the file directly from storage." +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,help:storage_media.field_storage_media__url +msgid "HTTP accessible path to the file" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__human_file_size +msgid "Human File Size" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_file__id +#: model:ir.model.fields,field_description:storage_media.field_storage_media__id +#: model:ir.model.fields,field_description:storage_media.field_storage_media_type__id +msgid "ID" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__internal_url +msgid "Internal Url" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_file____last_update +#: model:ir.model.fields,field_description:storage_media.field_storage_media____last_update +#: model:ir.model.fields,field_description:storage_media.field_storage_media_type____last_update +msgid "Last Modified on" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__write_uid +#: model:ir.model.fields,field_description:storage_media.field_storage_media_type__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__write_date +#: model:ir.model.fields,field_description:storage_media.field_storage_media_type__write_date +msgid "Last Updated on" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields.selection,name:storage_media.selection__storage_file__file_type__media +msgid "Media" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__media_type_id +msgid "Media Type" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__mimetype +msgid "Mime Type" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__name +#: model:ir.model.fields,field_description:storage_media.field_storage_media_type__name +msgid "Name" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__relative_path +msgid "Relative Path" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,help:storage_media.field_storage_media__relative_path +msgid "Relative location for backend" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__slug +msgid "Slug" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,help:storage_media.field_storage_media__slug +msgid "Slug-ified name with ID for URL" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__backend_id +msgid "Storage" +msgstr "" + +#. module: storage_media +#: model:ir.model,name:storage_media.model_storage_file +msgid "Storage File" +msgstr "" + +#. module: storage_media +#: model:ir.actions.act_window,name:storage_media.storage_media_action +#: model:ir.model,name:storage_media.model_storage_media +#: model:ir.ui.menu,name:storage_media.storage_media_menu +#: model_terms:ir.ui.view,arch_db:storage_media.storage_media_view_form +#: model_terms:ir.ui.view,arch_db:storage_media.storage_media_view_tree +msgid "Storage Media" +msgstr "" + +#. module: storage_media +#: model:res.groups,name:storage_media.group_media_manager +msgid "Storage Media Manager" +msgstr "" + +#. module: storage_media +#: model:ir.actions.act_window,name:storage_media.act_open_storage_media_type_view +#: model:ir.model,name:storage_media.model_storage_media_type +#: model:ir.ui.menu,name:storage_media.menu_storage_media_type +#: model_terms:ir.ui.view,arch_db:storage_media.storage_media_type_view_form +#: model_terms:ir.ui.view,arch_db:storage_media.storage_media_type_view_search +#: model_terms:ir.ui.view,arch_db:storage_media.storage_media_type_view_tree +msgid "Storage Media Type" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__to_delete +msgid "To Delete" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__url +msgid "Url" +msgstr "" + +#. module: storage_media +#: model:ir.model.fields,field_description:storage_media.field_storage_media__url_path +msgid "Url Path" +msgstr "" diff --git a/storage_media/models/__init__.py b/storage_media/models/__init__.py new file mode 100644 index 0000000000..d2bf407514 --- /dev/null +++ b/storage_media/models/__init__.py @@ -0,0 +1,3 @@ +from . import storage_file +from . import storage_media +from . import storage_media_type diff --git a/storage_media/models/storage_file.py b/storage_media/models/storage_file.py new file mode 100644 index 0000000000..9874b0331b --- /dev/null +++ b/storage_media/models/storage_file.py @@ -0,0 +1,13 @@ +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import fields, models + + +class StorageFile(models.Model): + _inherit = "storage.file" + + file_type = fields.Selection( + selection_add=[("media", "Media")], ondelete={"media": "set null"} + ) diff --git a/storage_media/models/storage_media.py b/storage_media/models/storage_media.py new file mode 100644 index 0000000000..4a5e6a9972 --- /dev/null +++ b/storage_media/models/storage_media.py @@ -0,0 +1,44 @@ +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import logging +import os + +from odoo import api, fields, models + +_logger = logging.getLogger(__name__) + +try: + from slugify import slugify +except ImportError: # pragma: no cover + _logger.debug("Cannot `import slugify`.") + + +class StorageMedia(models.Model): + _name = "storage.media" + _description = "Storage Media" + _inherits = {"storage.file": "file_id"} + _default_file_type = "media" + + file_id = fields.Many2one("storage.file", "File", required=True, ondelete="cascade") + media_type_id = fields.Many2one("storage.media.type", "Media Type") + + @api.onchange("name") + def onchange_name(self): + for record in self: + if record.name: + filename, extension = os.path.splitext(record.name) + record.name = "{}{}".format(slugify(filename), extension) + + @api.model + def create(self, vals): + vals["file_type"] = self._default_file_type + if "backend_id" not in vals: + vals["backend_id"] = self._get_default_backend_id() + return super(StorageMedia, self).create(vals) + + def _get_default_backend_id(self): + return self.env["storage.backend"]._get_backend_id_from_param( + self.env, "storage.media.backend_id" + ) diff --git a/storage_media/models/storage_media_type.py b/storage_media/models/storage_media_type.py new file mode 100644 index 0000000000..10f4470502 --- /dev/null +++ b/storage_media/models/storage_media_type.py @@ -0,0 +1,13 @@ +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import fields, models + + +class MediaType(models.Model): + _name = "storage.media.type" + _description = "Storage Media Type" + + name = fields.Char(translate=True, required=True) + code = fields.Char() diff --git a/storage_media/security/ir.model.access.csv b/storage_media/security/ir.model.access.csv new file mode 100644 index 0000000000..15dddd77a7 --- /dev/null +++ b/storage_media/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_storage_media_edit,storage_media edit,model_storage_media,group_media_manager,1,1,1,1 +access_storage_file_media_edit,storage_file media edit,storage_file.model_storage_file,group_media_manager,1,1,1,1 +access_storage_thumbnail_edit,storage_thumbnail edit,storage_thumbnail.model_storage_thumbnail,group_media_manager,1,1,1,1 +access_storage_media_read,storage_media read,model_storage_media,base.group_user,1,0,0,0 +access_storage_media_type_edit,storage_media_type edit,model_storage_media_type,base.group_system,1,1,1,1 +access_storage_media_type_read,storage_media_type read,model_storage_media_type,base.group_user,1,0,0,0 diff --git a/storage_media/security/ir_rule.xml b/storage_media/security/ir_rule.xml new file mode 100644 index 0000000000..98baf117be --- /dev/null +++ b/storage_media/security/ir_rule.xml @@ -0,0 +1,15 @@ + + + + + Storage File for Media + + [('file_type','=', 'media')] + + + + + + + + diff --git a/storage_media/security/res_group.xml b/storage_media/security/res_group.xml new file mode 100644 index 0000000000..0973e4d56f --- /dev/null +++ b/storage_media/security/res_group.xml @@ -0,0 +1,12 @@ + + + + + Storage Media Manager + + + + diff --git a/storage_media/static/description/icon.png b/storage_media/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/storage_media/static/description/icon.png differ diff --git a/storage_media/tests/__init__.py b/storage_media/tests/__init__.py new file mode 100644 index 0000000000..4a12853692 --- /dev/null +++ b/storage_media/tests/__init__.py @@ -0,0 +1 @@ +from . import test_storage_media diff --git a/storage_media/tests/test_storage_media.py b/storage_media/tests/test_storage_media.py new file mode 100644 index 0000000000..0e4a106dc8 --- /dev/null +++ b/storage_media/tests/test_storage_media.py @@ -0,0 +1,28 @@ +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo.addons.component.tests.common import TransactionComponentCase + + +class StorageMediaCase(TransactionComponentCase): + def setUp(self): + super(StorageMediaCase, self).setUp() + self.backend = self.env.ref("storage_backend.default_storage_backend") + self.filename = "test of my_file.txt" + + def test_onchange_name(self): + media = self.env["storage.media"].create( + {"name": self.filename, "backend_id": self.backend.id} + ) + self.assertEqual(media.name, self.filename) + new_filename = "new file name.txt" + media.name = new_filename + media.onchange_name() + values = media._convert_to_write(media._cache) + self.assertEqual(values["name"], "new-file-name.txt") + + def test_create_media(self): + media = self.env["storage.media"].create({"name": self.filename}) + self.assertEqual(media.file_type, "media") + self.assertIsNotNone(media.backend_id) diff --git a/storage_media/views/storage_media_view.xml b/storage_media/views/storage_media_view.xml new file mode 100644 index 0000000000..f35f063d8e --- /dev/null +++ b/storage_media/views/storage_media_view.xml @@ -0,0 +1,114 @@ + + + + + storage.media + + + + + + + + + + storage.media + +
+ + + + + + + +
+
+
+ + + storage.media.type + + + + + + + + + + storage.media.type + +
+ + + + +
+
+
+ + + storage.media.type + + + + + + + + + + Storage Media Type + ir.actions.act_window + storage.media.type + tree,form + + [] + {} + + + + + + form + + + + + + + tree + + + + + + + Storage Media + storage.media + tree,form + + + + +