diff --git a/frappe/__init__.py b/frappe/__init__.py index 86e8e6063f..c19e7a22cf 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -79,7 +79,7 @@ class _dict(dict): return _dict(self) -def _(msg, lang=None, context=None): +def _(msg, lang=None, context=None) -> str: """Returns translated string in current lang, if exists. Usage: _('Change') diff --git a/frappe/core/doctype/system_settings/system_settings.json b/frappe/core/doctype/system_settings/system_settings.json index c954e41202..a444062b5a 100644 --- a/frappe/core/doctype/system_settings/system_settings.json +++ b/frappe/core/doctype/system_settings/system_settings.json @@ -63,6 +63,7 @@ "otp_issuer_name", "email", "email_footer_address", + "email_retry_limit", "column_break_18", "disable_standard_email_footer", "hide_footer_in_auto_email_reports", @@ -495,8 +496,8 @@ "fieldname": "allow_older_web_view_links", "fieldtype": "Check", "label": "Allow Older Web View Links (Insecure)" - }, - { + }, + { "fieldname": "column_break_64", "fieldtype": "Column Break" }, @@ -518,12 +519,18 @@ "fieldtype": "Duration", "label": "Reset Password Link Expiry Duration", "non_negative": 1 + }, + { + "default": "3", + "fieldname": "email_retry_limit", + "fieldtype": "Int", + "label": "Email Retry Limit" } ], "icon": "fa fa-cog", "issingle": 1, "links": [], - "modified": "2022-05-19 00:00:18.095269", + "modified": "2022-06-21 13:55:04.796152", "modified_by": "Administrator", "module": "Core", "name": "System Settings", diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index c3002607b4..c51446947c 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -30,8 +30,6 @@ from frappe.utils import ( split_emails, ) -MAX_RETRY_COUNT = 3 - class EmailQueue(Document): DOCTYPE = "Email Queue" @@ -183,7 +181,7 @@ def send_mail(email_queue_name, is_background_task=False): class SendMailContext: def __init__(self, queue_doc: Document, is_background_task: bool = False): - self.queue_doc = queue_doc + self.queue_doc: EmailQueue = queue_doc self.is_background_task = is_background_task self.email_account_doc = queue_doc.get_email_account() self.smtp_server = self.email_account_doc.get_smtp_server() @@ -210,7 +208,7 @@ class SendMailContext: email_status = (self.sent_to and "Partially Sent") or "Not Sent" self.queue_doc.update_status(status=email_status, commit=True) elif exc_type: - if self.queue_doc.retry < MAX_RETRY_COUNT: + if self.queue_doc.retry < get_email_retry_limit(): update_fields = {"status": "Not Sent", "retry": self.queue_doc.retry + 1} else: update_fields = {"status": (self.sent_to and "Partially Errored") or "Error"} @@ -287,16 +285,16 @@ class SendMailContext: ).decode() return message - def get_unsubscribe_str(self, recipient_email): + def get_unsubscribe_str(self, recipient_email: str) -> str: unsubscribe_url = "" + if self.queue_doc.add_unsubscribe_link and self.queue_doc.reference_doctype: - doctype, doc_name = self.queue_doc.reference_doctype, self.queue_doc.reference_name unsubscribe_url = get_unsubcribed_url( - doctype, - doc_name, - recipient_email, - self.queue_doc.unsubscribe_method, - self.queue_doc.unsubscribe_param, + reference_doctype=self.queue_doc.reference_doctype, + reference_name=self.queue_doc.reference_name, + email=recipient_email, + unsubscribe_method=self.queue_doc.unsubscribe_method, + unsubscribe_params=self.queue_doc.unsubscribe_param, ) return quopri.encodestring(unsubscribe_url.encode()).decode() @@ -372,6 +370,10 @@ def on_doctype_update(): ) +def get_email_retry_limit(): + return cint(frappe.db.get_system_setting("email_retry_limit")) or 3 + + class QueueBuilder: """Builds Email Queue from the given data""" diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py index 50c66e1ad2..3a952e1487 100755 --- a/frappe/email/email_body.py +++ b/frappe/email/email_body.py @@ -7,6 +7,7 @@ import re from email import policy from email.header import Header from email.mime.multipart import MIMEMultipart +from typing import Optional import frappe from frappe.email.doctype.email_account.email_account import EmailAccount @@ -353,7 +354,7 @@ def get_formatted_html( print_html=None, email_account=None, header=None, - unsubscribe_link=None, + unsubscribe_link: Optional[frappe._dict] = None, sender=None, with_container=False, ): diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 1519c26841..45abe0374a 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -67,37 +67,24 @@ def get_emails_sent_today(email_account=None): return frappe.db.sql(q, q_args)[0][0] -def get_unsubscribe_message(unsubscribe_message, expose_recipients): - if unsubscribe_message: - unsubscribe_html = """{0}""".format( - unsubscribe_message - ) - else: - unsubscribe_link = """{0}""".format( - _("Unsubscribe") - ) - unsubscribe_html = _("{0} to stop receiving emails of this type").format(unsubscribe_link) - - html = """
+def get_unsubscribe_message( + unsubscribe_message: str, expose_recipients: str +) -> frappe._dict[str, str]: + unsubscribe_message = unsubscribe_message or _("Unsubscribe") + unsubscribe_link = f'{unsubscribe_message}' + unsubscribe_html = _("{0} to stop receiving emails of this type").format(unsubscribe_link) + html = f"""
- {0} + {unsubscribe_html}
-
""".format( - unsubscribe_html - ) +
""" + text = f"\n\n{unsubscribe_message}: \n" if expose_recipients == "footer": - text = "\n" - else: - text = "" - text += "\n\n{unsubscribe_message}: \n".format( - unsubscribe_message=unsubscribe_message - ) + text = f"\n{text}" - return frappe._dict({"html": html, "text": text}) + return frappe._dict(html=html, text=text) def get_unsubcribed_url( diff --git a/frappe/public/icons/timeless/icons.svg b/frappe/public/icons/timeless/icons.svg index fbd72d6fb5..af63d0c8ff 100644 --- a/frappe/public/icons/timeless/icons.svg +++ b/frappe/public/icons/timeless/icons.svg @@ -613,8 +613,8 @@ - + diff --git a/frappe/translations/de.csv b/frappe/translations/de.csv index d00548458c..b22befd321 100644 --- a/frappe/translations/de.csv +++ b/frappe/translations/de.csv @@ -1993,7 +1993,7 @@ QR Code,QR-Code, QR Code for Login Verification,QR Code für Login-Bestätigung, QZ Tray Connection Active!,QZ-Tray-Verbindung aktiv!, QZ Tray Failed: ,QZ-Fach fehlgeschlagen:, -Quarter Day,Quartalstag, +Quarter Day,Viertel-Tag, Query,Abfrage, Query Report,Abfragebericht, Query must be a SELECT,Abfrage muss ein SELECT sein, @@ -4793,3 +4793,17 @@ Reset to default,Auf Standard zurücksetzen, Column Width,Spaltenbreite, Choose Kanban Board,Kanban-Tafel auswählen, Create New Board,Neue Tafel erstellen, +Only If Creator,Nur wenn Ersteller, +Rebuild Tree,Baum neu aufbauen, +Customize Dashboard,Dashboard anpassen, +Reset Dashboard Customizations,Dashboard-Anpassungen zurücksetzen, +Add {0},{0} hinzufügen, +descending,absteigend, +ascending,aufsteigend, +Next Document,Nächstes Dokument, +Previous Document,Vorheriges Dokument, +Mark all as read,Alle als gelesen markieren, +See all Activity,Alle Aktivitäten anzeigen, +Go to Notification Settings List,Gehe zur Listenansicht Benachrichtigungseinstellungen, +Load more,Mehr laden, +Edit Full Form,Vollständiges Formular bearbeiten,