From 5a09449ddc874257a02e7abc53a0312e8f56a9c9 Mon Sep 17 00:00:00 2001 From: Antoine Dupuis Date: Tue, 13 Sep 2022 16:37:26 +0000 Subject: [PATCH] [FIX] tools: fix filename resolution in XSD imports With PRs odoo/89145 and enterprise/26393 came new methods for retrieving XSD files and using them for XML validation. The new retrieval method expects modules to provide a 'prefix' that is prepended to the XSD's filename. For example, l10n_cl_edi will name its XSD files 'l10n_cl_edi..xsd'. However, this messes things up when one XSD file needs to import another. For example, l10n_cl_edi.DTE_v10.xsd has the statement '' Currently, the filename resolver has no way of knowing that this should resolve to 'l10n_cl_edi.SiiTypes_v10.xsd', not 'SiiTypes_v10.xsd'. In addition, the new retrieval method saves the ZIP archives received over the network under the ''. Thus 'SiiTypes_v10.xsd' might actually be a ZIP-encoded file. So, we need to do something to fix the imports. Here are two possible solutions: 1. We scrap this 'prefix' stuff and either save the ZIP files under a different name, or we just don't save them. 2. Or, we provide a mechanism for indicating a prefix to the filename resolver. Personally, I don't see the point in saving the ZIP files, and this 'prefix' stuff seems pointless. So I prefer solution 1. But, because I assume there must be a reason to all of that 'prefix' stuff, here is an implementation of solution 2. I'd be keen to know the reason, btw. EDIT: In addition to the first issue described above, we have the second issue that some XSD files returned by the Chilean SII are encoded using ISO-8859-1 encoding (e.g. SiiTypes_v10.xsd). If we leave them in this encoding, then LXML isn't able to parse them when performing imports. closes odoo/odoo#102601 Solution: convert the files to UTF-8 before storing them. X-original-commit: 75555df56475b457331938453657c1f73d231e33 Related: odoo/enterprise#32482 Signed-off-by: Josse Colpaert --- odoo/tools/xml_utils.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/odoo/tools/xml_utils.py b/odoo/tools/xml_utils.py index f93c1077e07..d57c7da5bf5 100644 --- a/odoo/tools/xml_utils.py +++ b/odoo/tools/xml_utils.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- """Utilities for generating, parsing and checking XML/XSD files on top of the lxml.etree module.""" -import base64 import logging import requests import zipfile @@ -20,18 +19,20 @@ class odoo_resolver(etree.Resolver): It will search filenames in the ir.attachments """ - def __init__(self, env): + def __init__(self, env, prefix): super().__init__() self.env = env + self.prefix = prefix def resolve(self, url, id, context): """Search url in ``ir.attachment`` and return the resolved content.""" - attachment = self.env['ir.attachment'].search([('name', '=', url)]) + attachment_name = f'{self.prefix}.{url}' if self.prefix else url + attachment = self.env['ir.attachment'].search([('name', '=', attachment_name)]) if attachment: - return self.resolve_string(base64.b64decode(attachment.datas), context) + return self.resolve_string(attachment.raw, context) -def _check_with_xsd(tree_or_str, stream, env=None): +def _check_with_xsd(tree_or_str, stream, env=None, prefix=None): """Check an XML against an XSD schema. This will raise a UserError if the XML file is not valid according to the @@ -42,12 +43,15 @@ def _check_with_xsd(tree_or_str, stream, env=None): If env is given, it can also be the name of an attachment in the filestore :param odoo.api.Environment env: If it is given, it enables resolving the imports of the schema in the filestore with ir.attachments. + :param str prefix: if given, provides a prefix to try when + resolving the imports of the schema. e.g. prefix='l10n_cl_edi' will + enable 'SiiTypes_v10.xsd' to be resolved to 'l10n_cl_edi.SiiTypes_v10.xsd'. """ if not isinstance(tree_or_str, etree._Element): tree_or_str = etree.fromstring(tree_or_str) parser = etree.XMLParser() if env: - parser.resolvers.add(odoo_resolver(env)) + parser.resolvers.add(odoo_resolver(env, prefix)) if isinstance(stream, str) and stream.endswith('.xsd'): attachment = env['ir.attachment'].search([('name', '=', stream)]) if not attachment: @@ -259,7 +263,7 @@ def load_xsd_files_from_url(env, url, file_name, force_reload=False, return fetched_attachment -def validate_xml_from_attachment(env, xml_content, xsd_name, reload_files_function=None): +def validate_xml_from_attachment(env, xml_content, xsd_name, reload_files_function=None, prefix=None): """Try and validate the XML content with an XSD attachment. If the XSD attachment cannot be found in database, (re)load it. @@ -275,7 +279,7 @@ def validate_xml_from_attachment(env, xml_content, xsd_name, reload_files_functi if env.context.get('skip_xsd', False): return try: - _check_with_xsd(xml_content, xsd_name, env) + _check_with_xsd(xml_content, xsd_name, env, prefix) except FileNotFoundError: if not reload_files_function: _logger.warning("You need to provide a function used to (re)load XSD files")