From 26aa982db6a0bcb29004a8cd1071ef5f76adad51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Mon, 9 Apr 2018 17:37:07 +0200 Subject: [PATCH 01/74] [REF] start refactoring storage_image, split storage_image in two module, storage_image and storage_thumbnail add a generic mixing for thumbnail owner --- storage_thumbnail/README.rst | 33 ++++++++++ storage_thumbnail/__init__.py | 4 ++ storage_thumbnail/__manifest__.py | 31 ++++++++++ storage_thumbnail/data/ir_parameter.xml | 9 +++ storage_thumbnail/models/__init__.py | 4 ++ storage_thumbnail/models/storage_thumbnail.py | 62 +++++++++++++++++++ storage_thumbnail/models/thumbnail_owner.py | 61 ++++++++++++++++++ .../security/ir.model.access.csv | 3 + .../views/storage_thumbnail_view.xml | 27 ++++++++ 9 files changed, 234 insertions(+) create mode 100644 storage_thumbnail/README.rst create mode 100644 storage_thumbnail/__init__.py create mode 100644 storage_thumbnail/__manifest__.py create mode 100644 storage_thumbnail/data/ir_parameter.xml create mode 100644 storage_thumbnail/models/__init__.py create mode 100644 storage_thumbnail/models/storage_thumbnail.py create mode 100644 storage_thumbnail/models/thumbnail_owner.py create mode 100644 storage_thumbnail/security/ir.model.access.csv create mode 100644 storage_thumbnail/views/storage_thumbnail_view.xml diff --git a/storage_thumbnail/README.rst b/storage_thumbnail/README.rst new file mode 100644 index 0000000000..22d41c424e --- /dev/null +++ b/storage_thumbnail/README.rst @@ -0,0 +1,33 @@ + +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +================= +Storage Thumbnail +================= + + +External image thumbnail management depending on Storage File module. + + +Known issues / Roadmap +====================== + +Update README with the last model of README when migration to v11 in OCA + + +Credits +======= + + +Contributors +------------ + +* Sebastien Beau +* Raphaël Reverdy + +Maintainer +---------- + +* Akretion diff --git a/storage_thumbnail/__init__.py b/storage_thumbnail/__init__.py new file mode 100644 index 0000000000..77bbdbd391 --- /dev/null +++ b/storage_thumbnail/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import models + diff --git a/storage_thumbnail/__manifest__.py b/storage_thumbnail/__manifest__.py new file mode 100644 index 0000000000..d13f0909b1 --- /dev/null +++ b/storage_thumbnail/__manifest__.py @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Storage Thumbnail", + "summary": "Abstract module that add the possibility to have thumbnail", + "version": "10.0.1.0.0", + "category": "Storage", + "website": "www.akretion.com", + "author": " Akretion", + "license": "AGPL-3", + 'installable': True, + "external_dependencies": { + "python": [], + "bin": [], + }, + "depends": [ + "storage_file", + ], + "data": [ + 'data/ir_parameter.xml', + 'views/storage_thumbnail_view.xml', + 'security/ir.model.access.csv', + ], + "demo": [ + ], + "qweb": [ + ] +} diff --git a/storage_thumbnail/data/ir_parameter.xml b/storage_thumbnail/data/ir_parameter.xml new file mode 100644 index 0000000000..ed6c13d071 --- /dev/null +++ b/storage_thumbnail/data/ir_parameter.xml @@ -0,0 +1,9 @@ + + + + + storage.thumbnail.backend_id + + + + diff --git a/storage_thumbnail/models/__init__.py b/storage_thumbnail/models/__init__.py new file mode 100644 index 0000000000..4dbc62a406 --- /dev/null +++ b/storage_thumbnail/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import storage_thumbnail +from . import thumbnail_owner diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py new file mode 100644 index 0000000000..0fc8f89238 --- /dev/null +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo import api, fields, models +from odoo.tools import image_resize_image +import logging +_logger = logging.getLogger(__name__) + + +class StorageThumbnail(models.Model): + _name = 'storage.thumbnail' + _description = 'Storage Thumbnail' + _inherits = {'storage.file': 'file_id'} + + size_x = fields.Integer("weight") + size_y = fields.Integer("height") + file_id = fields.Many2one( + 'storage.file', + 'File', + required=True, + ondelete='cascade') + res_model = fields.Char( + readonly=False, + index=True) + res_id = fields.Integer( + readonly=False, + index=True) + + def _prepare_thumbnail(self, image, size_x, size_y): + return { + 'datas': self._resize(image, size_x, size_y), + 'res_model': image._name, + 'res_id': image.id, + 'name': '%s_%s_%s%s' % ( + image.filename, size_x, size_y, image.extension), + 'size_x': size_x, + 'size_y': size_y, + } + + def _resize(self, image, size_x, size_y): + return image_resize_image(image.datas, size=(size_x, size_y)) + + def _create_thumbnail(self, image, size_x, size_y): + vals = self._prepare_thumbnail(image, size_x, size_y) + return self.create(vals) + + def _get_backend_id(self): + """Choose the correct backend. + + By default : it's the one configured as ir.config_parameter + Overload this method if you need something more powerfull + """ + return int(self.env['ir.config_parameter'].get_param( + 'storage.thumbnail.backend_id')) + + @api.model + def create(self, vals): + vals['backend_id'] = self._get_backend_id() + return super(StorageThumbnail, self).create(vals) diff --git a/storage_thumbnail/models/thumbnail_owner.py b/storage_thumbnail/models/thumbnail_owner.py new file mode 100644 index 0000000000..b3f1463244 --- /dev/null +++ b/storage_thumbnail/models/thumbnail_owner.py @@ -0,0 +1,61 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from openerp import api, fields, models + + +class ThumbnailOwner(models.AbstractModel): + _name = 'thumbnail.owner' + _description = 'Thumbnail Owner' + + thumbnail_ids = fields.One2many( + comodel_name='storage.thumbnail', + string='Thumbnails', + inverse_name='res_id', + domain=lambda self: [("res_model", "=", self._name)]) + image_medium_url = fields.Char( + compute="_compute_image_url", + readonly=True) + image_small_url = fields.Char( + compute="_compute_image_url", + readonly=True) + + def _get_medium_thumbnail(self): + return self.get_thumbnail(128, 128) + + def _get_small_thumbnail(self): + return self.get_thumbnail(64, 64) + + @api.multi + def get_thumbnail(self, size_x, size_y): + self.ensure_one() + thumbnail = self.env['storage.thumbnail'].search([ + ('size_x', '=', size_x), + ('size_y', '=', size_y), + ('res_id', '=', self.id), + ('res_model', '=', self._name), + ]) + if not thumbnail and self.datas: + thumbnail = self.env['storage.thumbnail']._create_thumbnail( + self, size_x, size_y) + return thumbnail + + @api.multi + @api.depends('url') + def _compute_image_url(self): + # We need a clear env for getting the thumbnail + # as a potential create/write can be called + # This avoid useless recomputation of field + # TODO we should see with odoo how we can improve the ORM + todo = self.env.all.todo + self.env.all.todo = {} + for rec in self: + if rec.url: + medium_url = rec.sudo()._get_medium_thumbnail().url + small_url = rec.sudo()._get_small_thumbnail().url + rec.image_medium_url = medium_url + rec.image_small_url = small_url + self.env.all.todo = todo diff --git a/storage_thumbnail/security/ir.model.access.csv b/storage_thumbnail/security/ir.model.access.csv new file mode 100644 index 0000000000..7396bfed3f --- /dev/null +++ b/storage_thumbnail/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_storage_thumbnail_edit,storage_thumbnail edit,model_storage_thumbnail,base.group_system,1,1,1,1 +access_storage_thumbnail_read,storage_thumbnail read,model_storage_thumbnail,base.group_user,1,0,0,0 diff --git a/storage_thumbnail/views/storage_thumbnail_view.xml b/storage_thumbnail/views/storage_thumbnail_view.xml new file mode 100644 index 0000000000..91e54bbb2d --- /dev/null +++ b/storage_thumbnail/views/storage_thumbnail_view.xml @@ -0,0 +1,27 @@ + + + + + storage.thumbnail + +
+ + + + +
+
+
+ + + storage.thumbnail + + + + + + + + + +
From f7852426ef1e82a76c4bebf9253c6f348fba1ccb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Wed, 11 Apr 2018 19:42:42 +0200 Subject: [PATCH 02/74] [REF] rename method store and retrieve by more explicit method add/get with the specifiation of the type of file binary or base64 --- storage_thumbnail/models/storage_thumbnail.py | 4 ++-- storage_thumbnail/models/thumbnail_owner.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index 0fc8f89238..0dc2567eed 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -31,7 +31,7 @@ class StorageThumbnail(models.Model): def _prepare_thumbnail(self, image, size_x, size_y): return { - 'datas': self._resize(image, size_x, size_y), + 'data': self._resize(image, size_x, size_y), 'res_model': image._name, 'res_id': image.id, 'name': '%s_%s_%s%s' % ( @@ -41,7 +41,7 @@ def _prepare_thumbnail(self, image, size_x, size_y): } def _resize(self, image, size_x, size_y): - return image_resize_image(image.datas, size=(size_x, size_y)) + return image_resize_image(image.data, size=(size_x, size_y)) def _create_thumbnail(self, image, size_x, size_y): vals = self._prepare_thumbnail(image, size_x, size_y) diff --git a/storage_thumbnail/models/thumbnail_owner.py b/storage_thumbnail/models/thumbnail_owner.py index b3f1463244..fce89dd5db 100644 --- a/storage_thumbnail/models/thumbnail_owner.py +++ b/storage_thumbnail/models/thumbnail_owner.py @@ -38,7 +38,7 @@ def get_thumbnail(self, size_x, size_y): ('res_id', '=', self.id), ('res_model', '=', self._name), ]) - if not thumbnail and self.datas: + if not thumbnail and self.data: thumbnail = self.env['storage.thumbnail']._create_thumbnail( self, size_x, size_y) return thumbnail From 03a2b2e7582aa9930406248b7ce256fdf122a238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Thu, 12 Apr 2018 10:37:35 +0200 Subject: [PATCH 03/74] [REF] rename thumbnail.owner to thumbnail.mixin --- storage_thumbnail/models/__init__.py | 2 +- .../models/{thumbnail_owner.py => thumbnail_mixin.py} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename storage_thumbnail/models/{thumbnail_owner.py => thumbnail_mixin.py} (95%) diff --git a/storage_thumbnail/models/__init__.py b/storage_thumbnail/models/__init__.py index 4dbc62a406..f3c9eb4855 100644 --- a/storage_thumbnail/models/__init__.py +++ b/storage_thumbnail/models/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- from . import storage_thumbnail -from . import thumbnail_owner +from . import thumbnail_mixin diff --git a/storage_thumbnail/models/thumbnail_owner.py b/storage_thumbnail/models/thumbnail_mixin.py similarity index 95% rename from storage_thumbnail/models/thumbnail_owner.py rename to storage_thumbnail/models/thumbnail_mixin.py index fce89dd5db..db9f1548d1 100644 --- a/storage_thumbnail/models/thumbnail_owner.py +++ b/storage_thumbnail/models/thumbnail_mixin.py @@ -8,8 +8,8 @@ class ThumbnailOwner(models.AbstractModel): - _name = 'thumbnail.owner' - _description = 'Thumbnail Owner' + _name = 'thumbnail.mixin' + _description = 'Thumbnail Mixin add the thumbnail capability' thumbnail_ids = fields.One2many( comodel_name='storage.thumbnail', From 0bdd2ebde6e8f389a56fafe006f4713a146ddd28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Thu, 12 Apr 2018 14:40:56 +0200 Subject: [PATCH 04/74] [FIX] fix issue when generating thumbnail with the context bin_size=True --- storage_thumbnail/models/thumbnail_mixin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/storage_thumbnail/models/thumbnail_mixin.py b/storage_thumbnail/models/thumbnail_mixin.py index db9f1548d1..a368f52502 100644 --- a/storage_thumbnail/models/thumbnail_mixin.py +++ b/storage_thumbnail/models/thumbnail_mixin.py @@ -32,6 +32,7 @@ def _get_small_thumbnail(self): @api.multi def get_thumbnail(self, size_x, size_y): self.ensure_one() + self = self.with_context(bin_size=False) thumbnail = self.env['storage.thumbnail'].search([ ('size_x', '=', size_x), ('size_y', '=', size_y), From 595cf970b74cdad259274ac02b2aec845a26503c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Fri, 13 Apr 2018 19:20:18 +0200 Subject: [PATCH 05/74] [IMP] add support of deletation of storage.file, storage.image and storage.thumbnail --- storage_thumbnail/models/storage_thumbnail.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index 0dc2567eed..78a95fe234 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -60,3 +60,9 @@ def _get_backend_id(self): def create(self, vals): vals['backend_id'] = self._get_backend_id() return super(StorageThumbnail, self).create(vals) + + def unlink(self): + files = self.mapped('file_id') + super(StorageThumbnail, self).unlink() + files.unlink() + return True From 19783923139933fd4a568092e9ec3c635de9d9b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Tue, 17 Apr 2018 10:24:19 +0200 Subject: [PATCH 06/74] [REF] review access right add special group for images, make method for add and removing file on backend private --- storage_thumbnail/models/__init__.py | 1 + storage_thumbnail/models/storage_file.py | 12 ++++++++++++ storage_thumbnail/models/storage_thumbnail.py | 5 ++++- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 storage_thumbnail/models/storage_file.py diff --git a/storage_thumbnail/models/__init__.py b/storage_thumbnail/models/__init__.py index f3c9eb4855..3e583d1f06 100644 --- a/storage_thumbnail/models/__init__.py +++ b/storage_thumbnail/models/__init__.py @@ -2,3 +2,4 @@ from . import storage_thumbnail from . import thumbnail_mixin +from . import storage_file diff --git a/storage_thumbnail/models/storage_file.py b/storage_thumbnail/models/storage_file.py new file mode 100644 index 0000000000..cb8ca75196 --- /dev/null +++ b/storage_thumbnail/models/storage_file.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import fields, models + + +class StorageFile(models.Model): + _inherit = 'storage.file' + + file_type = fields.Selection(selection_add=[('thumbnail', 'Thumbnail')]) diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index 78a95fe234..73a8f95169 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -58,7 +58,10 @@ def _get_backend_id(self): @api.model def create(self, vals): - vals['backend_id'] = self._get_backend_id() + vals.update({ + 'backend_id': self._get_backend_id(), + 'file_type': 'thumbnail', + }) return super(StorageThumbnail, self).create(vals) def unlink(self): From 9cbeaecfbcc3a1f3c4d9ddbbebe461bc401ec4bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Tue, 17 Apr 2018 11:18:00 +0200 Subject: [PATCH 07/74] [FIX] fixed cache issue when generating the thumbnail, no need to clear the cache manually anymore --- storage_thumbnail/models/thumbnail_mixin.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/storage_thumbnail/models/thumbnail_mixin.py b/storage_thumbnail/models/thumbnail_mixin.py index a368f52502..3448876ee1 100644 --- a/storage_thumbnail/models/thumbnail_mixin.py +++ b/storage_thumbnail/models/thumbnail_mixin.py @@ -18,9 +18,11 @@ class ThumbnailOwner(models.AbstractModel): domain=lambda self: [("res_model", "=", self._name)]) image_medium_url = fields.Char( compute="_compute_image_url", + compute_sudo=True, readonly=True) image_small_url = fields.Char( compute="_compute_image_url", + compute_sudo=True, readonly=True) def _get_medium_thumbnail(self): @@ -55,8 +57,7 @@ def _compute_image_url(self): self.env.all.todo = {} for rec in self: if rec.url: - medium_url = rec.sudo()._get_medium_thumbnail().url - small_url = rec.sudo()._get_small_thumbnail().url - rec.image_medium_url = medium_url - rec.image_small_url = small_url + rec.image_medium_url = rec._get_medium_thumbnail().url + rec.image_small_url = rec._get_small_thumbnail().url + rec._cache.pop('thumbnail_ids', None) self.env.all.todo = todo From a64d7905c8e9b6ffd14318a5fa9873c8c499cc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Thu, 19 Apr 2018 15:33:46 +0200 Subject: [PATCH 08/74] [REF] refactor store medium, small url and generate thumbnail when creating the image --- storage_thumbnail/models/thumbnail_mixin.py | 46 ++++++++------------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/storage_thumbnail/models/thumbnail_mixin.py b/storage_thumbnail/models/thumbnail_mixin.py index 3448876ee1..2cd46054d5 100644 --- a/storage_thumbnail/models/thumbnail_mixin.py +++ b/storage_thumbnail/models/thumbnail_mixin.py @@ -7,7 +7,7 @@ from openerp import api, fields, models -class ThumbnailOwner(models.AbstractModel): +class ThumbnailMixing(models.AbstractModel): _name = 'thumbnail.mixin' _description = 'Thumbnail Mixin add the thumbnail capability' @@ -16,23 +16,16 @@ class ThumbnailOwner(models.AbstractModel): string='Thumbnails', inverse_name='res_id', domain=lambda self: [("res_model", "=", self._name)]) - image_medium_url = fields.Char( - compute="_compute_image_url", - compute_sudo=True, - readonly=True) - image_small_url = fields.Char( - compute="_compute_image_url", - compute_sudo=True, - readonly=True) + image_medium_url = fields.Char(readonly=True) + image_small_url = fields.Char(readonly=True) def _get_medium_thumbnail(self): - return self.get_thumbnail(128, 128) + return self.get_or_create_thumbnail(128, 128) def _get_small_thumbnail(self): - return self.get_thumbnail(64, 64) + return self.get_or_create_thumbnail(64, 64) - @api.multi - def get_thumbnail(self, size_x, size_y): + def get_or_create_thumbnail(self, size_x, size_y): self.ensure_one() self = self.with_context(bin_size=False) thumbnail = self.env['storage.thumbnail'].search([ @@ -46,18 +39,15 @@ def get_thumbnail(self, size_x, size_y): self, size_x, size_y) return thumbnail - @api.multi - @api.depends('url') - def _compute_image_url(self): - # We need a clear env for getting the thumbnail - # as a potential create/write can be called - # This avoid useless recomputation of field - # TODO we should see with odoo how we can improve the ORM - todo = self.env.all.todo - self.env.all.todo = {} - for rec in self: - if rec.url: - rec.image_medium_url = rec._get_medium_thumbnail().url - rec.image_small_url = rec._get_small_thumbnail().url - rec._cache.pop('thumbnail_ids', None) - self.env.all.todo = todo + def generate_odoo_thumbnail(self): + self.write({ + 'image_medium_url': self.sudo()._get_medium_thumbnail().url, + 'image_small_url': self.sudo()._get_small_thumbnail().url, + }) + return True + + @api.model + def create(self, vals): + record = super(ThumbnailMixing, self).create(vals) + record.generate_odoo_thumbnail() + return record From 2fe422d9b1872bcb58521743b90ab31320e12591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Honor=C3=A9?= Date: Thu, 26 Apr 2018 09:54:20 +0200 Subject: [PATCH 09/74] Add alt name of thumbnail + update openerp into odoo --- storage_thumbnail/models/storage_file.py | 2 +- storage_thumbnail/models/storage_thumbnail.py | 15 ++++--- storage_thumbnail/models/thumbnail_mixin.py | 22 +++++++--- .../views/storage_thumbnail_view.xml | 42 ++++++++++--------- 4 files changed, 50 insertions(+), 31 deletions(-) diff --git a/storage_thumbnail/models/storage_file.py b/storage_thumbnail/models/storage_file.py index cb8ca75196..feaf04b67a 100644 --- a/storage_thumbnail/models/storage_file.py +++ b/storage_thumbnail/models/storage_file.py @@ -3,7 +3,7 @@ # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import fields, models +from odoo import fields, models class StorageFile(models.Model): diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index 73a8f95169..c6d6621c59 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -17,6 +17,10 @@ class StorageThumbnail(models.Model): size_x = fields.Integer("weight") size_y = fields.Integer("height") + alt_name = fields.Char( + "Alt Image name", + help="Alt name of the picture", + ) file_id = fields.Many2one( 'storage.file', 'File', @@ -29,7 +33,7 @@ class StorageThumbnail(models.Model): readonly=False, index=True) - def _prepare_thumbnail(self, image, size_x, size_y): + def _prepare_thumbnail(self, image, size_x, size_y, alt_name=''): return { 'data': self._resize(image, size_x, size_y), 'res_model': image._name, @@ -38,13 +42,14 @@ def _prepare_thumbnail(self, image, size_x, size_y): image.filename, size_x, size_y, image.extension), 'size_x': size_x, 'size_y': size_y, + 'alt_name': alt_name, } def _resize(self, image, size_x, size_y): return image_resize_image(image.data, size=(size_x, size_y)) - def _create_thumbnail(self, image, size_x, size_y): - vals = self._prepare_thumbnail(image, size_x, size_y) + def _create_thumbnail(self, image, size_x, size_y, alt_name): + vals = self._prepare_thumbnail(image, size_x, size_y, alt_name) return self.create(vals) def _get_backend_id(self): @@ -66,6 +71,6 @@ def create(self, vals): def unlink(self): files = self.mapped('file_id') - super(StorageThumbnail, self).unlink() + result = super(StorageThumbnail, self).unlink() files.unlink() - return True + return result diff --git a/storage_thumbnail/models/thumbnail_mixin.py b/storage_thumbnail/models/thumbnail_mixin.py index 2cd46054d5..c4b91611f9 100644 --- a/storage_thumbnail/models/thumbnail_mixin.py +++ b/storage_thumbnail/models/thumbnail_mixin.py @@ -2,9 +2,16 @@ # Copyright 2017 Akretion (http://www.akretion.com). # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import api, fields, models +import logging -from openerp import api, fields, models +_logger = logging.getLogger(__name__) + +try: + from slugify import slugify +except ImportError: + _logger.debug('Cannot `import slugify`.') class ThumbnailMixing(models.AbstractModel): @@ -25,18 +32,23 @@ def _get_medium_thumbnail(self): def _get_small_thumbnail(self): return self.get_or_create_thumbnail(64, 64) - def get_or_create_thumbnail(self, size_x, size_y): + def get_or_create_thumbnail(self, size_x, size_y, alt_name=False): self.ensure_one() self = self.with_context(bin_size=False) - thumbnail = self.env['storage.thumbnail'].search([ + if not alt_name: + alt_name = self.display_name + alt_name = slugify(alt_name) + domain = [ ('size_x', '=', size_x), ('size_y', '=', size_y), ('res_id', '=', self.id), ('res_model', '=', self._name), - ]) + ('alt_name', '=', alt_name), + ] + thumbnail = self.env['storage.thumbnail'].search(domain, limit=1) if not thumbnail and self.data: thumbnail = self.env['storage.thumbnail']._create_thumbnail( - self, size_x, size_y) + self, size_x, size_y, alt_name=alt_name) return thumbnail def generate_odoo_thumbnail(self): diff --git a/storage_thumbnail/views/storage_thumbnail_view.xml b/storage_thumbnail/views/storage_thumbnail_view.xml index 91e54bbb2d..77d57e9a58 100644 --- a/storage_thumbnail/views/storage_thumbnail_view.xml +++ b/storage_thumbnail/views/storage_thumbnail_view.xml @@ -1,27 +1,29 @@ - - storage.thumbnail - -
- - - - -
-
-
- - - storage.thumbnail - - - + + storage.thumbnail + +
+ + - - - + +
+
+
+ + + storage.thumbnail + + + + + + + + +
From 549fd964811702574968b9bd343e65ad9d6bfa05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Wed, 2 May 2018 15:04:05 +0200 Subject: [PATCH 10/74] [REF] rename alt_name in url_key as this will be only used for the url key, extract method for building the domain, generate the url of the image correclty and add a test --- storage_thumbnail/models/storage_thumbnail.py | 19 ++++++++++-------- storage_thumbnail/models/thumbnail_mixin.py | 20 +++++++++++-------- .../views/storage_thumbnail_view.xml | 6 +++--- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index c6d6621c59..511e123eb3 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -17,9 +17,9 @@ class StorageThumbnail(models.Model): size_x = fields.Integer("weight") size_y = fields.Integer("height") - alt_name = fields.Char( - "Alt Image name", - help="Alt name of the picture", + url_key = fields.Char( + "Url key", + help="Specific URL key for generating the url of the image", ) file_id = fields.Many2one( 'storage.file', @@ -33,23 +33,26 @@ class StorageThumbnail(models.Model): readonly=False, index=True) - def _prepare_thumbnail(self, image, size_x, size_y, alt_name=''): + def _prepare_thumbnail(self, image, size_x, size_y, url_key): return { 'data': self._resize(image, size_x, size_y), 'res_model': image._name, 'res_id': image.id, 'name': '%s_%s_%s%s' % ( - image.filename, size_x, size_y, image.extension), + url_key or image.filename, + size_x, + size_y, + image.extension), 'size_x': size_x, 'size_y': size_y, - 'alt_name': alt_name, + 'url_key': url_key, } def _resize(self, image, size_x, size_y): return image_resize_image(image.data, size=(size_x, size_y)) - def _create_thumbnail(self, image, size_x, size_y, alt_name): - vals = self._prepare_thumbnail(image, size_x, size_y, alt_name) + def _create_thumbnail(self, image, size_x, size_y, url_key): + vals = self._prepare_thumbnail(image, size_x, size_y, url_key) return self.create(vals) def _get_backend_id(self): diff --git a/storage_thumbnail/models/thumbnail_mixin.py b/storage_thumbnail/models/thumbnail_mixin.py index c4b91611f9..cc1a6d4b12 100644 --- a/storage_thumbnail/models/thumbnail_mixin.py +++ b/storage_thumbnail/models/thumbnail_mixin.py @@ -32,23 +32,27 @@ def _get_medium_thumbnail(self): def _get_small_thumbnail(self): return self.get_or_create_thumbnail(64, 64) - def get_or_create_thumbnail(self, size_x, size_y, alt_name=False): - self.ensure_one() - self = self.with_context(bin_size=False) - if not alt_name: - alt_name = self.display_name - alt_name = slugify(alt_name) + def _get_domain(self, size_x, size_y, url_key): domain = [ ('size_x', '=', size_x), ('size_y', '=', size_y), ('res_id', '=', self.id), ('res_model', '=', self._name), - ('alt_name', '=', alt_name), ] + if url_key: + domain.append(('url_key', '=', url_key)) + return domain + + def get_or_create_thumbnail(self, size_x, size_y, url_key=None): + self.ensure_one() + self = self.with_context(bin_size=False) + if url_key: + url_key = slugify(url_key) + domain = self._get_domain(size_x, size_y, url_key) thumbnail = self.env['storage.thumbnail'].search(domain, limit=1) if not thumbnail and self.data: thumbnail = self.env['storage.thumbnail']._create_thumbnail( - self, size_x, size_y, alt_name=alt_name) + self, size_x, size_y, url_key) return thumbnail def generate_odoo_thumbnail(self): diff --git a/storage_thumbnail/views/storage_thumbnail_view.xml b/storage_thumbnail/views/storage_thumbnail_view.xml index 77d57e9a58..b5eb22fe43 100644 --- a/storage_thumbnail/views/storage_thumbnail_view.xml +++ b/storage_thumbnail/views/storage_thumbnail_view.xml @@ -4,9 +4,9 @@ storage.thumbnail -
+ - + @@ -19,7 +19,7 @@ - + From 0ea96fca7bfa5e08cf65d7fc45c22b97e021ed60 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Wed, 30 Jan 2019 18:33:29 +0100 Subject: [PATCH 11/74] [IMP] storage_thumbnail: Improve performance by avoiding 1 search for each thumbnail to retrieve --- storage_thumbnail/models/thumbnail_mixin.py | 23 ++++++++------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/storage_thumbnail/models/thumbnail_mixin.py b/storage_thumbnail/models/thumbnail_mixin.py index cc1a6d4b12..524fe06ee2 100644 --- a/storage_thumbnail/models/thumbnail_mixin.py +++ b/storage_thumbnail/models/thumbnail_mixin.py @@ -32,24 +32,19 @@ def _get_medium_thumbnail(self): def _get_small_thumbnail(self): return self.get_or_create_thumbnail(64, 64) - def _get_domain(self, size_x, size_y, url_key): - domain = [ - ('size_x', '=', size_x), - ('size_y', '=', size_y), - ('res_id', '=', self.id), - ('res_model', '=', self._name), - ] - if url_key: - domain.append(('url_key', '=', url_key)) - return domain - def get_or_create_thumbnail(self, size_x, size_y, url_key=None): self.ensure_one() - self = self.with_context(bin_size=False) + # preserve the prefetch when changing context + self = self.with_context(bin_size=False).with_prefetch(self._prefetch) if url_key: url_key = slugify(url_key) - domain = self._get_domain(size_x, size_y, url_key) - thumbnail = self.env['storage.thumbnail'].search(domain, limit=1) + thumbnail = self.env['storage.thumbnail'].browse() + for th in self.thumbnail_ids: + if th.size_x == size_x and th.size_y == size_y: + if url_key and url_key != th.url_key: + continue + thumbnail = th + break if not thumbnail and self.data: thumbnail = self.env['storage.thumbnail']._create_thumbnail( self, size_x, size_y, url_key) From e90749c3b71991030a2f66cd916a05bf13d01b32 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Wed, 30 Jan 2019 19:56:34 +0100 Subject: [PATCH 12/74] [FIX] storage_thumbnail: Thumbnail must be created using the relation to avoid trouble with the ORM --- storage_thumbnail/models/storage_thumbnail.py | 4 ---- storage_thumbnail/models/thumbnail_mixin.py | 6 +++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index 511e123eb3..a6cfc738c8 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -51,10 +51,6 @@ def _prepare_thumbnail(self, image, size_x, size_y, url_key): def _resize(self, image, size_x, size_y): return image_resize_image(image.data, size=(size_x, size_y)) - def _create_thumbnail(self, image, size_x, size_y, url_key): - vals = self._prepare_thumbnail(image, size_x, size_y, url_key) - return self.create(vals) - def _get_backend_id(self): """Choose the correct backend. diff --git a/storage_thumbnail/models/thumbnail_mixin.py b/storage_thumbnail/models/thumbnail_mixin.py index 524fe06ee2..1916d3dcd9 100644 --- a/storage_thumbnail/models/thumbnail_mixin.py +++ b/storage_thumbnail/models/thumbnail_mixin.py @@ -46,8 +46,12 @@ def get_or_create_thumbnail(self, size_x, size_y, url_key=None): thumbnail = th break if not thumbnail and self.data: - thumbnail = self.env['storage.thumbnail']._create_thumbnail( + vals = self.env['storage.thumbnail']._prepare_thumbnail( self, size_x, size_y, url_key) + # use the relation to create the thumbnail to be sure that the + # record is added to the cache of this relation. + self.write({'thumbnail_ids': [(0, 0, vals)]}) + return self.get_or_create_thumbnail(size_x, size_y, url_key) return thumbnail def generate_odoo_thumbnail(self): From 3492af58ff18bc68fe050445c34685da29192816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Fri, 15 Feb 2019 17:02:31 +0100 Subject: [PATCH 13/74] [FIX] fix missing noupdate --- storage_thumbnail/data/ir_parameter.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage_thumbnail/data/ir_parameter.xml b/storage_thumbnail/data/ir_parameter.xml index ed6c13d071..fee676dc38 100644 --- a/storage_thumbnail/data/ir_parameter.xml +++ b/storage_thumbnail/data/ir_parameter.xml @@ -1,5 +1,5 @@ - + storage.thumbnail.backend_id From 6ede35f0a3fef3da27102a609aa87ff8d0c1bd3c Mon Sep 17 00:00:00 2001 From: Benoit Date: Wed, 10 Apr 2019 18:27:35 +0200 Subject: [PATCH 14/74] [FIX] clean with pre-commit and pep 8 --- storage_thumbnail/__init__.py | 1 - storage_thumbnail/__manifest__.py | 23 +++---- storage_thumbnail/models/storage_file.py | 4 +- storage_thumbnail/models/storage_thumbnail.py | 62 +++++++++---------- storage_thumbnail/models/thumbnail_mixin.py | 38 +++++++----- 5 files changed, 59 insertions(+), 69 deletions(-) diff --git a/storage_thumbnail/__init__.py b/storage_thumbnail/__init__.py index 77bbdbd391..cde864bae2 100644 --- a/storage_thumbnail/__init__.py +++ b/storage_thumbnail/__init__.py @@ -1,4 +1,3 @@ # -*- coding: utf-8 -*- from . import models - diff --git a/storage_thumbnail/__manifest__.py b/storage_thumbnail/__manifest__.py index d13f0909b1..07d911e676 100644 --- a/storage_thumbnail/__manifest__.py +++ b/storage_thumbnail/__manifest__.py @@ -11,21 +11,14 @@ "website": "www.akretion.com", "author": " Akretion", "license": "AGPL-3", - 'installable': True, - "external_dependencies": { - "python": [], - "bin": [], - }, - "depends": [ - "storage_file", - ], + "installable": True, + "external_dependencies": {"python": [], "bin": []}, + "depends": ["storage_file"], "data": [ - 'data/ir_parameter.xml', - 'views/storage_thumbnail_view.xml', - 'security/ir.model.access.csv', - ], - "demo": [ + "data/ir_parameter.xml", + "views/storage_thumbnail_view.xml", + "security/ir.model.access.csv", ], - "qweb": [ - ] + "demo": [], + "qweb": [], } diff --git a/storage_thumbnail/models/storage_file.py b/storage_thumbnail/models/storage_file.py index feaf04b67a..f14ff54cb3 100644 --- a/storage_thumbnail/models/storage_file.py +++ b/storage_thumbnail/models/storage_file.py @@ -7,6 +7,6 @@ class StorageFile(models.Model): - _inherit = 'storage.file' + _inherit = "storage.file" - file_type = fields.Selection(selection_add=[('thumbnail', 'Thumbnail')]) + file_type = fields.Selection(selection_add=[("thumbnail", "Thumbnail")]) diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index a6cfc738c8..14a1b12d41 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -4,48 +4,40 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging + from odoo import api, fields, models from odoo.tools import image_resize_image -import logging + _logger = logging.getLogger(__name__) class StorageThumbnail(models.Model): - _name = 'storage.thumbnail' - _description = 'Storage Thumbnail' - _inherits = {'storage.file': 'file_id'} + _name = "storage.thumbnail" + _description = "Storage Thumbnail" + _inherits = {"storage.file": "file_id"} size_x = fields.Integer("weight") size_y = fields.Integer("height") url_key = fields.Char( - "Url key", - help="Specific URL key for generating the url of the image", + "Url key", help="Specific URL key for generating the url of the image" ) file_id = fields.Many2one( - 'storage.file', - 'File', - required=True, - ondelete='cascade') - res_model = fields.Char( - readonly=False, - index=True) - res_id = fields.Integer( - readonly=False, - index=True) + "storage.file", "File", required=True, ondelete="cascade" + ) + res_model = fields.Char(readonly=False, index=True) + res_id = fields.Integer(readonly=False, index=True) def _prepare_thumbnail(self, image, size_x, size_y, url_key): return { - 'data': self._resize(image, size_x, size_y), - 'res_model': image._name, - 'res_id': image.id, - 'name': '%s_%s_%s%s' % ( - url_key or image.filename, - size_x, - size_y, - image.extension), - 'size_x': size_x, - 'size_y': size_y, - 'url_key': url_key, + "data": self._resize(image, size_x, size_y), + "res_model": image._name, + "res_id": image.id, + "name": "%s_%s_%s%s" + % (url_key or image.filename, size_x, size_y, image.extension), + "size_x": size_x, + "size_y": size_y, + "url_key": url_key, } def _resize(self, image, size_x, size_y): @@ -57,19 +49,21 @@ def _get_backend_id(self): By default : it's the one configured as ir.config_parameter Overload this method if you need something more powerfull """ - return int(self.env['ir.config_parameter'].get_param( - 'storage.thumbnail.backend_id')) + return int( + self.env["ir.config_parameter"].get_param( + "storage.thumbnail.backend_id" + ) + ) @api.model def create(self, vals): - vals.update({ - 'backend_id': self._get_backend_id(), - 'file_type': 'thumbnail', - }) + vals.update( + {"backend_id": self._get_backend_id(), "file_type": "thumbnail"} + ) return super(StorageThumbnail, self).create(vals) def unlink(self): - files = self.mapped('file_id') + files = self.mapped("file_id") result = super(StorageThumbnail, self).unlink() files.unlink() return result diff --git a/storage_thumbnail/models/thumbnail_mixin.py b/storage_thumbnail/models/thumbnail_mixin.py index 1916d3dcd9..03a44620e4 100644 --- a/storage_thumbnail/models/thumbnail_mixin.py +++ b/storage_thumbnail/models/thumbnail_mixin.py @@ -2,27 +2,28 @@ # Copyright 2017 Akretion (http://www.akretion.com). # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models - import logging +from odoo import api, fields, models + _logger = logging.getLogger(__name__) try: from slugify import slugify except ImportError: - _logger.debug('Cannot `import slugify`.') + _logger.debug("Cannot `import slugify`.") class ThumbnailMixing(models.AbstractModel): - _name = 'thumbnail.mixin' - _description = 'Thumbnail Mixin add the thumbnail capability' + _name = "thumbnail.mixin" + _description = "Thumbnail Mixin add the thumbnail capability" thumbnail_ids = fields.One2many( - comodel_name='storage.thumbnail', - string='Thumbnails', - inverse_name='res_id', - domain=lambda self: [("res_model", "=", self._name)]) + comodel_name="storage.thumbnail", + string="Thumbnails", + inverse_name="res_id", + domain=lambda self: [("res_model", "=", self._name)], + ) image_medium_url = fields.Char(readonly=True) image_small_url = fields.Char(readonly=True) @@ -38,7 +39,7 @@ def get_or_create_thumbnail(self, size_x, size_y, url_key=None): self = self.with_context(bin_size=False).with_prefetch(self._prefetch) if url_key: url_key = slugify(url_key) - thumbnail = self.env['storage.thumbnail'].browse() + thumbnail = self.env["storage.thumbnail"].browse() for th in self.thumbnail_ids: if th.size_x == size_x and th.size_y == size_y: if url_key and url_key != th.url_key: @@ -46,19 +47,22 @@ def get_or_create_thumbnail(self, size_x, size_y, url_key=None): thumbnail = th break if not thumbnail and self.data: - vals = self.env['storage.thumbnail']._prepare_thumbnail( - self, size_x, size_y, url_key) + vals = self.env["storage.thumbnail"]._prepare_thumbnail( + self, size_x, size_y, url_key + ) # use the relation to create the thumbnail to be sure that the # record is added to the cache of this relation. - self.write({'thumbnail_ids': [(0, 0, vals)]}) + self.write({"thumbnail_ids": [(0, 0, vals)]}) return self.get_or_create_thumbnail(size_x, size_y, url_key) return thumbnail def generate_odoo_thumbnail(self): - self.write({ - 'image_medium_url': self.sudo()._get_medium_thumbnail().url, - 'image_small_url': self.sudo()._get_small_thumbnail().url, - }) + self.write( + { + "image_medium_url": self.sudo()._get_medium_thumbnail().url, + "image_small_url": self.sudo()._get_small_thumbnail().url, + } + ) return True @api.model From 424ccfe39e223abb46f400814ea8784ea9ce909a Mon Sep 17 00:00:00 2001 From: Benoit Date: Fri, 12 Apr 2019 15:57:40 +0200 Subject: [PATCH 15/74] [IMP] add tests and support pilbox for thumbnail --- storage_thumbnail/models/storage_thumbnail.py | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index 14a1b12d41..9d407c1738 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -6,6 +6,7 @@ import logging +import requests from odoo import api, fields, models from odoo.tools import image_resize_image @@ -29,18 +30,38 @@ class StorageThumbnail(models.Model): res_id = fields.Integer(readonly=False, index=True) def _prepare_thumbnail(self, image, size_x, size_y, url_key): + image_resize_format = self.env["ir.config_parameter"].get_param( + "storage.image.resize.format" + ) + if image_resize_format: + extension = image_resize_format + else: + extension = image.extension return { "data": self._resize(image, size_x, size_y), "res_model": image._name, "res_id": image.id, "name": "%s_%s_%s%s" - % (url_key or image.filename, size_x, size_y, image.extension), + % (url_key or image.filename, size_x, size_y, extension), "size_x": size_x, "size_y": size_y, "url_key": url_key, } def _resize(self, image, size_x, size_y): + image_server_resize = self.env["ir.config_parameter"].get_param( + "storage.image.server.resize" + ) + if image_server_resize and image.backend_id.served_by != "odoo": + image_resize_format = self.env["ir.config_parameter"].get_param( + "storage.image.resize.format" + ) + values = {"url": image.url, "width": size_x, "height": size_y} + if image_resize_format: + values["format"] = image_resize_format + url = image_server_resize % values + request = requests.get(url) + return request.content.encode("base64") return image_resize_image(image.data, size=(size_x, size_y)) def _get_backend_id(self): From 9a331ac5c880569731a7b51033b3e19203b3dd31 Mon Sep 17 00:00:00 2001 From: Benoit Date: Fri, 12 Apr 2019 18:50:42 +0200 Subject: [PATCH 16/74] [IMP] tests --- storage_thumbnail/models/storage_thumbnail.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index 9d407c1738..3feb91599a 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -58,8 +58,8 @@ def _resize(self, image, size_x, size_y): ) values = {"url": image.url, "width": size_x, "height": size_y} if image_resize_format: - values["format"] = image_resize_format - url = image_server_resize % values + values["fmt"] = image_resize_format + url = image_server_resize.format(**values) request = requests.get(url) return request.content.encode("base64") return image_resize_image(image.data, size=(size_x, size_y)) From 7af7eecaaab97f760e86128bb0dd1b50f42fe9a4 Mon Sep 17 00:00:00 2001 From: Benoit Date: Fri, 12 Apr 2019 19:39:47 +0200 Subject: [PATCH 17/74] [FIX] clean code --- storage_thumbnail/models/storage_thumbnail.py | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index 3feb91599a..94de28655d 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -38,7 +38,7 @@ def _prepare_thumbnail(self, image, size_x, size_y, url_key): else: extension = image.extension return { - "data": self._resize(image, size_x, size_y), + "data": self._resize(image, size_x, size_y, image_resize_format), "res_model": image._name, "res_id": image.id, "name": "%s_%s_%s%s" @@ -48,20 +48,16 @@ def _prepare_thumbnail(self, image, size_x, size_y, url_key): "url_key": url_key, } - def _resize(self, image, size_x, size_y): - image_server_resize = self.env["ir.config_parameter"].get_param( - "storage.image.server.resize" + def _resize(self, image, size_x, size_y, fmt): + image_resize_server = self.env["ir.config_parameter"].get_param( + "storage.image.resize.server" ) - if image_server_resize and image.backend_id.served_by != "odoo": - image_resize_format = self.env["ir.config_parameter"].get_param( - "storage.image.resize.format" - ) + if image_resize_server and image.backend_id.served_by != "odoo": values = {"url": image.url, "width": size_x, "height": size_y} - if image_resize_format: - values["fmt"] = image_resize_format - url = image_server_resize.format(**values) - request = requests.get(url) - return request.content.encode("base64") + if fmt: + values["fmt"] = fmt + url = image_resize_server.format(**values) + return requests.get(url).content.encode("base64") return image_resize_image(image.data, size=(size_x, size_y)) def _get_backend_id(self): From 99ca8bc24d5129b452b80de24c09fe9b8f3c773e Mon Sep 17 00:00:00 2001 From: Benoit Date: Fri, 12 Apr 2019 19:47:13 +0200 Subject: [PATCH 18/74] [FIX] always send the extension --- storage_thumbnail/models/storage_thumbnail.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index 94de28655d..93c6a3495e 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -38,7 +38,7 @@ def _prepare_thumbnail(self, image, size_x, size_y, url_key): else: extension = image.extension return { - "data": self._resize(image, size_x, size_y, image_resize_format), + "data": self._resize(image, size_x, size_y, extension), "res_model": image._name, "res_id": image.id, "name": "%s_%s_%s%s" @@ -53,9 +53,12 @@ def _resize(self, image, size_x, size_y, fmt): "storage.image.resize.server" ) if image_resize_server and image.backend_id.served_by != "odoo": - values = {"url": image.url, "width": size_x, "height": size_y} - if fmt: - values["fmt"] = fmt + values = { + "url": image.url, + "width": size_x, + "height": size_y, + "fmt": fmt, + } url = image_resize_server.format(**values) return requests.get(url).content.encode("base64") return image_resize_image(image.data, size=(size_x, size_y)) From 7c376e8a8f85b672dc3a8be17835aec57ac6aaee Mon Sep 17 00:00:00 2001 From: Denis Roussel Date: Fri, 7 Jun 2019 11:55:47 +0200 Subject: [PATCH 19/74] [12.0] storage*: Make installable False --- storage_thumbnail/__manifest__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage_thumbnail/__manifest__.py b/storage_thumbnail/__manifest__.py index 07d911e676..2d20682eba 100644 --- a/storage_thumbnail/__manifest__.py +++ b/storage_thumbnail/__manifest__.py @@ -11,7 +11,7 @@ "website": "www.akretion.com", "author": " Akretion", "license": "AGPL-3", - "installable": True, + "installable": False, "external_dependencies": {"python": [], "bin": []}, "depends": ["storage_file"], "data": [ From e004a5676674143d02696e7e2bfce10ceeebb35d Mon Sep 17 00:00:00 2001 From: Denis Roussel Date: Tue, 11 Jun 2019 11:15:20 +0200 Subject: [PATCH 20/74] [12.0][MIG] storage_thumbnail --- storage_thumbnail/README.rst | 47 +- storage_thumbnail/__init__.py | 2 - storage_thumbnail/__manifest__.py | 9 +- storage_thumbnail/models/__init__.py | 2 - storage_thumbnail/models/storage_file.py | 1 - storage_thumbnail/models/storage_thumbnail.py | 27 +- storage_thumbnail/models/thumbnail_mixin.py | 8 +- storage_thumbnail/readme/CONTRIBUTORS.rst | 3 + storage_thumbnail/readme/DESCRIPTION.rst | 1 + .../static/description/index.html | 416 ++++++++++++++++++ storage_thumbnail/tests/__init__.py | 2 + storage_thumbnail/tests/models.py | 32 ++ .../tests/static/akretion-logo.png | Bin 0 -> 4522 bytes storage_thumbnail/tests/test_thumbnail.py | 82 ++++ 14 files changed, 593 insertions(+), 39 deletions(-) create mode 100644 storage_thumbnail/readme/CONTRIBUTORS.rst create mode 100644 storage_thumbnail/readme/DESCRIPTION.rst create mode 100644 storage_thumbnail/static/description/index.html create mode 100644 storage_thumbnail/tests/__init__.py create mode 100644 storage_thumbnail/tests/models.py create mode 100644 storage_thumbnail/tests/static/akretion-logo.png create mode 100644 storage_thumbnail/tests/test_thumbnail.py diff --git a/storage_thumbnail/README.rst b/storage_thumbnail/README.rst index 22d41c424e..38a6ad07ea 100644 --- a/storage_thumbnail/README.rst +++ b/storage_thumbnail/README.rst @@ -1,33 +1,56 @@ - -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 - ================= Storage Thumbnail ================= +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge2| image:: https://img.shields.io/badge/github-akretion%2Fstorage-lightgray.png?logo=github + :target: https://github.com/akretion/storage/tree/12.0/storage_thumbnail + :alt: akretion/storage + +|badge1| |badge2| External image thumbnail management depending on Storage File module. +**Table of contents** + +.. contents:: + :local: -Known issues / Roadmap -====================== +Bug Tracker +=========== -Update README with the last model of README when migration to v11 in OCA +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 `_. +Do not contact contributors directly about support or help with technical issues. Credits ======= +Authors +~~~~~~~ + +* Akretion Contributors ------------- +~~~~~~~~~~~~ * Sebastien Beau * Raphaël Reverdy +* Denis Roussel -Maintainer ----------- +Maintainers +~~~~~~~~~~~ -* Akretion +This module is part of the `akretion/storage `_ project on GitHub. + +You are welcome to contribute. diff --git a/storage_thumbnail/__init__.py b/storage_thumbnail/__init__.py index cde864bae2..0650744f6b 100644 --- a/storage_thumbnail/__init__.py +++ b/storage_thumbnail/__init__.py @@ -1,3 +1 @@ -# -*- coding: utf-8 -*- - from . import models diff --git a/storage_thumbnail/__manifest__.py b/storage_thumbnail/__manifest__.py index 2d20682eba..7ee9ad94c1 100644 --- a/storage_thumbnail/__manifest__.py +++ b/storage_thumbnail/__manifest__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Akretion (http://www.akretion.com). # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -6,19 +5,17 @@ { "name": "Storage Thumbnail", "summary": "Abstract module that add the possibility to have thumbnail", - "version": "10.0.1.0.0", + "version": "12.0.1.0.0", "category": "Storage", "website": "www.akretion.com", "author": " Akretion", "license": "AGPL-3", - "installable": False, - "external_dependencies": {"python": [], "bin": []}, + "development_status": "Stable/Production", + "installable": True, "depends": ["storage_file"], "data": [ "data/ir_parameter.xml", "views/storage_thumbnail_view.xml", "security/ir.model.access.csv", ], - "demo": [], - "qweb": [], } diff --git a/storage_thumbnail/models/__init__.py b/storage_thumbnail/models/__init__.py index 3e583d1f06..914c5542e3 100644 --- a/storage_thumbnail/models/__init__.py +++ b/storage_thumbnail/models/__init__.py @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - from . import storage_thumbnail from . import thumbnail_mixin from . import storage_file diff --git a/storage_thumbnail/models/storage_file.py b/storage_thumbnail/models/storage_file.py index f14ff54cb3..24124b74ca 100644 --- a/storage_thumbnail/models/storage_file.py +++ b/storage_thumbnail/models/storage_file.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Akretion (http://www.akretion.com). # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). diff --git a/storage_thumbnail/models/storage_thumbnail.py b/storage_thumbnail/models/storage_thumbnail.py index 93c6a3495e..f8542d6625 100644 --- a/storage_thumbnail/models/storage_thumbnail.py +++ b/storage_thumbnail/models/storage_thumbnail.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Akretion (http://www.akretion.com). # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - +import base64 import logging import requests @@ -30,8 +29,10 @@ class StorageThumbnail(models.Model): res_id = fields.Integer(readonly=False, index=True) def _prepare_thumbnail(self, image, size_x, size_y, url_key): - image_resize_format = self.env["ir.config_parameter"].get_param( - "storage.image.resize.format" + image_resize_format = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("storage.image.resize.format") ) if image_resize_format: extension = image_resize_format @@ -49,8 +50,10 @@ def _prepare_thumbnail(self, image, size_x, size_y, url_key): } def _resize(self, image, size_x, size_y, fmt): - image_resize_server = self.env["ir.config_parameter"].get_param( - "storage.image.resize.server" + image_resize_server = ( + self.env["ir.config_parameter"] + .sudo() + .get_param("storage.image.resize.server") ) if image_resize_server and image.backend_id.served_by != "odoo": values = { @@ -60,7 +63,7 @@ def _resize(self, image, size_x, size_y, fmt): "fmt": fmt, } url = image_resize_server.format(**values) - return requests.get(url).content.encode("base64") + return base64.encodestring(requests.get(url).content) return image_resize_image(image.data, size=(size_x, size_y)) def _get_backend_id(self): @@ -70,9 +73,9 @@ def _get_backend_id(self): Overload this method if you need something more powerfull """ return int( - self.env["ir.config_parameter"].get_param( - "storage.thumbnail.backend_id" - ) + self.env["ir.config_parameter"] + .sudo() + .get_param("storage.thumbnail.backend_id") ) @api.model @@ -80,10 +83,10 @@ def create(self, vals): vals.update( {"backend_id": self._get_backend_id(), "file_type": "thumbnail"} ) - return super(StorageThumbnail, self).create(vals) + return super().create(vals) def unlink(self): files = self.mapped("file_id") - result = super(StorageThumbnail, self).unlink() + result = super().unlink() files.unlink() return result diff --git a/storage_thumbnail/models/thumbnail_mixin.py b/storage_thumbnail/models/thumbnail_mixin.py index 03a44620e4..635c8bc1ea 100644 --- a/storage_thumbnail/models/thumbnail_mixin.py +++ b/storage_thumbnail/models/thumbnail_mixin.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # Copyright 2017 Akretion (http://www.akretion.com). # @author Sébastien BEAU # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). @@ -57,16 +56,17 @@ def get_or_create_thumbnail(self, size_x, size_y, url_key=None): return thumbnail def generate_odoo_thumbnail(self): + self_sudo = self.sudo() self.write( { - "image_medium_url": self.sudo()._get_medium_thumbnail().url, - "image_small_url": self.sudo()._get_small_thumbnail().url, + "image_medium_url": self_sudo._get_medium_thumbnail().url, + "image_small_url": self_sudo._get_small_thumbnail().url, } ) return True @api.model def create(self, vals): - record = super(ThumbnailMixing, self).create(vals) + record = super().create(vals) record.generate_odoo_thumbnail() return record diff --git a/storage_thumbnail/readme/CONTRIBUTORS.rst b/storage_thumbnail/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..a204eb1849 --- /dev/null +++ b/storage_thumbnail/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* Sebastien Beau +* Raphaël Reverdy +* Denis Roussel diff --git a/storage_thumbnail/readme/DESCRIPTION.rst b/storage_thumbnail/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..44035c092a --- /dev/null +++ b/storage_thumbnail/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +External image thumbnail management depending on Storage File module. diff --git a/storage_thumbnail/static/description/index.html b/storage_thumbnail/static/description/index.html new file mode 100644 index 0000000000..c45ae5e2a0 --- /dev/null +++ b/storage_thumbnail/static/description/index.html @@ -0,0 +1,416 @@ + + + + + + +Storage Thumbnail + + + +
+

Storage Thumbnail

+ + +

License: AGPL-3 akretion/storage

+

External image thumbnail management depending on Storage File module.

+

Table of contents

+ +
+

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.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Akretion
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the akretion/storage project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + diff --git a/storage_thumbnail/tests/__init__.py b/storage_thumbnail/tests/__init__.py new file mode 100644 index 0000000000..81ff4f599c --- /dev/null +++ b/storage_thumbnail/tests/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import test_thumbnail diff --git a/storage_thumbnail/tests/models.py b/storage_thumbnail/tests/models.py new file mode 100644 index 0000000000..9f5f55783d --- /dev/null +++ b/storage_thumbnail/tests/models.py @@ -0,0 +1,32 @@ +from odoo import api, fields, models + + +# Declare as Transient to avoid ACL missing warning +class ModelTest(models.TransientModel): + + _name = "model.test" + _inherit = "thumbnail.mixin" + _inherits = {"storage.file": "file_id"} + + alt_name = fields.Char(string="Alt Image name") + file_id = fields.Many2one( + "storage.file", "File", required=True, ondelete="cascade" + ) + + @api.model + def _get_backend_id(self): + return int( + self.env["ir.config_parameter"].get_param( + "storage.thumbnail.backend_id" + ) + ) + + @api.model + def create(self, vals): + vals["file_type"] = "thumbnail" + if "backend_id" not in vals: + vals.update({"backend_id": self._get_backend_id()}) + for key in ["image_medium_url", "image_small_url"]: + if key in vals: + vals["data"] = vals.pop(key) + return super().create(vals) diff --git a/storage_thumbnail/tests/static/akretion-logo.png b/storage_thumbnail/tests/static/akretion-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..12b824c034d80660906445dc5be2ac310f0391c9 GIT binary patch literal 4522 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 IM6N<$g5;jCoB#j- literal 0 HcmV?d00001 diff --git a/storage_thumbnail/tests/test_thumbnail.py b/storage_thumbnail/tests/test_thumbnail.py new file mode 100644 index 0000000000..6211e3f0ff --- /dev/null +++ b/storage_thumbnail/tests/test_thumbnail.py @@ -0,0 +1,82 @@ +import base64 +import os +from operator import attrgetter + +from odoo.addons.component.tests.common import TransactionComponentCase +from odoo.fields import first + +from .models import ModelTest + + +class TestStorageThumbnail(TransactionComponentCase): + def setUp(self): + super().setUp() + + # Register model inheritance + ModelTest._build_model(self.env.registry, self.env.cr) + self.env.registry.setup_models(self.env.cr) + ctx = dict(self.env.context, update_custom_fields=True, module="base") + self.env.registry.init_models(self.env.cr, [ModelTest._name], ctx) + + # create model + path = os.path.dirname(os.path.abspath(__file__)) + with open(os.path.join(path, "static/akretion-logo.png"), "rb") as f: + data = f.read() + self.filesize = len(data) + self.filedata = base64.b64encode(data) + self.filename = "akretion-logo.png" + + def tearDown(self): + super().tearDown() + env = self.env + del env.registry.models[ModelTest._name] + parents = ModelTest._inherit + parents = [parents] if isinstance(parents, str) else (parents or []) + # keep a copy to be sure to not modify the original _inherit + parents = list(parents) + parents.extend(ModelTest._inherits.keys()) + parents.append("base") + funcs = [ + attrgetter(kind + "_children") + for kind in ["_inherits", "_inherit"] + ] + for parent in parents: + for func in funcs: + children = func(env.registry[parent]) + if ModelTest._name in children: + # at this stage our cls is referenced as children of + # parent -> must un reference it + children.remove(ModelTest._name) + + def _create_thumbnail(self): + # create thumbnail + vals = {"name": "TEST THUMB"} + self.thumbnail = self.env["storage.thumbnail"].create(vals) + + def _create_model(self, resize=False): + if resize: + self.env["ir.config_parameter"].sudo().create( + {"key": "storage.image.resize.format", "value": ".webp"} + ) + vals = {"name": self.filename, "image_medium_url": self.filedata} + self.image = self.env["model.test"].create(vals) + + def test_thumbnail(self): + self._create_thumbnail() + self.assertTrue(self.thumbnail.url) + file_id = self.thumbnail.file_id + self.assertTrue(file_id) + + self.thumbnail.unlink() + self.assertTrue(file_id.to_delete) + + def test_model(self): + self._create_model() + self.assertTrue(self.image.url) + self.assertEquals(2, len(self.image.thumbnail_ids)) + self.assertEquals(".png", first(self.image.thumbnail_ids).extension) + + def test_model_resize(self): + self._create_model(resize=True) + self.assertIn("webp", first(self.image.thumbnail_ids).url) + self.assertEquals(".webp", first(self.image.thumbnail_ids).extension) From bfbc1385d0f4ef08d0181f45af000b7d0e0dae90 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Sun, 15 Sep 2019 19:41:11 +0000 Subject: [PATCH 21/74] [UPD] README.rst --- storage_thumbnail/README.rst | 34 ++++++++++++++----- .../static/description/index.html | 17 ++++++---- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/storage_thumbnail/README.rst b/storage_thumbnail/README.rst index 38a6ad07ea..bacf869f23 100644 --- a/storage_thumbnail/README.rst +++ b/storage_thumbnail/README.rst @@ -10,11 +10,17 @@ Storage Thumbnail .. |badge1| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 -.. |badge2| image:: https://img.shields.io/badge/github-akretion%2Fstorage-lightgray.png?logo=github - :target: https://github.com/akretion/storage/tree/12.0/storage_thumbnail - :alt: akretion/storage - -|badge1| |badge2| +.. |badge2| image:: https://img.shields.io/badge/github-OCA%2Fstorage-lightgray.png?logo=github + :target: https://github.com/OCA/storage/tree/12.0/storage_thumbnail + :alt: OCA/storage +.. |badge3| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/storage-12-0/storage-12-0-storage_thumbnail + :alt: Translate me on Weblate +.. |badge4| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/275/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| External image thumbnail management depending on Storage File module. @@ -26,10 +32,10 @@ External image thumbnail management depending on Storage File module. Bug Tracker =========== -Bugs are tracked on `GitHub Issues `_. +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 `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -51,6 +57,16 @@ Contributors Maintainers ~~~~~~~~~~~ -This module is part of the `akretion/storage `_ project on GitHub. +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 `_ project on GitHub. -You are welcome to contribute. +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/storage_thumbnail/static/description/index.html b/storage_thumbnail/static/description/index.html index c45ae5e2a0..9acb8cf2e6 100644 --- a/storage_thumbnail/static/description/index.html +++ b/storage_thumbnail/static/description/index.html @@ -3,7 +3,7 @@ - + Storage Thumbnail