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);