From 30db58e5e6f423e1328fc29d9d8a239ed8a2ce8e Mon Sep 17 00:00:00 2001 From: Xavier-Do Date: Fri, 1 Dec 2023 10:36:29 +0000 Subject: [PATCH] [FIX] base, website: don't cleanup invalid bundle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this commit the clean_assetbundle could unlink invalid attachment, mostly when generating a no website assetbundle with a different version than a website one, the website assetbundle will be deleted. Generating a new asset bundle attachment /web/assets/439-b4c80c3/1/web.assets_frontend.min.css (id:439) Generating a new asset bundle attachment /web/assets/440-3723971/web.assets_frontend.min.css (id:440) Deleting attachments [439] (matching /web/assets/%-%/web.assets_frontend.min.css) because it was replaced with /web/assets/%-3723971/%%% The issue is that %-%/ will match 439-b4c80c3/1/ and not only 439-b4c80c3/ Note that it looks like this issue existed for a while but was invisible because before 16.4 clean_attachment was invalidating the ormcache, hiding the fact that a still valid asset was deleted and regenerated. The proposed fix replaces the domain with %-_______/. The unique is always 7 character long. Note that this change was already made in 17.0 when removing the id from the asset url so this doesn't need to be completely forward-ported. opw-3558552 closes odoo/odoo#145452 X-original-commit: 2ac466547e01bfd65415a53ae1efc9d45d9299fb Signed-off-by: Xavier Dollé (xdo) --- addons/website/tests/test_assets.py | 36 +++++++++++++++++++ odoo/addons/base/models/assetsbundle.py | 3 +- .../tests/test_assetsbundle.py | 4 +-- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/addons/website/tests/test_assets.py b/addons/website/tests/test_assets.py index 2ff336a9cfe..35f31350aa8 100644 --- a/addons/website/tests/test_assets.py +++ b/addons/website/tests/test_assets.py @@ -100,6 +100,42 @@ class TestWebsiteAssets(odoo.tests.HttpCase): new_public_assets_links = re.findall(r'(/web/assets/\d+/\w{7}/web.assets_frontend\..+)"/>', page) self.assertEqual(new_admin_assets_links, new_public_assets_links, "t-cache should have been invalidated for public user too") + def test_invalid_unlink(self): + self.env['ir.attachment'].search([('url', '=like', '/web/assets/%')]).unlink() + + asset_bundle_xmlid = 'web.assets_frontend' + website_default = self.env['website'].search([], limit=1) + + code = b"document.body.dataset.hello = 'world';" + attach = self.env['ir.attachment'].create({ + 'name': 'EditorExtension.css', + 'mimetype': 'text/css', + 'raw': code, + }) + custom_url = '/_custom/web/content/%s/%s' % (attach.id, attach.name) + attach.url = custom_url + + self.env['ir.asset'].create({ + 'name': 'EditorExtension', + 'bundle': asset_bundle_xmlid, + 'path': custom_url, + 'website_id': website_default.id, + }) + + website_bundle = self.env['ir.qweb']._get_asset_bundle(asset_bundle_xmlid, assets_params={'website_id': website_default.id}) + self.assertIn(custom_url, [f['url'] for f in website_bundle.files]) + base_website_css_version = website_bundle.get_version('css') + + no_website_bundle = self.env['ir.qweb']._get_asset_bundle(asset_bundle_xmlid) + self.assertNotIn(custom_url, [f['url'] for f in no_website_bundle.files]) + self.assertNotEqual(no_website_bundle.get_version('css'), base_website_css_version) + + website_attach = website_bundle.css() + self.assertTrue(website_attach.exists()) + no_website_bundle.css() + self.assertTrue(website_attach.exists(), 'attachment for website should still exist after generating attachment for no website') + + @odoo.tests.tagged('-at_install', 'post_install') class TestWebAssets(odoo.tests.HttpCase): def test_assets_url_validation(self): diff --git a/odoo/addons/base/models/assetsbundle.py b/odoo/addons/base/models/assetsbundle.py index 6f5d85bc9f4..e78e17533d4 100644 --- a/odoo/addons/base/models/assetsbundle.py +++ b/odoo/addons/base/models/assetsbundle.py @@ -150,7 +150,7 @@ class AssetsBundle(object): self._checksum_cache[asset_type] = hashlib.sha512(unique_descriptor.encode()).hexdigest()[:64] return self._checksum_cache[asset_type] - def get_asset_url(self, unique='%', extension='%', ignore_params=False): + def get_asset_url(self, unique=ANY_UNIQUE, extension='%', ignore_params=False): direction = '.rtl' if self.is_css(extension) and self.rtl else '' bundle_name = f"{self.name}{direction}.{extension}" return self.env['ir.asset']._get_asset_bundle_url(bundle_name, unique, self.assets_params, ignore_params) @@ -264,6 +264,7 @@ class AssetsBundle(object): } attachment = self.env['ir.attachment'].with_user(SUPERUSER_ID).create(values) attachment_id = attachment.id + self.clean_attachments(extension) return self.env['ir.attachment'].sudo().browse(attachment_id) diff --git a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py index 665224c9f6a..4e49ba99abd 100644 --- a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py +++ b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py @@ -15,7 +15,7 @@ import base64 import odoo from odoo import api, http from odoo.addons import __path__ as ADDONS_PATH -from odoo.addons.base.models.assetsbundle import AssetsBundle +from odoo.addons.base.models.assetsbundle import AssetsBundle, ANY_UNIQUE from odoo.addons.base.models.ir_asset import AssetPaths from odoo.addons.base.models.ir_attachment import IrAttachment from odoo.modules.module import get_manifest @@ -147,7 +147,7 @@ class TestJavascriptAssetsBundle(FileTouchable): bundle = self.jsbundle_name if extension in ['js', 'min.js'] else self.cssbundle_name direction = '.rtl' if rtl else '' bundle_name = f"{bundle}{direction}.{extension}" - url = self.env['ir.asset']._get_asset_bundle_url(bundle_name, '%', {}) + url = self.env['ir.asset']._get_asset_bundle_url(bundle_name, ANY_UNIQUE, {}) domain = [('url', '=like', url)] return self.env['ir.attachment'].search(domain)