8000 [FW][FIX] *: domain field: option allow_expressions by fw-bot · Pull Request #210482 · odoo/odoo · GitHub
[go: up one dir, main page]

Skip to content

[FW][FIX] *: domain field: option allow_expressions #210482

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 0 additions & 18 deletions addons/mail/tests/test_mail_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

from odoo.addons.mail.tests.common import MailCommon
from odoo.tests import tagged, users
from odoo.addons.mail.tools.parser import domain_eval
from freezegun import freeze_time


@tagged('mail_tools', 'res_partner')
Expand Down Expand Up @@ -174,22 +172,6 @@ def test_mail_find_partner_from_emails_multicompany(self):
found = Partner._mail_find_partner_from_emails([self._test_email], records=record)
self.assertEqual(found, [expected_partner], msg)

@freeze_time('2030-05-24')
def test_domain_eval(self):
success_pairs = [
("list()", []),
("list(range(1, 4))", [1, 2, 3]),
("['|', (1, '=', 1), (1, '>', 0)]", ['|', (1, '=', 1), (1, '>', 0)]),
("[(2, '=', 1 + 1)]", [(2, '=', 2)]),
(
"[('create_date', '<', datetime.datetime.combine(context_today() - relativedelta(days=100), datetime.time(1, 2, 3)).to_utc().strftime('%Y-%m-%d %H:%M:%S'))]",
[('create_date', '<', "2030-02-13 01:02:03")],
), # use the date utils used by front-end domains
]
for domain_expression, domain_value in success_pairs:
with self.subTest(domain_expression=domain_expression, domain_value=domain_value):
self.assertEqual(domain_eval(domain_expression), domain_value)


@tagged('mail_tools', 'mail_init')
class TestMailUtils(MailCommon):
Expand Down
14 changes: 1 addition & 13 deletions addons/mail/tools/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import ast

from odoo.exceptions import ValidationError
from odoo.tools import is_list_of, safe_eval
from odoo.tools import is_list_of


def parse_res_ids(res_ids, env):
Expand Down Expand Up @@ -35,15 +35,3 @@ def parse_res_ids(res_ids, env):
raise ValidationError(error_msg)

return res_ids


def domain_eval(domain):
domain = domain.replace('.to_utc()', '')
evaluated_domain = safe_eval.safe_eval(domain, {
'context_today': safe_eval.datetime.datetime.today,
'datetime': safe_eval.datetime,
'dateutil': safe_eval.dateutil,
'relativedelta': safe_eval.dateutil.relativedelta.relativedelta,
'time': safe_eval.time,
})
return evaluated_domain
21 changes: 21 additions & 0 deletions addons/web/i18n/web.pot
Original file line number Diff line number Diff line change
Expand Up @@ -3311,6 +3311,13 @@ msgid ""
"dropdown at all."
msgstr ""

#. module: web
#. odoo-javascript
#: code:addons/web/static/src/views/fields/domain/domain_field.js:0
#, python-format
msgid "If true, non-literals are accepted"
msgstr ""

#. module: web
#. odoo-javascript
#: code:addons/web/static/src/core/file_viewer/file_viewer.xml:0
Expand Down Expand Up @@ -6458,6 +6465,20 @@ msgid ""
"characters). Please use the CSV format for this export."
msgstr ""

#. module: web
#. odoo-javascript
#: code:addons/web/static/src/views/fields/domain/domain_field.js:0
#, python-format
msgid "The domain involves non-literals. Their evaluation might fail."
msgstr ""

#. module: web
#. odoo-javascript
#: code:addons/web/static/src/views/fields/domain/domain_field.js:0
#, python-format
msgid "The domain should not involve non-literals"
msgstr ""

#. module: web
#. odoo-javascript
#: code:addons/web/static/src/core/errors/error_dialogs.xml:0
Expand Down
10 changes: 8 additions & 2 deletions addons/web/static/src/core/domain_selector/domain_selector.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ export class DomainSelector extends Component {
className: { type: String, optional: true },
defaultConnector: { type: [{ value: "&" }, { value: "|" }], optional: true },
isDebugMode: { type: Boolean, optional: true },
allowExpressions: { type: Boolean, optional: true },
readonly: { type: Boolean, optional: true },
update: { type: Function, optional: true },
debugUpdate: { type: Function, optional: true },
};
static defaultProps = {
isDebugMode: false,
allowExpressions: true,
readonly: true,
update: () => {},
};
Expand Down Expand Up @@ -107,11 +109,15 @@ export class DomainSelector extends Component {
}

getDefaultOperator(fieldDef) {
return getDomainDisplayedOperators(fieldDef)[0];
return getDomainDisplayedOperators(fieldDef, {
allowExpressions: this.props.allowExpressions,
})[0];
}

