This was going to be a fix at first. Until I realised it couldn't...so I removed the changes and kept the things back that helped me debug the issueversion-14
@@ -1,29 +1,32 @@ | |||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# License: MIT. See LICENSE | # License: MIT. See LICENSE | ||||
import frappe | |||||
import json | import json | ||||
from email.utils import formataddr | |||||
from frappe.core.utils import get_parent_doc | |||||
from frappe.utils import (get_url, get_formatted_email, cint, list_to_str, | |||||
validate_email_address, split_emails, parse_addr, get_datetime) | |||||
from frappe.email.email_body import get_message_id | |||||
from typing import TYPE_CHECKING, Dict | |||||
import frappe | |||||
import frappe.email.smtp | import frappe.email.smtp | ||||
import time | |||||
from frappe import _ | from frappe import _ | ||||
from frappe.utils.background_jobs import enqueue | |||||
from frappe.email.email_body import get_message_id | |||||
from frappe.utils import (cint, get_datetime, get_formatted_email, | |||||
list_to_str, split_emails, validate_email_address) | |||||
if TYPE_CHECKING: | |||||
from frappe.core.doctype.communication.communication import Communication | |||||
OUTGOING_EMAIL_ACCOUNT_MISSING = _(""" | OUTGOING_EMAIL_ACCOUNT_MISSING = _(""" | ||||
Unable to send mail because of a missing email account. | Unable to send mail because of a missing email account. | ||||
Please setup default Email Account from Setup > Email > Email Account | Please setup default Email Account from Setup > Email > Email Account | ||||
""") | """) | ||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent", | def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent", | ||||
sender=None, sender_full_name=None, recipients=None, communication_medium="Email", send_email=False, | sender=None, sender_full_name=None, recipients=None, communication_medium="Email", send_email=False, | ||||
print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, bcc=None, | print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, bcc=None, | ||||
flags=None, read_receipt=None, print_letterhead=True, email_template=None, communication_type=None, | flags=None, read_receipt=None, print_letterhead=True, email_template=None, communication_type=None, | ||||
ignore_permissions=False): | |||||
ignore_permissions=False) -> Dict[str, str]: | |||||
"""Make a new communication. | """Make a new communication. | ||||
:param doctype: Reference DocType. | :param doctype: Reference DocType. | ||||
@@ -56,7 +59,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = | |||||
cc = list_to_str(cc) if isinstance(cc, list) else cc | cc = list_to_str(cc) if isinstance(cc, list) else cc | ||||
bcc = list_to_str(bcc) if isinstance(bcc, list) else bcc | bcc = list_to_str(bcc) if isinstance(bcc, list) else bcc | ||||
comm = frappe.get_doc({ | |||||
comm: "Communication" = frappe.get_doc({ | |||||
"doctype":"Communication", | "doctype":"Communication", | ||||
"subject": subject, | "subject": subject, | ||||
"content": content, | "content": content, | ||||
@@ -93,12 +96,13 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = | |||||
send_me_a_copy=send_me_a_copy, print_letterhead=print_letterhead) | send_me_a_copy=send_me_a_copy, print_letterhead=print_letterhead) | ||||
emails_not_sent_to = comm.exclude_emails_list(include_sender=send_me_a_copy) | emails_not_sent_to = comm.exclude_emails_list(include_sender=send_me_a_copy) | ||||
return { | return { | ||||
"name": comm.name, | "name": comm.name, | ||||
"emails_not_sent_to": ", ".join(emails_not_sent_to or []) | |||||
"emails_not_sent_to": ", ".join(emails_not_sent_to) | |||||
} | } | ||||
def validate_email(doc): | |||||
def validate_email(doc: "Communication") -> None: | |||||
"""Validate Email Addresses of Recipients and CC""" | """Validate Email Addresses of Recipients and CC""" | ||||
if not (doc.communication_type=="Communication" and doc.communication_medium == "Email") or doc.flags.in_receive: | if not (doc.communication_type=="Communication" and doc.communication_medium == "Email") or doc.flags.in_receive: | ||||
return | return | ||||
@@ -114,8 +118,6 @@ def validate_email(doc): | |||||
for email in split_emails(doc.bcc): | for email in split_emails(doc.bcc): | ||||
validate_email_address(email, throw=True) | validate_email_address(email, throw=True) | ||||
# validate sender | |||||
def set_incoming_outgoing_accounts(doc): | def set_incoming_outgoing_accounts(doc): | ||||
from frappe.email.doctype.email_account.email_account import EmailAccount | from frappe.email.doctype.email_account.email_account import EmailAccount | ||||
incoming_email_account = EmailAccount.find_incoming( | incoming_email_account = EmailAccount.find_incoming( | ||||
@@ -1,3 +1,4 @@ | |||||
from typing import List | |||||
import frappe | import frappe | ||||
from frappe import _ | from frappe import _ | ||||
from frappe.core.utils import get_parent_doc | from frappe.core.utils import get_parent_doc | ||||
@@ -194,14 +195,18 @@ class CommunicationEmailMixin: | |||||
return _("Leave this conversation") | return _("Leave this conversation") | ||||
return '' | return '' | ||||
def exclude_emails_list(self, is_inbound_mail_communcation=False, include_sender=False): | |||||
def exclude_emails_list(self, is_inbound_mail_communcation=False, include_sender=False) -> List: | |||||
"""List of mail id's excluded while sending mail. | """List of mail id's excluded while sending mail. | ||||
""" | """ | ||||
all_ids = self.get_all_email_addresses(exclude_displayname=True) | all_ids = self.get_all_email_addresses(exclude_displayname=True) | ||||
final_ids = self.mail_recipients(is_inbound_mail_communcation = is_inbound_mail_communcation) + \ | |||||
self.mail_bcc(is_inbound_mail_communcation = is_inbound_mail_communcation) + \ | |||||
self.mail_cc(is_inbound_mail_communcation = is_inbound_mail_communcation, include_sender=include_sender) | |||||
return set(all_ids) - set(final_ids) | |||||
final_ids = ( | |||||
self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation) | |||||
+ self.mail_bcc(is_inbound_mail_communcation=is_inbound_mail_communcation) | |||||
+ self.mail_cc(is_inbound_mail_communcation=is_inbound_mail_communcation, include_sender=include_sender) | |||||
) | |||||
return list(set(all_ids) - set(final_ids)) | |||||
def get_assignees(self): | def get_assignees(self): | ||||
"""Get owners of the reference document. | """Get owners of the reference document. | ||||
@@ -1,5 +1,6 @@ | |||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors | |||||
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors | |||||
# License: MIT. See LICENSE | # License: MIT. See LICENSE | ||||
import email.utils | import email.utils | ||||
import functools | import functools | ||||
import imaplib | import imaplib | ||||
@@ -7,6 +8,7 @@ import socket | |||||
import time | import time | ||||
from datetime import datetime, timedelta | from datetime import datetime, timedelta | ||||
from poplib import error_proto | from poplib import error_proto | ||||
from typing import List | |||||
import frappe | import frappe | ||||
from frappe import _, are_emails_muted, safe_encode | from frappe import _, are_emails_muted, safe_encode | ||||
@@ -82,9 +84,6 @@ class EmailAccount(Document): | |||||
if frappe.local.flags.in_patch or frappe.local.flags.in_test: | if frappe.local.flags.in_patch or frappe.local.flags.in_test: | ||||
return | return | ||||
#if self.enable_incoming and not self.append_to: | |||||
# frappe.throw(_("Append To is mandatory for incoming mails")) | |||||
if (not self.awaiting_password and not frappe.local.flags.in_install | if (not self.awaiting_password and not frappe.local.flags.in_install | ||||
and not frappe.local.flags.in_patch): | and not frappe.local.flags.in_patch): | ||||
if self.password or self.smtp_server in ('127.0.0.1', 'localhost'): | if self.password or self.smtp_server in ('127.0.0.1', 'localhost'): | ||||
@@ -458,7 +457,7 @@ class EmailAccount(Document): | |||||
if exceptions: | if exceptions: | ||||
raise Exception(frappe.as_json(exceptions)) | raise Exception(frappe.as_json(exceptions)) | ||||
def get_inbound_mails(self, test_mails=None): | |||||
def get_inbound_mails(self, test_mails=None) -> List[InboundMail]: | |||||
"""retrive and return inbound mails. | """retrive and return inbound mails. | ||||
""" | """ | ||||
@@ -625,7 +624,6 @@ class EmailAccount(Document): | |||||
if frappe.db.exists("Email Account", {"enable_automatic_linking": 1, "name": ('!=', self.name)}): | if frappe.db.exists("Email Account", {"enable_automatic_linking": 1, "name": ('!=', self.name)}): | ||||
frappe.throw(_("Automatic Linking can be activated only for one Email Account.")) | frappe.throw(_("Automatic Linking can be activated only for one Email Account.")) | ||||
def append_email_to_sent_folder(self, message): | def append_email_to_sent_folder(self, message): | ||||
email_server = None | email_server = None | ||||
try: | try: | ||||
@@ -20,6 +20,7 @@ from frappe.email.queue import get_unsubcribed_url, get_unsubscribe_message | |||||
from frappe.email.email_body import add_attachment, get_formatted_html, get_email | from frappe.email.email_body import add_attachment, get_formatted_html, get_email | ||||
from frappe.utils import cint, split_emails, add_days, nowdate, cstr, get_hook_method | from frappe.utils import cint, split_emails, add_days, nowdate, cstr, get_hook_method | ||||
from frappe.email.doctype.email_account.email_account import EmailAccount | from frappe.email.doctype.email_account.email_account import EmailAccount | ||||
from frappe.query_builder.utils import DocType | |||||
MAX_RETRY_COUNT = 3 | MAX_RETRY_COUNT = 3 | ||||
@@ -477,18 +478,24 @@ class QueueBuilder: | |||||
all_ids = list(set(self.recipients + self.cc)) | all_ids = list(set(self.recipients + self.cc)) | ||||
EmailUnsubscribe = frappe.qb.DocType("Email Unsubscribe") | |||||
unsubscribed = (frappe.qb.from_(EmailUnsubscribe) | |||||
.select(EmailUnsubscribe.email) | |||||
.where(EmailUnsubscribe.email.isin(all_ids) & | |||||
( | |||||
( | |||||
(EmailUnsubscribe.reference_doctype == self.reference_doctype) & (EmailUnsubscribe.reference_name == self.reference_name) | |||||
) | EmailUnsubscribe.global_unsubscribe == 1 | |||||
) | |||||
).distinct() | |||||
).run(pluck=True) | |||||
EmailUnsubscribe = DocType("Email Unsubscribe") | |||||
unsubscribed = ( | |||||
frappe.qb.from_(EmailUnsubscribe).select( | |||||
EmailUnsubscribe.email | |||||
).where( | |||||
EmailUnsubscribe.email.isin(all_ids) | |||||
& ( | |||||
( | |||||
(EmailUnsubscribe.reference_doctype == self.reference_doctype) | |||||
& (EmailUnsubscribe.reference_name == self.reference_name) | |||||
) | ( | |||||
EmailUnsubscribe.global_unsubscribe == 1 | |||||
) | |||||
) | |||||
).distinct() | |||||
).run(pluck=True) | |||||
self._unsubscribed_user_emails = unsubscribed or [] | self._unsubscribed_user_emails = unsubscribed or [] | ||||
return self._unsubscribed_user_emails | return self._unsubscribed_user_emails | ||||
@@ -5,6 +5,7 @@ import mimetypes | |||||
import os | import os | ||||
import re | import re | ||||
from functools import wraps | from functools import wraps | ||||
from typing import Dict, Optional | |||||
import yaml | import yaml | ||||
from six import iteritems | from six import iteritems | ||||
@@ -453,7 +454,7 @@ def cache_html(func): | |||||
return cache_html_decorator | return cache_html_decorator | ||||
def build_response(path, data, http_status_code, headers=None): | |||||
def build_response(path, data, http_status_code, headers: Optional[Dict] = None): | |||||
# build response | # build response | ||||
response = Response() | response = Response() | ||||
response.data = set_content_type(response, data, path) | response.data = set_content_type(response, data, path) | ||||