From e205acae3c7605bae79dd5f661dbd7820572fa85 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 28 Sep 2021 13:08:28 +0530 Subject: [PATCH 01/23] feat: handle timeout and deadlocks in db.sql --- frappe/database/database.py | 4 ++++ frappe/exceptions.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/frappe/database/database.py b/frappe/database/database.py index c48e86d301..61eeac950f 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -158,6 +158,10 @@ class Database(object): # only for mariadb frappe.errprint('Syntax error in query:') frappe.errprint(query) + elif self.is_deadlocked(e) or self.is_timedout(e): + err_msg = _('There was a problem accessing required resources to complete the transaction. Please retry.') + exception = frappe.QueryTimeout if self.is_timedout(e) else frappe.QueryDeadlock + frappe.throw(msg=err_msg, title=_('Request Timeout'), exc=exception) if ignore_ddl and (self.is_missing_column(e) or self.is_missing_table(e) or self.cant_drop_field_or_key(e)): pass diff --git a/frappe/exceptions.py b/frappe/exceptions.py index 31a94ac883..6c063127c2 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -101,6 +101,8 @@ class DataTooLongException(ValidationError): pass class FileAlreadyAttachedException(Exception): pass class DocumentAlreadyRestored(Exception): pass class AttachmentLimitReached(Exception): pass +class QueryTimeout(ValidationError): pass +class QueryDeadlock(ValidationError): pass # OAuth exceptions class InvalidAuthorizationHeader(CSRFTokenError): pass class InvalidAuthorizationPrefix(CSRFTokenError): pass From 4109caf1bfee2687ad0d8dbe82cc42f4ee5435c6 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 28 Sep 2021 16:15:24 +0530 Subject: [PATCH 02/23] chore: improve message & title --- frappe/database/database.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 61eeac950f..1325ad166a 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -158,10 +158,14 @@ class Database(object): # only for mariadb frappe.errprint('Syntax error in query:') frappe.errprint(query) - elif self.is_deadlocked(e) or self.is_timedout(e): - err_msg = _('There was a problem accessing required resources to complete the transaction. Please retry.') - exception = frappe.QueryTimeout if self.is_timedout(e) else frappe.QueryDeadlock - frappe.throw(msg=err_msg, title=_('Request Timeout'), exc=exception) + elif self.is_deadlocked(e): + err_msg = _('There was a problem accessing required resources to complete the transaction.') + '
' + err_msg += _('Please try again.') + frappe.throw(msg=err_msg, title=_('Deadlock Occurred'), exc=frappe.QueryDeadlock) + elif self.is_timedout(e): + err_msg = _('There was a problem accessing required resources to complete the transaction.') + '
' + err_msg += _('Please try again.') + frappe.throw(msg=err_msg, title=_('Request Timeout'), exc=frappe.QueryTimeout) if ignore_ddl and (self.is_missing_column(e) or self.is_missing_table(e) or self.cant_drop_field_or_key(e)): pass From bd9f14d696502591c582a8af14afe2048653d238 Mon Sep 17 00:00:00 2001 From: Summayya Hashmani <58825865+sumaiya2908@users.noreply.github.com> Date: Thu, 14 Oct 2021 15:00:02 +0530 Subject: [PATCH 03/23] feat: add login fields --- frappe/www/login.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/www/login.html b/frappe/www/login.html index 4c0c54d76f..85e5ae9296 100644 --- a/frappe/www/login.html +++ b/frappe/www/login.html @@ -79,7 +79,11 @@ {% else %} {{ email_login_body() }} From db081527105cc4c3fdbd28ce59d2ebc722ce2a2a Mon Sep 17 00:00:00 2001 From: Summayya Hashmani <58825865+sumaiya2908@users.noreply.github.com> Date: Thu, 14 Oct 2021 15:05:55 +0530 Subject: [PATCH 04/23] refactor: add padding between social login button --- frappe/public/scss/login.bundle.scss | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frappe/public/scss/login.bundle.scss b/frappe/public/scss/login.bundle.scss index 17f33b0a67..3963fbecc6 100644 --- a/frappe/public/scss/login.bundle.scss +++ b/frappe/public/scss/login.bundle.scss @@ -135,7 +135,7 @@ body { } .social-logins { - margin: var(--margin-md) 0; + margin-top: var(--margin-md); font-size: var(--text-md); .social-login-buttons { @@ -147,7 +147,11 @@ body { } min-width: 50%; padding: 0 4px; - margin-bottom: var(--margin-sm); + margin-bottom: var(--margin-md); + + &:last-child { + margin-bottom: 0; + } } } } From b11c99137c90bd59ffb0dc49c7be6cb8bb97fa7f Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 19 Oct 2021 18:01:18 +0530 Subject: [PATCH 05/23] chore: rename exceptions --- frappe/database/database.py | 4 ++-- frappe/exceptions.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 1325ad166a..88c643540f 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -161,11 +161,11 @@ class Database(object): elif self.is_deadlocked(e): err_msg = _('There was a problem accessing required resources to complete the transaction.') + '
' err_msg += _('Please try again.') - frappe.throw(msg=err_msg, title=_('Deadlock Occurred'), exc=frappe.QueryDeadlock) + frappe.throw(msg=err_msg, title=_('Deadlock Occurred'), exc=frappe.QueryDeadlockError) elif self.is_timedout(e): err_msg = _('There was a problem accessing required resources to complete the transaction.') + '
' err_msg += _('Please try again.') - frappe.throw(msg=err_msg, title=_('Request Timeout'), exc=frappe.QueryTimeout) + frappe.throw(msg=err_msg, title=_('Request Timeout'), exc=frappe.QueryTimeoutError) if ignore_ddl and (self.is_missing_column(e) or self.is_missing_table(e) or self.cant_drop_field_or_key(e)): pass diff --git a/frappe/exceptions.py b/frappe/exceptions.py index 6c063127c2..81b4b2e24d 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -101,8 +101,8 @@ class DataTooLongException(ValidationError): pass class FileAlreadyAttachedException(Exception): pass class DocumentAlreadyRestored(Exception): pass class AttachmentLimitReached(Exception): pass -class QueryTimeout(ValidationError): pass -class QueryDeadlock(ValidationError): pass +class QueryTimeoutError(ValidationError): pass +class QueryDeadlockError(ValidationError): pass # OAuth exceptions class InvalidAuthorizationHeader(CSRFTokenError): pass class InvalidAuthorizationPrefix(CSRFTokenError): pass From 6cd18711d10ae4a27032434606bb674d94044f60 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 19 Oct 2021 18:06:32 +0530 Subject: [PATCH 06/23] chore: better message --- frappe/database/database.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 88c643540f..d05284e2ea 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -158,14 +158,12 @@ class Database(object): # only for mariadb frappe.errprint('Syntax error in query:') frappe.errprint(query) - elif self.is_deadlocked(e): - err_msg = _('There was a problem accessing required resources to complete the transaction.') + '
' + elif self.is_deadlocked(e) or self.is_timedout(e): + title = _('Deadlock Occurred') if self.is_deadlocked(e) else _('Request Timeout') + err_msg = _('Server was too busy to process this request.') + '
' err_msg += _('Please try again.') - frappe.throw(msg=err_msg, title=_('Deadlock Occurred'), exc=frappe.QueryDeadlockError) - elif self.is_timedout(e): - err_msg = _('There was a problem accessing required resources to complete the transaction.') + '
' - err_msg += _('Please try again.') - frappe.throw(msg=err_msg, title=_('Request Timeout'), exc=frappe.QueryTimeoutError) + exception = frappe.QueryDeadlockError if self.is_deadlocked(e) else frappe.QueryTimeoutError + frappe.throw(msg=err_msg, title=title, exc=exception) if ignore_ddl and (self.is_missing_column(e) or self.is_missing_table(e) or self.cant_drop_field_or_key(e)): pass From 67647724e84faaa02f0319e4e24a251ff9baa490 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 19 Oct 2021 18:33:31 +0530 Subject: [PATCH 07/23] refactor(minor): DRY-er human friendly error messages --- frappe/database/database.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index d05284e2ea..14f2297398 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -18,6 +18,13 @@ from frappe.utils import now, getdate, cast, get_datetime, get_table_name from frappe.model.utils.link_count import flush_local_link_count +def get_human_friendly_error_message(): + return ( + _('Server was too busy to process this request.') + + '
' + + _('Please try again.') + ) + class Database(object): """ Open a database connection with the given parmeters, if use_default is True, use the @@ -158,12 +165,20 @@ class Database(object): # only for mariadb frappe.errprint('Syntax error in query:') frappe.errprint(query) - elif self.is_deadlocked(e) or self.is_timedout(e): - title = _('Deadlock Occurred') if self.is_deadlocked(e) else _('Request Timeout') - err_msg = _('Server was too busy to process this request.') + '
' - err_msg += _('Please try again.') - exception = frappe.QueryDeadlockError if self.is_deadlocked(e) else frappe.QueryTimeoutError - frappe.throw(msg=err_msg, title=title, exc=exception) + + elif self.is_deadlocked(e): + frappe.throw( + title=_('Deadlock Occurred'), + msg=get_human_friendly_error_message(), + exc=frappe.QueryDeadlockError, + ) + + elif self.is_timedout(e): + frappe.throw( + title=_('Request Timeout'), + msg=get_human_friendly_error_message(), + exc=frappe.QueryTimeoutError, + ) if ignore_ddl and (self.is_missing_column(e) or self.is_missing_table(e) or self.cant_drop_field_or_key(e)): pass From 4f288db878321086f3681ca6bf078ad5dd377d9b Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Tue, 19 Oct 2021 20:00:35 +0530 Subject: [PATCH 08/23] refactor: handle exception messages on client side --- frappe/database/database.py | 19 ++------------- frappe/public/js/frappe/request.js | 37 +++++++++++++++++++++++++++--- frappe/utils/response.py | 5 +++- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 037374dc09..3695c1c18b 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -23,13 +23,6 @@ from .query import Query from pypika.terms import PseudoColumn -def get_human_friendly_error_message(): - return ( - _('Server was too busy to process this request.') - + '
' - + _('Please try again.') - ) - class Database(object): """ Open a database connection with the given parmeters, if use_default is True, use the @@ -176,18 +169,10 @@ class Database(object): frappe.errprint(query) elif self.is_deadlocked(e): - frappe.throw( - title=_('Deadlock Occurred'), - msg=get_human_friendly_error_message(), - exc=frappe.QueryDeadlockError, - ) + raise frappe.QueryDeadlockError elif self.is_timedout(e): - frappe.throw( - title=_('Request Timeout'), - msg=get_human_friendly_error_message(), - exc=frappe.QueryTimeoutError, - ) + raise frappe.QueryTimeoutError if ignore_ddl and (self.is_missing_column(e) or self.is_missing_table(e) or self.cant_drop_field_or_key(e)): pass diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index 12fa9c8e21..fc2367b37c 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -206,6 +206,25 @@ frappe.request.call = function(opts) { } }; + var exception_handlers = { + 'QueryTimeoutError': function(data) { + frappe.utils.play_sound("error"); + frappe.msgprint({ + title:__('Request Timeout'), + indicator:'red', + message:__("Server was too busy to process this request. Please try again.") + }); + }, + 'QueryDeadlockError': function(data) { + frappe.utils.play_sound("error"); + frappe.msgprint({ + title:__('Deadlock Occurred'), + indicator:'red', + message:__("Server was too busy to process this request. Please try again.") + }); + } + } + var ajax_args = { url: opts.url || frappe.request.url, data: opts.args, @@ -272,13 +291,25 @@ frappe.request.call = function(opts) { }) .fail(function(xhr, textStatus) { try { + if (xhr.responseText) { + var data = JSON.parse(xhr.responseText); + if (data.exception) { + // frappe.exceptions.CustomError -> CustomError + var exception = data.exception.split('.').at(-1); + var exception_handler = exception_handlers[exception]; + if (exception_handler) { + exception_handler(data); + return; + } + } + } var status_code_handler = statusCode[xhr.statusCode().status]; if (status_code_handler) { status_code_handler(xhr); - } else { - // if not handled by error handler! - opts.error_callback && opts.error_callback(xhr); + return; } + // if not handled by error handler! + opts.error_callback && opts.error_callback(xhr); } catch(e) { console.log("Unable to handle failed response"); // eslint-disable-line console.trace(e); // eslint-disable-line diff --git a/frappe/utils/response.py b/frappe/utils/response.py index d7119ebe3b..f7aa09d7fc 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -26,7 +26,10 @@ def report_error(status_code): allow_traceback = cint(frappe.db.get_system_setting('allow_error_traceback')) if frappe.db else True if (allow_traceback and (status_code!=404 or frappe.conf.logging) and not frappe.local.flags.disable_traceback): - frappe.errprint(frappe.utils.get_traceback()) + traceback = frappe.utils.get_traceback() + frappe.errprint(traceback) + if traceback: + frappe.local.response.exception = traceback.splitlines()[-1] response = build_response("json") response.status_code = status_code From 4ffc8e7660679a2d355ee69d88a3614f8b7b6594 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 25 Oct 2021 13:18:52 +0530 Subject: [PATCH 09/23] chore(dev): Remove duplicate set owner & modifiedby --- frappe/core/doctype/doctype/doctype.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 5a91016e32..93188ca628 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -87,10 +87,6 @@ class DocType(Document): if self.default_print_format and not self.custom: frappe.throw(_('Standard DocType cannot have default print format, use Customize Form')) - if frappe.conf.get('developer_mode'): - self.owner = 'Administrator' - self.modified_by = 'Administrator' - def validate_field_name_conflicts(self): """Check if field names dont conflict with controller properties and methods""" core_doctypes = [ @@ -177,7 +173,6 @@ class DocType(Document): if self.is_virtual and self.custom: frappe.throw(_("Not allowed to create custom Virtual DocType."), CannotCreateStandardDoctypeError) - if frappe.conf.get('developer_mode'): self.owner = 'Administrator' self.modified_by = 'Administrator' From 1b9961e508f0cea92e228c1a07802545ff3d1a96 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 25 Oct 2021 13:19:34 +0530 Subject: [PATCH 10/23] fix(doctype): Update controller file on has_web_view change --- frappe/core/doctype/doctype/doctype.py | 56 ++++++++++++++++++-------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index 93188ca628..a35cb5317f 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -310,9 +310,7 @@ class DocType(Document): if allow_doctype_export: self.export_doc() self.make_controller_template() - - if self.has_web_view: - self.set_base_class_for_controller() + self.set_base_class_for_controller() # update index if not self.custom: @@ -350,23 +348,49 @@ class DocType(Document): now=now, doctype=self.name) def set_base_class_for_controller(self): - '''Updates the controller class to subclass from `WebsiteGenertor`, - if it is a subclass of `Document`''' - controller_path = frappe.get_module_path(frappe.scrub(self.module), - 'doctype', frappe.scrub(self.name), frappe.scrub(self.name) + '.py') + """If DocType.has_web_view has been changed, updates the controller class and import + from `WebsiteGenertor` to `Document` or viceversa""" - with open(controller_path, 'r') as f: + if not self.has_value_changed("has_web_view"): + return + + despaced_name = self.name.replace(" ", "_") + scrubbed_name = frappe.scrub(self.name) + scrubbed_module = frappe.scrub(self.module) + controller_path = frappe.get_module_path( + scrubbed_module, "doctype", scrubbed_name, f"{scrubbed_name}.py" + ) + + document_cls_tag = f"class {despaced_name}(Document)" + document_import_tag = "from frappe.model.document import Document" + website_generator_cls_tag = f"class {despaced_name}(WebsiteGenerator)" + website_generator_import_tag = "from frappe.website.generators.website_generator import WebsiteGenerator" + + with open(controller_path) as f: code = f.read() + updated_code = code - class_string = '\nclass {0}(Document)'.format(self.name.replace(' ', '')) - if '\nfrom frappe.model.document import Document' in code and class_string in code: - code = code.replace('from frappe.model.document import Document', - 'from frappe.website.website_generator import WebsiteGenerator') - code = code.replace('class {0}(Document)'.format(self.name.replace(' ', '')), - 'class {0}(WebsiteGenerator)'.format(self.name.replace(' ', ''))) + is_website_generator_class = all( + website_generator_cls_tag in code, + website_generator_import_tag in code + ) - with open(controller_path, 'w') as f: - f.write(code) + if self.has_web_view and not is_website_generator_class: + updated_code = updated_code.replace( + document_import_tag, website_generator_import_tag + ).replace( + document_cls_tag, website_generator_cls_tag + ) + elif not self.has_web_view and is_website_generator_class: + updated_code = updated_code.replace( + website_generator_import_tag, document_import_tag + ).replace( + website_generator_cls_tag, document_cls_tag + ) + + if updated_code != code: + with open(controller_path, "w") as f: + f.write(updated_code) def run_module_method(self, method): from frappe.modules import load_doctype_module From 46a8e20ac20d3187a32bf04bab9152581265ebc7 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 25 Oct 2021 14:45:03 +0530 Subject: [PATCH 11/23] fix: Don't export migration_hash column in std json Fixes https://github.com/frappe/frappe/issues/14563 --- frappe/modules/export_file.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frappe/modules/export_file.py b/frappe/modules/export_file.py index 17e84ee488..ab6ffd4985 100644 --- a/frappe/modules/export_file.py +++ b/frappe/modules/export_file.py @@ -24,7 +24,7 @@ def write_document_file(doc, record_module=None, create_init=True, folder_name=N doc_export = doc.as_dict(no_nulls=True) doc.run_method("before_export", doc_export) - strip_default_fields(doc, doc_export) + doc_export = strip_default_fields(doc, doc_export) module = record_module or get_module_name(doc) # create folder @@ -42,12 +42,17 @@ def write_document_file(doc, record_module=None, create_init=True, folder_name=N def strip_default_fields(doc, doc_export): # strip out default fields from children + if doc.doctype == "DocType" and doc.migration_hash: + del doc_export["migration_hash"] + for df in doc.meta.get_table_fields(): for d in doc_export.get(df.fieldname): for fieldname in frappe.model.default_fields: if fieldname in d: del d[fieldname] + return doc_export + def write_code_files(folder, fname, doc, doc_export): '''Export code files and strip from values''' if hasattr(doc, 'get_code_fields'): @@ -59,8 +64,6 @@ def write_code_files(folder, fname, doc, doc_export): # remove from exporting del doc_export[key] - - def get_module_name(doc): if doc.doctype == 'Module Def': module = doc.name From 78ba785cbd3270ddd9d77e9b76b82327d72294a8 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Mon, 25 Oct 2021 15:59:24 +0530 Subject: [PATCH 12/23] fix: Use all properly ugh --- frappe/core/doctype/doctype/doctype.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.py b/frappe/core/doctype/doctype/doctype.py index a35cb5317f..738fb73a34 100644 --- a/frappe/core/doctype/doctype/doctype.py +++ b/frappe/core/doctype/doctype/doctype.py @@ -370,10 +370,10 @@ class DocType(Document): code = f.read() updated_code = code - is_website_generator_class = all( + is_website_generator_class = all([ website_generator_cls_tag in code, website_generator_import_tag in code - ) + ]) if self.has_web_view and not is_website_generator_class: updated_code = updated_code.replace( From 1bf4359f9c90cb3350dd470acc42c8a4a1b1fa0e Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 25 Oct 2021 16:53:51 +0530 Subject: [PATCH 13/23] fix: only import xmlrunner if coverage is required --- frappe/test_runner.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/frappe/test_runner.py b/frappe/test_runner.py index ecacaa1a89..b6a8145cfb 100644 --- a/frappe/test_runner.py +++ b/frappe/test_runner.py @@ -3,7 +3,6 @@ import frappe import unittest, json, sys, os import time -import xmlrunner import importlib from frappe.modules import load_doctype_module, get_module_name import frappe.utils.scheduler @@ -17,6 +16,13 @@ SLOW_TEST_THRESHOLD = 2 def xmlrunner_wrapper(output): """Convenience wrapper to keep method signature unchanged for XMLTestRunner and TextTestRunner""" + try: + import xmlrunner + except ImportError: + print("Development dependencies are required to execute this command. To install run:") + print("$ bench setup requirements --dev") + raise + def _runner(*args, **kwargs): kwargs['output'] = output return xmlrunner.XMLTestRunner(*args, **kwargs) From 248f97f99be4713449cc5fd8d2304074acaf52fd Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 25 Oct 2021 18:41:56 +0530 Subject: [PATCH 14/23] fix: sider issues --- frappe/public/js/frappe/request.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/frappe/public/js/frappe/request.js b/frappe/public/js/frappe/request.js index fc2367b37c..adc3bb5626 100644 --- a/frappe/public/js/frappe/request.js +++ b/frappe/public/js/frappe/request.js @@ -207,23 +207,23 @@ frappe.request.call = function(opts) { }; var exception_handlers = { - 'QueryTimeoutError': function(data) { + 'QueryTimeoutError': function() { frappe.utils.play_sound("error"); frappe.msgprint({ - title:__('Request Timeout'), - indicator:'red', - message:__("Server was too busy to process this request. Please try again.") + title: __('Request Timeout'), + indicator: 'red', + message: __("Server was too busy to process this request. Please try again.") }); }, - 'QueryDeadlockError': function(data) { + 'QueryDeadlockError': function() { frappe.utils.play_sound("error"); frappe.msgprint({ - title:__('Deadlock Occurred'), - indicator:'red', - message:__("Server was too busy to process this request. Please try again.") + title: __('Deadlock Occurred'), + indicator: 'red', + message: __("Server was too busy to process this request. Please try again.") }); } - } + }; var ajax_args = { url: opts.url || frappe.request.url, From 4670207dbd7dcdefa0ecf13cd2f48b2ed0d5f981 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 25 Oct 2021 18:46:54 +0530 Subject: [PATCH 15/23] refactor: extend Exception instead of ValidationError --- frappe/exceptions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/exceptions.py b/frappe/exceptions.py index 6a2494c20c..8449425bc1 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -101,8 +101,8 @@ class DataTooLongException(ValidationError): pass class FileAlreadyAttachedException(Exception): pass class DocumentAlreadyRestored(ValidationError): pass class AttachmentLimitReached(ValidationError): pass -class QueryTimeoutError(ValidationError): pass -class QueryDeadlockError(ValidationError): pass +class QueryTimeoutError(Exception): pass +class QueryDeadlockError(Exception): pass # OAuth exceptions class InvalidAuthorizationHeader(CSRFTokenError): pass class InvalidAuthorizationPrefix(CSRFTokenError): pass From b8470ba7f4806b23d318fa479980b02b8e450b63 Mon Sep 17 00:00:00 2001 From: Lev Date: Mon, 25 Oct 2021 20:33:22 +0300 Subject: [PATCH 16/23] fix(grid): Make user-wise grid customization translatable --- frappe/public/js/frappe/form/grid_row.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/form/grid_row.js b/frappe/public/js/frappe/form/grid_row.js index de174cf37f..311a5b7a1e 100644 --- a/frappe/public/js/frappe/form/grid_row.js +++ b/frappe/public/js/frappe/form/grid_row.js @@ -320,7 +320,7 @@ export default class GridRow { @@ -397,7 +397,7 @@ export default class GridRow { ${frappe.utils.icon('drag', 'xs')}
- ${docfield.label} + ${__(docfield.label)}
Date: Tue, 26 Oct 2021 07:34:23 +0200 Subject: [PATCH 17/23] fix: Website URL parsing function parses absolute telephone/phone tel: URLs as relative (#14568) * Update utils.py Fix: #14567 tel: URLs should be parsed as absolute path * fix: Parsing telephone/phone tel: URLs as absolute --- frappe/website/utils.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/website/utils.py b/frappe/website/utils.py index 869c82b917..cb8008277c 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -166,6 +166,8 @@ def abs_url(path): return if path.startswith('http://') or path.startswith('https://'): return path + if path.startswith('tel:'): + return path if path.startswith('data:'): return path if not path.startswith("/"): From e8e90d4320cd8aae6a7da2163944d0be0827cfa1 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 26 Oct 2021 11:30:26 +0530 Subject: [PATCH 18/23] fix: patch failure due to incorrect args in query --- frappe/patches/v11_0/remove_skip_for_doctype.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/patches/v11_0/remove_skip_for_doctype.py b/frappe/patches/v11_0/remove_skip_for_doctype.py index 1063dca3ff..1bbe74bb6d 100644 --- a/frappe/patches/v11_0/remove_skip_for_doctype.py +++ b/frappe/patches/v11_0/remove_skip_for_doctype.py @@ -75,7 +75,7 @@ def execute(): if new_user_permissions_list: frappe.qb.into("User Permission").columns( "name", "user", "allow", "for_value", "applicable_for", "apply_to_all_doctypes", "creation", "modified" - ).insert(tuple(new_user_permissions_list)).run() + ).insert(*new_user_permissions_list).run() if user_permissions_to_delete: frappe.db.delete( From 125233315f9d898fb4608f6fff2544cd04f3e439 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Tue, 26 Oct 2021 17:33:03 +0530 Subject: [PATCH 19/23] fix: errprint only if traceback? --- frappe/utils/response.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/utils/response.py b/frappe/utils/response.py index f7aa09d7fc..3dc6fa0c80 100644 --- a/frappe/utils/response.py +++ b/frappe/utils/response.py @@ -27,8 +27,8 @@ def report_error(status_code): if (allow_traceback and (status_code!=404 or frappe.conf.logging) and not frappe.local.flags.disable_traceback): traceback = frappe.utils.get_traceback() - frappe.errprint(traceback) if traceback: + frappe.errprint(traceback) frappe.local.response.exception = traceback.splitlines()[-1] response = build_response("json") From a76f64bbaff11e12da1047fd956e825a06b1cbf0 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Tue, 26 Oct 2021 17:51:37 +0530 Subject: [PATCH 20/23] fix: fixing query in translate.py --- frappe/translate.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frappe/translate.py b/frappe/translate.py index e5c1c9ef10..1926def907 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -339,9 +339,11 @@ def get_messages_for_app(app, deduplicate=True): # doctypes if modules: + if isinstance(modules, str): + modules = [modules] filtered_doctypes = frappe.qb.from_("DocType").where( Field("module").isin(modules) - ).select("name").run() + ).select("name").run(pluck=True) for name in filtered_doctypes: messages.extend(get_messages_from_doctype(name)) From 56dfae64f3407d0297886e1d1b17c351b8513d79 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Tue, 26 Oct 2021 18:32:50 +0530 Subject: [PATCH 21/23] fix: converted dependent queries --- frappe/translate.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index 1926def907..c7abc56663 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -20,7 +20,7 @@ from typing import List, Union, Tuple import frappe from frappe.model.utils import InvalidIncludePath, render_include from frappe.utils import get_bench_path, is_html, strip, strip_html_tags -from frappe.query_builder import Field +from frappe.query_builder import Field, DocType from pypika.terms import PseudoColumn @@ -334,8 +334,7 @@ def clear_cache(): def get_messages_for_app(app, deduplicate=True): """Returns all messages (list) for a specified `app`""" messages = [] - modules = ", ".join('"{}"'.format(m.title().replace("_", " ")) \ - for m in frappe.local.app_modules[app]) + modules = [frappe.unscrub(m) for m in frappe.local.app_modules[app]] # doctypes if modules: @@ -357,9 +356,11 @@ def get_messages_for_app(app, deduplicate=True): # reports - for name in frappe.db.sql_list("""select tabReport.name from tabDocType, tabReport - where tabReport.ref_doctype = tabDocType.name - and tabDocType.module in ({})""".format(modules)): + report = DocType("Report") + doctype = DocType("DocType") + for name in frappe.qb.from_(doctype).from_(report).where( + (report.ref_doctype == doctype.name) & doctype.module.isin(modules)).select( + report.name).run(pluck=True): messages.append((None, name)) messages.extend(get_messages_from_report(name)) for i in messages: From 92ad93c9d75ad3321ea10908419fdfe2d68b4ca1 Mon Sep 17 00:00:00 2001 From: Aradhya-Tripathi Date: Tue, 26 Oct 2021 18:41:27 +0530 Subject: [PATCH 22/23] fix: sider issues --- frappe/translate.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/frappe/translate.py b/frappe/translate.py index c7abc56663..03720f115d 100644 --- a/frappe/translate.py +++ b/frappe/translate.py @@ -358,9 +358,12 @@ def get_messages_for_app(app, deduplicate=True): # reports report = DocType("Report") doctype = DocType("DocType") - for name in frappe.qb.from_(doctype).from_(report).where( - (report.ref_doctype == doctype.name) & doctype.module.isin(modules)).select( - report.name).run(pluck=True): + names = ( + frappe.qb.from_(doctype) + .from_(report) + .where((report.ref_doctype == doctype.name) & doctype.module.isin(modules)) + .select(report.name).run(pluck=True)) + for name in names: messages.append((None, name)) messages.extend(get_messages_from_report(name)) for i in messages: From 50eb39eb5eb7552ceb3884c600e10801d07b1337 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 26 Oct 2021 22:17:00 +0530 Subject: [PATCH 23/23] fix(dx): error message for missing system deps --- frappe/utils/weasyprint.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/frappe/utils/weasyprint.py b/frappe/utils/weasyprint.py index 006bab2dd0..44770f7412 100644 --- a/frappe/utils/weasyprint.py +++ b/frappe/utils/weasyprint.py @@ -1,8 +1,20 @@ # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # MIT License. See LICENSE +import click + import frappe -from weasyprint import HTML, CSS + +try: + from weasyprint import HTML, CSS +except OSError: + click.secho( + "\n".join(["WeasyPrint depdends on additional system dependencies.", + "Follow instructions specific to your operating system:", + "https://doc.courtbouillon.org/weasyprint/stable/first_steps.html"]), + fg="yellow" + ) + raise @frappe.whitelist()