From 1f47f7a7460a403f598903dd294836920c704d55 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 6 Mar 2024 12:06:08 +0530 Subject: [PATCH 01/27] fix: Delete EPS logs before deleting user (cherry picked from commit 072c2a1ca31002781455ca479c9e85e8611ce101) --- frappe/core/doctype/user/user.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index d492781795a..58a5da702d8 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -552,6 +552,9 @@ def on_trash(self): frappe.db.delete("OAuth Authorization Code", {"user": self.name}) frappe.db.delete("Token Cache", {"user": self.name}) + # Delete EPS data + frappe.db.delete("Energy Point Log", {"user": self.name}) + def before_rename(self, old_name, new_name, merge=False): frappe.clear_cache(user=old_name) self.validate_rename(old_name, new_name) From 1da957f622e9f8d2ba507580cb3078c77d6bf94e Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 6 Mar 2024 12:11:35 +0530 Subject: [PATCH 02/27] fix(UX): Nudge to disable user instead of deleting (cherry picked from commit 081be53e17b9e55a55b5c502e4976008a020f297) --- frappe/core/doctype/user/user.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 58a5da702d8..ac645fff536 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -16,6 +16,7 @@ toggle_notifications, ) from frappe.desk.notifications import clear_notifications +from frappe.model.delete_doc import check_if_doc_is_linked from frappe.model.document import Document from frappe.query_builder import DocType from frappe.rate_limiter import rate_limit @@ -555,6 +556,12 @@ def on_trash(self): # Delete EPS data frappe.db.delete("Energy Point Log", {"user": self.name}) + # Ask user to disable instead if document is still linked + try: + check_if_doc_is_linked(self) + except frappe.LinkExistsError: + frappe.throw(_("You can disable the user instead of deleting it."), frappe.LinkExistsError) + def before_rename(self, old_name, new_name, merge=False): frappe.clear_cache(user=old_name) self.validate_rename(old_name, new_name) From 0c934de1ba56938d2699633ce41b4b16a2a25e8e Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Wed, 6 Mar 2024 12:37:32 +0530 Subject: [PATCH 03/27] fix: loosen validation on fetch from columns (#25247) Any existing DB column can work so no need to check for meta explicitly. (cherry picked from commit 457654cf40885ea04b52a708ded57dfe668ed811) --- frappe/core/doctype/doctype/doctype.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 5cd180e58d4..169b6f56a33 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1623,7 +1623,9 @@ def check_fetch_from(docfield): doctype = link_df[0].options fetch_from_doctype = frappe.get_meta(doctype) - if not fetch_from_doctype.get_field(source_fieldname): + if not frappe.db.has_column(doctype, source_fieldname) and not fetch_from_doctype.get_field( + source_fieldname + ): frappe.throw( _("Fetch From for field {0} is invalid: {1} does not have a field {2}").format( frappe.bold(fieldname), frappe.bold(doctype), frappe.bold(source_fieldname) From b6311120aa13a8d766e7daa8c17ac4fd793afa89 Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Wed, 6 Mar 2024 13:39:52 +0530 Subject: [PATCH 04/27] fix(uninstall): don't allow uninstalling an app if we have other apps dependent on it Support ticket 10929 / Sentry FRAPPE-3T8 Signed-off-by: Akhil Narang (cherry picked from commit 0576a085d7c288d68e1b611ceb36c299b79c0b37) --- frappe/installer.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frappe/installer.py b/frappe/installer.py index 52c04cbe462..8c65a0cba12 100644 --- a/frappe/installer.py +++ b/frappe/installer.py @@ -357,6 +357,14 @@ def remove_app(app_name, dry_run=False, yes=False, no_backup=False, force=False) click.secho(f"App {app_name} not installed on Site {site}", fg="yellow") return + # Don't allow uninstalling if we have dependent apps installed + for app in frappe.get_installed_apps(): + if app != app_name: + hooks = frappe.get_hooks(app_name=app) + if hooks.required_apps and any(app_name in required_app for required_app in hooks.required_apps): + click.secho(f"App {app_name} is a dependency of {app}. Uninstall {app} first.", fg="yellow") + return + print(f"Uninstalling App {app_name} from Site {site}...") if not dry_run and not yes: From 7357c5ef2dd66fdbf1bdeba97a8f275d54fcae82 Mon Sep 17 00:00:00 2001 From: Corentin Flr <10946971+cogk@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:33:32 +0100 Subject: [PATCH 05/27] fix(ModuleDef): Add list view filter options to app_name Select (cherry picked from commit daa315f04a9cc96bde48135f43cacbaf754c06f5) --- .../core/doctype/module_def/module_def_list.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 frappe/core/doctype/module_def/module_def_list.js diff --git a/frappe/core/doctype/module_def/module_def_list.js b/frappe/core/doctype/module_def/module_def_list.js new file mode 100644 index 00000000000..4ff0c203e07 --- /dev/null +++ b/frappe/core/doctype/module_def/module_def_list.js @@ -0,0 +1,16 @@ +frappe.listview_settings["Module Def"] = { + onload: function (list_view) { + frappe.call({ + method: "frappe.core.doctype.module_def.module_def.get_installed_apps", + callback: (r) => { + const field = list_view.page.fields_dict.app_name; + if (!field) return; + + const options = JSON.parse(r.message); + options.unshift(""); // Add empty option + field.df.options = options; + field.set_options(); + }, + }); + }, +}; From 7f147b1e807897d1f9aa815a50a2f01c7d93ff9d Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Wed, 6 Mar 2024 16:35:44 +0530 Subject: [PATCH 06/27] fix: formatting Broke in 30a2f2743df74edefeeafc6efe12ac6f2dedf246 Signed-off-by: Akhil Narang --- frappe/realtime.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frappe/realtime.py b/frappe/realtime.py index 59d0351304e..d544cb8a4ff 100644 --- a/frappe/realtime.py +++ b/frappe/realtime.py @@ -9,9 +9,7 @@ from frappe.utils.data import cstr -def publish_progress( - percent, title=None, doctype=None, docname=None, description=None, task_id=None -): +def publish_progress(percent, title=None, doctype=None, docname=None, description=None, task_id=None): publish_realtime( "progress", {"percent": percent, "title": title, "description": description}, From 4d5e45310258c58ef35dd842f1bfa7b5d60915ec Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Thu, 7 Mar 2024 17:02:05 +0530 Subject: [PATCH 07/27] fix: Setup fail if fail function exists fixes https://trace.frappe.cloud/organizations/frappe/issues/9865/events/9a3299ee7593457bae9a30d939d563c5/?project=11 (cherry picked from commit 692878d97e41282b789f5a4d94ea145c8f25840f) --- frappe/public/js/frappe/desk.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index 1bce44dfa66..475c5f70ca3 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -507,7 +507,7 @@ frappe.Application = class Application { frappe.set_route("Form", newdoc.doctype, newdoc.name); frappe.dom.unfreeze(); }); - res && res.fail(frappe.dom.unfreeze); + res && res.fail?.(frappe.dom.unfreeze); }); } } catch (e) { From 800298dcdbb3a5873d73aaeb407a07bbf666fdd8 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 7 Mar 2024 18:43:42 +0530 Subject: [PATCH 08/27] fix: dont set fetch from if partial (cherry picked from commit 26cf1c8acbde08a8db6e9745f16107683e3803af) --- .../form_builder/components/controls/FetchFromControl.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frappe/public/js/form_builder/components/controls/FetchFromControl.vue b/frappe/public/js/form_builder/components/controls/FetchFromControl.vue index 1368c7e5937..1873a7b5d4b 100644 --- a/frappe/public/js/form_builder/components/controls/FetchFromControl.vue +++ b/frappe/public/js/form_builder/components/controls/FetchFromControl.vue @@ -71,7 +71,11 @@ watch( watch([() => doctype.value, () => fieldname.value], ([doctype_value, fieldname_value]) => { let [doctype_name, field_name] = props.value?.split(".") || ["", ""]; if (doctype_value != doctype_name || fieldname_value != field_name) { - emit("update:modelValue", `${doctype_value}.${fieldname_value}`); + let fetch_expression = ""; + if (doctype_value && fieldname_value) { + fetch_expression = `${doctype_value}.${fieldname_value}`; + } + emit("update:modelValue", fetch_expression); } }); From cf8078dc525ecdee4c55986523af3821baa08982 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 10 Mar 2024 12:19:37 +0530 Subject: [PATCH 09/27] fix: Add `is_virtual` in bootstrap schema (cherry picked from commit 62afa419f8faf430203190a909f7f4357cba63e6) --- frappe/database/mariadb/framework_mariadb.sql | 1 + frappe/database/postgres/framework_postgres.sql | 1 + 2 files changed, 2 insertions(+) diff --git a/frappe/database/mariadb/framework_mariadb.sql b/frappe/database/mariadb/framework_mariadb.sql index 4c69eafdedd..91ef69645c0 100644 --- a/frappe/database/mariadb/framework_mariadb.sql +++ b/frappe/database/mariadb/framework_mariadb.sql @@ -176,6 +176,7 @@ CREATE TABLE `tabDocType` ( `idx` int(8) NOT NULL DEFAULT 0, `search_fields` varchar(255) DEFAULT NULL, `issingle` int(1) NOT NULL DEFAULT 0, + `is_virtual` int(1) NOT NULL DEFAULT 0, `is_tree` int(1) NOT NULL DEFAULT 0, `istable` int(1) NOT NULL DEFAULT 0, `editable_grid` int(1) NOT NULL DEFAULT 1, diff --git a/frappe/database/postgres/framework_postgres.sql b/frappe/database/postgres/framework_postgres.sql index 37605be0f63..e27cbffe0f8 100644 --- a/frappe/database/postgres/framework_postgres.sql +++ b/frappe/database/postgres/framework_postgres.sql @@ -179,6 +179,7 @@ CREATE TABLE "tabDocType" ( "idx" bigint NOT NULL DEFAULT 0, "search_fields" varchar(255) DEFAULT NULL, "issingle" smallint NOT NULL DEFAULT 0, + "is_virtual" smallint NOT NULL DEFAULT 0, "is_tree" smallint NOT NULL DEFAULT 0, "istable" smallint NOT NULL DEFAULT 0, "editable_grid" smallint NOT NULL DEFAULT 1, From 74a8d8761a8628fc11c2177b2f0ff4683734a8c3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 10 Mar 2024 12:34:12 +0530 Subject: [PATCH 10/27] fix: delete doctype cache before starting migration (cherry picked from commit 05dca169e3b1bc4832e3b86a6ead4325753a658b) --- frappe/utils/install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/utils/install.py b/frappe/utils/install.py index df918c27e09..69907cff919 100644 --- a/frappe/utils/install.py +++ b/frappe/utils/install.py @@ -16,6 +16,7 @@ def before_install(): frappe.reload_doc("desk", "doctype", "form_tour_step") frappe.reload_doc("desk", "doctype", "form_tour") frappe.reload_doc("core", "doctype", "doctype") + frappe.clear_cache() def after_install(): From bdbed3ef047c20c50a16bbe2432f1876ce0ea3dc Mon Sep 17 00:00:00 2001 From: Akhil Narang Date: Thu, 7 Mar 2024 12:10:30 +0530 Subject: [PATCH 11/27] chore: include a link to FC docs regarding what to do with encryption key for restored sites Signed-off-by: Akhil Narang (cherry picked from commit 1032d9b4da37dbeb74118962ed10e3dcc131f04d) --- frappe/utils/password.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/frappe/utils/password.py b/frappe/utils/password.py index f5f83cef1e1..cb6674fe306 100644 --- a/frappe/utils/password.py +++ b/frappe/utils/password.py @@ -200,6 +200,10 @@ def decrypt(txt, encryption_key=None): + _( "If you have recently restored the site you may need to copy the site config contaning original Encryption Key." ) + + "
" + + _( + "Please visit https://frappecloud.com/docs/sites/migrate-an-existing-site#encryption-key for more information." + ), ) From 5061a0807b1b5c1048b8af4ba4dc1970cdec0e71 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 10 Mar 2024 12:58:18 +0530 Subject: [PATCH 12/27] fix: ignore route history and access log from link check (cherry picked from commit cfeb1db3f5dc1676dc0f393222c21f7dd2cc00d0) --- frappe/hooks.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/hooks.py b/frappe/hooks.py index ddb3fa31c94..2315293ff8a 100644 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -419,6 +419,8 @@ "Unhandled Email", "Webhook Request Log", "Workspace", + "Route History", + "Access Log", ] # Request Hooks From 27357fba10e3d65efc184774f745da5037b5a157 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 11 Mar 2024 11:40:52 +0530 Subject: [PATCH 13/27] fix: missing timeline timestamp for comments resolves https://github.com/frappe/frappe/pull/24884/files#r1519195726 (cherry picked from commit c89eace582c7510638f4edb2b726d37fbadee97c) --- .../js/frappe/form/templates/timeline_message_box.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/frappe/public/js/frappe/form/templates/timeline_message_box.html b/frappe/public/js/frappe/form/templates/timeline_message_box.html index 830e4cd39f6..9a36c630b79 100644 --- a/frappe/public/js/frappe/form/templates/timeline_message_box.html +++ b/frappe/public/js/frappe/form/templates/timeline_message_box.html @@ -23,7 +23,7 @@ {% } %}
- {{ comment_when(doc.communication_date) }} + {{ comment_when(doc.communication_date || doc.creation) }}
{% } else if (doc.comment_type && doc.comment_type == "Comment") { %} @@ -33,7 +33,7 @@ {{ __("commented") }} . - {{ comment_when(doc.communication_date) }} + {{ comment_when(doc.communication_date || doc.creation) }} {% } else { %} @@ -44,7 +44,7 @@ {{ doc.user_full_name || frappe.user.full_name(doc.owner) }} . - {{ comment_when(doc.communication_date) }} + {{ comment_when(doc.communication_date || doc.creation) }} {% if (doc.subject) { %}
{{doc.subject}}
From b1afc2902728b3f0a2cf28f1da673a46dccd3765 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 11 Mar 2024 13:11:21 +0530 Subject: [PATCH 14/27] fix!: Enqueue action after commit (cherry picked from commit 68a78d33d3ae1693be2af02b6090a5fe8152bf4a) --- frappe/model/document.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frappe/model/document.py b/frappe/model/document.py index 8a3cab5bb0f..00052e33317 100644 --- a/frappe/model/document.py +++ b/frappe/model/document.py @@ -1536,11 +1536,16 @@ def queue_action(self, action, **kwargs): primary_action=primary_action, ) + enqueue_after_commit = kwargs.pop("enqueue_after_commit", None) + if enqueue_after_commit is None: + enqueue_after_commit = True + return enqueue( "frappe.model.document.execute_action", __doctype=self.doctype, __name=self.name, __action=action, + enqueue_after_commit=enqueue_after_commit, **kwargs, ) From d6bff86205ca40f79c583c5c49c55670a8cc7dca Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sun, 3 Mar 2024 17:17:12 +0530 Subject: [PATCH 15/27] fix: rollback invalid customize form changes (#25198) * fix: rollback invalid customize form changes * fix: commit before doing DDL (cherry picked from commit b60c64cacc19b637c203f8460bfe3a12ab5d44af) --- frappe/database/mariadb/database.py | 1 - frappe/database/mariadb/schema.py | 2 +- frappe/database/postgres/database.py | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py index 3fb5756ae7e..20d666c698e 100644 --- a/frappe/database/mariadb/database.py +++ b/frappe/database/mariadb/database.py @@ -436,7 +436,6 @@ def updatedb(self, doctype, meta=None): db_table = MariaDBTable(doctype, meta) db_table.validate() - self.commit() db_table.sync() self.begin() diff --git a/frappe/database/mariadb/schema.py b/frappe/database/mariadb/schema.py index 552ad88f209..c4afc106d8e 100644 --- a/frappe/database/mariadb/schema.py +++ b/frappe/database/mariadb/schema.py @@ -60,7 +60,7 @@ def create(self): CHARACTER SET=utf8mb4 COLLATE=utf8mb4_unicode_ci""" - frappe.db.sql(query) + frappe.db.sql_ddl(query) def alter(self): for col in self.columns.values(): diff --git a/frappe/database/postgres/database.py b/frappe/database/postgres/database.py index 7545cfee427..d21c6c5e51b 100644 --- a/frappe/database/postgres/database.py +++ b/frappe/database/postgres/database.py @@ -323,7 +323,6 @@ def updatedb(self, doctype, meta=None): db_table = PostgresTable(doctype, meta) db_table.validate() - self.commit() db_table.sync() self.begin() From 160cf295c75b9141d068ca93edb2c09cb3f82afe Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 20 Feb 2024 11:04:58 +0530 Subject: [PATCH 16/27] perf: Add optional socketio native packages These are native implementations of some of the things socketio relies on. Just installing makes few things faster. These are optional so yarn should ignore failure to install. (cherry picked from commit 692a400632a922a93387531b9eac722b51d9b3f9) --- package.json | 4 ++++ yarn.lock | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/package.json b/package.json index d10de5c256f..376c72d5bf3 100644 --- a/package.json +++ b/package.json @@ -83,5 +83,9 @@ }, "nyc": { "report-dir": ".cypress-coverage" + }, + "optionalDependencies": { + "bufferutil": "^4.0.8", + "utf-8-validate": "^6.0.3" } } diff --git a/yarn.lock b/yarn.lock index e54afba1f88..d58923443f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -574,6 +574,13 @@ browserslist@^4.0.0, browserslist@^4.21.10, browserslist@^4.21.4: node-releases "^2.0.13" update-browserslist-db "^1.0.13" +bufferutil@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" + integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw== + dependencies: + node-gyp-build "^4.3.0" + call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" @@ -2287,6 +2294,11 @@ negotiator@0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +node-gyp-build@^4.3.0: + version "4.8.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.0.tgz#3fee9c1731df4581a3f9ead74664369ff00d26dd" + integrity sha512-u6fs2AEUljNho3EYTJNBfImO5QTo/J/1Etd+NVdCj7qWKUSN/bSLkZwhDv7I+w/MSC6qJ4cknepkAYykDdK8og== + node-releases@^2.0.13: version "2.0.13" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" @@ -3441,6 +3453,13 @@ url-polyfill@^1.1.12: resolved "https://registry.yarnpkg.com/url-polyfill/-/url-polyfill-1.1.12.tgz#6cdaa17f6b022841b3aec0bf8dbd87ac0cd33331" integrity sha512-mYFmBHCapZjtcNHW0MDq9967t+z4Dmg5CJ0KqysK3+ZbyoNOWQHksGCTWwDhxGXllkWlOc10Xfko6v4a3ucM6A== +utf-8-validate@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-6.0.3.tgz#7d8c936d854e86b24d1d655f138ee27d2636d777" + integrity sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA== + dependencies: + node-gyp-build "^4.3.0" + util-deprecate@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" From 0ec4bc88c3a3009d6587154726793f401433d09a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 09:16:49 +0000 Subject: [PATCH 17/27] fix(patch): Remove obviously invalid fetch from expressions (backport #25284) (#25302) * fix(patch): Remove obviously invalid fetch from expressions (#25284) (cherry picked from commit 4f4a387b7ee89281338ca920935ed771dd42e67a) # Conflicts: # frappe/patches.txt * chore: conflicts --------- Co-authored-by: Ankush Menat --- .../property_setter/patches/__init__.py | 0 .../remove_invalid_fetch_from_expressions.py | 26 +++++++++++++++++++ frappe/patches.txt | 1 + 3 files changed, 27 insertions(+) create mode 100644 frappe/custom/doctype/property_setter/patches/__init__.py create mode 100644 frappe/custom/doctype/property_setter/patches/remove_invalid_fetch_from_expressions.py diff --git a/frappe/custom/doctype/property_setter/patches/__init__.py b/frappe/custom/doctype/property_setter/patches/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/frappe/custom/doctype/property_setter/patches/remove_invalid_fetch_from_expressions.py b/frappe/custom/doctype/property_setter/patches/remove_invalid_fetch_from_expressions.py new file mode 100644 index 00000000000..777e2af2d86 --- /dev/null +++ b/frappe/custom/doctype/property_setter/patches/remove_invalid_fetch_from_expressions.py @@ -0,0 +1,26 @@ +import frappe + + +def execute(): + """Remove invalid fetch from expressions""" + + property_setters = frappe.get_all( + "Property Setter", {"doctype_or_field": "DocField", "property": "fetch_from"}, ["name", "value"] + ) + for ps in property_setters: + if not is_valid_expression(ps.value): + frappe.db.delete("Property Setter", {"name": ps.name}) + + custom_fields = frappe.get_all("Custom Field", {"fetch_from": ("is", "set")}, ["name", "fetch_from"]) + for cf in custom_fields: + if not is_valid_expression(cf.fetch_from): + frappe.db.set_value("Custom Field", cf.name, "fetch_from", "") + + +def is_valid_expression(expr) -> bool: + if not expr or "." not in expr: + return False + source_field, target_field = expr.split(".") + if not source_field or not target_field: + return False + return True diff --git a/frappe/patches.txt b/frappe/patches.txt index f1bbeacbc34..d86f4f5f95c 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -233,3 +233,4 @@ frappe.patches.v15_0.set_file_type frappe.core.doctype.data_import.patches.remove_stale_docfields_from_legacy_version frappe.patches.v15_0.validate_newsletter_recipients frappe.patches.v15_0.sanitize_workspace_titles +frappe.custom.doctype.property_setter.patches.remove_invalid_fetch_from_expressions From 092bd0e4f3db5aa359bdb7f6f22620837d783fac Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 11 Mar 2024 15:17:05 +0530 Subject: [PATCH 18/27] fix: commit before ddl --- frappe/database/mariadb/database.py | 2 +- frappe/database/mariadb/schema.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/database/mariadb/database.py b/frappe/database/mariadb/database.py index 20d666c698e..4c2fcd716c5 100644 --- a/frappe/database/mariadb/database.py +++ b/frappe/database/mariadb/database.py @@ -437,7 +437,7 @@ def updatedb(self, doctype, meta=None): db_table.validate() db_table.sync() - self.begin() + self.commit() def get_database_list(self): return self.sql("SHOW DATABASES", pluck=True) diff --git a/frappe/database/mariadb/schema.py b/frappe/database/mariadb/schema.py index c4afc106d8e..8eee72d835b 100644 --- a/frappe/database/mariadb/schema.py +++ b/frappe/database/mariadb/schema.py @@ -102,7 +102,7 @@ def alter(self): if query_parts: query_body = ", ".join(query_parts) query = f"ALTER TABLE `{self.table_name}` {query_body}" - frappe.db.sql(query) + frappe.db.sql_ddl(query) except Exception as e: if query := locals().get("query"): # this weirdness is to avoid potentially unbounded vars From c7c726e2616a063c1d15f8ef93e1269495de28c5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:36:14 +0530 Subject: [PATCH 19/27] fix: commit regardless of framework's transaction_writes count (#25322) (#25328) - There is code that depends on "commit", everything that happens with `db.after_commit`. - There are operations that will not write anything to DB but just enqueue the function, if it's enqueue_after_commit then it will break. (cherry picked from commit acf398f46ecbd0cad1270ec41d11c759f1ceb54d) Co-authored-by: Ankush Menat --- frappe/app.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/frappe/app.py b/frappe/app.py index 61f626bfcd3..555752a13b1 100644 --- a/frappe/app.py +++ b/frappe/app.py @@ -399,11 +399,7 @@ def handle_exception(e): def sync_database(rollback: bool) -> bool: # if HTTP method would change server state, commit if necessary - if ( - frappe.db - and (frappe.local.flags.commit or frappe.local.request.method in UNSAFE_HTTP_METHODS) - and frappe.db.transaction_writes - ): + if frappe.db and (frappe.local.flags.commit or frappe.local.request.method in UNSAFE_HTTP_METHODS): frappe.db.commit() rollback = False elif frappe.db: From 841d88584807aa86703199c01af0cdbdc304aed1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 17:13:55 +0530 Subject: [PATCH 20/27] fix: do not mark custom field as translatable by default (#25330) This is a special feature that should be enabled explicitly. In DocField, it's already disabled by default. (cherry picked from commit b26dcdc14f55551370fda6a9f0372aa4c21e4825) Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> --- frappe/custom/doctype/custom_field/custom_field.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/custom/doctype/custom_field/custom_field.json b/frappe/custom/doctype/custom_field/custom_field.json index fc802b79850..32c4552f19b 100644 --- a/frappe/custom/doctype/custom_field/custom_field.json +++ b/frappe/custom/doctype/custom_field/custom_field.json @@ -361,7 +361,7 @@ "label": "Ignore XSS Filter" }, { - "default": "1", + "default": "0", "depends_on": "eval:['Data', 'Select', 'Text', 'Small Text', 'Text Editor'].includes(doc.fieldtype)", "fieldname": "translatable", "fieldtype": "Check", @@ -457,7 +457,7 @@ "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2024-02-21 18:15:19.384933", + "modified": "2024-03-07 17:34:47.167183", "modified_by": "Administrator", "module": "Custom", "name": "Custom Field", From 24d713ebcf2607694a95d1c22c9fc1768945f7a6 Mon Sep 17 00:00:00 2001 From: Ponnusamy1-V Date: Mon, 11 Mar 2024 17:08:40 +0530 Subject: [PATCH 21/27] fix: list view click and drag select rows (cherry picked from commit b09833ac1c2adbbc6be31506361e1082b5497620) --- frappe/public/js/frappe/list/list_view.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 24d34182172..c7b558e97ba 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -1283,6 +1283,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { */ this.dragClick = false; this.$result.on("mousedown", ".list-row-checkbox", (e) => { + e.stopPropagation?.(); + e.preventDefault?.(); this.dragClick = true; this.check = !e.target.checked; }); From bda498fb3e6b9d74c06a468c49594c42d8da807f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 18:41:25 +0530 Subject: [PATCH 22/27] ci: split pre-commit config (#24437) (#25338) (cherry picked from commit 885fab62f2ee7ac0656f1a5810ca897ad555844a) Co-authored-by: Ankush Menat --- .github/workflows/linters.yml | 3 +-- .github/workflows/pre-commit.yml | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/pre-commit.yml diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index f1d5553c07d..4552fb22b12 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -51,7 +51,7 @@ jobs: python $GITHUB_WORKSPACE/.github/helper/documentation.py $PR_NUMBER linter: - name: 'Frappe Linter' + name: 'Semgrep Rules' runs-on: ubuntu-latest if: github.event_name == 'pull_request' @@ -61,7 +61,6 @@ jobs: with: python-version: '3.10' cache: pip - - uses: pre-commit/action@v3.0.0 - name: Download Semgrep rules run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 00000000000..32ececc78aa --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,26 @@ +name: Pre-commit + +on: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: precommit-frappe-${{ github.event_name }}-${{ github.event.number }} + cancel-in-progress: true + +jobs: + linter: + name: 'precommit' + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + cache: pip + - uses: pre-commit/action@v3.0.0 From 24479caa0519983e2a0c5dc1fe93a8c51b7fdd82 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 13:17:47 +0000 Subject: [PATCH 23/27] fix: Use debug log to log DB queries (backport #25265) (#25301) * fix: Use debug log to log DB queries (cherry picked from commit 13c74ec23146fbc17a96b21b7c0913436d5c8fc9) * fix: avoid linking to ephemeral virtual docs (cherry picked from commit 448991d74a828913e4e1fc7950b37818fd6a6731) * ci: add debug gha option (cherry picked from commit 77c907a9283d459fdd9c72fd1a32e743706a0798) # Conflicts: # .github/workflows/server-tests.yml --------- Co-authored-by: Ankush Menat --- .github/workflows/server-tests.yml | 13 +++++++++++- .../prepared_report/prepared_report.json | 5 ++--- .../prepared_report/prepared_report.py | 2 +- frappe/database/database.py | 20 +++++++++---------- frappe/handler.py | 1 - frappe/tests/test_api.py | 6 +++--- frappe/utils/response.py | 4 ++-- 7 files changed, 30 insertions(+), 21 deletions(-) diff --git a/.github/workflows/server-tests.yml b/.github/workflows/server-tests.yml index 4ef2066be65..efea6e90a72 100644 --- a/.github/workflows/server-tests.yml +++ b/.github/workflows/server-tests.yml @@ -139,7 +139,18 @@ jobs: - name: Show bench output if: ${{ always() }} - run: cat ~/frappe-bench/bench_start.log || true + run: | + cd ~/frappe-bench + cat bench_start.log || true + cd logs + for f in ./*.log*; do + echo "Printing log: $f"; + cat $f + done + + - name: Setup tmate session + uses: mxschmitt/action-tmate@v3 + if: ${{ contains( github.event.pull_request.labels.*.name, 'debug-gha') }} # This is required because github still doesn't understand knowingly skipped tests faux-test: diff --git a/frappe/core/doctype/prepared_report/prepared_report.json b/frappe/core/doctype/prepared_report/prepared_report.json index b64b91c4ef6..ec86237d7fb 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.json +++ b/frappe/core/doctype/prepared_report/prepared_report.json @@ -83,10 +83,9 @@ }, { "fieldname": "job_id", - "fieldtype": "Link", + "fieldtype": "Data", "label": "Job ID", "no_copy": 1, - "options": "RQ Job", "read_only": 1 }, { @@ -106,7 +105,7 @@ ], "in_create": 1, "links": [], - "modified": "2023-07-01 18:29:12.700239", + "modified": "2024-03-07 12:01:58.209879", "modified_by": "Administrator", "module": "Core", "name": "Prepared Report", diff --git a/frappe/core/doctype/prepared_report/prepared_report.py b/frappe/core/doctype/prepared_report/prepared_report.py index 5cf7829460e..4781b2e8e97 100644 --- a/frappe/core/doctype/prepared_report/prepared_report.py +++ b/frappe/core/doctype/prepared_report/prepared_report.py @@ -31,7 +31,7 @@ class PreparedReport(Document): error_message: DF.Text | None filters: DF.SmallText | None - job_id: DF.Link | None + job_id: DF.Data | None queued_at: DF.Datetime | None queued_by: DF.Data | None report_end_time: DF.Datetime | None diff --git a/frappe/database/database.py b/frappe/database/database.py index 9bc241f8a1a..1156890ca62 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -194,7 +194,7 @@ def sql( """ if isinstance(query, MySQLQueryBuilder | PostgreSQLQueryBuilder): - frappe.errprint("Use run method to execute SQL queries generated by Query Engine") + frappe.log("Use run method to execute SQL queries generated by Query Engine") debug = debug or getattr(self, "debug", False) query = str(query) @@ -234,7 +234,7 @@ def sql( self._cursor.execute(query, values) except Exception as e: if self.is_syntax_error(e): - frappe.errprint(f"Syntax error in query:\n{query} {values or ''}") + frappe.log(f"Syntax error in query:\n{query} {values or ''}") elif self.is_deadlocked(e): raise frappe.QueryDeadlockError(e) from e @@ -254,13 +254,13 @@ def sql( # TODO: added temporarily elif self.db_type == "postgres": traceback.print_stack() - frappe.errprint(f"Error in query:\n{e}") + frappe.log(f"Error in query:\n{e}") raise elif isinstance(e, self.ProgrammingError): if frappe.conf.developer_mode: traceback.print_stack() - frappe.errprint(f"Error in query:\n{query, values}") + frappe.log(f"Error in query:\n{query, values}") raise if not ( @@ -271,7 +271,7 @@ def sql( if debug: time_end = time() - frappe.errprint(f"Execution time: {time_end - time_start:.2f} sec") + frappe.log(f"Execution time: {time_end - time_start:.2f} sec") self.log_query(query, values, debug, explain) @@ -343,7 +343,7 @@ def _log_query( _query = _query or str(mogrified_query) if explain and is_query_type(_query, "select"): self.explain_query(_query) - frappe.errprint(_query) + frappe.log(_query) if frappe.conf.logging == 2: _query = _query or str(mogrified_query) @@ -391,14 +391,14 @@ def lazy_mogrify(self, query: Query, values: QueryValues) -> LazyMogrify: def explain_query(self, query, values=None): """Print `EXPLAIN` in error log.""" - frappe.errprint("--- query explain ---") + frappe.log("--- query explain ---") try: self._cursor.execute(f"EXPLAIN {query}", values) except Exception as e: - frappe.errprint(f"error in query explain: {e}") + frappe.log(f"error in query explain: {e}") else: - frappe.errprint(json.dumps(self.fetch_as_dict(), indent=1)) - frappe.errprint("--- query explain end ---") + frappe.log(json.dumps(self.fetch_as_dict(), indent=1)) + frappe.log("--- query explain end ---") def sql_list(self, query, values=(), debug=False, **kwargs): """Return data as list of single elements (first column). diff --git a/frappe/handler.py b/frappe/handler.py index fdee4d86881..956c4ffeb5d 100644 --- a/frappe/handler.py +++ b/frappe/handler.py @@ -286,7 +286,6 @@ def get_attr(cmd): f"Calling shorthand for {cmd} is deprecated, please specify full path in RPC call." ) method = globals()[cmd] - frappe.log("method:" + cmd) return method diff --git a/frappe/tests/test_api.py b/frappe/tests/test_api.py index 9ad11ae5d4c..cab59153500 100644 --- a/frappe/tests/test_api.py +++ b/frappe/tests/test_api.py @@ -187,9 +187,9 @@ def test_get_list_debug(self): with suppress_stdout(): response = self.get(self.resource_path(self.DOCTYPE), {"sid": self.sid, "debug": True}) self.assertEqual(response.status_code, 200) - self.assertIn("exc", response.json) - self.assertIsInstance(response.json["exc"], str) - self.assertIsInstance(eval(response.json["exc"]), list) + self.assertIn("_debug_messages", response.json) + self.assertIsInstance(response.json["_debug_messages"], str) + self.assertIsInstance(json.loads(response.json["_debug_messages"]), list) def test_get_list_fields(self): # test 6: fetch response with fields diff --git a/frappe/utils/response.py b/frappe/utils/response.py index f61294744ab..4b98bb3ad4f 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -183,7 +183,7 @@ def _make_logs_v1(): if frappe.local.message_log: response["_server_messages"] = json.dumps([json.dumps(d) for d in frappe.local.message_log]) - if frappe.debug_log and frappe.conf.get("logging"): + if frappe.debug_log: response["_debug_messages"] = json.dumps(frappe.local.debug_log) if frappe.flags.error_message: @@ -196,7 +196,7 @@ def _make_logs_v2(): if frappe.local.message_log: response["messages"] = frappe.local.message_log - if frappe.debug_log and frappe.conf.get("logging"): + if frappe.debug_log: response["debug"] = [{"message": m} for m in frappe.local.debug_log] From 45499c2c73bd070b11caeae47fe9b8b9f030537b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 13:34:41 +0000 Subject: [PATCH 24/27] fix: Skip virtual doctypes in link field checks (#24759) (#25333) (cherry picked from commit b6eef6ebc2d32d5f3c9232e105791f4646d5cfee) Co-authored-by: Ankush Menat --- frappe/desk/form/linked_with.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py index 7fd865d165e..e36b067231a 100644 --- a/frappe/desk/form/linked_with.py +++ b/frappe/desk/form/linked_with.py @@ -613,6 +613,10 @@ def get_linked_fields(doctype, without_ignore_user_permissions_enabled=False): if options in ret: del ret[options] + virtual_doctypes = frappe.get_all("DocType", {"is_virtual": 1}, pluck="name") + for dt in virtual_doctypes: + ret.pop(dt, None) + return ret @@ -639,7 +643,11 @@ def get_dynamic_linked_fields(doctype, without_ignore_user_permissions_enabled=F if is_single(df.doctype): continue - is_child = frappe.get_meta(df.doctype).istable + meta = frappe.get_meta(df.doctype) + if meta.is_virtual: + continue + + is_child = meta.istable possible_link = frappe.get_all( df.doctype, filters={df.doctype_fieldname: doctype}, From a870b5ac1101628ca2498d7abca31449c798b5ef Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:23:21 +0000 Subject: [PATCH 25/27] fix(auto_email_report): check if column (docfield) parent is a child table (#25335) (#25342) This code tried to fetch the child doctype with the parent's ID otherwise Support ticket 11145 Signed-off-by: Akhil Narang Co-authored-by: Ankush Menat (cherry picked from commit c441c844f9ef4c4e1b9c30293a1e88b4ce226f9d) Co-authored-by: Akhil Narang --- frappe/email/doctype/auto_email_report/auto_email_report.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index 4c96dacf913..990dfe6e6f9 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -345,7 +345,9 @@ def make_links(columns, data): if col.options and row.get(col.options): row[col.fieldname] = get_link_to_form(row[col.options], row[col.fieldname]) elif col.fieldtype == "Currency": - doc = frappe.get_doc(col.parent, doc_name) if doc_name and col.get("parent") else None + doc = None + if doc_name and col.get("parent") and not frappe.get_meta(col.parent).istable: + doc = frappe.get_doc(col.parent, doc_name) # Pass the Document to get the currency based on docfield option row[col.fieldname] = frappe.format_value(row[col.fieldname], col, doc=doc) return columns, data From 7cc3686a0c82aed949aa6c99059555e1ce402106 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 15:19:05 +0000 Subject: [PATCH 26/27] revert: fetch validations (backport #25343) (#25345) * revert: fetch validations (#25343) (cherry picked from commit fc7366deefa66d0efb5fcc868fb8904db90e646b) # Conflicts: # frappe/core/doctype/doctype/doctype.py * chore: conflicts --------- Co-authored-by: Ankush Menat --- frappe/core/doctype/doctype/doctype.py | 44 -------------------------- 1 file changed, 44 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 169b6f56a33..befbf815dca 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -1589,49 +1589,6 @@ def check_no_of_ratings(docfield): if docfield.options and (int(docfield.options) > 10 or int(docfield.options) < 3): frappe.throw(_("Options for Rating field can range from 3 to 10")) - def check_fetch_from(docfield): - if not frappe.request: - return - - fetch_from = docfield.fetch_from - fieldname = docfield.fieldname - if not fetch_from: - return - - if "." not in fetch_from: - frappe.throw( - _("Fetch From syntax for field {0} is invalid. `.` dot missing: {1}").format( - frappe.bold(fieldname), frappe.bold(fetch_from) - ) - ) - link_fieldname, source_fieldname = docfield.fetch_from.split(".", 1) - if not link_fieldname or not source_fieldname: - frappe.throw( - _( - "Fetch From syntax for field {0} is invalid: {1}. Fetch From should be in form of 'link_fieldname.source_fieldname'" - ).format(frappe.bold(fieldname), frappe.bold(fetch_from)) - ) - - link_df = meta.get("fields", {"fieldname": link_fieldname, "fieldtype": "Link"}) - if not link_df: - frappe.throw( - _("Fetch From for field {0} is invalid: {1}. Link field {2} not found.").format( - frappe.bold(fieldname), frappe.bold(fetch_from), frappe.bold(link_fieldname) - ) - ) - - doctype = link_df[0].options - fetch_from_doctype = frappe.get_meta(doctype) - - if not frappe.db.has_column(doctype, source_fieldname) and not fetch_from_doctype.get_field( - source_fieldname - ): - frappe.throw( - _("Fetch From for field {0} is invalid: {1} does not have a field {2}").format( - frappe.bold(fieldname), frappe.bold(doctype), frappe.bold(source_fieldname) - ) - ) - fields = meta.get("fields") fieldname_list = [d.fieldname for d in fields] @@ -1667,7 +1624,6 @@ def check_fetch_from(docfield): check_child_table_option(d) check_max_height(d) check_no_of_ratings(d) - check_fetch_from(d) if not frappe.flags.in_migrate: check_fold(fields) From 785b5c28c02472c9c1f86cb6df3f8d7be5c3df92 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 11 Mar 2024 21:29:24 +0530 Subject: [PATCH 27/27] fix: MacOS shortcuts (backport #25272) (#25280) * fix: mac shortcuts - "Command" symbol in Navbar - Show "Alt" symbol instead of text (cherry picked from commit 4e77e5f0c4c13da5754bdd527fe39db486a47b85) * fix: show "Shift" symbol istead of text (cherry picked from commit e9a57deff96b0aa29bfc7c87f969685760f1f6b0) * fix: settings and help shortcuts (cherry picked from commit 04a1b54281b66f5bae18073867175eb8bc1ff2ad) --------- Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> --- frappe/public/js/frappe/form/toolbar.js | 4 ++-- frappe/public/js/frappe/ui/keyboard.js | 9 ++++++--- frappe/public/js/frappe/ui/page.js | 7 ++++++- frappe/public/js/frappe/ui/toolbar/navbar.html | 2 +- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js index 19b6c120271..2f3bc1edc13 100644 --- a/frappe/public/js/frappe/form/toolbar.js +++ b/frappe/public/js/frappe/form/toolbar.js @@ -453,7 +453,7 @@ frappe.ui.form.Toolbar = class Toolbar { }, true, { - shortcut: "ctrl+z", + shortcut: "Ctrl+Z", condition: () => !this.frm.is_form_builder(), description: __("Undo last action"), } @@ -465,7 +465,7 @@ frappe.ui.form.Toolbar = class Toolbar { }, true, { - shortcut: "ctrl+y", + shortcut: "Ctrl+Y", condition: () => !this.frm.is_form_builder(), description: __("Redo last action"), } diff --git a/frappe/public/js/frappe/ui/keyboard.js b/frappe/public/js/frappe/ui/keyboard.js index 2e3bd956163..740a6b341c8 100644 --- a/frappe/public/js/frappe/ui/keyboard.js +++ b/frappe/public/js/frappe/ui/keyboard.js @@ -98,8 +98,11 @@ frappe.ui.keys.show_keyboard_shortcut_dialog = () => { .map(frappe.utils.to_title_case) .join("+"); if (frappe.utils.is_mac()) { - shortcut_label = shortcut_label.replace("Ctrl", "⌘"); + shortcut_label = shortcut_label.replace("Ctrl", "⌘").replace("Alt", "⌥"); } + + shortcut_label = shortcut_label.replace("Shift", "⇧"); + return ` ${shortcut_label} ${shortcut.description || ""} @@ -215,7 +218,7 @@ frappe.ui.keys.add_shortcut({ shortcut: "alt+s", action: function (e) { e.preventDefault(); - $(".dropdown-navbar-user a").eq(0).click(); + $(".dropdown-navbar-user button").eq(0).click(); }, description: __("Open Settings"), }); @@ -232,7 +235,7 @@ frappe.ui.keys.add_shortcut({ shortcut: "alt+h", action: function (e) { e.preventDefault(); - $(".dropdown-help a").eq(0).click(); + $(".dropdown-help button").eq(0).click(); }, description: __("Open Help"), }); diff --git a/frappe/public/js/frappe/ui/page.js b/frappe/public/js/frappe/ui/page.js index ec1b0de2a01..5ecc82a26ac 100644 --- a/frappe/public/js/frappe/ui/page.js +++ b/frappe/public/js/frappe/ui/page.js @@ -536,10 +536,15 @@ frappe.ui.Page = class Page { } // label if (frappe.utils.is_mac()) { - shortcut_obj.shortcut_label = shortcut_obj.shortcut.replace("Ctrl", "⌘"); + shortcut_obj.shortcut_label = shortcut_obj.shortcut + .replace("Ctrl", "⌘") + .replace("Alt", "⌥"); } else { shortcut_obj.shortcut_label = shortcut_obj.shortcut; } + + shortcut_obj.shortcut_label = shortcut_obj.shortcut_label.replace("Shift", "⇧"); + // actual shortcut string shortcut_obj.shortcut = shortcut_obj.shortcut.toLowerCase(); // action is button click diff --git a/frappe/public/js/frappe/ui/toolbar/navbar.html b/frappe/public/js/frappe/ui/toolbar/navbar.html index db478939b87..c8fdb346bce 100644 --- a/frappe/public/js/frappe/ui/toolbar/navbar.html +++ b/frappe/public/js/frappe/ui/toolbar/navbar.html @@ -26,7 +26,7 @@ id="navbar-search" type="text" class="form-control" - placeholder="{%= __("Search or type a command (Ctrl + G)") %}" + placeholder="{%= __('Search or type a command ({0})', [frappe.utils.is_mac() ? '⌘ + G' : 'Ctrl + G']) %}" aria-haspopup="true" >