feat: evaluate Web Form dynamic filters server-side
This commit is contained in:
parent
d0d7ef8ae6
commit
6dd4539002
4 changed files with 51 additions and 35 deletions
|
|
@ -116,40 +116,6 @@ export default class WebFormList {
|
|||
this.filters = Object.assign(this.filters, JSON.parse(filter));
|
||||
}
|
||||
|
||||
if (this.dynamic_filters_json) {
|
||||
let dynamic_filters = JSON.parse(this.dynamic_filters_json);
|
||||
if (dynamic_filters.length) {
|
||||
dynamic_filters.forEach((f) => {
|
||||
let expression = f[3];
|
||||
try {
|
||||
f[3] = eval(f[3]);
|
||||
} catch (e) {
|
||||
frappe.throw(
|
||||
__("Invalid expression set in filter {0} ({1}): {2}", [
|
||||
f[1],
|
||||
f[0],
|
||||
expression,
|
||||
])
|
||||
);
|
||||
}
|
||||
if (f[3] == null) {
|
||||
frappe.throw(
|
||||
__("Invalid expression set in filter {0} ({1}): {2}", [
|
||||
f[1],
|
||||
f[0],
|
||||
expression,
|
||||
])
|
||||
);
|
||||
}
|
||||
});
|
||||
let df_obj = {};
|
||||
dynamic_filters.forEach((f) => {
|
||||
df_obj[f[1]] = [f[2], f[3]];
|
||||
});
|
||||
this.filters = Object.assign(this.filters, df_obj);
|
||||
}
|
||||
}
|
||||
|
||||
let args = {
|
||||
method: "frappe.www.list.get_list_data",
|
||||
args: {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ frappe.ready(function () {
|
|||
web_form_name: web_form_doc.name,
|
||||
list_columns: web_form_doc.list_columns,
|
||||
condition_json: web_form_doc.condition_json,
|
||||
dynamic_filters_json: web_form_doc.dynamic_filters_json,
|
||||
settings: {
|
||||
allow_delete: web_form_doc.allow_delete,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -346,6 +346,18 @@ frappe.ui.form.on("Web Form", {
|
|||
frm.dynamic_filters
|
||||
);
|
||||
|
||||
// Override description to show Python expressions (evaluated server-side)
|
||||
let desc_field = fields.find((f) => f.fieldname === "description");
|
||||
if (desc_field) {
|
||||
desc_field.options = `<div>
|
||||
<p>${__("Set dynamic filter values as Python expressions.")}</p>
|
||||
<p>${__("For example:")}
|
||||
<code>frappe.session.user</code> ${__("or")}
|
||||
<code>frappe.utils.now()</code>
|
||||
</p>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
frm.dynamic_filter_table.on("click", () => {
|
||||
if (!frm.has_perm("write")) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ def get_list_data(
|
|||
if list_context.filters:
|
||||
filters.update(list_context.filters)
|
||||
|
||||
if web_form_name:
|
||||
dynamic_filters = get_dynamic_filters(web_form_name)
|
||||
if dynamic_filters:
|
||||
filters.update(dynamic_filters)
|
||||
|
||||
_get_list = list_context.get_list or get_list
|
||||
|
||||
kwargs = dict(
|
||||
|
|
@ -192,3 +197,37 @@ def get_list(
|
|||
order_by=order_by,
|
||||
distinct=distinct,
|
||||
)
|
||||
|
||||
|
||||
def get_dynamic_filters(web_form_name):
|
||||
"""Evaluate dynamic filter expressions from Web Form.
|
||||
Uses same safe_eval + get_workflow_safe_globals pattern as Workflow."""
|
||||
from frappe.model.workflow import get_workflow_safe_globals
|
||||
|
||||
web_form = frappe.get_cached_doc("Web Form", web_form_name)
|
||||
|
||||
if not web_form.dynamic_filters_json:
|
||||
return None
|
||||
|
||||
dynamic_filters = json.loads(web_form.dynamic_filters_json)
|
||||
if not dynamic_filters:
|
||||
return None
|
||||
|
||||
safe_globals = get_workflow_safe_globals()
|
||||
safe_globals["frappe"]["defaults"] = frappe._dict(
|
||||
get_user_default=frappe.defaults.get_user_default,
|
||||
get_global_default=frappe.defaults.get_global_default,
|
||||
)
|
||||
|
||||
evaluated = {}
|
||||
for f in dynamic_filters:
|
||||
try:
|
||||
value = frappe.safe_eval(f[3], safe_globals)
|
||||
evaluated[f[1]] = [f[2], value]
|
||||
except Exception as e:
|
||||
frappe.throw(
|
||||
_("Invalid expression in Web Form Dynamic Filter for {0}: {1}").format(f[1], e),
|
||||
title=_("Dynamic Filter Error"),
|
||||
)
|
||||
|
||||
return evaluated
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue