[IMP] base: constraint to check view root node is matching the view type

This PR follows odoo/odoo#108073

Just ensure the root node of the view architecture matches
the view type.

e.g.

Valid:
```xml
<form>
...
</form>
```

Invalid:
```xml
<data>
  <div>
    ...
  </div>
  <form>
    ...
  </form>
</data>
```

closes odoo/odoo#108525

Related: odoo/upgrade#4155
Related: odoo/enterprise#35501
Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
This commit is contained in:
Denis Ledoux
2023-01-06 15:32:55 +01:00
parent 7fd5cec72b
commit d814c9bee4
2 changed files with 42 additions and 4 deletions
+11 -4
View File
@@ -1394,7 +1394,7 @@ actual arch.
# view validation
#-------------------------------------------------------------------
def _validate_view(self, node, model_name, editable=True, full=False):
def _validate_view(self, node, model_name, view_type=None, editable=True, full=False):
""" Validate the given architecture node, and return its corresponding
NameManager.
@@ -1407,6 +1407,13 @@ actual arch.
"""
self.ensure_one()
view_type = view_type or self.type
if node.tag != view_type:
self._raise_view_error(_(
'The root node of a %(view_type)s view should be a <%(view_type)s>, not a <%(tag)s>',
view_type=view_type, tag=node.tag,
), node)
if model_name not in self.env:
self._raise_view_error(_('Model not found: %(model)s', model=model_name), node)
@@ -1492,7 +1499,7 @@ actual arch.
if len(searchpanels) > 1:
self._raise_view_error(_('Search tag can only contain one search panel'), node)
node.remove(searchpanels[0])
self._validate_view(searchpanels[0], name_manager.model._name,
self._validate_view(searchpanels[0], name_manager.model._name, view_type="searchpanel",
editable=False, full=node_info['validate'])
def _validate_tag_field(self, node, name_manager, node_info):
@@ -1531,7 +1538,7 @@ actual arch.
continue
node.remove(child)
sub_manager = self._validate_view(
child, field.comodel_name, editable=node_info['editable'], full=validate,
child, field.comodel_name, view_type=child.tag, editable=node_info['editable'], full=validate,
)
for fname, groups_uses in sub_manager.mandatory_parent_fields.items():
for groups, use in groups_uses.items():
@@ -1645,7 +1652,7 @@ actual arch.
groupby_node = E.groupby(*node)
# validate the node as a nested view
sub_manager = self._validate_view(
groupby_node, field.comodel_name, editable=False, full=node_info['validate'],
groupby_node, field.comodel_name, view_type="groupby", editable=False, full=node_info['validate'],
)
name_manager.has_field(node, name)
for fname, groups_uses in sub_manager.mandatory_parent_fields.items():
+31
View File
@@ -1548,6 +1548,11 @@ class TestViews(ViewCase):
kw.setdefault('active', True)
if 'arch_db' in kw:
arch_db = kw['arch_db']
if kw.get('inherit_id'):
self.cr.execute('SELECT type FROM ir_ui_view WHERE id = %s', [kw['inherit_id']])
kw['type'] = self.cr.fetchone()[0]
else:
kw['type'] = etree.fromstring(arch_db).tag
kw['arch_db'] = Json({'en_US': arch_db}) if self.env.lang == 'en_US' else Json({'en_US': arch_db, self.env.lang: arch_db})
keys = sorted(kw)
@@ -1558,6 +1563,32 @@ class TestViews(ViewCase):
self.cr.execute(query, kw)
return self.cr.fetchone()[0]
def test_view_root_node_matches_view_type(self):
view = self.View.create({
'name': 'foo',
'model': 'ir.ui.view',
'arch': """
<form>
</form>
""",
})
self.assertEqual(view.type, 'form')
with self.assertRaises(ValidationError):
self.View.create({
'name': 'foo',
'model': 'ir.ui.view',
'type': 'form',
'arch': """
<data>
<div>
</div>
<form>
</form>
</data>
""",
})
def test_custom_view_validation(self):
model = 'ir.actions.act_url'
validate = partial(self.View._validate_custom_views, model)