From 568ae60b7b44d828938bf3852afe6c3912bd6a38 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 28 May 2019 20:39:12 +0530 Subject: [PATCH 01/11] fix: same result set shwoing multiple times in the multi-select popup --- .../public/js/frappe/form/multi_select_dialog.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/multi_select_dialog.js b/frappe/public/js/frappe/form/multi_select_dialog.js index 3438c59a1f..4e569033ba 100644 --- a/frappe/public/js/frappe/form/multi_select_dialog.js +++ b/frappe/public/js/frappe/form/multi_select_dialog.js @@ -59,6 +59,7 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ { fieldtype: "Button", fieldname: "more_btn", label: __("More"), click: function(){ me.start += 20; + frappe.flags.auto_scroll = true; me.get_results(); } } @@ -117,10 +118,12 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ }); this.$parent.find('.input-with-feedback').on('change', (e) => { + frappe.flags.auto_scroll = false; this.get_results(); }); this.$parent.find('[data-fieldname="date_range"]').on('blur', (e) => { + frappe.flags.auto_scroll = false; this.get_results(); }); @@ -128,6 +131,7 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ var $this = $(this); clearTimeout($this.data('timeout')); $this.data('timeout', setTimeout(function() { + frappe.flags.auto_scroll = false; me.get_results(); }, 300)); }); @@ -176,11 +180,17 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ var me = this; var more_btn = me.dialog.fields_dict.more_btn.$wrapper; + + // Make empty result set if filter is set + if (!frappe.flags.auto_scroll) { + this.$results.empty(); + } + if(results.length === 0) { this.$results.empty(); more_btn.hide(); return; - } else { + } else if(more) { more_btn.show(); } @@ -188,7 +198,9 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ me.$results.append(me.make_list_row(result)); }); - this.$results.animate({scrollTop: me.$results.prop('scrollHeight')}, 500); + if (frappe.flags.auto_scroll) { + this.$results.animate({scrollTop: me.$results.prop('scrollHeight')}, 500); + } }, get_results: function() { From 3992d7ab21766f4187b1f7f8c213264f0947ffe2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 29 May 2019 10:07:11 +0530 Subject: [PATCH 02/11] fix: Update frappe datatable (#7579) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3bf733cf8a..8052734731 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "cookie": "^0.3.1", "express": "^4.16.2", "fast-deep-equal": "^2.0.1", - "frappe-datatable": "^1.13.1", + "frappe-datatable": "^1.13.2", "frappe-gantt": "^0.1.0", "fuse.js": "^3.2.0", "highlight.js": "^9.12.0", diff --git a/yarn.lock b/yarn.lock index 611503e063..75aa246a07 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1224,10 +1224,10 @@ forwarded@~0.1.2: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= -frappe-datatable@^1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-1.13.1.tgz#dbfa27fe735832cea54a0b35e3d3bfb839778c8d" - integrity sha512-FOC8dpsOSI+KnF5sBrYja9b2Y+3qUvYy/H6108QchKSvXYvuWVr/uuLk2N5xlz38PWU3d7n5lK0dkx+zXTKJ0w== +frappe-datatable@^1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-1.13.2.tgz#8b36c7cfc0ea660fc72eea8b1ae3c5dcc2a7d67d" + integrity sha512-4PyPDX22K4e4S3WGlLQx3oyxIW+ENsbGiN9L6aUpmjU+fOCC7J/FfSwGKdua2f+4yD+2ObpkyJYazBl3inAeCA== dependencies: hyperlist "^1.0.0-beta" lodash "^4.17.5" From 778bc4349b96a4c802495c6cb1f592e30d60f903 Mon Sep 17 00:00:00 2001 From: Faris Ansari Date: Wed, 29 May 2019 11:40:53 +0530 Subject: [PATCH 03/11] fix: Check if value is string (#7583) Breaks the check when value is a map. So we reverse the check. --- frappe/model/db_query.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index e009dd354c..a7ff19a2cd 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -381,7 +381,7 @@ class DatabaseQuery(object): elif f.operator.lower() in ('in', 'not in'): values = f.value or '' - if not isinstance(values, (list, tuple)): + if isinstance(values, frappe.string_types): values = values.split(",") fallback = "''" From 5a194be77bae6cb4f41da69c8048b542070fec19 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 29 May 2019 13:03:48 +0530 Subject: [PATCH 04/11] fix: UniqueFieldnameError while migration --- .../v11_0/apply_customization_to_custom_doctype.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/frappe/patches/v11_0/apply_customization_to_custom_doctype.py b/frappe/patches/v11_0/apply_customization_to_custom_doctype.py index d7c10a903d..0855616711 100644 --- a/frappe/patches/v11_0/apply_customization_to_custom_doctype.py +++ b/frappe/patches/v11_0/apply_customization_to_custom_doctype.py @@ -39,13 +39,17 @@ def execute(): df.set(ps.property, value) for cf in custom_fields: - df = frappe.new_doc('DocField', meta, 'fields') cf.pop('parenttype') cf.pop('parentfield') cf.pop('parent') cf.pop('name') - df.update(cf) - meta.fields.append(df) + field = meta.get_field(cf.fieldname) + if field: + field.update(cf) + else: + df = frappe.new_doc('DocField', meta, 'fields') + df.update(cf) + meta.fields.append(df) frappe.db.sql('DELETE FROM `tabCustom Field` WHERE name=%s', cf.name) meta.save() From be427b146439d5dace46d18e2b18a6b21fc9a98c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 29 May 2019 13:13:10 +0530 Subject: [PATCH 05/11] fix: issue in the latest version of babel, set it to 2.6.0 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index cc94f7cc5c..e832dbdf13 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,7 +15,7 @@ rauth>=0.6.2 requests redis==2.10.6 selenium -babel +babel==2.6.0 ipython html2text==2016.9.19 email_reply_parser From a1f441578cce51c342b91a5f96b07eff1864e546 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Wed, 29 May 2019 13:25:54 +0530 Subject: [PATCH 06/11] fix: Update regex for capturing touched tables from query Previous regex used to yield false positives and false negatives for queries like UPDATE tabToDo SET description = "something" Instead of yielding "tabToDo" it used to yield "tabToDo SET". Now two separate regexes handle single word and multi-word names In case of multi-word surrounding quotes are a must --- frappe/database.py | 20 +++++++++++++++++--- frappe/tests/test_db.py | 5 +++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/frappe/database.py b/frappe/database.py index 9323aa9518..3a2e0ce160 100644 --- a/frappe/database.py +++ b/frappe/database.py @@ -987,12 +987,26 @@ class Database: if values: query = self._cursor.mogrify(query, values) if query.strip().lower().split()[0] in ('insert', 'delete', 'update', 'alter'): - # ([`\"']?) Captures ', " or ` at the begining of the table name (if provided) + # single_word_regex is designed to match following patterns + # `tabXxx`, tabXxx and "tabXxx" + + # multi_word_regex is designed to match following patterns + # `tabXxx Xxx` and "tabXxx Xxx" + + # ([`"]?) Captures " or ` at the begining of the table name (if provided) + # \1 matches the first captured group (quote character) at the end of the table name + # multi word table name must have surrounding quotes. + # (tab([A-Z]\w+)( [A-Z]\w+)*) Captures table names that start with "tab" # and are continued with multiple words that start with a captital letter # e.g. 'tabXxx' or 'tabXxx Xxx' or 'tabXxx Xxx Xxx' and so on - # \1 matches the first captured group (quote character) at the end of the table name - tables = [groups[1] for groups in re.findall(r'([`"\']?)(tab([A-Z]\w+)( [A-Z]\w+)*)\1', query)] + + single_word_regex = r'([`"]?)(tab([A-Z]\w+))\1' + multi_word_regex = r'([`"])(tab([A-Z]\w+)( [A-Z]\w+)+)\1' + tables = [] + for regex in (single_word_regex, multi_word_regex): + tables += [groups[1] for groups in re.findall(regex, query)] + if frappe.flags.touched_tables is None: frappe.flags.touched_tables = set() frappe.flags.touched_tables.update(tables) diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index e3c1a62a78..551b0f2f27 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -45,6 +45,11 @@ class TestDB(unittest.TestCase): todo.save() self.assertIn('tabToDo', frappe.flags.touched_tables) + frappe.flags.touched_tables = set() + frappe.db.sql("UPDATE tabToDo SET description = 'Updated Description'") + self.assertNotIn('tabToDo SET', frappe.flags.touched_tables) + self.assertIn('tabToDo', frappe.flags.touched_tables) + frappe.flags.touched_tables = set() todo.delete() self.assertIn('tabToDo', frappe.flags.touched_tables) From 0fa0d4324a6951af296f722b79c31bec92adb9c2 Mon Sep 17 00:00:00 2001 From: Himanshu Mishra Date: Wed, 29 May 2019 17:49:48 +0530 Subject: [PATCH 07/11] perf: Don't fetch complete global unsubscribe (#7578) * Update queue.py * Update email_unsubscribe.json * Update email_unsubscribe.json --- .../email_unsubscribe/email_unsubscribe.json | 6 ++-- frappe/email/queue.py | 34 ++++++++++++++----- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/frappe/email/doctype/email_unsubscribe/email_unsubscribe.json b/frappe/email/doctype/email_unsubscribe/email_unsubscribe.json index 3624a55641..b24359f82f 100644 --- a/frappe/email/doctype/email_unsubscribe/email_unsubscribe.json +++ b/frappe/email/doctype/email_unsubscribe/email_unsubscribe.json @@ -3,7 +3,7 @@ "allow_import": 0, "allow_rename": 0, "beta": 0, - "creation": "2015-03-18 09:41:20.216319", + "creation": "2015-03-18 09:41:20.216320", "custom": 0, "docstatus": 0, "doctype": "DocType", @@ -35,7 +35,7 @@ "remember_last_selected_value": 0, "report_hide": 0, "reqd": 1, - "search_index": 0, + "search_index": 1, "set_only_once": 0, "unique": 0 }, @@ -172,4 +172,4 @@ "sort_order": "DESC", "track_changes": 1, "track_seen": 0 -} \ No newline at end of file +} diff --git a/frappe/email/queue.py b/frappe/email/queue.py index e62e7ca674..d0893d66d9 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -78,19 +78,35 @@ def send(recipients=None, sender=None, subject=None, message=None, text_content= except HTMLParser.HTMLParseError: text_content = "See html attachment" - if reference_doctype and reference_name: - unsubscribed = [d.email for d in frappe.db.get_all("Email Unsubscribe", "email", - {"reference_doctype": reference_doctype, "reference_name": reference_name})] + recipients = list(set(recipients)) + cc = list(set(cc)) - unsubscribed += [d.email for d in frappe.db.get_all("Email Unsubscribe", "email", - {"global_unsubscribe": 1})] - else: - unsubscribed = [] + all_ids = recipients + cc - recipients = [r for r in list(set(recipients)) if r and r not in unsubscribed] + unsubscribed = frappe.db.sql_list(''' + SELECT + distinct email + from + `tabEmail Unsubscribe` + where + email in %(all_ids)s + and ( + ( + reference_doctype = %(reference_doctype)s + and reference_name = %(reference_name)s + ) + or global_unsubscribe = 1 + ) + ''', { + 'all_ids': all_ids, + 'reference_doctype': reference_doctype, + 'reference_name': reference_name, + }) + + recipients = [r for r in recipients if r and r not in unsubscribed] if cc: - cc = [r for r in list(set(cc)) if r and r not in unsubscribed] + cc = [r for r in cc if r and r not in unsubscribed] if not recipients and not cc: # Recipients may have been unsubscribed, exit quietly From 279ec5e5331ad14ac967574a3283d2c23aaca254 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Wed, 29 May 2019 17:57:19 +0530 Subject: [PATCH 08/11] fix(email): Search contacts by email_id as well Email auto suggest now performs lookup on contact name and email_id fields --- frappe/email/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/email/__init__.py b/frappe/email/__init__.py index 53c6140d04..a48dbec195 100644 --- a/frappe/email/__init__.py +++ b/frappe/email/__init__.py @@ -23,7 +23,7 @@ def get_contact_list(txt, page_length=20): out = frappe.db.sql("""select email_id as value, concat(first_name, ifnull(concat(' ',last_name), '' )) as description from tabContact - where name like %(txt)s + where name like %(txt)s or email_id like %(txt)s %(condition)s limit %(page_length)s """, {'txt': "%%%s%%" % frappe.db.escape(txt), From e9c54ff6a8138b3dbaeb375708a17a7a29cef664 Mon Sep 17 00:00:00 2001 From: Aditya Hase Date: Thu, 30 May 2019 10:52:36 +0530 Subject: [PATCH 09/11] fix(list-view): Escape quotes in data-name (#7599) Fixes misbehaviour in bulk actions when selected documents have names with quotes. --- frappe/public/js/frappe/list/list_view.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 841f62d6a3..b5b94339e1 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -642,7 +642,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { .includes(user) ? '' : 'bold'; let subject_html = ` - +