Merge PR #1848 into 12.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot
2021-03-12 16:48:36 +00:00
11 changed files with 187 additions and 518 deletions

View File

@@ -21,7 +21,6 @@
],
"data": [
"templates/assets.xml",
"templates/service_worker.xml",
"views/res_config_settings_views.xml",
],
'images': ['static/description/pwa.png'],

View File

@@ -1,3 +1,4 @@
# Copyright 2020 Lorenzo Battistini @ TAKOBI
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
from . import main
from . import service_worker

View File

@@ -12,7 +12,9 @@ class PWA(Controller):
"""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/jquery-sw-compat.js",
"/web/static/src/js/boot.js",
"/web/static/src/js/core/class.js",
"/web_pwa_oca/static/src/js/worker/pwa.js",
]

View File

@@ -0,0 +1,87 @@
# Copyright 2021 Tecnativa - Alexandre D. Díaz
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
from odoo.http import request, route
from .main import PWA
class ServiceWorker(PWA):
JS_PWA_CORE_EVENT_INSTALL = """
self.addEventListener('install', evt => {{
console.log('[ServiceWorker] Installing...');
{}
}});
"""
JS_PWA_CORE_EVENT_FETCH = """
self.addEventListener('fetch', evt => {{
{}
}});
"""
JS_PWA_CORE_EVENT_ACTIVATE = """
self.addEventListener('activate', evt => {{
{}
}});
"""
JS_PWA_MAIN = """
self.importScripts(...{pwa_scripts});
odoo.define("web_pwa_oca.ServiceWorker", function (require) {{
"use strict";
{pwa_requires}
{pwa_init}
{pwa_core_event_install}
{pwa_core_event_activate}
{pwa_core_event_fetch}
}});
"""
def _get_js_pwa_requires(self):
return """
const PWA = require('web_pwa_oca.PWA');
"""
def _get_js_pwa_init(self):
return """
const oca_pwa = new PWA({});
""".format(self._get_pwa_params())
def _get_js_pwa_core_event_install_impl(self):
return """
evt.waitUntil(oca_pwa.installWorker());
self.skipWaiting();
"""
def _get_js_pwa_core_event_activate_impl(self):
return """
console.log('[ServiceWorker] Activating...');
evt.waitUntil(oca_pwa.activateWorker());
self.clients.claim();
"""
def _get_js_pwa_core_event_fetch_impl(self):
return ""
@route("/service-worker.js", type="http", auth="public")
def render_service_worker(self):
"""Route to register the service worker in the 'main' scope ('/')"""
sw_code = self.JS_PWA_MAIN.format(**{
'pwa_scripts': self._get_pwa_scripts(),
'pwa_requires': self._get_js_pwa_requires(),
'pwa_init': self._get_js_pwa_init(),
'pwa_core_event_install': self.JS_PWA_CORE_EVENT_INSTALL.format(
self._get_js_pwa_core_event_install_impl()),
'pwa_core_event_activate': self.JS_PWA_CORE_EVENT_ACTIVATE.format(
self._get_js_pwa_core_event_activate_impl()),
'pwa_core_event_fetch': self.JS_PWA_CORE_EVENT_FETCH.format(
self._get_js_pwa_core_event_fetch_impl()),
})
return request.make_response(
sw_code,
[('Content-Type', "text/javascript;charset=utf-8"),
('Content-Length', len(sw_code))])

View File

@@ -6,15 +6,16 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2021-02-17 10:45+0000\n"
"POT-Creation-Date: 2021-03-11 16:50+0000\n"
"PO-Revision-Date: 2021-03-11 19:03+0100\n"
"Last-Translator: claudiagn <claudia.gargallo@qubiq.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"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.3.2\n"
"X-Generator: Poedit 2.4.1\n"
#. module: web_pwa_oca
#: model:ir.model.fields,field_description:web_pwa_oca.field_res_config_settings__pwa_background_color
@@ -123,266 +124,3 @@ msgstr "[ServiceWorker] Registrada:"
#, python-format
msgid "[ServiceWorker] Registration failed: "
msgstr "[ServiceWorker] Error en el registro: "
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_core_events
msgid ""
"console.log('[ServiceWorker] Activating...');\n"
" evt.waitUntil(oca_pwa.activateWorker());\n"
" self.clients.claim();"
msgstr ""
"console.log('[ServiceWorker] Activating...');\n"
" evt.waitUntil(oca_pwa.activateWorker());\n"
" self.clients.claim();"
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_init
msgid "const oca_pwa = new PWA("
msgstr "const oca_pwa = nuevo PWA("
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_core_events
msgid ""
"evt.waitUntil(oca_pwa.installWorker());\n"
" self.skipWaiting();"
msgstr ""
"evt.waitUntil(oca_pwa.installWorker());\n"
" self.skipWaiting();"
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_core_events
msgid "self.addEventListener('activate', evt =&gt; {"
msgstr "self.addEventListener('activate', evt =&gt; {"
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_core_events
msgid "self.addEventListener('fetch', evt =&gt; {"
msgstr "self.addEventListener('fetch', evt =&gt; {"
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_core_events
msgid ""
"self.addEventListener('install', evt =&gt; {\n"
" console.log('[ServiceWorker] Installing...');"
msgstr ""
"self.addEventListener('install', evt =&gt; {\n"
" console.log('[ServiceWorker] Installing...');"
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.service_worker
msgid "self.importScripts(..."
msgstr "self.importScripts(..."
#~ msgid "Install PWA"
#~ msgstr "Instalar PWA"
#~ msgid ""
#~ "\",\n"
#~ " \"sizes\": \"128x128\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }, {\n"
#~ " \"src\": \""
#~ msgstr ""
#~ "\",\n"
#~ " \"sizes\": \"128x128\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }, {\n"
#~ " \"src\": \""
#~ msgid ""
#~ "\",\n"
#~ " \"sizes\": \"144x144\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }, {\n"
#~ " \"src\": \""
#~ msgstr ""
#~ "\",\n"
#~ " \"sizes\": \"144x144\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }, {\n"
#~ " \"src\": \""
#~ msgid ""
#~ "\",\n"
#~ " \"sizes\": \"152x152\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }, {\n"
#~ " \"src\": \""
#~ msgstr ""
#~ "\",\n"
#~ " \"sizes\": \"152x152\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }, {\n"
#~ " \"src\": \""
#~ msgid ""
#~ "\",\n"
#~ " \"sizes\": \"192x192\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }, {\n"
#~ " \"src\": \""
#~ msgstr ""
#~ "\",\n"
#~ " \"sizes\": \"192x192\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }, {\n"
#~ " \"src\": \""
#~ msgid ""
#~ "\",\n"
#~ " \"sizes\": \"256x256\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }, {\n"
#~ " \"src\": \""
#~ msgstr ""
#~ "\",\n"
#~ " \"sizes\": \"256x256\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }, {\n"
#~ " \"src\": \""
#~ msgid ""
#~ "\",\n"
#~ " \"sizes\": \"512x512\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }],\n"
#~ " \"start_url\": \"/web\",\n"
#~ " \"display\": \"standalone\",\n"
#~ " \"background_color\": \""
#~ msgstr ""
#~ "\",\n"
#~ " \"sizes\": \"512x512\",\n"
#~ " \"type\": \"image/png\"\n"
#~ " }],\n"
#~ " \"start_url\": \"/web\",\n"
#~ " \"display\": \"standalone\",\n"
#~ " \"background_color\": \""
#~ msgid ""
#~ "\",\n"
#~ " \"icons\": [{\n"
#~ " \"src\": \""
#~ msgstr ""
#~ "\",\n"
#~ " \"icons\": [{\n"
#~ " \"src\": \""
#~ msgid ""
#~ "\",\n"
#~ " \"short_name\": \""
#~ msgstr ""
#~ "\",\n"
#~ " \"short_name\": \""
#~ msgid ""
#~ "\",\n"
#~ " \"theme_color\": \""
#~ msgstr ""
#~ "\",\n"
#~ " \"theme_color\": \""
#~ msgid ""
#~ "';\n"
#~ "const FILES_TO_CACHE = ["
#~ msgstr ""
#~ "';\n"
#~ "const FILES_TO_CACHE = ["
#~ msgid ""
#~ "'use strict';\n"
#~ "const CACHE_NAME = '"
#~ msgstr ""
#~ "'use strict';\n"
#~ "const CACHE_NAME = '"
#~ msgid ""
#~ "];\n"
#~ "self.addEventListener('install', function (evt) {\n"
#~ " console.log('[ServiceWorker] Install');\n"
#~ " evt.waitUntil(\n"
#~ " caches.open(CACHE_NAME).then(function (cache) {\n"
#~ " console.log('[ServiceWorker] Pre-caching offline page');\n"
#~ " return cache.addAll(FILES_TO_CACHE);\n"
#~ " })\n"
#~ " );\n"
#~ " self.skipWaiting();\n"
#~ "});\n"
#~ "self.addEventListener('activate', function(evt) {\n"
#~ " console.log('[ServiceWorker] Activate');\n"
#~ " evt.waitUntil(\n"
#~ " caches.keys().then(function(keyList) {\n"
#~ " return Promise.all(keyList.map(function(key) {\n"
#~ " if (key !== CACHE_NAME) {\n"
#~ " console.log('[ServiceWorker] Removing old cache', "
#~ "key);\n"
#~ " return caches.delete(key);\n"
#~ " }\n"
#~ " }));\n"
#~ " })\n"
#~ " );\n"
#~ " self.clients.claim();\n"
#~ "});\n"
#~ "self.addEventListener('fetch', function(evt) {\n"
#~ " if (evt.request.cache === 'only-if-cached' &amp;&amp; evt.request.mode !"
#~ "== 'same-origin') {\n"
#~ " return;\n"
#~ " }\n"
#~ " console.log('[ServiceWorker] Fetch', evt.request.url);\n"
#~ " evt.respondWith(\n"
#~ " caches.open(CACHE_NAME).then(function(cache) {\n"
#~ " return cache.match(evt.request)\n"
#~ " .then(function(response) {\n"
#~ " return response || fetch(evt.request);\n"
#~ " });\n"
#~ " })\n"
#~ " );\n"
#~ "});"
#~ msgstr ""
#~ "];\n"
#~ "self.addEventListener('install', function (evt) {\n"
#~ " console.log('[ServiceWorker] Install');\n"
#~ " evt.waitUntil(\n"
#~ " caches.open(CACHE_NAME).then(function (cache) {\n"
#~ " console.log('[ServiceWorker] Pre-caching offline page');\n"
#~ " return cache.addAll(FILES_TO_CACHE);\n"
#~ " })\n"
#~ " );\n"
#~ " self.skipWaiting();\n"
#~ "});\n"
#~ "self.addEventListener('activate', function(evt) {\n"
#~ " console.log('[ServiceWorker] Activate');\n"
#~ " evt.waitUntil(\n"
#~ " caches.keys().then(function(keyList) {\n"
#~ " return Promise.all(keyList.map(function(key) {\n"
#~ " if (key !== CACHE_NAME) {\n"
#~ " console.log('[ServiceWorker] Removing old cache', "
#~ "key);\n"
#~ " return caches.delete(key);\n"
#~ " }\n"
#~ " }));\n"
#~ " })\n"
#~ " );\n"
#~ " self.clients.claim();\n"
#~ "});\n"
#~ "self.addEventListener('fetch', function(evt) {\n"
#~ " if (evt.request.cache === 'only-if-cached' &amp;&amp; evt.request.mode !"
#~ "== 'same-origin') {\n"
#~ " return;\n"
#~ " }\n"
#~ " console.log('[ServiceWorker] Fetch', evt.request.url);\n"
#~ " evt.respondWith(\n"
#~ " caches.open(CACHE_NAME).then(function(cache) {\n"
#~ " return cache.match(evt.request)\n"
#~ " .then(function(response) {\n"
#~ " return response || fetch(evt.request);\n"
#~ " });\n"
#~ " })\n"
#~ " );\n"
#~ "});"
#~ msgid ""
#~ "{\n"
#~ " \"name\": \""
#~ msgstr ""
#~ "{\n"
#~ " \"name\": \""

View File

@@ -6,6 +6,8 @@ msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-03-11 16:50+0000\n"
"PO-Revision-Date: 2021-03-11 16:50+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -117,42 +119,3 @@ msgstr ""
msgid "[ServiceWorker] Registration failed: "
msgstr ""
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_core_events
msgid "console.log('[ServiceWorker] Activating...');\n"
" evt.waitUntil(oca_pwa.activateWorker());\n"
" self.clients.claim();"
msgstr ""
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_init
msgid "const oca_pwa = new PWA("
msgstr ""
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_core_events
msgid "evt.waitUntil(oca_pwa.installWorker());\n"
" self.skipWaiting();"
msgstr ""
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_core_events
msgid "self.addEventListener('activate', evt =&gt; {"
msgstr ""
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_core_events
msgid "self.addEventListener('fetch', evt =&gt; {"
msgstr ""
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.pwa_core_events
msgid "self.addEventListener('install', evt =&gt; {\n"
" console.log('[ServiceWorker] Installing...');"
msgstr ""
#. module: web_pwa_oca
#: model_terms:ir.ui.view,arch_db:web_pwa_oca.service_worker
msgid "self.importScripts(..."
msgstr ""

View File

@@ -31,6 +31,5 @@
* Fix issue when trying to run in localhost with several databases. The browser
doesn't send the cookie and web manifest returns 404.
* Evaluate to support 'require' system.
* Firefox can't detect 'standalone' mode. See https://bugzilla.mozilla.org/show_bug.cgi?id=1285858
* Firefox disable service worker in private mode. See https://bugzilla.mozilla.org/show_bug.cgi?id=1601916

View File

@@ -0,0 +1,62 @@
/* Copyright 2020 Tecnativa - Alexandre D. Díaz
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
// Compatibility layer to load some Odoo modules
// This is a very simple implementation!!!
function JQuery (selector, context) {
return new JQuery.prototype.init(selector, context);
};
JQuery.prototype = {
init: function (selector, context) {
if (typeof selector === "function") {
selector();
}
},
// This is a hack, not a complete implementation!
// only expected to be used by boot.js
deparam: function (data) {
const params = data.split(',');
const res = [];
for (let param of params) {
res.push(param.split('='));
}
return _.object(res);
},
param: {
querystring: function () {
return "debug=1";
}
},
when: function (tasks) {
if (!(tasks instanceof Array)) {
tasks = [tasks];
}
return Promise.all(tasks).then((results) => {
return results.length === 1 ? results[0] : results;
});
},
};
class Deferred {
constructor() {
this.promise = new Promise((resolve, reject)=> {
this.reject = reject;
this.resolve = resolve;
})
}
};
JQuery.prototype.Deferred = () => new Deferred();
self.$ = JQuery;
self.$.deparam = JQuery.prototype.deparam;
self.$.param = JQuery.prototype.param;
self.$.Deferred = JQuery.prototype.Deferred;
self.$.when = JQuery.prototype.when;
self.window = self;

View File

@@ -1,150 +0,0 @@
/**
* Improved John Resig's inheritance, based on:
*
* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
*
* Adds "include()"
*
* Defines The Class object. That object can be used to define and inherit classes using
* the extend() method.
*
* Example::
*
* var Person = Class.extend({
* init: function(isDancing){
* this.dancing = isDancing;
* },
* dance: function(){
* return this.dancing;
* }
* });
*
* The init() method act as a constructor. This class can be instanced this way::
*
* var person = new Person(true);
* person.dance();
*
* The Person class can also be extended again:
*
* var Ninja = Person.extend({
* init: function(){
* this._super( false );
* },
* dance: function(){
* // Call the inherited version of dance()
* return this._super();
* },
* swingSword: function(){
* return true;
* }
* });
*
* When extending a class, each re-defined method can use this._super() to call the previous
* implementation of that method.
*
* @class Class
*/
function OdooClass(){}
var initializing = false;
var fnTest = /xyz/.test(function(){xyz();}) ? /\b_super\b/ : /.*/;
/**
* Subclass an existing class
*
* @param {Object} prop class-level properties (class attributes and instance methods) to set on the new class
*/
OdooClass.extend = function() {
var _super = this.prototype;
// Support mixins arguments
var args = _.toArray(arguments);
args.unshift({});
var prop = _.extend.apply(_,args);
// Instantiate a web class (but only create the instance,
// don't run the init constructor)
initializing = true;
var This = this;
var prototype = new This();
initializing = false;
// Copy the properties over onto the new prototype
_.each(prop, function(val, name) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
fnTest.test(prop[name]) ?
(function(name, fn) {
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same
// method but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so
// we remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
});
// The dummy class constructor
function Class() {
if(this.constructor !== OdooClass){
throw new Error("You can only instanciate objects with the 'new' operator");
}
// All construction is actually done in the init method
this._super = null;
if (!initializing && this.init) {
var ret = this.init.apply(this, arguments);
if (ret) { return ret; }
}
return this;
}
Class.include = function (properties) {
_.each(properties, function(val, name) {
if (typeof properties[name] !== 'function'
|| !fnTest.test(properties[name])) {
prototype[name] = properties[name];
} else if (typeof prototype[name] === 'function'
&& prototype.hasOwnProperty(name)) {
prototype[name] = (function (name, fn, previous) {
return function () {
var tmp = this._super;
this._super = previous;
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, properties[name], prototype[name]);
} else if (typeof _super[name] === 'function') {
prototype[name] = (function (name, fn) {
return function () {
var tmp = this._super;
this._super = _super[name];
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, properties[name]);
}
});
};
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.constructor = Class;
// And make this class extendable
Class.extend = this.extend;
return Class;
};

View File

@@ -1,7 +1,3 @@
"use strict";
/* eslint strict: ["error", "global"] */
/* eslint-disable no-undef, no-empty-function, no-implicit-globals,
no-unused-vars */
/* Copyright 2020 Tecnativa - Alexandre D. Díaz
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
@@ -11,25 +7,36 @@ no-unused-vars */
* When the service worker is called to be installed from the "pwa_manager"
* this class is instantiated.
*/
var PWA = OdooClass.extend({
// eslint-disable-next-line
init: function (params) {
// To be overridden
},
/**
* @returns {Promise}
*/
installWorker: function () {
return Promise.resolve();
},
odoo.define("web_pwa_oca.PWA", function (require) {
"use strict";
/**
* @returns {Promise}
*/
activateWorker: function () {
return Promise.resolve();
},
const OdooClass = require("web.Class");
const PWA = OdooClass.extend({
// eslint-disable-next-line
init: function (params) {
// To be overridden
},
/**
* @returns {Promise}
*/
installWorker: function () {
// To be overridden
return Promise.resolve();
},
/**
* @returns {Promise}
*/
activateWorker: function () {
// To be overridden
return Promise.resolve();
},
});
return PWA;
});

View File

@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="pwa_init" name="PWA Initialization">
const oca_pwa = new PWA(<t t-esc="str(pwa_params)"/>);
</template>
<template id="pwa_core_events" name="PWA Core Events">
<t t-set="evt_install">
evt.waitUntil(oca_pwa.installWorker());
self.skipWaiting();
</t>
self.addEventListener('install', evt =&gt; {
console.log('[ServiceWorker] Installing...');
<t t-raw="evt_install" />
});
<!-- This is necessary to get PWA installable.
Other modules can add logic using 'evt_fetch' -->
<t t-set="evt_fetch" />
self.addEventListener('fetch', evt =&gt; {
<t t-raw="evt_fetch" />
});
<t t-set="evt_activate">
console.log('[ServiceWorker] Activating...');
evt.waitUntil(oca_pwa.activateWorker());
self.clients.claim();
</t>
self.addEventListener('activate', evt =&gt; {
<t t-raw="evt_activate" />
});
</template>
<template id="service_worker" name="PWA Service Worker">
self.importScripts(...<t t-esc="str(pwa_scripts)" />);
<t t-call="web_pwa_oca.pwa_init" />
<t t-call="web_pwa_oca.pwa_core_events" />
</template>
</odoo>