From 3ce15f3b07dbb8615ccbc01b6da90433605d641b Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Mon, 20 Nov 2023 12:45:41 +0530 Subject: [PATCH 01/30] feat: Allow usage of `print()` within `safe_exec()` (#23084) * feat(safe_exec): allow usage of `print()` Signed-off-by: Akhil Narang * refactor(system_console): update description to mention `print()` instead of `log()` Signed-off-by: Akhil Narang * feat: unconditionally add debug logs to response if present Signed-off-by: Akhil Narang * chore(safe_exec): add in a test for running `print()` within safe_exec Signed-off-by: Akhil Narang * fix(safe_exec): ignore warning RestrictedPython warns us if we call `print()` don't use their `printed` variable Signed-off-by: Akhil Narang * feat: store debug logs from scheduled jobs Signed-off-by: Akhil Narang * fix: avoid ignoring warnings, disabled in prod anyway * chore: remove unnecessary logging This can be moved to level 2 when required --------- Co-authored-by: Ankush Menat (cherry picked from commit eb45da391325766ab2c74b84a648192a78da08e3) Signed-off-by: Akhil Narang --- frappe/__init__.py | 2 +- .../scheduled_job_log/scheduled_job_log.json | 11 +++++++++-- .../scheduled_job_log/scheduled_job_log.py | 1 + .../scheduled_job_type/scheduled_job_type.py | 2 ++ .../doctype/system_console/system_console.json | 4 ++-- frappe/public/js/frappe/request.js | 2 +- frappe/tests/test_safe_exec.py | 5 +++++ frappe/utils/safe_exec.py | 16 +++++++++++++++- 8 files changed, 36 insertions(+), 7 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 2f28b15ddd2..1a287d6257e 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -500,7 +500,7 @@ def print_sql(enable: bool = True) -> None: def log(msg: str) -> None: - """Add to `debug_log`. + """Add to `debug_log` :param msg: Message.""" if not request: diff --git a/frappe/core/doctype/scheduled_job_log/scheduled_job_log.json b/frappe/core/doctype/scheduled_job_log/scheduled_job_log.json index 451c4108a0a..782c0749f87 100644 --- a/frappe/core/doctype/scheduled_job_log/scheduled_job_log.json +++ b/frappe/core/doctype/scheduled_job_log/scheduled_job_log.json @@ -7,7 +7,8 @@ "field_order": [ "status", "scheduled_job_type", - "details" + "details", + "debug_log" ], "fields": [ { @@ -35,10 +36,16 @@ "options": "Scheduled Job Type", "read_only": 1, "reqd": 1 + }, + { + "fieldname": "debug_log", + "fieldtype": "Code", + "label": "Debug Log", + "read_only": 1 } ], "links": [], - "modified": "2022-06-13 05:41:21.090972", + "modified": "2023-11-09 12:06:41.781270", "modified_by": "Administrator", "module": "Core", "name": "Scheduled Job Log", diff --git a/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py b/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py index a6e70f3b3ae..54a8ab1e52d 100644 --- a/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py +++ b/frappe/core/doctype/scheduled_job_log/scheduled_job_log.py @@ -16,6 +16,7 @@ class ScheduledJobLog(Document): if TYPE_CHECKING: from frappe.types import DF + debug_log: DF.Code | None details: DF.Code | None scheduled_job_type: DF.Link status: DF.Literal["Scheduled", "Complete", "Failed"] diff --git a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py index 8006a43e937..3fb251362ef 100644 --- a/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py +++ b/frappe/core/doctype/scheduled_job_type/scheduled_job_type.py @@ -164,6 +164,8 @@ def update_scheduler_log(self, status): dict(doctype="Scheduled Job Log", scheduled_job_type=self.name) ).insert(ignore_permissions=True) self.scheduler_log.db_set("status", status) + if frappe.debug_log: + self.scheduler_log.db_set("debug_log", "\n".join(frappe.debug_log)) if status == "Failed": self.scheduler_log.db_set("details", frappe.get_traceback(with_context=True)) if status == "Start": diff --git a/frappe/desk/doctype/system_console/system_console.json b/frappe/desk/doctype/system_console/system_console.json index a851831909a..9573e23b526 100644 --- a/frappe/desk/doctype/system_console/system_console.json +++ b/frappe/desk/doctype/system_console/system_console.json @@ -29,7 +29,7 @@ ], "fields": [ { - "description": "To print output use log(text)", + "description": "To print output use print(text)", "fieldname": "console", "fieldtype": "Code", "label": "Console", @@ -86,7 +86,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2022-04-15 14:15:58.398590", + "modified": "2023-11-03 13:02:00.706806", "modified_by": "Administrator", "module": "Desk", "name": "System Console", diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 10a7158e4a2..f0195089f8c 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -483,8 +483,8 @@ frappe.request.cleanup = function (opts, r) { if (opts.args) { console.log("======== arguments ========"); console.log(opts.args); - console.log("========"); } + console.log("======== debug messages ========"); $.each(JSON.parse(r._debug_messages), function (i, v) { console.log(v); }); diff --git a/frappe/tests/test_safe_exec.py b/frappe/tests/test_safe_exec.py index 87a8b7c6bf3..e86669f1530 100644 --- a/frappe/tests/test_safe_exec.py +++ b/frappe/tests/test_safe_exec.py @@ -118,6 +118,11 @@ def test_write_wrapper(self): # dont Allow modifying _dict class self.assertRaises(Exception, safe_exec, "_dict.x = 1") + def test_print(self): + test_str = frappe.generate_hash() + safe_exec(f"print('{test_str}')") + self.assertEqual(frappe.local.debug_log[-1], test_str) + class TestNoSafeExec(FrappeTestCase): def test_safe_exec_disabled_by_default(self): diff --git a/frappe/utils/safe_exec.py b/frappe/utils/safe_exec.py index 82a79c829f4..6b9ae3e0cb7 100644 --- a/frappe/utils/safe_exec.py +++ b/frappe/utils/safe_exec.py @@ -1,6 +1,7 @@ import ast import copy import inspect +import io import json import mimetypes import types @@ -8,7 +9,7 @@ from functools import lru_cache import RestrictedPython.Guards -from RestrictedPython import compile_restricted, safe_globals +from RestrictedPython import PrintCollector, compile_restricted, safe_globals from RestrictedPython.transformer import RestrictingNodeTransformer import frappe @@ -61,6 +62,16 @@ def check_name(self, node, name, *args, **kwargs): return super().check_name(node, name, *args, **kwargs) +class FrappePrintCollector(PrintCollector): + """Collect written text, and return it when called.""" + + def _call_print(self, *objects, **kwargs): + output = io.StringIO() + print(*objects, file=output, **kwargs) + frappe.log(output.getvalue().strip()) + output.close() + + def is_safe_exec_enabled() -> bool: # server scripts can only be enabled via common_site_config.json return bool(frappe.get_common_site_config().get(SAFE_EXEC_CONFIG_KEY)) @@ -281,6 +292,9 @@ def get_safe_globals(): out._getitem_ = _getitem out._getattr_ = _getattr_for_safe_exec + # Allow using `print()` calls with `safe_exec()` + out._print_ = FrappePrintCollector + # allow iterators and list comprehension out._getiter_ = iter out._iter_unpack_sequence_ = RestrictedPython.Guards.guarded_iter_unpack_sequence From ca7148ed821bc900c14f748510eaaa42fac78fa4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:20:12 +0000 Subject: [PATCH 02/30] feat: Allow user to optimize tables (#26109) (#26121) * feat: Allow user to optimize tables * fix: Update butto label * fix: Filter out single & virtual doctypes * fix: Create long job for optimization * fix: check perm --------- Co-authored-by: Ankush Menat (cherry picked from commit 3f0c2330fcd8883b866ddbd9cfc149d368c3cd2c) Co-authored-by: Niraj Gautam --- .../database_storage_usage_by_tables.js | 45 +++++++++++++++++++ .../database_storage_usage_by_tables.py | 24 ++++++++++ 2 files changed, 69 insertions(+) diff --git a/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js b/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js index 8a4dbefc453..5a1ae6f87ac 100644 --- a/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js +++ b/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.js @@ -3,4 +3,49 @@ frappe.query_reports["Database Storage Usage By Tables"] = { filters: [], + onload: function (report) { + report.page.add_inner_button( + __("Optimize"), + function () { + let d = new frappe.ui.Dialog({ + title: "Optimize Doctype", + fields: [ + { + label: "Select a DocType", + fieldname: "doctype_name", + fieldtype: "Link", + options: "DocType", + get_query: function () { + return { + filters: { issingle: ["=", false], is_virtual: ["=", false] }, + }; + }, + }, + ], + size: "small", + primary_action_label: "Optimize", + primary_action(values) { + frappe.call({ + method: "frappe.core.report.database_storage_usage_by_tables.database_storage_usage_by_tables.optimize_doctype", + args: { + doctype_name: values.doctype_name, + }, + callback: function (r) { + if (!r.exec) { + frappe.show_alert( + __( + `${values.doctype_name} has been added to queue for optimization` + ) + ); + } + }, + }); + d.hide(); + }, + }); + d.show(); + }, + __("Actions") + ); + }, }; diff --git a/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py b/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py index c88262552e3..73052ad170e 100644 --- a/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py +++ b/frappe/core/report/database_storage_usage_by_tables/database_storage_usage_by_tables.py @@ -38,3 +38,27 @@ def execute(filters=None): as_dict=1, ) return COLUMNS, data + + +@frappe.whitelist() +def optimize_doctype(doctype_name: str): + frappe.only_for("System Manager") + frappe.enqueue( + optimize_doctype_job, + queue="long", + job_id=f"optimize-{doctype_name}", + doctype_name=doctype_name, + deduplicate=True, + ) + + +def optimize_doctype_job(doctype_name: str): + from frappe.utils import get_table_name + + doctype_table = get_table_name(doctype_name, wrap_in_backticks=True) + if frappe.db.db_type == "mariadb": + query = f"OPTIMIZE TABLE {doctype_table};" + else: + query = f"VACUUM (ANALYZE) {doctype_table};" + + frappe.db.sql(query) From 0c38f944d914ef5d8fc6c84bd8d3789c823b0bae Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 23 Apr 2024 17:57:51 +0530 Subject: [PATCH 03/30] fix: web form filtering (#26122) (cherry picked from commit b0cc93e9d718f76e8ff489b638e4b09a09bb017d) --- frappe/website/doctype/web_form/web_form.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/website/doctype/web_form/web_form.py b/frappe/website/doctype/web_form/web_form.py index 80d4ac92ca3..d46ec9fb100 100644 --- a/frappe/website/doctype/web_form/web_form.py +++ b/frappe/website/doctype/web_form/web_form.py @@ -663,7 +663,7 @@ def get_link_options(web_form_name, doctype, allow_read_on_all_link_options=Fals ) link_options, filters = [], {} - if not allow_read_on_all_link_options: + if web_form.login_required and not allow_read_on_all_link_options: filters = {"owner": frappe.session.user} fields = ["name as value"] From 724f9444d08513832eb43fec2520f7a9cf9ed769 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 18:15:00 +0530 Subject: [PATCH 04/30] fix: Renaming when doctype is used as a virtual link docfield fails (#26025) (#26119) (cherry picked from commit 27948bccc7eadfe9b76bd30c468640b1c5b33817) Co-authored-by: Kareem Kouddous --- frappe/model/rename_doc.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frappe/model/rename_doc.py b/frappe/model/rename_doc.py index 4f624c6a161..d996180a56a 100644 --- a/frappe/model/rename_doc.py +++ b/frappe/model/rename_doc.py @@ -463,7 +463,12 @@ def get_link_fields(doctype: str) -> list[dict]: .inner_join(dt) .on(df.parent == dt.name) .select(df.parent, df.fieldname, dt.issingle.as_("issingle")) - .where((df.options == doctype) & (df.fieldtype == "Link") & (dt.is_virtual == 0)) + .where( + (df.options == doctype) + & (df.fieldtype == "Link") + & (df.is_virtual == 0) + & (dt.is_virtual == 0) + ) .run(as_dict=True) ) From 69e503ce46408a4ca579b1472d178c115a7157f2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 12:54:00 +0000 Subject: [PATCH 05/30] fix: frappe.template.compile(...) is not a function (#26125) (#26127) (cherry picked from commit 8ea443825311a33628dbd4809557c8264ab590e1) Co-authored-by: Samuel Danieli <23150094+scdanieli@users.noreply.github.com> --- frappe/public/js/frappe/views/image/photoswipe_dom.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/views/image/photoswipe_dom.html b/frappe/public/js/frappe/views/image/photoswipe_dom.html index 24d5e13ac0c..c2307360223 100644 --- a/frappe/public/js/frappe/views/image/photoswipe_dom.html +++ b/frappe/public/js/frappe/views/image/photoswipe_dom.html @@ -13,7 +13,7 @@ + Do not modify these 3 pswp__item elements, data is added later on. -->
@@ -70,4 +70,4 @@
- \ No newline at end of file + From 0c518a2ce54fde84cf5854c33848ca8aaf226a01 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 23 Apr 2024 21:26:37 +0530 Subject: [PATCH 06/30] fix: handle case where mimetype is None (#26131) (#26133) (cherry picked from commit 2ec9ef5bf36b9b29ea7a158bf274ad936a46a53a) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- frappe/utils/pdf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/pdf.py b/frappe/utils/pdf.py index e5f067ab686..41bd4c41d12 100644 --- a/frappe/utils/pdf.py +++ b/frappe/utils/pdf.py @@ -276,7 +276,7 @@ def _get_base64_image(src): path = parsed_url.path query = parse_qs(parsed_url.query) mime_type = mimetypes.guess_type(path)[0] - if not mime_type.startswith("image/"): + if mime_type is None or not mime_type.startswith("image/"): return filename = query.get("fid") and query["fid"][0] or None file = find_file_by_url(path, name=filename) From 876d23021f7036e4e944277fb867b686862ee090 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 24 Apr 2024 08:38:09 +0000 Subject: [PATCH 07/30] fix: use document language for workflow action (#26138) (#26139) (cherry picked from commit e5271bc03443a419333c41513887bb8a9c772ef7) Co-authored-by: Ankush Menat --- .../doctype/workflow_action/workflow_action.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/frappe/workflow/doctype/workflow_action/workflow_action.py b/frappe/workflow/doctype/workflow_action/workflow_action.py index 45c5ded724e..f7b57982d0b 100644 --- a/frappe/workflow/doctype/workflow_action/workflow_action.py +++ b/frappe/workflow/doctype/workflow_action/workflow_action.py @@ -476,10 +476,26 @@ def get_common_email_args(doc): subject = _("Workflow Action") + f" on {doctype}: {docname}" response = get_link_to_form(doctype, docname, f"{doctype}: {docname}") + print_format = doc.meta.default_print_format + lang = doc.get("language") or ( + frappe.get_cached_value("Print Format", print_format, "default_print_language") + if print_format + else None + ) + return { "template": "workflow_action", "header": "Workflow Action", - "attachments": [frappe.attach_print(doctype, docname, file_name=docname, doc=doc)], + "attachments": [ + frappe.attach_print( + doctype, + docname, + file_name=docname, + doc=doc, + lang=lang, + print_format=print_format, + ) + ], "subject": subject, "message": response, } From dd78c7fd287a32105d46d5ac42f2c40f75f5a83e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 24 Apr 2024 19:04:03 +0530 Subject: [PATCH 08/30] [fix] Add missing translate function call (#25747) (#26147) * Update Properties.vue * Update store.js * Update Section.vue * Update list_settings.js * Update grid_row.js * Update list_settings.js * chore: add in missing context for some strings Co-authored-by: Corentin Flr <10946971+cogk@users.noreply.github.com> * chore: formatting and lint fixes Signed-off-by: Akhil Narang --------- Signed-off-by: Akhil Narang Co-authored-by: Akhil Narang Co-authored-by: Corentin Flr <10946971+cogk@users.noreply.github.com> (cherry picked from commit ea61af2cebed536a829d3b3905a9a85ce91de955) Co-authored-by: Fisher Yu <12823863+szufisher@users.noreply.github.com> --- .../js/form_builder/components/Section.vue | 4 ++-- frappe/public/js/frappe/form/grid_row.js | 4 ++-- frappe/public/js/frappe/list/list_settings.js | 20 +++++++++---------- .../components/Properties.vue | 6 +++--- frappe/public/js/workflow_builder/store.js | 2 +- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/frappe/public/js/form_builder/components/Section.vue b/frappe/public/js/form_builder/components/Section.vue index cd5edabe067..6f01d5eaed4 100644 --- a/frappe/public/js/form_builder/components/Section.vue +++ b/frappe/public/js/form_builder/components/Section.vue @@ -260,14 +260,14 @@ function delete_column(with_children) { const options = computed(() => { let groups = [ { - group: "Section", + group: __("Section"), items: [ { label: __("Add section below"), onClick: add_section_below }, { label: __("Remove section"), onClick: remove_section }, ], }, { - group: "Column", + group: __("Column"), items: [{ label: __("Add column"), onClick: add_column }], }, ]; diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index a298c4dbd7f..fa4a5f9af5d 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -504,7 +504,7 @@ export default class GridRow { ); if (selectedColumn && !selectedColumn.hidden && show_field(selectedColumn.fieldtype)) { fields.push({ - label: selectedColumn.label, + label: __(selectedColumn.label, null, this.grid.doctype), value: selectedColumn.fieldname, checked: true, }); @@ -519,7 +519,7 @@ export default class GridRow { show_field(column.fieldtype) ) { fields.push({ - label: column.label, + label: __(column.label, null, this.grid.doctype), value: column.fieldname, checked: false, }); diff --git a/frappe/public/js/frappe/list/list_settings.js b/frappe/public/js/frappe/list/list_settings.js index aa1cb31427b..45d10517e31 100644 --- a/frappe/public/js/frappe/list/list_settings.js +++ b/frappe/public/js/frappe/list/list_settings.js @@ -118,7 +118,7 @@ export default class ListSettings { ${frappe.utils.icon("drag", "xs", "", "", "sortable-handle " + show_sortable_handle)}
- ${me.fields[idx].label} + ${__(me.fields[idx].label, null, me.doctype)}
@@ -132,14 +132,14 @@ export default class ListSettings { fields_html.html(` @@ -209,7 +209,7 @@ export default class ListSettings { for (let idx = 0; idx < fields_order.length; idx++) { me.fields.push({ fieldname: fields_order.item(idx).getAttribute("data-fieldname"), - label: fields_order.item(idx).getAttribute("data-label"), + label: __(fields_order.item(idx).getAttribute("data-label")), }); } @@ -264,7 +264,7 @@ export default class ListSettings { let field = frappe.meta.get_docfield(me.doctype, value); if (field) { me.fields.push({ - label: field.label, + label: __(field.label, null, me.doctype), fieldname: field.fieldname, }); } @@ -320,7 +320,7 @@ export default class ListSettings { me.subject_field.fieldname != field.fieldname ) { me.fields.push({ - label: field.label, + label: __(field.label, null, me.doctype), fieldname: field.fieldname, }); } @@ -331,7 +331,7 @@ export default class ListSettings { let me = this; me.subject_field = { - label: "ID", + label: __("ID"), fieldname: "name", }; @@ -339,7 +339,7 @@ export default class ListSettings { let field = frappe.meta.get_docfield(me.doctype, meta.title_field.trim()); me.subject_field = { - label: field.label, + label: __(field.label, null, me.doctype), fieldname: field.fieldname, }; } @@ -353,7 +353,7 @@ export default class ListSettings { if (frappe.has_indicator(me.doctype)) { me.fields.push({ type: "Status", - label: "Status", + label: __("Status"), fieldname: "status_field", }); } @@ -365,7 +365,7 @@ export default class ListSettings { meta.fields.forEach((field) => { if (!frappe.model.no_value_type.includes(field.fieldtype)) { multiselect_fields.push({ - label: field.label, + label: __(field.label, null, field.doctype), value: field.fieldname, checked: fields.includes(field.fieldname), }); diff --git a/frappe/public/js/workflow_builder/components/Properties.vue b/frappe/public/js/workflow_builder/components/Properties.vue index f8a9fbe9fae..3331950c8b3 100644 --- a/frappe/public/js/workflow_builder/components/Properties.vue +++ b/frappe/public/js/workflow_builder/components/Properties.vue @@ -16,12 +16,12 @@ let properties = computed(() => { if (field.val() === "") field.focus(); }); if (store.workflow.selected && "action" in store.workflow.selected.data) { - title.value = "Transition Properties"; + title.value = __("Transition Properties"); return store.transitionfields.filter((df) => in_list(["action", "allowed", "allow_self_approval", "condition"], df.fieldname) ); } else if (store.workflow.selected && "state" in store.workflow.selected.data) { - title.value = "State Properties"; + title.value = __("State Properties"); let allow_edit = store.statefields.find((df) => df.fieldname == "allow_edit"); store.statefields = store.statefields.filter( (df) => !in_list(["allow_edit", "workflow_builder_id"], df.fieldname) @@ -39,7 +39,7 @@ let properties = computed(() => { return true; }); } - title.value = "Workflow Details"; + title.value = __("Workflow Details"); return store.workflowfields.filter( (df) => !in_list(["states", "transitions", "workflow_data", "workflow_name"], df.fieldname) ); diff --git a/frappe/public/js/workflow_builder/store.js b/frappe/public/js/workflow_builder/store.js index 46cda01ec9c..179150fc325 100644 --- a/frappe/public/js/workflow_builder/store.js +++ b/frappe/public/js/workflow_builder/store.js @@ -80,7 +80,7 @@ export const useStore = defineStore("workflow-builder-store", () => { const workflow_data = clean_workflow_data(); doc.workflow_data = JSON.stringify(workflow_data); await frappe.call("frappe.client.save", { doc }); - frappe.toast("Workflow updated successfully"); + frappe.toast(__("Workflow updated successfully")); fetch(); } catch (e) { console.error(e); From fd42cbf009d718c430f6ea9927725a0400a11cfa Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 24 Apr 2024 19:57:17 +0530 Subject: [PATCH 09/30] fix: hide tab button on child table (#26148) (#26149) (cherry picked from commit d758f0c3f504f0ca170c91fd73a74d03d0d83bb0) Co-authored-by: Ankush Menat --- frappe/public/js/form_builder/components/Sidebar.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/form_builder/components/Sidebar.vue b/frappe/public/js/form_builder/components/Sidebar.vue index 11c65d57a1a..c70321d316b 100644 --- a/frappe/public/js/form_builder/components/Sidebar.vue +++ b/frappe/public/js/form_builder/components/Sidebar.vue @@ -40,7 +40,10 @@ function resize(e) {