web/web_pwa_oca/models/res_config_settings.py

160 lines
6.0 KiB
Python

# Copyright 2020 Tecnativa - João Marques
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
import base64
import io
import sys
from PIL import Image
from odoo import _, api, exceptions, fields, models
from odoo.tools.mimetypes import guess_mimetype
class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
_pwa_icon_url_base = "/web_pwa_oca/icon"
pwa_name = fields.Char(
"Progressive Web App Name", help="Name of the Progressive Web Application"
)
pwa_short_name = fields.Char(
"Progressive Web App Short Name",
help="Short Name of the Progressive Web Application",
)
pwa_icon = fields.Binary("Icon", readonly=False)
pwa_background_color = fields.Char("Background Color")
pwa_theme_color = fields.Char("Theme Color")
@api.model
def get_values(self):
config_parameter_obj_sudo = self.env["ir.config_parameter"].sudo()
res = super(ResConfigSettings, self).get_values()
res["pwa_name"] = config_parameter_obj_sudo.get_param(
"pwa.manifest.name", default="Odoo PWA"
)
res["pwa_short_name"] = config_parameter_obj_sudo.get_param(
"pwa.manifest.short_name", default="Odoo"
)
pwa_icon_ir_attachment = (
self.env["ir.attachment"]
.sudo()
.search([("url", "like", self._pwa_icon_url_base + ".")])
)
res["pwa_icon"] = (
pwa_icon_ir_attachment.datas if pwa_icon_ir_attachment else False
)
res["pwa_background_color"] = config_parameter_obj_sudo.get_param(
"pwa.manifest.background_color", default="#2E69B5"
)
res["pwa_theme_color"] = config_parameter_obj_sudo.get_param(
"pwa.manifest.theme_color", default="#2E69B5"
)
return res
def _unpack_icon(self, icon):
# Wrap decoded_icon in BytesIO object
decoded_icon = base64.b64decode(icon)
icon_bytes = io.BytesIO(decoded_icon)
return Image.open(icon_bytes)
def _write_icon_to_attachment(self, extension, mimetype, size=None):
url = self._pwa_icon_url_base + extension
icon = self.pwa_icon
# Resize image
if size:
image = self._unpack_icon(icon)
resized_image = image.resize(size)
icon_bytes_output = io.BytesIO()
resized_image.save(icon_bytes_output, format=extension.lstrip(".").upper())
icon = base64.b64encode(icon_bytes_output.getvalue())
url = "{}{}x{}{}".format(
self._pwa_icon_url_base,
str(size[0]),
str(size[1]),
extension,
)
# Retreive existing attachment
existing_attachment = (
self.env["ir.attachment"].sudo().search([("url", "like", url)])
)
# Write values to ir_attachment
values = {
"datas": icon,
"db_datas": icon,
"url": url,
"name": url,
"type": "binary",
"mimetype": mimetype,
}
# Rewrite if exists, else create
if existing_attachment:
existing_attachment.sudo().write(values)
else:
self.env["ir.attachment"].sudo().create(values)
@api.model
def set_values(self):
config_parameter_obj_sudo = self.env["ir.config_parameter"].sudo()
res = super(ResConfigSettings, self).set_values()
config_parameter_obj_sudo.set_param("pwa.manifest.name", self.pwa_name)
config_parameter_obj_sudo.set_param(
"pwa.manifest.short_name", self.pwa_short_name
)
config_parameter_obj_sudo.set_param(
"pwa.manifest.background_color", self.pwa_background_color
)
config_parameter_obj_sudo.set_param(
"pwa.manifest.theme_color", self.pwa_theme_color
)
# Retrieve previous value for pwa_icon from ir_attachment
pwa_icon_ir_attachments = (
self.env["ir.attachment"]
.sudo()
.search([("url", "like", self._pwa_icon_url_base)])
)
# Delete or ignore if no icon provided
if not self.pwa_icon:
if pwa_icon_ir_attachments:
pwa_icon_ir_attachments.unlink()
return res
# Fail if icon provided is larger than 2mb
if sys.getsizeof(self.pwa_icon) > 2196608:
raise exceptions.UserError(
_("You can't upload a file with more than 2 MB.")
)
# Confirm if the pwa_icon binary content is an SVG or PNG
# and process accordingly
decoded_pwa_icon = base64.b64decode(self.pwa_icon)
# Full mimetype detection
pwa_icon_mimetype = guess_mimetype(decoded_pwa_icon)
pwa_icon_extension = "." + pwa_icon_mimetype.split("/")[-1].split("+")[0]
if not pwa_icon_mimetype.startswith(
"image/svg"
) and not pwa_icon_mimetype.startswith("image/png"):
raise exceptions.UserError(
_("You can only upload SVG or PNG files. Found: %s.")
% pwa_icon_mimetype
)
# Delete all previous records if we are writting new ones
if pwa_icon_ir_attachments:
pwa_icon_ir_attachments.unlink()
self._write_icon_to_attachment(pwa_icon_extension, pwa_icon_mimetype)
# write multiple sizes if not SVG
if pwa_icon_extension != ".svg":
# Fail if provided PNG is smaller than 512x512
if self._unpack_icon(self.pwa_icon).size < (512, 512):
raise exceptions.UserError(
_("You can only upload PNG files bigger than 512x512")
)
for size in [
(128, 128),
(144, 144),
(152, 152),
(192, 192),
(256, 256),
(512, 512),
]:
self._write_icon_to_attachment(
pwa_icon_extension, pwa_icon_mimetype, size=size
)