From 7e86933d94fbbfbaea57742abbbcf6409a39c357 Mon Sep 17 00:00:00 2001 From: Jorge Pinna Puissant Date: Mon, 27 Jun 2022 10:05:38 +0000 Subject: [PATCH] [FIX] web: evaluate string domain in modifiers Before this commit, in `getModifier` method when a modifier contains a domain in a string, the domain is not evaluated and just converted into a boolean value that is if the string is not empty then the method will return `true` and `false` otherwise. Also, the `applyInvisible` method does not take into account an invisible domain in a string. That is if the `invisible` variable contains a domain in an `Array` then the domain is evaluated otherwise the domain is considered as a boolean value. This commit checks if the modifier contains a boolean value as value to evaluate it as a boolean and return it otherwise the modifier value is returned in the `getModifier` method. In the `applyInvisible`, this commit allows to evaluate the domain if the domain is containing in a string. Part-of: odoo/odoo#92475 Co-authored-by: Xavier BOL (xbo) --- .../static/src/views/form/form_renderer.js | 7 ++-- .../static/src/views/kanban/kanban_record.js | 7 ++-- addons/web/static/src/views/utils.js | 2 +- addons/web/static/src/views/view_compiler.js | 15 +------ .../tests/views/form/form_compiler_tests.js | 41 ++++++++++++++++++- 5 files changed, 51 insertions(+), 21 deletions(-) diff --git a/addons/web/static/src/views/form/form_renderer.js b/addons/web/static/src/views/form/form_renderer.js index b60176448cb..cf6a769efb2 100644 --- a/addons/web/static/src/views/form/form_renderer.js +++ b/addons/web/static/src/views/form/form_renderer.js @@ -5,9 +5,10 @@ import { Field } from "@web/views/fields/field"; import { ButtonBox } from "@web/views/form/button_box/button_box"; import { InnerGroup, OuterGroup } from "@web/views/form/form_group/form_group"; import { ViewButton } from "@web/views/view_button/view_button"; -import { evalDomainFromRecord, useViewCompiler } from "@web/views/view_compiler"; +import { useViewCompiler } from "@web/views/view_compiler"; import { useBounceButton } from "@web/views/view_hook"; import { Widget } from "@web/views/widgets/widget"; +import { evalDomain } from "../utils"; import { FormCompiler } from "./form_compiler"; import { FormLabel } from "./form_label"; import { StatusBarButtons } from "./status_bar_buttons/status_bar_buttons"; @@ -25,8 +26,8 @@ export class FormRenderer extends Component { }); } - evalDomainFromRecord() { - return evalDomainFromRecord(...arguments); + evalDomainFromRecord(record, expr) { + return evalDomain(expr, record.evalContext); } } diff --git a/addons/web/static/src/views/kanban/kanban_record.js b/addons/web/static/src/views/kanban/kanban_record.js index a2bfeede376..b741369e4a5 100644 --- a/addons/web/static/src/views/kanban/kanban_record.js +++ b/addons/web/static/src/views/kanban/kanban_record.js @@ -11,9 +11,10 @@ import { url } from "@web/core/utils/urls"; import { Field } from "@web/views/fields/field"; import { fileTypeMagicWordMap } from "@web/views/fields/image/image_field"; import { ViewButton } from "@web/views/view_button/view_button"; -import { evalDomainFromRecord, useViewCompiler } from "@web/views/view_compiler"; +import { useViewCompiler } from "@web/views/view_compiler"; import { Widget } from "@web/views/widgets/widget"; import { useTooltip } from "@web/core/tooltip/tooltip_hook"; +import { evalDomain } from "../utils"; import { KANBAN_BOX_ATTRIBUTE, KANBAN_TOOLTIP_ATTRIBUTE } from "./kanban_arch_parser"; import { KanbanCompiler } from "./kanban_compiler"; import { KanbanCoverImageDialog } from "./kanban_cover_image_dialog"; @@ -228,8 +229,8 @@ export class KanbanRecord extends Component { }; } - evalDomainFromRecord() { - return evalDomainFromRecord(...arguments); + evalDomainFromRecord(record, expr) { + return evalDomain(expr, record.evalContext); } getRecordClasses() { diff --git a/addons/web/static/src/views/utils.js b/addons/web/static/src/views/utils.js index 74c0a7efb1a..88b5bff104c 100644 --- a/addons/web/static/src/views/utils.js +++ b/addons/web/static/src/views/utils.js @@ -89,7 +89,7 @@ export const computeReportMeasures = ( * @returns {boolean} */ export function evalDomain(modifier, evalContext) { - if (Array.isArray(modifier)) { + if (modifier && typeof modifier !== "boolean") { modifier = new Domain(modifier).contains(evalContext); } return Boolean(modifier); diff --git a/addons/web/static/src/views/view_compiler.js b/addons/web/static/src/views/view_compiler.js index fa41b3ea459..5d13c7ebe99 100644 --- a/addons/web/static/src/views/view_compiler.js +++ b/addons/web/static/src/views/view_compiler.js @@ -1,6 +1,5 @@ /** @odoo-module **/ -import { Domain } from "@web/core/domain"; import { append, combineAttributes, @@ -142,10 +141,6 @@ export function encodeObjectForTemplate(obj) { return `"${encodeURI(JSON.stringify(obj))}"`; } -export function evalDomainFromRecord(record, expr) { - return new Domain(expr).contains(record.evalContext); -} - /** * @param {Element} el * @param {string} modifierName @@ -156,7 +151,7 @@ export function getModifier(el, modifierName) { // modifiers' string are evaluated to their boolean or array form const modifiers = JSON.parse(el.getAttribute("modifiers") || "{}"); const mod = modifierName in modifiers ? modifiers[modifierName] : false; - return Array.isArray(mod) ? mod : !!mod; + return typeof mod !== "boolean" ? mod : !!mod; } /** @@ -251,13 +246,7 @@ export class ViewCompiler { " and " ); } else { - let expr; - if (Array.isArray(invisible)) { - expr = `evalDomainFromRecord(props.record,${JSON.stringify(invisible)})`; - } else { - expr = invisible; - } - appendAttr(compiled, "class", `o_invisible_modifier:${expr}`); + appendAttr(compiled, "class", `o_invisible_modifier:${invisible}`); } return compiled; } diff --git a/addons/web/static/tests/views/form/form_compiler_tests.js b/addons/web/static/tests/views/form/form_compiler_tests.js index 5e5a3287cfc..89023e91967 100644 --- a/addons/web/static/tests/views/form/form_compiler_tests.js +++ b/addons/web/static/tests/views/form/form_compiler_tests.js @@ -3,6 +3,7 @@ import { makeView } from "../helpers"; import { setupViewRegistries } from "@web/../tests/views/helpers"; import { FormCompiler } from "@web/views/form/form_compiler"; import { registry } from "@web/core/registry"; +import { click, getFixture } from "../../helpers/utils"; function compileTemplate(arch) { const parser = new DOMParser(); @@ -141,12 +142,28 @@ QUnit.module("Form Compiler", () => { assert.areContentEquivalent(compileTemplate(arch), expected); }); + + QUnit.test("compile invisible containing string as domain", async (assert) => { + const arch = /*xml*/ ` +
+ +
+
+ `; + + const expected = /*xml*/ ` +
+
+ `; + assert.areContentEquivalent(compileTemplate(arch), expected); + }); }); QUnit.module("Form Renderer", (hooks) => { - let serverData; + let target, serverData; hooks.beforeEach(() => { + target = getFixture(); setupViewRegistries(); serverData = { models: { @@ -163,6 +180,28 @@ QUnit.module("Form Renderer", (hooks) => { }; }); + QUnit.test("compile form with modifiers and attrs - string as domain", async (assert) => { + serverData.views = { + "partner,1,form": /*xml*/ ` +
+
+ +
+ + `, + }; + + await makeView({ + serverData, + resModel: "partner", + type: "form", + resId: 1, + }); + + await click(target.querySelector(".o_form_button_edit")); + assert.containsN(target, ".o_form_editable input", 2); + }); + QUnit.test("compile notebook with modifiers", async (assert) => { assert.expect(0);