[FIX] website, *: lazy load widget assets of the correct website

*: web

Before this commit, when a widget declared assets to lazy load through
the assetLibs key, the asset was loaded through a context-less call to
the server through the base `rpc` function. This caused problems in
website: the website ID was not given to the called route and the loaded
assets were then stripped of their specific-website parts.

This commit solves the problem by allowing the `loadLibs` and
`loadAsset` functions to receive an extra argument: an extra `context`
to use for the actual call to the server. The method is now available
in the ajax service, so that website can add its custom context
automatically, the same way it does it for normal `_rpc` calls.

Part of https://github.com/odoo/odoo/pull/44596

closes odoo/odoo#44596

Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
qsm-odoo
2020-02-05 10:48:44 +00:00
parent d04ca05b2e
commit 6f4c60fe1b
9 changed files with 89 additions and 9 deletions
+9 -4
View File
@@ -428,6 +428,8 @@ var loadXML = (function () {
* Loads a template file according to the given xmlId.
*
* @param {string} [xmlId] - the template xmlId
* @param {Object} [context]
* additionnal rpc context to be merged with the default one
* @returns {Deferred} resolved with an object
* cssLibs: list of css files
* cssContents: list of style tag contents
@@ -437,16 +439,17 @@ var loadXML = (function () {
var loadAsset = (function () {
var cache = {};
var load = function loadAsset(xmlId) {
var load = function loadAsset(xmlId, context) {
if (cache[xmlId]) {
return Promise.resolve(cache[xmlId]);
}
context = _.extend({}, odoo.session_info.user_context, context);
var params = {
args: [xmlId, {
debug: config.isDebug()
}],
kwargs: {
context: odoo.session_info.user_context,
context: context,
},
method: 'render_template',
model: 'ir.ui.view',
@@ -499,10 +502,12 @@ var loadAsset = (function () {
* List of inline styles to add after loading the CSS files.
* @param {string[]} [libs.jsContents=[]]
* List of inline scripts to add after loading the JS files.
* @param {Object} [context]
* additionnal rpc context to be merged with the default one
*
* @returns {Promise}
*/
function loadLibs(libs) {
function loadLibs(libs, context) {
var mutex = new concurrency.Mutex();
mutex.exec(function () {
var defs = [];
@@ -525,7 +530,7 @@ function loadLibs(libs) {
});
mutex.exec(function () {
return _loadArray(libs.assetLibs || [], function (xmlID) {
return ajax.loadAsset(xmlID).then(function (asset) {
return ajax.loadAsset(xmlID, context).then(function (asset) {
return ajax.loadLibs(asset);
});
});
@@ -123,6 +123,15 @@ var ServicesMixin = {
});
return result;
},
/**
* @private
* @param {Object} libs - @see ajax.loadLibs
* @param {Object} [context] - @see ajax.loadLibs
* @returns {Promise}
*/
_loadLibs: function (libs, context) {
return this.call('ajax', 'loadLibs', libs, context);
},
/**
* Builds and executes RPC query. Returns a promise resolved with
* the RPC result.
+1 -1
View File
@@ -148,7 +148,7 @@ var Widget = core.Class.extend(mixins.PropertiesMixin, ServicesMixin, {
}));
}
if (this.jsLibs || this.cssLibs || this.assetLibs) {
proms.push(ajax.loadLibs(this));
proms.push(this._loadLibs(this));
}
return Promise.all(proms);
},
+13 -3
View File
@@ -101,15 +101,25 @@ var PublicRoot = publicWidget.RootWidget.extend(ServiceProviderMixin, {
if (_.str.startsWith(route, '/web/dataset/call_kw/')) {
var params = ev.data.args[1];
var options = ev.data.args[2];
params.kwargs.context = _.extend({}, this._getContext(), params.kwargs.context || {});
var noContextKeys = undefined;
if (options) {
params.kwargs.context = _.omit(params.kwargs.context, options.noContextKeys);
noContextKeys = options.noContextKeys;
ev.data.args[2] = _.omit(options, 'noContextKeys');
}
params.kwargs.context = JSON.parse(JSON.stringify(params.kwargs.context));
params.kwargs.context = _computeContext.call(this, params.kwargs.context, noContextKeys);
}
} else if (ev.data.service === 'ajax' && ev.data.method === 'loadLibs') {
ev.data.args[1] = _computeContext.call(this, ev.data.args[1]);
}
return ServiceProviderMixin._call_service.apply(this, arguments);
function _computeContext(context, noContextKeys) {
context = _.extend({}, this._getContext(), context);
if (noContextKeys) {
context = _.omit(context, noContextKeys);
}
return JSON.parse(JSON.stringify(context));
}
},
/**
* Retrieves the global context of the public environment. This is the
@@ -2,10 +2,18 @@ odoo.define('web.AjaxService', function (require) {
"use strict";
var AbstractService = require('web.AbstractService');
var ajax = require('web.ajax');
var core = require('web.core');
var session = require('web.session');
var AjaxService = AbstractService.extend({
/**
* @param {Object} libs - @see ajax.loadLibs
* @param {Object} [context] - @see ajax.loadLibs
*/
loadLibs: function (libs, context) {
return ajax.loadLibs(libs, context);
},
rpc: function (route, args, options, target) {
var rpcPromise;
var promise = new Promise(function (resolve, reject) {
@@ -253,7 +253,7 @@ function addMockEnvironment(widget, params) {
var service = services[ev.data.service];
args = (ev.data.args || []);
result = service[ev.data.method].apply(service, args);
} else if (ev.data.service === 'ajax') {
} else if (ev.data.service === 'ajax' && ev.data.method === 'rpc') {
// use ajax service that is mocked by the server
var route = ev.data.args[0];
args = ev.data.args[1];
@@ -0,0 +1,29 @@
odoo.define('website.tour.specific_website_editor', function (require) {
'use strict';
var tour = require('web_tour.tour');
tour.register('generic_website_editor', {
test: true,
}, [{
trigger: 'a[data-action=edit]',
content: 'Click edit button',
}, {
trigger: 'body:not([data-hello="world"])',
extra_trigger: '#oe_snippets.o_loaded',
content: 'Check that the editor DOM matches its website-generic features',
run: function () {}, // Simple check
}]);
tour.register('specific_website_editor', {
test: true,
}, [{
trigger: 'a[data-action=edit]',
content: 'Click edit button',
}, {
trigger: 'body[data-hello="world"]',
extra_trigger: '#oe_snippets.o_loaded',
content: 'Check that the editor DOM matches its website-specific features',
run: function () {}, // Simple check
}]);
});
+18
View File
@@ -112,3 +112,21 @@ class TestUi(odoo.tests.HttpCase):
def test_04_backend_dashboard(self):
self.start_tour("/", 'backend_dashboard', login='admin')
def test_05_specific_website_editor(self):
website_default = self.env['website'].search([], limit=1)
new_website = self.env['website'].create({'name': 'New Website'})
website_editor_assets_view = self.env.ref('website.assets_wysiwyg')
self.env['ir.ui.view'].create({
'name': 'Editor Extension',
'type': 'qweb',
'inherit_id': website_editor_assets_view.id,
'website_id': new_website.id,
'arch': """
<xpath expr="." position="inside">
<script type="text/javascript">document.body.dataset.hello = 'world';</script>
</xpath>
""",
})
self.start_tour("/?fw=%s" % website_default.id, "generic_website_editor", login='admin')
self.start_tour("/?fw=%s" % new_website.id, "specific_website_editor", login='admin')
+1
View File
@@ -24,6 +24,7 @@
<script type="text/javascript" src="/website/static/tests/tours/html_editor.js"/>
<script type="text/javascript" src="/website/static/tests/tours/restricted_editor.js"/>
<script type="text/javascript" src="/website/static/tests/tours/dashboard_tour.js"/>
<script type="text/javascript" src="/website/static/tests/tours/specific_website_editor.js"/>
</xpath>
</template>