From db7b485d984e50fae5a52308634166460e5fc908 Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Mon, 5 Jul 2021 09:37:06 +0000 Subject: [PATCH] [REF] web, website, *: make wowl services available in frontend *: http_routing When rewriting the webclient, a new environment was created, as well as a new type of service and a new mechanism to start those services. This environment and the services that go with it were not made available in the frontend however. This commit adapts the setup code for the frontend's root widget (and the website specific equivalent, WebsiteRoot) so that the new environment exists, and some of the calls to the legacy services are redirected to the new services. The point is to allow removing some legacy services whose code is currently only needed by the frontent because of the lack of availability of this new env and services Part of #72675 --- addons/http_routing/controllers/main.py | 2 +- .../static/tests/tours/error_views.js | 10 +-- addons/web/__manifest__.py | 10 ++- addons/web/static/src/core/assets.js | 2 +- .../src/core/l10n/localization_service.js | 4 +- .../static/src/legacy/js/core/translation.js | 4 +- .../src/legacy/js/public/public_root.js | 68 +++++++++++++++++-- .../legacy/js/public/public_root_instance.js | 30 +------- .../static/src/js/content/website_root.js | 2 +- .../src/js/content/website_root_instance.js | 28 ++------ 10 files changed, 93 insertions(+), 67 deletions(-) diff --git a/addons/http_routing/controllers/main.py b/addons/http_routing/controllers/main.py index dbc22d81f34..dd57e485b10 100644 --- a/addons/http_routing/controllers/main.py +++ b/addons/http_routing/controllers/main.py @@ -9,7 +9,7 @@ from odoo.addons.web.controllers.main import WebClient, Home class Routing(Home): @http.route('/website/translations/', type='http', auth="public", website=True) - def get_website_translations(self, unique, lang, mods=None): + def get_website_translations(self, unique, lang=None, mods=None): IrHttp = request.env['ir.http'].sudo() modules = IrHttp.get_translation_frontend_modules() if mods: diff --git a/addons/test_website/static/tests/tours/error_views.js b/addons/test_website/static/tests/tours/error_views.js index 0d0d4961ea8..de6f69b68f7 100644 --- a/addons/test_website/static/tests/tours/error_views.js +++ b/addons/test_website/static/tests/tours/error_views.js @@ -14,35 +14,35 @@ tour.register('test_error_website', { trigger: 'a[href="/test_user_error_json"]', }, { content: "rpc user error modal has message", - extra_trigger: 'div.toast-body:contains("This is a user rpc test")', + extra_trigger: 'div.o_notification_content:contains("This is a user rpc test")', trigger: 'button.o_notification_close', }, { content: "trigger rpc access error", trigger: 'a[href="/test_access_error_json"]', }, { content: "rpc access error modal has message", - extra_trigger: 'div.toast-body:contains("This is an access rpc test")', + extra_trigger: 'div.o_notification_content:contains("This is an access rpc test")', trigger: 'button.o_notification_close', }, { content: "trigger validation rpc error", trigger: 'a[href="/test_validation_error_json"]', }, { content: "rpc validation error modal has message", - extra_trigger: 'div.toast-body:contains("This is a validation rpc test")', + extra_trigger: 'div.o_notification_content:contains("This is a validation rpc test")', trigger: 'button.o_notification_close', }, { content: "trigger rpc missing error", trigger: 'a[href="/test_missing_error_json"]', }, { content: "rpc missing error modal has message", - extra_trigger: 'div.toast-body:contains("This is a missing rpc test")', + extra_trigger: 'div.o_notification_content:contains("This is a missing rpc test")', trigger: 'button.o_notification_close', }, { content: "trigger rpc error 403", trigger: 'a[href="/test_access_denied_json"]', }, { content: "rpc error 403 modal has message", - extra_trigger: 'div.toast-body:contains("This is an access denied rpc test")', + extra_trigger: 'div.o_notification_content:contains("This is an access denied rpc test")', trigger: 'button.o_notification_close', }, { content: "trigger rpc error 500", diff --git a/addons/web/__manifest__.py b/addons/web/__manifest__.py index d158d4d658e..3147f685333 100644 --- a/addons/web/__manifest__.py +++ b/addons/web/__manifest__.py @@ -195,10 +195,16 @@ This module provides the core of the Odoo Web Client. ('include', 'web._assets_frontend_helpers'), 'web/static/lib/bootstrap/scss/_variables.scss', + 'web/static/lib/luxon/luxon.js', ('include', 'web._assets_bootstrap'), - 'web/static/src/core/registry.js', + 'web/static/src/env.js', + 'web/static/src/core/utils/transitions.scss', # included early because used by other files + 'web/static/src/core/**/*', + # TODO: split debug files correctly when converting frontend debug + # manager so we don't need action-service and stuff + ('remove', 'web/static/src/core/debug/**/*'), 'web/static/src/legacy/scss/base_frontend.scss', 'web/static/src/legacy/scss/lazyloader.scss', @@ -206,6 +212,8 @@ This module provides the core of the Odoo Web Client. ('include', 'web.assets_frontend_minimal'), + 'web/static/src/legacy/utils.js', + 'web/static/src/legacy/js/owl_compatibility.js', 'web/static/src/legacy/js/services/session.js', 'web/static/src/legacy/js/public/public_env.js', 'web/static/src/legacy/js/public/public_root.js', diff --git a/addons/web/static/src/core/assets.js b/addons/web/static/src/core/assets.js index 9d3196b9e4c..6f1ffd16aa1 100644 --- a/addons/web/static/src/core/assets.js +++ b/addons/web/static/src/core/assets.js @@ -99,7 +99,7 @@ const loadCSS = memoize(function loadCSS(url) { * @returns {Promise} A Promise of an XML document containing the * owl templates. */ -const loadBundleTemplates = memoize(async function loadBundleTemplates(name) { +export const loadBundleTemplates = memoize(async function loadBundleTemplates(name) { // TODO: quid of the "unique" in the URL? We can"t have one cache_hash // for each and every bundle I"m guessing. const bundleURL = new URL(`/web/webclient/qweb/${Date.now()}`, window.location.origin); diff --git a/addons/web/static/src/core/l10n/localization_service.js b/addons/web/static/src/core/l10n/localization_service.js index bd715f0d1ff..bca3fd6609b 100644 --- a/addons/web/static/src/core/l10n/localization_service.js +++ b/addons/web/static/src/core/l10n/localization_service.js @@ -13,7 +13,8 @@ export const localizationService = { const cacheHashes = session.cache_hashes; const translationsHash = cacheHashes.translations || new Date().getTime().toString(); const lang = user.lang || null; - let url = `/web/webclient/translations/${translationsHash}`; + const translationURL = session.translationURL || "/web/webclient/translations"; + let url = `${translationURL}/${translationsHash}`; if (lang) { url += `?lang=${lang}`; } @@ -22,6 +23,7 @@ export const localizationService = { if (!response.ok) { throw new Error("Error while fetching translations"); } + const { lang_parameters: userLocalization, modules: modules } = await response.json(); // FIXME We flatten the result of the python route. diff --git a/addons/web/static/src/legacy/js/core/translation.js b/addons/web/static/src/legacy/js/core/translation.js index 81b220e1e76..169cf9422e8 100644 --- a/addons/web/static/src/legacy/js/core/translation.js +++ b/addons/web/static/src/legacy/js/core/translation.js @@ -70,7 +70,9 @@ var TranslationDataBase = Class.extend(/** @lends instance.TranslationDataBase# if (modules) { paramsGet.modules = modules.join(','); } - paramsGet.lang = lang || null; + if (lang) { + paramsGet.lang = lang; + } return $.get(url, paramsGet).then(function (trans) { self.set_bundle(trans); }); diff --git a/addons/web/static/src/legacy/js/public/public_root.js b/addons/web/static/src/legacy/js/public/public_root.js index 5b22cea2099..65c66496e44 100644 --- a/addons/web/static/src/legacy/js/public/public_root.js +++ b/addons/web/static/src/legacy/js/public/public_root.js @@ -8,6 +8,23 @@ import utils from 'web.utils'; import publicWidget from 'web.public.widget'; import { registry } from '@web/core/registry'; +import AbstractService from "web.AbstractService"; +import legacyEnv from "web.public_env"; +import lazyloader from "web.public.lazyloader"; + +import { + makeLegacyNotificationService, + makeLegacyRpcService, + makeLegacySessionService, + makeLegacyDialogMappingService, +} from "../../utils"; +import { ComponentAdapter } from "web.OwlCompatibility"; + +import { loadBundleTemplates } from "@web/core/assets"; +import { makeEnv, startServices } from "@web/env"; +import { MainComponentsContainer } from "@web/core/main_components_container"; +const serviceRegistry = registry.category("services"); + // Load localizations outside the PublicRoot to not wait for DOM ready (but // wait for them in PublicRoot) function getLang() { @@ -23,7 +40,7 @@ var localeDef = ajax.loadJS('/web/webclient/locale/' + lang.replace('-', '_')); * this Class instance. Its main role will be to retrieve RPC demands from its * children and handle them. */ -var PublicRoot = publicWidget.RootWidget.extend({ +export const PublicRoot = publicWidget.RootWidget.extend({ events: _.extend({}, publicWidget.RootWidget.prototype.events || {}, { 'submit .js_website_submit_form': '_onWebsiteFormSubmit', 'click .js_disable_on_click': '_onDisableOnClick', @@ -240,11 +257,14 @@ var PublicRoot = publicWidget.RootWidget.extend({ } } else if (payload.service === 'ajax' && payload.method === 'loadLibs') { args[1] = _computeContext.call(this, args[1]); + } else { + return; } const service = this.env.services[payload.service]; const result = service[payload.method].apply(service, args); payload.callback(result); + ev.stopPropagation(); }, /** * Called when someone asked for the global public context. @@ -328,6 +348,46 @@ var PublicRoot = publicWidget.RootWidget.extend({ }, }); -export default { - PublicRoot: PublicRoot, -}; +const { Component, mount } = owl; + +/** + * Configure Owl with the public env + */ +owl.config.mode = legacyEnv.isDebug() ? "dev" : "prod"; +owl.Component.env = legacyEnv; + +/** + * This widget is important, because the tour manager needs a root widget in + * order to work. The root widget must be a service provider with the ajax + * service, so that the tour manager can let the server know when tours have + * been consumed. + */ +export async function createPublicRoot(RootWidget) { + await lazyloader.allScriptsLoaded; + AbstractService.prototype.deployServices(legacyEnv); + // add a bunch of mapping services that will redirect service calls from the legacy env + // to the wowl env + serviceRegistry.add("legacy_rpc", makeLegacyRpcService(legacyEnv)); + serviceRegistry.add("legacy_session", makeLegacySessionService(legacyEnv, session)); + serviceRegistry.add("legacy_notification", makeLegacyNotificationService(legacyEnv)); + serviceRegistry.add("legacy_crash_manager", makeLegacyCrashManagerService(legacyEnv)); + serviceRegistry.add("legacy_dialog_mapping", makeLegacyDialogMappingService(legacyEnv)); + await Promise.all([owl.utils.whenReady(), session.is_bound]); + + const wowlEnv = makeEnv(); + wowlEnv.qweb.addTemplates(await loadBundleTemplates("web.assets_frontend")); + await startServices(wowlEnv); + + const adapter = new ComponentAdapter(null, { Component }); // Used for _trigger_up compat layer + const publicRoot = new RootWidget(adapter); + await Promise.all([ + mount(MainComponentsContainer, { + target: document.body, + env: wowlEnv, + }), + publicRoot.attachTo(document.body), + ]); + return publicRoot; +} + +export default { PublicRoot, createPublicRoot }; diff --git a/addons/web/static/src/legacy/js/public/public_root_instance.js b/addons/web/static/src/legacy/js/public/public_root_instance.js index 7bf9ec85b42..b18036634c1 100644 --- a/addons/web/static/src/legacy/js/public/public_root_instance.js +++ b/addons/web/static/src/legacy/js/public/public_root_instance.js @@ -1,30 +1,4 @@ /** @odoo-module alias=root.widget */ +import { PublicRoot, createPublicRoot } from "./public_root"; -import AbstractService from 'web.AbstractService'; -import env from 'web.public_env'; -import lazyloader from 'web.public.lazyloader'; -import rootData from 'web.public.root'; - -/** - * Configure Owl with the public env - */ -owl.config.mode = env.isDebug() ? "dev" : "prod"; -owl.Component.env = env; - -/** - * Deploy services in the env - */ -AbstractService.prototype.deployServices(env); - -/** - * This widget is important, because the tour manager needs a root widget in - * order to work. The root widget must be a service provider with the ajax - * service, so that the tour manager can let the server know when tours have - * been consumed. - */ -var publicRoot = new rootData.PublicRoot(null); -export default lazyloader.allScriptsLoaded.then(function () { - return publicRoot.attachTo(document.body).then(function () { - return publicRoot; - }); -}); +export default createPublicRoot(PublicRoot); diff --git a/addons/website/static/src/js/content/website_root.js b/addons/website/static/src/js/content/website_root.js index f7021f5cd06..c4e1a02373b 100644 --- a/addons/website/static/src/js/content/website_root.js +++ b/addons/website/static/src/js/content/website_root.js @@ -8,7 +8,7 @@ import publicRootData from 'web.public.root'; import "web.zoomodoo"; import { FullscreenIndication } from '@website/js/widgets/fullscreen_indication'; -var WebsiteRoot = publicRootData.PublicRoot.extend(KeyboardNavigationMixin, { +export const WebsiteRoot = publicRootData.PublicRoot.extend(KeyboardNavigationMixin, { events: _.extend({}, KeyboardNavigationMixin.events, publicRootData.PublicRoot.prototype.events || {}, { 'click .js_change_lang': '_onLangChangeClick', 'click .js_publish_management .js_publish_btn': '_onPublishBtnClick', diff --git a/addons/website/static/src/js/content/website_root_instance.js b/addons/website/static/src/js/content/website_root_instance.js index fbbefb03651..63a62c2f4a8 100644 --- a/addons/website/static/src/js/content/website_root_instance.js +++ b/addons/website/static/src/js/content/website_root_instance.js @@ -1,26 +1,6 @@ -odoo.define('root.widget', function (require) { -'use strict'; +/** @odoo-module alias=root.widget */ -const AbstractService = require('web.AbstractService'); -const env = require('web.public_env'); -var lazyloader = require('web.public.lazyloader'); -var websiteRootData = require('website.root'); +import { createPublicRoot } from "@web/legacy/js/public/public_root"; +import { WebsiteRoot } from "./website_root"; -/** - * Configure Owl with the public env - */ -owl.config.mode = env.isDebug() ? "dev" : "prod"; -owl.Component.env = env; - -/** - * Deploy services in the env - */ -AbstractService.prototype.deployServices(env); - -var websiteRoot = new websiteRootData.WebsiteRoot(null); -return lazyloader.allScriptsLoaded.then(function () { - return websiteRoot.attachTo(document.body).then(function () { - return websiteRoot; - }); -}); -}); +export default createPublicRoot(WebsiteRoot);