From ca8dc2d9b42195ce5afb32f3aefca4c6b47465c4 Mon Sep 17 00:00:00 2001 From: Xavier-Do Date: Tue, 6 Jun 2023 10:15:07 +0000 Subject: [PATCH] [IMP] base, website: small refactoring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mainly to simplify website overrides and general api closes odoo/odoo#121376 Signed-off-by: Xavier Dollé (xdo) --- addons/bus/controllers/websocket.py | 4 +- addons/im_livechat/controllers/main.py | 9 ++-- addons/test_website/tests/test_qweb.py | 3 +- addons/web_editor/controllers/main.py | 4 +- addons/website/models/ir_asset.py | 11 ++++- addons/website/models/ir_qweb.py | 19 -------- addons/website/tests/test_ui.py | 10 ++-- odoo/addons/base/models/assetsbundle.py | 46 +++++++------------ odoo/addons/base/models/ir_asset.py | 13 ++++-- odoo/addons/base/models/ir_qweb.py | 18 ++++---- .../tests/test_assetsbundle.py | 3 +- 11 files changed, 55 insertions(+), 85 deletions(-) diff --git a/addons/bus/controllers/websocket.py b/addons/bus/controllers/websocket.py index e97871ff120..6449d1ee69a 100644 --- a/addons/bus/controllers/websocket.py +++ b/addons/bus/controllers/websocket.py @@ -64,9 +64,7 @@ class WebsocketController(Controller): prevent new worker versions to be loaded from the browser cache. """ bundle = 'bus.websocket_worker_assets' - assets_params = request.env["ir.asset"]._get_assets_params() - files, _ = request.env["ir.qweb"]._get_asset_content(bundle, assets_params=assets_params) - asset = AssetsBundle(bundle, files, assets_params=assets_params) + asset = request.env["ir.qweb"]._get_asset_bundle(bundle) stream = request.env['ir.binary']._get_stream_from(asset.js( is_minified="assets" not in request.session.debug )) diff --git a/addons/im_livechat/controllers/main.py b/addons/im_livechat/controllers/main.py index 2379bf00669..5b7455e63e1 100644 --- a/addons/im_livechat/controllers/main.py +++ b/addons/im_livechat/controllers/main.py @@ -20,8 +20,7 @@ class LivechatController(http.Controller): DOM of the livechat to avoid conflicts. """ bundle = 'im_livechat.assets_embed' - files, _ = request.env["ir.qweb"]._get_asset_content(bundle) - asset = AssetsBundle(bundle, files) + asset = request.env["ir.qweb"]._get_asset_bundle(bundle) if ext == 'css': raise request.not_found() stream = request.env['ir.binary']._get_stream_from(asset.js()) @@ -30,8 +29,7 @@ class LivechatController(http.Controller): @http.route('/im_livechat/assets_embed.', type='http', auth='public', cors='*') def assets_embed(self, ext, **kwargs): bundle = 'im_livechat.assets_embed' - files, _ = request.env["ir.qweb"]._get_asset_content(bundle) - asset = AssetsBundle(bundle, files) + asset = request.env["ir.qweb"]._get_asset_bundle(bundle) if ext not in ('css', 'js'): raise request.not_found() stream = request.env['ir.binary']._get_stream_from(getattr(asset, ext)()) @@ -44,8 +42,7 @@ class LivechatController(http.Controller): @http.route('/im_livechat/emoji_bundle', type='http', auth='public', cors='*') def get_emoji_bundle(self): bundle = 'mail.assets_emoji' - files, _ = request.env["ir.qweb"]._get_asset_content(bundle) - asset = AssetsBundle(bundle, files) + asset = request.env["ir.qweb"]._get_asset_bundle(bundle) stream = request.env['ir.binary']._get_stream_from(asset.js()) return stream.get_response() diff --git a/addons/test_website/tests/test_qweb.py b/addons/test_website/tests/test_qweb.py index c66615a6b02..3f22322fad0 100644 --- a/addons/test_website/tests/test_qweb.py +++ b/addons/test_website/tests/test_qweb.py @@ -36,8 +36,7 @@ class TestQweb(TransactionCaseWithUserDemo): html = demo_env['ir.qweb']._render('test_website.test_template', {"user": demo}, website_id=website.id) asset_bundle_xmlid = 'test_website.test_bundle' qweb = self.env['ir.qweb'] - files, _ = qweb._get_asset_content(asset_bundle_xmlid) - bundle = qweb._get_asset_bundle(asset_bundle_xmlid, files, env=self.env, css=True, js=True) + bundle = qweb._get_asset_bundle(asset_bundle_xmlid, css=True, js=True) asset_version_js = bundle.get_version('js') asset_version_css = bundle.get_version('css') diff --git a/addons/web_editor/controllers/main.py b/addons/web_editor/controllers/main.py index c0a98a41d10..47ece23cec1 100644 --- a/addons/web_editor/controllers/main.py +++ b/addons/web_editor/controllers/main.py @@ -607,9 +607,7 @@ class Web_Editor(http.Controller): if re.match('^o-color-([1-5])$', css_color_value): if not bundle_css: bundle = 'web.assets_frontend' - files, _ = request.env["ir.qweb"]._get_asset_content(bundle) - assets_params = request.env["ir.asset"]._get_assets_params() - asset = AssetsBundle(bundle, files, assets_params=assets_params) + asset = request.env["ir.qweb"]._get_asset_bundle(bundle) bundle_css = asset.css().index_content color_search = re.search(r'(?i)--%s:\s+(%s|%s)' % (css_color_value, regex_hex, regex_rgba), bundle_css) if not color_search: diff --git a/addons/website/models/ir_asset.py b/addons/website/models/ir_asset.py index 889d7ce9a47..154b6a4de7a 100644 --- a/addons/website/models/ir_asset.py +++ b/addons/website/models/ir_asset.py @@ -10,11 +10,18 @@ class IrAsset(models.Model): key = fields.Char(copy=False) # used to resolve multiple assets in a multi-website environment website_id = fields.Many2one('website', ondelete='cascade') - def _get_assets_params(self): - params = super()._get_assets_params() + def _get_asset_params(self): + params = super()._get_asset_params() params['website_id'] = self.env['website'].get_current_website(fallback=False).id return params + def _get_asset_extra(self, extra, website_id=None, **params): + extra = super()._get_asset_extra(extra, **params) + if extra == '%': + return extra + website_id_path = website_id and ('%s/' % website_id) or '' + return website_id_path + extra + def _get_related_assets(self, domain, website_id=None, **params): if website_id: domain += self.env['website'].website_domain(website_id) diff --git a/addons/website/models/ir_qweb.py b/addons/website/models/ir_qweb.py index 367ee8cd6c6..50e09e678bd 100644 --- a/addons/website/models/ir_qweb.py +++ b/addons/website/models/ir_qweb.py @@ -18,22 +18,6 @@ _logger = logging.getLogger(__name__) re_background_image = re.compile(r"(background-image\s*:\s*url\(\s*['\"]?\s*)([^)'\"]+)") -class AssetsBundleMultiWebsite(AssetsBundle): - def _get_asset_url_values(self, id, unique, extra, name, sep, extension): - if extra != '%': - website_id = self.assets_params.get('website_id') - website_id_path = website_id and ('%s/' % website_id) or '' - extra = website_id_path + extra - res = super(AssetsBundleMultiWebsite, self)._get_asset_url_values(id, unique, extra, name, sep, extension) - return res - - def get_debug_asset_url(self, extra='', name='%', extension='%'): - if extra != '%': - website_id = self.assets_params.get('website_id') - website_id_path = website_id and ('%s/' % website_id) or '' - extra = website_id_path + extra - return super(AssetsBundleMultiWebsite, self).get_debug_asset_url(extra, name, extension) - class IrQWeb(models.AbstractModel): """ IrQWeb object for rendering stuff in the website context """ @@ -108,9 +92,6 @@ class IrQWeb(models.AbstractModel): return irQweb - def _get_asset_bundle(self, xmlid, files=None, env=None, css=True, js=True, debug_assets=False, rtl=False, assets_params=None): - return AssetsBundleMultiWebsite(xmlid, files, env=env, css=css, js=js, debug_assets=debug_assets, rtl=rtl, assets_params=assets_params) - def _post_processing_att(self, tagName, atts): if atts.get('data-no-post-process'): return atts diff --git a/addons/website/tests/test_ui.py b/addons/website/tests/test_ui.py index 0cce294033d..2ca43bd9da8 100644 --- a/addons/website/tests/test_ui.py +++ b/addons/website/tests/test_ui.py @@ -243,15 +243,13 @@ class TestUi(odoo.tests.HttpCase): 'website_id': new_website.id, }) - files, _ = self.env['ir.qweb']._get_asset_content(asset_bundle_xmlid, assets_params={'website_id': website_default.id}) - self.assertNotIn(custom_url, [f['url'] for f in files]) - base_website_bundle = self.env['ir.qweb']._get_asset_bundle(asset_bundle_xmlid, files, env=self.env, assets_params={'website_id': website_default.id}) + base_website_bundle = self.env['ir.qweb']._get_asset_bundle(asset_bundle_xmlid, assets_params={'website_id': website_default.id}) + self.assertNotIn(custom_url, [f['url'] for f in base_website_bundle.files]) base_website_css_version = base_website_bundle.get_version('css') base_website_js_version = base_website_bundle.get_version('js') - files, _ = self.env['ir.qweb']._get_asset_content('website.assets_wysiwyg', assets_params={'website_id': new_website.id}) - self.assertIn(custom_url, [f['url'] for f in files]) - new_website_bundle_modified = self.env['ir.qweb']._get_asset_bundle(asset_bundle_xmlid, files, env=self.env, assets_params={'website_id': new_website.id}) + new_website_bundle_modified = self.env['ir.qweb']._get_asset_bundle('website.assets_wysiwyg', assets_params={'website_id': new_website.id}) + self.assertIn(custom_url, [f['url'] for f in new_website_bundle_modified.files]) self.assertEqual(new_website_bundle_modified.get_version('css'), base_website_css_version) self.assertNotEqual(new_website_bundle_modified.get_version('js'), base_website_js_version, "js version for new website should now have been changed") diff --git a/odoo/addons/base/models/assetsbundle.py b/odoo/addons/base/models/assetsbundle.py index e672fe1dd66..bfda143199c 100644 --- a/odoo/addons/base/models/assetsbundle.py +++ b/odoo/addons/base/models/assetsbundle.py @@ -32,6 +32,7 @@ from odoo.modules.module import get_resource_path from odoo.tools import (func, misc, transpile_javascript, is_odoo_module, SourceMapGenerator, profiler, apply_inheritance_specs) +from odoo.tools.constants import SCRIPT_EXTENSIONS, STYLE_EXTENSIONS from odoo.tools.misc import file_open, html_escape as escape from odoo.tools.pycompat import to_text @@ -39,7 +40,6 @@ _logger = logging.getLogger(__name__) EXTENSIONS = (".js", ".css", ".scss", ".sass", ".less", ".xml") - class CompileError(RuntimeError): pass class AssetError(Exception): @@ -57,7 +57,7 @@ class AssetsBundle(object): TRACKED_BUNDLES = ['web.assets_common', 'web.assets_backend'] - def __init__(self, name, files, env=None, css=True, js=True, debug_assets=False, rtl=False, assets_params=None): + def __init__(self, name, files, external_assets=(), env=None, css=True, js=True, debug_assets=False, rtl=False, assets_params=None): """ :param name: bundle name :param files: files to be added to the bundle @@ -77,6 +77,11 @@ class AssetsBundle(object): self.has_js = js self._checksum_cache = {} self.is_debug_assets = debug_assets + self.external_assets = [ + node + for node in external_assets + if (css and node[0].rpartition('.')[2] in STYLE_EXTENSIONS) or (js and node[0].rpartition('.')[2] in SCRIPT_EXTENSIONS) + ] # asset-wide html "media" attribute for f in files: @@ -139,7 +144,7 @@ class AssetsBundle(object): src = self.get_debug_asset_url(name=js_attachment.name, extension='') if self.is_debug_assets else js_attachment[0].url response.append((src, None)) - return response + return self.external_assets + response def get_version(self, asset_type): return self.get_checksum(asset_type)[0:7] @@ -162,26 +167,12 @@ class AssetsBundle(object): self._checksum_cache[asset_type] = hashlib.sha512(unique_descriptor.encode()).hexdigest()[:64] return self._checksum_cache[asset_type] - def _get_asset_template_url(self): - return "/web/assets/{id}-{unique}/{extra}{name}{sep}{extension}" - - def _get_asset_url_values(self, id, unique, extra, name, sep, extension): # extra can contain direction or/and website - return { - 'id': id, - 'unique': unique, - 'extra': extra, - 'name': name, - 'sep': sep, - 'extension': extension, - 'params': self.assets_params, - } - - def get_asset_url(self, id='%', unique='%', extra='', name='%', sep="%", extension='%'): - return self._get_asset_template_url().format( - **self._get_asset_url_values(id=id, unique=unique, extra=extra, name=name, sep=sep, extension=extension) - ) + def get_asset_url(self, attachment_id='%', unique='%', extra='', name='%', sep=".", extension='%'): + extra = self.env['ir.asset']._get_asset_extra(extra, **self.assets_params) + return f"/web/assets/{attachment_id}-{unique}/{extra}{name}{sep}{extension}" def get_debug_asset_url(self, extra='', name='%', extension='%'): + extra = self.env['ir.asset']._get_asset_extra(extra, **self.assets_params) return f"/web/assets/debug/{extra}{name}{extension}" def _unlink_attachments(self, attachments): @@ -213,13 +204,12 @@ class AssetsBundle(object): url = self.get_asset_url( extra='%s' % ('rtl/' if is_css and self.rtl else ''), name=self.name, - sep='.', extension=extension, ) domain = [ ('url', '=like', url), - '!', ('url', '=like', self.get_asset_url(unique=self.get_version('css' if is_css else 'js'))) + '!', ('url', '=like', self.get_asset_url(unique=self.get_version('css' if is_css else 'js'), sep='%')) ] attachments = ira.sudo().search(domain) # avoid to invalidate cache if it's already empty (mainly useful for test) @@ -251,7 +241,6 @@ class AssetsBundle(object): unique=unique, extra=extra, # not sure about css.map name=self.name, - sep='.', extension=extension, ) query = """ @@ -273,8 +262,7 @@ class AssetsBundle(object): unique=unique, extra='%', #ignore website_id and rtl name=self.name, - sep='.', - extension='%s' % extension, + extension=extension, ) self.env.cr.execute(query, [SUPERUSER_ID, fallback_url_pattern]) @@ -295,11 +283,10 @@ class AssetsBundle(object): self.add_post_rollback() attachment = self.env['ir.attachment'].with_user(SUPERUSER_ID).create(values) url = self.get_asset_url( - id=attachment.id, + attachment_id=attachment.id, unique=unique, extra=extra, name=self.name, - sep='.', extension=extension, ) attachment.url = url @@ -353,11 +340,10 @@ class AssetsBundle(object): self.add_post_rollback() attachment = ira.with_user(SUPERUSER_ID).create(values) url = self.get_asset_url( - id=attachment.id, + attachment_id=attachment.id, unique=self.get_version('css' if is_css else 'js'), extra='%s' % ('rtl/' if extension in ['css', 'min.css'] and self.rtl else ''), name=self.name, - sep='.', extension=extension, ) attachment.url = url diff --git a/odoo/addons/base/models/ir_asset.py b/odoo/addons/base/models/ir_asset.py index 1c68e368661..deaa7711c5a 100644 --- a/odoo/addons/base/models/ir_asset.py +++ b/odoo/addons/base/models/ir_asset.py @@ -90,13 +90,20 @@ class IrAsset(models.Model): active = fields.Boolean(string='active', default=True) sequence = fields.Integer(string="Sequence", default=DEFAULT_SEQUENCE, required=True) - def _get_assets_params(self): + def _get_asset_params(self): """ This method can be overriden to add param _get_asset_paths call. Those params will be part of the orm cache key """ return {} + def _get_asset_extra(self, extra): + """ + This method can be overriden to use param as keyword arguments + Return the extra based on additionnal assets_params + """ + return extra + @tools.conditional( 'xml' not in tools.config['dev_mode'], tools.ormcache('bundle', 'tuple(sorted(assets_params.items()))'), @@ -120,7 +127,7 @@ class IrAsset(models.Model): :param bundle: name of the bundle from which to fetch the file paths :param assets_params: parameters needed by overrides, mainly website_id - see _get_assets_params + see _get_asset_params :returns: the list of tuples (path, addon, bundle) """ installed = self._get_installed_addons_list() @@ -242,7 +249,7 @@ class IrAsset(models.Model): """ installed = self._get_installed_addons_list() target_path, _full_path, _modified = self._get_paths(target_path_def, installed)[0] - assets_params = self._get_assets_params() + assets_params = self._get_asset_params() asset_paths = self._get_asset_paths(root_bundle, assets_params) for path, _full_path, bundle, _modified in asset_paths: diff --git a/odoo/addons/base/models/ir_qweb.py b/odoo/addons/base/models/ir_qweb.py index dfd22c05ae9..91a179b1c9f 100644 --- a/odoo/addons/base/models/ir_qweb.py +++ b/odoo/addons/base/models/ir_qweb.py @@ -2426,7 +2426,7 @@ class IrQWeb(models.AbstractModel): Else, the assets will be generated only once and then stored in cache. """ rtl = self.env['res.lang']._lang_get_direction(self.env.context.get('lang') or self.env.user.lang) == 'rtl' - assets_params = self.env['ir.asset']._get_assets_params() # website_id + assets_params = self.env['ir.asset']._get_asset_params() # website_id debug_assets = debug and 'assets' in debug if debug_assets: @@ -2491,7 +2491,7 @@ class IrQWeb(models.AbstractModel): def _get_asset_content(self, bundle, assets_params=None): if assets_params is None: - assets_params = self.env['ir.asset']._get_assets_params() # website_id + assets_params = self.env['ir.asset']._get_asset_params() # website_id asset_paths = self.env['ir.asset']._get_asset_paths(bundle=bundle, assets_params=assets_params) files = [] external_asset = [] @@ -2507,8 +2507,11 @@ class IrQWeb(models.AbstractModel): external_asset.append((path, None)) return (files, external_asset) - def _get_asset_bundle(self, bundle_name, files=None, env=None, css=True, js=True, debug_assets=False, rtl=False, assets_params=None): - return AssetsBundle(bundle_name, files, env=env, css=css, js=js, debug_assets=debug_assets, rtl=rtl, assets_params=assets_params) + def _get_asset_bundle(self, bundle_name, css=True, js=True, debug_assets=False, rtl=False, assets_params=None): + if assets_params is None: + assets_params = self.env['ir.asset']._get_asset_params() + files, external_assets = self._get_asset_content(bundle_name, assets_params) + return AssetsBundle(bundle_name, files, external_assets, env=self.env, css=css, js=js, debug_assets=debug_assets, rtl=rtl, assets_params=assets_params) def _links_to_nodes(self, paths, defer_load=False, lazy_load=False, media=None): return [self._link_to_node(path, defer_load=defer_load, lazy_load=lazy_load, media=media) for path in paths] @@ -2564,11 +2567,8 @@ class IrQWeb(models.AbstractModel): return ('script', attributes, None) def _generate_asset_links(self, bundle, css=True, js=True, debug_assets=False, assets_params=None, rtl=False): - aggregable_files, external_assets = self._get_asset_content(bundle, assets_params=assets_params) - external_assets = [node for node in external_assets if (css and node[0].rpartition('.')[2] in STYLE_EXTENSIONS) or (js and node[0].rpartition('.')[2] in SCRIPT_EXTENSIONS)] - asset_bundle = self._get_asset_bundle(bundle, aggregable_files, env=self.env, css=css, js=js, debug_assets=debug_assets, rtl=rtl, assets_params=assets_params) - bundle_links = asset_bundle.get_links() - return external_assets + bundle_links + asset_bundle = self._get_asset_bundle(bundle, css=css, js=js, debug_assets=debug_assets, rtl=rtl, assets_params=assets_params) + return asset_bundle.get_links() def _get_asset_link_urls(self, bundle, debug=False): asset_nodes = self._get_asset_nodes(bundle, js=False, debug=debug) diff --git a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py index 136de75af5f..d5d7a001a19 100644 --- a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py +++ b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py @@ -742,8 +742,7 @@ class TestAssetsBundleWithIRAMock(FileTouchable): def _get_asset(self): with patch.object(type(self.env['ir.asset']), '_get_installed_addons_list', Mock(return_value=self.installed_modules)): - files, _ = self.env['ir.qweb']._get_asset_content(self.stylebundle_name) - return AssetsBundle(self.stylebundle_name, files, env=self.env, debug_assets=True) + return self.env['ir.qweb']._get_asset_bundle(self.stylebundle_name, debug_assets=True) def _bundle(self, asset, should_create, should_unlink, reason=''): self.counter.clear()