Merge pull request #21246 from phot0n/minor-fixes-email

fix: minor fixes email
This commit is contained in:
Ritwik Puri 2023-06-08 16:30:36 +05:30 committed by GitHub
commit 76c3cd3f9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 57 additions and 110 deletions

View file

@ -508,7 +508,7 @@
},
{
"default": "0",
"depends_on": "eval:!doc.domain && doc.enable_outgoing",
"depends_on": "eval:!doc.domain && doc.enable_outgoing && doc.enable_incoming && doc.use_imap",
"fieldname": "append_emails_to_sent_folder",
"fieldtype": "Check",
"hide_days": 1,
@ -616,7 +616,7 @@
"icon": "fa fa-inbox",
"index_web_pages_for_search": 1,
"links": [],
"modified": "2022-12-28 14:56:18.754804",
"modified": "2023-06-05 15:03:08.538819",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Account",
@ -639,4 +639,4 @@
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View file

@ -386,7 +386,7 @@ class EmailAccount(Document):
"from_site_config": {"default": True},
"no_smtp_authentication": {
"conf_names": ("disable_mail_smtp_authentication",),
"default": 0,
"default": 0,
},
}
@ -652,21 +652,16 @@ class EmailAccount(Document):
frappe.throw(_("Automatic Linking can be activated only for one Email Account."))
def append_email_to_sent_folder(self, message):
email_server = None
try:
email_server = self.get_incoming_server(in_receive=True)
except Exception:
self.log_error("Email Connection Error")
if not email_server:
if not (self.enable_incoming and self.use_imap):
# don't try appending if enable incoming and imap is not set
return
if email_server.imap:
try:
message = safe_encode(message)
email_server.imap.append("Sent", "\\Seen", imaplib.Time2Internaldate(time.time()), message)
except Exception:
self.log_error("Unable to add to Sent folder")
try:
email_server = self.get_incoming_server(in_receive=True)
message = safe_encode(message)
email_server.imap.append("Sent", "\\Seen", imaplib.Time2Internaldate(time.time()), message)
except Exception:
self.log_error("Unable to add to Sent folder")
def get_oauth_token(self):
if self.auth_method == "OAuth":

View file

@ -107,6 +107,7 @@
},
{
"default": "0",
"depends_on": "eval:doc.use_imap",
"fieldname": "append_emails_to_sent_folder",
"fieldtype": "Check",
"label": "Append Emails to Sent Folder"
@ -133,7 +134,7 @@
"link_fieldname": "domain"
}
],
"modified": "2022-08-19 12:55:06.434541",
"modified": "2023-06-05 12:55:06.434541",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Domain",

View file

