Merge pull request #13057 from leela/email-refactoring
refactor: Email Module - 1
This commit is contained in:
commit
3875004d7e
10 changed files with 233 additions and 167 deletions
2
.github/helper/semgrep_rules/translate.yml
vendored
2
.github/helper/semgrep_rules/translate.yml
vendored
|
|
@ -42,7 +42,7 @@ rules:
|
|||
|
||||
- id: frappe-translation-python-splitting
|
||||
pattern-either:
|
||||
- pattern: _(...) + ... + _(...)
|
||||
- pattern: _(...) + _(...)
|
||||
- pattern: _("..." + "...")
|
||||
- pattern-regex: '_\([^\)]*\\\s*' # lines broken by `\`
|
||||
- pattern-regex: '_\(\s*\n' # line breaks allowed by python for using ( )
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ def make_form_dict(request):
|
|||
args = request.form or request.args
|
||||
|
||||
if not isinstance(args, dict):
|
||||
frappe.throw("Invalid request arguments")
|
||||
frappe.throw(_("Invalid request arguments"))
|
||||
|
||||
try:
|
||||
frappe.local.form_dict = frappe._dict({ k:v[0] if isinstance(v, (list, tuple)) else v \
|
||||
|
|
@ -296,7 +296,7 @@ def serve(port=8000, profile=False, no_reload=False, no_threading=False, site=No
|
|||
from werkzeug.serving import run_simple
|
||||
patch_werkzeug_reloader()
|
||||
|
||||
if profile:
|
||||
if profile or os.environ.get('USE_PROFILER'):
|
||||
application = ProfilerMiddleware(application, sort_by=('cumtime', 'calls'))
|
||||
|
||||
if not os.environ.get('NO_STATICS'):
|
||||
|
|
|
|||
|
|
@ -272,22 +272,13 @@ def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None)
|
|||
doc.attachments.append(a)
|
||||
|
||||
def set_incoming_outgoing_accounts(doc):
|
||||
doc.incoming_email_account = doc.outgoing_email_account = None
|
||||
from frappe.email.doctype.email_account.email_account import EmailAccount
|
||||
incoming_email_account = EmailAccount.find_incoming(
|
||||
match_by_email=doc.sender, match_by_doctype=doc.reference_doctype)
|
||||
doc.incoming_email_account = incoming_email_account.email_id if incoming_email_account else None
|
||||
|
||||
if not doc.incoming_email_account and doc.sender:
|
||||
doc.incoming_email_account = frappe.db.get_value("Email Account",
|
||||
{"email_id": doc.sender, "enable_incoming": 1}, "email_id")
|
||||
|
||||
if not doc.incoming_email_account and doc.reference_doctype:
|
||||
doc.incoming_email_account = frappe.db.get_value("Email Account",
|
||||
{"append_to": doc.reference_doctype, }, "email_id")
|
||||
|
||||
if not doc.incoming_email_account:
|
||||
doc.incoming_email_account = frappe.db.get_value("Email Account",
|
||||
{"default_incoming": 1, "enable_incoming": 1}, "email_id")
|
||||
|
||||
doc.outgoing_email_account = frappe.email.smtp.get_outgoing_email_account(raise_exception_not_set=False,
|
||||
append_to=doc.doctype, sender=doc.sender)
|
||||
doc.outgoing_email_account = EmailAccount.find_outgoing(
|
||||
match_by_email=doc.sender, match_by_doctype=doc.reference_doctype)
|
||||
|
||||
if doc.sent_or_received == "Sent":
|
||||
doc.db_set("email_account", doc.outgoing_email_account.name)
|
||||
|
|
|
|||
|
|
@ -8,9 +8,14 @@ import re
|
|||
import json
|
||||
import socket
|
||||
import time
|
||||
from frappe import _
|
||||
import functools
|
||||
|
||||
import email.utils
|
||||
|
||||
from frappe import _, are_emails_muted
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import validate_email_address, cint, cstr, get_datetime, DATE_FORMAT, strip, comma_or, sanitize_html, add_days
|
||||
from frappe.utils import (validate_email_address, cint, cstr, get_datetime,
|
||||
DATE_FORMAT, strip, comma_or, sanitize_html, add_days, parse_addr)
|
||||
from frappe.utils.user import is_system_user
|
||||
from frappe.utils.jinja import render_template
|
||||
from frappe.email.smtp import SMTPServer
|
||||
|
|
@ -21,17 +26,40 @@ from datetime import datetime, timedelta
|
|||
from frappe.desk.form import assign_to
|
||||
from frappe.utils.user import get_system_managers
|
||||
from frappe.utils.background_jobs import enqueue, get_jobs
|
||||
from frappe.core.doctype.communication.email import set_incoming_outgoing_accounts
|
||||
from frappe.utils.html_utils import clean_email_html
|
||||
from frappe.utils.error import raise_error_on_no_output
|
||||
from frappe.email.utils import get_port
|
||||
|
||||
OUTGOING_EMAIL_ACCOUNT_MISSING = _("Please setup default Email Account from Setup > Email > Email Account")
|
||||
|
||||
class SentEmailInInbox(Exception):
|
||||
pass
|
||||
|
||||
class InvalidEmailCredentials(frappe.ValidationError):
|
||||
pass
|
||||
|
||||
def cache_email_account(cache_name):
|
||||
def decorator_cache_email_account(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_cache_email_account(*args, **kwargs):
|
||||
if not hasattr(frappe.local, cache_name):
|
||||
setattr(frappe.local, cache_name, {})
|
||||
|
||||
cached_accounts = getattr(frappe.local, cache_name)
|
||||
match_by = list(kwargs.values()) + ['default']
|
||||
matched_accounts = list(filter(None, [cached_accounts.get(key) for key in match_by]))
|
||||
if matched_accounts:
|
||||
return matched_accounts[0]
|
||||
|
||||
matched_accounts = func(*args, **kwargs)
|
||||
cached_accounts.update(matched_accounts or {})
|
||||
return matched_accounts and list(matched_accounts.values())[0]
|
||||
return wrapper_cache_email_account
|
||||
return decorator_cache_email_account
|
||||
|
||||
class EmailAccount(Document):
|
||||
DOCTYPE = 'Email Account'
|
||||
|
||||
def autoname(self):
|
||||
"""Set name as `email_account_name` or make title from Email Address."""
|
||||
if not self.email_account_name:
|
||||
|
|
@ -249,6 +277,15 @@ class EmailAccount(Document):
|
|||
else:
|
||||
raise
|
||||
|
||||
@property
|
||||
def _password(self):
|
||||
raise_exception = not self.no_smtp_authentication
|
||||
return self.get_password(raise_exception=raise_exception)
|
||||
|
||||
@property
|
||||
def default_sender(self):
|
||||
return email.utils.formataddr((self.name, self.get("email_id")))
|
||||
|
||||
@classmethod
|
||||
def throw_invalid_credentials_exception(cls):
|
||||
frappe.throw(
|
||||
|
|
@ -257,6 +294,114 @@ class EmailAccount(Document):
|
|||
title=_("Invalid Credentials")
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_record(cls, record):
|
||||
email_account = frappe.new_doc(cls.DOCTYPE)
|
||||
email_account.update(record)
|
||||
return email_account
|
||||
|
||||
@classmethod
|
||||
def find(cls, name):
|
||||
return frappe.get_doc(cls.DOCTYPE, name)
|
||||
|
||||
@classmethod
|
||||
def find_one_by_filters(cls, **kwargs):
|
||||
name = frappe.db.get_value(cls.DOCTYPE, kwargs)
|
||||
return cls.find(name) if name else None
|
||||
|
||||
@classmethod
|
||||
def find_from_config(cls):
|
||||
config = cls.get_account_details_from_site_config()
|
||||
return cls.from_record(config) if config else None
|
||||
|
||||
@classmethod
|
||||
def create_dummy(cls):
|
||||
return cls.from_record({"sender": "notifications@example.com"})
|
||||
|
||||
@classmethod
|
||||
@raise_error_on_no_output(
|
||||
keep_quiet = lambda: not cint(frappe.get_system_settings('setup_complete')),
|
||||
error_message = OUTGOING_EMAIL_ACCOUNT_MISSING, error_type = frappe.OutgoingEmailError) # noqa
|
||||
@cache_email_account('outgoing_email_account')
|
||||
def find_outgoing(cls, match_by_email=None, match_by_doctype=None, _raise_error=False):
|
||||
"""Find the outgoing Email account to use.
|
||||
|
||||
:param match_by_email: Find account using emailID
|
||||
:param match_by_doctype: Find account by matching `Append To` doctype
|
||||
:param _raise_error: This is used by raise_error_on_no_output decorator to raise error.
|
||||
"""
|
||||
if match_by_email:
|
||||
match_by_email = parse_addr(match_by_email)[1]
|
||||
doc = cls.find_one_by_filters(enable_outgoing=1, email_id=match_by_email)
|
||||
if doc:
|
||||
return {match_by_email: doc}
|
||||
|
||||
if match_by_doctype:
|
||||
doc = cls.find_one_by_filters(enable_outgoing=1, enable_incoming=1, append_to=match_by_doctype)
|
||||
if doc:
|
||||
return {match_by_doctype: doc}
|
||||
|
||||
doc = cls.find_default_outgoing()
|
||||
if doc:
|
||||
return {'default': doc}
|
||||
|
||||
@classmethod
|
||||
def find_default_outgoing(cls):
|
||||
""" Find default outgoing account.
|
||||
"""
|
||||
doc = cls.find_one_by_filters(enable_outgoing=1, default_outgoing=1)
|
||||
doc = doc or cls.find_from_config()
|
||||
return doc or (are_emails_muted() and cls.create_dummy())
|
||||
|
||||
@classmethod
|
||||
def find_incoming(cls, match_by_email=None, match_by_doctype=None):
|
||||
"""Find the incoming Email account to use.
|
||||
:param match_by_email: Find account using emailID
|
||||
:param match_by_doctype: Find account by matching `Append To` doctype
|
||||
"""
|
||||
doc = cls.find_one_by_filters(enable_incoming=1, email_id=match_by_email)
|
||||
if doc:
|
||||
return doc
|
||||
|
||||
doc = cls.find_one_by_filters(enable_incoming=1, append_to=match_by_doctype)
|
||||
if doc:
|
||||
return doc
|
||||
|
||||
doc = cls.find_default_incoming()
|
||||
return doc
|
||||
|
||||
@classmethod
|
||||
def find_default_incoming(cls):
|
||||
doc = cls.find_one_by_filters(enable_incoming=1, default_incoming=1)
|
||||
return doc
|
||||
|
||||
@classmethod
|
||||
def get_account_details_from_site_config(cls):
|
||||
if not frappe.conf.get("mail_server"):
|
||||
return {}
|
||||
|
||||
field_to_conf_name_map = {
|
||||
'smtp_server': {'conf_names': ('mail_server',)},
|
||||
'smtp_port': {'conf_names': ('mail_port',)},
|
||||
'use_tls': {'conf_names': ('use_tls', 'mail_login')},
|
||||
'login_id': {'conf_names': ('mail_login',)},
|
||||
'email_id': {'conf_names': ('auto_email_id', 'mail_login'), 'default': 'notifications@example.com'},
|
||||
'password': {'conf_names': ('mail_password',)},
|
||||
'always_use_account_email_id_as_sender':
|
||||
{'conf_names': ('always_use_account_email_id_as_sender',), 'default': 0},
|
||||
'always_use_account_name_as_sender_name':
|
||||
{'conf_names': ('always_use_account_name_as_sender_name',), 'default': 0},
|
||||
'name': {'conf_names': ('email_sender_name',), 'default': 'Frappe'},
|
||||
'from_site_config': {'default': True}
|
||||
}
|
||||
|
||||
account_details = {}
|
||||
for doc_field_name, d in field_to_conf_name_map.items():
|
||||
conf_names, default = d.get('conf_names') or [], d.get('default')
|
||||
value = [frappe.conf.get(k) for k in conf_names if frappe.conf.get(k)]
|
||||
account_details[doc_field_name] = (value and value[0]) or default
|
||||
return account_details
|
||||
|
||||
def handle_incoming_connect_error(self, description):
|
||||
if test_internet():
|
||||
if self.get_failed_attempts_count() > 2:
|
||||
|
|
@ -642,6 +787,8 @@ class EmailAccount(Document):
|
|||
|
||||
def send_auto_reply(self, communication, email):
|
||||
"""Send auto reply if set."""
|
||||
from frappe.core.doctype.communication.email import set_incoming_outgoing_accounts
|
||||
|
||||
if self.enable_auto_reply:
|
||||
set_incoming_outgoing_accounts(communication)
|
||||
|
||||
|
|
@ -653,7 +800,7 @@ class EmailAccount(Document):
|
|||
frappe.sendmail(recipients = [email.from_email],
|
||||
sender = self.email_id,
|
||||
reply_to = communication.incoming_email_account,
|
||||
subject = _("Re: ") + communication.subject,
|
||||
subject = " ".join([_("Re:"), communication.subject]),
|
||||
content = render_template(self.auto_reply_message or "", communication.as_dict()) or \
|
||||
frappe.get_template("templates/emails/auto_reply.html").render(communication.as_dict()),
|
||||
reference_doctype = communication.reference_doctype,
|
||||
|
|
|
|||
|
|
@ -10,7 +10,8 @@
|
|||
"incoming_port": "993",
|
||||
"attachment_limit": "1",
|
||||
"smtp_server": "smtp.test.com",
|
||||
"smtp_port": "587"
|
||||
"smtp_port": "587",
|
||||
"password": "password"
|
||||
},
|
||||
{
|
||||
"doctype": "Email Account",
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
"incoming_port": "143",
|
||||
"attachment_limit": "1",
|
||||
"smtp_server": "smtp.test.com",
|
||||
"smtp_port": "587"
|
||||
"smtp_port": "587",
|
||||
"password": "password"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe, re, os
|
||||
from frappe.utils.pdf import get_pdf
|
||||
from frappe.email.smtp import get_outgoing_email_account
|
||||
from frappe.email.doctype.email_account.email_account import EmailAccount
|
||||
from frappe.utils import (get_url, scrub_urls, strip, expand_relative_urls, cint,
|
||||
split_emails, to_markdown, markdown, random_string, parse_addr)
|
||||
import email.utils
|
||||
|
|
@ -75,7 +75,8 @@ class EMail:
|
|||
self.bcc = bcc or []
|
||||
self.html_set = False
|
||||
|
||||
self.email_account = email_account or get_outgoing_email_account(sender=sender)
|
||||
self.email_account = email_account or \
|
||||
EmailAccount.find_outgoing(match_by_email=sender, _raise_error=True)
|
||||
|
||||
def set_html(self, message, text_content = None, footer=None, print_html=None,
|
||||
formatted=None, inline_images=None, header=None):
|
||||
|
|
@ -249,8 +250,8 @@ class EMail:
|
|||
|
||||
def get_formatted_html(subject, message, footer=None, print_html=None,
|
||||
email_account=None, header=None, unsubscribe_link=None, sender=None, with_container=False):
|
||||
if not email_account:
|
||||
email_account = get_outgoing_email_account(False, sender=sender)
|
||||
|
||||
email_account = email_account or EmailAccount.find_outgoing(match_by_email=sender)
|
||||
|
||||
signature = None
|
||||
if "<!-- signature-included -->" not in message:
|
||||
|
|
@ -480,4 +481,4 @@ def sanitize_email_header(str):
|
|||
return str.replace('\r', '').replace('\n', '')
|
||||
|
||||
def get_brand_logo(email_account):
|
||||
return email_account.get('brand_logo')
|
||||
return email_account.get('brand_logo')
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ import sys
|
|||
from six.moves import html_parser as HTMLParser
|
||||
import smtplib, quopri, json
|
||||
from frappe import msgprint, _, safe_decode, safe_encode, enqueue
|
||||
from frappe.email.smtp import SMTPServer, get_outgoing_email_account
|
||||
from frappe.email.smtp import SMTPServer
|
||||
from frappe.email.doctype.email_account.email_account import EmailAccount
|
||||
from frappe.email.email_body import get_email, get_formatted_html, add_attachment
|
||||
from frappe.utils.verified_command import get_signed_params, verify_request
|
||||
from html2text import html2text
|
||||
|
|
@ -73,7 +74,9 @@ def send(recipients=None, sender=None, subject=None, message=None, text_content=
|
|||
if isinstance(send_after, int):
|
||||
send_after = add_days(nowdate(), send_after)
|
||||
|
||||
email_account = get_outgoing_email_account(True, append_to=reference_doctype, sender=sender)
|
||||
email_account = EmailAccount.find_outgoing(
|
||||
match_by_doctype=reference_doctype, match_by_email=sender, _raise_error=True)
|
||||
|
||||
if not sender or sender == "Administrator":
|
||||
sender = email_account.default_sender
|
||||
|
||||
|
|
@ -516,7 +519,7 @@ def prepare_message(email, recipient, recipients_list):
|
|||
return ""
|
||||
|
||||
# Parse "Email Account" from "Email Sender"
|
||||
email_account = get_outgoing_email_account(raise_exception_not_set=False, sender=email.sender)
|
||||
email_account = EmailAccount.find_outgoing(match_by_email=email.sender)
|
||||
if frappe.conf.use_ssl and email_account.track_email_status:
|
||||
# Using SSL => Publically available domain => Email Read Reciept Possible
|
||||
message = message.replace("<!--email open check-->", quopri.encodestring('<img src="https://{}/api/method/frappe.core.doctype.communication.email.mark_email_as_seen?name={}"/>'.format(frappe.local.site, email.communication).encode()).decode())
|
||||
|
|
|
|||
|
|
@ -34,126 +34,6 @@ def send(email, append_to=None, retry=1):
|
|||
|
||||
_send(retry)
|
||||
|
||||
def get_outgoing_email_account(raise_exception_not_set=True, append_to=None, sender=None):
|
||||
"""Returns outgoing email account based on `append_to` or the default
|
||||
outgoing account. If default outgoing account is not found, it will
|
||||
try getting settings from `site_config.json`."""
|
||||
|
||||
sender_email_id = None
|
||||
_email_account = None
|
||||
|
||||
if sender:
|
||||
sender_email_id = parse_addr(sender)[1]
|
||||
|
||||
if not getattr(frappe.local, "outgoing_email_account", None):
|
||||
frappe.local.outgoing_email_account = {}
|
||||
|
||||
if not (frappe.local.outgoing_email_account.get(append_to)
|
||||
or frappe.local.outgoing_email_account.get(sender_email_id)
|
||||
or frappe.local.outgoing_email_account.get("default")):
|
||||
email_account = None
|
||||
|
||||
if sender_email_id:
|
||||
# check if the sender has an email account with enable_outgoing
|
||||
email_account = _get_email_account({"enable_outgoing": 1,
|
||||
"email_id": sender_email_id})
|
||||
|
||||
if not email_account and append_to:
|
||||
# append_to is only valid when enable_incoming is checked
|
||||
email_accounts = frappe.db.get_values("Email Account", {
|
||||
"enable_outgoing": 1,
|
||||
"enable_incoming": 1,
|
||||
"append_to": append_to,
|
||||
}, cache=True)
|
||||
|
||||
if email_accounts:
|
||||
_email_account = email_accounts[0]
|
||||
|
||||
else:
|
||||
email_account = _get_email_account({
|
||||
"enable_outgoing": 1,
|
||||
"enable_incoming": 1,
|
||||
"append_to": append_to
|
||||
})
|
||||
|
||||
if not email_account:
|
||||
# sender don't have the outging email account
|
||||
sender_email_id = None
|
||||
email_account = get_default_outgoing_email_account(raise_exception_not_set=raise_exception_not_set)
|
||||
|
||||
if not email_account and _email_account:
|
||||
# if default email account is not configured then setup first email account based on append to
|
||||
email_account = _email_account
|
||||
|
||||
if not email_account and raise_exception_not_set and cint(frappe.db.get_single_value('System Settings', 'setup_complete')):
|
||||
frappe.throw(_("Please setup default Email Account from Setup > Email > Email Account"),
|
||||
frappe.OutgoingEmailError)
|
||||
|
||||
if email_account:
|
||||
if email_account.enable_outgoing and not getattr(email_account, 'from_site_config', False):
|
||||
raise_exception = True
|
||||
if email_account.smtp_server in ['localhost','127.0.0.1'] or email_account.no_smtp_authentication:
|
||||
raise_exception = False
|
||||
email_account.password = email_account.get_password(raise_exception=raise_exception)
|
||||
email_account.default_sender = email.utils.formataddr((email_account.name, email_account.get("email_id")))
|
||||
|
||||
frappe.local.outgoing_email_account[append_to or sender_email_id or "default"] = email_account
|
||||
|
||||
return frappe.local.outgoing_email_account.get(append_to) \
|
||||
or frappe.local.outgoing_email_account.get(sender_email_id) \
|
||||
or frappe.local.outgoing_email_account.get("default")
|
||||
|
||||
def get_default_outgoing_email_account(raise_exception_not_set=True):
|
||||
'''conf should be like:
|
||||
{
|
||||
"mail_server": "smtp.example.com",
|
||||
"mail_port": 587,
|
||||
"use_tls": 1,
|
||||
"mail_login": "emails@example.com",
|
||||
"mail_password": "Super.Secret.Password",
|
||||
"auto_email_id": "emails@example.com",
|
||||
"email_sender_name": "Example Notifications",
|
||||
"always_use_account_email_id_as_sender": 0,
|
||||
"always_use_account_name_as_sender_name": 0
|
||||
}
|
||||
'''
|
||||
email_account = _get_email_account({"enable_outgoing": 1, "default_outgoing": 1})
|
||||
if email_account:
|
||||
email_account.password = email_account.get_password(raise_exception=False)
|
||||
|
||||
if not email_account and frappe.conf.get("mail_server"):
|
||||
# from site_config.json
|
||||
email_account = frappe.new_doc("Email Account")
|
||||
email_account.update({
|
||||
"smtp_server": frappe.conf.get("mail_server"),
|
||||
"smtp_port": frappe.conf.get("mail_port"),
|
||||
|
||||
# legacy: use_ssl was used in site_config instead of use_tls, but meant the same thing
|
||||
"use_tls": cint(frappe.conf.get("use_tls") or 0) or cint(frappe.conf.get("use_ssl") or 0),
|
||||
"login_id": frappe.conf.get("mail_login"),
|
||||
"email_id": frappe.conf.get("auto_email_id") or frappe.conf.get("mail_login") or 'notifications@example.com',
|
||||
"password": frappe.conf.get("mail_password"),
|
||||
"always_use_account_email_id_as_sender": frappe.conf.get("always_use_account_email_id_as_sender", 0),
|
||||
"always_use_account_name_as_sender_name": frappe.conf.get("always_use_account_name_as_sender_name", 0)
|
||||
})
|
||||
email_account.from_site_config = True
|
||||
email_account.name = frappe.conf.get("email_sender_name") or "Frappe"
|
||||
|
||||
if not email_account and not raise_exception_not_set:
|
||||
return None
|
||||
|
||||
if frappe.are_emails_muted():
|
||||
# create a stub
|
||||
email_account = frappe.new_doc("Email Account")
|
||||
email_account.update({
|
||||
"email_id": "notifications@example.com"
|
||||
})
|
||||
|
||||
return email_account
|
||||
|
||||
def _get_email_account(filters):
|
||||
name = frappe.db.get_value("Email Account", filters)
|
||||
return frappe.get_doc("Email Account", name) if name else None
|
||||
|
||||
class SMTPServer:
|
||||
def __init__(self, login=None, password=None, server=None, port=None, use_tls=None, use_ssl=None, append_to=None):
|
||||
|
|
@ -176,17 +56,15 @@ class SMTPServer:
|
|||
self.setup_email_account(append_to)
|
||||
|
||||
def setup_email_account(self, append_to=None, sender=None):
|
||||
self.email_account = get_outgoing_email_account(raise_exception_not_set=False, append_to=append_to, sender=sender)
|
||||
from frappe.email.doctype.email_account.email_account import EmailAccount
|
||||
self.email_account = EmailAccount.find_outgoing(match_by_doctype=append_to, match_by_email=sender)
|
||||
if self.email_account:
|
||||
self.server = self.email_account.smtp_server
|
||||
self.login = (getattr(self.email_account, "login_id", None) or self.email_account.email_id)
|
||||
if not self.email_account.no_smtp_authentication:
|
||||
if self.email_account.ascii_encode_password:
|
||||
self.password = frappe.safe_encode(self.email_account.password, 'ascii')
|
||||
else:
|
||||
self.password = self.email_account.password
|
||||
else:
|
||||
if self.email_account.no_smtp_authentication or frappe.local.flags.in_test:
|
||||
self.password = None
|
||||
else:
|
||||
self.password = self.email_account._password
|
||||
self.port = self.email_account.smtp_port
|
||||
self.use_tls = self.email_account.use_tls
|
||||
self.sender = self.email_account.email_id
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
import unittest
|
||||
import frappe
|
||||
from frappe.email.smtp import SMTPServer
|
||||
from frappe.email.smtp import get_outgoing_email_account
|
||||
from frappe.email.doctype.email_account.email_account import EmailAccount
|
||||
|
||||
class TestSMTP(unittest.TestCase):
|
||||
def test_smtp_ssl_session(self):
|
||||
|
|
@ -33,13 +33,13 @@ class TestSMTP(unittest.TestCase):
|
|||
|
||||
frappe.local.outgoing_email_account = {}
|
||||
# lowest preference given to email account with default incoming enabled
|
||||
create_email_account(email_id="default_outgoing_enabled@gmail.com", password="***", enable_outgoing = 1, default_outgoing=1)
|
||||
self.assertEqual(get_outgoing_email_account().email_id, "default_outgoing_enabled@gmail.com")
|
||||
create_email_account(email_id="default_outgoing_enabled@gmail.com", password="password", enable_outgoing = 1, default_outgoing=1)
|
||||
self.assertEqual(EmailAccount.find_outgoing().email_id, "default_outgoing_enabled@gmail.com")
|
||||
|
||||
frappe.local.outgoing_email_account = {}
|
||||
# highest preference given to email account with append_to matching
|
||||
create_email_account(email_id="append_to@gmail.com", password="***", enable_outgoing = 1, default_outgoing=1, append_to="Blog Post")
|
||||
self.assertEqual(get_outgoing_email_account(append_to="Blog Post").email_id, "append_to@gmail.com")
|
||||
create_email_account(email_id="append_to@gmail.com", password="password", enable_outgoing = 1, default_outgoing=1, append_to="Blog Post")
|
||||
self.assertEqual(EmailAccount.find_outgoing(match_by_doctype="Blog Post").email_id, "append_to@gmail.com")
|
||||
|
||||
# add back the mail_server
|
||||
frappe.conf['mail_server'] = mail_server
|
||||
|
|
|
|||
|
|
@ -4,12 +4,14 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe.utils import cstr, encode
|
||||
import os
|
||||
import sys
|
||||
import inspect
|
||||
import traceback
|
||||
import functools
|
||||
|
||||
import frappe
|
||||
from frappe.utils import cstr, encode
|
||||
import inspect
|
||||
import linecache
|
||||
import pydoc
|
||||
import cgitb
|
||||
|
|
@ -190,3 +192,45 @@ def clear_old_snapshots():
|
|||
|
||||
def get_error_snapshot_path():
|
||||
return frappe.get_site_path('error-snapshots')
|
||||
|
||||
def get_default_args(func):
|
||||
"""Get default arguments of a function from its signature.
|
||||
"""
|
||||
signature = inspect.signature(func)
|
||||
return {k: v.default
|
||||
for k, v in signature.parameters.items() if v.default is not inspect.Parameter.empty}
|
||||
|
||||
def raise_error_on_no_output(error_message, error_type=None, keep_quiet=None):
|
||||
"""Decorate any function to throw error incase of missing output.
|
||||
|
||||
TODO: Remove keep_quiet flag after testing and fixing sendmail flow.
|
||||
|
||||
:param error_message: error message to raise
|
||||
:param error_type: type of error to raise
|
||||
:param keep_quiet: control error raising with external factor.
|
||||
:type error_message: str
|
||||
:type error_type: Exception Class
|
||||
:type keep_quiet: function
|
||||
|
||||
>>> @raise_error_on_no_output("Ingradients missing")
|
||||
... def get_indradients(_raise_error=1): return
|
||||
...
|
||||
>>> get_indradients()
|
||||
`Exception Name`: Ingradients missing
|
||||
"""
|
||||
def decorator_raise_error_on_no_output(func):
|
||||
@functools.wraps(func)
|
||||
def wrapper_raise_error_on_no_output(*args, **kwargs):
|
||||
response = func(*args, **kwargs)
|
||||
if callable(keep_quiet) and keep_quiet():
|
||||
return response
|
||||
|
||||
default_kwargs = get_default_args(func)
|
||||
default_raise_error = default_kwargs.get('_raise_error')
|
||||
raise_error = kwargs.get('_raise_error') if '_raise_error' in kwargs else default_raise_error
|
||||
|
||||
if (not response) and raise_error:
|
||||
frappe.throw(error_message, error_type or Exception)
|
||||
return response
|
||||
return wrapper_raise_error_on_no_output
|
||||
return decorator_raise_error_on_no_output
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue