[FIX] website: prevent circular dependency of duplicated fields

Steps to reproduce the bug:
- Drop a "Form" snippet on the website.
- Add three fields and rename them by "a", "b" and "b".
- Change the conditional visibility of the first "b" and make it depend
on "a".
- Change the conditional visibility of "a" and make it depend on "b".
- Save.

-> Traceback "Maximum call stack exceeded" appears.

A field with a conditional visibility is visible if at at least one
field with the dependency name is visible. The problem is that in our
case, it exists a circular dependency between "a" and one of the "b"
leading to an infinite loop during this check. Before this commit all
the fields of the form were checked and all the labels of the fields
that do not create a circular dependency were proposed in the file
visibility selector. The problem is that in our case, one of the "b"
field does not create a circular dependency while the other does. To
solve the problem, the `_recursiveFindCircular()` function has been
adapted in order to not propose a label that would create a circular
dependency in the file visibility selector.

task-3291044

X-original-commit: f010b128be29e884c89dd58f1c575a163e229d5b
Part-of: odoo/odoo#140928
This commit is contained in:
Louis (loco)
2023-11-06 11:16:42 +00:00
parent eae9980e0c
commit 34e4049b80
2 changed files with 35 additions and 10 deletions
@@ -266,11 +266,12 @@ const FieldEditor = FormEditor.extend({
* Returns the name of the field
*
* @private
* @param {HTMLElement} fieldEl
* @returns {string}
*/
_getFieldName: function () {
const multipleName = this.$target[0].querySelector('.s_website_form_multiple');
return multipleName ? multipleName.dataset.name : this.$target[0].querySelector('.s_website_form_input').name;
_getFieldName: function (fieldEl = this.$target[0]) {
const multipleName = fieldEl.querySelector('.s_website_form_multiple');
return multipleName ? multipleName.dataset.name : fieldEl.querySelector('.s_website_form_input').name;
},
/**
* Returns the type of the field, can be used for both custom and existing fields
@@ -1277,14 +1278,26 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({
*/
_renderCustomXML: async function (uiFragment) {
const recursiveFindCircular = (el) => {
if (el.dataset.visibilityDependency === this._getFieldName()) {
return true;
const dependentFieldName = this._getFieldName(el);
// Get all the fields that have the same label as the dependent
// field.
let dependentFieldEls = Array.from(this.formEl
.querySelectorAll(`.s_website_form_input[name="${dependentFieldName}"]`))
.map((el) => el.closest(".s_website_form_field"));
// Remove the duplicated fields. This could happen if the field has
// multiple inputs ("Multiple Checkboxes" for example.)
dependentFieldEls = new Set(dependentFieldEls);
const fieldName = this._getFieldName();
for (const dependentFieldEl of dependentFieldEls) {
if (dependentFieldEl.dataset.visibilityDependency === fieldName) {
return true;
}
const dependencyInputEl = this._getDependencyEl(dependentFieldEl);
if (dependencyInputEl && recursiveFindCircular(dependencyInputEl.closest(".s_website_form_field"))) {
return true;
}
}
const dependencyInputEl = this._getDependencyEl(el);
if (!dependencyInputEl) {
return false;
}
return recursiveFindCircular(dependencyInputEl.closest('.s_website_form_field'));
return false;
};
// Update available visibility dependencies
@@ -182,6 +182,18 @@
content: 'Change the label position of the phone field',
trigger: 'we-button[data-select-label-position="right"]',
},
...addCustomField("char", "text", "Conditional Visibility Check 1", false),
...addCustomField("char", "text", "Conditional Visibility Check 2", false),
...selectButtonByData("data-set-visibility='conditional'"),
...selectButtonByData("data-set-visibility-dependency='Conditional Visibility Check 1'"),
...addCustomField("char", "text", "Conditional Visibility Check 2", false),
...selectFieldByLabel("Conditional Visibility Check 1"),
...selectButtonByData("data-set-visibility='conditional'"),
{
content: "Check that 'Conditional Visibility Check 2' is not in the list of the visibility selector of Conditional Visibility Check 1",
trigger: "we-select[data-name='hidden_condition_opt']:not(:has(we-button[data-set-visibility-dependency='Conditional Visibility Check 2']))",
run: () => null,
},
...addExistingField('email_cc', 'text', 'Test conditional visibility', false, {visibility: CONDITIONALVISIBILITY, condition: 'odoo'}),
...addExistingField('date', 'text', 'Test Date', true),