3
0
Fork 0

[12.0][IMP] web_pwa_oca: configurable name and icon

Add ability to configure name, short name and icon for PWA in Odoo
General Settings.
The different icon sizes also get replaced by a single SVG

TT25979
12.0
João Marques 2020-10-07 11:47:17 +01:00 committed by Alexandre D. Díaz
parent a74fbb34c0
commit 7473a485e9
15 changed files with 490 additions and 124 deletions

View File

@ -63,16 +63,25 @@ In case you previously installed `web_pwa`, run the following steps with `odoo s
Configuration
=============
The following system parameters con be set to customize the appearance of the application
This module allows you to set the following parameters under settings to customize the appearance of the application
* pwa.manifest.name (defaults to "Odoo PWA")
* pwa.manifest.short_name (defaults to "Odoo PWA")
* pwa.manifest.icon128x128 (defaults to "/web_pwa_oca/static/img/icons/icon-128x128.png")
* pwa.manifest.icon144x144 (defaults to "/web_pwa_oca/static/img/icons/icon-144x144.png")
* pwa.manifest.icon152x152 (defaults to "/web_pwa_oca/static/img/icons/icon-152x152.png")
* pwa.manifest.icon192x192 (defaults to "/web_pwa_oca/static/img/icons/icon-192x192.png")
* pwa.manifest.icon256x256 (defaults to "/web_pwa_oca/static/img/icons/icon-256x256.png")
* pwa.manifest.icon512x512 (defaults to "/web_pwa_oca/static/img/icons/icon-512x512.png")
* PWA Name (defaults to "Odoo PWA")
* PWA Short Name (defaults to "Odoo PWA")
* PWA Icon (**SVG**) (defaults to "/web_pwa_oca/static/img/icons/odoo-logo.svg")
To configure your PWA:
#. Go to **Settings > General Settings > Progressive Web App**.
#. Set the parameters (*Note:* Icon **must be a SVG file**)
#. **Save**
Usage
=====
To use your PWA:
#. Open the Odoo web app using a supported browser (like Chrome/Chromium)
#. Install the PWA
Known issues / Roadmap
======================
@ -81,11 +90,11 @@ Known issues / Roadmap
* Integrate `Notification API <https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification>`_
* Integrate `Web Share API <https://web.dev/web-share/>`_
* Create ``portal_pwa`` module, intended to be used by front-end users (customers, suppliers...)
* Current ``John Resig's inheritance`` doesn't support ``async`` functions. So we need use the following workaround:
* Current ``John Resig's inheritance`` implementation doesn't support ``async`` functions because ``this._super`` can't be called inside a promise. So we need use the following workaround:
- Natural 'async/await' example (This breaks "_super" call)
.. code-block:: javascript
const MyClass = OdooClass.extend({
var MyClass = OdooClass.extend({
myFunc: async function() {
const mydata = await ...do await stuff...
return mydata;
@ -95,7 +104,7 @@ Known issues / Roadmap
- Same code with the workaround:
.. code-block:: javascript
const MyClass = OdooClass.extend({
var MyClass = OdooClass.extend({
myFunc: function() {
return new Promise(async (resolve, reject) => {
const mydata = await ...do await stuff...
@ -103,6 +112,7 @@ Known issues / Roadmap
});
}
});
* Fix issue when trying to run in localhost with several databases. The browser doesn't send the cookie and web manifest returns 404.
Bug Tracker
===========
@ -133,6 +143,7 @@ Contributors
* `Tecnativa <https://tecnativa.com>`_:
* Alexandre D. Díaz
* João Marques
Maintainers
~~~~~~~~~~~

View File

@ -1 +1,2 @@
from . import controllers
from . import models

View File

@ -1,5 +1,6 @@
# Copyright 2020 Lorenzo Battistini @ TAKOBI
# Copyright 2020 Tecnativa - Alexandre D. Díaz
# Copyright 2020 Tecnativa - João Marques
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
{
@ -21,6 +22,7 @@
"data": [
"templates/assets.xml",
"templates/service_worker.xml",
"views/res_config_settings_views.xml",
],
'qweb': [
'static/src/xml/pwa_install.xml',

View File

@ -1,32 +1,33 @@
# Copyright 2020 Lorenzo Battistini @ TAKOBI
# Copyright 2020 Tecnativa - Alexandre D. Díaz
# Copyright 2020 Tecnativa - João Marques
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
import json
from odoo.http import request, Controller, route
class PWA(Controller):
def _get_pwa_scripts(self):
""" Scripts to be imported in the service worker (Order is important) """
return [
'/web/static/lib/underscore/underscore.js',
'/web_pwa_oca/static/src/js/worker/libs/class.js',
'/web_pwa_oca/static/src/js/worker/base/tools.js',
'/web_pwa_oca/static/src/js/worker/base/cache_manager.js',
'/web_pwa_oca/static/src/js/worker/pwa.js',
"/web/static/lib/underscore/underscore.js",
"/web_pwa_oca/static/src/js/worker/libs/class.js",
"/web_pwa_oca/static/src/js/worker/base/tools.js",
"/web_pwa_oca/static/src/js/worker/base/cache_manager.js",
"/web_pwa_oca/static/src/js/worker/pwa.js",
]
def _get_asset_urls(self, asset_xml_id):
"""Get all url's that have 'asset_xml_id'"""
qweb_sudo = request.env['ir.qweb'].sudo()
"""Get all urls that have 'asset_xml_id'"""
qweb_sudo = request.env["ir.qweb"].sudo()
assets = qweb_sudo._get_asset_nodes(asset_xml_id, {}, True, True)
urls = []
for asset in assets:
if asset[0] == 'link':
urls.append(asset[1]['href'])
if asset[0] == 'script':
urls.append(asset[1]['src'])
if asset[0] == "link":
urls.append(asset[1]["href"])
if asset[0] == "script":
urls.append(asset[1]["src"])
return urls
def _get_pwa_params(self):
@ -36,86 +37,105 @@ class PWA(Controller):
urls.extend(self._get_asset_urls("web.assets_backend"))
version_list = []
for url in urls:
version_list.append(url.split('/')[3])
cache_version = '-'.join(version_list)
return [
cache_version,
urls
]
version_list.append(url.split("/")[3])
cache_version = "-".join(version_list)
return [cache_version, urls]
@route('/service-worker.js', type='http', auth="public")
@route("/service-worker.js", type="http", auth="public")
def render_service_worker(self):
"""Route to register the service worker in the 'main' scope ('/')"""
return request.render('web_pwa_oca.service_worker', {
'pwa_scripts': self._get_pwa_scripts(),
'pwa_params': self._get_pwa_params(),
}, headers=[('Content-Type', 'text/javascript;charset=utf-8')])
return request.render(
"web_pwa_oca.service_worker",
{
"pwa_scripts": self._get_pwa_scripts(),
"pwa_params": self._get_pwa_params(),
},
headers=[("Content-Type", "text/javascript;charset=utf-8")],
)
def _get_pwa_manifest_icons(self, pwa_icon):
icons = []
if not pwa_icon:
for size in [
(128, 128),
(144, 144),
(152, 152),
(192, 192),
(256, 256),
(512, 512),
]:
icons.append(
{
"src": "/web_pwa_oca/static/img/icons/icon-%sx%s.png"
% (str(size[0]), str(size[1])),
"sizes": "%sx%s" % (str(size[0]), str(size[1])),
"type": "image/png",
}
)
elif not pwa_icon.mimetype.startswith("image/svg"):
all_icons = (
request.env["ir.attachment"]
.sudo()
.search(
[
("url", "like", "/web_pwa_oca/icon"),
(
"url",
"not like",
"/web_pwa_oca/icon.",
), # Get only resized icons
]
)
)
for icon in all_icons:
icon_size_name = icon.url.split("/")[-1].lstrip("icon").split(".")[0]
icons.append(
{
"src": icon.url,
"sizes": icon_size_name,
"type": icon.mimetype,
}
)
else:
icons = [
{
"src": pwa_icon.url,
"sizes": "128x128 144x144 152x152 192x192 256x256 512x512",
"type": pwa_icon.mimetype,
}
]
return icons
def _get_pwa_manifest(self):
"""Webapp manifest"""
config_param_sudo = request.env['ir.config_parameter'].sudo()
config_param_sudo = request.env["ir.config_parameter"].sudo()
pwa_name = config_param_sudo.get_param("pwa.manifest.name", "Odoo PWA")
pwa_short_name = config_param_sudo.get_param(
"pwa.manifest.short_name", "Odoo PWA")
icon128x128 = config_param_sudo.get_param(
"pwa.manifest.icon128x128",
"/web_pwa_oca/static/img/icons/icon-128x128.png")
icon144x144 = config_param_sudo.get_param(
"pwa.manifest.icon144x144",
"/web_pwa_oca/static/img/icons/icon-144x144.png")
icon152x152 = config_param_sudo.get_param(
"pwa.manifest.icon152x152",
"/web_pwa_oca/static/img/icons/icon-152x152.png")
icon192x192 = config_param_sudo.get_param(
"pwa.manifest.icon192x192",
"/web_pwa_oca/static/img/icons/icon-192x192.png")
icon256x256 = config_param_sudo.get_param(
"pwa.manifest.icon256x256",
"/web_pwa_oca/static/img/icons/icon-256x256.png")
icon512x512 = config_param_sudo.get_param(
"pwa.manifest.icon512x512",
"/web_pwa_oca/static/img/icons/icon-512x512.png")
"pwa.manifest.short_name", "Odoo PWA"
)
pwa_icon = (
request.env["ir.attachment"]
.sudo()
.search([("url", "like", "/web_pwa_oca/icon.")])
)
background_color = config_param_sudo.get_param(
"pwa.manifest.background_color", "#2E69B5")
theme_color = config_param_sudo.get_param(
"pwa.manifest.theme_color", "#2E69B5")
"pwa.manifest.background_color", "#2E69B5"
)
theme_color = config_param_sudo.get_param("pwa.manifest.theme_color", "#2E69B5")
return {
"name": pwa_name,
"short_name": pwa_short_name,
"icons": [{
"src": icon128x128,
"sizes": "128x128",
"type": "image/png"
}, {
"src": icon144x144,
"sizes": "144x144",
"type": "image/png"
}, {
"src": icon152x152,
"sizes": "152x152",
"type": "image/png"
}, {
"src": icon192x192,
"sizes": "192x192",
"type": "image/png"
}, {
"src": icon256x256,
"sizes": "256x256",
"type": "image/png"
}, {
"src": icon512x512,
"sizes": "512x512",
"type": "image/png"
}],
"start_url": "/web",
"display": "standalone",
"background_color": background_color,
"theme_color": theme_color
"name": pwa_name,
"short_name": pwa_short_name,
"icons": self._get_pwa_manifest_icons(pwa_icon),
"start_url": "/web",
"display": "standalone",
"background_color": background_color,
"theme_color": theme_color,
}
@route('/web_pwa_oca/manifest.webmanifest', type='http', auth="public")
@route("/web_pwa_oca/manifest.webmanifest", type="http", auth="public")
def pwa_manifest(self):
"""Returns the manifest used to install the page as app"""
return request.make_response(
json.dumps(self._get_pwa_manifest()),
headers=[('Content-Type', 'text/javascript;charset=utf-8')])
headers=[("Content-Type", "text/javascript;charset=utf-8")],
)

View File

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

View File

@ -0,0 +1,146 @@
# Copyright 2020 Tecnativa - João Marques
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
import sys
import base64
import io
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_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_icon_url_base = "/web_pwa_oca/icon"
@api.model
def get_values(self):
res = super(ResConfigSettings, self).get_values()
res["pwa_name"] = (
self.env["ir.config_parameter"]
.sudo()
.get_param("pwa.manifest.name", default="")
)
res["pwa_short_name"] = (
self.env["ir.config_parameter"]
.sudo()
.get_param("pwa.manifest.short_name", default="")
)
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
)
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 = "%s%sx%s%s" % (
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):
res = super(ResConfigSettings, self).set_values()
self.env["ir.config_parameter"].sudo().set_param(
"pwa.manifest.name", self.pwa_name
)
self.env["ir.config_parameter"].sudo().set_param(
"pwa.manifest.short_name", self.pwa_short_name
)
# 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"))
# 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
)

View File

@ -1,10 +1,11 @@
The following system parameters con be set to customize the appearance of the application
This module allows you to set the following parameters under settings to customize the appearance of the application
* pwa.manifest.name (defaults to "Odoo PWA")
* pwa.manifest.short_name (defaults to "Odoo PWA")
* pwa.manifest.icon128x128 (defaults to "/web_pwa_oca/static/img/icons/icon-128x128.png")
* pwa.manifest.icon144x144 (defaults to "/web_pwa_oca/static/img/icons/icon-144x144.png")
* pwa.manifest.icon152x152 (defaults to "/web_pwa_oca/static/img/icons/icon-152x152.png")
* pwa.manifest.icon192x192 (defaults to "/web_pwa_oca/static/img/icons/icon-192x192.png")
* pwa.manifest.icon256x256 (defaults to "/web_pwa_oca/static/img/icons/icon-256x256.png")
* pwa.manifest.icon512x512 (defaults to "/web_pwa_oca/static/img/icons/icon-512x512.png")
* PWA Name (defaults to "Odoo PWA")
* PWA Short Name (defaults to "Odoo PWA")
* PWA Icon (**SVG**) (defaults to "/web_pwa_oca/static/img/icons/odoo-logo.svg")
To configure your PWA:
#. Go to **Settings > General Settings > Progressive Web App**.
#. Set the parameters (*Note:* Icon **must be a SVG file**)
#. **Save**

View File

@ -5,3 +5,4 @@
* `Tecnativa <https://tecnativa.com>`_:
* Alexandre D. Díaz
* João Marques

View File

@ -24,3 +24,4 @@
});
}
});
* Fix issue when trying to run in localhost with several databases. The browser doesn't send the cookie and web manifest returns 404.

