diff --git a/addons/account/__manifest__.py b/addons/account/__manifest__.py index 48ca3195fc0..5518bce7bba 100644 --- a/addons/account/__manifest__.py +++ b/addons/account/__manifest__.py @@ -100,6 +100,7 @@ You could use this simplified accounting in case you work with an (external) acc 'account/static/src/js/legacy_account_move_form.js', 'account/static/src/components/**/*', 'account/static/src/js/tours/account.js', + 'account/static/src/xml/**/*', ], 'web.assets_frontend': [ 'account/static/src/js/account_portal_sidebar.js', @@ -111,10 +112,6 @@ You could use this simplified accounting in case you work with an (external) acc 'web.qunit_suite_tests': [ 'account/static/tests/*.js', ], - 'web.assets_qweb': [ - 'account/static/src/xml/**/*', - 'account/static/src/components/**/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/base_automation/__manifest__.py b/addons/base_automation/__manifest__.py index 12b7cf164f2..2b2c2e980f5 100644 --- a/addons/base_automation/__manifest__.py +++ b/addons/base_automation/__manifest__.py @@ -22,11 +22,9 @@ trigger an automatic reminder email. 'views/base_automation_views.xml', ], 'assets': { - 'web.assets_qweb': [ - 'base_automation/static/src/xml/*.xml', - ], 'web.assets_backend': [ 'base_automation/static/src/js/**/*', + 'base_automation/static/src/xml/*.xml', ], 'web.qunit_suite_tests': [ 'base_automation/static/tests/**/*', diff --git a/addons/base_import/__manifest__.py b/addons/base_import/__manifest__.py index 5a0f33b46ca..6afb4288e83 100644 --- a/addons/base_import/__manifest__.py +++ b/addons/base_import/__manifest__.py @@ -30,13 +30,11 @@ Re-implement Odoo's file import system: 'security/ir.model.access.csv', ], 'assets': { - 'web.assets_qweb': [ - 'base_import/static/src/**/*.xml', - ], 'web.assets_backend': [ 'base_import/static/lib/javascript-state-machine/state-machine.js', 'base_import/static/src/**/*.scss', 'base_import/static/src/**/*.js', + 'base_import/static/src/**/*.xml', ], 'web.qunit_suite_tests': [ 'base_import/static/tests/**/*', diff --git a/addons/board/__manifest__.py b/addons/board/__manifest__.py index c9b8dae28d5..5cb23ba5b2a 100644 --- a/addons/board/__manifest__.py +++ b/addons/board/__manifest__.py @@ -23,13 +23,11 @@ Allows users to create custom dashboard. 'web.assets_backend': [ 'board/static/src/**/*.scss', 'board/static/src/**/*.js', + 'board/static/src/**/*.xml', ], 'web.qunit_suite_tests': [ 'board/static/tests/**/*', ], - 'web.assets_qweb': [ - 'board/static/src/**/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/calendar/__manifest__.py b/addons/calendar/__manifest__.py index 5b64740b2e9..db175f20256 100644 --- a/addons/calendar/__manifest__.py +++ b/addons/calendar/__manifest__.py @@ -45,20 +45,18 @@ If you need to manage your meetings, you should install the CRM module. 'web.assets_backend': [ 'calendar/static/src/scss/calendar.scss', 'calendar/static/src/js/base_calendar.js', + 'calendar/static/src/xml/base_calendar.xml', 'calendar/static/src/js/calendar_renderer.js', 'calendar/static/src/js/calendar_controller.js', 'calendar/static/src/js/calendar_model.js', 'calendar/static/src/js/calendar_view.js', 'calendar/static/src/js/services/calendar_notification_service.js', 'calendar/static/src/views/**/*', + 'calendar/static/src/components/*/*.xml', ], 'web.qunit_suite_tests': [ 'calendar/static/tests/**/*', ], - 'web.assets_qweb': [ - 'calendar/static/src/xml/base_calendar.xml', - 'calendar/static/src/components/*/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/crm/__manifest__.py b/addons/crm/__manifest__.py index a247d683053..c97b1722055 100644 --- a/addons/crm/__manifest__.py +++ b/addons/crm/__manifest__.py @@ -73,10 +73,6 @@ 'mail.assets_messaging': [ 'crm/static/src/models/*.js', ], - 'web.assets_qweb': [ - 'crm/static/src/xml/crm_kanban.xml', - 'crm/static/src/xml/forecast_kanban.xml', - ], 'web.assets_backend': [ 'crm/static/src/js/crm_form.js', 'crm/static/src/js/crm_kanban.js', @@ -84,6 +80,8 @@ 'crm/static/src/js/tours/crm.js', 'crm/static/src/scss/crm.scss', 'crm/static/src/scss/crm_team_member_views.scss', + 'crm/static/src/xml/crm_kanban.xml', + 'crm/static/src/xml/forecast_kanban.xml', ], 'web.assets_tests': [ 'crm/static/tests/tours/**/*', diff --git a/addons/crm_iap_mine/__manifest__.py b/addons/crm_iap_mine/__manifest__.py index c00386253a3..1ba5163a420 100644 --- a/addons/crm_iap_mine/__manifest__.py +++ b/addons/crm_iap_mine/__manifest__.py @@ -27,8 +27,6 @@ 'assets': { 'web.assets_backend': [ 'crm_iap_mine/static/src/js/**/*', - ], - 'web.assets_qweb': [ 'crm_iap_mine/static/src/xml/**/*', ], }, diff --git a/addons/data_recycle/__manifest__.py b/addons/data_recycle/__manifest__.py index d1acdf7f32e..8de01188115 100644 --- a/addons/data_recycle/__manifest__.py +++ b/addons/data_recycle/__manifest__.py @@ -22,8 +22,6 @@ 'assets': { 'web.assets_backend': [ 'data_recycle/static/src/views/*.js', - ], - 'web.assets_qweb': [ 'data_recycle/static/src/views/*.xml', ], }, diff --git a/addons/event/__manifest__.py b/addons/event/__manifest__.py index f33b5a6f7bd..a8b9be90e0d 100644 --- a/addons/event/__manifest__.py +++ b/addons/event/__manifest__.py @@ -50,13 +50,11 @@ Key Features 'web.assets_backend': [ 'event/static/src/scss/event.scss', 'event/static/src/icon_selection_field/icon_selection_field.js', + 'event/static/src/icon_selection_field/icon_selection_field.xml', ], 'web.assets_common': [ 'event/static/src/js/tours/**/*', ], - 'web.assets_qweb': [ - 'event/static/src/**/*', - ], 'web.report_assets_common': [ '/event/static/src/scss/event_foldable_badge_report.scss', '/event/static/src/scss/event_full_page_ticket_report.scss', diff --git a/addons/google_calendar/__manifest__.py b/addons/google_calendar/__manifest__.py index ff737785bbc..48c43f00e17 100644 --- a/addons/google_calendar/__manifest__.py +++ b/addons/google_calendar/__manifest__.py @@ -21,6 +21,7 @@ 'google_calendar/static/src/js/google_calendar_popover.js', 'google_calendar/static/src/js/google_calendar.js', 'google_calendar/static/src/scss/google_calendar.scss', + 'google_calendar/static/src/xml/*.xml', ], 'web.qunit_suite_tests': [ 'google_calendar/static/tests/**/*', @@ -28,9 +29,6 @@ 'web.qunit_mobile_suite_tests': [ 'google_calendar/static/tests/mock_server.js', ], - 'web.assets_qweb': [ - 'google_calendar/static/src/xml/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/google_recaptcha/__manifest__.py b/addons/google_recaptcha/__manifest__.py index 234e1349954..6eb476eddd1 100644 --- a/addons/google_recaptcha/__manifest__.py +++ b/addons/google_recaptcha/__manifest__.py @@ -16,6 +16,7 @@ 'web.assets_frontend': [ 'google_recaptcha/static/src/scss/recaptcha.scss', 'google_recaptcha/static/src/js/recaptcha.js', + 'google_recaptcha/static/src/xml/recaptcha.xml', ], }, 'license': 'LGPL-3', diff --git a/addons/google_recaptcha/static/src/js/recaptcha.js b/addons/google_recaptcha/static/src/js/recaptcha.js index 46ec368e6e3..4d7e876d1d8 100644 --- a/addons/google_recaptcha/static/src/js/recaptcha.js +++ b/addons/google_recaptcha/static/src/js/recaptcha.js @@ -1,10 +1,10 @@ odoo.define('google_recaptcha.ReCaptchaV3', function (require) { "use strict"; -const ajax = require('web.ajax'); const Class = require('web.Class'); const core = require('web.core'); const { session } = require('@web/session'); +const { loadJS } = require('@web/core/assets'); const _t = core._t; @@ -22,7 +22,7 @@ const ReCaptcha = Class.extend({ */ loadLibs: function () { if (this._publicKey) { - this._recaptchaReady = ajax.loadJS(`https://www.recaptcha.net/recaptcha/api.js?render=${this._publicKey}`) + this._recaptchaReady = loadJS(`https://www.recaptcha.net/recaptcha/api.js?render=${this._publicKey}`) .then(() => new Promise(resolve => window.grecaptcha.ready(() => resolve()))); return this._recaptchaReady.then(() => !!document.querySelector('.grecaptcha-badge')); } diff --git a/addons/hr/__manifest__.py b/addons/hr/__manifest__.py index c36d371ccee..f170ee9c8fd 100644 --- a/addons/hr/__manifest__.py +++ b/addons/hr/__manifest__.py @@ -55,16 +55,12 @@ 'web.assets_backend': [ 'hr/static/src/mixins/*.js', 'hr/static/src/views/**/*.js', - 'hr/static/src/components/**/*.js', - 'hr/static/src/components/**/*.scss', + 'hr/static/src/components/**/*', 'hr/static/src/user_menu/*.js', 'hr/static/src/scss/hr.scss', 'hr/static/src/js/m2x_avatar_employee.js', 'hr/static/src/js/standalone_m2o_avatar_employee.js', 'hr/static/src/js/work_permit_upload.js', - ], - 'web.assets_qweb': [ - 'hr/static/src/components/**/*.xml', 'hr/static/src/xml/*.xml', ], 'web.qunit_suite_tests': [ diff --git a/addons/hr_attendance/__manifest__.py b/addons/hr_attendance/__manifest__.py index b0cf465a82b..2cc8850ce2f 100644 --- a/addons/hr_attendance/__manifest__.py +++ b/addons/hr_attendance/__manifest__.py @@ -35,13 +35,11 @@ actions(Check in/Check out) performed by them. 'assets': { 'web.assets_backend': [ 'hr_attendance/static/src/**/*', + 'hr_attendance/static/src/xml/**/*', ], 'web.qunit_suite_tests': [ ('after', 'web/static/tests/legacy/views/kanban_tests.js', 'hr_attendance/static/tests/hr_attendance_tests.js'), ], - 'web.assets_qweb': [ - 'hr_attendance/static/src/xml/**/*', - ], }, 'license': 'LGPL-3', } diff --git a/addons/hr_contract/__manifest__.py b/addons/hr_contract/__manifest__.py index f497354bfdd..741c249b614 100644 --- a/addons/hr_contract/__manifest__.py +++ b/addons/hr_contract/__manifest__.py @@ -35,11 +35,7 @@ You can assign several contracts per employee. 'application': True, 'assets': { 'web.assets_backend': [ - 'hr_contract/static/src/scss/*', - 'hr_contract/static/src/js/*', - ], - 'web.assets_qweb': [ - 'hr_contract/static/src/xml/*', + 'hr_contract/static/src/**/*', ], }, 'license': 'LGPL-3', diff --git a/addons/hr_expense/__manifest__.py b/addons/hr_expense/__manifest__.py index 0504030081b..132262cccd8 100644 --- a/addons/hr_expense/__manifest__.py +++ b/addons/hr_expense/__manifest__.py @@ -55,19 +55,17 @@ This module also uses analytic accounting and is compatible with the invoice on 'assets': { 'web.assets_backend': [ 'hr_expense/static/src/components/*.js', + 'hr_expense/static/src/components/*.xml', 'hr_expense/static/src/mixins/*.js', 'hr_expense/static/src/views/*.js', + 'hr_expense/static/src/views/*.xml', 'hr_expense/static/src/scss/hr_expense.scss', + 'hr_expense/static/src/xml/**/*', ], 'web.assets_tests': [ 'hr_expense/static/src/js/tours/hr_expense.js', 'hr_expense/static/tests/tours/expense_upload_tours.js', ], - 'web.assets_qweb': [ - 'hr_expense/static/src/components/*.xml', - 'hr_expense/static/src/views/*.xml', - 'hr_expense/static/src/xml/**/*', - ], }, 'license': 'LGPL-3', } diff --git a/addons/hr_fleet/__manifest__.py b/addons/hr_fleet/__manifest__.py index 86ff8f97cde..7fa9ce07a0a 100644 --- a/addons/hr_fleet/__manifest__.py +++ b/addons/hr_fleet/__manifest__.py @@ -20,8 +20,6 @@ 'assets': { 'web.assets_backend': [ 'hr_fleet/static/src/js/attachment_kanban.js', - ], - 'web.assets_qweb': [ 'hr_fleet/static/src/xml/attachment_kanban.xml', ], }, diff --git a/addons/hr_holidays/__manifest__.py b/addons/hr_holidays/__manifest__.py index e08ad3d6e81..8de38cc3759 100644 --- a/addons/hr_holidays/__manifest__.py +++ b/addons/hr_holidays/__manifest__.py @@ -71,12 +71,17 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p 'web.assets_backend': [ 'hr_holidays/static/src/js/**/*.js', 'hr_holidays/static/src/components/*/*.scss', + 'hr_holidays/static/src/components/*/*.xml', 'hr_holidays/static/src/dashboard/**/*.js', 'hr_holidays/static/src/dashboard/**/*.scss', + 'hr_holidays/static/src/dashboard/**/*.xml', 'hr_holidays/static/src/leave_stats/**/*.js', + 'hr_holidays/static/src/leave_stats/**/*.xml', 'hr_holidays/static/src/scss/*.scss', + 'hr_holidays/static/src/xml/**/*.xml', 'hr_holidays/static/src/tours/*.js', 'hr_holidays/static/src/radio_image_field/*.js', + 'hr_holidays/static/src/radio_image_field/*.xml', ], 'web.tests_assets': [ 'hr_holidays/static/tests/helpers/**/*', @@ -84,13 +89,6 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p 'web.qunit_suite_tests': [ 'hr_holidays/static/tests/qunit_suite_tests/**/*.js', ], - 'web.assets_qweb': [ - 'hr_holidays/static/src/components/*/*.xml', - 'hr_holidays/static/src/dashboard/**/*.xml', - 'hr_holidays/static/src/leave_stats/**/*.xml', - 'hr_holidays/static/src/xml/**/*.xml', - 'hr_holidays/static/src/radio_image_field/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/hr_holidays_attendance/__manifest__.py b/addons/hr_holidays_attendance/__manifest__.py index 4415b462667..33851c54c6a 100644 --- a/addons/hr_holidays_attendance/__manifest__.py +++ b/addons/hr_holidays_attendance/__manifest__.py @@ -23,7 +23,7 @@ Convert employee's extra hours to leave allocations. 'data/hr_holidays_attendance_demo.xml', ], 'assets': { - 'web.assets_qweb': [ + 'web.assets_backend': [ 'hr_holidays_attendance/static/src/xml/time_off_calendar.xml', ], }, diff --git a/addons/hr_org_chart/__manifest__.py b/addons/hr_org_chart/__manifest__.py index d4d078e4df6..ba4038c1888 100644 --- a/addons/hr_org_chart/__manifest__.py +++ b/addons/hr_org_chart/__manifest__.py @@ -23,15 +23,11 @@ This module extend the employee form with a organizational chart. 'hr_org_chart/static/src/scss/variables.scss', ], 'web.assets_backend': [ - 'hr_org_chart/static/src/fields/*.scss', - 'hr_org_chart/static/src/fields/*.js', + 'hr_org_chart/static/src/fields/*', ], 'web.qunit_suite_tests': [ 'hr_org_chart/static/tests/**/*', ], - 'web.assets_qweb': [ - 'hr_org_chart/static/src/fields/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/hr_skills/__manifest__.py b/addons/hr_skills/__manifest__.py index c5b9e2d7de3..4b6348431fe 100644 --- a/addons/hr_skills/__manifest__.py +++ b/addons/hr_skills/__manifest__.py @@ -34,13 +34,9 @@ This module introduces skills and resumé management for employees. 'application': True, 'assets': { 'web.assets_backend': [ - 'hr_skills/static/src/fields/*.js', - 'hr_skills/static/src/fields/*.scss', - 'hr_skills/static/src/views/*.js', - ], - 'web.assets_qweb': [ 'hr_skills/static/src/fields/skills_one2many.xml', - 'hr_skills/static/src/fields/*.xml', + 'hr_skills/static/src/fields/*', + 'hr_skills/static/src/views/*.js', 'hr_skills/static/src/xml/**/*', ], 'web.assets_tests': [ diff --git a/addons/hr_skills_slides/__manifest__.py b/addons/hr_skills_slides/__manifest__.py index dc32ead25ba..eefdf6a959a 100644 --- a/addons/hr_skills_slides/__manifest__.py +++ b/addons/hr_skills_slides/__manifest__.py @@ -23,8 +23,6 @@ This module add completed courses to resumé for employees. 'assets': { 'web.assets_backend': [ 'hr_skills_slides/static/src/scss/**/*', - ], - 'web.assets_qweb': [ 'hr_skills_slides/static/src/xml/**/*', ], }, diff --git a/addons/hr_skills_survey/__manifest__.py b/addons/hr_skills_survey/__manifest__.py index d18241e4832..1b4d617c78f 100644 --- a/addons/hr_skills_survey/__manifest__.py +++ b/addons/hr_skills_survey/__manifest__.py @@ -20,7 +20,7 @@ This module adds certification to resumé for employees. ], 'auto_install': True, 'assets': { - 'web.assets_qweb': [ + 'web.assets_backend': [ 'hr_skills_survey/static/src/xml/**/*', ], }, diff --git a/addons/hr_timesheet/__manifest__.py b/addons/hr_timesheet/__manifest__.py index 6ef99c523c1..1e7e722bbe0 100644 --- a/addons/hr_timesheet/__manifest__.py +++ b/addons/hr_timesheet/__manifest__.py @@ -48,9 +48,8 @@ up a management by affair. 'uninstall_hook': '_uninstall_hook', 'assets': { 'web.assets_backend': [ - 'hr_timesheet/static/src/**/*.scss', - 'hr_timesheet/static/src/**/*.js', - ('remove', 'hr_timesheet/static/src/js/*_legacy.js') + 'hr_timesheet/static/src/**/*', + ('remove', 'hr_timesheet/static/src/js/*_legacy.js'), ], "web.assets_backend_legacy_lazy": [ 'hr_timesheet/static/src/js/*_legacy.js', diff --git a/addons/hr_work_entry/__manifest__.py b/addons/hr_work_entry/__manifest__.py index a500bc89b27..100194e0d2a 100644 --- a/addons/hr_work_entry/__manifest__.py +++ b/addons/hr_work_entry/__manifest__.py @@ -19,7 +19,7 @@ 'views/resource_calendar_views.xml', ], 'assets': { - 'web.assets_qweb': [ + 'web.assets_backend': [ 'hr_work_entry/static/**/*', ], }, diff --git a/addons/iap/__manifest__.py b/addons/iap/__manifest__.py index 45b8d4b9804..8a7a8445d59 100644 --- a/addons/iap/__manifest__.py +++ b/addons/iap/__manifest__.py @@ -23,13 +23,11 @@ to support In-App purchases inside Odoo. """, 'assets': { 'web.assets_backend': [ 'iap/static/src/**/*.js', + 'iap/static/src/**/*.xml', ], 'web.tests_assets': [ 'iap/static/tests/**/*', ], - 'web.assets_qweb': [ - 'iap/static/src/**/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/im_livechat/__manifest__.py b/addons/im_livechat/__manifest__.py index 5a9fa271dcf..f5c85a73204 100644 --- a/addons/im_livechat/__manifest__.py +++ b/addons/im_livechat/__manifest__.py @@ -67,6 +67,7 @@ Help your customers with this chat, and analyse their feedback. 'im_livechat/static/src/components/*/*.js', 'im_livechat/static/src/scss/im_livechat_history.scss', 'im_livechat/static/src/scss/im_livechat_form.scss', + 'im_livechat/static/src/components/*/*.xml', ], 'web.tests_assets': [ 'im_livechat/static/tests/helpers/**/*.js', @@ -78,9 +79,6 @@ Help your customers with this chat, and analyse their feedback. 'web.assets_tests': [ 'im_livechat/static/tests/tours/**/*', ], - 'web.assets_qweb': [ - 'im_livechat/static/src/components/*/*.xml', - ], 'mail.assets_messaging': [ 'im_livechat/static/src/legacy/models/*', 'im_livechat/static/src/legacy/widgets/*', @@ -108,7 +106,6 @@ Help your customers with this chat, and analyse their feedback. 'web/static/src/legacy/js/promise_extension.js', 'web/static/src/boot.js', 'web/static/src/legacy/legacy_component.js', - 'web/static/src/core/assets.js', 'web/static/src/core/browser/browser.js', 'web/static/src/core/browser/feature_detection.js', 'web/static/src/core/dialog/dialog.js', @@ -122,6 +119,7 @@ Help your customers with this chat, and analyse their feedback. 'web/static/src/core/l10n/translation.js', 'web/static/src/core/main_components_container.js', 'web/static/src/core/network/rpc_service.js', + 'web/static/src/core/assets.js', 'web/static/src/core/notifications/notification.js', 'web/static/src/core/notifications/notification_container.js', 'web/static/src/core/notifications/notification_service.js', diff --git a/addons/im_livechat/static/src/js/ajax_external.js b/addons/im_livechat/static/src/js/ajax_external.js index debe12492bc..1ef672b8ec2 100644 --- a/addons/im_livechat/static/src/js/ajax_external.js +++ b/addons/im_livechat/static/src/js/ajax_external.js @@ -1,12 +1,12 @@ /** @odoo-module **/ -import ajax from 'web.ajax'; +import { assets } from "@web/core/assets"; /** * This file should be used in the context of an external widget loading (e.g: live chat in a non-Odoo website) * It overrides the 'loadJS' method that is supposed to load additional scripts, based on a relative URL (e.g: '/web/webclient/locale/en_US') * As we're not in an Odoo website context, the calls will not work, and we avoid a 404 request. */ -ajax.loadJS = function (url) { +assets.loadJS = function (url) { console.log('Tried to load the following script on an external website: ' + url); }; diff --git a/addons/im_livechat/static/src/services/public_livechat_service.js b/addons/im_livechat/static/src/services/public_livechat_service.js index 181d0aa08cc..1b1c06ba240 100644 --- a/addons/im_livechat/static/src/services/public_livechat_service.js +++ b/addons/im_livechat/static/src/services/public_livechat_service.js @@ -11,7 +11,9 @@ export const publicLivechatService = { return { mountLivechatButton() { const livechatButton = new LivechatButton(rootWidget, messaging); - livechatButton.appendTo(document.body); + livechatButton.appendTo(document.body).catch(error => { + console.info("Can't load 'LivechatButton' because:", error); + }); return livechatButton; }, }; diff --git a/addons/l10n_co_pos/__manifest__.py b/addons/l10n_co_pos/__manifest__.py index f5b9b29c0b4..6a775c40391 100644 --- a/addons/l10n_co_pos/__manifest__.py +++ b/addons/l10n_co_pos/__manifest__.py @@ -18,8 +18,6 @@ 'assets': { 'point_of_sale.assets': [ 'l10n_co_pos/static/src/js/**/*', - ], - 'web.assets_qweb': [ 'l10n_co_pos/static/src/xml/**/*', ], }, diff --git a/addons/l10n_fr_pos_cert/__manifest__.py b/addons/l10n_fr_pos_cert/__manifest__.py index 46df5dde437..84d42429a42 100644 --- a/addons/l10n_fr_pos_cert/__manifest__.py +++ b/addons/l10n_fr_pos_cert/__manifest__.py @@ -40,8 +40,6 @@ The module adds following features: 'point_of_sale.assets': [ 'l10n_fr_pos_cert/static/src/js/**/*', 'l10n_fr_pos_cert/static/src/css/pos.css', - ], - 'web.assets_qweb': [ 'l10n_fr_pos_cert/static/src/xml/**/*', ], }, diff --git a/addons/l10n_gcc_pos/__manifest__.py b/addons/l10n_gcc_pos/__manifest__.py index 147748c0c2c..34c3e0b8c23 100644 --- a/addons/l10n_gcc_pos/__manifest__.py +++ b/addons/l10n_gcc_pos/__manifest__.py @@ -11,12 +11,10 @@ GCC POS Localization 'license': 'LGPL-3', 'depends': ['point_of_sale', 'l10n_gcc_invoice'], 'assets': { - 'web.assets_qweb': [ - 'l10n_gcc_pos/static/src/xml/OrderReceipt.xml', - ], 'point_of_sale.assets': [ 'l10n_gcc_pos/static/src/js/OrderReceipt.js', 'l10n_gcc_pos/static/src/css/OrderReceipt.css', + 'l10n_gcc_pos/static/src/xml/OrderReceipt.xml', ] }, 'auto_install': True, diff --git a/addons/l10n_in_pos/__manifest__.py b/addons/l10n_in_pos/__manifest__.py index 3e882e777fc..c3e64818117 100644 --- a/addons/l10n_in_pos/__manifest__.py +++ b/addons/l10n_in_pos/__manifest__.py @@ -18,8 +18,6 @@ 'assets': { 'point_of_sale.assets': [ 'l10n_in_pos/static/src/js/**/*', - ], - 'web.assets_qweb': [ 'l10n_in_pos/static/src/xml/**/*', ], }, diff --git a/addons/l10n_sa_pos/__manifest__.py b/addons/l10n_sa_pos/__manifest__.py index df99fcadc62..25e75d33c35 100644 --- a/addons/l10n_sa_pos/__manifest__.py +++ b/addons/l10n_sa_pos/__manifest__.py @@ -14,12 +14,10 @@ K.S.A. POS Localization 'l10n_sa', ], 'assets': { - 'web.assets_qweb': [ - 'l10n_sa_pos/static/src/xml/OrderReceipt.xml', - ], 'point_of_sale.assets': [ 'web/static/lib/zxing-library/zxing-library.js', 'l10n_sa_pos/static/src/js/models.js', + 'l10n_sa_pos/static/src/xml/OrderReceipt.xml', ] }, 'auto_install': True, diff --git a/addons/loyalty/__manifest__.py b/addons/loyalty/__manifest__.py index 50e33b4a69e..59ddcc94418 100644 --- a/addons/loyalty/__manifest__.py +++ b/addons/loyalty/__manifest__.py @@ -30,8 +30,6 @@ 'loyalty/static/src/js/loyalty_list_view.js', 'loyalty/static/src/scss/loyalty.scss', 'loyalty/static/src/js/filterable_selection_field/filterable_selection_field.js', - ], - 'web.assets_qweb': [ 'loyalty/static/src/xml/loyalty_templates.xml', ], 'web.qunit_suite_tests': [ diff --git a/addons/lunch/__manifest__.py b/addons/lunch/__manifest__.py index 3d4c5589bc9..78f22addc75 100644 --- a/addons/lunch/__manifest__.py +++ b/addons/lunch/__manifest__.py @@ -42,10 +42,9 @@ If you want to save your employees' time and avoid them to always have coins in 'application': True, 'assets': { 'web.assets_backend': [ - 'lunch/static/src/components/*.js', - 'lunch/static/src/components/*.scss', + 'lunch/static/src/components/*', 'lunch/static/src/mixins/*.js', - 'lunch/static/src/views/*.js', + 'lunch/static/src/views/*', 'lunch/static/src/scss/lunch_view.scss', 'lunch/static/src/scss/lunch_kanban.scss', ], @@ -55,10 +54,6 @@ If you want to save your employees' time and avoid them to always have coins in 'web.qunit_suite_tests': [ 'lunch/static/tests/lunch_kanban_tests.js', ], - 'web.assets_qweb': [ - 'lunch/static/src/components/*.xml', - 'lunch/static/src/views/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/mail/__manifest__.py b/addons/mail/__manifest__.py index 1fd5aa1a154..e4e5898d1e4 100644 --- a/addons/mail/__manifest__.py +++ b/addons/mail/__manifest__.py @@ -191,11 +191,14 @@ For more specific needs, you may also assign custom-defined actions 'mail/static/src/components/notification_list/notification_list_item.scss', 'mail/static/src/js/**/*.js', 'mail/static/src/fields/*.js', + 'mail/static/src/fields/*.xml', 'mail/static/src/utils/*.js', 'mail/static/src/scss/*.scss', + 'mail/static/src/xml/*.xml', 'mail/static/src/component_hooks/*.js', 'mail/static/src/components/*/*.js', 'mail/static/src/components/*/*.scss', + 'mail/static/src/components/*/*.xml', ('include', 'mail.assets_messaging'), 'mail/static/src/services/*.js', 'mail/static/src/views/**/*.js', @@ -223,11 +226,6 @@ For more specific needs, you may also assign custom-defined actions 'web.qunit_mobile_suite_tests': [ 'mail/static/tests/qunit_mobile_suite_tests/**/*.js', ], - 'web.assets_qweb': [ - 'mail/static/src/xml/*.xml', - 'mail/static/src/fields/*.xml', - 'mail/static/src/components/*/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/mail/models/ir_http.py b/addons/mail/models/ir_http.py index e8d446aa040..2c99c97c2b3 100644 --- a/addons/mail/models/ir_http.py +++ b/addons/mail/models/ir_http.py @@ -3,7 +3,6 @@ import odoo from odoo import models from odoo.http import request -from odoo.addons.web.controllers.utils import HomeStaticTemplateHelpers class IrHttp(models.AbstractModel): @@ -14,8 +13,6 @@ class IrHttp(models.AbstractModel): result = super(IrHttp, self).session_info() if self.env.user._is_internal(): result['notification_type'] = user.notification_type - assets_discuss_public_hash = HomeStaticTemplateHelpers.get_qweb_templates_checksum(debug=request.session.debug, bundle='mail.assets_discuss_public') - result['cache_hashes']['assets_discuss_public'] = assets_discuss_public_hash guest = self.env.context.get('guest') if not request.session.uid and guest: user_context = {'lang': guest.lang} diff --git a/addons/mail/static/src/public/discuss_public_boot.js b/addons/mail/static/src/public/discuss_public_boot.js index 792664030ed..db40fc016a3 100644 --- a/addons/mail/static/src/public/discuss_public_boot.js +++ b/addons/mail/static/src/public/discuss_public_boot.js @@ -8,7 +8,6 @@ import { DiscussPublicViewContainer } from '@mail/components/discuss_public_view import { PopoverManagerContainer } from '@mail/components/popover_manager_container/popover_manager_container'; import { messagingService } from '@mail/services/messaging_service'; -import { processTemplates } from '@web/core/assets'; import { MainComponentsContainer } from '@web/core/main_components_container'; import { registry } from '@web/core/registry'; import { makeEnv, startServices } from '@web/env'; @@ -21,6 +20,7 @@ import { mapLegacyEnvToWowlEnv, } from '@web/legacy/utils'; import { session } from '@web/session'; +import { templates } from '@web/core/assets'; import * as AbstractService from 'web.AbstractService'; import * as legacyEnv from 'web.env'; @@ -69,10 +69,7 @@ Component.env = legacyEnv; isReady: false, }); const env = makeEnv(); - const [, templates] = await Promise.all([ - startServices(env), - odoo.loadTemplatesPromise.then(processTemplates), - ]); + await startServices(env); mapLegacyEnvToWowlEnv(Component.env, env); odoo.isReady = true; await mount(MainComponentsContainer, document.body, { env, templates, dev: env.debug }); diff --git a/addons/mail/views/discuss_public_templates.xml b/addons/mail/views/discuss_public_templates.xml index 22e90e0d2cf..b22ab14cd4f 100644 --- a/addons/mail/views/discuss_public_templates.xml +++ b/addons/mail/views/discuss_public_templates.xml @@ -15,7 +15,6 @@ csrf_token: "", debug: "", }; - odoo.loadTemplatesPromise = fetch(`/web/webclient/qweb/${odoo.__session_info__.cache_hashes.assets_discuss_public}?bundle=mail.assets_discuss_public`).then(doc => doc.text()); { const { user_context, cache_hashes } = odoo.__session_info__; fetch(`/web/webclient/translations/${cache_hashes.translations}?lang=${user_context.lang}`); diff --git a/addons/mass_mailing/__manifest__.py b/addons/mass_mailing/__manifest__.py index 78ec56e4a35..874eb9f2bb2 100644 --- a/addons/mass_mailing/__manifest__.py +++ b/addons/mass_mailing/__manifest__.py @@ -97,6 +97,8 @@ 'mass_mailing/static/src/js/mass_mailing_html_field.js', 'mass_mailing/static/src/js/mailing_mailing_view_form_full_width.js', 'mass_mailing/static/src/js/mailing_portal.js', + 'mass_mailing/static/src/xml/*.xml', + 'mass_mailing/static/src/views/*.xml', ], 'mass_mailing.assets_mail_themes': [ 'mass_mailing/static/src/scss/themes/**/*', @@ -122,10 +124,6 @@ 'mass_mailing/static/src/snippets/s_rating/options.js', 'mass_mailing/static/tests/mass_mailing_html_tests.js', ], - 'web.assets_qweb': [ - 'mass_mailing/static/src/xml/*.xml', - 'mass_mailing/static/src/views/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/mass_mailing/static/src/js/mass_mailing_html_field.js b/addons/mass_mailing/static/src/js/mass_mailing_html_field.js index a379ce314a3..1a2549f6474 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing_html_field.js +++ b/addons/mass_mailing/static/src/js/mass_mailing_html_field.js @@ -5,7 +5,7 @@ import { _lt } from "@web/core/l10n/translation"; import { standardFieldProps } from "@web/views/fields/standard_field_props"; import { initializeDesignTabCss } from "mass_mailing.design_constants"; import { toInline } from "web_editor.convertInline"; -import { loadLibs, loadXML } from "web.ajax"; +import { loadBundle } from "@web/core/assets"; import { qweb } from 'web.core'; import { useService } from "@web/core/utils/hooks"; import { buildQuery } from "web.rpc"; @@ -107,8 +107,7 @@ export class MassMailingHtmlField extends HtmlField { async startWysiwyg(...args) { await super.startWysiwyg(...args); - await loadXML('/mass_mailing/static/src/xml/mass_mailing.xml', qweb); - await loadLibs({ + await loadBundle({ jsLibs: [ '/mass_mailing/static/src/js/mass_mailing_link_dialog_fix.js', '/mass_mailing/static/src/js/mass_mailing_snippets.js', diff --git a/addons/microsoft_calendar/__manifest__.py b/addons/microsoft_calendar/__manifest__.py index 7bf9b3a6aad..21e4419f1ce 100644 --- a/addons/microsoft_calendar/__manifest__.py +++ b/addons/microsoft_calendar/__manifest__.py @@ -21,8 +21,6 @@ 'microsoft_calendar/static/src/js/microsoft_calendar_popover.js', 'microsoft_calendar/static/src/js/microsoft_calendar.js', 'microsoft_calendar/static/src/scss/microsoft_calendar.scss', - ], - 'web.assets_qweb': [ 'microsoft_calendar/static/src/xml/*.xml', ], }, diff --git a/addons/mrp/__manifest__.py b/addons/mrp/__manifest__.py index 5c0730b95d6..398395012d2 100644 --- a/addons/mrp/__manifest__.py +++ b/addons/mrp/__manifest__.py @@ -58,15 +58,11 @@ 'uninstall_hook': 'uninstall_hook', 'assets': { 'web.assets_backend': [ - 'mrp/static/src/**/*.js', - 'mrp/static/src/**/*.scss', + 'mrp/static/src/**/*', ], 'web.qunit_suite_tests': [ 'mrp/static/tests/**/*', ], - 'web.assets_qweb': [ - 'mrp/static/src/**/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/mrp_subcontracting/__manifest__.py b/addons/mrp_subcontracting/__manifest__.py index 958c4372997..1d9009276db 100644 --- a/addons/mrp_subcontracting/__manifest__.py +++ b/addons/mrp_subcontracting/__manifest__.py @@ -34,23 +34,12 @@ 'mrp_subcontracting/static/tests/tours/subcontracting_portal_tour.js', ], 'web.assets_backend': [ - 'mrp_subcontracting/static/src/components/**/*.js', - ], - 'web.assets_qweb': [ - 'mrp_subcontracting/static/**/*.xml', - ], - 'mrp_subcontracting.assets_qweb': [ - ('include', 'web.assets_qweb'), - 'mrp_subcontracting/static/src/subcontracting_portal/subcontracting_portal.xml', - 'mrp_subcontracting/static/src/subcontracting_portal/picking_form_controller.xml', + 'mrp_subcontracting/static/src/components/**/*', ], 'mrp_subcontracting.webclient': [ ('include', 'web.assets_backend'), ('remove', 'web/static/src/webclient/menus/*.js'), - 'mrp_subcontracting/static/src/subcontracting_portal/main.js', - 'mrp_subcontracting/static/src/subcontracting_portal/subcontracting_portal.js', - 'mrp_subcontracting/static/src/subcontracting_portal/picking_form_controller.js', - 'mrp_subcontracting/static/src/subcontracting_portal/move_list_view.js', + 'mrp_subcontracting/static/src/subcontracting_portal/*', 'web/static/src/start.js', 'web/static/src/legacy/legacy_setup.js', ], diff --git a/addons/mrp_subcontracting/controllers/portal.py b/addons/mrp_subcontracting/controllers/portal.py index c428cf02a54..6343fdadfdb 100644 --- a/addons/mrp_subcontracting/controllers/portal.py +++ b/addons/mrp_subcontracting/controllers/portal.py @@ -9,7 +9,6 @@ from odoo.http import request from odoo.exceptions import AccessError, MissingError from odoo.addons.portal.controllers import portal from odoo.addons.portal.controllers.portal import pager as portal_pager -from odoo.addons.web.controllers.utils import HomeStaticTemplateHelpers class CustomerPortal(portal.CustomerPortal): @@ -91,11 +90,9 @@ class CustomerPortal(portal.CustomerPortal): session_info = request.env['ir.http'].session_info() user_context = dict(request.env.context) if request.session.uid else {} mods = conf.server_wide_modules or [] - qweb_checksum = HomeStaticTemplateHelpers.get_qweb_templates_checksum(debug=request.session.debug, bundle="mrp_subcontracting.assets_qweb") lang = user_context.get("lang") translation_hash = request.env['ir.translation'].get_web_translations_hash(mods, lang) cache_hashes = { - "qweb": qweb_checksum, "translations": translation_hash, } production_company = picking.company_id diff --git a/addons/mrp_subcontracting/views/subcontracting_portal_templates.xml b/addons/mrp_subcontracting/views/subcontracting_portal_templates.xml index 29bd37c362a..0dfccf97bd8 100644 --- a/addons/mrp_subcontracting/views/subcontracting_portal_templates.xml +++ b/addons/mrp_subcontracting/views/subcontracting_portal_templates.xml @@ -70,7 +70,6 @@ diff --git a/addons/note/__manifest__.py b/addons/note/__manifest__.py index bb4ecebaee3..68446bef823 100644 --- a/addons/note/__manifest__.py +++ b/addons/note/__manifest__.py @@ -29,15 +29,12 @@ 'note/static/src/models/*.js', ], 'web.assets_backend': [ - 'note/static/src/components/*/*.js', + 'note/static/src/components/**/*', 'note/static/src/scss/note.scss', ], 'web.qunit_suite_tests': [ 'note/static/tests/**/*', ], - 'web.assets_qweb': [ - 'note/static/src/components/*/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/partner_autocomplete/__manifest__.py b/addons/partner_autocomplete/__manifest__.py index 8ff8337e075..72cdffb709b 100644 --- a/addons/partner_autocomplete/__manifest__.py +++ b/addons/partner_autocomplete/__manifest__.py @@ -28,6 +28,7 @@ 'partner_autocomplete/static/src/js/partner_autocomplete_fieldchar.js', 'partner_autocomplete/static/src/js/partner_autocomplete_many2one.js', 'partner_autocomplete/static/src/js/web_company_autocomplete.js', + 'partner_autocomplete/static/src/xml/**/*', ], 'web.tests_assets': [ 'partner_autocomplete/static/lib/**/*', @@ -35,9 +36,6 @@ 'web.qunit_suite_tests': [ 'partner_autocomplete/static/tests/**/*', ], - 'web.assets_qweb': [ - 'partner_autocomplete/static/src/xml/**/*', - ], }, 'license': 'LGPL-3', } diff --git a/addons/payment/__manifest__.py b/addons/payment/__manifest__.py index bbfd968c880..36a377a2903 100644 --- a/addons/payment/__manifest__.py +++ b/addons/payment/__manifest__.py @@ -41,6 +41,7 @@ 'payment/static/src/js/manage_form.js', 'payment/static/src/js/payment_form_mixin.js', 'payment/static/src/js/post_processing.js', + 'payment/static/src/xml/payment_post_processing.xml', ], 'web.assets_backend': [ 'payment/static/src/scss/payment_provider.scss', diff --git a/addons/payment/static/src/js/post_processing.js b/addons/payment/static/src/js/post_processing.js index 9af3d9eed61..f397cb7f306 100644 --- a/addons/payment/static/src/js/post_processing.js +++ b/addons/payment/static/src/js/post_processing.js @@ -13,7 +13,6 @@ odoo.define('payment.post_processing', function (require) { publicWidget.registry.PaymentPostProcessing = publicWidget.Widget.extend({ selector: 'div[name="o_payment_status"]', - xmlDependencies: ['/payment/static/src/xml/payment_post_processing.xml'], _pollCount: 0, diff --git a/addons/payment_authorize/static/src/js/payment_form.js b/addons/payment_authorize/static/src/js/payment_form.js index bd564fc0322..e1a23d80d71 100644 --- a/addons/payment_authorize/static/src/js/payment_form.js +++ b/addons/payment_authorize/static/src/js/payment_form.js @@ -3,7 +3,7 @@ odoo.define('payment_authorize.payment_form', require => { 'use strict'; const core = require('web.core'); - const ajax = require('web.ajax'); + const { loadJS } = require('@web/core/assets'); const checkoutForm = require('payment.checkout_form'); const manageForm = require('payment.manage_form'); @@ -102,7 +102,7 @@ odoo.define('payment_authorize.payment_form', require => { } this.authorizeInfo = providerInfo; }).then(() => { - ajax.loadJS(acceptJSUrl); + loadJS(acceptJSUrl); }).guardedCatch((error) => { error.event.preventDefault(); this._displayError( diff --git a/addons/point_of_sale/__manifest__.py b/addons/point_of_sale/__manifest__.py index eeb37400e5c..73e763c2afc 100644 --- a/addons/point_of_sale/__manifest__.py +++ b/addons/point_of_sale/__manifest__.py @@ -59,8 +59,7 @@ ## ## 1. When defining new component, put the .js files in `point_of_sale/static/src/js/` ## and the corresponding .xml files in `point_of_sale/static/src/xml/` - ## * POS is setup to automatically include the .xml files in `web.assets_qweb` and the `.js` files - ## * in `point_of_sale.assets`. + ## * POS is setup to automatically include the .xml and `.js` files in `point_of_sale.assets`. ## 2. When adding new tour tests, put the .js files in `point_of_sale/static/tests/tours/`. ## 3. When adding new qunit tests, put the .js files in `point_of_sale/static/tests/unit/`. ## @@ -84,9 +83,6 @@ 'web.assets_tests': [ 'point_of_sale/static/tests/tours/**/*', ], - 'web.assets_qweb': [ - 'point_of_sale/static/src/xml/**/*.xml', - ], #################################################### ## Exclusive POS Assets 1: For running the POS UI ## @@ -128,6 +124,7 @@ 'web_editor/static/lib/html2canvas.js', 'point_of_sale/static/src/js/**/*.js', 'web/static/lib/zxing-library/zxing-library.js', + 'point_of_sale/static/src/xml/**/*.xml', ], # This bundle contains the code responsible for starting the POS UI. # It is practically the entry point. diff --git a/addons/point_of_sale/static/src/js/Chrome.js b/addons/point_of_sale/static/src/js/Chrome.js index 908a836951e..d0d7039d4f3 100644 --- a/addons/point_of_sale/static/src/js/Chrome.js +++ b/addons/point_of_sale/static/src/js/Chrome.js @@ -1,7 +1,7 @@ odoo.define('point_of_sale.Chrome', function(require) { 'use strict'; - const { loadCSS } = require('web.ajax'); + const { loadCSS } = require('@web/core/assets'); const { useListener } = require("@web/core/utils/hooks"); const BarcodeParser = require('barcodes.BarcodeParser'); const PosComponent = require('point_of_sale.PosComponent'); diff --git a/addons/point_of_sale/views/pos_assets_index.xml b/addons/point_of_sale/views/pos_assets_index.xml index 833d8ad2897..ae8fcb34133 100644 --- a/addons/point_of_sale/views/pos_assets_index.xml +++ b/addons/point_of_sale/views/pos_assets_index.xml @@ -36,7 +36,6 @@ // Prevent the menu_service to load anything. In an ideal world, POS assets would only contain // what is genuinely necessary, and not the whole backend. odoo.loadMenusPromise = Promise.resolve(); - odoo.loadTemplatesPromise = fetch(`/web/webclient/qweb/${odoo.__session_info__.cache_hashes.qweb}?bundle=web.assets_qweb`).then(doc => doc.text()); diff --git a/addons/portal/__manifest__.py b/addons/portal/__manifest__.py index 3d11a22f5fe..46e14fff2ab 100644 --- a/addons/portal/__manifest__.py +++ b/addons/portal/__manifest__.py @@ -40,17 +40,16 @@ a dependency towards website editing and customization capabilities.""", 'portal/static/src/scss/portal.scss', 'portal/static/src/js/portal.js', 'portal/static/src/js/portal_chatter.js', + 'portal/static/src/xml/portal_chatter.xml', 'portal/static/src/js/portal_composer.js', 'portal/static/src/js/portal_signature.js', + 'portal/static/src/xml/portal_signature.xml', 'portal/static/src/js/portal_sidebar.js', + 'portal/static/src/xml/portal_security.xml', ], 'web.assets_tests': [ 'portal/static/tests/**/*', ], - 'web.assets_qweb': [ - 'portal/static/src/xml/portal_chatter.xml', - 'portal/static/src/xml/portal_signature.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/portal/static/src/js/portal.js b/addons/portal/static/src/js/portal.js index fff2910367d..37ac3e5a66b 100644 --- a/addons/portal/static/src/js/portal.js +++ b/addons/portal/static/src/js/portal.js @@ -4,7 +4,6 @@ odoo.define('portal.portal', function (require) { var publicWidget = require('web.public.widget'); const Dialog = require('web.Dialog'); const {_t, qweb} = require('web.core'); -const ajax = require('web.ajax'); const session = require('web.session'); publicWidget.registry.portalDetails = publicWidget.Widget.extend({ @@ -198,7 +197,6 @@ publicWidget.registry.NewAPIKeyButton = publicWidget.Widget.extend({ method: 'api_key_wizard', args: [session.user_id], })); - await ajax.loadXML('/portal/static/src/xml/portal_security.xml', qweb); const self = this; const d_description = new Dialog(self, { title: _t('New API Key'), @@ -304,7 +302,7 @@ function handleCheckIdentity(rpc, wrapped) { return r; } const check_id = r.res_id; - return ajax.loadXML('/portal/static/src/xml/portal_security.xml', qweb).then(() => new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { const d = new Dialog(null, { title: _t("Security Control"), $content: qweb.render('portal.identitycheck'), @@ -355,7 +353,7 @@ function handleCheckIdentity(rpc, wrapped) { }); }); d.open(); - })); + }); }); } return { diff --git a/addons/portal/static/src/js/portal_chatter.js b/addons/portal/static/src/js/portal_chatter.js index 26f404e5a0d..3a34228ece3 100644 --- a/addons/portal/static/src/js/portal_chatter.js +++ b/addons/portal/static/src/js/portal_chatter.js @@ -20,7 +20,6 @@ var _t = core._t; */ var PortalChatter = publicWidget.Widget.extend({ template: 'portal.Chatter', - xmlDependencies: ['/portal/static/src/xml/portal_chatter.xml'], events: { 'click .o_portal_chatter_pager_btn': '_onClickPager', 'click .o_portal_chatter_js_is_internal': 'async _onClickUpdateIsInternal', diff --git a/addons/portal/static/src/js/portal_composer.js b/addons/portal/static/src/js/portal_composer.js index 1622f34bda0..1925a8a4c2f 100644 --- a/addons/portal/static/src/js/portal_composer.js +++ b/addons/portal/static/src/js/portal_composer.js @@ -16,7 +16,6 @@ var _t = core._t; */ var PortalComposer = publicWidget.Widget.extend({ template: 'portal.Composer', - xmlDependencies: ['/portal/static/src/xml/portal_chatter.xml'], events: { 'change .o_portal_chatter_file_input': '_onFileInputChange', 'click .o_portal_chatter_attachment_btn': '_onAttachmentButtonClick', diff --git a/addons/portal/static/src/js/portal_signature.js b/addons/portal/static/src/js/portal_signature.js index eac46519172..cfb7a457323 100644 --- a/addons/portal/static/src/js/portal_signature.js +++ b/addons/portal/static/src/js/portal_signature.js @@ -15,7 +15,6 @@ var _t = core._t; */ var SignatureForm = publicWidget.Widget.extend({ template: 'portal.portal_signature', - xmlDependencies: ['/portal/static/src/xml/portal_signature.xml'], events: { 'click .o_portal_sign_submit': 'async _onClickSignSubmit', }, diff --git a/addons/portal_rating/__manifest__.py b/addons/portal_rating/__manifest__.py index 2ffa6deccc6..a751e41bb13 100644 --- a/addons/portal_rating/__manifest__.py +++ b/addons/portal_rating/__manifest__.py @@ -23,8 +23,11 @@ inclusion of rating directly within the customer portal discuss widget. 'web.assets_frontend': [ 'portal_rating/static/src/scss/portal_rating.scss', 'portal_rating/static/src/js/portal_chatter.js', + 'portal_rating/static/src/xml/portal_chatter.xml', 'portal_rating/static/src/js/portal_composer.js', 'portal_rating/static/src/js/portal_rating_composer.js', + 'portal_rating/static/src/xml/portal_rating_composer.xml', + 'portal_rating/static/src/xml/portal_tools.xml', ], }, 'license': 'LGPL-3', diff --git a/addons/portal_rating/static/src/js/portal_chatter.js b/addons/portal_rating/static/src/js/portal_chatter.js index c57b4faa179..caeb6667dbd 100644 --- a/addons/portal_rating/static/src/js/portal_chatter.js +++ b/addons/portal_rating/static/src/js/portal_chatter.js @@ -27,12 +27,6 @@ PortalChatter.include({ 'click .o_wrating_js_publisher_comment_submit': '_onClickPublisherCommentSubmit', 'click .o_wrating_js_publisher_comment_cancel': '_onClickPublisherCommentCancel', }), - xmlDependencies: (PortalChatter.prototype.xmlDependencies || []) - .concat([ - '/portal_rating/static/src/xml/portal_tools.xml', - '/portal_rating/static/src/xml/portal_chatter.xml' - ]), - /** * @constructor */ diff --git a/addons/portal_rating/static/src/js/portal_rating_composer.js b/addons/portal_rating/static/src/js/portal_rating_composer.js index 96139fb1be7..08e723a7430 100644 --- a/addons/portal_rating/static/src/js/portal_rating_composer.js +++ b/addons/portal_rating/static/src/js/portal_rating_composer.js @@ -19,12 +19,6 @@ const RatingPopupComposer = publicWidget.Widget.extend({ custom_events: { reload_rating_popup_composer: '_onReloadRatingPopupComposer', }, - xmlDependencies: [ - '/portal/static/src/xml/portal_chatter.xml', - '/portal_rating/static/src/xml/portal_chatter.xml', - '/portal_rating/static/src/xml/portal_tools.xml', - '/portal_rating/static/src/xml/portal_rating_composer.xml', - ], willStart: function (parent) { const def = this._super.apply(this, arguments); diff --git a/addons/pos_discount/__manifest__.py b/addons/pos_discount/__manifest__.py index ba31f26e74f..e35125e312a 100644 --- a/addons/pos_discount/__manifest__.py +++ b/addons/pos_discount/__manifest__.py @@ -23,8 +23,6 @@ discount to a customer. 'assets': { 'point_of_sale.assets': [ 'pos_discount/static/src/js/**/*', - ], - 'web.assets_qweb': [ 'pos_discount/static/src/xml/**/*', ], }, diff --git a/addons/pos_epson_printer/__manifest__.py b/addons/pos_epson_printer/__manifest__.py index 7c5bca007c7..778d142a801 100644 --- a/addons/pos_epson_printer/__manifest__.py +++ b/addons/pos_epson_printer/__manifest__.py @@ -22,8 +22,6 @@ Use Epson ePOS Printers without the IoT Box in the Point of Sale 'assets': { 'point_of_sale.assets': [ 'pos_epson_printer/static/src/js/**/*', - ], - 'web.assets_qweb': [ 'pos_epson_printer/static/src/xml/**/*', ], }, diff --git a/addons/pos_hr/__manifest__.py b/addons/pos_hr/__manifest__.py index 79bd54a2755..82945f8ecbf 100644 --- a/addons/pos_hr/__manifest__.py +++ b/addons/pos_hr/__manifest__.py @@ -31,13 +31,11 @@ The actual till still requires one user but an unlimited number of employees can 'pos_hr/static/src/js/CashierName.js', 'pos_hr/static/src/js/LoginScreen.js', 'pos_hr/static/src/js/PaymentScreen.js', + 'pos_hr/static/src/xml/**/*', ], 'web.assets_tests': [ 'pos_hr/static/tests/**/*', ], - 'web.assets_qweb': [ - 'pos_hr/static/src/xml/**/*', - ], }, 'license': 'LGPL-3', } diff --git a/addons/pos_loyalty/__manifest__.py b/addons/pos_loyalty/__manifest__.py index 2e613d159f8..ddaa682d047 100644 --- a/addons/pos_loyalty/__manifest__.py +++ b/addons/pos_loyalty/__manifest__.py @@ -37,13 +37,11 @@ 'pos_loyalty/static/src/js/ControlButtons/PromoCodeButton.js', 'pos_loyalty/static/src/js/ControlButtons/RewardButton.js', 'pos_loyalty/static/src/js/Popups/GiftCardPopup.js', + 'pos_loyalty/static/src/xml/**/*', ], 'web.assets_tests': [ 'pos_loyalty/static/src/js/tours/**/*', ], - 'web.assets_qweb': [ - 'pos_loyalty/static/src/xml/**/*', - ], }, 'license': 'LGPL-3', } diff --git a/addons/pos_mercury/__manifest__.py b/addons/pos_mercury/__manifest__.py index eeb331403d3..a94fca12f9e 100644 --- a/addons/pos_mercury/__manifest__.py +++ b/addons/pos_mercury/__manifest__.py @@ -42,8 +42,6 @@ following: 'pos_mercury/static/src/js/PaymentTransactionPopup.js', 'pos_mercury/static/src/js/ProductScreen.js', 'pos_mercury/static/src/css/pos_mercury.css', - ], - 'web.assets_qweb': [ 'pos_mercury/static/src/xml/**/*', ], }, diff --git a/addons/pos_restaurant/__manifest__.py b/addons/pos_restaurant/__manifest__.py index 5a075470505..b8b945f9ffa 100644 --- a/addons/pos_restaurant/__manifest__.py +++ b/addons/pos_restaurant/__manifest__.py @@ -33,6 +33,7 @@ This module adds several features to the Point of Sale that are specific to rest 'pos_restaurant/static/lib/**/*.js', 'pos_restaurant/static/src/js/**/*.js', ('after', 'point_of_sale/static/src/scss/pos.scss', 'pos_restaurant/static/src/scss/restaurant.scss'), + 'pos_restaurant/static/src/xml/**/*', ], 'web.assets_backend': [ 'point_of_sale/static/src/scss/pos_dashboard.scss', @@ -40,9 +41,6 @@ This module adds several features to the Point of Sale that are specific to rest 'web.assets_tests': [ 'pos_restaurant/static/tests/tours/**/*', ], - 'web.assets_qweb': [ - 'pos_restaurant/static/src/xml/**/*', - ], }, 'license': 'LGPL-3', } diff --git a/addons/pos_sale/__manifest__.py b/addons/pos_sale/__manifest__.py index 84540100104..ed121f6f668 100644 --- a/addons/pos_sale/__manifest__.py +++ b/addons/pos_sale/__manifest__.py @@ -37,8 +37,6 @@ This module adds a custom Sales Team for the Point of Sale. This enables you to 'pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementControlPanel.js', 'pos_sale/static/src/js/OrderManagementScreen/SaleOrderManagementScreen.js', 'pos_sale/static/src/js/OrderManagementScreen/SaleOrderRow.js', - ], - 'web.assets_qweb': [ 'pos_sale/static/src/xml/**/*', ], }, diff --git a/addons/pos_sale_product_configurator/__manifest__.py b/addons/pos_sale_product_configurator/__manifest__.py index fa847551424..28c5b54389d 100644 --- a/addons/pos_sale_product_configurator/__manifest__.py +++ b/addons/pos_sale_product_configurator/__manifest__.py @@ -18,8 +18,6 @@ This module adds features depending on both modules. 'point_of_sale.assets': [ 'pos_sale_product_configurator/static/src/js/models.js', 'pos_sale_product_configurator/static/src/css/popups/product_info_popup.css', - ], - 'web.assets_qweb': [ 'pos_sale_product_configurator/static/src/xml/**/*' ] }, diff --git a/addons/pos_six/__manifest__.py b/addons/pos_six/__manifest__.py index 79ff4e14434..e52f2f99ee4 100644 --- a/addons/pos_six/__manifest__.py +++ b/addons/pos_six/__manifest__.py @@ -19,8 +19,6 @@ 'pos_six/static/src/js/Chrome.js', 'pos_six/static/src/js/models.js', 'pos_six/static/src/js/payment_six.js', - ], - 'web.assets_qweb': [ 'pos_six/static/src/xml/**/*', ], } diff --git a/addons/product/__manifest__.py b/addons/product/__manifest__.py index 584e1292d6e..e99a32a1618 100644 --- a/addons/product/__manifest__.py +++ b/addons/product/__manifest__.py @@ -60,6 +60,7 @@ Print product labels with barcode. 'assets': { 'web.assets_backend': [ 'product/static/src/js/**/*', + 'product/static/src/xml/**/*', ], 'web.report_assets_common': [ 'product/static/src/scss/report_label_sheet.scss', @@ -67,9 +68,6 @@ Print product labels with barcode. 'web.qunit_suite_tests': [ 'product/static/tests/**/*', ], - 'web.assets_qweb': [ - 'product/static/src/xml/**/*', - ], }, 'license': 'LGPL-3', } diff --git a/addons/product_matrix/__manifest__.py b/addons/product_matrix/__manifest__.py index 9501afbdceb..d43d245d31d 100644 --- a/addons/product_matrix/__manifest__.py +++ b/addons/product_matrix/__manifest__.py @@ -22,8 +22,6 @@ Please refer to Sale Matrix or Purchase Matrix for the use of this module. 'web.assets_backend': [ 'product_matrix/static/src/js/section_and_note_widget.js', 'product_matrix/static/src/scss/product_matrix.scss', - ], - 'web.assets_qweb': [ 'product_matrix/static/src/xml/**/*', ], }, diff --git a/addons/project/__manifest__.py b/addons/project/__manifest__.py index ed881247ff4..f5e122d456e 100644 --- a/addons/project/__manifest__.py +++ b/addons/project/__manifest__.py @@ -78,6 +78,7 @@ 'project/static/src/scss/project_dashboard.scss', 'project/static/src/scss/project_form.scss', 'project/static/src/scss/project_widgets.scss', + 'project/static/src/xml/**/*', ], "web.assets_backend_legacy_lazy": [ 'project/static/src/js/*_legacy.js', @@ -86,11 +87,6 @@ 'project/static/src/scss/portal_rating.scss', 'project/static/src/js/portal_rating.js', ], - 'web.assets_qweb': [ - 'project/static/src/xml/**/*', - 'project/static/src/components/**/*.xml', - 'project/static/src/views/**/*.xml', - ], 'web.qunit_suite_tests': [ 'project/static/tests/project_test_utils.js', 'project/static/tests/burndown_chart_tests.js', @@ -99,10 +95,6 @@ 'web.assets_tests': [ 'project/static/tests/tours/**/*', ], - 'project.assets_qweb': [ - ('include', 'web.assets_qweb'), - 'project/static/src/project_sharing/**/*.xml', - ], 'project.webclient': [ ('include', 'web.assets_backend'), @@ -120,6 +112,9 @@ 'project/static/src/scss/project_sharing/*', 'web/static/src/start.js', 'web/static/src/legacy/legacy_setup.js', + + 'project/static/src/project_sharing/**/*.xml', + 'portal/static/src/xml/portal_chatter.xml', ], }, 'license': 'LGPL-3', diff --git a/addons/project/controllers/portal.py b/addons/project/controllers/portal.py index eccc59f7fac..105fbe6cb1d 100644 --- a/addons/project/controllers/portal.py +++ b/addons/project/controllers/portal.py @@ -12,8 +12,6 @@ from odoo.tools import groupby as groupbyelem from odoo.osv.expression import OR, AND -from odoo.addons.web.controllers.utils import HomeStaticTemplateHelpers - class ProjectCustomerPortal(CustomerPortal): @@ -135,7 +133,6 @@ class ProjectCustomerPortal(CustomerPortal): session_info = request.env['ir.http'].session_info() user_context = dict(request.env.context) if request.session.uid else {} mods = conf.server_wide_modules or [] - qweb_checksum = HomeStaticTemplateHelpers.get_qweb_templates_checksum(debug=request.session.debug, bundle="project.assets_qweb") if request.env.lang: lang = request.env.lang session_info['user_context']['lang'] = lang @@ -144,7 +141,6 @@ class ProjectCustomerPortal(CustomerPortal): lang = user_context.get("lang") translation_hash = request.env['ir.translation'].get_web_translations_hash(mods, lang) cache_hashes = { - "qweb": qweb_checksum, "translations": translation_hash, } diff --git a/addons/project/views/project_sharing_templates.xml b/addons/project/views/project_sharing_templates.xml index d404721055a..5182521f05d 100644 --- a/addons/project/views/project_sharing_templates.xml +++ b/addons/project/views/project_sharing_templates.xml @@ -21,7 +21,6 @@ // Prevent the menu_service to load anything. In an ideal world, Project Sharing assets would only contain // what is genuinely necessary, and not the whole backend. odoo.loadMenusPromise = Promise.resolve(); - odoo.loadTemplatesPromise = fetch(`/web/webclient/qweb/${odoo.__session_info__.cache_hashes.qweb}?bundle=project.assets_qweb`).then(doc => doc.text()); diff --git a/addons/purchase/__manifest__.py b/addons/purchase/__manifest__.py index 31243b81fa4..44745fe2114 100644 --- a/addons/purchase/__manifest__.py +++ b/addons/purchase/__manifest__.py @@ -43,14 +43,12 @@ 'purchase/static/src/views/*.scss', 'purchase/static/src/js/purchase_toaster_button.js', 'purchase/static/src/js/tours/purchase.js', + 'purchase/static/src/**/*.xml', ], 'web.assets_frontend': [ 'purchase/static/src/js/purchase_datetimepicker.js', 'purchase/static/src/js/purchase_portal_sidebar.js', ], - 'web.assets_qweb': [ - 'purchase/static/src/**/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/purchase/static/src/js/purchase_toaster_button.js b/addons/purchase/static/src/js/purchase_toaster_button.js index cddf26c0bd7..f4128838f9e 100644 --- a/addons/purchase/static/src/js/purchase_toaster_button.js +++ b/addons/purchase/static/src/js/purchase_toaster_button.js @@ -7,7 +7,6 @@ odoo.define('purchase.ToasterButton', function (require) { const ToasterButton = Widget.extend({ template: 'purchase.ToasterButton', - xmlDependencies: ['/purchase/static/src/xml/purchase_toaster_button.xml'], events: Object.assign({}, Widget.prototype.events, { 'click .fa-info-circle': '_onClickButton', }), diff --git a/addons/purchase_requisition/__manifest__.py b/addons/purchase_requisition/__manifest__.py index 921e4025b7c..66802a19092 100644 --- a/addons/purchase_requisition/__manifest__.py +++ b/addons/purchase_requisition/__manifest__.py @@ -32,8 +32,6 @@ are agreements you have with vendors to benefit from a predetermined pricing. 'purchase_requisition/static/src/js/purchase_order_line_compare_list_controller.js', 'purchase_requisition/static/src/js/purchase_order_line_compare_list_renderer.js', 'purchase_requisition/static/src/js/purchase_order_line_compare_list_view.js', - ], - 'web.assets_qweb': [ 'purchase_requisition/static/src/xml/purchase_order_line_compare.xml', ], }, diff --git a/addons/rating/__manifest__.py b/addons/rating/__manifest__.py index 4ccee1f5574..58cb0563576 100644 --- a/addons/rating/__manifest__.py +++ b/addons/rating/__manifest__.py @@ -25,13 +25,11 @@ This module allows a customer to give rating. 'web.assets_backend': [ 'rating/static/src/scss/rating_rating_views.scss', 'rating/static/src/components/*/*.scss', + 'rating/static/src/components/*/*.xml', ], 'web.assets_frontend': [ 'rating/static/src/scss/rating_templates.scss', ], - 'web.assets_qweb': [ - 'rating/static/src/components/*/*.xml', - ], 'web.tests_assets': [ 'rating/static/tests/helpers/*.js', ], diff --git a/addons/resource/__manifest__.py b/addons/resource/__manifest__.py index abffc221c01..2d310c58a7a 100644 --- a/addons/resource/__manifest__.py +++ b/addons/resource/__manifest__.py @@ -27,9 +27,6 @@ associated to every resource. It also manages the leaves of every resource. 'web.qunit_suite_tests': [ 'resource/static/tests/components/*.js', ], - 'web.assets_qweb': [ - 'resource/static/src/**/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/sale/__manifest__.py b/addons/sale/__manifest__.py index 5e1d6549f5a..569f3640da1 100644 --- a/addons/sale/__manifest__.py +++ b/addons/sale/__manifest__.py @@ -72,6 +72,7 @@ This module contains all the common features of Sales Management and eCommerce. 'sale/static/src/js/sale_order_controller.js', 'sale/static/src/js/sale_order_view.js', 'sale/static/src/js/product_discount_widget.js', + 'sale/static/src/xml/**/*', ], 'web.assets_frontend': [ 'sale/static/src/scss/sale_portal.scss', @@ -89,9 +90,6 @@ This module contains all the common features of Sales Management and eCommerce. 'web.report_assets_common': [ 'sale/static/src/scss/sale_report.scss', ], - 'web.assets_qweb': [ - 'sale/static/src/xml/**/*', - ], }, 'post_init_hook': '_synchronize_cron', 'license': 'LGPL-3', diff --git a/addons/sale_project/__manifest__.py b/addons/sale_project/__manifest__.py index 62e8563dd8b..bc713118894 100644 --- a/addons/sale_project/__manifest__.py +++ b/addons/sale_project/__manifest__.py @@ -25,9 +25,6 @@ This module allows to generate a project/task from sales orders. 'web.assets_backend': [ 'sale_project/static/src/components/project_right_side_panel/**/*', ], - 'web.assets_qweb': [ - 'sale_project/static/src/components/project_right_side_panel/**/*.xml', - ], }, 'auto_install': True, 'license': 'LGPL-3', diff --git a/addons/sale_stock/__manifest__.py b/addons/sale_stock/__manifest__.py index 657512f3cf7..704e3e9d1b2 100644 --- a/addons/sale_stock/__manifest__.py +++ b/addons/sale_stock/__manifest__.py @@ -47,8 +47,6 @@ Preferences 'assets': { 'web.assets_backend': [ 'sale_stock/static/src/js/**/*', - ], - 'web.assets_qweb': [ 'sale_stock/static/src/xml/**/*', ], }, diff --git a/addons/sale_timesheet/__manifest__.py b/addons/sale_timesheet/__manifest__.py index b825719b507..c58214ce70b 100644 --- a/addons/sale_timesheet/__manifest__.py +++ b/addons/sale_timesheet/__manifest__.py @@ -45,13 +45,11 @@ have real delivered quantities in sales orders. 'web.assets_backend': [ 'sale_timesheet/static/src/components/**/*', 'sale_timesheet/static/src/js/so_line_one2many.js', + 'sale_timesheet/static/src/xml/**/*', ], 'web.assets_tests': [ 'sale_timesheet/static/tests/**/*', ], - 'web.assets_qweb': [ - 'sale_timesheet/static/src/xml/**/*', - ], }, 'license': 'LGPL-3', 'post_init_hook': '_sale_timesheet_post_init', diff --git a/addons/sms/__manifest__.py b/addons/sms/__manifest__.py index cce1acbcde5..a0c04bb9e7f 100644 --- a/addons/sms/__manifest__.py +++ b/addons/sms/__manifest__.py @@ -44,8 +44,7 @@ The service is provided by the In App Purchase Odoo platform. 'sms/static/src/models/*.js', ], 'mail.assets_discuss_public': [ - 'sms/static/src/components/*/*', - ('remove', 'sms/static/src/components/phone_field/*'), + 'sms/static/src/components/sms_button/*', ], 'web.assets_backend': [ 'sms/static/src/js/fields_phone_widget.js', @@ -56,9 +55,6 @@ The service is provided by the In App Purchase Odoo platform. 'sms/static/tests/sms_widget_test.js', 'sms/static/tests/qunit_suite_tests/**/*.js', ], - 'web.assets_qweb': [ - 'sms/static/src/components/*/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/snailmail/__manifest__.py b/addons/snailmail/__manifest__.py index 0079a1ca7b8..4d450500a4d 100644 --- a/addons/snailmail/__manifest__.py +++ b/addons/snailmail/__manifest__.py @@ -38,6 +38,7 @@ Allows users to send documents by post 'web.assets_backend': [ 'snailmail/static/src/components/*/*.js', 'snailmail/static/src/components/*/*.scss', + 'snailmail/static/src/components/*/*.xml', ], 'web.tests_assets': [ 'snailmail/static/tests/helpers/*.js', @@ -45,9 +46,6 @@ Allows users to send documents by post 'web.qunit_suite_tests': [ 'snailmail/static/tests/qunit_suite_tests/**/*.js', ], - 'web.assets_qweb': [ - 'snailmail/static/src/components/*/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/spreadsheet/__manifest__.py b/addons/spreadsheet/__manifest__.py index c8a1684ef85..84ec95d8380 100644 --- a/addons/spreadsheet/__manifest__.py +++ b/addons/spreadsheet/__manifest__.py @@ -15,17 +15,18 @@ 'assets': { 'spreadsheet.o_spreadsheet': [ 'spreadsheet/static/src/o_spreadsheet/o_spreadsheet.js', + # Load all o_spreadsheet templates first to allow to inherit them + 'spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml', + 'spreadsheet/static/src/chart/odoo_menu/chart_figure.xml', 'spreadsheet/static/src/**/*.js', - ('remove', 'spreadsheet/static/src/assets_backend/**/*.js') + ('remove', 'spreadsheet/static/src/assets_backend/**/*') ], 'web.assets_backend': [ 'spreadsheet/static/src/**/*.scss', - 'spreadsheet/static/src/assets_backend/**/*.js' - ], - 'web.assets_qweb': [ - # Load all o_spreadsheet templates first to allow to inherit them - 'spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml', 'spreadsheet/static/src/**/*.xml', + 'spreadsheet/static/src/assets_backend/**/*', + ('remove', 'spreadsheet/static/src/o_spreadsheet/o_spreadsheet.xml'), + ('remove', 'spreadsheet/static/src/chart/odoo_menu/chart_figure.xml') ], 'web.qunit_suite_tests': [ 'spreadsheet/static/tests/**/*', diff --git a/addons/spreadsheet/static/src/assets_backend/spreadsheet_action_loader.js b/addons/spreadsheet/static/src/assets_backend/spreadsheet_action_loader.js index d85a0319f99..138c9c3cb58 100644 --- a/addons/spreadsheet/static/src/assets_backend/spreadsheet_action_loader.js +++ b/addons/spreadsheet/static/src/assets_backend/spreadsheet_action_loader.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "@web/core/registry"; -import { loadBundle } from "@web/core/assets"; +import { getBundle, loadBundle } from "@web/core/assets"; import { sprintf } from "@web/core/utils/strings"; const actionRegistry = registry.category("actions"); @@ -13,7 +13,8 @@ const actionRegistry = registry.category("actions"); * @param {function} actionLazyLoader */ export async function loadSpreadsheetAction(env, actionName, actionLazyLoader) { - await loadBundle("spreadsheet.o_spreadsheet"); + const desc = await getBundle("spreadsheet.o_spreadsheet"); + await loadBundle(desc); if (actionRegistry.get(actionName) === actionLazyLoader) { // At this point, the real spreadsheet client action should be loaded and have diff --git a/addons/spreadsheet/static/tests/utils/ui.js b/addons/spreadsheet/static/tests/utils/ui.js index d296f1f547b..24acf92b838 100644 --- a/addons/spreadsheet/static/tests/utils/ui.js +++ b/addons/spreadsheet/static/tests/utils/ui.js @@ -3,7 +3,7 @@ import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended"; import { registerCleanup } from "@web/../tests/helpers/cleanup"; import { getFixture } from "@web/../tests/helpers/utils"; -import { loadJS } from "@web/core/assets"; +import { loadJS, templates } from "@web/core/assets"; const { App } = owl; const { Spreadsheet } = spreadsheet; @@ -20,7 +20,7 @@ export async function mountSpreadsheet(model) { await loadJS("/web/static/lib/Chart/Chart.js"); const app = new App(Spreadsheet, { props: { model }, - templates: window.__OWL_TEMPLATES__, + templates: templates, env: model.config.evalContext.env, test: true, }); diff --git a/addons/spreadsheet_dashboard/__manifest__.py b/addons/spreadsheet_dashboard/__manifest__.py index 4863720fd51..90848adf0f4 100644 --- a/addons/spreadsheet_dashboard/__manifest__.py +++ b/addons/spreadsheet_dashboard/__manifest__.py @@ -25,8 +25,6 @@ "web.assets_backend": [ "spreadsheet_dashboard/static/src/assets/**/*.js", "spreadsheet_dashboard/static/src/**/*.scss", - ], - "web.assets_qweb": [ "spreadsheet_dashboard/static/src/**/*.xml", ], "web.qunit_suite_tests": [ diff --git a/addons/spreadsheet_dashboard_event_sale/__manifest__.py b/addons/spreadsheet_dashboard_event_sale/__manifest__.py index e3e0feadf5b..abd06b69c16 100644 --- a/addons/spreadsheet_dashboard_event_sale/__manifest__.py +++ b/addons/spreadsheet_dashboard_event_sale/__manifest__.py @@ -17,7 +17,6 @@ 'assets': { 'spreadsheet_dashboard.o_spreadsheet': [], 'web.assets_backend': [], - 'web.assets_qweb': [], 'web.qunit_suite_tests': [], 'web.assets_tests': [ 'spreadsheet_dashboard_event_sale/static/tests/**/*', diff --git a/addons/spreadsheet_dashboard_hr_expense/__manifest__.py b/addons/spreadsheet_dashboard_hr_expense/__manifest__.py index d164d36c4b1..709d5bcb6c0 100644 --- a/addons/spreadsheet_dashboard_hr_expense/__manifest__.py +++ b/addons/spreadsheet_dashboard_hr_expense/__manifest__.py @@ -17,7 +17,6 @@ 'assets': { 'spreadsheet_dashboard.o_spreadsheet': [], 'web.assets_backend': [], - 'web.assets_qweb': [], 'web.qunit_suite_tests': [], 'web.assets_tests': [ 'spreadsheet_dashboard_hr_expense/static/tests/**/*', diff --git a/addons/spreadsheet_dashboard_pos_hr/__manifest__.py b/addons/spreadsheet_dashboard_pos_hr/__manifest__.py index a17a095ed6e..c94598a8d6b 100644 --- a/addons/spreadsheet_dashboard_pos_hr/__manifest__.py +++ b/addons/spreadsheet_dashboard_pos_hr/__manifest__.py @@ -17,7 +17,6 @@ 'assets': { 'spreadsheet_dashboard.o_spreadsheet': [], 'web.assets_backend': [], - 'web.assets_qweb': [], 'web.qunit_suite_tests': [], 'web.assets_tests': [ 'spreadsheet_dashboard_pos_hr/static/tests/**/*', diff --git a/addons/spreadsheet_dashboard_sale/__manifest__.py b/addons/spreadsheet_dashboard_sale/__manifest__.py index 22d4593f429..13dc9b8e121 100644 --- a/addons/spreadsheet_dashboard_sale/__manifest__.py +++ b/addons/spreadsheet_dashboard_sale/__manifest__.py @@ -17,7 +17,6 @@ 'assets': { 'spreadsheet_dashboard.o_spreadsheet': [], 'web.assets_backend': [], - 'web.assets_qweb': [], 'web.qunit_suite_tests': [], 'web.assets_tests': [ 'spreadsheet_dashboard_sale/static/tests/**/*', diff --git a/addons/stock/__manifest__.py b/addons/stock/__manifest__.py index 6e895a32700..d50d6710513 100644 --- a/addons/stock/__manifest__.py +++ b/addons/stock/__manifest__.py @@ -110,10 +110,10 @@ ], 'web.assets_backend': [ 'stock/static/src/**/*.js', + 'stock/static/src/**/*.xml', 'stock/static/src/scss/forecast_widget.scss', 'stock/static/src/scss/stock_empty_screen.scss', - 'stock/static/src/views/**/*.js', - 'stock/static/src/views/**/**.scss', + 'stock/static/src/views/**/*', ], 'web.assets_tests': [ 'stock/static/tests/tours/stock_report_tests.js', @@ -124,10 +124,6 @@ 'stock/static/tests/lazy_column_list_tests.js', 'stock/static/tests/stock_traceability_report_backend_tests.js', ], - 'web.assets_qweb': [ - 'stock/static/src/**/*.xml', - 'stock/static/src/views/**/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/stock/static/src/js/report_stock_forecasted.js b/addons/stock/static/src/js/report_stock_forecasted.js index a6aedc07d40..df148ab384b 100644 --- a/addons/stock/static/src/js/report_stock_forecasted.js +++ b/addons/stock/static/src/js/report_stock_forecasted.js @@ -50,7 +50,7 @@ const ReplenishReport = clientAction.extend({ return Promise.all([ this._super.apply(this, arguments), loadWarehouses, - loadLegacyViews({ rpc: this._rpc.bind(this) }), + loadLegacyViews(), ]); }, diff --git a/addons/stock_account/__manifest__.py b/addons/stock_account/__manifest__.py index f1b67867cd2..5e5055ef5fa 100644 --- a/addons/stock_account/__manifest__.py +++ b/addons/stock_account/__manifest__.py @@ -44,8 +44,6 @@ Dashboard / Reports for Warehouse Management includes: 'assets': { 'web.assets_backend': [ 'stock_account/static/src/js/report_stock_forecasted.js', - ], - 'web.assets_qweb': [ 'stock_account/static/src/xml/inventory_report.xml', ], }, diff --git a/addons/stock_account/static/src/xml/inventory_report.xml b/addons/stock_account/static/src/xml/inventory_report.xml index e4c6cce3f1e..288e08ab6ea 100644 --- a/addons/stock_account/static/src/xml/inventory_report.xml +++ b/addons/stock_account/static/src/xml/inventory_report.xml @@ -1,12 +1,12 @@ - - + + Valuation at Date - + diff --git a/addons/survey/__manifest__.py b/addons/survey/__manifest__.py index dc54e3b80df..7eb18c4ffb4 100644 --- a/addons/survey/__manifest__.py +++ b/addons/survey/__manifest__.py @@ -62,6 +62,7 @@ sent mails with personal token for the invitation of the survey. 'survey.survey_assets': [ 'web/static/lib/Chart/Chart.js', 'survey/static/src/js/survey_image_zoomer.js', + '/survey/static/src/xml/survey_image_zoomer_templates.xml', 'survey/static/src/js/survey_quick_access.js', 'survey/static/src/js/survey_timer.js', 'survey/static/src/js/survey_breadcrumb.js', @@ -75,6 +76,7 @@ sent mails with personal token for the invitation of the survey. 'web/static/lib/bootstrap/scss/_variables.scss', 'survey/static/src/scss/survey_templates_form.scss', 'survey/static/src/scss/survey_templates_results.scss', + 'survey/static/src/xml/survey_breadcrumb_templates.xml', ], 'survey.survey_user_input_session_assets': [ 'survey/static/src/js/libs/chartjs-plugin-datalabels.min.js', @@ -83,12 +85,13 @@ sent mails with personal token for the invitation of the survey. 'survey/static/src/js/survey_session_text_answers.js', 'survey/static/src/js/survey_session_leaderboard.js', 'survey/static/src/js/survey_session_manage.js', + 'survey/static/src/xml/survey_session_text_answer_template.xml', ], 'web.report_assets_common': [ 'survey/static/src/scss/survey_reports.scss', ], 'web.assets_backend': [ - 'survey/static/src/question_page/*.js', + 'survey/static/src/question_page/*', 'survey/static/src/js/fields_section_one2many.js', 'survey/static/src/js/fields_form_page_description.js', 'survey/static/src/views/*.js', @@ -105,9 +108,6 @@ sent mails with personal token for the invitation of the survey. 'web.assets_common': [ 'survey/static/src/js/tours/survey_tour.js', ], - 'web.assets_qweb': [ - 'survey/static/src/question_page/*.xml', - ], }, 'license': 'LGPL-3', } diff --git a/addons/survey/static/src/js/survey_breadcrumb.js b/addons/survey/static/src/js/survey_breadcrumb.js index 4cc3796f9cb..a04b678d3c4 100644 --- a/addons/survey/static/src/js/survey_breadcrumb.js +++ b/addons/survey/static/src/js/survey_breadcrumb.js @@ -4,7 +4,6 @@ odoo.define('survey.breadcrumb', function (require) { var publicWidget = require('web.public.widget'); publicWidget.registry.SurveyBreadcrumbWidget = publicWidget.Widget.extend({ - xmlDependencies: ['/survey/static/src/xml/survey_breadcrumb_templates.xml'], template: "survey.survey_breadcrumb_template", events: { 'click .breadcrumb-item a': '_onBreadcrumbClick', diff --git a/addons/survey/static/src/js/survey_image_zoomer.js b/addons/survey/static/src/js/survey_image_zoomer.js index 5a7b446a7ac..cd31005cea1 100644 --- a/addons/survey/static/src/js/survey_image_zoomer.js +++ b/addons/survey/static/src/js/survey_image_zoomer.js @@ -4,7 +4,6 @@ import publicWidget from 'web.public.widget'; export const SurveyImageZoomer = publicWidget.Widget.extend({ template: 'survey.survey_image_zoomer', - xmlDependencies: ['/survey/static/src/xml/survey_image_zoomer_templates.xml'], events: { 'wheel .o_survey_img_zoom_image': '_onImgScroll', 'click': '_onZoomerClick', diff --git a/addons/survey/static/src/js/survey_result.js b/addons/survey/static/src/js/survey_result.js index c05a138d790..83b41713ba5 100644 --- a/addons/survey/static/src/js/survey_result.js +++ b/addons/survey/static/src/js/survey_result.js @@ -2,7 +2,7 @@ odoo.define('survey.result', function (require) { 'use strict'; var _t = require('web.core')._t; -var ajax = require('web.ajax'); +const { loadJS } = require('@web/core/assets'); var publicWidget = require('web.public.widget'); // The given colors are the same as those used by D3 @@ -427,7 +427,7 @@ publicWidget.registry.SurveyResultWidget = publicWidget.Widget.extend({ */ willStart: function () { var url = '/web/webclient/locale/' + (document.documentElement.getAttribute('lang') || 'en_US').replace('-', '_'); - var localeReady = ajax.loadJS(url); + var localeReady = loadJS(url); return Promise.all([this._super.apply(this, arguments), localeReady]); }, diff --git a/addons/survey/static/src/js/survey_session_text_answers.js b/addons/survey/static/src/js/survey_session_text_answers.js index 4e669c17ea0..5649f9ae6a6 100644 --- a/addons/survey/static/src/js/survey_session_text_answers.js +++ b/addons/survey/static/src/js/survey_session_text_answers.js @@ -9,7 +9,6 @@ var SESSION_CHART_COLORS = require('survey.session_colors'); var QWeb = core.qweb; publicWidget.registry.SurveySessionTextAnswers = publicWidget.Widget.extend({ - xmlDependencies: ['/survey/static/src/xml/survey_session_text_answer_template.xml'], init: function (parent, options) { this._super.apply(this, arguments); diff --git a/addons/web/__manifest__.py b/addons/web/__manifest__.py index 31cf8bdbd9e..4c59c64ff9e 100644 --- a/addons/web/__manifest__.py +++ b/addons/web/__manifest__.py @@ -38,21 +38,6 @@ This module provides the core of the Odoo Web Client. # Exemples: # > web.assets_common = assets common to both frontend and backend clients. # > web_editor.assets_wysiwyg = assets needed by components defined in the "web_editor" module. - - 'web.assets_qweb': [ - 'web/static/src/**/*.xml', - ('remove', 'web/static/src/legacy/**/*.xml'), - 'web/static/src/legacy/xml/base.xml', - 'web/static/src/legacy/xml/control_panel.xml', - 'web/static/src/legacy/xml/chart.xml', - 'web/static/src/legacy/xml/fields.xml', - 'web/static/src/legacy/xml/kanban.xml', - 'web/static/src/legacy/xml/pivot.xml', - 'web/static/src/legacy/xml/web_calendar.xml', - 'web/static/src/legacy/xml/graph.xml', - 'web/static/src/legacy/xml/search_panel.xml', - 'web/static/src/legacy/xml/week_days.xml', - ], 'web.assets_common_minimal': [ 'web/static/src/legacy/js/promise_extension.js', 'web/static/src/boot.js', @@ -94,15 +79,17 @@ This module provides the core of the Odoo Web Client. 'web/static/src/core/**/*', 'web/static/src/legacy/legacy_fields.scss', 'web/static/src/search/**/*', + 'web/static/src/webclient/icons.scss', # variables required in list_controller.scss + 'web/static/src/views/**/*', 'web/static/src/webclient/**/*', ('remove', 'web/static/src/webclient/navbar/navbar.scss'), # already in _assets_common_styles ('remove', 'web/static/src/webclient/clickbot/clickbot.js'), # lazy loaded - 'web/static/src/views/**/*', ('remove', 'web/static/src/views/form/button_box/*.scss'), # remove the report code and whitelist only what's needed ('remove', 'web/static/src/webclient/actions/reports/**/*'), 'web/static/src/webclient/actions/reports/*.js', + 'web/static/src/webclient/actions/reports/*.xml', 'web/static/src/env.js', @@ -195,6 +182,18 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/js/owl_compatibility.js', ("remove", 'web/static/src/legacy/js/views/graph/**/*'), ("remove", 'web/static/src/legacy/js/views/pivot/**/*'), + + 'web/static/src/legacy/xml/base.xml', + 'web/static/src/legacy/xml/ribbon.xml', + 'web/static/src/legacy/xml/control_panel.xml', + 'web/static/src/legacy/xml/chart.xml', + 'web/static/src/legacy/xml/fields.xml', + 'web/static/src/legacy/xml/kanban.xml', + 'web/static/src/legacy/xml/pivot.xml', + 'web/static/src/legacy/xml/web_calendar.xml', + 'web/static/src/legacy/xml/graph.xml', + 'web/static/src/legacy/xml/search_panel.xml', + 'web/static/src/legacy/xml/week_days.xml', ], "web.assets_backend_legacy_lazy": [ ("include", "web._assets_helpers"), @@ -204,6 +203,7 @@ This module provides the core of the Odoo Web Client. # graph 'web/static/src/legacy/js/views/graph/**/*', 'web/static/src/legacy/scss/graph_view.scss', + 'web/static/src/legacy/xml/graph.xml', # pivot 'web/static/src/legacy/js/views/pivot/**/*', 'web/static/src/legacy/scss/pivot_view.scss', @@ -406,6 +406,7 @@ This module provides the core of the Odoo Web Client. 'web/static/lib/clipboard/clipboard.js', 'web/static/lib/jSignature/jSignatureCustom.js', 'web/static/lib/qweb/qweb2.js', + 'web/static/src/legacy/js/assets.js', 'web/static/src/legacy/js/libs/autocomplete.js', 'web/static/src/legacy/js/libs/bootstrap.js', 'web/static/src/legacy/js/libs/content-disposition.js', @@ -426,6 +427,7 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/js/core/collections.js', 'web/static/src/legacy/js/core/concurrency.js', 'web/static/src/legacy/js/core/dialog.js', + 'web/static/src/legacy/xml/dialog.xml', 'web/static/src/legacy/js/core/owl_dialog.js', 'web/static/src/legacy/js/core/popover.js', 'web/static/src/legacy/js/core/dom.js', @@ -449,9 +451,12 @@ This module provides the core of the Odoo Web Client. 'web/static/src/legacy/js/services/session_storage_service.js', 'web/static/src/legacy/js/common_env.js', 'web/static/src/legacy/js/widgets/name_and_signature.js', + 'web/static/src/legacy/xml/name_and_signature.xml', 'web/static/src/legacy/js/core/smooth_scroll_on_drag.js', 'web/static/src/legacy/js/widgets/colorpicker.js', + 'web/static/src/legacy/xml/colorpicker.xml', 'web/static/src/legacy/js/widgets/translation_dialog.js', + 'web/static/src/legacy/xml/translation_dialog.xml', ], # Used during the transition of the web architecture @@ -490,14 +495,20 @@ This module provides the core of the Odoo Web Client. 'web/static/lib/fullcalendar/daygrid/main.js', 'web/static/lib/fullcalendar/timegrid/main.js', 'web/static/lib/fullcalendar/list/main.js', + 'web/static/lib/ace/ace.js', 'web/static/lib/ace/javascript_highlight_rules.js', 'web/static/lib/ace/mode-python.js', 'web/static/lib/ace/mode-xml.js', 'web/static/lib/ace/mode-js.js', + 'web/static/lib/ace/mode-qweb.js', 'web/static/lib/nearest/jquery.nearest.js', 'web/static/lib/daterangepicker/daterangepicker.js', + 'web/static/src/legacy/js/libs/daterangepicker.js', 'web/static/lib/stacktracejs/stacktrace.js', + 'web/static/lib/Chart/Chart.js', + + '/web/static/lib/daterangepicker/daterangepicker.js', # 'web/static/tests/legacy/main_tests.js', 'web/static/tests/helpers/**/*.js', diff --git a/addons/web/controllers/binary.py b/addons/web/controllers/binary.py index 208c2823ab7..ee80fe8f5ed 100644 --- a/addons/web/controllers/binary.py +++ b/addons/web/controllers/binary.py @@ -75,6 +75,7 @@ class Binary(http.Controller): stream = request.env['ir.binary']._get_stream_from(record, field, filename, filename_field, mimetype) send_file_kwargs = {'as_attachment': download} if unique: + send_file_kwargs['immutable'] = True send_file_kwargs['max_age'] = http.STATIC_CACHE_LONG if nocache: send_file_kwargs['max_age'] = None @@ -100,6 +101,7 @@ class Binary(http.Controller): send_file_kwargs = {'as_attachment': False} if unique: + send_file_kwargs['immutable'] = True send_file_kwargs['max_age'] = http.STATIC_CACHE_LONG if nocache: send_file_kwargs['max_age'] = None @@ -147,6 +149,7 @@ class Binary(http.Controller): send_file_kwargs = {'as_attachment': download} if unique: + send_file_kwargs['immutable'] = True send_file_kwargs['max_age'] = http.STATIC_CACHE_LONG if nocache: send_file_kwargs['max_age'] = None diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 6071debe315..6054d838471 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -32,7 +32,6 @@ _MOVED_TO_MAP = { 'GroupExportXlsxWriter': export, 'GroupsTreeNode': export, 'Home': home, - 'HomeStaticTemplateHelpers': utils, 'none_values_filtered': export, 'OPERATOR_MAPPING': export, 'ReportController': report, diff --git a/addons/web/controllers/utils.py b/addons/web/controllers/utils.py index c076e47cb23..cf625a623c9 100644 --- a/addons/web/controllers/utils.py +++ b/addons/web/controllers/utils.py @@ -215,212 +215,3 @@ def _local_web_translations(trans_file): if x.id and x.string and "openerp-web" in x.auto_comments: messages.append({'id': x.id, 'string': x.string}) return messages - - -class HomeStaticTemplateHelpers: - r""" - Helper Class that wraps the reading of static qweb templates files - and xpath inheritance applied to those templates - /!\ Template inheritance order is defined by ir.module.module natural order - which is "sequence, name" - Then a topological sort is applied, which just puts dependencies - of a module before that module - """ - NAME_TEMPLATE_DIRECTIVE = 't-name' - STATIC_INHERIT_DIRECTIVE = 't-inherit' - STATIC_INHERIT_MODE_DIRECTIVE = 't-inherit-mode' - PRIMARY_MODE = 'primary' - EXTENSION_MODE = 'extension' - DEFAULT_MODE = PRIMARY_MODE - COMMENT_PATTERN = r'Modified by [\s\w\-.]+ from [\s\w\-.]+' - - def __init__(self, addons, db, checksum_only=False, debug=False): - """ - :param str|list addons: plain list or comma separated list of addons - :param str db: the current db we are working on - :param bool checksum_only: only computes the checksum of all files for addons - :param str debug: the debug mode of the session - """ - self.addons = addons.split(',') if isinstance(addons, str) else addons - self.db = db - self.debug = debug - self.checksum_only = checksum_only - self.template_dict = OrderedDict() - - def _get_parent_template(self, addon, template): - """Computes the real addon name and the template name - of the parent template (the one that is inherited from) - - :param str addon: the addon the template is declared in - :param etree template: the current template we are are handling - :returns: (str, str) - """ - original_template_name = template.attrib[self.STATIC_INHERIT_DIRECTIVE] - split_name_attempt = original_template_name.split('.', 1) - parent_addon, parent_name = tuple(split_name_attempt) if len(split_name_attempt) == 2 else (addon, original_template_name) - if parent_addon not in self.template_dict: - if original_template_name in self.template_dict[addon]: - parent_addon = addon - parent_name = original_template_name - else: - raise ValueError(_('Module %s not loaded or inexistent, or templates of addon being loaded (%s) are misordered') % (parent_addon, addon)) - - if parent_name not in self.template_dict[parent_addon]: - raise ValueError(_("No template found to inherit from. Module %s and template name %s") % (parent_addon, parent_name)) - - return parent_addon, parent_name - - def _compute_xml_tree(self, addon, file_name, source): - """Computes the xml tree that 'source' contains - Applies inheritance specs in the process - - :param str addon: the current addon we are reading files for - :param str file_name: the current name of the file we are reading - :param str source: the content of the file - :returns: etree - """ - try: - all_templates_tree = etree.parse(io.BytesIO(source), parser=etree.XMLParser(remove_comments=True)).getroot() - except etree.ParseError as e: - _logger.error("Could not parse file %s: %s", file_name, e.msg) - raise - - self.template_dict.setdefault(addon, OrderedDict()) - for template_tree in list(all_templates_tree): - if self.NAME_TEMPLATE_DIRECTIVE in template_tree.attrib: - template_name = template_tree.attrib[self.NAME_TEMPLATE_DIRECTIVE] - dotted_names = template_name.split('.', 1) - if len(dotted_names) > 1 and dotted_names[0] == addon: - template_name = dotted_names[1] - else: - # self.template_dict[addon] grows after processing each template - template_name = 'anonymous_template_%s' % len(self.template_dict[addon]) - if self.STATIC_INHERIT_DIRECTIVE in template_tree.attrib: - inherit_mode = template_tree.attrib.get(self.STATIC_INHERIT_MODE_DIRECTIVE, self.DEFAULT_MODE) - if inherit_mode not in [self.PRIMARY_MODE, self.EXTENSION_MODE]: - raise ValueError(_("Invalid inherit mode. Module %s and template name %s") % (addon, template_name)) - - parent_addon, parent_name = self._get_parent_template(addon, template_tree) - - # After several performance tests, we found out that deepcopy is the most efficient - # solution in this case (compared with copy, xpath with '.' and stringifying). - parent_tree = copy.deepcopy(self.template_dict[parent_addon][parent_name]) - - xpaths = list(template_tree) - # owl chokes on comments, disable debug comments for now - # pylint: disable=W0125 - if False: # self.debug and inherit_mode == self.EXTENSION_MODE: - for xpath in xpaths: - xpath.insert(0, etree.Comment(" Modified by %s from %s " % (template_name, addon))) - elif inherit_mode == self.PRIMARY_MODE: - parent_tree.tag = template_tree.tag - inherited_template = apply_inheritance_specs(parent_tree, xpaths) - - if inherit_mode == self.PRIMARY_MODE: # New template_tree: A' = B(A) - for attr_name, attr_val in template_tree.attrib.items(): - if attr_name not in ('t-inherit', 't-inherit-mode'): - inherited_template.set(attr_name, attr_val) - if self.debug: - self._remove_inheritance_comments(inherited_template) - self.template_dict[addon][template_name] = inherited_template - - else: # Modifies original: A = B(A) - self.template_dict[parent_addon][parent_name] = inherited_template - else: - if template_name in self.template_dict[addon]: - raise ValueError(_("Template %s already exists in module %s") % (template_name, addon)) - self.template_dict[addon][template_name] = template_tree - return all_templates_tree - - def _remove_inheritance_comments(self, inherited_template): - '''Remove the comments added in the template already, they come from other templates extending - the base of this inheritance - - :param inherited_template: - ''' - for comment in inherited_template.xpath('//comment()'): - if re.match(self.COMMENT_PATTERN, comment.text.strip()): - comment.getparent().remove(comment) - - def _read_addon_file(self, path_or_url): - """Read the content of a file or an ``ir.attachment`` record given by - ``path_or_url``. - - :param str path_or_url: - :returns: bytes - :raises FileNotFoundError: if the path does not match a module file - or an attachment - """ - try: - with file_open(path_or_url, 'rb') as fp: - contents = fp.read() - except FileNotFoundError as e: - attachment = request.env['ir.attachment'].sudo().search([ - ('url', '=', path_or_url), - ('type', '=', 'binary'), - ], limit=1) - if attachment: - contents = attachment.raw - else: - raise e - return contents - - def _concat_xml(self, file_dict): - """Concatenate xml files - - :param dict(list) file_dict: - key: addon name - value: list of files for an addon - :returns: (concatenation_result, checksum) - :rtype: (bytes, str) - """ - checksum = hashlib.new('sha512') # sha512/256 - if not file_dict: - return b'', checksum.hexdigest() - - root = None - for addon, fnames in file_dict.items(): - for fname in fnames: - contents = self._read_addon_file(fname) - checksum.update(contents) - if not self.checksum_only: - self._compute_xml_tree(addon, fname, contents) - - if root is None: - root = etree.Element('templates') - - for addon in self.template_dict.values(): - for template in addon.values(): - root.append(template) - - return etree.tostring(root, encoding='utf-8') if root is not None else b'', checksum.hexdigest()[:64] - - def _get_asset_paths(self, bundle): - """Proxy for ir_asset._get_asset_paths - Useful to make 'self' testable. - """ - return request.env['ir.asset']._get_asset_paths(addons=self.addons, bundle=bundle, xml=True) - - def _get_qweb_templates(self, bundle): - """One and only entry point that gets and evaluates static qweb templates - - :rtype: (str, str) - """ - xml_paths = defaultdict(list) - - # group paths by module, keeping them in order - for path, addon, _ in self._get_asset_paths(bundle): - addon_paths = xml_paths[addon] - if path not in addon_paths: - addon_paths.append(path) - - content, checksum = self._concat_xml(xml_paths) - return content, checksum - - @classmethod - def get_qweb_templates_checksum(cls, addons=None, db=None, debug=False, bundle=None): - return cls(addons, db, checksum_only=True, debug=debug)._get_qweb_templates(bundle)[1] - - @classmethod - def get_qweb_templates(cls, addons=None, db=None, debug=False, bundle=None): - return cls(addons, db, debug=debug)._get_qweb_templates(bundle)[0] diff --git a/addons/web/controllers/webclient.py b/addons/web/controllers/webclient.py index 66406771701..d5e1b7e76ac 100644 --- a/addons/web/controllers/webclient.py +++ b/addons/web/controllers/webclient.py @@ -17,7 +17,7 @@ from odoo.modules import get_manifest, get_resource_path from odoo.http import request from odoo.tools import lazy from odoo.tools.misc import file_open -from .utils import HomeStaticTemplateHelpers, _local_web_translations +from .utils import _local_web_translations _logger = logging.getLogger(__name__) @@ -53,17 +53,6 @@ class WebClient(http.Controller): ('Cache-Control', f'max-age={http.STATIC_CACHE}'), ]) - @http.route('/web/webclient/qweb/', type='http', auth="none", cors="*") - def qweb(self, unique, mods=None, db=None, bundle=None): - if not request.db and mods is None: - mods = odoo.conf.server_wide_modules or [] - content = HomeStaticTemplateHelpers.get_qweb_templates(mods, db, debug=request.session.debug, bundle=bundle) - - return request.make_response(content, [ - ('Content-Type', 'text/xml'), - ('Cache-Control', f'public, max-age={http.STATIC_CACHE_LONG}') - ]) - @http.route('/web/webclient/bootstrap_translations', type='json', auth="none") def bootstrap_translations(self, mods=None): """ Load local translations from *.po files, as a temporary solution @@ -139,14 +128,20 @@ class WebClient(http.Controller): def benchmarks(self, mod=None, **kwargs): return request.render('web.benchmark_suite') - @http.route('/web/bundle/', auth="user", methods=["GET"]) - def bundle(self, bundle_name): + @http.route('/web/bundle/', auth="public", methods=["GET"]) + def bundle(self, bundle_name, **bundle_params): """ Request the definition of a bundle, including its javascript and css bundled assets """ - files = request.env["ir.qweb"]._get_asset_nodes(bundle_name, debug=request.session.debug, js=True, css=True) - data = json.dumps([{ + if 'lang' in bundle_params: + request.update_context(lang=bundle_params['lang']) + + debug = bundle_params.get('debug', request.session.debug) + files = request.env["ir.qweb"]._get_asset_nodes(bundle_name, debug=debug, js=True, css=True) + data = [{ "type": tag, - "src": attrs.get("src") or attrs.get('href'), - } for tag, attrs, _ in files]) - return request.make_response(data, [('Content-Type', 'application/json')]) + "src": attrs.get("src") or attrs.get("data-src") or attrs.get('href'), + "content": content, + } for tag, attrs, content in files] + + return request.make_json_response(data) diff --git a/addons/web/models/ir_http.py b/addons/web/models/ir_http.py index 027e956e6b0..1be2dfa9f20 100644 --- a/addons/web/models/ir_http.py +++ b/addons/web/models/ir_http.py @@ -9,7 +9,6 @@ from odoo import api, http, models from odoo.http import request from odoo.tools import file_open, image_process, ustr from odoo.tools.misc import str2bool -from odoo.addons.web.controllers.utils import HomeStaticTemplateHelpers _logger = logging.getLogger(__name__) @@ -110,7 +109,12 @@ class Http(models.AbstractModel): ) if session_uid else None, }, "currencies": self.sudo().get_currencies(), + 'bundle_params': { + 'lang': request.session.context['lang'], + }, } + if request.session.debug: + session_info['bundle_params']['debug'] = request.session.debug if self.env.user.has_group('base.group_user'): # the following is only useful in the context of a webclient bootstrapping # but is still included in some other calls (e.g. '/web/session/authenticate') @@ -118,13 +122,11 @@ class Http(models.AbstractModel): # with access to the backend ('internal'-type users) if request.db: mods = list(request.registry._init_modules) + mods - qweb_checksum = HomeStaticTemplateHelpers.get_qweb_templates_checksum(debug=request.session.debug, bundle="web.assets_qweb") menus = request.env['ir.ui.menu'].load_menus(request.session.debug) ordered_menus = {str(k): v for k, v in menus.items()} menu_json_utf8 = json.dumps(ordered_menus, default=ustr, sort_keys=True).encode() session_info['cache_hashes'].update({ "load_menus": hashlib.sha512(menu_json_utf8).hexdigest()[:64], # sha512/256 - "qweb": qweb_checksum, }) session_info.update({ # current_company should be default_company @@ -157,7 +159,12 @@ class Http(models.AbstractModel): 'profile_collectors': request.session.profile_collectors, 'profile_params': request.session.profile_params, 'show_effect': bool(request.env['ir.config_parameter'].sudo().get_param('base_setup.show_effect')), + 'bundle_params': { + 'lang': request.session.context['lang'], + }, } + if request.session.debug: + session_info['bundle_params']['debug'] = request.session.debug if session_uid: version_info = odoo.service.common.exp_version() session_info.update({ diff --git a/addons/web/static/lib/qweb/qweb2.js b/addons/web/static/lib/qweb/qweb2.js index 7c66e48fefd..9757ee39ad2 100644 --- a/addons/web/static/lib/qweb/qweb2.js +++ b/addons/web/static/lib/qweb/qweb2.js @@ -267,7 +267,7 @@ QWeb2.Engine = (function() { if (name && extend) { // Clone template and extend it if (!this.templates[extend]) { - return this.tools.exception("Can't clone undefined template " + extend); + return this.tools.exception("Can't clone undefined template '" + extend + "' to create '" + name + "'"); } this.templates[name] = this.templates[extend].cloneNode(true); this.extend_templates[name] = (this.extend_templates[extend] || []).slice(); diff --git a/addons/web/static/src/boot.js b/addons/web/static/src/boot.js index 2006b59466f..0e573ac123a 100644 --- a/addons/web/static/src/boot.js +++ b/addons/web/static/src/boot.js @@ -111,6 +111,93 @@ return !!job.error; }); }, + processJobs: function () { + var job; + + function processJob(job) { + var require = makeRequire(job); + + var jobExec; + function onError(e) { + job.error = e; + console.error(`Error while loading ${job.name}: ${e.message}`, e); + } + var def = new Promise(function (resolve) { + try { + jobExec = job.factory.call(null, require); + jobs.splice(jobs.indexOf(job), 1); + } catch (e) { + onError(e); + } + if (!job.error) { + Promise.resolve(jobExec) + .then(function (data) { + services[job.name] = data; + resolve(); + odoo.__DEBUG__.processJobs(); + }) + .guardedCatch(function (e) { + job.rejected = e || true; + jobs.push(job); + }) + .catch(function (e) { + if (e instanceof Error) { + onError(e); + } + resolve(); + }); + } else { + resolve(); + } + }); + jobPromises.push(def); + def.then(job.resolve); + } + + function isReady(job) { + return ( + !job.error && + !job.rejected && + job.factory.deps.every(function (name) { + return name in services; + }) + ); + } + + function makeRequire(job) { + var deps = {}; + Object.keys(services) + .filter(function (item) { + return job.deps.indexOf(item) >= 0; + }) + .forEach(function (key) { + deps[key] = services[key]; + }); + + return function require(name) { + if (!(name in deps)) { + console.error("Undefined dependency: ", name); + } + return deps[name]; + }; + } + + while (jobs.length) { + job = undefined; + for (var i = 0; i < jobs.length; i++) { + if (isReady(jobs[i])) { + job = jobs[i]; + break; + } + } + if (!job) { + break; + } + processJob(job); + } + + return services; + }, factories: factories, services: services, }; @@ -147,17 +234,21 @@ factory.deps = deps; factories[name] = factory; + let promiseResolve; + const promise = new Promise(resolve => {promiseResolve = resolve;}); jobs.push({ name: name, factory: factory, deps: deps, + resolve: promiseResolve, + promise: promise, }); deps.forEach(function (dep) { jobDeps.push({ from: dep, to: name }); }); - this.processJobs(jobs, services); + odoo.__DEBUG__.processJobs(); }; odoo.log = function () { var missing = []; @@ -271,91 +362,21 @@ }; didLogInfoResolve(true); }; - odoo.processJobs = function (jobs, services) { - var job; - - function processJob(job) { - var require = makeRequire(job); - - var jobExec; - function onError(e) { - job.error = e; - console.error(`Error while loading ${job.name}: ${e.message}`, e); - } - var def = new Promise(function (resolve) { - try { - jobExec = job.factory.call(null, require); - jobs.splice(jobs.indexOf(job), 1); - } catch (e) { - onError(e); - } - if (!job.error) { - Promise.resolve(jobExec) - .then(function (data) { - services[job.name] = data; - resolve(); - odoo.processJobs(jobs, services); - }) - .guardedCatch(function (e) { - job.rejected = e || true; - jobs.push(job); - }) - .catch(function (e) { - if (e instanceof Error) { - onError(e); - } - resolve(); - }); - } else { - resolve(); - } - }); - jobPromises.push(def); + /** + * Returns a resolved promise when the targeted services are loaded. + * If no service is found the promise is used directly. + * + * @param {string|RegExp} serviceName name of the service to expect + * or regular expression matching the service. + * @returns {Promise} resolved when the services ares + * loaded. The value is equal to the number of services found. + */ + odoo.ready = async function (serviceName) { + function match (name) { + return typeof serviceName === 'string' ? name === serviceName : serviceName.test(name); } - - function isReady(job) { - return ( - !job.error && - !job.rejected && - job.factory.deps.every(function (name) { - return name in services; - }) - ); - } - - function makeRequire(job) { - var deps = {}; - Object.keys(services) - .filter(function (item) { - return job.deps.indexOf(item) >= 0; - }) - .forEach(function (key) { - deps[key] = services[key]; - }); - - return function require(name) { - if (!(name in deps)) { - console.error("Undefined dependency: ", name); - } - return deps[name]; - }; - } - - while (jobs.length) { - job = undefined; - for (var i = 0; i < jobs.length; i++) { - if (isReady(jobs[i])) { - job = jobs[i]; - break; - } - } - if (!job) { - break; - } - processJob(job); - } - - return services; + await Promise.all(jobs.filter(job => match(job.name)).map(job => job.promise)); + return Object.keys(factories).filter(match).length; }; // Automatically log errors detected when loading modules diff --git a/addons/web/static/src/core/assets.js b/addons/web/static/src/core/assets.js index f84799eb2d8..705f0110227 100644 --- a/addons/web/static/src/core/assets.js +++ b/addons/web/static/src/core/assets.js @@ -3,16 +3,16 @@ import { memoize } from "./utils/functions"; import { browser } from "./browser/browser"; import { registry } from "./registry"; - -class AssetsLoadingError extends Error {} +import { session } from "@web/session"; /** - * An object describing a bundle to load - * @typedef {Object} BundleInfo - * @property {'script'|'link'} [type] the type of file in this bundle - * @property {string} [src] the url of the file for this bundle, for this type of file - * @example `[{"type": "script", "src": "/web/assets/266-d34b0b4/documents_spreadsheet.o_spreadsheet.min.js"}]` + * This export is done only in order to modify the behavior of the exported + * functions. This is done in order to be able to make a test environment. + * Modules should only use the methods exported below. */ +export const assets = {}; + +class AssetsLoadingError extends Error {} /** * Loads the given url inside a script tag. @@ -20,7 +20,7 @@ class AssetsLoadingError extends Error {} * @param {string} url the url of the script * @returns {Promise} resolved when the script has been loaded */ -export const loadJS = memoize(function loadJS(url) { +assets.loadJS = memoize(function loadJS(url) { if (document.querySelector(`script[src="${url}"]`)) { // Already in the DOM and wasn't loaded through this function // Unfortunately there is no way to check whether a script has loaded @@ -28,6 +28,7 @@ export const loadJS = memoize(function loadJS(url) { // so we assume it is. return Promise.resolve(); } + const scriptEl = document.createElement("script"); scriptEl.type = "text/javascript"; scriptEl.src = url; @@ -39,13 +40,14 @@ export const loadJS = memoize(function loadJS(url) { }); }); }); + /** * Loads the given url as a stylesheet. * * @param {string} url the url of the stylesheet * @returns {Promise} resolved when the stylesheet has been loaded */ -export const loadCSS = memoize(function loadCSS(url) { +assets.loadCSS = memoize(function loadCSS(url) { if (document.querySelector(`link[href="${url}"]`)) { // Already in the DOM and wasn't loaded through this function // Unfortunately there is no way to check whether a link has loaded @@ -65,110 +67,187 @@ export const loadCSS = memoize(function loadCSS(url) { }); }); }); -/** - * Loads the qweb templates from a given bundle name. - * TODO: merge this into loadBundleDefinition? - * - * @param {string} bundle the name of the bundle as declared in the manifest. - * @returns {Promise} A Promise of an XML document containing - * the owl templates or an empty string if the bundle has none. - */ -export const fetchAndProcessTemplates = memoize(async function fetchAndProcessTemplates(bundle) { - // 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 = `/web/webclient/qweb/${Date.now()}?bundle=${bundle}`; - const templates = await (await browser.fetch(bundleURL)).text(); - if (!templates) { - return ""; - } - return processTemplates(templates); -}); /** - * Loads the content definition of a bundle. - * - * @param {string} name the bundleName of the bundle as declared in the manifest. - * @returns {Promise} A promise of the content definition of the bundle + * Container dom containing all the owl templates that have been loaded. + * This can be imported by the modules in order to use it when loading the + * application and the components. */ -const loadBundleDefinition = memoize(async function (bundleName) { - const request = await browser.fetch(`/web/bundle/${bundleName}`); - return await request.json(); -}); - -const bundlesCache = {}; +export const templates = new DOMParser().parseFromString("", "text/xml"); +let defaultApp; /** - * Loads a bundle. + * Loads the given xml template. * - * @param {string} name the name of the bundle to load - * @param {owl.App} [app] the app in which the bundle's templates should be - * loaded. Defaults to the app that's written on the function itself, this - * is considered the main app, and should be written on the function by the - * code that bootstraps the app. In most cases, this will be the webclient, - * and is set in start.js - * @returns {Promise} a promise that is resolved after the bundle has been - * loaded. + * @param {string} xml the string defining the templates + * @param {App} [app=defaultApp] optional owl App instance (default value + * can be changed with setLoadXmlDefaultApp method) + * @returns {Promise} resolved when the template xml has been loaded */ -export async function loadBundle(name, app = loadBundle.app) { - if (!bundlesCache[name]) { - bundlesCache[name] = Promise.all([ - fetchAndProcessTemplates(name).then((templates) => app.addTemplates(templates, app)), - loadBundleDefinition(name).then((bundleInfo) => - Promise.all([ - ...bundleInfo.filter((i) => i.type === "script").map((i) => loadJS(i.src)), - ...bundleInfo.filter((i) => i.type === "link").map((i) => loadCSS(i.src)), - ]) - ), - ]).then(() => {}); + assets.loadXML = function loadXML (xml, app=defaultApp) { + const doc = new DOMParser().parseFromString(xml, "text/xml"); + if (doc.querySelector('parsererror')) { + throw doc.querySelector('parsererror div').textContent.split(':')[0]; } - return bundlesCache[name]; -} - -/** - * Process the qweb templates to obtain only the owl templates. This function - * does NOT register the templates into Owl. - * - * @param {string} templates An xml string describing templates - * @returns {XMLDocument} An xml document containing only the owl templates - */ -export function processTemplates(templates) { - const doc = new DOMParser().parseFromString(templates, "text/xml"); - // as we currently have two qweb engines (owl and legacy), owl templates are - // flagged with attribute `owl="1"`. The following lines removes the "owl" - // attribute from the templates, so that it doesn't appear in the DOM. We - // also remove the non-owl templates, as those shouldn't be loaded in the - // owl application, and will be loaded separately. - for (const template of [...doc.querySelector("templates").children]) { - if (template.hasAttribute("owl")) { - template.removeAttribute("owl"); + for (const element of doc.querySelectorAll("templates > [t-name][owl]")) { + element.removeAttribute("owl"); + const name = element.getAttribute('t-name'); + const previous = templates.querySelector(`[t-name="${name}"]`); + if (previous) { + console.debug('Override template: ' + name); + previous.replaceWith(element); } else { - template.remove(); + templates.documentElement.appendChild(element); } } - return doc; + if (app || defaultApp) { + console.debug('Add templates in Owl app.'); + app.addTemplates(templates, app || defaultApp); + } else { + console.debug('Add templates on window Owl container.'); + } +}; +/** + * Update the default app to load templates. + * + * @param {App} app owl App instance + */ +export function setLoadXmlDefaultApp(app) { + defaultApp = app; } /** - * Renders a public asset template and loads the libraries defined inside of it. - * Only loads js and css, template declarations will be ignored. Only loads - * scripts and styles that are defined in script src and link href, ignores - * inline scripts and styles. + * Get the files information as descriptor object from a public asset template. * - * @deprecated - * @param {string} xmlid The xmlid of the template that defines the public asset - * @param {ORM} orm An ORM object capable of calling methods on models - * @returns {Promise} Resolved when the contents of the asset is loaded + * @param {string} bundleName Name of the bundle containing the list of files + * @returns {Promise<{cssLibs, cssContents, jsLibs, jsContents}>} */ -export const loadPublicAsset = memoize(async function loadPublicAsset(xmlid, orm) { - const xml = await orm.call("ir.ui.view", "render_public_asset", [xmlid]); - const doc = new DOMParser().parseFromString(`${xml}`, "text/xml"); - return Promise.all([ - ...[...doc.querySelectorAll("link[href]")].map((el) => loadCSS(el.getAttribute("href"))), - ...[...doc.querySelectorAll("script[src]")].map((el) => loadJS(el.getAttribute("src"))), - ]); +assets.getBundle = memoize(async function getBundle(bundleName) { + const url = new URL(`/web/bundle/${bundleName}`, location.origin); + for (const [key, value] of Object.entries(session.bundle_params || {})) { + url.searchParams.set(key, value); + } + const response = await browser.fetch(url.href); + const json = await response.json(); + const assets = { + cssLibs: [], + cssContents: [], + jsLibs: [], + jsContents: [], + }; + for (const key in json) { + const file = json[key]; + if (file.type === 'link') { + assets.cssLibs.push(file.src); + } else if (file.type === 'style') { + assets.cssContents.push(file.content); + } else { + if (file.src) { + assets.jsLibs.push(file.src); + } else { + assets.jsContents.push(file.content); + } + } + } + return assets; }); +/** + * Loads the given js/css libraries and asset bundles. Note that no library or + * asset will be loaded if it was already done before. + * + * @param {Object} desc + * @param {Array} [desc.assetLibs=[]] + * The list of assets to load. Each list item may be a string (the xmlID + * of the asset to load) or a list of strings. The first level is loaded + * sequentially (so use this if the order matters) while the assets in + * inner lists are loaded in parallel (use this for efficiency but only + * if the order does not matter, should rarely be the case for assets). + * @param {string[]} [desc.cssLibs=[]] + * The list of CSS files to load. They will all be loaded in parallel but + * put in the DOM in the given order (only the order in the DOM is used + * to determine priority of CSS rules, not loaded time). + * @param {Array} [desc.jsLibs=[]] + * The list of JS files to load. Each list item may be a string (the URL + * of the file to load) or a list of strings. The first level is loaded + * sequentially (so use this if the order matters) while the files in inner + * lists are loaded in parallel (use this for efficiency but only + * if the order does not matter). + * @param {string[]} [desc.cssContents=[]] + * List of inline styles to add after loading the CSS files. + * @param {string[]} [desc.jsContents=[]] + * List of inline scripts to add after loading the JS files. + * + * @returns {Promise} + */ +assets.loadBundle = memoize(async function loadBundle(desc) { + // Load css in parallel + const promiseCSS = Promise.all((desc.cssLibs || []).map(assets.loadCSS)).then(() => { + if (desc.cssContents && desc.cssContents.length) { + const style = document.createElement("style"); + style.textContent = desc.cssContents.join('\n'); + document.head.appendChild(style); + } + }); + // Load JavaScript (don't wait for the css loading) + for (const urlData of desc.jsLibs || []) { + if (typeof urlData === 'string') { + // serial loading + await assets.loadJS(urlData); + // Wait template if the JavaScript come from bundle. + const bundle = urlData.match(/\/web\/assets\/.*\/([^\/]+?)(\.min)?\.js/); + if (bundle) { + await odoo.ready(bundle[1] + '.bundle.xml'); + } + } else { + // parallel loading + await Promise.all(urlData.map(loadJS)); + } + } + + if (desc.jsContents && desc.jsContents.length) { + const script = document.createElement("script"); + script.type = "text/javascript"; + script.textContent = desc.jsContents.join('\n'); + document.head.appendChild(script); + } + // Wait for the scc loading to be completed before loading the other bundle + await promiseCSS; + // Load other desc + for (const bundleName of desc.assetLibs || []) { + if (typeof bundleName === 'string') { + // serial loading + const desc = await assets.getBundle(bundleName); + await assets.loadBundle(desc); + } else { + // parallel loading + await Promise.all(bundleName.map(async bundleName => { + const desc = await assets.getBundle(bundleName); + return assets.loadBundle(desc); + })); + } + } +}); + + +export const loadJS = function (url) { + return assets.loadJS(url); +} +export const loadCSS = function (url) { + return assets.loadCSS(url); +} +export const loadXML = function (xml, app=defaultApp) { + return assets.loadXML(xml, app=app); +} +export const getBundle = function (bundleName) { + return assets.getBundle(bundleName); +} +export const loadBundle = function (desc) { + return assets.loadBundle(desc); +} + + const { Component, xml, onWillStart } = owl; /** * Utility component that loads an asset bundle before instanciating a component @@ -176,7 +255,8 @@ const { Component, xml, onWillStart } = owl; export class LazyComponent extends Component { setup() { onWillStart(async () => { - await loadBundle(this.props.bundle); + const bundle = await getBundle(this.props.bundle); + await loadBundle(bundle); this.Component = registry.category("lazy_components").get(this.props.Component); }); } diff --git a/addons/web/static/src/env.js b/addons/web/static/src/env.js index e96bc44aea9..3ee7d10811f 100644 --- a/addons/web/static/src/env.js +++ b/addons/web/static/src/env.js @@ -125,7 +125,7 @@ async function _startServices(env, toStart) { }) .catch((error) => { services[name] = error; - console.error(error); + console.error("Can't load service '" + name + "' because:", error); }) .finally(resolve); }) diff --git a/addons/web/static/src/legacy/js/assets.js b/addons/web/static/src/legacy/js/assets.js new file mode 100644 index 00000000000..8768d84ec92 --- /dev/null +++ b/addons/web/static/src/legacy/js/assets.js @@ -0,0 +1,23 @@ +odoo.define('web.assets.qweb', function (require) { +"use strict"; + +const {assets} = require('@web/core/assets'); + +const loadXML = assets.loadXML; +assets.loadXML = function (xml, app) { + loadXML(xml, app); + + const doc = new DOMParser().parseFromString(xml, "text/xml"); + const qwebTemplates = document.createElement("templates"); + for (const element of doc.querySelectorAll("templates > [t-name]:not([owl]), templates > [t-extend]:not([owl])")) { + qwebTemplates.appendChild(element); + } + + // don't use require to apply the patch before the first template loading. + odoo.ready('web.core').then(function () { + const core = odoo.__DEBUG__.services['web.core']; + core.qweb.add_template(qwebTemplates); + }); +} + +}); diff --git a/addons/web/static/src/legacy/js/core/ajax.js b/addons/web/static/src/legacy/js/core/ajax.js index 27d7eebbd67..fca5219a4dd 100644 --- a/addons/web/static/src/legacy/js/core/ajax.js +++ b/addons/web/static/src/legacy/js/core/ajax.js @@ -2,7 +2,6 @@ odoo.define('web.ajax', function (require) { "use strict"; var config = require('web.config'); -var concurrency = require('web.concurrency'); var core = require('web.core'); const {Markup} = require('web.utils'); var time = require('web.time'); @@ -137,90 +136,6 @@ function rpc(url, params, settings) { return jsonRpc(url, 'call', params, settings); } - -/** - * Load css asynchronously: fetch it from the url parameter and add a link tag - * to . - * If the url has already been requested and loaded, the promise will resolve - * immediately. - * - * @param {String} url of the css to be fetched - * @returns {Promise} resolved when the css has been loaded. - */ -var loadCSS = (function () { - var urlDefs = {}; - - return function loadCSS(url) { - if (url in urlDefs) { - // nothing to do here - } else if ($('link[href="' + url + '"]').length) { - // the link is already in the DOM, the promise can be resolved - urlDefs[url] = Promise.resolve(); - } else { - var $link = $('', { - 'href': url, - 'rel': 'stylesheet', - 'type': 'text/css' - }); - urlDefs[url] = new Promise(function (resolve, reject) { - $link.on('load', function () { - resolve(); - }).on('error', function () { - reject(new Error("Couldn't load css dependency: " + $link[0].href)); - }); - }); - $('head').append($link); - } - return urlDefs[url]; - }; -})(); - -var loadJS = (function () { - var dependenciesPromise = {}; - - var load = function loadJS(url) { - // Check the DOM to see if a script with the specified url is already there - var alreadyRequired = ($('script[src="' + url + '"]').length > 0); - - // If loadJS was already called with the same URL, it will have a registered promise indicating if - // the script has been fully loaded. If not, the promise has to be initialized. - // This is initialized as already resolved if the script was already there without the need of loadJS. - if (url in dependenciesPromise) { - return dependenciesPromise[url]; - } - var scriptLoadedPromise = new Promise(function (resolve, reject) { - if (alreadyRequired) { - resolve(); - } else { - // Get the script associated promise and returns it after initializing the script if needed. The - // promise is marked to be resolved on script load and rejected on script error. - var script = document.createElement('script'); - script.type = 'text/javascript'; - script.src = url; - script.onload = script.onreadystatechange = function() { - if ((script.readyState && script.readyState !== "loaded" && script.readyState !== "complete") || script.onload_done) { - return; - } - script.onload_done = true; - resolve(url); - }; - script.onerror = function () { - console.error("Error loading file", script.src); - reject(url); - }; - var head = document.head || document.getElementsByTagName('head')[0]; - head.appendChild(script); - } - }); - - dependenciesPromise[url] = scriptLoadedPromise; - return scriptLoadedPromise; - }; - - return load; -})(); - - /** * Cooperative file download implementation, for ajaxy APIs. * @@ -335,87 +250,6 @@ function post (controller_url, data) { }); } -/** - * Loads an XML file according to the given URL and adds its associated qweb - * templates to the given qweb engine. The function can also be used to get - * the promise which indicates when all the calls to the function are finished. - * - * Note: "all the calls" = the calls that happened before the current no-args - * one + the calls that will happen after but when the previous ones are not - * finished yet. - * - * @param {string} [url] - an URL where to find qweb templates - * @param {QWeb} [qweb] - the engine to which the templates need to be added - * @returns {Promise} - * If no argument is given to the function, the promise's state - * indicates if "all the calls" are finished (see main description). - * Otherwise, it indicates when the templates associated to the given - * url have been loaded. - */ -var loadXML = (function () { - // Some "static" variables associated to the loadXML function - var isLoading = false; - var loadingsData = []; - var seenURLs = []; - - return function (url, qweb) { - function _load() { - isLoading = true; - if (loadingsData.length) { - // There is something to load, load it, resolve the associated - // promise then start loading the next one - var loadingData = loadingsData[0]; - loadingData.qweb.add_template(loadingData.url, function () { - // Remove from array only now so that multiple calls to - // loadXML with the same URL returns the right promise - loadingsData.shift(); - loadingData.resolve(); - _load(); - }); - } else { - // There is nothing to load anymore, so resolve the - // "all the calls" promise - isLoading = false; - } - } - - // If no argument, simply returns the promise which indicates when - // "all the calls" are finished - if (!url || !qweb) { - return Promise.resolve(); - } - - // If the given URL has already been seen, do nothing but returning the - // associated promise - if (_.contains(seenURLs, url)) { - var oldLoadingData = _.findWhere(loadingsData, {url: url}); - return oldLoadingData ? oldLoadingData.def : Promise.resolve(); - } - seenURLs.push(url); - - - // Add the information about the new data to load: the url, the qweb - // engine and the associated promise - var newLoadingData = { - url: url, - qweb: qweb, - }; - newLoadingData.def = new Promise(function (resolve, reject) { - newLoadingData.resolve = resolve; - newLoadingData.reject = reject; - }); - loadingsData.push(newLoadingData); - - // If not already started, start the loading loop (reinitialize the - // "all the calls" promise to an unresolved state) - if (!isLoading) { - _load(); - } - - // Return the promise associated to the new given URL - return newLoadingData.def; - }; -})(); /** * Loads a template file according to the given xmlId. @@ -478,92 +312,10 @@ var loadAsset = (function () { return load; })(); -/** - * Loads the given js/css libraries and asset bundles. Note that no library or - * asset will be loaded if it was already done before. - * - * @param {Object} libs - * @param {Array} [libs.assetLibs=[]] - * The list of assets to load. Each list item may be a string (the xmlID - * of the asset to load) or a list of strings. The first level is loaded - * sequentially (so use this if the order matters) while the assets in - * inner lists are loaded in parallel (use this for efficiency but only - * if the order does not matter, should rarely be the case for assets). - * @param {string[]} [libs.cssLibs=[]] - * The list of CSS files to load. They will all be loaded in parallel but - * put in the DOM in the given order (only the order in the DOM is used - * to determine priority of CSS rules, not loaded time). - * @param {Array} [libs.jsLibs=[]] - * The list of JS files to load. Each list item may be a string (the URL - * of the file to load) or a list of strings. The first level is loaded - * sequentially (so use this if the order matters) while the files in inner - * lists are loaded in parallel (use this for efficiency but only - * if the order does not matter). - * @param {string[]} [libs.cssContents=[]] - * 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 - * @param {string} [tplRoute] - * Custom route to use for template rendering of the potential assets - * to load (see libs.assetLibs). - * - * @returns {Promise} - */ -function loadLibs(libs, context, tplRoute) { - var mutex = new concurrency.Mutex(); - mutex.exec(function () { - var defs = []; - var cssLibs = [libs.cssLibs || []]; // Force loading in parallel - defs.push(_loadArray(cssLibs, ajax.loadCSS).then(function () { - if (libs.cssContents && libs.cssContents.length) { - $('head').append($('