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,47 @@
# Fs Image Thumbnail
Odoo addon: fs_image_thumbnail
## Installation
```bash
pip install odoo-bringout-oca-storage-fs_image_thumbnail
```
## Dependencies
This addon depends on:
- fs_image
- base_partition
## Manifest Information
- **Name**: Fs Image Thumbnail
- **Version**: 16.0.1.0.2
- **Category**: N/A
- **License**: AGPL-3
- **Installable**: False
## Source
Based on [OCA/storage](https://github.com/OCA/storage) branch 16.0, addon `fs_image_thumbnail`.
## License
This package maintains the original AGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Reports: doc/REPORTS.md
- Security: doc/SECURITY.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph Fs_image_thumbnail Module - fs_image_thumbnail
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for fs_image_thumbnail. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,6 @@
# Dependencies
This addon depends on:
- [fs_image](../../odoo-bringout-oca-storage-fs_image)
- [base_partition](../../odoo-bringout-oca-server-tools-base_partition)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon fs_image_thumbnail or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-storage-fs_image_thumbnail"
# or
uv pip install odoo-bringout-oca-storage-fs_image_thumbnail"
```

View file

@ -0,0 +1,15 @@
# Models
Detected core models and extensions in fs_image_thumbnail.
```mermaid
classDiagram
class fs_image_thumbnail_mixin
class fs_thumbnail
class fs_image_thumbnail_mixin
class ir_attachment
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: fs_image_thumbnail. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon fs_image_thumbnail
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,73 @@
# Security
Access control and security definitions in fs_image_thumbnail.
## Access Control Lists (ACLs)
Model access permissions defined in:
- **[all_odoo_addons_repos.txt](../all_odoo_addons_repos.txt)**
- 318 model access rules
- **[bosnian_translations.json](../bosnian_translations.json)**
- 50 model access rules
- **[bosnian_translations_output.json](../bosnian_translations_output.json)**
- 444 model access rules
- **[CHANGELOG.md](../CHANGELOG.md)**
- 132 model access rules
- **[delete_all_odoo_addons.sh](../delete_all_odoo_addons.sh)**
- 50 model access rules
- **[delete_odoo_addons.sh](../delete_odoo_addons.sh)**
- 44 model access rules
- **[doc](../doc)**
- **[docker](../docker)**
- **[input](../input)**
- **[nix](../nix)**
- **[odoo.conf](../odoo.conf)**
- 58 model access rules
- **[odoo_packages_bez_l10n.txt](../odoo_packages_bez_l10n.txt)**
- 1947 model access rules
- **[odoo_packages_bringout.txt](../odoo_packages_bringout.txt)**
- 1947 model access rules
- **[odoo_packages.txt](../odoo_packages.txt)**
- 2085 model access rules
- **[output](../output)**
- **[packages](../packages)**
- **[PACKAGES.md](../PACKAGES.md)**
- 298 model access rules
- **[README.md](../README.md)**
- 338 model access rules
- **[scripts](../scripts)**
- **[temp](../temp)**
- **[TRANSLATION_BS_SUMMARY.md](../TRANSLATION_BS_SUMMARY.md)**
- 146 model access rules
- **[verify_deletions.sh](../verify_deletions.sh)**
- 55 model access rules
## Record Rules
Row-level security rules defined in:
## Security Groups & Configuration
Security groups and permissions defined in:
- **[fs_thumbnail.xml](../fs_image_thumbnail/security/fs_thumbnail.xml)**
```mermaid
graph TB
subgraph "Security Layers"
A[Users] --> B[Groups]
B --> C[Access Control Lists]
C --> D[Models]
B --> E[Record Rules]
E --> F[Individual Records]
end
```
Security files overview:
- **[fs_thumbnail.xml](../fs_image_thumbnail/security/fs_thumbnail.xml)**
- Security groups, categories, and XML-based rules
Notes
- Access Control Lists define which groups can access which models
- Record Rules provide row-level security (filter records by user/group)
- Security groups organize users and define permission sets
- All security is enforced at the ORM level by Odoo

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

View file

@ -0,0 +1,7 @@
# Usage
Start Odoo including this addon (from repo root):
```bash
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon fs_image_thumbnail
```

View file

@ -0,0 +1,3 @@
# Wizards
This module does not include UI wizards.

View file

@ -0,0 +1,196 @@
==================
Fs Image Thumbnail
==================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ae84af058fd490c7c8916156dc7db31813b6d5f7535e722740b152d6955e0d57
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| 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
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstorage-lightgray.png?logo=github
:target: https://github.com/OCA/storage/tree/16.0/fs_image_thumbnail
: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_image_thumbnail
: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 module extends the **fs_image** addon to support the creation and the storage of
thumbnails for images. This module is a **technical module** and is not
meant to be installed by end-users. It only provides a mixin to be used
by other modules and a model to store the thumbnails.
.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_
**Table of contents**
.. contents::
:local:
Use Cases / Context
===================
In some specific cases you may need to generate and store thumbnails of images in Odoo.
This is the case for example when you want to provide image in specific sizes for a website
or a mobile application.
This module provides a generic way to generate thumbnails of images and store them in a
specific filesystem storage. Indeed, you could need to store the thumbnails in a different
storage than the original image (eg: store the thumbnails in a CDN) to make sure the
thumbnails are served quickly when requested by an external application and to
avoid to expose the original image storage.
This module uses the `fs_image <https://github.com/oca/storage/blob/16.0/fs_image/README.rst>`_
module to store the thumbnails in a filesystem storage.
The `shopinvader_product_image <https://github.com/shopinvader/odoo-shopinvader/
blob/16.0/shopinvader_product_image>`_ addon uses this module to generate and
store the thumbnails of the images of the products and categories to be accessible
by the website.
Usage
=====
This addon provides a convenient way to get and create if not exists image
thumbnails. All the logic is implemented by the abstract model
`fs.image.thumbnail.mixin`. The main method is `get_or_create_thumbnails` which
accepts a *FSImageValue* instance, a list of thumbnail sizes and a base name.
When the method is called, it will check if the thumbnail exists for the given
sizes and base name. If not, it will create it.
The `fs.thumbnail` model provided by this addon is a concrete implementation of
the abstract model `fs.image.thumbnail.mixin`. The motivation to implement all the
logic in an abstract model is to allow developers to create their own thumbnail
models. This could be useful if you want to store the thumbnails in a different
storage since you can specify the storage to use by model on the `fs.storage`
form view.
Creating / retrieving thumbnails is as simple as:
.. code-block:: python
from odoo.addons.fs_image.fields import FSImageValue
# create an attachment with a image file
attachment = self.env['ir.attachment'].create({
'name': 'test',
'datas': base64.b64encode(open('test.png', 'rb').read()),
'datas_fname': 'test.png',
})
# create a FSImageValue instance for the attachment
image_value = FSImageValue(attachment)
# get or create the thumbnails
thumbnails = self.env['fs.thumbnail'].get_or_create_thumbnails(
image_value, [(800,600), (400, 200)], 'my base name')
If you've a model with a *FSImage* field, the call to `get_or_create_thumbnails`
is even simpler:
.. code-block:: python
from odoo import models
from odoo.addons.fs_image.fields import FSImage
class MyModel(models.Model):
_name = 'my.model'
image = FSImage('Image')
my_record = cls.env['my.model'].create({
'image': open('test.png', 'rb'),
})
# get or create the thumbnails
thumbnails = record.image.get_or_create_thumbnails(my_record.image,
[(800,600), (400, 200)], 'my base name')
Changelog
=========
16.0.1.0.1 (2023-10-04)
~~~~~~~~~~~~~~~~~~~~~~~
**Bugfixes**
- The call to the method *get_or_create_thumbnails* on the *fs.image.thumbnail.mixin*
class returns now an ordered dictionary where the key is the original image and
the value is a recordset of thumbnail images. The order of the dict is the order
of the images passed to the method. This ensures that when you process the result
of the method you can be sure that the order of the images is the same as the
order of the images passed to the method. (`#282 <https://github.com/OCA/storage/issues/282>`_)
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_image_thumbnail%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> (https://acsone.eu)
Other credits
~~~~~~~~~~~~~
The development of this module has been financially supported by:
* `Alcyon Belux <https://www.alcyonbelux.be/>`_
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.
.. |maintainer-lmignon| image:: https://github.com/lmignon.png?size=40px
:target: https://github.com/lmignon
:alt: lmignon
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-lmignon|
This module is part of the `OCA/storage <https://github.com/OCA/storage/tree/16.0/fs_image_thumbnail>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

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

View file

@ -0,0 +1,23 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Fs Image Thumbnail",
"summary": """
Generate and store thumbnail for images""",
"version": "16.0.1.0.2",
"license": "AGPL-3",
"author": "ACSONE SA/NV,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/storage",
"depends": ["fs_image", "base_partition"],
"data": [
"views/ir_attachment.xml",
"security/fs_thumbnail.xml",
"views/fs_image_thumbnail_mixin.xml",
"views/fs_thumbnail.xml",
],
"demo": [],
"maintainers": ["lmignon"],
"development_status": "Alpha",
"external_dependencies": {"python": ["python_slugify"]},
}

View file

@ -0,0 +1,170 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fs_image_thumbnail
#
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_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_ir_attachment
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__attachment_id
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__attachment_id
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Attachment"
msgstr "Prilog"
#. module: fs_image_thumbnail
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_image_thumbnail_mixin__attachment_id
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_thumbnail__attachment_id
msgid "Attachment containing the original image"
msgstr "Prilog koji sadrži originalnu sliku"
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Base Name"
msgstr "Osnovno ime"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__create_uid
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_thumbnail_search_view
msgid "Created by"
msgstr "Kreirao"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__create_date
msgid "Created on"
msgstr "Kreirano"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__display_name
msgid "Display Name"
msgstr "Prikazani naziv"
#. module: fs_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_fs_image_thumbnail_mixin
msgid "Fs Image Thumbnail Mixin"
msgstr "Fs Image Thumbnail Mixin"
#. module: fs_image_thumbnail
#: model:ir.ui.menu,name:fs_image_thumbnail.fs_thumbnail_menu
msgid "Fs Image Thumbnails"
msgstr "Fs slike minijatura"
#. module: fs_image_thumbnail
#: model:ir.actions.act_window,name:fs_image_thumbnail.fs_thumbnail_act_window
msgid "Fs Thumbnail"
msgstr "Fs minijatura"
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Group By"
msgstr "Grupiši po"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__id
msgid "ID"
msgstr "ID"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__image
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__image
msgid "Image"
msgstr "Slika"
#. module: fs_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_fs_thumbnail
msgid "Image Thumbnail"
msgstr "Minijatura slike"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail____last_update
msgid "Last Modified on"
msgstr "Zadnje mijenjano"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__write_uid
msgid "Last Updated by"
msgstr "Zadnji ažurirao"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__write_date
msgid "Last Updated on"
msgstr "Zadnje ažurirano"
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "MimeType"
msgstr "MimeType"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__mimetype
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__mimetype
msgid "Mimetype"
msgstr "Mimetype"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__name
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__name
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Name"
msgstr "Naziv:"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__original_image
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__original_image
msgid "Original Image"
msgstr "Originalna slika"
#. module: fs_image_thumbnail
#. odoo-python
#: code:addons/fs_image_thumbnail/models/fs_image_thumbnail_mixin.py:0
#, python-format
msgid "The base name must be set when multiple images are given"
msgstr "Osnovno ime mora biti postavljen kad je dano više slika"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__base_name
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__base_name
msgid "The base name of the thumbnail image (without extension)"
msgstr "Osnovno ime minijature slike (bez ekstenzije)"
#. module: fs_image_thumbnail
#. odoo-python
#: code:addons/fs_image_thumbnail/models/fs_image_thumbnail_mixin.py:0
#, python-format
msgid "The image %(name)s must be attached to an attachment"
msgstr "Slika %(name)s mora biti priložena priloga"
#. module: fs_image_thumbnail
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_image_thumbnail_mixin__base_name
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_thumbnail__base_name
msgid ""
"The thumbnail image will be named as base_name + _ + size_x + _ + size_y + . + extension.\n"
"If not set, the base name will be the name of the original image.This base name is used to find all existing thumbnail of an image generated for the same base name."
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_ir_attachment__thumbnail_ids
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.ir_attachment_form_view
msgid "Thumbnails"
msgstr "Sličice"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__size_x
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__size_x
msgid "X size"
msgstr "X veličina"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__size_y
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__size_y
msgid "Y size"
msgstr "Y veličina"

View file

@ -0,0 +1,178 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fs_image_thumbnail
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-10-29 00:15+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_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_ir_attachment
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__attachment_id
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__attachment_id
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Attachment"
msgstr "Archivo Adjunto"
#. module: fs_image_thumbnail
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_image_thumbnail_mixin__attachment_id
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_thumbnail__attachment_id
msgid "Attachment containing the original image"
msgstr "Archivo adjunto con la imagen original"
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Base Name"
msgstr "Nombre de la Base"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__create_uid
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_thumbnail_search_view
msgid "Created by"
msgstr "Creado por"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__create_date
msgid "Created on"
msgstr "Creado el"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__display_name
msgid "Display Name"
msgstr "Mostrar Nombre"
#. module: fs_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_fs_image_thumbnail_mixin
msgid "Fs Image Thumbnail Mixin"
msgstr "Mezcla de Miniaturas de imágenes Fs"
#. module: fs_image_thumbnail
#: model:ir.ui.menu,name:fs_image_thumbnail.fs_thumbnail_menu
msgid "Fs Image Thumbnails"
msgstr "Miniaturas de imágenes Fs"
#. module: fs_image_thumbnail
#: model:ir.actions.act_window,name:fs_image_thumbnail.fs_thumbnail_act_window
msgid "Fs Thumbnail"
msgstr "Miniatura Fs"
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Group By"
msgstr "Agrupado Por"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__id
msgid "ID"
msgstr "ID (identificación)"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__image
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__image
msgid "Image"
msgstr "Imagen"
#. module: fs_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_fs_thumbnail
msgid "Image Thumbnail"
msgstr "Imagen en Miniatura"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail____last_update
msgid "Last Modified on"
msgstr "Última Modificación el"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__write_uid
msgid "Last Updated by"
msgstr "Última Actualización por"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__write_date
msgid "Last Updated on"
msgstr "Última Actualización el"
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "MimeType"
msgstr "Tipo Mimo"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__mimetype
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__mimetype
msgid "Mimetype"
msgstr "Tipo Mimo"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__name
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__name
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Name"
msgstr "Nombre"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__original_image
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__original_image
msgid "Original Image"
msgstr "Imagen Original"
#. module: fs_image_thumbnail
#. odoo-python
#: code:addons/fs_image_thumbnail/models/fs_image_thumbnail_mixin.py:0
#, python-format
msgid "The base name must be set when multiple images are given"
msgstr "El nombre base debe establecerse cuando se dan varias imágenes"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__base_name
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__base_name
msgid "The base name of the thumbnail image (without extension)"
msgstr "El nombre base de la imagen en miniatura (sin extensión)"
#. module: fs_image_thumbnail
#. odoo-python
#: code:addons/fs_image_thumbnail/models/fs_image_thumbnail_mixin.py:0
#, python-format
msgid "The image %(name)s must be attached to an attachment"
msgstr "La imagen %(name)s debe adjuntarse a un archivo adjunto"
#. module: fs_image_thumbnail
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_image_thumbnail_mixin__base_name
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_thumbnail__base_name
msgid ""
"The thumbnail image will be named as base_name + _ + size_x + _ + size_y + . + extension.\n"
"If not set, the base name will be the name of the original image.This base name is used to find all existing thumbnail of an image generated for the same base name."
msgstr ""
"La imagen en miniatura se denominará como nombre_base + _ + tamaño_x + _ + "
"tamaño_y + . + extensión.\n"
"Si no se establece, el nombre base será el nombre de la imagen original. "
"Este nombre base se utiliza para encontrar todas las miniaturas existentes "
"de una imagen generada para el mismo nombre base."
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_ir_attachment__thumbnail_ids
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.ir_attachment_form_view
msgid "Thumbnails"
msgstr "Miniaturas"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__size_x
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__size_x
msgid "X size"
msgstr "Tamaño X"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__size_y
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__size_y
msgid "Y size"
msgstr "Talla Y"

View file

@ -0,0 +1,170 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fs_image_thumbnail
#
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_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_ir_attachment
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__attachment_id
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__attachment_id
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Attachment"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_image_thumbnail_mixin__attachment_id
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_thumbnail__attachment_id
msgid "Attachment containing the original image"
msgstr ""
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Base Name"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__create_uid
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_thumbnail_search_view
msgid "Created by"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__create_date
msgid "Created on"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__display_name
msgid "Display Name"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_fs_image_thumbnail_mixin
msgid "Fs Image Thumbnail Mixin"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.ui.menu,name:fs_image_thumbnail.fs_thumbnail_menu
msgid "Fs Image Thumbnails"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.actions.act_window,name:fs_image_thumbnail.fs_thumbnail_act_window
msgid "Fs Thumbnail"
msgstr ""
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Group By"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__id
msgid "ID"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__image
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__image
msgid "Image"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_fs_thumbnail
msgid "Image Thumbnail"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail____last_update
msgid "Last Modified on"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__write_uid
msgid "Last Updated by"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__write_date
msgid "Last Updated on"
msgstr ""
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "MimeType"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__mimetype
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__mimetype
msgid "Mimetype"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__name
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__name
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Name"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__original_image
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__original_image
msgid "Original Image"
msgstr ""
#. module: fs_image_thumbnail
#. odoo-python
#: code:addons/fs_image_thumbnail/models/fs_image_thumbnail_mixin.py:0
#, python-format
msgid "The base name must be set when multiple images are given"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__base_name
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__base_name
msgid "The base name of the thumbnail image (without extension)"
msgstr ""
#. module: fs_image_thumbnail
#. odoo-python
#: code:addons/fs_image_thumbnail/models/fs_image_thumbnail_mixin.py:0
#, python-format
msgid "The image %(name)s must be attached to an attachment"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_image_thumbnail_mixin__base_name
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_thumbnail__base_name
msgid ""
"The thumbnail image will be named as base_name + _ + size_x + _ + size_y + . + extension.\n"
"If not set, the base name will be the name of the original image.This base name is used to find all existing thumbnail of an image generated for the same base name."
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_ir_attachment__thumbnail_ids
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.ir_attachment_form_view
msgid "Thumbnails"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__size_x
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__size_x
msgid "X size"
msgstr ""
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__size_y
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__size_y
msgid "Y size"
msgstr ""

View file

@ -0,0 +1,178 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * fs_image_thumbnail
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-12-12 11:33+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 4.17\n"
#. module: fs_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_ir_attachment
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__attachment_id
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__attachment_id
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Attachment"
msgstr "Allegato"
#. module: fs_image_thumbnail
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_image_thumbnail_mixin__attachment_id
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_thumbnail__attachment_id
msgid "Attachment containing the original image"
msgstr "Allegato contenente l'immagine originale"
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Base Name"
msgstr "Nome base"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__create_uid
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_thumbnail_search_view
msgid "Created by"
msgstr "Creato da"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__create_date
msgid "Created on"
msgstr "Creato il"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__display_name
msgid "Display Name"
msgstr "Nome visualizzato"
#. module: fs_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_fs_image_thumbnail_mixin
msgid "Fs Image Thumbnail Mixin"
msgstr "Mixin anteprima immagine FS"
#. module: fs_image_thumbnail
#: model:ir.ui.menu,name:fs_image_thumbnail.fs_thumbnail_menu
msgid "Fs Image Thumbnails"
msgstr "Anteprima immagine FS"
#. module: fs_image_thumbnail
#: model:ir.actions.act_window,name:fs_image_thumbnail.fs_thumbnail_act_window
msgid "Fs Thumbnail"
msgstr "Anteprima FS"
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Group By"
msgstr "Raggruppa per"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__id
msgid "ID"
msgstr "ID"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__image
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__image
msgid "Image"
msgstr "Immagine"
#. module: fs_image_thumbnail
#: model:ir.model,name:fs_image_thumbnail.model_fs_thumbnail
msgid "Image Thumbnail"
msgstr "Anteprima immagine"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail____last_update
msgid "Last Modified on"
msgstr "Ultima modifica il"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__write_uid
msgid "Last Updated by"
msgstr "Ultimo aggiornamento di"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__write_date
msgid "Last Updated on"
msgstr "Ultimo aggiornamento il"
#. module: fs_image_thumbnail
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "MimeType"
msgstr "Tipo MIME"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__mimetype
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__mimetype
msgid "Mimetype"
msgstr "Tipo MIME"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__name
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__name
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.fs_image_thumbnail_mixin_search_view
msgid "Name"
msgstr "Nome"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__original_image
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__original_image
msgid "Original Image"
msgstr "Immagine originale"
#. module: fs_image_thumbnail
#. odoo-python
#: code:addons/fs_image_thumbnail/models/fs_image_thumbnail_mixin.py:0
#, python-format
msgid "The base name must be set when multiple images are given"
msgstr "Il nome base deve essere impostato qando vengono fornite più immagini"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__base_name
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__base_name
msgid "The base name of the thumbnail image (without extension)"
msgstr "Il nome base dell'immagIne anteprima (senza estensione)"
#. module: fs_image_thumbnail
#. odoo-python
#: code:addons/fs_image_thumbnail/models/fs_image_thumbnail_mixin.py:0
#, python-format
msgid "The image %(name)s must be attached to an attachment"
msgstr "L'immagine %(name)s deve essere collegata ad una allegato"
#. module: fs_image_thumbnail
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_image_thumbnail_mixin__base_name
#: model:ir.model.fields,help:fs_image_thumbnail.field_fs_thumbnail__base_name
msgid ""
"The thumbnail image will be named as base_name + _ + size_x + _ + size_y + . + extension.\n"
"If not set, the base name will be the name of the original image.This base name is used to find all existing thumbnail of an image generated for the same base name."
msgstr ""
"L'immagine anteprima verrà denominata come "
"nome_bae+_+dimensione_x+dimensione_y+.+estensione.\n"
"Se non impostato, il nome base sarà il nome dell'immagine originale. Questo "
"nome base è utilizzato per trovare tutte le anteprime di una immagine "
"generate per lo stesso nome base."
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_ir_attachment__thumbnail_ids
#: model_terms:ir.ui.view,arch_db:fs_image_thumbnail.ir_attachment_form_view
msgid "Thumbnails"
msgstr "Anteprime"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__size_x
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__size_x
msgid "X size"
msgstr "Dimensione X"
#. module: fs_image_thumbnail
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_image_thumbnail_mixin__size_y
#: model:ir.model.fields,field_description:fs_image_thumbnail.field_fs_thumbnail__size_y
msgid "Y size"
msgstr "Dimensione Y"

View file

@ -0,0 +1,3 @@
from . import fs_image_thumbnail_mixin
from . import fs_thumbnail
from . import ir_attachment

View file

@ -0,0 +1,242 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from collections import OrderedDict
from slugify import slugify
from odoo import _, api, fields, models
from odoo.exceptions import UserError
from odoo.addons.fs_image.fields import FSImage, FSImageValue
class FsImageThumbnailMixin(models.AbstractModel):
"""Mixin defining what is a thumbnail image and providing a
method to generate a thumbnail image from an image.
"""
_name = "fs.image.thumbnail.mixin"
_description = "Fs Image Thumbnail Mixin"
image = FSImage("Image", required=True)
original_image = FSImage("Original Image", compute="_compute_original_image")
size_x = fields.Integer("X size", required=True)
size_y = fields.Integer("Y size", required=True)
base_name = fields.Char(
"The base name of the thumbnail image (without extension)",
required=True,
help="The thumbnail image will be named as base_name "
"+ _ + size_x + _ + size_y + . + extension.\n"
"If not set, the base name will be the name of the original image."
"This base name is used to find all existing thumbnail of an image generated "
"for the same base name.",
)
attachment_id = fields.Many2one(
comodel_name="ir.attachment",
string="Attachment",
help="Attachment containing the original image",
required=True,
ondelete="cascade",
)
name = fields.Char(
compute="_compute_name",
store=True,
)
mimetype = fields.Char(
compute="_compute_mimetype",
store=True,
)
@api.depends("image")
def _compute_name(self):
for record in self:
record.name = record.image.name if record.image else None
@api.depends("image")
def _compute_mimetype(self):
for record in self:
record.mimetype = record.image.mimetype if record.image else None
@api.depends("attachment_id")
def _compute_original_image(self):
original_image_field = self._fields["original_image"]
for record in self:
value = None
if record.attachment_id:
value = original_image_field._convert_attachment_to_cache(
record.attachment_id
)
record.original_image = value
@api.model
def _resize(self, image: FSImage, size_x: int, size_y: int, fmt: str = "") -> bytes:
"""Resize the given image to the given size.
:param image: the image to resize
:param size_x: the new width of the image
:param size_y: the new height of the image
:param fmt: the output format of the image. Can be PNG, JPEG, GIF, or ICO.
Default to the format of the original image. BMP is converted to
PNG, other formats than those mentioned above are converted to JPEG.
:return: the resized image
"""
# image_process only accept PNG, JPEG, GIF, or ICO as output format
# in uppercase. Remove the dot if present and convert to uppercase.
fmt = fmt.upper().replace(".", "")
return image.image_process(size=(size_x, size_y), output_format=fmt)
@api.model
def _get_resize_format(self, image: FSImage) -> str:
"""Get the format to use to resize an image.
:return: the format to use to resize an image
"""
fmt = (
self.env["ir.config_parameter"]
.sudo()
.get_param("fs_image_thumbnail.resize_format")
)
return fmt or image.extension
@api.model
def _prepare_tumbnail(
self, image: FSImage, size_x: int, size_y: int, base_name: str
) -> dict:
"""Prepare the values to create a thumbnail image from the given image.
:param image: the image to resize
:param size_x: the new width of the image
:param size_y: the new height of the image
:param base_name: the base name of the thumbnail image (without extension)
:return: the values to create a thumbnail image
"""
fmt = self._get_resize_format(image)
extension = fmt
# Add a dot before the extension if needed and convert to lowercase.
extension = extension.lower()
if extension and not extension.startswith("."):
extension = "." + extension
new_image = FSImageValue(
value=self._resize(image, size_x, size_y, fmt),
name="%s_%s_%s%s" % (base_name, size_x, size_y, extension),
alt_text=image.alt_text,
)
return {
"image": new_image,
"size_x": size_x,
"size_y": size_y,
"base_name": base_name,
"attachment_id": image.attachment.id,
}
@api.model
def _slugify_base_name(self, base_name: str) -> str:
"""Slugify the given base name.
:param base_name: the base name to slugify
:return: the slugified base name
"""
return slugify(base_name) if base_name else base_name
@api.model
def _get_existing_thumbnail_domain(
self, *images: tuple[FSImageValue], base_name: str = ""
) -> list:
"""Get the domain to find existing thumbnail images from the given image.
:param images: a list of images we want to find existing thumbnails
:param base_name: the base name of the thumbnail image (without extension)
The base name must be set when multiple images are given.
:return: the domain to find existing thumbnail images
"""
attachment_ids = []
for image in images:
if image.attachment:
attachment_ids.append(image.attachment.id)
else:
raise UserError(
_(
"The image %(name)s must be attached to an attachment",
name=image.name,
)
)
base_name = self._get_slugified_base_name(*images, base_name=base_name)
return [
("attachment_id", "in", attachment_ids),
("base_name", "=", base_name),
]
@api.model
def get_thumbnails(
self, *images: tuple[FSImageValue], base_name: str = ""
) -> list["FsImageThumbnailMixin"]:
"""Get existing thumbnail images from the given image.
:param images: a list of images we want to find existing thumbnails
:param base_name: the base name of the thumbnail image (without extension)
The base name must be set when multiple images are given.
:return: a recordset of thumbnail images
"""
domain = self._get_existing_thumbnail_domain(*images, base_name=base_name)
return self.search(domain)
@api.model
def get_or_create_thumbnails(
self,
*images: tuple[FSImageValue],
sizes: list[tuple[int, int]],
base_name: str = ""
) -> OrderedDict[FSImageValue, list["FsImageThumbnailMixin"]]:
"""Get or create a thumbnail images from the given image.
:param images: the list of images we want to get or create thumbnails
:param sizes: the list of sizes to use to resize the image
(list of tuple (size_x, size_y))
:param base_name: the base name of the thumbnail image (without extension)
The base name must be set when multiple images are given.
:return: an ordered dictionary where the key is the original image and
the value is a recordset of thumbnail images. The order of the dict
is the order of the images passed to the method.
"""
base_name = self._get_slugified_base_name(*images, base_name=base_name)
thumbnails = self.get_thumbnails(*images, base_name=base_name)
thumbnails_by_attachment_id = thumbnails.partition("attachment_id")
ret = OrderedDict[FSImageValue, list["FsImageThumbnailMixin"]]()
for image in images:
thumbnails_by_size = {
(thumbnail.size_x, thumbnail.size_y): thumbnail
for thumbnail in thumbnails_by_attachment_id.get(image.attachment, [])
}
ids_to_return = []
for size_x, size_y in sizes:
thumbnail = thumbnails_by_size.get((size_x, size_y))
if not thumbnail:
values = self._prepare_tumbnail(image, size_x, size_y, base_name)
# no creation possible outside of this method -> sudo() is
# required since no access rights defined on create
thumbnail = self.sudo().create(values)
ids_to_return.append(thumbnail.id)
# return the thumbnails browsed in the same security context as the method
# caller
ret[image] = self.browse(ids_to_return)
return ret
@api.model
def _get_slugified_base_name(
self, *images: tuple[FSImageValue], base_name: str
) -> str:
"""Get the base name of the thumbnail image (without extension).
:param images: the list of images we want to get the base name
:return: the base name of the thumbnail image
"""
if not base_name:
if len(images) > 1:
raise UserError(
_("The base name must be set when multiple images are given")
)
base_name = images[0].name
return self._slugify_base_name(base_name)

View file

@ -0,0 +1,11 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models
class FsThumbnail(models.Model):
_name = "fs.thumbnail"
_inherit = "fs.image.thumbnail.mixin"
_description = "Image Thumbnail"

View file

@ -0,0 +1,16 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class IrAttachment(models.Model):
_inherit = "ir.attachment"
thumbnail_ids = fields.One2many(
comodel_name="fs.thumbnail",
inverse_name="attachment_id",
string="Thumbnails",
readonly=True,
)

View file

@ -0,0 +1,17 @@
In some specific cases you may need to generate and store thumbnails of images in Odoo.
This is the case for example when you want to provide image in specific sizes for a website
or a mobile application.
This module provides a generic way to generate thumbnails of images and store them in a
specific filesystem storage. Indeed, you could need to store the thumbnails in a different
storage than the original image (eg: store the thumbnails in a CDN) to make sure the
thumbnails are served quickly when requested by an external application and to
avoid to expose the original image storage.
This module uses the `fs_image <https://github.com/oca/storage/blob/16.0/fs_image/README.rst>`_
module to store the thumbnails in a filesystem storage.
The `shopinvader_product_image <https://github.com/shopinvader/odoo-shopinvader/
blob/16.0/shopinvader_product_image>`_ addon uses this module to generate and
store the thumbnails of the images of the products and categories to be accessible
by the website.

View file

@ -0,0 +1 @@
* Laurent Mignon <laurent.mignon@acsone.eu> (https://acsone.eu)

View file

@ -0,0 +1,3 @@
The development of this module has been financially supported by:
* `Alcyon Belux <https://www.alcyonbelux.be/>`_

View file

@ -0,0 +1,4 @@
This module extends the **fs_image** addon to support the creation and the storage of
thumbnails for images. This module is a **technical module** and is not
meant to be installed by end-users. It only provides a mixin to be used
by other modules and a model to store the thumbnails.

View file

@ -0,0 +1,11 @@
16.0.1.0.1 (2023-10-04)
~~~~~~~~~~~~~~~~~~~~~~~
**Bugfixes**
- The call to the method *get_or_create_thumbnails* on the *fs.image.thumbnail.mixin*
class returns now an ordered dictionary where the key is the original image and
the value is a recordset of thumbnail images. The order of the dict is the order
of the images passed to the method. This ensures that when you process the result
of the method you can be sure that the order of the images is the same as the
order of the images passed to the method. (`#282 <https://github.com/OCA/storage/issues/282>`_)

View file

@ -0,0 +1,57 @@
This addon provides a convenient way to get and create if not exists image
thumbnails. All the logic is implemented by the abstract model
`fs.image.thumbnail.mixin`. The main method is `get_or_create_thumbnails` which
accepts a *FSImageValue* instance, a list of thumbnail sizes and a base name.
When the method is called, it will check if the thumbnail exists for the given
sizes and base name. If not, it will create it.
The `fs.thumbnail` model provided by this addon is a concrete implementation of
the abstract model `fs.image.thumbnail.mixin`. The motivation to implement all the
logic in an abstract model is to allow developers to create their own thumbnail
models. This could be useful if you want to store the thumbnails in a different
storage since you can specify the storage to use by model on the `fs.storage`
form view.
Creating / retrieving thumbnails is as simple as:
.. code-block:: python
from odoo.addons.fs_image.fields import FSImageValue
# create an attachment with a image file
attachment = self.env['ir.attachment'].create({
'name': 'test',
'datas': base64.b64encode(open('test.png', 'rb').read()),
'datas_fname': 'test.png',
})
# create a FSImageValue instance for the attachment
image_value = FSImageValue(attachment)
# get or create the thumbnails
thumbnails = self.env['fs.thumbnail'].get_or_create_thumbnails(
image_value, [(800,600), (400, 200)], 'my base name')
If you've a model with a *FSImage* field, the call to `get_or_create_thumbnails`
is even simpler:
.. code-block:: python
from odoo import models
from odoo.addons.fs_image.fields import FSImage
class MyModel(models.Model):
_name = 'my.model'
image = FSImage('Image')
my_record = cls.env['my.model'].create({
'image': open('test.png', 'rb'),
})
# get or create the thumbnails
thumbnails = record.image.get_or_create_thumbnails(my_record.image,
[(800,600), (400, 200)], 'my base name')

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2023 ACSONE SA/NV
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record model="ir.model.access" id="fs_thumbnail_access_read">
<field name="name">fs.thumbnail access read</field>
<field name="model_id" ref="model_fs_thumbnail" />
<field name="group_id" ref="base.group_user" />
<field name="perm_read" eval="1" />
<field name="perm_create" eval="0" />
<field name="perm_write" eval="0" />
<field name="perm_unlink" eval="0" />
</record>
</odoo>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -0,0 +1,528 @@
<!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>Fs Image Thumbnail</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
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: grey; } /* 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 {
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" id="fs-image-thumbnail">
<h1 class="title">Fs Image Thumbnail</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:ae84af058fd490c7c8916156dc7db31813b6d5f7535e722740b152d6955e0d57
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/storage/tree/16.0/fs_image_thumbnail"><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_image_thumbnail"><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 module extends the <strong>fs_image</strong> addon to support the creation and the storage of
thumbnails for images. This module is a <strong>technical module</strong> and is not
meant to be installed by end-users. It only provides a mixin to be used
by other modules and a model to store the thumbnails.</p>
<div class="admonition important">
<p class="first admonition-title">Important</p>
<p class="last">This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
<a class="reference external" href="https://odoo-community.org/page/development-status">More details on development status</a></p>
</div>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#use-cases-context" id="toc-entry-1">Use Cases / Context</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
<li><a class="reference internal" href="#changelog" id="toc-entry-3">Changelog</a><ul>
<li><a class="reference internal" href="#section-1" id="toc-entry-4">16.0.1.0.1 (2023-10-04)</a></li>
</ul>
</li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-5">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-6">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-7">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-8">Contributors</a></li>
<li><a class="reference internal" href="#other-credits" id="toc-entry-9">Other credits</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-10">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="use-cases-context">
<h1><a class="toc-backref" href="#toc-entry-1">Use Cases / Context</a></h1>
<p>In some specific cases you may need to generate and store thumbnails of images in Odoo.
This is the case for example when you want to provide image in specific sizes for a website
or a mobile application.</p>
<p>This module provides a generic way to generate thumbnails of images and store them in a
specific filesystem storage. Indeed, you could need to store the thumbnails in a different
storage than the original image (eg: store the thumbnails in a CDN) to make sure the
thumbnails are served quickly when requested by an external application and to
avoid to expose the original image storage.</p>
<p>This module uses the <a class="reference external" href="https://github.com/oca/storage/blob/16.0/fs_image/README.rst">fs_image</a>
module to store the thumbnails in a filesystem storage.</p>
<p>The <a class="reference external" href="https://github.com/shopinvader/odoo-shopinvader/blob/16.0/shopinvader_product_image">shopinvader_product_image</a> addon uses this module to generate and
store the thumbnails of the images of the products and categories to be accessible
by the website.</p>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<p>This addon provides a convenient way to get and create if not exists image
thumbnails. All the logic is implemented by the abstract model
<cite>fs.image.thumbnail.mixin</cite>. The main method is <cite>get_or_create_thumbnails</cite> which
accepts a <em>FSImageValue</em> instance, a list of thumbnail sizes and a base name.</p>
<p>When the method is called, it will check if the thumbnail exists for the given
sizes and base name. If not, it will create it.</p>
<p>The <cite>fs.thumbnail</cite> model provided by this addon is a concrete implementation of
the abstract model <cite>fs.image.thumbnail.mixin</cite>. The motivation to implement all the
logic in an abstract model is to allow developers to create their own thumbnail
models. This could be useful if you want to store the thumbnails in a different
storage since you can specify the storage to use by model on the <cite>fs.storage</cite>
form view.</p>
<p>Creating / retrieving thumbnails is as simple as:</p>
<pre class="code python literal-block">
<span class="kn">from</span> <span class="nn">odoo.addons.fs_image.fields</span> <span class="kn">import</span> <span class="n">FSImageValue</span><span class="w">
</span><span class="c1"># create an attachment with a image file</span><span class="w">
</span><span class="n">attachment</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="p">[</span><span class="s1">'ir.attachment'</span><span class="p">]</span><span class="o">.</span><span class="n">create</span><span class="p">({</span><span class="w">
</span> <span class="s1">'name'</span><span class="p">:</span> <span class="s1">'test'</span><span class="p">,</span><span class="w">
</span> <span class="s1">'datas'</span><span class="p">:</span> <span class="n">base64</span><span class="o">.</span><span class="n">b64encode</span><span class="p">(</span><span class="nb">open</span><span class="p">(</span><span class="s1">'test.png'</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">)</span><span class="o">.</span><span class="n">read</span><span class="p">()),</span><span class="w">
</span> <span class="s1">'datas_fname'</span><span class="p">:</span> <span class="s1">'test.png'</span><span class="p">,</span><span class="w">
</span><span class="p">})</span><span class="w">
</span><span class="c1"># create a FSImageValue instance for the attachment</span><span class="w">
</span><span class="n">image_value</span> <span class="o">=</span> <span class="n">FSImageValue</span><span class="p">(</span><span class="n">attachment</span><span class="p">)</span><span class="w">
</span><span class="c1"># get or create the thumbnails</span><span class="w">
</span><span class="n">thumbnails</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="p">[</span><span class="s1">'fs.thumbnail'</span><span class="p">]</span><span class="o">.</span><span class="n">get_or_create_thumbnails</span><span class="p">(</span><span class="w">
</span> <span class="n">image_value</span><span class="p">,</span> <span class="p">[(</span><span class="mi">800</span><span class="p">,</span><span class="mi">600</span><span class="p">),</span> <span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="mi">200</span><span class="p">)],</span> <span class="s1">'my base name'</span><span class="p">)</span>
</pre>
<p>If youve a model with a <em>FSImage</em> field, the call to <cite>get_or_create_thumbnails</cite>
is even simpler:</p>
<pre class="code python literal-block">
<span class="kn">from</span> <span class="nn">odoo</span> <span class="kn">import</span> <span class="n">models</span><span class="w">
</span><span class="kn">from</span> <span class="nn">odoo.addons.fs_image.fields</span> <span class="kn">import</span> <span class="n">FSImage</span><span class="w">
</span><span class="k">class</span> <span class="nc">MyModel</span><span class="p">(</span><span class="n">models</span><span class="o">.</span><span class="n">Model</span><span class="p">):</span><span class="w">
</span> <span class="n">_name</span> <span class="o">=</span> <span class="s1">'my.model'</span><span class="w">
</span> <span class="n">image</span> <span class="o">=</span> <span class="n">FSImage</span><span class="p">(</span><span class="s1">'Image'</span><span class="p">)</span><span class="w">
</span><span class="n">my_record</span> <span class="o">=</span> <span class="bp">cls</span><span class="o">.</span><span class="n">env</span><span class="p">[</span><span class="s1">'my.model'</span><span class="p">]</span><span class="o">.</span><span class="n">create</span><span class="p">({</span><span class="w">
</span> <span class="s1">'image'</span><span class="p">:</span> <span class="nb">open</span><span class="p">(</span><span class="s1">'test.png'</span><span class="p">,</span> <span class="s1">'rb'</span><span class="p">),</span><span class="w">
</span><span class="p">})</span><span class="w">
</span><span class="c1"># get or create the thumbnails</span><span class="w">
</span><span class="n">thumbnails</span> <span class="o">=</span> <span class="n">record</span><span class="o">.</span><span class="n">image</span><span class="o">.</span><span class="n">get_or_create_thumbnails</span><span class="p">(</span><span class="n">my_record</span><span class="o">.</span><span class="n">image</span><span class="p">,</span><span class="w">
</span> <span class="p">[(</span><span class="mi">800</span><span class="p">,</span><span class="mi">600</span><span class="p">),</span> <span class="p">(</span><span class="mi">400</span><span class="p">,</span> <span class="mi">200</span><span class="p">)],</span> <span class="s1">'my base name'</span><span class="p">)</span>
</pre>
</div>
<div class="section" id="changelog">
<h1><a class="toc-backref" href="#toc-entry-3">Changelog</a></h1>
<div class="section" id="section-1">
<h2><a class="toc-backref" href="#toc-entry-4">16.0.1.0.1 (2023-10-04)</a></h2>
<p><strong>Bugfixes</strong></p>
<ul class="simple">
<li>The call to the method <em>get_or_create_thumbnails</em> on the <em>fs.image.thumbnail.mixin</em>
class returns now an ordered dictionary where the key is the original image and
the value is a recordset of thumbnail images. The order of the dict is the order
of the images passed to the method. This ensures that when you process the result
of the method you can be sure that the order of the images is the same as the
order of the images passed to the method. (<a class="reference external" href="https://github.com/OCA/storage/issues/282">#282</a>)</li>
</ul>
</div>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-5">Bug Tracker</a></h1>
<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_image_thumbnail%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">
<h1><a class="toc-backref" href="#toc-entry-6">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-7">Authors</a></h2>
<ul class="simple">
<li>ACSONE SA/NV</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-8">Contributors</a></h2>
<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; (<a class="reference external" href="https://acsone.eu">https://acsone.eu</a>)</li>
</ul>
</div>
<div class="section" id="other-credits">
<h2><a class="toc-backref" href="#toc-entry-9">Other credits</a></h2>
<p>The development of this module has been financially supported by:</p>
<ul class="simple">
<li><a class="reference external" href="https://www.alcyonbelux.be/">Alcyon Belux</a></li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-10">Maintainers</a></h2>
<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>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external image-reference" href="https://github.com/lmignon"><img alt="lmignon" src="https://github.com/lmignon.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/storage/tree/16.0/fs_image_thumbnail">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>
</body>
</html>

View file

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

View file

@ -0,0 +1,81 @@
# Copyright 2023 ACSONE SA/NV
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import base64
import io
from PIL import Image
from odoo.tests.common import TransactionCase
from odoo.addons.fs_image.fields import FSImageValue
class TestFsImageThumbnail(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.white_image = cls._create_image(32, 32, color="#FFFFFF")
cls.image_attachment = cls.env["ir.attachment"].create(
{
"name": "Test Image",
"datas": base64.b64encode(cls.white_image),
"mimetype": "image/png",
}
)
cls.fs_image_value = FSImageValue(attachment=cls.image_attachment)
cls.fs_thumbnail_model = cls.env["fs.thumbnail"]
def setUp(self):
super().setUp()
self.temp_dir = self.env["fs.storage"].create(
{
"name": "Temp FS Storage",
"protocol": "memory",
"code": "mem_dir",
"directory_path": "/tmp/",
"model_xmlids": "fs_image_thumbnail.model_fs_thumbnail",
}
)
@classmethod
def _create_image(cls, width, height, color="#4169E1", img_format="PNG"):
f = io.BytesIO()
Image.new("RGB", (width, height), color).save(f, img_format)
f.seek(0)
return f.read()
def assert_image_size(self, value: bytes, width, height):
self.assertEqual(Image.open(io.BytesIO(value)).size, (width, height))
def test_create_multi(self):
self.assertFalse(self.image_attachment.thumbnail_ids)
thumbnails = self.fs_thumbnail_model.get_or_create_thumbnails(
self.fs_image_value, sizes=[(16, 16), (8, 8)], base_name="My super test"
)[self.fs_image_value]
self.assertEqual(len(thumbnails), 2)
self.assertEqual(thumbnails[0].name, "my-super-test_16_16.png")
self.assert_image_size(thumbnails[0].image.getvalue(), 16, 16)
self.assertEqual(thumbnails[1].name, "my-super-test_8_8.png")
self.assert_image_size(thumbnails[1].image.getvalue(), 8, 8)
self.assertEqual(self.image_attachment.thumbnail_ids, thumbnails)
# if we call the method again for the same size, we should get the same thumbnail
new_thumbnails = self.fs_thumbnail_model.get_or_create_thumbnails(
self.fs_image_value, sizes=[(16, 16), (8, 8)], base_name="My super test"
)[self.fs_image_value]
self.assertEqual(new_thumbnails, thumbnails)
def test_create_with_specific_format(self):
self.env["ir.config_parameter"].set_param(
"fs_image_thumbnail.resize_format", "JPEG"
)
thumbnail = self.fs_thumbnail_model.get_or_create_thumbnails(
self.fs_image_value, sizes=[(8, 8)], base_name="My super test"
)[self.fs_image_value]
self.assertEqual(thumbnail[0].name, "my-super-test_8_8.jpeg")
self.assertEqual(thumbnail[0].mimetype, "image/jpeg")
self.assert_image_size(thumbnail[0].image.getvalue(), 8, 8)

View file

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2023 ACSONE SA/NV
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record model="ir.ui.view" id="fs_image_thumbnail_mixin_form_view">
<field name="name">fs.image.thumbnail.mixin.form (in fs_image_thumbnail)</field>
<field name="model">fs.image.thumbnail.mixin</field>
<field name="arch" type="xml">
<form>
<header>
</header>
<sheet>
<label for="name" />
<h1>
<field name="name" />
</h1>
<group>
<group>
<field name="image" />
</group>
<group>
<field name="base_name" />
<field name="size_x" />
<field name="size_y" />
<field name="mimetype" />
</group>
</group>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="fs_image_thumbnail_mixin_search_view">
<field
name="name"
>fs.image.thumbnail.mixin.search (in fs_image_thumbnail)</field>
<field name="model">fs.image.thumbnail.mixin</field>
<field name="arch" type="xml">
<search>
<field
name="name"
filter_domain="[('name','ilike',self)]"
string="Name"
/><field
name="base_name"
filter_domain="[('base_name','ilike',self)]"
string="Base Name"
/>
<separator />
<field name="mimetype" />
<group expand="0" string="Group By">
<filter
string="MimeType"
name="mimetype"
domain="[]"
context="{'group_by':'mimetype'}"
/>
<filter
string="Attachment"
name="attachment"
domain="[]"
context="{'group_by':'attachment_id'}"
/>
</group>
</search>
</field>
</record>
<record model="ir.ui.view" id="fs_image_thumbnail_mixin_tree_view">
<field name="name">fs.image.thumbnail.mixin.tree (in fs_image_thumbnail)</field>
<field name="model">fs.image.thumbnail.mixin</field>
<field name="arch" type="xml">
<tree>
<field name="name" />
<field name="base_name" />
<field name="size_x" />
<field name="size_y" />
<field name="mimetype" />
</tree>
</field>
</record>
</odoo>

View file

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2023 ACSONE SA/NV
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record model="ir.ui.view" id="fs_thumbnail_form_view">
<field name="name">fs.thumbnail.form</field>
<field name="model">fs.thumbnail</field>
<field name="inherit_id" ref="fs_image_thumbnail_mixin_form_view" />
<field name="mode">primary</field>
<field name="arch" type="xml">
<!-- add a hidden field to be able to extend the base view
defined for the mixin
-->
<field name="base_name" position="after">
<field name="id" invisible="1" />
</field>
</field>
</record>
<record model="ir.ui.view" id="fs_thumbnail_search_view">
<field name="name">fs.thumbnail.search</field>
<field name="model">fs.thumbnail</field>
<field name="inherit_id" ref="fs_image_thumbnail_mixin_search_view" />
<field name="mode">primary</field>
<field name="arch" type="xml">
<field name="mimetype" position="before">
<field name="create_uid" string="Created by" />
</field>
</field>
</record>
<record model="ir.ui.view" id="fs_thumbnail_tree_view">
<field name="name">fs.thumbnail.tree</field>
<field name="model">fs.thumbnail</field>
<field name="inherit_id" ref="fs_image_thumbnail_mixin_tree_view" />
<field name="mode">primary</field>
<field name="arch" type="xml">
<!-- add a hidden field to be able to extend the base view
defined for the mixin
-->
<field name="base_name" position="after">
<field name="id" invisible="1" />
</field>
</field>
</record>
<record model="ir.actions.act_window" id="fs_thumbnail_act_window">
<field name="name">Fs Thumbnail</field>
<field name="res_model">fs.thumbnail</field>
<field name="view_mode">tree,form</field>
<field name="domain">[]</field>
<field name="context">{}</field>
</record>
<record model="ir.ui.menu" id="fs_thumbnail_menu">
<field name="name">Fs Image Thumbnails</field>
<field name="parent_id" ref="fs_storage.menu_storage" />
<field name="action" ref="fs_thumbnail_act_window" />
</record>
</odoo>

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2023 ACSONE SA/NV
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record model="ir.ui.view" id="ir_attachment_form_view">
<field name="name">ir.attachment.form (in fs_image_thumbnail)</field>
<field name="model">ir.attachment</field>
<field name="inherit_id" ref="base.view_attachment_form" />
<field name="arch" type="xml">
<group name="description_group" position="after">
<group name="thumbnails" string="Thumbnails" colspan="4">
<field name="thumbnail_ids" nolabel="1" colspan="2" />
</group>
</group>
</field>
</record>
</odoo>

View file

@ -0,0 +1,44 @@
[project]
name = "odoo-bringout-oca-storage-fs_image_thumbnail"
version = "16.0.0"
description = "Fs Image Thumbnail -
Generate and store thumbnail for images"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-storage-fs_image>=16.0.0",
"odoo-bringout-oca-storage-base_partition>=16.0.0",
"requests>=2.25.1"
]
readme = "README.md"
requires-python = ">= 3.11"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]
[project.urls]
homepage = "https://github.com/bringout/0"
repository = "https://github.com/bringout/0"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["fs_image_thumbnail"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]