View File

@ -0,0 +1,4 @@
To use your PWA:
#. Open the Odoo web app using a supported browser (like Chrome/Chromium)
#. Install the PWA

View File

@ -377,12 +377,13 @@ If youre building a web app today, youre already on the path towards build
<ul class="simple">
<li><a class="reference internal" href="#installation" id="id1">Installation</a></li>
<li><a class="reference internal" href="#configuration" id="id2">Configuration</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="id3">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id4">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id5">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id6">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id7">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id8">Maintainers</a></li>
<li><a class="reference internal" href="#usage" id="id3">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="id4">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id5">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id6">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id7">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id8">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id9">Maintainers</a></li>
</ul>
</li>
</ul>
@ -406,20 +407,29 @@ And like all other installed apps, its a top level app in the task switcher.<
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#id2">Configuration</a></h1>
<p>The following system parameters con be set to customize the appearance of the application</p>
<p>This module allows you to set the following parameters under settings to customize the appearance of the application</p>
<ul class="simple">
<li>pwa.manifest.name (defaults to “Odoo PWA”)</li>
<li>pwa.manifest.short_name (defaults to “Odoo PWA”)</li>
<li>pwa.manifest.icon128x128 (defaults to “/web_pwa_oca/static/img/icons/icon-128x128.png”)</li>
<li>pwa.manifest.icon144x144 (defaults to “/web_pwa_oca/static/img/icons/icon-144x144.png”)</li>
<li>pwa.manifest.icon152x152 (defaults to “/web_pwa_oca/static/img/icons/icon-152x152.png”)</li>
<li>pwa.manifest.icon192x192 (defaults to “/web_pwa_oca/static/img/icons/icon-192x192.png”)</li>
<li>pwa.manifest.icon256x256 (defaults to “/web_pwa_oca/static/img/icons/icon-256x256.png”)</li>
<li>pwa.manifest.icon512x512 (defaults to “/web_pwa_oca/static/img/icons/icon-512x512.png”)</li>
<li>PWA Name (defaults to “Odoo PWA”)</li>
<li>PWA Short Name (defaults to “Odoo PWA”)</li>
<li>PWA Icon (<strong>SVG</strong>) (defaults to “/web_pwa_oca/static/img/icons/odoo-logo.svg”)</li>
</ul>
<p>To configure your PWA:</p>
<ol class="arabic simple">
<li>Go to <strong>Settings &gt; General Settings &gt; Progressive Web App</strong>.</li>
<li>Set the parameters (<em>Note:</em> Icon <strong>must be a SVG file</strong>)</li>
<li><strong>Save</strong></li>
</ol>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id3">Usage</a></h1>
<p>To use your PWA:</p>
<ol class="arabic simple">
<li>Open the Odoo web app using a supported browser (like Chrome/Chromium)</li>
<li>Install the PWA</li>
</ol>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id3">Known issues / Roadmap</a></h1>
<h1><a class="toc-backref" href="#id4">Known issues / Roadmap</a></h1>
<ul>
<li><p class="first">Evaluate to extend <tt class="docutils literal">FILES_TO_CACHE</tt></p>
</li>
@ -430,12 +440,12 @@ And like all other installed apps, its a top level app in the task switcher.<
<li><p class="first">Create <tt class="docutils literal">portal_pwa</tt> module, intended to be used by front-end users (customers, suppliers…)</p>
</li>
<li><dl class="first docutils">
<dt>Current <tt class="docutils literal">John Resig's inheritance</tt> doesnt support <tt class="docutils literal">async</tt> functions. So we need use the following workaround:</dt>
<dt>Current <tt class="docutils literal">John Resig's inheritance</tt> implementation doesnt support <tt class="docutils literal">async</tt> functions because <tt class="docutils literal">this._super</tt> cant be called inside a promise. So we need use the following workaround:</dt>
<dd><ul class="first last">
<li><dl class="first docutils">
<dt>Natural async/await example (This breaks “_super” call)</dt>
<dd><pre class="code javascript first last literal-block">
<span class="kr">const</span> <span class="nx">MyClass</span> <span class="o">=</span> <span class="nx">OdooClass</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
<span class="kd">var</span> <span class="nx">MyClass</span> <span class="o">=</span> <span class="nx">OdooClass</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
<span class="nx">myFunc</span><span class="o">:</span> <span class="nx">async</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">mydata</span> <span class="o">=</span> <span class="nx">await</span> <span class="p">...</span><span class="k">do</span> <span class="nx">await</span> <span class="nx">stuff</span><span class="p">...</span>
<span class="k">return</span> <span class="nx">mydata</span><span class="p">;</span>
@ -448,7 +458,7 @@ And like all other installed apps, its a top level app in the task switcher.<
<li><dl class="first docutils">
<dt>Same code with the workaround:</dt>
<dd><pre class="code javascript first last literal-block">
<span class="kr">const</span> <span class="nx">MyClass</span> <span class="o">=</span> <span class="nx">OdooClass</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
<span class="kd">var</span> <span class="nx">MyClass</span> <span class="o">=</span> <span class="nx">OdooClass</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
<span class="nx">myFunc</span><span class="o">:</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nb">Promise</span><span class="p">(</span><span class="nx">async</span> <span class="p">(</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="p">=&gt;</span> <span class="p">{</span>
<span class="kr">const</span> <span class="nx">mydata</span> <span class="o">=</span> <span class="nx">await</span> <span class="p">...</span><span class="k">do</span> <span class="nx">await</span> <span class="nx">stuff</span><span class="p">...</span>
@ -464,10 +474,12 @@ And like all other installed apps, its a top level app in the task switcher.<
</dd>
</dl>
</li>
<li><p class="first">Fix issue when trying to run in localhost with several databases. The browser doesnt send the cookie and web manifest returns 404.</p>
</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id4">Bug Tracker</a></h1>
<h1><a class="toc-backref" href="#id5">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/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 smashing it by providing a detailed and welcomed
@ -475,16 +487,16 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<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="#id5">Credits</a></h1>
<h1><a class="toc-backref" href="#id6">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id6">Authors</a></h2>
<h2><a class="toc-backref" href="#id7">Authors</a></h2>
<ul class="simple">
<li>TAKOBI</li>
<li>Tecnativa</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id7">Contributors</a></h2>
<h2><a class="toc-backref" href="#id8">Contributors</a></h2>
<ul class="simple">
<li><a class="reference external" href="https://takobi.online">TAKOBI</a>:<ul>
<li>Lorenzo Battistini</li>
@ -492,12 +504,13 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
</li>
<li><a class="reference external" href="https://tecnativa.com">Tecnativa</a>:<ul>
<li>Alexandre D. Díaz</li>
<li>João Marques</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id8">Maintainers</a></h2>
<h2><a class="toc-backref" href="#id9">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

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="919" height="495" viewBox="0 0 919 495"><g fill="none"><path fill="#8F8F8F" d="M695 346c-41.421 0-75-33.579-75-75s33.579-75 75-75 75 33.579 75 75-33.579 75-75 75zm0-31c24.3 0 44-19.7 44-44s-19.7-44-44-44-44 19.7-44 44 19.7 44 44 44zm-157 31c-41.421 0-75-33.579-75-75s33.579-75 75-75 75 33.579 75 75-33.579 75-75 75zm0-31c24.3 0 44-19.7 44-44s-19.7-44-44-44-44 19.7-44 44 19.7 44 44 44zm-82-45c0 41.935-33.592 76-75.009 76C339.575 346 306 312.005 306 270.07c0-41.936 30.5-74.07 74.991-74.07 16.442 0 31.647 3.496 44.007 12.58l.002-43.49c0-8.334 7.27-15.09 15.5-15.09 8.228 0 15.5 6.762 15.5 15.09V270zm-75 45c24.3 0 44-19.7 44-44s-19.7-44-44-44-44 19.7-44 44 19.7 44 44 44z"/><path fill="#875A7B" d="M224 346c-41.421 0-75-33.579-75-75s33.579-75 75-75 75 33.579 75 75-33.579 75-75 75zm0-31c24.3 0 44-19.7 44-44s-19.7-44-44-44-44 19.7-44 44 19.7 44 44 44z"/></g><script xmlns=""/><script xmlns="" type="text/javascript"/><script xmlns="" type="text/javascript"/></svg>

