8000 [FIX] website: Supports dynamic domain · odoo/odoo@a678755 · GitHub
[go: up one dir, main page]

Skip to content

Commit a678755

Browse files
committed
[FIX] website: Supports dynamic domain
Similar to: #184830 We've recently introduced a new operator: “is within” in the domain selector, which can be used to find out whether a date is within a dynamic range (e.g. within a month, within 4 days, within 3 weeks, etc.). To works, this domain needs dynamic elements, such as `context_today`, which is defined in `py_builtin.js` and dynamically retrieves the current date. https://github.com/odoo/odoo/blob/17.0/addons/web/static/src/core/py_js/py_builtin.js#L77-L79 However, the problem isn't limited to this operator in the selector domain, as it's only been available since 18.0, and this pr target is 17.0.s In fact, it is possible in certain cases to use these fields via debug mode, and there are several cases where `uid`, `user`, etc. are used. There is therefore an inconsistency where users see the use of these variables in these cases and when they try to use them elsewhere with, for example, a field of this style: `("date", "=", context_today())`, they get a traceback. This happens mainly because, in Python, the domain is evaluated via `literal_eval`, and since it contains variables that are designed for the web, it causes a traceback because this function expects to receive only a correctly formatted string, with no context and no variables. This commit handles: - website/model_page.py: https://github.com/odoo/odoo/blob/17.0/addons/website/controllers/model_page.py#L13-L18 https://github.com/odoo/odoo/blob/17.0/addons/website/controllers/model_page.py#L47-L50 The enterprise commit (odoo/enterprise#82564) handles two other cases: - web_studio/approval: Here in this case there are several calls to literal_eval on domains received from the web, notably to create and check its approval spec. a function has been used to avoid rewriting the same thing several times in the file.2 - marketing_automation/activity: Here too, several calls are made to this file, as in the case of approval, a function has been created to replace all calls to `literal_eval` In all three cases, the problem is the same: the problem is not only present in `is_within` but in the fact that python has no way of understanding the domain received from the web, so the same fix has been applied everywhere: - First, `to_utc()` is removed from the domain, since it's purely client-side and this notion doesn't exist in et la the python server - We replace the `literal_eval` call with `safe_eval`, which will do more than just transform a string containing only a literal value of type X into type X (e.g. tuple, string, number, array, etc.) - `safe_eval` can therefore either evaluate expressions or execute statements. In our case, what we really want is to evaluate just a string like literal_eval with just one more context, and to be able to define local or global values, such as defining `context_today()` - For the moment, the values we use are the same as those used by `is_within`, i.e.: -- context_today() -- relative_delta() -- datetime opw-4551335 opw-4672902 opw-4678894 opw-4669315 opw-4577091 closes #204172 Related: odoo/enterprise#82564 Signed-off-by: Lucas Perais (lpe) <lpe@odoo.com>
1 parent a2e76a9 commit a678755

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

addons/mail/tests/test_mail_tools.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33

44
from odoo.addons.mail.tests.common import MailCommon
55
from odoo.tests import tagged, users
6+
from odoo.addons.mail.tools.parser import domain_eval
7+
from freezegun import freeze_time
68

79

810
@tagged('mail_tools', 'res_partner')
@@ -179,6 +181,22 @@ def test_mail_find_partner_from_emails_multicompany(self):
179181
found = Partner._mail_find_partner_from_emails([self._test_email], records=record)
180182
self.assertEqual(found, [expected_partner], msg)
181183

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

183201
@tagged('mail_tools', 'mail_init')
184202
class TestMailUtils(MailCommon):

addons/mail/tools/parser.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from odoo import _, tools
77
from odoo.exceptions import ValidationError
8+
from odoo.tools import safe_eval
89

910

1011
def parse_res_ids(res_ids):
@@ -33,3 +34,15 @@ def parse_res_ids(res_ids):
3334
raise ValidationError(error_msg)
3435

3536
return res_ids
37+
38+
39+
def domain_eval(domain):
40+
domain = domain.replace('.to_utc()', '')
41+
evaluated_domain = safe_eval.safe_eval(domain, {
42+
'context_today': safe_eval.datetime.datetime.today,
43+
'datetime': safe_eval.datetime,
44+
'dateutil': safe_eval.dateutil,
45+
'relativedelta': safe_eval.dateutil.relativedelta.relativedelta,
46+
'time': safe_eval.time,
47+
})
48+
return evaluated_domain

addons/website/controllers/model_page.py

+2Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from odoo.addons.http_routing.models.ir_http import slug, unslug
66
from odoo.http import Controller, request, route
77
from odoo.osv.expression import AND, OR
8+
from odoo.addons.mail.tools.parser import domain_eval
89

910

1011
class ModelPageController(Controller):
@@ -45,7 +46,7 @@ def generic_model(self, page_name_slugified=None, page_number=1, record_slug=Non
4546
if not Model.check_access_rights("read", raise_exception=False):
4647
raise werkzeug.exceptions.Forbidden()
4748

48-
rec_domain = ast.literal_eval(page.record_domain or "[]")
49+
rec_domain = domain_eval(page.record_domain or "[]")
4950
domains = [rec_domain]
5051

5152
if record_slug:

0 commit comments

Comments
 (0)
0