mirror of https://github.com/OCA/web.git
160 lines
6.0 KiB
Python
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
|
|
)
|