After

Width:  |  Height:  |  Size: 1012 B

View File

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

View File

@ -0,0 +1,123 @@
# Copyright 2020 João Marques
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import json
import base64
import odoo.tests
from odoo import exceptions
from odoo.modules.module import get_resource_path
class TestUi(odoo.tests.HttpCase):
def setUp(self):
super().setUp()
self.user = self.env.ref("base.user_admin")
self.res_config_settings_obj = (
self.env["res.config.settings"].sudo(self.user.id).create({})
)
def test_manifest_valid_json(self):
# Call the manifest controller
manifest_data = self.url_open("/web_pwa_oca/manifest.webmanifest")
# should be valid json
manifest_content_str = manifest_data.content.decode("utf-8")
json.loads(manifest_content_str)
def test_manifest_correct_paramenters(self):
# Set PWA parameters in settings
self.res_config_settings_obj.pwa_name = "Test PWA"
self.res_config_settings_obj.pwa_short_name = "Test"
# icon should remain the default one
self.res_config_settings_obj.pwa_icon = False
self.res_config_settings_obj.set_values()
# Call the manifest controller
manifest_data = self.url_open("/web_pwa_oca/manifest.webmanifest")
manifest_content_str = manifest_data.content.decode("utf-8")
manifest_content = json.loads(manifest_content_str)
self.assertEquals(manifest_content["name"], "Test PWA")
self.assertEquals(manifest_content["short_name"], "Test")
# icon should remain the default one
self.assertEquals(
manifest_content["icons"][0]["src"],
"/web_pwa_oca/static/img/icons/icon-128x128.png",
)
self.assertEquals(manifest_content["icons"][0]["sizes"], "128x128")
self.assertTrue(manifest_content["icons"][0]["type"].startswith("image/png"))
def test_manifest_logo_upload(self):
with open(
"%s/static/img/icons/odoo_logo.svg" % get_resource_path("web_pwa_oca"),
"rb"
) as fi:
icon_to_send = base64.b64encode(fi.read())
# Set PWA icon in settings
self.res_config_settings_obj.pwa_icon = icon_to_send
self.res_config_settings_obj.set_values()
# Call the manifest controller
manifest_data = self.url_open("/web_pwa_oca/manifest.webmanifest")
manifest_content_str = manifest_data.content.decode("utf-8")
manifest_content = json.loads(manifest_content_str)
self.assertEquals(manifest_content["icons"][0]["src"], "/web_pwa_oca/icon.svg")
self.assertTrue(manifest_content["icons"][0]["type"].startswith("image/svg"))
self.assertEquals(manifest_content["icons"][0]["sizes"], "128x128 144x144 152x152 192x192 256x256 512x512")
# Get the icon and compare it
icon_data = self.url_open("/web_pwa_oca/icon.svg")
icon_data_bytes = base64.b64encode(icon_data.content)
self.assertEquals(icon_data_bytes, icon_to_send)
def test_png_logo_upload(self):
with open(
"%s/static/img/icons/icon-512x512.png" % get_resource_path("web_pwa_oca"),
"rb"
) as fi:
icon_to_send = base64.b64encode(fi.read())
# Set PWA icon in settings
self.res_config_settings_obj.pwa_icon = icon_to_send
self.res_config_settings_obj.set_values()
# Call the manifest controller
manifest_data = self.url_open("/web_pwa_oca/manifest.webmanifest")
manifest_content_str = manifest_data.content.decode("utf-8")
manifest_content = json.loads(manifest_content_str)
expected_vals = {
"src": "/web_pwa_oca/icon512x512.png",
"sizes": "512x512",
"type": "image/png",
}
self.assertTrue(expected_vals in manifest_content["icons"])
def test_manifest_logo_upload_big(self):
# Set PWA icon in settings
with self.assertRaises(exceptions.UserError):
# Image with more than 2MB
self.res_config_settings_obj.pwa_icon = b"a" * 3000000
self.res_config_settings_obj.set_values()
def test_manifest_logo_upload_extension(self):
with self.assertRaises(exceptions.UserError):
# Image that is not SVG or PNG
self.res_config_settings_obj.pwa_icon = b"a" * 1000
self.res_config_settings_obj.set_values()
def test_manifest_logo_upload_small(self):
icon_to_send = None
with open(
"%s/static/img/icons/icon-128x128.png" % get_resource_path("web_pwa_oca"),
"rb"
) as fi:
icon_to_send = base64.b64encode(fi.read())
# Set PWA icon in settings
with self.assertRaises(exceptions.UserError):
# Image smaller than 512X512
self.res_config_settings_obj.pwa_icon = icon_to_send
self.res_config_settings_obj.set_values()

View File

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.pwa</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
<field name="arch" type="xml">
<div id="emails" position='after'>
<h2>Progressive Web App</h2>
<div class="row mt16 o_settings_container" id="pwa_settings">
<div class="col-12 col-lg-6 o_setting_box" id="domain_setting">
<div class="o_setting_right_pane">
<label for="pwa_name" string="PWA Title"/>
<span class="fa fa-lg fa-globe"/>
<div class="text-muted">
Name and icon of your PWA
</div>
<div class="content-group">
<div class="row mt16">
<label class="col-lg-3 o_light_label" string="Name" for="pwa_name"/>
<field name="pwa_name"/>
</div>
<div class="row mt16">
<label class="col-lg-3 o_light_label" string="Short Name" for="pwa_short_name"/>
<field name="pwa_short_name"/>
</div>
<div class="row">
<label class="col-lg-3 o_light_label" for="pwa_icon" />
<field name="pwa_icon" widget="image" class="float-left oe_avatar"/>
</div>
</div>
</div>
</div>
</div>
</div>
</field>
</record>
</data>
</odoo>