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 | |||
import frappe | |||
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 time | |||
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 = _(""" | |||
Unable to send mail because of a missing email account. | |||
Please setup default Email Account from Setup > Email > Email Account | |||
""") | |||
@frappe.whitelist() | |||
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, | |||
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, | |||
ignore_permissions=False): | |||
ignore_permissions=False) -> Dict[str, str]: | |||
"""Make a new communication. | |||
: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 | |||
bcc = list_to_str(bcc) if isinstance(bcc, list) else bcc | |||
comm = frappe.get_doc({ | |||
comm: "Communication" = frappe.get_doc({ | |||
"doctype":"Communication", | |||
"subject": subject, | |||
"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) | |||
emails_not_sent_to = comm.exclude_emails_list(include_sender=send_me_a_copy) | |||
return { | |||
"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""" | |||
if not (doc.communication_type=="Communication" and doc.communication_medium == "Email") or doc.flags.in_receive: | |||
return | |||
@@ -114,8 +118,6 @@ def validate_email(doc): | |||
for email in split_emails(doc.bcc): | |||
validate_email_address(email, throw=True) | |||
# validate sender | |||
def set_incoming_outgoing_accounts(doc): | |||
from frappe.email.doctype.email_account.email_account import EmailAccount | |||
incoming_email_account = EmailAccount.find_incoming( | |||
@@ -1,3 +1,4 @@ | |||
from typing import List | |||
import frappe | |||
from frappe import _ | |||
from frappe.core.utils import get_parent_doc | |||
@@ -194,14 +195,18 @@ class CommunicationEmailMixin: | |||
return _("Leave this conversation") | |||
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. | |||
""" | |||
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): | |||
"""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 | |||
import email.utils | |||
import functools | |||
import imaplib | |||
@@ -7,6 +8,7 @@ import socket | |||
import time | |||
from datetime import datetime, timedelta | |||
from poplib import error_proto | |||
from typing import List | |||
import frappe | |||
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: | |||
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 | |||
and not frappe.local.flags.in_patch): | |||
if self.password or self.smtp_server in ('127.0.0.1', 'localhost'): | |||
@@ -458,7 +457,7 @@ class EmailAccount(Document): | |||
if 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. | |||
""" | |||
@@ -625,7 +624,6 @@ class EmailAccount(Document): | |||
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.")) | |||
def append_email_to_sent_folder(self, message): | |||
email_server = None | |||
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.utils import cint, split_emails, add_days, nowdate, cstr, get_hook_method | |||
from frappe.email.doctype.email_account.email_account import EmailAccount | |||
from frappe.query_builder.utils import DocType | |||
MAX_RETRY_COUNT = 3 | |||
@@ -477,18 +478,24 @@ class QueueBuilder: | |||
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 [] | |||
return self._unsubscribed_user_emails | |||
@@ -5,6 +5,7 @@ import mimetypes | |||
import os | |||
import re | |||
from functools import wraps | |||
from typing import Dict, Optional | |||
import yaml | |||
from six import iteritems | |||
@@ -453,7 +454,7 @@ def cache_html(func): | |||
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 | |||
response = Response() | |||
response.data = set_content_type(response, data, path) | |||