[FIX] base: res.config.settings must not have <t> nodes

The res.config.settings form view is handled with a custom widget
expecting a very specific structure.

a `<t>` node in the middle of
`<div class="settings">`
and
`<div class="app_settings_block">`
screws things up, and the app block will no longer be shown as expected
in the web interface.

This issue has been introduced with
odoo/odoo#103799

The current revision reverts the change so it becomes as before,
the `<t>` blocks with only `groups="..."` are removed from the
architecture, even if they have not been added by the postprocessing
itself.

An alternative fix is done,
directly in the `web_studio` module of odoo/enterprise,
to overcome the issue odoo/odoo#103799 was solving.

Both issues are covered with unit tests:
- the issue odoo/odoo#103799 was solving is covered with the unit test
  added in the related odoo/enterprise#33075
- the issue solved by the current revision is covered with units tests
  added in this revision directly.

closes odoo/odoo#104542

X-original-commit: 56d2de23919f9efa3cd1eef756725e8bc9b86d81
Related: odoo/enterprise#33410
Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
This commit is contained in:
Denis Ledoux
2022-10-28 21:33:58 +02:00
parent cc6546677d
commit ab7ca604be
2 changed files with 64 additions and 2 deletions
+2 -2
View File
@@ -1036,7 +1036,7 @@ actual arch.
for node in tree.xpath('//*[@groups]'):
if not self.user_has_groups(node.attrib.pop('groups')):
node.getparent().remove(node)
elif node.get('postprocess_remove'):
elif node.tag == 't' and not node.attrib:
# Move content of <t> blocks created in `_postprocess_tag_field` to the parent
# and remove the <t> node.
# This is to keep the structure
@@ -1223,7 +1223,7 @@ actual arch.
# set on the field in the Python model
# e.g. <t groups="base.group_system"><field name="foo" groups="base.group_no_one"/></t>
# The <t> node will be removed later, in _postprocess_access_rights.
node_t = E.t(groups=field.groups, postprocess_remove="1")
node_t = E.t(groups=field.groups)
node.getparent().replace(node, node_t)
node_t.append(node)
else:
+62
View File
@@ -85,6 +85,68 @@ class TestResConfig(TransactionCase):
# Check returned value
self.assertEqual(res.args[0], self.expected_final_error_msg_wo_menu)
def test_40_view_expected_architecture(self):
"""Tests the res.config.settings form view architecture expected by the web client.
The res.config.settings form view is handled with a custom widget expecting a very specific
structure. This architecture is tested extensively in Javascript unit tests.
Here we briefly ensure the view sent by the server to the web client has the right architecture,
the right blocks with the right classes in the right order.
This tests is to ensure the specification/requirements are listed and tested server side, and
if a change occurs in future development, this test will need to be adapted to specify these changes."""
view = self.env['ir.ui.view'].create({
'name': 'foo',
'type': 'form',
'model': 'res.config.settings',
'inherit_id': self.env.ref('base.res_config_settings_view_form').id,
'arch': """
<xpath expr="//div[hasclass('settings')]" position="inside">
<t groups="base.group_system">
<div class="app_settings_block" data-string="Foo" string="Foo" data-key="foo">
<h2>Foo</h2>
</div>
</t>
</xpath>
""",
})
arch = self.env['res.config.settings'].get_view(view.id)['arch']
tree = etree.fromstring(arch)
self.assertTrue(tree.xpath("""
//form[@class="oe_form_configuration o_base_settings"]
/div[@class="o_setting_container"]
/div[@class="settings"]
/div[@class="app_settings_block"][@data-key="foo"]
"""), 'The res.config.settings form view architecture is not what is expected by the web client.')
def test_50_view_expected_architecture_t_node_groups(self):
"""Tests the behavior of the res.config.settings form view postprocessing when a block `app_settings_block`
is wrapped in a `<t groups="...">`, which is used when you need to display an app settings section
only for users part of two groups at the same time."""
view = self.env['ir.ui.view'].create({
'name': 'foo',
'type': 'form',
'model': 'res.config.settings',
'inherit_id': self.env.ref('base.res_config_settings_view_form').id,
'arch': """
<xpath expr="//div[hasclass('settings')]" position="inside">
<t groups="base.group_system">
<div class="app_settings_block" data-string="Foo"
string="Foo" data-key="foo" groups="base.group_no_one">
<h2>Foo</h2>
</div>
</t>
</xpath>
""",
})
with self.debug_mode():
arch = self.env['res.config.settings'].get_view(view.id)['arch']
tree = etree.fromstring(arch)
# The <t> must be removed from the structure
self.assertFalse(tree.xpath('//t'), 'The `<t groups="...">` block must not remain in the view')
self.assertTrue(tree.xpath("""
//div[@class="settings"]
/div[@class="app_settings_block"][@data-key="foo"]
"""), 'The `class="app_settings_block"` block must be a direct child of the `class="settings"` block')
@tagged('post_install', '-at_install')
class TestResConfigExecute(TransactionCase):