getOperatorEditorInfo(fieldDef) {
const operators = getDomainDisplayedOperators(fieldDef);
const operators = getDomainDisplayedOperators(fieldDef, {
allowExpressions: this.props.allowExpressions,
});
return getOperatorEditorInfo(operators, fieldDef);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function getDomainDisplayedOperators(fieldDef) {
export function getDomainDisplayedOperators(fieldDef, params = {}) {
if (!fieldDef) {
fieldDef = {};
}
Expand Down Expand Up @@ -37,7 +37,18 @@ export function getDomainDisplayedOperators(fieldDef) {
];
case "date":
case "datetime":
return ["=", "!=", ">", ">=", "<", "<=", "between", "within", "set", "not_set"];
return [
"=",
"!=",
">",
">=",
"<",
"<=",
"between",
...("allowExpressions" in params && !params.allowExpressions ? [] : ["within"]),
"set",
"not_set",
];
case "integer":
case "float":
case "monetary":
Expand Down
28 changes: 28 additions & 0 deletions addons/web/static/src/core/tree_editor/condition_tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,34 @@ export function complexCondition(value) {
return { type: "complex_condition", value };
}

function treeContainsExpressions(tree) {
if (tree.type === "condition") {
const { path, operator, value } = tree;
return [path, operator, value].some(
(v) =>
v instanceof Expression ||
(Array.isArray(v) && v.some((w) => w instanceof Expression))
);
}
for (const child of tree.children) {
if (treeContainsExpressions(child)) {
return true;
}
}
return false;
}

export function domainContainsExpresssions(domain) {
let tree;
try {
tree = treeFromDomain(domain);
} catch {
return null;
}
// detect expressions in the domain tree, which we know is well-formed
return treeContainsExpressions(tree);
}

/**
* @param {Value} value
* @returns {Value}
Expand Down
33 changes: 32 additions & 1 deletion addons/web/static/src/views/fields/domain/domain_field.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { standardFieldProps } from "../standard_field_props";
import { useBus, useService, useOwnedDialogs } from "@web/core/utils/hooks";
import { useGetTreeDescription, useMakeGetFieldDef } from "@web/core/tree_editor/utils";
import { useGetDefaultLeafDomain } from "@web/core/domain_selector/utils";
import { treeFromDomain } from "@web/core/tree_editor/condition_tree";
import { domainContainsExpresssions, treeFromDomain } from "@web/core/tree_editor/condition_tree";
import { useRecordObserver } from "@web/model/relational_model/utils";

export class DomainField extends Component {
Expand All @@ -25,16 +25,19 @@ export class DomainField extends Component {
editInDialog: { type: Boolean, optional: true },
resModel: { type: String, optional: true },
isFoldable: { type: Boolean, optional: true },
allowExpressions: { type: Boolean, optional: true },
};
static defaultProps = {
editInDialog: false,
isFoldable: false,
allowExpressions: false,
};

setup() {
this.orm = useService("orm");
this.getDomainTreeDescription = useGetTreeDescription();
this.makeGetFieldDef = useMakeGetFieldDef();
this.notification = useService("notification");
this.getDefaultLeafDomain = useGetDefaultLeafDomain();
this.addDialog = useOwnedDialogs();

Expand Down Expand Up @@ -84,6 +87,13 @@ export class DomainField extends Component {
});
}

allowExpressions(props) {
return (
props.allowExpressions ||
["base.automation", "ir.filters"].includes(props.record.resModel)
);
}

getContext(props = this.props) {
return props.context;
}
Expand All @@ -95,6 +105,20 @@ export class DomainField extends Component {
getEvaluatedDomain(props = this.props) {
const domainStringRepr = this.getDomain(props);
const evalContext = this.getContext(props);
if (domainContainsExpresssions(domainStringRepr)) {
const allowExpressions = this.allowExpressions(props);
if (domainStringRepr !== this.lastDomainChecked) {
this.lastDomainChecked = domainStringRepr;
this.notification.add(
allowExpressions
? _t("The domain involves non-literals. Their evaluation might fail.")
: _t("The domain should not involve non-literals")
);
}
if (!allowExpressions) {
return { isInvalid: true };
}
}
try {
const domain = new Domain(domainStringRepr).toList(evalContext);
// Here, there is still some incertitude on the domain validity.
Expand Down Expand Up @@ -275,6 +299,12 @@ export const domainField = {
type: "boolean",
help: _t("Display the domain using facets"),
},
{
label: _t("Allow expressions"),
name: "allow_expressions",
type: "boolean",
help: _t("If true, non-literals are accepted"),
},
{
label: _t("Model"),
name: "model",
Expand All @@ -287,6 +317,7 @@ export const domainField = {
return {
editInDialog: options.in_dialog,
isFoldable: options.foldable,
allowExpressions: options.allow_expressions,
resModel: options.model,
context: dynamicInfo.context,
};
Expand Down
1 change: 1 addition & 0 deletions addons/web/static/src/views/fields/domain/domain_field.xml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
debugUpdate.bind="debugUpdate"
update.bind="update"
isDebugMode="!!env.debug"
allowExpressions="allowExpressions(props)"
className="props.readonly ? 'o_read_mode' : 'o_edit_mode'"
/>
</div>
Expand Down
Loading
0