diff --git a/odoo/addons/base/ir/ir_qweb/qweb.py b/odoo/addons/base/ir/ir_qweb/qweb.py
index fcd24de2f79..89281963de7 100644
--- a/odoo/addons/base/ir/ir_qweb/qweb.py
+++ b/odoo/addons/base/ir/ir_qweb/qweb.py
@@ -702,10 +702,10 @@ class QWeb(object):
def _compile_static_node(self, el, options):
""" Compile a purely static element into a list of AST nodes. """
- extra_attrib = {}
if not el.nsmap:
unqualified_el_tag = el_tag = el.tag
content = self._compile_directive_content(el, options)
+ attrib = el.attrib
else:
# Etree will remove the ns prefixes indirection by inlining the corresponding
# nsmap definition into the tag attribute. Restore the tag and prefix here.
@@ -714,13 +714,25 @@ class QWeb(object):
if el.prefix:
el_tag = '%s:%s' % (el.prefix, el_tag)
+ attrib = {}
# If `el` introduced new namespaces, write them as attribute by using the
- # `extra_attrib` dict.
+ # `attrib` dict.
for ns_prefix, ns_definition in set(el.nsmap.items()) - set(options['nsmap'].items()):
if ns_prefix is None:
- extra_attrib['xmlns'] = ns_definition
+ attrib['xmlns'] = ns_definition
else:
- extra_attrib['xmlns:%s' % ns_prefix] = ns_definition
+ attrib['xmlns:%s' % ns_prefix] = ns_definition
+
+ # Etree will also remove the ns prefixes indirection in the attributes. As we only have
+ # the namespace definition, we'll use an nsmap where the keys are the definitions and
+ # the values the prefixes in order to get back the right prefix and restore it.
+ nsprefixmap = {v: k for k, v in options['nsmap'].items() + el.nsmap.items()}
+ for key, value in el.attrib.items():
+ attrib_qname = etree.QName(key)
+ if attrib_qname.namespace:
+ attrib['%s:%s' % (nsprefixmap[attrib_qname.namespace], attrib_qname.localname)] = value
+ else:
+ attrib[key] = value
# Update the dict of inherited namespaces before continuing the recursion. Note:
# since `options['nsmap']` is a dict (and therefore mutable) and we do **not**
@@ -733,7 +745,7 @@ class QWeb(object):
if unqualified_el_tag == 't':
return content
- tag = u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(unicodifier(value))) for name, value in chain(el.attrib.iteritems(), extra_attrib.iteritems())]))
+ tag = u'<%s%s' % (el_tag, u''.join([u' %s="%s"' % (name, escape(unicodifier(value))) for name, value in attrib.iteritems()]))
if unqualified_el_tag in self._void_elements:
return [self._append(ast.Str(tag + '/>'))] + content
else:
@@ -742,15 +754,26 @@ class QWeb(object):
def _compile_static_attributes(self, el, options):
""" Compile the static attributes of the given element into a list of
pairs (name, expression AST). """
+ # Etree will also remove the ns prefixes indirection in the attributes. As we only have
+ # the namespace definition, we'll use an nsmap where the keys are the definitions and
+ # the values the prefixes in order to get back the right prefix and restore it.
+ nsprefixmap = {v: k for k, v in options['nsmap'].items() + el.nsmap.items()}
+
nodes = []
for key, value in el.attrib.iteritems():
if not key.startswith('t-'):
+ attrib_qname = etree.QName(key)
+ if attrib_qname.namespace:
+ key = '%s:%s' % (nsprefixmap[attrib_qname.namespace], attrib_qname.localname)
nodes.append((key, ast.Str(value)))
return nodes
def _compile_dynamic_attributes(self, el, options):
""" Compile the dynamic attributes of the given element into a list of
- pairs (name, expression AST). """
+ pairs (name, expression AST).
+
+ We do not support namespaced dynamic attributes.
+ """
nodes = []
for name, value in el.attrib.iteritems():
if name.startswith('t-attf-'):
@@ -883,6 +906,7 @@ class QWeb(object):
else:
# Etree will remove the ns prefixes indirection by inlining the corresponding
# nsmap definition into the tag attribute. Restore the tag and prefix here.
+ # Note: we do not support namespace dynamic attributes.
unqualified_el_tag = etree.QName(el.tag).localname
el_tag = unqualified_el_tag
if el.prefix:
diff --git a/odoo/addons/base/tests/test_qweb.py b/odoo/addons/base/tests/test_qweb.py
index 67cb98ffcb7..dcf41b092d6 100644
--- a/odoo/addons/base/tests/test_qweb.py
+++ b/odoo/addons/base/tests/test_qweb.py
@@ -168,6 +168,21 @@ class TestQWebNS(TransactionCase):
self.assertEquals(dedent_and_strip(view1.render()), dedent_and_strip(expected_result))
+ def test_render_static_xml_with_namespace_3(self):
+ expected_result = """
+
+ """
+
+ view1 = self.env['ir.ui.view'].create({
+ 'name': "dummy",
+ 'type': 'qweb',
+ 'arch': """
+ %s
+ """ % expected_result
+ })
+
+ self.assertEquals(dedent_and_strip(view1.render()), dedent_and_strip(expected_result))
+
def test_render_dynamic_xml_with_namespace_t_esc(self):
""" Test that rendering a template containing a node having both an ns declaration and a t-esc attribute correctly
handles the t-esc attribute and keep the ns declaration.
@@ -325,6 +340,36 @@ class TestQWebNS(TransactionCase):
self.assertEquals(dedent_and_strip(view1.render(dict(version_id=1.0))), dedent_and_strip(expected_result))
+ def test_render_static_xml_with_namespaced_attributes(self):
+ view1 = self.env['ir.ui.view'].create({
+ 'name': "dummy",
+ 'type': 'qweb',
+ 'arch': """
+
+ abc
+
+ """
+ })
+
+ expected_result = """abc"""
+
+ self.assertEquals(dedent_and_strip(view1.render()), dedent_and_strip(expected_result))
+
+ def test_render_dynamic_xml_with_namespaced_attributes(self):
+ view1 = self.env['ir.ui.view'].create({
+ 'name': "dummy",
+ 'type': 'qweb',
+ 'arch': """
+
+
+
+ """
+ })
+
+ expected_result = """abc"""
+
+ self.assertEquals(dedent_and_strip(view1.render()), dedent_and_strip(expected_result))
+
def test_render_static_xml_with_t_call(self):
view1 = self.env['ir.ui.view'].create({
'name': "dummy",