From 1b6b19b38e475ecf306ae65800ce70d580789750 Mon Sep 17 00:00:00 2001 From: vvrithof <98533401+vvrithof@users.noreply.github.com> Date: Tue, 11 Apr 2023 12:23:41 +0200 Subject: [PATCH 1/8] fix: float formatting --- frappe/public/js/frappe/form/controls/float.js | 1 + 1 file changed, 1 insertion(+) diff --git a/frappe/public/js/frappe/form/controls/float.js b/frappe/public/js/frappe/form/controls/float.js index d6b542804f..0066020a8f 100644 --- a/frappe/public/js/frappe/form/controls/float.js +++ b/frappe/public/js/frappe/form/controls/float.js @@ -6,6 +6,7 @@ frappe.ui.form.ControlFloat = class ControlFloat extends frappe.ui.form.ControlI else { let value = this.get_input_value(); this.parse_validate_and_set_in_model(value, e); + this.refresh(); } }; // convert to number format on focusout since focus converts it to flt. From af500bb3f59c865a58a46dfbff452fc2e4f6971d Mon Sep 17 00:00:00 2001 From: P-Froggy <60393001+P-Froggy@users.noreply.github.com> Date: Tue, 18 Apr 2023 07:56:18 +0200 Subject: [PATCH 2/8] fix: format filter values in report print view (#20717) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michael Weißer --- frappe/public/js/frappe/views/reports/query_report.js | 4 ++-- frappe/public/js/frappe/views/reports/report_view.js | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js index ee255032bb..9f81f9f6f1 100644 --- a/frappe/public/js/frappe/views/reports/query_report.js +++ b/frappe/public/js/frappe/views/reports/query_report.js @@ -1424,9 +1424,9 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList { const applied_filters = this.get_filter_values(); return Object.keys(applied_filters) .map((fieldname) => { - const label = frappe.query_report.get_filter(fieldname).df.label; + const docfield = frappe.query_report.get_filter(fieldname).df; const value = applied_filters[fieldname]; - return `
${__(label)}: ${value}
`; + return `
${__(docfield.label)}: ${frappe.format(value, docfield)}
`; }) .join(""); } diff --git a/frappe/public/js/frappe/views/reports/report_view.js b/frappe/public/js/frappe/views/reports/report_view.js index 83411f0ddf..5e76d61c09 100644 --- a/frappe/public/js/frappe/views/reports/report_view.js +++ b/frappe/public/js/frappe/views/reports/report_view.js @@ -1349,9 +1349,8 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView { .map((f) => { const [doctype, fieldname, condition, value] = f; if (condition !== "=") return ""; - - const label = frappe.meta.get_label(doctype, fieldname); - return `
${__(label)}: ${value}
`; + const docfield = frappe.meta.get_docfield(doctype, fieldname); + return `
${__(docfield.label)}: ${frappe.format(value, docfield)}
`; }) .join(""); } From b483deac3df8613fb155e7ca794e2cfae1d61661 Mon Sep 17 00:00:00 2001 From: phot0n Date: Tue, 18 Apr 2023 13:59:40 +0530 Subject: [PATCH 3/8] refactor(minor): email retreiveing --- frappe/email/receive.py | 158 ++++++++++------------------------------ 1 file changed, 38 insertions(+), 120 deletions(-) diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 59d41b543f..80f8d190d6 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -9,6 +9,7 @@ import json import poplib import re import time +from contextlib import suppress from email.header import decode_header import _socket @@ -51,10 +52,6 @@ class EmailTimeoutError(frappe.ValidationError): pass -class TotalSizeExceededError(frappe.ValidationError): - pass - - class LoginLimitExceeded(frappe.ValidationError): pass @@ -67,26 +64,11 @@ class EmailServer: """Wrapper for POP server to pull emails.""" def __init__(self, args=None): - self.setup(args) - - def setup(self, args=None): - # overrride self.settings = args or frappe._dict() - def check_mails(self): - # overrride - return True - - def process_message(self, mail): - # overrride - pass - def connect(self): """Connect to **Email Account**.""" - if cint(self.settings.use_imap): - return self.connect_imap() - else: - return self.connect_pop() + return self.connect_imap() if cint(self.settings.use_imap) else self.connect_pop() def connect_imap(self): """Connect to IMAP""" @@ -150,8 +132,7 @@ class EmailServer: return True except _socket.error: - # log performs rollback and logs error in Error Log - self.log_error("POP: Unable to connect") + frappe.log_error("POP: Unable to connect") # Invalid mail server -- due to refusing connection frappe.msgprint(_("Invalid Mail Server. Please rectify and try again.")) @@ -177,58 +158,25 @@ class EmailServer: return def get_messages(self, folder="INBOX"): - """Returns new email messages in a list.""" - if not (self.check_mails() or self.connect()): - return [] + """Returns new email messages.""" - frappe.db.commit() + self.latest_messages = [] + self.seen_status = {} + self.uid_reindexed = False - uid_list = [] + email_list = self.get_new_mails(folder) - try: - # track if errors arised - self.errors = False - self.latest_messages = [] - self.seen_status = {} - self.uid_reindexed = False - - uid_list = email_list = self.get_new_mails(folder) - - if not email_list: - return - - num = num_copy = len(email_list) - - # WARNING: Hard coded max no. of messages to be popped - if num > 50: - num = 50 - - # size limits - self.total_size = 0 - self.max_email_size = cint(frappe.local.conf.get("max_email_size")) - self.max_total_size = 5 * self.max_email_size - - for i, message_meta in enumerate(email_list[:num]): - try: - self.retrieve_message(message_meta, i + 1) - except (TotalSizeExceededError, EmailTimeoutError, LoginLimitExceeded): - break - # WARNING: Mark as read - message number 101 onwards from the pop list - # This is to avoid having too many messages entering the system - num = num_copy - if not cint(self.settings.use_imap): - if num > 100 and not self.errors: - for m in range(101, num + 1): - self.pop.dele(m) - - except Exception as e: - if not self.has_login_limit_exceeded(e): - raise + for i, message_meta in enumerate(email_list[:100]): + try: + self.retrieve_message(message_meta, i + 1) + except (EmailTimeoutError, LoginLimitExceeded): + # get whatever messages were retrieved + break out = {"latest_messages": self.latest_messages} if self.settings.use_imap: out.update( - {"uid_list": uid_list, "seen_status": self.seen_status, "uid_reindexed": self.uid_reindexed} + {"uid_list": email_list, "seen_status": self.seen_status, "uid_reindexed": self.uid_reindexed} ) return out @@ -248,7 +196,7 @@ class EmailServer: else: email_list = self.pop.list()[1] - return email_list + return email_list or [] def check_imap_uidvalidity(self, folder): # compare the UIDVALIDITY of email account and imap server @@ -294,9 +242,6 @@ class EmailServer: self.settings.email_sync_rule = f"UID {from_uid}:{uidnext}" self.uid_reindexed = True - elif uid_validity == current_uid_validity: - return - def parse_imap_response(self, cmd, response): pattern = rf"(?<={cmd} )[0-9]*" match = re.search(pattern, response.decode("utf-8"), re.U | re.I) @@ -307,10 +252,7 @@ class EmailServer: return None def retrieve_message(self, message_meta, msg_num=None): - incoming_mail = None try: - self.validate_message_limits(message_meta) - if cint(self.settings.use_imap): status, message = self.imap.uid("fetch", message_meta, "(BODY.PEEK[] BODY.PEEK[HEADER] FLAGS)") raw = message[0] @@ -320,35 +262,17 @@ class EmailServer: else: msg = self.pop.retr(msg_num) self.latest_messages.append(b"\n".join(msg[1])) - except (TotalSizeExceededError, EmailTimeoutError): + except EmailTimeoutError: # propagate this error to break the loop - self.errors = True raise except Exception as e: if self.has_login_limit_exceeded(e): - self.errors = True raise LoginLimitExceeded(e) - else: - # log performs rollback and logs error in Error Log - self.log_error("Unable to fetch email", self.make_error_msg(msg_num, incoming_mail)) - self.errors = True - frappe.db.rollback() + frappe.log_error("Unable to fetch email", self.make_error_msg(msg_num)) - if not cint(self.settings.use_imap): - self.pop.dele(msg_num) - else: - # mark as seen if email sync rule is UNSEEN (syncing only unseen mails) - if self.settings.email_sync_rule == "UNSEEN": - self.imap.uid("STORE", message_meta, "+FLAGS", "(\\SEEN)") - else: - if not cint(self.settings.use_imap): - self.pop.dele(msg_num) - else: - # mark as seen if email sync rule is UNSEEN (syncing only unseen mails) - if self.settings.email_sync_rule == "UNSEEN": - self.imap.uid("STORE", message_meta, "+FLAGS", "(\\SEEN)") + self.post_retrieve_cleanup(message_meta, msg_num) def get_email_seen_status(self, uid, flag_string): """parse the email FLAGS response""" @@ -368,6 +292,15 @@ class EmailServer: def has_login_limit_exceeded(self, e): return "-ERR Exceeded the login limit" in strip(cstr(e)) + def post_retrieve_cleanup(self, message_meta, msg_num=None): + with suppress(Exception): + if not cint(self.settings.use_imap): + self.pop.dele(msg_num) + else: + # mark as seen if email sync rule is UNSEEN (syncing only unseen mails) + if self.settings.email_sync_rule == "UNSEEN": + self.imap.uid("STORE", message_meta, "+FLAGS", "(\\SEEN)") + def is_temporary_system_problem(self, e): messages = ( "-ERR [SYS/TEMP] Temporary system problem. Please try again later.", @@ -378,37 +311,22 @@ class EmailServer: return True return False - def validate_message_limits(self, message_meta): - # throttle based on email size - if not self.max_email_size: - return + def make_error_msg(self, message_meta, msg_num): + incoming_mail = None + with suppress(Exception): + # retrieve headers + if not cint(self.settings.use_imap): + partial_message = self.pop.top(msg_num, 5)[1] + else: + partial_message = self.imap.uid("fetch", message_meta, "(BODY.PEEK[HEADER])")[1] - m, size = message_meta.split() - size = cint(size) - - if size < self.max_email_size: - self.total_size += size - if self.total_size > self.max_total_size: - raise TotalSizeExceededError - else: - raise EmailSizeExceededError - - def make_error_msg(self, msg_num, incoming_mail): - error_msg = "Error in retrieving email." - if not incoming_mail: - try: - # retrieve headers - incoming_mail = Email(b"\n".join(self.pop.top(msg_num, 5)[1])) - except Exception: - pass + incoming_mail = Email(b"\n".join(partial_message)) if incoming_mail: - error_msg += "\nDate: {date}\nFrom: {from_email}\nSubject: {subject}\n".format( + return "\nDate: {date}\nFrom: {from_email}\nSubject: {subject}\n".format( date=incoming_mail.date, from_email=incoming_mail.from_email, subject=incoming_mail.subject ) - return error_msg - def update_flag(self, folder, uid_list=None): """set all uids mails the flag as seen""" if not uid_list: From f6606781fb01630aace94284d3122401ffd9aba4 Mon Sep 17 00:00:00 2001 From: phot0n Date: Tue, 18 Apr 2023 14:31:19 +0530 Subject: [PATCH 4/8] chore: take out email_list to function scope --- frappe/email/receive.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 1afdb9aa7a..d2b95db4ce 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -183,8 +183,8 @@ class EmailServer: def get_new_mails(self, folder): """Return list of new mails""" + email_list = [] if cint(self.settings.use_imap): - email_list = [] self.check_imap_uidvalidity(folder) readonly = False if self.settings.email_sync_rule == "UNSEEN" else True @@ -196,7 +196,7 @@ class EmailServer: else: email_list = self.pop.list()[1] - return email_list or [] + return email_list def check_imap_uidvalidity(self, folder): # compare the UIDVALIDITY of email account and imap server From 2a5ed2877730ce401b0ad85cdc84b32e57fdba24 Mon Sep 17 00:00:00 2001 From: phot0n Date: Tue, 18 Apr 2023 14:49:09 +0530 Subject: [PATCH 5/8] fix: make_error_msg for imap --- frappe/email/receive.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/frappe/email/receive.py b/frappe/email/receive.py index d2b95db4ce..e6b7fdda04 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -313,19 +313,26 @@ class EmailServer: def make_error_msg(self, message_meta, msg_num): incoming_mail = None + traceback = frappe.get_traceback(with_context=True) with suppress(Exception): # retrieve headers if not cint(self.settings.use_imap): - partial_message = self.pop.top(msg_num, 5)[1] + partial_message = b"\n".join(self.pop.top(msg_num, 5)[1]) else: - partial_message = self.imap.uid("fetch", message_meta, "(BODY.PEEK[HEADER])")[1] + partial_message = self.imap.uid("fetch", message_meta, "(BODY.PEEK[HEADER])")[1][0][1] - incoming_mail = Email(b"\n".join(partial_message)) + incoming_mail = Email(partial_message) if incoming_mail: - return "\nDate: {date}\nFrom: {from_email}\nSubject: {subject}\n".format( - date=incoming_mail.date, from_email=incoming_mail.from_email, subject=incoming_mail.subject + return ( + "\nDate: {date}\nFrom: {from_email}\nSubject: {subject}\n\n\nTraceback: \n{traceback}".format( + date=incoming_mail.date, + from_email=incoming_mail.from_email, + subject=incoming_mail.subject, + traceback=traceback, + ) ) + return traceback def update_flag(self, folder, uid_list=None): """set all uids mails the flag as seen""" From 97b2adfaf2c69e95f862ea2e2c6e516a089d6aa9 Mon Sep 17 00:00:00 2001 From: phot0n Date: Tue, 18 Apr 2023 19:22:42 +0530 Subject: [PATCH 6/8] chore: rename message_meta -> uid --- frappe/email/receive.py | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/frappe/email/receive.py b/frappe/email/receive.py index e6b7fdda04..254910c3ca 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -166,9 +166,9 @@ class EmailServer: email_list = self.get_new_mails(folder) - for i, message_meta in enumerate(email_list[:100]): + for i, uid in enumerate(email_list[:100]): try: - self.retrieve_message(message_meta, i + 1) + self.retrieve_message(uid, i + 1) except (EmailTimeoutError, LoginLimitExceeded): # get whatever messages were retrieved break @@ -251,13 +251,13 @@ class EmailServer: else: return None - def retrieve_message(self, message_meta, msg_num=None): + def retrieve_message(self, uid, msg_num): try: if cint(self.settings.use_imap): - status, message = self.imap.uid("fetch", message_meta, "(BODY.PEEK[] BODY.PEEK[HEADER] FLAGS)") + status, message = self.imap.uid("fetch", uid, "(BODY.PEEK[] BODY.PEEK[HEADER] FLAGS)") raw = message[0] - self.get_email_seen_status(message_meta, raw[0]) + self.get_email_seen_status(uid, raw[0]) self.latest_messages.append(raw[1]) else: msg = self.pop.retr(msg_num) @@ -270,9 +270,9 @@ class EmailServer: if self.has_login_limit_exceeded(e): raise LoginLimitExceeded(e) - frappe.log_error("Unable to fetch email", self.make_error_msg(msg_num)) + frappe.log_error("Unable to fetch email", self.make_error_msg(uid, msg_num)) - self.post_retrieve_cleanup(message_meta, msg_num) + self._post_retrieve_cleanup(uid, msg_num) def get_email_seen_status(self, uid, flag_string): """parse the email FLAGS response""" @@ -292,14 +292,14 @@ class EmailServer: def has_login_limit_exceeded(self, e): return "-ERR Exceeded the login limit" in strip(cstr(e)) - def post_retrieve_cleanup(self, message_meta, msg_num=None): + def _post_retrieve_cleanup(self, uid, msg_num): with suppress(Exception): if not cint(self.settings.use_imap): self.pop.dele(msg_num) else: # mark as seen if email sync rule is UNSEEN (syncing only unseen mails) if self.settings.email_sync_rule == "UNSEEN": - self.imap.uid("STORE", message_meta, "+FLAGS", "(\\SEEN)") + self.imap.uid("STORE", uid, "+FLAGS", "(\\SEEN)") def is_temporary_system_problem(self, e): messages = ( @@ -311,24 +311,24 @@ class EmailServer: return True return False - def make_error_msg(self, message_meta, msg_num): - incoming_mail = None + def make_error_msg(self, uid, msg_num): + partial_mail = None traceback = frappe.get_traceback(with_context=True) with suppress(Exception): # retrieve headers if not cint(self.settings.use_imap): - partial_message = b"\n".join(self.pop.top(msg_num, 5)[1]) + headers = b"\n".join(self.pop.top(msg_num, 5)[1]) else: - partial_message = self.imap.uid("fetch", message_meta, "(BODY.PEEK[HEADER])")[1][0][1] + headers = self.imap.uid("fetch", uid, "(BODY.PEEK[HEADER])")[1][0][1] - incoming_mail = Email(partial_message) + partial_mail = Email(headers) - if incoming_mail: + if partial_mail: return ( "\nDate: {date}\nFrom: {from_email}\nSubject: {subject}\n\n\nTraceback: \n{traceback}".format( - date=incoming_mail.date, - from_email=incoming_mail.from_email, - subject=incoming_mail.subject, + date=partial_mail.date, + from_email=partial_mail.from_email, + subject=partial_mail.subject, traceback=traceback, ) ) From cbbbb189ef384883b06e947fe75f6546ea70e757 Mon Sep 17 00:00:00 2001 From: Ernesto Ruiz Date: Tue, 18 Apr 2023 12:40:12 -0600 Subject: [PATCH 7/8] fix: translate file uploader buttons (#20763) --- frappe/public/js/frappe/file_uploader/FilePreview.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/public/js/frappe/file_uploader/FilePreview.vue b/frappe/public/js/frappe/file_uploader/FilePreview.vue index 6cb78142d8..76f7c43b01 100644 --- a/frappe/public/js/frappe/file_uploader/FilePreview.vue +++ b/frappe/public/js/frappe/file_uploader/FilePreview.vue @@ -24,8 +24,8 @@
- - + +
From e5b26b77db6e3f31bcac6f70184667932b761e1a Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Wed, 19 Apr 2023 12:14:18 +0530 Subject: [PATCH 8/8] fix(minor): typo in fixture syncing message (#20768) --- frappe/modules/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py index 8488328da4..57d3e8f7ad 100644 --- a/frappe/modules/utils.py +++ b/frappe/modules/utils.py @@ -160,7 +160,7 @@ def sync_customizations_for_doctype(data: dict, folder: str, filename: str = "") if not frappe.db.exists("DocType", doctype): print(_("DocType {0} does not exist.").format(doctype)) - print(_("Skipping fixture syncing for doctyoe {0} from file {1} ").format(doctype, filename)) + print(_("Skipping fixture syncing for doctype {0} from file {1}").format(doctype, filename)) return if data["custom_fields"]: