From 9bfc97a823328163d7c632072528c163e66c9e23 Mon Sep 17 00:00:00 2001 From: shariquerik Date: Tue, 23 Feb 2021 18:53:24 +0530 Subject: [PATCH 01/39] fix: 'Not Saved' even after saving/submitting a doctype --- frappe/core/doctype/user_permission/user_permission.js | 2 +- frappe/public/js/frappe/form/form.js | 4 ++-- frappe/public/js/frappe/model/model.js | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/core/doctype/user_permission/user_permission.js b/frappe/core/doctype/user_permission/user_permission.js index 4c3f5b4eb8..6c6b74c5df 100644 --- a/frappe/core/doctype/user_permission/user_permission.js +++ b/frappe/core/doctype/user_permission/user_permission.js @@ -45,7 +45,7 @@ frappe.ui.form.on('User Permission', { set_applicable_for_constraint: frm => { frm.toggle_reqd('applicable_for', !frm.doc.apply_to_all_doctypes); if (frm.doc.apply_to_all_doctypes) { - frm.set_value('applicable_for', null); + frm.set_value('applicable_for', null, null, true); } }, diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 8d96054d16..a0f546b42c 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -1320,7 +1320,7 @@ frappe.ui.form.Form = class FrappeForm { return doc; } - set_value(field, value, if_missing) { + set_value(field, value, if_missing, avoid_dirty=false) { var me = this; var _set = function(f, v) { var fieldobj = me.fields_dict[f]; @@ -1340,7 +1340,7 @@ frappe.ui.form.Form = class FrappeForm { me.refresh_field(f); return Promise.resolve(); } else { - return frappe.model.set_value(me.doctype, me.doc.name, f, v); + return frappe.model.set_value(me.doctype, me.doc.name, f, v, me.fieldtype, avoid_dirty); } } } else { diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index 9ec7b0e931..f93f712740 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -401,7 +401,7 @@ $.extend(frappe.model, { } }, - set_value: function(doctype, docname, fieldname, value, fieldtype) { + set_value: function(doctype, docname, fieldname, value, fieldtype, avoid_dirty=false) { /* help: Set a value locally (if changed) and execute triggers */ var doc; @@ -427,7 +427,7 @@ $.extend(frappe.model, { } doc[key] = value; - tasks.push(() => frappe.model.trigger(key, value, doc)); + if (!avoid_dirty) tasks.push(() => frappe.model.trigger(key, value, doc)); } else { // execute link triggers (want to reselect to execute triggers) if(in_list(["Link", "Dynamic Link"], fieldtype) && doc) { From 861ff16ac8d045f5a35191c2f15a182d9e46a183 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sat, 5 Mar 2022 19:53:45 +0100 Subject: [PATCH 02/39] refactor: `frappe.db.exists` --- frappe/database/database.py | 50 +++++++++++++++++++++++-------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index dc9f20d8c2..9b1828f811 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -884,27 +884,39 @@ class Database(object): return self.sql("select name from `tab{doctype}` limit 1".format(doctype=doctype)) def exists(self, dt, dn=None, cache=False): - """Returns true if document exists. + """Return the document name of a matching document, or None. - :param dt: DocType name. - :param dn: Document name or filter dict.""" - if isinstance(dt, str): - if dt!="DocType" and dt==dn: - return True # single always exists (!) - try: - return self.get_value(dt, dn, "name", cache=cache) - except Exception: - return None + Note: `cache` only works if `dt` and `dn` are of type `str`. - elif isinstance(dt, dict) and dt.get('doctype'): - try: - conditions = [] - for d in dt: - if d == 'doctype': continue - conditions.append([d, '=', dt[d]]) - return self.get_all(dt['doctype'], filters=conditions, as_list=1) - except Exception: - return None + ## Examples + + Pass doctype and docname (only in this case we can cache the result) + + ``` + exists("User", "jane@example.org", cache=True) + ``` + + Pass a dict of filters including the `"doctype"` key: + + ``` + exists({"doctype": "User", "full_name": "Jane Doe"}) + ``` + + Pass the doctype and a dict of filters: + + ``` + exists("User", {"full_name": "Jane Doe"}) + ``` + """ + if dt == dn: + # single always exists (!) + return dn + + if isinstance(dt, dict): + _dt = dt.pop("doctype") + dt, dn = _dt, dt + + return self.get_value(dt, dn, ignore=True, cache=cache) def count(self, dt, filters=None, debug=False, cache=False): """Returns `COUNT(*)` for given DocType and filters.""" From e6261f15cca054868f10596d1ec70921193d1a34 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Tue, 8 Mar 2022 12:36:18 +0530 Subject: [PATCH 03/39] fix: web form list button --- frappe/public/js/frappe/web_form/web_form_list.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 f4d41c2a0b..272c65cf69 100644 --- a/frappe/public/js/frappe/web_form/web_form_list.js +++ b/frappe/public/js/frappe/web_form/web_form_list.js @@ -212,8 +212,7 @@ export default class WebFormList { "btn", "btn-secondary", "btn-sm", - "ml-2", - "text-white" + "ml-2" ); } else if (type == "danger") { From 067e842e01d6425671b048a9bf5f745a053416dc Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Wed, 9 Mar 2022 09:58:44 +0530 Subject: [PATCH 04/39] fix: web form list empty state --- .../js/frappe/web_form/web_form_list.js | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) 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 f4d41c2a0b..42cc10142f 100644 --- a/frappe/public/js/frappe/web_form/web_form_list.js +++ b/frappe/public/js/frappe/web_form/web_form_list.js @@ -16,7 +16,8 @@ export default class WebFormList { if (this.table) { Array.from(this.table.tBodies).forEach(tbody => tbody.remove()); let check = document.getElementById('select-all'); - check.checked = false; + if (check) + check.checked = false; } this.rows = []; this.page_length = 20; @@ -131,9 +132,33 @@ export default class WebFormList { this.make_table_head(); } - this.append_rows(this.data); + if (this.data.length) { + this.append_rows(this.data); + this.wrapper.appendChild(this.table); + } + else { + let new_button = ""; + let empty_state = document.createElement("div"); + empty_state.classList.add("no-result", "text-muted", "flex", "justify-center", "align-center"); + + frappe.has_permission(this.doctype, "", "create", () => { + new_button = `

`; + }); + + empty_state.innerHTML = `
+
+ Generic Empty State +
+

${__("No {0} found", [__(this.doctype)])}

+ ${new_button}`; + + this.wrapper.appendChild(empty_state); + } - this.wrapper.appendChild(this.table); } make_table_head() { From 428de40dcef5b2a903153b353d956408d07d5880 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Wed, 9 Mar 2022 14:39:44 +0530 Subject: [PATCH 05/39] fix: formatting --- frappe/public/js/frappe/web_form/web_form_list.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 42cc10142f..b7e92052a4 100644 --- a/frappe/public/js/frappe/web_form/web_form_list.js +++ b/frappe/public/js/frappe/web_form/web_form_list.js @@ -135,8 +135,7 @@ export default class WebFormList { if (this.data.length) { this.append_rows(this.data); this.wrapper.appendChild(this.table); - } - else { + } else { let new_button = ""; let empty_state = document.createElement("div"); empty_state.classList.add("no-result", "text-muted", "flex", "justify-center", "align-center"); From 2558c6bee0b2d7254a78d77919302d5bc2af84b9 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 16 Mar 2022 18:30:22 +0530 Subject: [PATCH 06/39] feat: Drop site support for postgres --- frappe/database/__init__.py | 3 ++- frappe/database/postgres/setup_db.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/frappe/database/__init__.py b/frappe/database/__init__.py index 7b26ac31b3..5db0537ed7 100644 --- a/frappe/database/__init__.py +++ b/frappe/database/__init__.py @@ -18,7 +18,8 @@ def setup_database(force, source_sql=None, verbose=None, no_mariadb_socket=False def drop_user_and_database(db_name, root_login=None, root_password=None): import frappe if frappe.conf.db_type == 'postgres': - pass + import frappe.database.postgres.setup_db + return frappe.database.postgres.setup_db.drop_user_and_database(db_name, root_login, root_password) else: import frappe.database.mariadb.setup_db return frappe.database.mariadb.setup_db.drop_user_and_database(db_name, root_login, root_password) diff --git a/frappe/database/postgres/setup_db.py b/frappe/database/postgres/setup_db.py index b3b2e0fd41..4b265e7660 100644 --- a/frappe/database/postgres/setup_db.py +++ b/frappe/database/postgres/setup_db.py @@ -95,3 +95,11 @@ def get_root_connection(root_login=None, root_password=None): frappe.local.flags.root_connection = frappe.database.get_db(user=root_login, password=root_password) return frappe.local.flags.root_connection + + +def drop_user_and_database(db_name, root_login, root_password): + root_conn = get_root_connection(frappe.flags.root_login or root_login, frappe.flags.root_password or root_password) + root_conn.commit() + root_conn.sql(f"SELECT pg_terminate_backend (pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = %s", (db_name, )) + root_conn.sql(f"DROP DATABASE IF EXISTS {db_name}") + root_conn.sql(f"DROP USER IF EXISTS {db_name}") From 776ba30a4d836469776e37af28dc48d5883376df Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 16 Mar 2022 19:06:41 +0530 Subject: [PATCH 07/39] fix: Add more verbosity for drop-site command --- frappe/commands/site.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frappe/commands/site.py b/frappe/commands/site.py index b54f369e34..63da4db093 100644 --- a/frappe/commands/site.py +++ b/frappe/commands/site.py @@ -677,7 +677,9 @@ def _drop_site(site, db_root_username=None, db_root_password=None, archived_site try: if not no_backup: - scheduled_backup(ignore_files=False, force=True) + click.secho(f"Taking backup of {site}", fg="green") + odb = scheduled_backup(ignore_files=False, force=True, verbose=True) + odb.print_summary() except Exception as err: if force: pass @@ -692,6 +694,7 @@ def _drop_site(site, db_root_username=None, db_root_password=None, archived_site click.echo("\n".join(messages)) sys.exit(1) + click.secho("Dropping site database and user", fg="green") drop_user_and_database(frappe.conf.db_name, db_root_username, db_root_password) archived_sites_path = archived_sites_path or os.path.join(frappe.get_app_path('frappe'), '..', '..', '..', 'archived', 'sites') From 734d0b4fe8f6ee0e810d7e5bb205329ad9acf80a Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 17 Mar 2022 01:35:46 +0100 Subject: [PATCH 08/39] test: frappe.db.exists --- frappe/tests/test_db.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index bbd09590be..504a1eb3b8 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -301,6 +301,14 @@ class TestDB(unittest.TestCase): # recover transaction to continue other tests raise Exception + def test_exists(self): + dt, dn = "User", "Administrator" + self.assertEqual(frappe.db.exists(dt, dn, cache=True), dn) + self.assertEqual(frappe.db.exists(dt, dn), dn) + self.assertEqual(frappe.db.exists(dt, {"name": ("=", dn)}), dn) + self.assertEqual(frappe.db.exists({"doctype": dt, "name": ("like", "Admin%")}), dn) + self.assertEqual(frappe.db.exists(dt, [["name", "=", dn]]), dn) + @run_only_if(db_type_is.MARIADB) class TestDDLCommandsMaria(unittest.TestCase): From 7e550e919ca51d5986314db9a6227cb988f5644f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 17 Mar 2022 02:07:23 +0100 Subject: [PATCH 09/39] revert: don't check for doctype (861ff16) --- frappe/database/database.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 9b1828f811..5d91e0b4f1 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -908,7 +908,7 @@ class Database(object): exists("User", {"full_name": "Jane Doe"}) ``` """ - if dt == dn: + if dt != "DocType" and dt == dn: # single always exists (!) return dn From c26cf2547885b7b9c6d608a23c273d1501dd2f07 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 17 Mar 2022 02:46:00 +0100 Subject: [PATCH 10/39] fix: avoid invalid call to frappe.db.exists --- frappe/model/base_document.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py index 8a81aa5610..c251325bcb 100644 --- a/frappe/model/base_document.py +++ b/frappe/model/base_document.py @@ -963,7 +963,7 @@ class BaseDocument(object): from frappe.model.meta import get_default_df df = get_default_df(fieldname) - if not currency and df: + if df.fieldtype == "Currency" and not currency: currency = self.get(df.get("options")) if not frappe.db.exists('Currency', currency, cache=True): currency = None From d68187ab9679d3cc0fc95129716625154c5712db Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Thu, 17 Mar 2022 16:42:45 +0530 Subject: [PATCH 11/39] fix: empty state height --- .../public/js/frappe/web_form/web_form_list.js | 18 ++++++++---------- frappe/public/scss/website/index.scss | 13 +++++++++++++ 2 files changed, 21 insertions(+), 10 deletions(-) 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 b7e92052a4..1f3628ac38 100644 --- a/frappe/public/js/frappe/web_form/web_form_list.js +++ b/frappe/public/js/frappe/web_form/web_form_list.js @@ -141,23 +141,21 @@ export default class WebFormList { empty_state.classList.add("no-result", "text-muted", "flex", "justify-center", "align-center"); frappe.has_permission(this.doctype, "", "create", () => { - new_button = `

`; - empty_state.innerHTML = `
+ empty_state.innerHTML = `
- Generic Empty State + Generic Empty State
-

${__("No {0} found", [__(this.doctype)])}

+

${__("No {0} found", [__(this.doctype)])}

${new_button}`; - this.wrapper.appendChild(empty_state); + this.wrapper.appendChild(empty_state); + }); } - } make_table_head() { diff --git a/frappe/public/scss/website/index.scss b/frappe/public/scss/website/index.scss index 2cc0f64f76..e36e649eb7 100644 --- a/frappe/public/scss/website/index.scss +++ b/frappe/public/scss/website/index.scss @@ -311,3 +311,16 @@ h5.modal-title { .empty-list-icon { height: 70px; } + +.null-state { + height: 60px; + width: auto; + margin-bottom: var(--margin-md); + img { + fill: var(--fg-color); + } +} + +.no-result { + min-height: #{"calc(100vh - 284px)"}; +} From bbcb99d65d9e560d101b810918724b02e47d3ed2 Mon Sep 17 00:00:00 2001 From: Jannat Patel Date: Thu, 17 Mar 2022 16:55:14 +0530 Subject: [PATCH 12/39] fix: removed console --- frappe/public/js/frappe/web_form/web_form_list.js | 1 - 1 file changed, 1 deletion(-) 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 1f3628ac38..09127a9f7f 100644 --- a/frappe/public/js/frappe/web_form/web_form_list.js +++ b/frappe/public/js/frappe/web_form/web_form_list.js @@ -141,7 +141,6 @@ export default class WebFormList { empty_state.classList.add("no-result", "text-muted", "flex", "justify-center", "align-center"); frappe.has_permission(this.doctype, "", "create", () => { - console.log(this) new_button = ``; From c4be72c2d404f337ba10d747e27525feb66aa9b7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 18 Mar 2022 12:18:15 +0530 Subject: [PATCH 13/39] fix: Pass skip_dirty_trigger flag to the set_value and model trigger Also, renamed avoid_dirty to skip_dirty_trigger to be more explicit --- frappe/public/js/frappe/model/model.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/model/model.js b/frappe/public/js/frappe/model/model.js index d0b1c729c6..3b95a4b3f1 100644 --- a/frappe/public/js/frappe/model/model.js +++ b/frappe/public/js/frappe/model/model.js @@ -412,7 +412,7 @@ $.extend(frappe.model, { } }, - set_value: function(doctype, docname, fieldname, value, fieldtype, avoid_dirty=false) { + set_value: function(doctype, docname, fieldname, value, fieldtype, skip_dirty_trigger=false) { /* help: Set a value locally (if changed) and execute triggers */ var doc; @@ -438,11 +438,11 @@ $.extend(frappe.model, { } doc[key] = value; - if (!avoid_dirty) tasks.push(() => frappe.model.trigger(key, value, doc)); + tasks.push(() => frappe.model.trigger(key, value, doc, skip_dirty_trigger)); } else { // execute link triggers (want to reselect to execute triggers) if(in_list(["Link", "Dynamic Link"], fieldtype) && doc) { - tasks.push(() => frappe.model.trigger(key, value, doc)); + tasks.push(() => frappe.model.trigger(key, value, doc, skip_dirty_trigger)); } } }); @@ -467,7 +467,7 @@ $.extend(frappe.model, { frappe.model.events[doctype][fieldname].push(fn); }, - trigger: function(fieldname, value, doc) { + trigger: function(fieldname, value, doc, skip_dirty_trigger=false) { const tasks = []; function enqueue_events(events) { @@ -477,7 +477,7 @@ $.extend(frappe.model, { if (!fn) continue; tasks.push(() => { - const return_value = fn(fieldname, value, doc); + const return_value = fn(fieldname, value, doc, skip_dirty_trigger); // if the trigger returns a promise, return it, // or use the default promise frappe.after_ajax From adb989fff2458cc92678ef6b6f5c224904be1864 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 18 Mar 2022 12:19:36 +0530 Subject: [PATCH 14/39] feat: Option to set form dirty even if form save is disabled --- frappe/public/js/frappe/form/form.js | 14 +++++++++----- frappe/public/js/frappe/form/toolbar.js | 6 +++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/frappe/public/js/frappe/form/form.js b/frappe/public/js/frappe/form/form.js index 494e3af705..7ec6677c7f 100644 --- a/frappe/public/js/frappe/form/form.js +++ b/frappe/public/js/frappe/form/form.js @@ -246,10 +246,12 @@ frappe.ui.form.Form = class FrappeForm { var me = this; // on main doc - frappe.model.on(me.doctype, "*", function(fieldname, value, doc) { + frappe.model.on(me.doctype, "*", function(fieldname, value, doc, skip_dirty_trigger=false) { // set input if (cstr(doc.name) === me.docname) { - me.dirty(); + if (!skip_dirty_trigger) { + me.dirty(); + } let field = me.fields_dict[fieldname]; field && field.refresh(fieldname); @@ -953,10 +955,12 @@ frappe.ui.form.Form = class FrappeForm { this.toolbar.set_primary_action(); } - disable_save() { + disable_save(set_dirty=false) { // IMPORTANT: this function should be called in refresh event this.save_disabled = true; this.toolbar.current_status = null; + // field changes should make form dirty + this.set_dirty = set_dirty; this.page.clear_primary_action(); } @@ -1447,7 +1451,7 @@ frappe.ui.form.Form = class FrappeForm { return doc; } - set_value(field, value, if_missing, avoid_dirty=false) { + set_value(field, value, if_missing, skip_dirty_trigger=false) { var me = this; var _set = function(f, v) { var fieldobj = me.fields_dict[f]; @@ -1467,7 +1471,7 @@ frappe.ui.form.Form = class FrappeForm { me.refresh_field(f); return Promise.resolve(); } else { - return frappe.model.set_value(me.doctype, me.doc.name, f, v, me.fieldtype, avoid_dirty); + return frappe.model.set_value(me.doctype, me.doc.name, f, v, me.fieldtype, skip_dirty_trigger); } } } else { diff --git a/frappe/public/js/frappe/form/toolbar.js b/frappe/public/js/frappe/form/toolbar.js index 016390a4e1..e55eb9fdeb 100644 --- a/frappe/public/js/frappe/form/toolbar.js +++ b/frappe/public/js/frappe/form/toolbar.js @@ -534,14 +534,14 @@ frappe.ui.form.Toolbar = class Toolbar { }); } show_title_as_dirty() { - if(this.frm.save_disabled) + if (this.frm.save_disabled && !this.frm.set_dirty) return; - if(this.frm.doc.__unsaved) { + if (this.frm.is_dirty()) { this.page.set_indicator(__("Not Saved"), "orange"); } - $(this.frm.wrapper).attr("data-state", this.frm.doc.__unsaved ? "dirty" : "clean"); + $(this.frm.wrapper).attr("data-state", this.frm.is_dirty() ? "dirty" : "clean"); } show_jump_to_field_dialog() { From fedcf48ada2bd17ce01f2238af9749b68a837437 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Fri, 18 Mar 2022 12:20:51 +0530 Subject: [PATCH 15/39] fix: Customize form issue where it remains "Not Saved" even after update fixes: https://github.com/frappe/frappe/issues/16068 --- frappe/custom/doctype/customize_form/customize_form.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js index 4862185b99..9cfe315e44 100644 --- a/frappe/custom/doctype/customize_form/customize_form.js +++ b/frappe/custom/doctype/customize_form/customize_form.js @@ -14,7 +14,6 @@ frappe.ui.form.on("Customize Form", { }, onload: function(frm) { - frm.disable_save(); frm.set_query("doc_type", function() { return { translate_values: false, @@ -110,7 +109,7 @@ frappe.ui.form.on("Customize Form", { }, refresh: function(frm) { - frm.disable_save(); + frm.disable_save(true); frm.page.clear_icons(); if (frm.doc.doc_type) { @@ -169,7 +168,7 @@ frappe.ui.form.on("Customize Form", { doc_type = localStorage.getItem("customize_doctype"); } if (doc_type) { - setTimeout(() => frm.set_value("doc_type", doc_type), 1000); + setTimeout(() => frm.set_value("doc_type", doc_type, false, true), 1000); } }, @@ -341,11 +340,11 @@ frappe.customize_form.confirm = function(msg, frm) { } frappe.customize_form.clear_locals_and_refresh = function(frm) { + delete frm.doc.__unsaved; // clear doctype from locals frappe.model.clear_doc("DocType", frm.doc.doc_type); delete frappe.meta.docfield_copy[frm.doc.doc_type]; - frm.refresh(); -} +}; extend_cscript(cur_frm.cscript, new frappe.model.DocTypeController({frm: cur_frm})); From 5187663d7370df3f548e0d83583a2f88220232fa Mon Sep 17 00:00:00 2001 From: hrwx Date: Sat, 19 Mar 2022 23:50:30 +0000 Subject: [PATCH 16/39] chore: prefix test for parse_email --- frappe/core/doctype/communication/test_communication.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/communication/test_communication.py b/frappe/core/doctype/communication/test_communication.py index d933c2f494..8012d8facf 100644 --- a/frappe/core/doctype/communication/test_communication.py +++ b/frappe/core/doctype/communication/test_communication.py @@ -4,8 +4,8 @@ import unittest from urllib.parse import quote import frappe -from frappe.email.doctype.email_queue.email_queue import EmailQueue from frappe.core.doctype.communication.communication import get_emails +from frappe.email.doctype.email_queue.email_queue import EmailQueue test_records = frappe.get_test_records('Communication') @@ -202,7 +202,7 @@ class TestCommunication(unittest.TestCase): self.assertIn(("Note", note.name), doc_links) - def parse_emails(self): + def test_parse_emails(self): emails = get_emails( [ 'comm_recipient+DocType+DocName@example.com', From 687ca6972d63a7505211b89ba4dc6b7f4efc174e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 20 Mar 2022 01:53:30 +0100 Subject: [PATCH 17/39] refactor: remove unused fallback --- frappe/public/js/frappe/views/reports/report_view.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index f80d59350f..2747a2723d 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -1026,7 +1026,7 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { } if (!docfield || docfield.report_hide) return; - let title = __(docfield ? docfield.label : toTitle(fieldname)); + let title = __(docfield.label); if (doctype !== this.doctype) { title += ` (${__(doctype)})`; } From 0d8733c462b99063a70eb50c3de2538608f613ba Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 20 Mar 2022 02:19:07 +0100 Subject: [PATCH 18/39] refactor: remove unused import --- frappe/utils/backups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index 3a0c337042..de7ce2e38b 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -15,7 +15,7 @@ import click # imports - module imports import frappe -from frappe import _, conf +from frappe import conf from frappe.utils import get_file_size, get_url, now, now_datetime, cint from frappe.utils.password import get_encryption_key From 4b8efc5ebd4e62a45aa6a4fecd68f607ab35d127 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 20 Mar 2022 02:19:41 +0100 Subject: [PATCH 19/39] fix: typo in parameter name --- frappe/utils/backups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index de7ce2e38b..dc5cdf43a2 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -505,7 +505,7 @@ download only after 24 hours.""" % { datetime_str.strftime("%d/%m/%Y %H:%M:%S") + """ - Backup ready to be downloaded""" ) - frappe.sendmail(recipients=recipient_list, msg=msg, subject=subject) + frappe.sendmail(recipients=recipient_list, message=msg, subject=subject) return recipient_list From 1d3ec4297402c4a6769af7731bf88e14d38999fb Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 20 Mar 2022 02:20:27 +0100 Subject: [PATCH 20/39] fix: send_email doesn't take arguments --- frappe/utils/backups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/backups.py b/frappe/utils/backups.py index dc5cdf43a2..5197b20bd3 100644 --- a/frappe/utils/backups.py +++ b/frappe/utils/backups.py @@ -779,7 +779,7 @@ if __name__ == "__main__": db_type=db_type, db_port=db_port, ) - odb.send_email("abc.sql.gz") + odb.send_email() if cmd == "delete_temp_backups": delete_temp_backups() From 8a4f316ec5c3492b4f692f60079c2d299c5ff29d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 20 Mar 2022 02:35:35 +0100 Subject: [PATCH 21/39] refactor: remove useless pass, log error --- frappe/core/doctype/user/user.py | 4 ++-- frappe/translate.py | 2 -- frappe/utils/nestedset.py | 1 - 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index d08755f9a8..1ad977547c 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -253,8 +253,8 @@ class User(Document): self.email_new_password(new_password) except frappe.OutgoingEmailError: - print(frappe.get_traceback()) - pass # email server not set, don't send email + # email server not set, don't send email + frappe.log_error(frappe.get_traceback()) @Document.hook def validate_reset_password(self): diff --git a/frappe/translate.py b/frappe/translate.py index 292adc71c7..0367d33d3b 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -650,8 +650,6 @@ def extract_messages_from_code(code): if isinstance(e, InvalidIncludePath): frappe.clear_last_message() - pass - messages = [] pattern = r"_\(([\"']{,3})(?P((?!\1).)*)\1(\s*,\s*context\s*=\s*([\"'])(?P((?!\5).)*)\5)*(\s*,\s*(.)*?\s*(,\s*([\"'])(?P((?!\11).)*)\11)*)*\)" diff --git a/frappe/utils/nestedset.py b/frappe/utils/nestedset.py index 98ad337043..2517761c45 100644 --- a/frappe/utils/nestedset.py +++ b/frappe/utils/nestedset.py @@ -227,7 +227,6 @@ class NestedSet(Document): update_nsm(self) except frappe.DoesNotExistError: if self.flags.on_rollback: - pass frappe.message_log.pop() else: raise From 9a2a2e7abea43d5854b92ea994cd62ac3f2f64f6 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 20 Mar 2022 03:08:23 +0100 Subject: [PATCH 22/39] fix: assign result of concat --- frappe/public/js/frappe/list/list_settings.js | 2 +- frappe/public/js/frappe/utils/utils.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/list/list_settings.js b/frappe/public/js/frappe/list/list_settings.js index 782a077a78..186a4370bc 100644 --- a/frappe/public/js/frappe/list/list_settings.js +++ b/frappe/public/js/frappe/list/list_settings.js @@ -375,7 +375,7 @@ export default class ListSettings { let me = this; if (me.removed_fields) { - me.removed_fields.concat(fields); + me.removed_fields = me.removed_fields.concat(fields); } else { me.removed_fields = fields; } diff --git a/frappe/public/js/frappe/utils/utils.js b/frappe/public/js/frappe/utils/utils.js index a944af523d..0514576380 100644 --- a/frappe/public/js/frappe/utils/utils.js +++ b/frappe/public/js/frappe/utils/utils.js @@ -231,7 +231,7 @@ Object.assign(frappe.utils, { if (tt && (tt.substr(0, 1)===">" || tt.substr(0, 4)===">")) { part.push(t); } else { - out.concat(part); + out = out.concat(part); out.push(t); part = []; } From afd5956e31836634c6d63a6b3b68d30234588a91 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Mon, 21 Mar 2022 10:39:52 +0530 Subject: [PATCH 23/39] style: Fix formatting --- .../js/frappe/web_form/web_form_list.js | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) 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 09127a9f7f..8d2959e49f 100644 --- a/frappe/public/js/frappe/web_form/web_form_list.js +++ b/frappe/public/js/frappe/web_form/web_form_list.js @@ -141,16 +141,26 @@ export default class WebFormList { empty_state.classList.add("no-result", "text-muted", "flex", "justify-center", "align-center"); frappe.has_permission(this.doctype, "", "create", () => { - new_button = ``; - - empty_state.innerHTML = `
-
- Generic Empty State -
-

${__("No {0} found", [__(this.doctype)])}

- ${new_button}`; + new_button = ` + + `; + + empty_state.innerHTML = ` +
+
+ Generic Empty State +
+

${__("No {0} found", [__(this.doctype)])}

+ ${new_button} +
+ `; this.wrapper.appendChild(empty_state); }); From b97cfed6d7678409019bdf02a48f3c8b8abdb770 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Sat, 19 Mar 2022 20:46:53 +0530 Subject: [PATCH 24/39] perf: limit rows to 1 for get_value and exists --- frappe/database/database.py | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index a3476c9538..099c9f1fde 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -384,7 +384,7 @@ class Database(object): """ ret = self.get_values(doctype, filters, fieldname, ignore, as_dict, debug, - order_by, cache=cache, for_update=for_update, run=run, pluck=pluck, distinct=distinct) + order_by, cache=cache, for_update=for_update, run=run, pluck=pluck, distinct=distinct, limit=1) if not run: return ret @@ -393,7 +393,7 @@ class Database(object): def get_values(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False, debug=False, order_by="KEEP_DEFAULT_ORDERING", update=None, cache=False, for_update=False, - run=True, pluck=False, distinct=False): + run=True, pluck=False, distinct=False, limit=None): """Returns multiple document properties. :param doctype: DocType name. @@ -423,14 +423,15 @@ class Database(object): if isinstance(filters, list): out = self._get_value_for_many_names( - doctype, - filters, - fieldname, - order_by, + doctype=doctype, + names=filters, + field=fieldname, + order_by=order_by, debug=debug, run=run, pluck=pluck, distinct=distinct, + limit=limit, ) else: @@ -444,17 +445,18 @@ class Database(object): if order_by: order_by = "modified" if order_by == "KEEP_DEFAULT_ORDERING" else order_by out = self._get_values_from_table( - fields, - filters, - doctype, - as_dict, - debug, - order_by, - update, + fields=fields, + filters=filters, + doctype=doctype, + as_dict=as_dict, + debug=debug, + order_by=order_by, + update=update, for_update=for_update, run=run, pluck=pluck, - distinct=distinct + distinct=distinct, + limit=limit, ) except Exception as e: if ignore and (frappe.db.is_missing_column(e) or frappe.db.is_table_missing(e)): @@ -623,6 +625,7 @@ class Database(object): run=True, pluck=False, distinct=False, + limit=None, ): field_objects = [] @@ -641,6 +644,7 @@ class Database(object): field_objects=field_objects, fields=fields, distinct=distinct, + limit=limit, ) if ( fields == "*" @@ -654,7 +658,7 @@ class Database(object): ) return r - def _get_value_for_many_names(self, doctype, names, field, order_by, debug=False, run=True, pluck=False, distinct=False): + def _get_value_for_many_names(self, doctype, names, field, order_by, debug=False, run=True, pluck=False, distinct=False, limit=None): names = list(filter(None, names)) if names: return self.get_all( @@ -667,6 +671,7 @@ class Database(object): as_list=1, run=run, distinct=distinct, + limit_page_length=limit ) else: return {} From 85428e817d2c2034b10ea6d18eb30d65555b3240 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 21 Mar 2022 11:16:10 +0530 Subject: [PATCH 25/39] test: get_value(s) with limits --- frappe/tests/test_db.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 235211b6b8..1cd9287ec3 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -14,7 +14,7 @@ from frappe.database.database import Database from frappe.query_builder import Field from frappe.query_builder.functions import Concat_ws from frappe.tests.test_query_builder import db_type_is, run_only_if -from frappe.utils import add_days, now, random_string +from frappe.utils import add_days, now, random_string, cint from frappe.utils.testutils import clear_custom_fields @@ -84,6 +84,27 @@ class TestDB(unittest.TestCase): ), ) + def test_get_value_limits(self): + + # check both dict and list style filters + filters = [{"enabled": 1}, [["enabled", "=", 1]]] + for filter in filters: + self.assertEqual(1, len(frappe.db.get_values("User", filters=filter, limit=1))) + # count of last touched rows as per DB-API 2.0 https://peps.python.org/pep-0249/#rowcount + self.assertGreaterEqual(1, cint(frappe.db._cursor.rowcount)) + self.assertEqual(2, len(frappe.db.get_values("User", filters=filter, limit=2))) + self.assertGreaterEqual(2, cint(frappe.db._cursor.rowcount)) + + # without limits length == count + self.assertEqual(len(frappe.db.get_values("User", filters=filter)), + frappe.db.count("User", filter)) + + frappe.db.get_value("User", filters=filter) + self.assertGreaterEqual(1, cint(frappe.db._cursor.rowcount)) + + frappe.db.exists("User", filter) + self.assertGreaterEqual(1, cint(frappe.db._cursor.rowcount)) + def test_escape(self): frappe.db.escape("香港濟生堂製藥有限公司 - IT".encode("utf-8")) From 10fbb4330ac3c10839d8f32672fc2c563d6b549a Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 10 Mar 2022 12:58:13 +0530 Subject: [PATCH 26/39] fix: setting permissions to any role of some doctypes is not working (cherry picked from commit 6612232babd61852e90ccfc2ca836397d80506cf) # Conflicts: # frappe/permissions.py --- frappe/permissions.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frappe/permissions.py b/frappe/permissions.py index af17faba01..985d398749 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -7,7 +7,11 @@ import frappe.share from frappe import _, msgprint from frappe.utils import cint from frappe.query_builder import DocType +<<<<<<< HEAD +======= +import frappe.share +>>>>>>> 6612232bab (fix: setting permissions to any role of some doctypes is not working) rights = ("select", "read", "write", "create", "delete", "submit", "cancel", "amend", "print", "email", "report", "import", "export", "set_user_permissions", "share") @@ -466,6 +470,12 @@ def update_permission_property(doctype, role, permlevel, ptype, value=None, vali table = DocType("Custom DocPerm") frappe.qb.update(table).set(ptype, value).where(table.name == name).run() +<<<<<<< HEAD +======= + table = DocType("Custom DocPerm") + frappe.qb.update(table).set(ptype, value).where(table.name == name).run() + +>>>>>>> 6612232bab (fix: setting permissions to any role of some doctypes is not working) if validate: validate_permissions_for_doctype(doctype) From b2c0bf7a4ee491a9f871b8cd62d40be25b642214 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 17 Mar 2022 13:19:50 +0530 Subject: [PATCH 27/39] fix: remove tab \t and newlines \n from start of query and remove from middle (cherry picked from commit ac5effc7dd4d876d06daf945f0b8b77ecdd0c05f) # Conflicts: # frappe/database/database.py --- frappe/database/database.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frappe/database/database.py b/frappe/database/database.py index a3476c9538..f9a230b25c 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -116,8 +116,14 @@ class Database(object): """ query = str(query) +<<<<<<< HEAD if not run: return query +======= + + # remove \n \t from start of query and replace them with space anywhere in middle + query = re.sub(r'\s', ' ', query).lstrip() +>>>>>>> ac5effc7dd (fix: remove tab \t and newlines \n from start of query and remove from middle) if re.search(r'ifnull\(', query, flags=re.IGNORECASE): # replaces ifnull in query with coalesce From fbac6fbfb40e9e03aa67dc0df32328bdb96cd7a1 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 17 Mar 2022 15:06:20 +0530 Subject: [PATCH 28/39] fix: only remove \n\t from start and end (cherry picked from commit 7bb172365f2c9ae6cca98ccc4dfee7714b9c3f0c) # Conflicts: # frappe/database/database.py --- frappe/database/database.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frappe/database/database.py b/frappe/database/database.py index f9a230b25c..3ce2b32fc8 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -121,9 +121,14 @@ class Database(object): return query ======= +<<<<<<< HEAD # remove \n \t from start of query and replace them with space anywhere in middle query = re.sub(r'\s', ' ', query).lstrip() >>>>>>> ac5effc7dd (fix: remove tab \t and newlines \n from start of query and remove from middle) +======= + # remove \n \t from start and end of query + query = re.sub(r'^\s*|\s*$', '', query) +>>>>>>> 7bb172365f (fix: only remove \n\t from start and end) if re.search(r'ifnull\(', query, flags=re.IGNORECASE): # replaces ifnull in query with coalesce From a832aa27af79e20b5ec47d21d0f8fffb8cf84f0f Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 21 Mar 2022 11:26:36 +0530 Subject: [PATCH 29/39] chore: whitespace --- frappe/public/js/frappe/web_form/web_form_list.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 076f2f266d..1ad332e3c2 100644 --- a/frappe/public/js/frappe/web_form/web_form_list.js +++ b/frappe/public/js/frappe/web_form/web_form_list.js @@ -142,8 +142,8 @@ export default class WebFormList { frappe.has_permission(this.doctype, "", "create", () => { new_button = ` - @@ -152,9 +152,9 @@ export default class WebFormList { empty_state.innerHTML = `
- Generic Empty State

${__("No {0} found", [__(this.doctype)])}

From 1054a1203e0d3a79025b561d88cb342c6fdd6d10 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 21 Mar 2022 12:09:31 +0530 Subject: [PATCH 30/39] fix: assinging thread locals to global variables --- frappe/email/doctype/auto_email_report/auto_email_report.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5ffde0c37b..abeb681a25 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -15,8 +15,6 @@ from frappe.utils.csvutils import to_csv from frappe.utils.xlsxutils import make_xlsx from frappe.desk.query_report import build_xlsx_data -max_reports_per_user = frappe.local.conf.max_reports_per_user or 3 - class AutoEmailReport(Document): def autoname(self): @@ -46,6 +44,8 @@ class AutoEmailReport(Document): def validate_report_count(self): '''check that there are only 3 enabled reports per user''' count = frappe.db.sql('select count(*) from `tabAuto Email Report` where user=%s and enabled=1', self.user)[0][0] + max_reports_per_user = frappe.local.conf.max_reports_per_user or 3 + if count > max_reports_per_user + (-1 if self.flags.in_insert else 0): frappe.throw(_('Only {0} emailed reports are allowed per user').format(max_reports_per_user)) From dbb622fce177491a2d8d7ee92d1abbebeeba0333 Mon Sep 17 00:00:00 2001 From: Isaiah Galorport <86836253+icecliff@users.noreply.github.com> Date: Mon, 21 Mar 2022 16:43:06 +0800 Subject: [PATCH 31/39] fix: Other user must not able to delete other user's comment except System Manager (#16018) * fix: Other user must not able to delete other user's comment except Admin * Update frappe/public/js/frappe/form/footer/form_timeline.js Co-authored-by: Sagar Vora * fix: Close condition scope Co-authored-by: Sagar Vora Co-authored-by: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> (cherry picked from commit e4137ca8a1bea4e35358e0ba9844042c3e4d334a) # Conflicts: # frappe/public/js/frappe/form/footer/form_timeline.js --- frappe/public/js/frappe/form/footer/form_timeline.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frappe/public/js/frappe/form/footer/form_timeline.js b/frappe/public/js/frappe/form/footer/form_timeline.js index d440874f36..7182aa667d 100644 --- a/frappe/public/js/frappe/form/footer/form_timeline.js +++ b/frappe/public/js/frappe/form/footer/form_timeline.js @@ -453,6 +453,7 @@ class FormTimeline extends BaseTimeline { let edit_wrapper = $(`
`).hide(); let edit_box = this.make_editable(edit_wrapper); let content_wrapper = comment_wrapper.find('.content'); +<<<<<<< HEAD let more_actions_wrapper = comment_wrapper.find('.more-actions'); if (frappe.model.can_delete("Comment")) { const delete_option = $(` @@ -461,6 +462,15 @@ class FormTimeline extends BaseTimeline { ${__("Delete")} +======= + + let delete_button = $(); + if (frappe.model.can_delete("Comment") && (frappe.session.user == doc.owner || frappe.user.has_role("System Manager"))) { + delete_button = $(` + +>>>>>>> e4137ca8a1 (fix: Other user must not able to delete other user's comment except System Manager (#16018)) `).click(() => this.delete_comment(doc.name)); more_actions_wrapper.find('.dropdown-menu').append(delete_option); } From 810867a0d53d35eec27cc7599c4e2ed980e61967 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Mon, 21 Mar 2022 14:18:58 +0530 Subject: [PATCH 32/39] fix: merge conflict --- .../public/js/frappe/form/footer/form_timeline.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/frappe/public/js/frappe/form/footer/form_timeline.js b/frappe/public/js/frappe/form/footer/form_timeline.js index 7182aa667d..0070d384d7 100644 --- a/frappe/public/js/frappe/form/footer/form_timeline.js +++ b/frappe/public/js/frappe/form/footer/form_timeline.js @@ -453,24 +453,17 @@ class FormTimeline extends BaseTimeline { let edit_wrapper = $(`
`).hide(); let edit_box = this.make_editable(edit_wrapper); let content_wrapper = comment_wrapper.find('.content'); -<<<<<<< HEAD let more_actions_wrapper = comment_wrapper.find('.more-actions'); - if (frappe.model.can_delete("Comment")) { + if (frappe.model.can_delete("Comment") && ( + frappe.session.user == doc.owner || + frappe.user.has_role("System Manager") + )) { const delete_option = $(`
  • ${__("Delete")}
  • -======= - - let delete_button = $(); - if (frappe.model.can_delete("Comment") && (frappe.session.user == doc.owner || frappe.user.has_role("System Manager"))) { - delete_button = $(` - ->>>>>>> e4137ca8a1 (fix: Other user must not able to delete other user's comment except System Manager (#16018)) `).click(() => this.delete_comment(doc.name)); more_actions_wrapper.find('.dropdown-menu').append(delete_option); } From 880832671aec005d2f0f5bb256ec1a430416eaa6 Mon Sep 17 00:00:00 2001 From: Karthikeyan Singaravelan Date: Mon, 21 Mar 2022 11:03:59 +0000 Subject: [PATCH 33/39] fix: use assertEqual instead of assertEquals for Python 3.11 compatibility --- frappe/core/doctype/file/test_file.py | 6 +++--- frappe/email/doctype/notification/test_notification.py | 2 +- frappe/tests/test_base_document.py | 6 +++--- frappe/tests/test_db.py | 4 ++-- frappe/tests/test_document.py | 6 +++--- frappe/tests/test_search.py | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/frappe/core/doctype/file/test_file.py b/frappe/core/doctype/file/test_file.py index d8e748a518..fb98a18d6e 100644 --- a/frappe/core/doctype/file/test_file.py +++ b/frappe/core/doctype/file/test_file.py @@ -382,7 +382,7 @@ class TestFile(unittest.TestCase): }).insert(ignore_permissions=True) test_file.make_thumbnail() - self.assertEquals(test_file.thumbnail_url, '/files/image_small.jpg') + self.assertEqual(test_file.thumbnail_url, '/files/image_small.jpg') # test web image without extension test_file = frappe.get_doc({ @@ -399,7 +399,7 @@ class TestFile(unittest.TestCase): test_file.reload() test_file.file_url = "/files/image_small.jpg" test_file.make_thumbnail(suffix="xs", crop=True) - self.assertEquals(test_file.thumbnail_url, '/files/image_small_xs.jpg') + self.assertEqual(test_file.thumbnail_url, '/files/image_small_xs.jpg') frappe.clear_messages() test_file.db_set('thumbnail_url', None) @@ -407,7 +407,7 @@ class TestFile(unittest.TestCase): test_file.file_url = frappe.utils.get_url('unknown.jpg') test_file.make_thumbnail(suffix="xs") self.assertEqual(json.loads(frappe.message_log[0]).get("message"), f"File '{frappe.utils.get_url('unknown.jpg')}' not found") - self.assertEquals(test_file.thumbnail_url, None) + self.assertEqual(test_file.thumbnail_url, None) def test_file_unzip(self): file_path = frappe.get_app_path('frappe', 'www/_test/assets/file.zip') diff --git a/frappe/email/doctype/notification/test_notification.py b/frappe/email/doctype/notification/test_notification.py index f05d35be3e..f6f216ada2 100644 --- a/frappe/email/doctype/notification/test_notification.py +++ b/frappe/email/doctype/notification/test_notification.py @@ -240,7 +240,7 @@ class TestNotification(unittest.TestCase): self.assertTrue(email_queue) # check if description is changed after alert since set_property_after_alert is set - self.assertEquals(todo.description, 'Changed by Notification') + self.assertEqual(todo.description, 'Changed by Notification') recipients = [d.recipient for d in email_queue.recipients] self.assertTrue('test2@example.com' in recipients) diff --git a/frappe/tests/test_base_document.py b/frappe/tests/test_base_document.py index 7e165e9045..fda795b5b6 100644 --- a/frappe/tests/test_base_document.py +++ b/frappe/tests/test_base_document.py @@ -7,12 +7,12 @@ class TestBaseDocument(unittest.TestCase): def test_docstatus(self): doc = BaseDocument({"docstatus": 0}) self.assertTrue(doc.docstatus.is_draft()) - self.assertEquals(doc.docstatus, 0) + self.assertEqual(doc.docstatus, 0) doc.docstatus = 1 self.assertTrue(doc.docstatus.is_submitted()) - self.assertEquals(doc.docstatus, 1) + self.assertEqual(doc.docstatus, 1) doc.docstatus = 2 self.assertTrue(doc.docstatus.is_cancelled()) - self.assertEquals(doc.docstatus, 2) + self.assertEqual(doc.docstatus, 2) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 1cd9287ec3..19b683aa75 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -386,7 +386,7 @@ class TestDDLCommandsMaria(unittest.TestCase): WHERE Key_name = '{index_name}'; """ ) - self.assertEquals(len(indexs_in_table), 2) + self.assertEqual(len(indexs_in_table), 2) class TestDBSetValue(unittest.TestCase): @@ -590,7 +590,7 @@ class TestDDLCommandsPost(unittest.TestCase): AND indexname = '{index_name}' ; """, ) - self.assertEquals(len(indexs_in_table), 1) + self.assertEqual(len(indexs_in_table), 1) @run_only_if(db_type_is.POSTGRES) def test_modify_query(self): diff --git a/frappe/tests/test_document.py b/frappe/tests/test_document.py index 55dbf001f9..169d1ebb2c 100644 --- a/frappe/tests/test_document.py +++ b/frappe/tests/test_document.py @@ -260,15 +260,15 @@ class TestDocument(unittest.TestCase): 'doctype': 'Test Formatted', 'currency': 100000 }) - self.assertEquals(d.get_formatted('currency', currency='INR', format="#,###.##"), '₹ 100,000.00') + self.assertEqual(d.get_formatted('currency', currency='INR', format="#,###.##"), '₹ 100,000.00') def test_limit_for_get(self): doc = frappe.get_doc("DocType", "DocType") # assuming DocType has more than 3 Data fields - self.assertEquals(len(doc.get("fields", limit=3)), 3) + self.assertEqual(len(doc.get("fields", limit=3)), 3) # limit with filters - self.assertEquals(len(doc.get("fields", filters={"fieldtype": "Data"}, limit=3)), 3) + self.assertEqual(len(doc.get("fields", filters={"fieldtype": "Data"}, limit=3)), 3) def test_virtual_fields(self): """Virtual fields are accessible via API and Form views, whenever .as_dict is invoked diff --git a/frappe/tests/test_search.py b/frappe/tests/test_search.py index f644f2dfcc..38a00f689a 100644 --- a/frappe/tests/test_search.py +++ b/frappe/tests/test_search.py @@ -70,10 +70,10 @@ class TestSearch(unittest.TestCase): result = frappe.response['results'] # Check whether the result is sorted or not - self.assertEquals(self.parent_doctype_name, result[0]['value']) + self.assertEqual(self.parent_doctype_name, result[0]['value']) # Check whether searching for parent also list out children - self.assertEquals(len(result), len(self.child_doctypes_names) + 1) + self.assertEqual(len(result), len(self.child_doctypes_names) + 1) #Search for the word "pay", part of the word "pays" (country) in french. def test_link_search_in_foreign_language(self): From d97c7e7cafcebb79840b9bc9a584ce9955ac70fd Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Mon, 21 Mar 2022 18:05:01 +0530 Subject: [PATCH 34/39] fix: resolved conflicts in permissions.py --- frappe/permissions.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/frappe/permissions.py b/frappe/permissions.py index 985d398749..a6c17fb59f 100644 --- a/frappe/permissions.py +++ b/frappe/permissions.py @@ -7,11 +7,7 @@ import frappe.share from frappe import _, msgprint from frappe.utils import cint from frappe.query_builder import DocType -<<<<<<< HEAD -======= -import frappe.share ->>>>>>> 6612232bab (fix: setting permissions to any role of some doctypes is not working) rights = ("select", "read", "write", "create", "delete", "submit", "cancel", "amend", "print", "email", "report", "import", "export", "set_user_permissions", "share") @@ -470,12 +466,6 @@ def update_permission_property(doctype, role, permlevel, ptype, value=None, vali table = DocType("Custom DocPerm") frappe.qb.update(table).set(ptype, value).where(table.name == name).run() -<<<<<<< HEAD -======= - table = DocType("Custom DocPerm") - frappe.qb.update(table).set(ptype, value).where(table.name == name).run() - ->>>>>>> 6612232bab (fix: setting permissions to any role of some doctypes is not working) if validate: validate_permissions_for_doctype(doctype) @@ -604,4 +594,4 @@ def is_parent_valid(child_doctype, parent_doctype): from frappe.core.utils import find parent_meta = frappe.get_meta(parent_doctype) child_table_field_exists = find(parent_meta.get_table_fields(), lambda d: d.options == child_doctype) - return not parent_meta.istable and child_table_field_exists \ No newline at end of file + return not parent_meta.istable and child_table_field_exists From 5c6c9bb5c461fcdde9cdfda664451b8332a7fc8c Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Mon, 21 Mar 2022 18:16:54 +0530 Subject: [PATCH 35/39] fix: resolved conflicts in database.py --- frappe/database/database.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 3ce2b32fc8..7bf3b46bf0 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -116,19 +116,11 @@ class Database(object): """ query = str(query) -<<<<<<< HEAD if not run: return query -======= -<<<<<<< HEAD - # remove \n \t from start of query and replace them with space anywhere in middle - query = re.sub(r'\s', ' ', query).lstrip() ->>>>>>> ac5effc7dd (fix: remove tab \t and newlines \n from start of query and remove from middle) -======= # remove \n \t from start and end of query query = re.sub(r'^\s*|\s*$', '', query) ->>>>>>> 7bb172365f (fix: only remove \n\t from start and end) if re.search(r'ifnull\(', query, flags=re.IGNORECASE): # replaces ifnull in query with coalesce From 512c62248769ce3a92cf433ded2bb53db58f281c Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 21 Mar 2022 20:51:35 +0100 Subject: [PATCH 36/39] test: make sure `exists` doesn't eat the doctype key --- frappe/tests/test_db.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 19b683aa75..10c601db00 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -327,7 +327,13 @@ class TestDB(unittest.TestCase): self.assertEqual(frappe.db.exists(dt, dn, cache=True), dn) self.assertEqual(frappe.db.exists(dt, dn), dn) self.assertEqual(frappe.db.exists(dt, {"name": ("=", dn)}), dn) - self.assertEqual(frappe.db.exists({"doctype": dt, "name": ("like", "Admin%")}), dn) + + filters = {"doctype": dt, "name": ("like", "Admin%")} + self.assertEqual(frappe.db.exists(filters), dn) + self.assertEqual( + filters["doctype"], dt + ) # make sure that doctype was not removed from filters + self.assertEqual(frappe.db.exists(dt, [["name", "=", dn]]), dn) From 44a7c0dd9318d984b20ae95180f5cf35eb146c28 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 21 Mar 2022 20:51:59 +0100 Subject: [PATCH 37/39] fix: copy dict before popping keys --- frappe/database/database.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index b1b5abffca..82a7e6f919 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -919,8 +919,8 @@ class Database(object): return dn if isinstance(dt, dict): - _dt = dt.pop("doctype") - dt, dn = _dt, dt + dt = dt.copy() # don't modify the original dict + dt, dn = dt.pop("doctype"), dt return self.get_value(dt, dn, ignore=True, cache=cache) From 179c9f117c7ecf8c9c2882ecf192031331294bbf Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 21 Mar 2022 21:12:05 +0100 Subject: [PATCH 38/39] perf: exists is already called in delete_doc --- frappe/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frappe/__init__.py b/frappe/__init__.py index 86f8be35ea..80dd2f5f15 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -978,8 +978,7 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa def delete_doc_if_exists(doctype, name, force=0): """Delete document if exists.""" - if db.exists(doctype, name): - delete_doc(doctype, name, force=force) + delete_doc(doctype, name, force=force, ignore_missing=True) def reload_doctype(doctype, force=False, reset_permissions=False): """Reload DocType from model (`[module]/[doctype]/[name]/[name].json`) files.""" From 7a9536332eb862d359e5bf0d0b9819b758dc45d7 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 22 Mar 2022 09:16:39 +0530 Subject: [PATCH 39/39] feat: Hide page head while scrolling down - To create more reading area in the form --- frappe/public/js/frappe/ui/page.js | 14 +++++++++----- frappe/public/scss/desk/page.scss | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/ui/page.js b/frappe/public/js/frappe/ui/page.js index 91a2390cdb..e3134b1f38 100644 --- a/frappe/public/js/frappe/ui/page.js +++ b/frappe/public/js/frappe/ui/page.js @@ -47,13 +47,17 @@ frappe.ui.Page = class Page { } setup_scroll_handler() { - window.addEventListener('scroll', () => { - if (document.documentElement.scrollTop) { - $('.page-head').toggleClass('drop-shadow', true); + let last_scroll = 0; + window.addEventListener('scroll', frappe.utils.throttle(() => { + $('.page-head').toggleClass('drop-shadow', !!document.documentElement.scrollTop); + let current_scroll = document.documentElement.scrollTop; + if (current_scroll > 0 && last_scroll <= current_scroll) { + $('.page-head').css("top", "-15px"); } else { - $('.page-head').removeClass('drop-shadow'); + $('.page-head').css("top", "var(--navbar-height)"); } - }); + last_scroll = current_scroll; + }), 500); } get_empty_state(title, message, primary_action) { diff --git a/frappe/public/scss/desk/page.scss b/frappe/public/scss/desk/page.scss index f0a9152cfb..2df349cb6c 100644 --- a/frappe/public/scss/desk/page.scss +++ b/frappe/public/scss/desk/page.scss @@ -88,6 +88,7 @@ top: var(--navbar-height); background: var(--bg-color); margin-bottom: 5px; + transition: 0.5s top; .page-head-content { height: var(--page-head-height); }