diff --git a/frappe/public/js/frappe/web_form/web_form_list.js b/frappe/public/js/frappe/web_form/web_form_list.js index 92514498e2..ae49106693 100644 --- a/frappe/public/js/frappe/web_form/web_form_list.js +++ b/frappe/public/js/frappe/web_form/web_form_list.js @@ -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: { diff --git a/frappe/public/js/frappe/web_form/webform_script.js b/frappe/public/js/frappe/web_form/webform_script.js index 09b71870bb..4c745a3909 100644 --- a/frappe/public/js/frappe/web_form/webform_script.js +++ b/frappe/public/js/frappe/web_form/webform_script.js @@ -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, }, diff --git a/frappe/website/doctype/web_form/web_form.js b/frappe/website/doctype/web_form/web_form.js index 978a9cae9a..bca1356d96 100644 --- a/frappe/website/doctype/web_form/web_form.js +++ b/frappe/website/doctype/web_form/web_form.js @@ -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 = `
+

${__("Set dynamic filter values as Python expressions.")}

+

${__("For example:")} + frappe.session.user ${__("or")} + frappe.utils.now() +

+
`; + } + frm.dynamic_filter_table.on("click", () => { if (!frm.has_perm("write")) { return; diff --git a/frappe/www/list.py b/frappe/www/list.py index 4feb9a6943..976d05f286 100644 --- a/frappe/www/list.py +++ b/frappe/www/list.py @@ -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