diff options
| author | fja-odoo <fja@odoo.com> | 2020-05-13 08:52:12 +0000 |
|---|---|---|
| committer | fja-odoo <fja@odoo.com> | 2020-05-20 13:20:35 +0000 |
| commit | 049745c5fffcd11894cd5f9b38078c993d2ef969 (patch) | |
| tree | 0c52321af364561becd5fe193ddcc4ad9f71cb81 | |
| parent | e3b4f3ddf43f309eea7272fe78dcbb2eb4e90a6c (diff) | |
[FIX] web_editor: fix _view_get infinite recursion
The _view_get function is a recursive function used to retieve all the
views related to a view (inherited or t-called).
The issue is that by an odd set of circumstances it is possible to have
a loop in the view graph. Resulting in the recursive function being
called until a "maximum recursion depth exceeded" error occurs.
Example of a loop: A t-call B and A inherit from B
This is possible on an update of a view that has been forked by website:
If the view A was doing a t-call on B and is has been duplicated with
the arch modified.
When we update with the changes A now inherit from B instead of t-call B
Since the arch was modified it will not be updated so A will still
t-call B but the inherit_id of A is unchanged so it will be updated to
reference B resulting in a loop.
closes odoo/odoo#51620
X-original-commit: 63e1e84ebd7e609d6e06d83dc0f0092688b013eb
Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
| -rw-r--r-- | addons/web_editor/models/ir_ui_view.py | 15 | ||||
| -rw-r--r-- | addons/web_editor/tests/__init__.py | 1 | ||||
| -rw-r--r-- | addons/web_editor/tests/test_views.py | 27 |
3 files changed, 37 insertions, 6 deletions
diff --git a/addons/web_editor/models/ir_ui_view.py b/addons/web_editor/models/ir_ui_view.py index c56fdadbb6e..a3bfd773a92 100644 --- a/addons/web_editor/models/ir_ui_view.py +++ b/addons/web_editor/models/ir_ui_view.py @@ -224,7 +224,7 @@ class IrUiView(models.Model): # Used by translation mechanism, SEO and optional templates @api.model - def _views_get(self, view_id, get_children=True, bundles=False, root=True): + def _views_get(self, view_id, get_children=True, bundles=False, root=True, visited=None): """ For a given view ``view_id``, should return: * the view itself * all views inheriting from it, enabled or not @@ -238,6 +238,8 @@ class IrUiView(models.Model): _logger.warning("Could not find view object with view_id '%s'", view_id) return self.env['ir.ui.view'] + if visited is None: + visited = [] while root and view.inherit_id: view = view.inherit_id @@ -252,8 +254,8 @@ class IrUiView(models.Model): called_view = self._view_obj(child.get('t-call', child.get('t-call-assets'))) except ValueError: continue - if called_view and called_view not in views_to_return: - views_to_return += self._views_get(called_view, get_children=get_children, bundles=bundles) + if called_view and called_view not in views_to_return and called_view.id not in visited: + views_to_return += self._views_get(called_view, get_children=get_children, bundles=bundles, visited=visited + views_to_return.ids) if not get_children: return views_to_return @@ -263,9 +265,10 @@ class IrUiView(models.Model): # Keep children in a deterministic order regardless of their applicability for extension in extensions.sorted(key=lambda v: v.id): # only return optional grandchildren if this child is enabled - for ext_view in self._views_get(extension, get_children=extension.active, root=False): - if ext_view not in views_to_return: - views_to_return += ext_view + if extension.id not in visited: + for ext_view in self._views_get(extension, get_children=extension.active, root=False, visited=visited + views_to_return.ids): + if ext_view not in views_to_return: + views_to_return += ext_view return views_to_return @api.model diff --git a/addons/web_editor/tests/__init__.py b/addons/web_editor/tests/__init__.py index 8b77588a13c..d399772d6ca 100644 --- a/addons/web_editor/tests/__init__.py +++ b/addons/web_editor/tests/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import test_converter +from . import test_views diff --git a/addons/web_editor/tests/test_views.py b/addons/web_editor/tests/test_views.py new file mode 100644 index 00000000000..31a6892dc29 --- /dev/null +++ b/addons/web_editor/tests/test_views.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.tests import TransactionCase + + +class TestViews(TransactionCase): + def test_infinite_inherit_loop(self): + # Creates an infinite loop: A t-call B and A inherit from B + View = self.env['ir.ui.view'] + first_view = View.create({ + 'name': 'Test View 1', + 'type': 'qweb', + 'arch': '<div>Hello World</div>', + 'key': 'web_editor.test_first_view', + }) + second_view = View.create({ + 'name': 'Test View 2', + 'type': 'qweb', + 'arch': '<t t-call="web_editor.test_first_view"/>', + 'key': 'web_editor.test_second_view', + }) + second_view.write({ + 'inherit_id': first_view.id, + }) + # Test for RecursionError: maximum recursion depth exceeded in this function + View._views_get(first_view) |