@ -3,7 +3,7 @@
frappe.ui.form.on("Email Queue", {
refresh: function (frm) {
if (["Not Sent", "Partially Sent"].indexOf(frm.doc.status) != -1) {
if (["Not Sent", "Partially Sent"].includes(frm.doc.status)) {
let button = frm.add_custom_button("Send Now", function () {
frappe.call({
method: "frappe.email.doctype.email_queue.email_queue.send_now",
@ -16,9 +16,7 @@ frappe.ui.form.on("Email Queue", {
},
});
});
}
if (["Error", "Partially Errored"].indexOf(frm.doc.status) != -1) {
} else if (frm.doc.status == "Error") {
let button = frm.add_custom_button("Retry Sending", function () {
frm.call({
method: "retry_sending",
@ -26,10 +24,8 @@ frappe.ui.form.on("Email Queue", {
name: frm.doc.name,
},
btn: button,
callback: function (r) {
if (!r.exc) {
frm.set_value("status", "Not Sent");
}
callback: function () {
frm.reload_doc();
},
});
});

View file

@ -55,12 +55,14 @@
"default": "Not Sent",
"fieldname": "status",
"fieldtype": "Select",
"hidden": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
"options": "\nNot Sent\nSending\nSent\nError\nExpired"
"options": "Not Sent\nSending\nSent\nPartially Sent\nError\nExpired"
},
{
"depends_on": "eval:doc.error",
"fieldname": "error",
"fieldtype": "Code",
"label": "Error"
@ -152,7 +154,7 @@
"idx": 1,
"in_create": 1,
"links": [],
"modified": "2023-03-16 12:15:17.850292",
"modified": "2023-06-08 15:31:52.789186",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Queue",

View file

@ -123,31 +123,33 @@ class EmailQueue(Document):
return True
def send(self, is_background_task: bool = False, smtp_server_instance: SMTPServer = None):
def send(self, smtp_server_instance: SMTPServer = None):
"""Send emails to recipients."""
if not self.can_send_now():
return
with SendMailContext(self, is_background_task, smtp_server_instance) as ctx:
with SendMailContext(self, smtp_server_instance) as ctx:
message = None
for recipient in self.recipients:
if not recipient.is_mail_to_be_sent():
if recipient.is_mail_sent():
continue
message = ctx.build_message(recipient.recipient)
method = get_hook_method("override_email_send")
if method:
if method := get_hook_method("override_email_send"):
method(self, self.sender, recipient.recipient, message)
else:
if not frappe.flags.in_test:
ctx.smtp_session.sendmail(from_addr=self.sender, to_addrs=recipient.recipient, msg=message)
ctx.add_to_sent_list(recipient)
ctx.smtp_server.session.sendmail(
from_addr=self.sender, to_addrs=recipient.recipient, msg=message
)
ctx.update_recipient_status_to_sent(recipient)
if frappe.flags.in_test:
frappe.flags.sent_mail = message
return
if ctx.email_account_doc.append_emails_to_sent_folder and ctx.sent_to:
if ctx.email_account_doc.append_emails_to_sent_folder:
ctx.email_account_doc.append_email_to_sent_folder(message)
@staticmethod
@ -177,24 +179,22 @@ class EmailQueue(Document):
@task(queue="short")
def send_mail(email_queue_name, is_background_task=False, smtp_server_instance: SMTPServer = None):
def send_mail(email_queue_name, smtp_server_instance: SMTPServer = None):
"""This is equivalent to EmailQueue.send.
This provides a way to make sending mail as a background job.
"""
record = EmailQueue.find(email_queue_name)
record.send(is_background_task=is_background_task, smtp_server_instance=smtp_server_instance)
record.send(smtp_server_instance=smtp_server_instance)
class SendMailContext:
def __init__(
self,
queue_doc: Document,
is_background_task: bool = False,
smtp_server_instance: SMTPServer = None,
):
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 = smtp_server_instance or self.email_account_doc.get_smtp_server()
@ -203,7 +203,9 @@ class SendMailContext:
# Note: smtp session will have to be manually closed
self.retain_smtp_session = bool(smtp_server_instance)
self.sent_to = [rec.recipient for rec in self.queue_doc.recipients if rec.is_mail_sent()]
self.sent_to_atleast_one_recipient = any(
rec.recipient for rec in self.queue_doc.recipients if rec.is_mail_sent()
)
def __enter__(self):
self.queue_doc.update_status(status="Sending", commit=True)
@ -217,53 +219,35 @@ class SendMailContext:
smtplib.SMTPHeloError,
JobTimeoutException,
]
trace = "".join(traceback.format_tb(exc_tb)) if exc_tb else None
if not self.retain_smtp_session:
self.smtp_server.quit()
self.log_exception(exc_type, exc_val, exc_tb)
if exc_type in exceptions:
email_status = "Partially Sent" if self.sent_to else "Not Sent"
self.queue_doc.update_status(status=email_status, commit=True)
elif exc_type:
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"}
self.queue_doc.update_status(**update_fields, commit=True)
else:
email_status = self.is_mail_sent_to_all() and "Sent"
email_status = email_status or (self.sent_to and "Partially Sent") or "Not Sent"
update_fields = {
"status": email_status,
"email_account": self.email_account_doc.name
if self.email_account_doc.is_exists_in_db()
else None,
"status": "Partially Sent" if self.sent_to_atleast_one_recipient else "Not Sent",
"error": trace,
}
self.queue_doc.update_status(**update_fields, commit=True)
elif exc_type:
update_fields = {"error": trace}
if self.queue_doc.retry < get_email_retry_limit():
update_fields.update(
{
"status": "Partially Sent" if self.sent_to_atleast_one_recipient else "Not Sent",
"retry": self.queue_doc.retry + 1,
}
)
else:
update_fields.update({"status": "Error"})
else:
update_fields = {"status": "Sent"}
def log_exception(self, exc_type, exc_val, exc_tb):
if exc_type:
traceback_string = "".join(traceback.format_tb(exc_tb))
traceback_string += f"\n Queue Name: {self.queue_doc.name}"
self.queue_doc.update_status(**update_fields, commit=True)
self.queue_doc.log_error("Email sending failed", traceback_string)
@property
def smtp_session(self):
if frappe.flags.in_test:
return
return self.smtp_server.session
def add_to_sent_list(self, recipient):
# Update recipient status
def update_recipient_status_to_sent(self, recipient):
self.sent_to_atleast_one_recipient = True
recipient.update_db(status="Sent", commit=True)
self.sent_to.append(recipient.recipient)
def is_mail_sent_to_all(self):
return sorted(self.sent_to) == sorted(rec.recipient for rec in self.queue_doc.recipients)
def get_message_object(self, message):
return Parser(policy=SMTPUTF8).parsestr(message)
@ -379,7 +363,7 @@ def retry_sending(name):
doc = frappe.get_doc("Email Queue", name)
doc.check_permission()
if doc and (doc.status == "Error" or doc.status == "Partially Errored"):
if doc and doc.status == "Error":
doc.status = "Not Sent"
for d in doc.recipients:
if d.status != "Sent":

View file

@ -154,7 +154,6 @@ def flush(from_test=False):
frappe.enqueue(
method=send_mail,
email_queue_name=row.name,
is_background_task=not from_test,
now=from_test,
job_name=job_name,
queue="short",

View file

@ -13,36 +13,6 @@ class InvalidEmailCredentials(frappe.ValidationError):
pass
def send(email, append_to=None, retry=1):
"""Deprecated: Send the message or add it to Outbox Email"""
def _send(retry):
from frappe.email.doctype.email_account.email_account import EmailAccount
try:
email_account = EmailAccount.find_outgoing(match_by_doctype=append_to)
smtpserver = email_account.get_smtp_server()
# validate is called in as_string
email_body = email.as_string()
smtpserver.sess.sendmail(email.sender, email.recipients + (email.cc or []), email_body)
except smtplib.SMTPSenderRefused:
frappe.throw(_("Invalid login or password"), title="Email Failed")
raise
except smtplib.SMTPRecipientsRefused:
frappe.msgprint(_("Invalid recipient address"), title="Email Failed")
raise
except (smtplib.SMTPServerDisconnected, smtplib.SMTPAuthenticationError):
if not retry:
raise
else:
retry = retry - 1
_send(retry)
_send(retry)
class SMTPServer:
def __init__(
self,