From 0d3af7ce1cf843cf00fbd719f2007cd0c6615404 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Wed, 28 Dec 2016 13:54:29 +0100 Subject: [PATCH] [FIX] ir.ui.view: `locate_node` of a ns prefixed element According to [1], "If your XPath expression uses namespace prefixes, you must define them in a prefix mapping.". It makes sense because an xpath to a prefixed tag is not able to uniquely locate the tag in the parent view (it is possible to redefine the ns definition of a prefix, so you could have multiple elements matching). We chose to define the nsmap to locate the element in the parent view by using the lxml internal form to qualify a node ({ns def}tagname). This form is not compatible with etree.xpath but is with etree.ETXPath. Fortunately, etree.ETXPath is compatible with the classic xpath expressions so we'll use this one by default in `locate_node`. A test has been introduced to showcase the problem. http://lxml.de/xpathxslt.html#namespaces-and-prefixes: Namespaces and prefixes --- odoo/addons/base/ir/ir_ui_view.py | 2 +- odoo/addons/base/tests/test_qweb.py | 46 +++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/odoo/addons/base/ir/ir_ui_view.py b/odoo/addons/base/ir/ir_ui_view.py index daa8d72f26f..7f9d0340f02 100644 --- a/odoo/addons/base/ir/ir_ui_view.py +++ b/odoo/addons/base/ir/ir_ui_view.py @@ -466,7 +466,7 @@ actual arch. :return: a node in the source matching the spec """ if spec.tag == 'xpath': - nodes = arch.xpath(spec.get('expr')) + nodes = etree.ETXPath(spec.get('expr'))(arch) return nodes[0] if nodes else None elif spec.tag == 'field': # Only compare the field name: a field can be only once in a given view diff --git a/odoo/addons/base/tests/test_qweb.py b/odoo/addons/base/tests/test_qweb.py index c4b279c2f43..284cc104840 100644 --- a/odoo/addons/base/tests/test_qweb.py +++ b/odoo/addons/base/tests/test_qweb.py @@ -373,6 +373,52 @@ class TestQWebNS(TransactionCase): self.assertEquals(len(cac_lines), 2) self.assertEquals(result.count('Appel'), 2) + def test_render_static_xml_with_extension(self): + """ Test the extension of a view by an xpath expression on a ns prefixed element. + """ + # primary view + view1 = self.env['ir.ui.view'].create({ + 'name': "dummy", + 'type': 'qweb', + 'arch': """ + + + + + Apples + Bananas + + + + + """ + }) + # extension patching the primary view + view2 = self.env['ir.ui.view'].create({ + 'name': "dummy_ext", + 'type': 'qweb', + 'inherit_id': view1.id, + 'arch': """ + + Oranges + + """ + }) + + expected_result = """ + + + + Apples + Bananas + Oranges + + + + """ + + self.assertEquals(dedent_and_strip(view1.with_context(check_view_ids=[view1.id, view2.id]).render()), dedent_and_strip(expected_result)) + from copy import deepcopy class FileSystemLoader(object):