[mailbox] cleaned up, added tests #561

This commit is contained in:
Rushabh Mehta 2014-09-15 16:49:37 +05:30
parent 4d4f146fc8
commit ac0a9deaf1
24 changed files with 280 additions and 354 deletions

View file

@ -225,22 +225,22 @@ def get_request_header(key, default=None):
def sendmail(recipients=(), sender="", subject="No Subject", message="No Message",
as_markdown=False, bulk=False, ref_doctype=None, ref_docname=None,
add_unsubscribe_link=False, attachments=None):
add_unsubscribe_link=False, attachments=None, content=None):
if bulk:
import frappe.email.bulk
frappe.email.bulk.send(recipients=recipients, sender=sender,
subject=subject, message=message, ref_doctype = ref_doctype,
subject=subject, message=content or message, ref_doctype = ref_doctype,
ref_docname = ref_docname, add_unsubscribe_link=add_unsubscribe_link, attachments=attachments)
else:
import frappe.email
if as_markdown:
frappe.email.sendmail_md(recipients, sender=sender,
subject=subject, msg=message, attachments=attachments)
subject=subject, msg=content or message, attachments=attachments)
else:
frappe.email.sendmail(recipients, sender=sender,
subject=subject, msg=message, attachments=attachments)
subject=subject, msg=content or message, attachments=attachments)
logger = None
whitelisted = []

View file

@ -801,7 +801,7 @@ def run_tests(app=None, module=None, doctype=None, verbose=False, tests=(), driv
import frappe.test_runner
from frappe.utils import sel
sel.start(verbose, driver)
#sel.start(verbose, driver)
ret = 1
try:

View file

@ -5,31 +5,66 @@ from __future__ import unicode_literals
import frappe
import json
import urllib
from email.utils import formataddr
from frappe.website.utils import is_signup_enabled
from frappe.utils import get_url, cstr, cint, scrub_urls
from frappe.email.email_body import get_email
from frappe.email.smtp import send
import frappe.email.smtp
from frappe import _
from frappe.model.document import Document
class Communication(Document):
def validate(self):
if not self.parentfield:
self.parentfield = "communications"
if not self.sender:
self.sender = frappe.db.get_value("User", frappe.session.user, "email")
def get_parent_doc(self):
return frappe.get_doc(self.parenttype, self.parent)
def update_parent(self):
"""update status of parent Lead or Contact based on who is replying"""
parent_doc = self.get_parent_doc()
parent_doc.run_method("on_communication")
if not hasattr(self, "parent_doc"):
if self.reference_doctype and self.reference_name:
self.parent_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
else:
self.parent_doc = None
return self.parent_doc
def on_update(self):
self.update_parent()
def update_parent(self):
"""update status of parent Lead or Contact based on who is replying"""
parent = self.get_parent_doc()
if not parent:
return
status_field = parent.meta.get_field("status")
if status_field and "Open" in (status_field.options or "").split("\n"):
frappe.db.set_value(parent.doctype, parent.name, "status",
"Open" if self.sent_or_received=="Received" else "Replied")
def send(self, send_me_a_copy=False, print_html=None, print_format=None,
attachments=None):
mail = get_email(self.recipients, sender=self.sender, subject=self.subject,
content=self.content)
mail.set_message_id(self.name)
if send_me_a_copy:
mail.cc.append(frappe.db.get_value("User", frappe.session.user, "email"))
if print_html or print_format:
attach_print(mail, self.get_parent_doc(), print_html, print_format)
if isinstance(attachments, basestring):
attachments = json.loads(attachments)
for a in attachments:
try:
mail.attach_file(a)
except IOError:
frappe.throw(_("Unable to find attachment {0}").format(a))
frappe.email.smtp.send(mail)
def on_doctype_update():
frappe.db.add_index("Communication", ["reference_doctype", "reference_name"])
@ -42,112 +77,24 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
raise frappe.PermissionError("You are not allowed to send emails related to: {doctype} {name}".format(
doctype=doctype, name=name))
_make(doctype=doctype, name=name, content=content, subject=subject, sent_or_received=sent_or_received,
sender=sender, recipients=recipients, communication_medium=communication_medium, send_email=send_email,
print_html=print_html, print_format=print_format, attachments=attachments, send_me_a_copy=send_me_a_copy, set_lead=set_lead,
date=date)
def _make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent",
sender=None, recipients=None, communication_medium="Email", send_email=False,
print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, set_lead=True, date=None):
# add to Communication
sent_via = None
# since we are using fullname and email,
# if the fullname has any incompatible characters,formataddr can deal with it
try:
sender = json.loads(sender)
except ValueError:
pass
if isinstance(sender, (tuple, list)) and len(sender)==2:
sender = formataddr(sender)
comm = frappe.new_doc('Communication')
d = comm
d.subject = subject
d.content = content
d.sent_or_received = sent_or_received
d.sender = sender or frappe.db.get_value("User", frappe.session.user, "email")
d.recipients = recipients
# add as child
sent_via = frappe.get_doc(doctype, name)
d.parent = name
d.parenttype = doctype
d.parentfield = "communications"
if date:
d.communication_date = date
d.communication_medium = communication_medium
d.idx = cint(frappe.db.sql("""select max(idx) from `tabCommunication`
where parenttype=%s and parent=%s""", (doctype, name))[0][0]) + 1
comm.ignore_permissions = True
comm.insert()
comm = frappe.get_doc({
"doctype":"Communication",
"subject": subject,
"content": content,
"sender": sender,
"recipients": recipients,
"communication_medium": "Email",
"sent_or_received": sent_or_received,
})
comm.insert(ignore_permissions=True)
if send_email:
d = comm
send_comm_email(d, name, sent_via, print_html, print_format, attachments, send_me_a_copy)
comm.send(send_me_a_copy, print_html, print_format, attachments)
@frappe.whitelist()
def get_customer_supplier(args=None):
"""
Get Customer/Supplier, given a contact, if a unique match exists
"""
if not args: args = frappe.local.form_dict
if not args.get('contact'):
raise Exception, "Please specify a contact to fetch Customer/Supplier"
result = frappe.db.sql("""\
select customer, supplier
from `tabContact`
where name = %s""", args.get('contact'), as_dict=1)
if result and len(result)==1 and (result[0]['customer'] or result[0]['supplier']):
return {
'fieldname': result[0]['customer'] and 'customer' or 'supplier',
'value': result[0]['customer'] or result[0]['supplier']
}
return {}
def send_comm_email(d, name, sent_via=None, print_html=None, print_format=None, attachments='[]', send_me_a_copy=False):
footer = None
if sent_via:
if hasattr(sent_via, "get_sender"):
d.sender = sent_via.get_sender(d) or d.sender
if hasattr(sent_via, "get_subject"):
d.subject = sent_via.get_subject(d)
if hasattr(sent_via, "get_content"):
d.content = sent_via.get_content(d)
footer = "<hr>" + set_portal_link(sent_via, d)
mail = get_email(d.recipients, sender=d.sender, subject=d.subject,
msg=d.content, footer=footer)
mail.set_message_id(d.name)
if send_me_a_copy:
mail.cc.append(frappe.db.get_value("User", frappe.session.user, "email"))
if print_html or print_format:
attach_print(mail, sent_via, print_html, print_format)
for a in json.loads(attachments):
try:
mail.attach_file(a)
except IOError:
frappe.throw(_("Unable to find attachment {0}").format(a))
send(mail)
def attach_print(mail, sent_via, print_html, print_format):
name = sent_via.name
if not print_html and print_format:
print_html = frappe.get_print_format(sent_via.doctype, sent_via.name, print_format)
def attach_print(mail, parent_doc, print_html, print_format):
name = parent_doc.name if parent_doc else "attachment"
if (not print_html) and parent_doc and print_format:
print_html = frappe.get_print_format(parent_doc.doctype, parent_doc.name, print_format)
print_settings = frappe.db.get_singles_dict("Print Settings")
send_print_as_pdf = cint(print_settings.send_print_as_pdf)

View file

@ -7,14 +7,14 @@ import frappe
from frappe.email.email_body import get_email
from frappe.email.smtp import send
def sendmail_md(recipients, sender=None, msg=None, subject=None, attachments=None):
def sendmail_md(recipients, sender=None, msg=None, subject=None, attachments=None, content=None):
"""send markdown email"""
import markdown2
sendmail(recipients, sender, markdown2.markdown(msg), subject, attachments)
sendmail(recipients, sender, markdown2.markdown(content or msg), subject, attachments)
def sendmail(recipients, sender='', msg='', subject='[No Subject]', attachments=None):
def sendmail(recipients, sender='', msg='', subject='[No Subject]', attachments=None, content=None):
"""send an html email as multipart with attachments and all"""
send(get_email(recipients, sender, msg, subject, attachments=attachments))
send(get_email(recipients, sender, content or msg, subject, attachments=attachments))
def sendmail_to_system_managers(subject, content):
send(get_email(get_system_managers(), None, content, subject))
@ -34,7 +34,6 @@ def get_contact_list():
def get_system_managers():
return frappe.db.sql_list("""select parent FROM tabUserRole
WHERE role='System Manager'
AND parent!='Administrator'
AND parent IN
(SELECT email FROM tabUser WHERE enabled=1)""")
WHERE role='System Manager'
AND parent!='Administrator'
AND parent IN (SELECT email FROM tabUser WHERE enabled=1)""")

View file

@ -6,7 +6,7 @@ import frappe
import HTMLParser
import urllib
from frappe import msgprint, throw, _
from frappe.email.smtp import SMTPServer
from frappe.email.smtp import SMTPServer, get_outgoing_email_account
from frappe.email.email_body import get_email, get_formatted_html
from frappe.email.html2text import html2text
from frappe.utils import cint, get_url, nowdate
@ -57,7 +57,7 @@ def send(recipients=None, sender=None, doctype='User', email_field='email',
if not recipients: recipients = []
if not sender or sender == "Administrator":
sender = frappe.db.get_value('Outgoing Email Settings', None, 'auto_email_id')
sender = get_outgoing_email_account().email_id
check_bulk_limit(len(recipients))
formatted = get_formatted_html(subject, message)

View file

@ -26,7 +26,7 @@
},
{
"description": "Check this if this is a global email id like \"sales@yourcompany.com\"",
"fieldname": "global",
"fieldname": "is_global",
"fieldtype": "Check",
"label": "Global",
"permlevel": 0,
@ -62,15 +62,15 @@
},
{
"allow_on_submit": 0,
"default": "1",
"default": "",
"description": "Check this to pull emails from your mailbox",
"fieldname": "enabled",
"fieldname": "enable_incoming",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Enabled",
"label": "Enable Incoming",
"no_copy": 0,
"permlevel": 0,
"precision": "",
@ -125,7 +125,7 @@
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
@ -151,20 +151,20 @@
{
"allow_on_submit": 0,
"description": "e.g. pop.gmail.com",
"fieldname": "pop3_mail_server",
"fieldname": "pop3_server",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "POP3 Mail Server",
"label": "POP3 Server",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
@ -204,6 +204,7 @@
"precision": ""
},
{
"default": "1",
"fieldname": "enable_outgoing",
"fieldtype": "Check",
"label": "Enable Outgoing",
@ -337,6 +338,20 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"fieldname": "set_footer",
"fieldtype": "Section Break",
"label": "Set Footer",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "footer",
"fieldtype": "Text Editor",
"label": "Footer",
"permlevel": 0,
"precision": ""
}
],
"hide_heading": 0,
@ -347,7 +362,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"modified": "2014-09-11 15:42:06.931247",
"modified": "2014-09-15 12:01:38.649639",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Account",

View file

@ -2,7 +2,7 @@
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import frappe, os
from frappe import _
from frappe.model.document import Document
from frappe.utils import validate_email_add, cint
@ -10,24 +10,37 @@ from frappe.email.smtp import SMTPServer
from frappe.email.receive import POP3Server, Email
class EmailAccount(Document):
def autoname(self):
if not self.email_account_name:
self.email_account_name = self.email_id.split("@", 1)[0]\
.replace("_", " ").replace(".", " ").replace("-", " ").title()
if self.service:
self.email_account_name = self.email_account_name + " " + self.service
self.name = self.email_account_name
def validate(self):
if self.email_id and not validate_email_add(self.email_id):
frappe.throw(_("{0} is not a valid email id").format(self.email_id),
frappe.InvalidEmailAddressError)
self.there_must_be_atleast_one_default()
self.check_smtp()
self.append_to_must_have_subject_and_status()
if self.enabled:
if frappe.local.flags.in_patch or frappe.local.flags.in_test:
return
if self.enable_incoming:
self.get_pop3()
self.check_smtp()
def on_update(self):
self.there_must_be_only_one_default()
def there_must_be_atleast_one_default(self):
if not frappe.db.get_value("Email Account", {"is_default": 1}):
if not self.is_default:
if not self.is_default and (self.is_global and self.enable_outgoing):
self.is_default = 1
frappe.msgprint(_("Setting as Default"))
@ -42,15 +55,13 @@ class EmailAccount(Document):
email_account.is_default = 0
email_account.save()
def append_to_must_have_subject_and_status(self):
if self.append_to:
meta = frappe.get_meta(self.append_to)
if not (meta.has_field("subject") and meta.has_field("status")):
frappe.throw(_("Append To DocType must have fields 'Subject' and 'Status'"))
def check_smtp(self):
if self.enable_outgoing and self.smtp_server \
and not frappe.local.flags.in_patch:
if not self.smtp_server:
frappe.throw(_("{0} is required").format("SMTP Server"))
SMTPServer(login = self.email_id,
password = self.password,
server = self.smtp_server,
@ -60,20 +71,26 @@ class EmailAccount(Document):
def get_pop3(self):
args = {
"host": self.smtp_server,
"host": self.pop3_server,
"use_ssl": self.use_ssl,
"username": self.email_id,
"password": self.password
}
pop3 = POP3Server(args)
if not self.pop3_server:
frappe.throw(_("{0} is required").format("POP3 Server"))
pop3 = POP3Server(frappe._dict(args))
pop3.connect()
return pop3
def receive(self):
if self.enabled:
pop3 = self.get_pop3()
incoming_mails = pop3.get_messages()
if self.enable_incoming:
if frappe.local.flags.in_test:
incoming_mails = self.get_test_mails()
else:
pop3 = self.get_pop3()
incoming_mails = pop3.get_messages()
for raw in incoming_mails:
email = Email(raw)
@ -85,7 +102,7 @@ class EmailAccount(Document):
"sent_or_received": "Received",
"sender_full_name": email.from_real_name,
"sender": email.from_email,
"recipients": email.get("To"),
"recipients": email.mail.get("To"),
"email_account": self.name
})
@ -96,8 +113,11 @@ class EmailAccount(Document):
# save attachments
email.save_attachments_in_doc(communication)
if self.enable_auto_reply:
self.send_auto_reply(communication)
def set_thread(self, communication, email):
in_reply_to = (email.get("In-Reply-To") or "").strip(" <>")
in_reply_to = (email.mail.get("In-Reply-To") or "").strip(" <>")
parent = None
if in_reply_to:
if "@" in in_reply_to:
@ -112,11 +132,15 @@ class EmailAccount(Document):
parent = frappe.get_doc(parent.reference_doctype,
parent.reference_name)
if not parent and self.append_to:
# no parent found, but must be tagged
# insert parent type doc
parent = self.new_doc(self.append_to)
parent.subject = email.subject
if parent.meta.get_field("subject"):
parent.subject = email.subject
parent.ignore_mandatory = True
parent.insert(ignore_permissions=True)
@ -126,3 +150,23 @@ class EmailAccount(Document):
if parent:
communication.reference_doctype = parent.doctype
communication.reference_name = parent.name
def send_auto_reply(self, communication):
if self.auto_reply_message:
frappe.sendmail(recipients = [communication.from_email],
sender = self.email_id,
subject = _("Re: ") + communication.subject,
content = self.auto_reply_message or\
frappe.render_template("templates/emails/auto_reply.html", {}),
bulk=True)
def get_test_mails(self):
incoming_mails = []
with open(os.path.join(os.path.dirname(__file__), "test_mails", "incoming-1.raw"), "r") as f:
incoming_mails.append(f.read())
return incoming_mails
def sync_emails(self):
for email_account in frappe.get_list("Email Account", filters={"enable_incoming": 1}):
frappe.tasks.pull_from_email_account.delay(frappe.local.site, email_account.name)

View file

@ -6,5 +6,22 @@ import unittest
test_records = frappe.get_test_records('Email Account')
from frappe.core.doctype.communication.communication import make
class TestEmailAccount(unittest.TestCase):
pass
def test_incoming(self):
frappe.db.sql("delete from tabCommunication where sender='test_sender@example.com'")
email_account = frappe.get_doc("Email Account", "_Test Email Account 1")
email_account.receive()
comm = frappe.get_doc("Communication", {"sender": "test_sender@example.com"})
self.assertTrue("test_receiver@example.com" in comm.recipients)
def test_outgoing(self):
make(subject = "Test", content="test content", recipients="test_receiver@example.com",
send_email=True)
self.assertTrue(frappe.flags.sent_mail)

View file

@ -1,6 +1,18 @@
[
{
"is_default": 1,
"is_global": 1,
"doctype": "Email Account",
"name": "_Test Email Account 1"
"email_account_name": "_Test Email Account 1",
"enable_outgoing": 1,
"smtp_server": "test.example.com",
"email_id": "test@example.com",
"password": "password",
"add_signature": 1,
"signature": "\nBest Wishes\nTest Signature",
"enable_auto_reply": 1,
"auto_reply_message": "",
"enable_incoming": 1,
"pop3_server": "pop.test.example.com"
}
]

View file

@ -1,111 +0,0 @@
{
"allow_copy": 1,
"creation": "2014-03-03 19:48:01",
"description": "Email Settings for Outgoing and Incoming Emails.",
"docstatus": 0,
"doctype": "DocType",
"fields": [
{
"fieldname": "enabled",
"fieldtype": "Check",
"label": "Enabled",
"permlevel": 0
},
{
"depends_on": "eval:doc.enabled",
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"label": "Server & Credentials",
"permlevel": 0
},
{
"description": "SMTP Server (e.g. smtp.gmail.com)",
"fieldname": "mail_server",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Outgoing Mail Server",
"permlevel": 0
},
{
"description": "<a href=\"https://en.wikipedia.org/wiki/Transport_Layer_Security\" target=\"_blank\">[?]</a>",
"fieldname": "use_ssl",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Use TLS",
"permlevel": 0
},
{
"description": "If non standard port (e.g. 587)",
"fieldname": "mail_port",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Port",
"permlevel": 0
},
{
"fieldname": "cb0",
"fieldtype": "Column Break",
"permlevel": 0
},
{
"description": "Set Login and Password if authentication is required.",
"fieldname": "mail_login",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Login Id",
"permlevel": 0
},
{
"description": "Check this if you want to send emails as this id only (in case of restriction by your email provider).",
"fieldname": "always_use_login_id_as_sender",
"fieldtype": "Check",
"label": "Always use above Login Id as sender",
"permlevel": 0
},
{
"fieldname": "mail_password",
"fieldtype": "Password",
"label": "Mail Password",
"permlevel": 0
},
{
"description": "System generated mails will be sent from this email id.",
"fieldname": "auto_email_id",
"fieldtype": "Data",
"label": "Auto Email Id",
"permlevel": 0
},
{
"fieldname": "section_break_15",
"fieldtype": "Section Break",
"label": "Email Footer",
"permlevel": 0
},
{
"default": "<div style=\"padding: 7px; text-align: right; color: #888\"><small>Sent via \n\t<a style=\"color: #888\" href=\"http://frappe.io\">Frappe</a></div>",
"fieldname": "footer",
"fieldtype": "Text Editor",
"label": "",
"permlevel": 0,
"reqd": 0
}
],
"icon": "icon-cog",
"idx": 1,
"in_create": 1,
"issingle": 1,
"modified": "2014-07-17 08:08:00.483392",
"modified_by": "Administrator",
"module": "Email",
"name": "Outgoing Email Settings",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"permlevel": 0,
"read": 1,
"role": "System Manager",
"write": 1
}
]
}

View file

@ -1,32 +0,0 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _, throw
from frappe.utils import validate_email_add
from frappe.model.document import Document
class OutgoingEmailSettings(Document):
def validate(self):
if self.auto_email_id and not validate_email_add(self.auto_email_id):
throw(_("{0} is not a valid email id").format(self.auto_email_id), frappe.InvalidEmailAddressError)
if self.mail_server and not frappe.local.flags.in_patch:
from frappe.utils import cint
from frappe.email.smtp import SMTPServer
smtpserver = SMTPServer(login = self.mail_login,
password = self.mail_password,
server = self.mail_server,
port = cint(self.mail_port),
use_ssl = cint(self.use_ssl)
)
# exceptions are handled in session connect
sess = smtpserver.sess
def get_mail_footer():
return frappe.db.get_value("Outgoing Email Settings", "Outgoing Email Settings", "footer") or ""

View file

@ -3,19 +3,20 @@
from __future__ import unicode_literals
import frappe
from frappe import msgprint, throw, _
from frappe.utils import scrub_urls, get_url
from frappe import throw, _
from frappe.utils.pdf import get_pdf
from frappe.email.smtp import get_outgoing_email_account
from frappe.utils import get_url, scrub_urls
import email.utils
from markdown2 import markdown
def get_email(recipients, sender='', msg='', subject='[No Subject]',
text_content = None, footer=None, print_html=None, formatted=None, attachments=None):
text_content = None, footer=None, print_html=None, formatted=None, attachments=None,
content=None):
"""send an html email as multipart with attachments and all"""
emailobj = EMail(sender, recipients, subject)
msg = markdown(msg)
emailobj.set_html(msg, text_content, footer=footer, print_html=print_html, formatted=formatted)
msg = markdown(content or msg)
emailobj.set_html(content or msg, text_content, footer=footer, print_html=print_html, formatted=formatted)
if isinstance(attachments, dict):
attachments = [attachments]
@ -166,14 +167,7 @@ class EMail:
return email
if not self.sender:
self.sender = frappe.db.get_value('Outgoing Email Settings', None,
'auto_email_id') or frappe.conf.get('auto_email_id') or None
if not self.sender:
msg = _("Please specify 'Auto Email Id' in Setup > Outgoing Email Settings")
msgprint(msg)
if not "expires_on" in frappe.conf:
msgprint(_("Alternatively, you can also specify 'auto_email_id' in site_config.json"))
raise frappe.ValidationError, msg
self.sender = get_outgoing_email_account().email_id
self.sender = _validate(self.sender)
self.reply_to = _validate(self.reply_to)
@ -223,11 +217,16 @@ def get_footer(footer=None):
"""append a footer (signature)"""
footer = footer or ""
# hooks
for f in frappe.get_hooks("mail_footer"):
# mail_footer could be a function that returns a value
mail_footer = frappe.get_attr(f)
footer += (mail_footer if isinstance(mail_footer, basestring) else mail_footer())
email_account = get_outgoing_email_account()
if email_account.add_signature and email_account.signature:
footer += email_account.signature
if email_account.footer:
footer += email_account.footer
else:
for default_mail_footer in frappe.get_hooks("default_mail_footer"):
footer += default_mail_footer
footer += "<!--unsubscribe link here-->"

View file

@ -3,8 +3,9 @@
from __future__ import unicode_literals
import time
import poplib
import _socket, poplib
import frappe
from frappe import _
from frappe.utils import extract_email_id, convert_utc_to_user_timezone, now, cint
from frappe.utils.scheduler import log
@ -37,11 +38,11 @@ class POP3Server:
self.pop.user(self.settings.username)
self.pop.pass_(self.settings.password)
except _socket.error, e:
except _socket.error:
# Invalid mail server -- due to refusing connection
frappe.msgprint(_('Invalid Mail Server. Please rectify and try again.'))
raise
except poplib.error_proto, e:
except poplib.error_proto:
frappe.msgprint(_('Invalid User Name or Support Password. Please rectify and try again.'))
raise
@ -132,7 +133,7 @@ class POP3Server:
if not incoming_mail:
try:
# retrieve headers
incoming_mail = EMail(b'\n'.join(self.pop.top(msg_num, 5)[1]))
incoming_mail = Email(b'\n'.join(self.pop.top(msg_num, 5)[1]))
except:
pass
@ -232,7 +233,7 @@ class Email:
from frappe.utils.file_manager import save_file, MaxFileSizeReachedError
for attachment in self.attachments:
try:
fid = save_file(attachment['filename'], attachment['content'],
save_file(attachment['filename'], attachment['content'],
doc.doctype, doc.name)
except MaxFileSizeReachedError:
# WARNING: bypass max file size exception

View file

@ -15,6 +15,10 @@ def send(email, as_bulk=False):
frappe.msgprint(_("Emails are muted"))
return
if frappe.flags.in_test:
frappe.flags.sent_mail = email.as_string()
return
try:
smtpserver = SMTPServer()
if hasattr(smtpserver, "always_use_login_id_as_sender") and \
@ -33,34 +37,57 @@ def send(email, as_bulk=False):
frappe.msgprint(_("Invalid recipient address"))
raise
def get_outgoing_email_account(raise_exception_not_set=True):
if not getattr(frappe.local, "outgoing_email_account", None):
email_account = frappe.db.get_value("Email Account", {
"owner": frappe.session.user, "enable_outgoing": 1})
if not email_account:
email_account = frappe.db.get_value('Email Account', {"is_default": 1})
if not email_account and not raise_exception_not_set:
return None
if not email_account:
frappe.throw(_("Please setup default Email Account from Setup > Email > Email Account"))
frappe.local.outgoing_email_account = frappe.get_doc("Email Account", email_account)
return frappe.local.outgoing_email_account
class SMTPServer:
def __init__(self, login=None, password=None, server=None, port=None, use_ssl=None):
# get defaults from mail settings
try:
self.email_settings = frappe.get_doc('Outgoing Email Settings', 'Outgoing Email Settings')
except frappe.DoesNotExistError:
self.email_settings = None
self._sess = None
self.email_account = None
if server:
self.server = server
self.port = port
self.use_ssl = cint(use_ssl)
self.login = login
self.password = password
elif self.email_settings and cint(self.email_settings.enabled):
self.server = self.email_settings.mail_server
self.port = self.email_settings.mail_port
self.use_ssl = cint(self.email_settings.use_ssl)
self.login = self.email_settings.mail_login
self.password = self.email_settings.mail_password
self.always_use_login_id_as_sender = self.email_settings.always_use_login_id_as_sender
else:
self.server = frappe.conf.get("mail_server") or ""
self.port = frappe.conf.get("mail_port") or None
self.use_ssl = cint(frappe.conf.get("use_ssl") or 0)
self.login = frappe.conf.get("mail_login") or ""
self.password = frappe.conf.get("mail_password") or ""
self.setup_from_user_or_default_outgoing()
# from config
if not self.server:
self.server = frappe.conf.get("mail_server") or ""
self.port = frappe.conf.get("mail_port") or None
self.use_ssl = cint(frappe.conf.get("use_ssl") or 0)
self.login = frappe.conf.get("mail_login") or ""
self.password = frappe.conf.get("mail_password") or ""
def setup_from_user_or_default_outgoing(self):
self.email_account = get_outgoing_email_account(raise_exception_not_set=False)
if self.email_account:
self.server = self.email_account.smtp_server
self.login = self.email_account.email_id
self.password = self.email_account.password
self.port = self.email_account.smtp_port
self.use_ssl = self.email_account.use_tls
@property
def sess(self):
@ -70,7 +97,7 @@ class SMTPServer:
# check if email server specified
if not self.server:
err_msg = _('Outgoing Mail Server not specified')
err_msg = _('Email Account not setup. Please create a new Email Account from Setup > Email > Email Account')
frappe.msgprint(err_msg)
raise frappe.OutgoingEmailError, err_msg

View file

@ -83,7 +83,10 @@ doc_events = {
}
scheduler_events = {
"all": ["frappe.email.bulk.flush"],
"all": [
"frappe.email.bulk.flush",
"frappe.tasks.pull_emails"
],
"daily": [
"frappe.email.bulk.clear_outbox",
"frappe.core.doctype.notification_count.notification_count.clear_notifications",
@ -95,5 +98,3 @@ scheduler_events = {
"frappe.website.doctype.website_group.website_group.clear_event_cache"
]
}
mail_footer = "frappe.email.doctype.outgoing_email_settings.outgoing_email_settings.get_mail_footer"

View file

@ -136,8 +136,8 @@ class Document(BaseDocument):
self._set_defaults()
self._set_docstatus_user_and_timestamp()
self.check_if_latest()
self.set_new_name()
self.run_method("before_insert")
self.set_new_name()
self.set_parent_in_children()
self.set("__in_insert", True)

View file

@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe
def execute():
return
frappe.reload_doc("core", "doctype", "outgoing_email_settings")
if (frappe.db.get_value("Outgoing Email Settings", "Outgoing Email Settings", "mail_server") or "").strip():
frappe.db.set_value("Outgoing Email Settings", "Outgoing Email Settings", "enabled", 1)

View file

@ -15,12 +15,11 @@ def execute():
pass
else:
# if someone has already configured in Outgoing Email Settings
outgoing_email_settings = frappe.db.get_singles_dict("Outgoing Email Settings")
if "send_print_as_pdf" in outgoing_email_settings:
# outgoing_email_settings = frappe.db.get_singles_dict("Outgoing Email Settings")
if False: # "send_print_as_pdf" in outgoing_email_settings:
print_settings.send_print_as_pdf = outgoing_email_settings.send_print_as_pdf
print_settings.pdf_page_size = outgoing_email_settings.pdf_page_size
else:
print_settings.send_print_as_pdf = 1
print_settings.save()

View file

@ -7,7 +7,7 @@ def execute():
("desk", ("event", "event_role", "event_user", "todo", "feed",
"note", "note_user")),
("email", ("bulk_email", "email_alert", "email_alert_recipient",
"outgoing_email_settings", "standard_reply")),
"standard_reply")),
("geo", ("country", "currency")),
("print", ("letter_head", "print_format", "print_settings"))
)

View file

@ -132,7 +132,7 @@ frappe.ui.form.Comments = Class.extend({
}
// icon centering -- pixed perfect
if(in_list(["Comment", "Email"], c.comment_type)) {
if(in_list(["Comment", "Email", "Assignment Completed"], c.comment_type)) {
c.padding = "padding-left: 8px;";
} else if(in_list(["Created"], c.comment_type)) {
c.padding = "padding-left: 9px;";

View file

@ -108,3 +108,13 @@ def enqueue_events_for_site(site):
enqueue_events(site)
finally:
frappe.destroy()
@celery_task()
def pull_from_email_account(site, email_account):
try:
frappe.init(site=site)
frappe.connect(site=site)
email_account = frappe.get_doc("Email Account", email_account)
email_account.receive()
finally:
frappe.destroy()

View file

@ -45,7 +45,6 @@ def add_comment(args=None):
owner = frappe.db.get_value(comment.comment_doctype, comment.comment_docname, "owner")
recipients = list(set(commentors if owner=="Administrator" else (commentors + [owner])))
from frappe.email.bulk import send
send(recipients=recipients,
doctype='Comment',

View file

@ -115,8 +115,6 @@ def _add_test(path, filename, verbose, test_suite=None):
test_suite.addTest(unittest.TestLoader().loadTestsFromModule(module))
def make_test_records(doctype, verbose=0, force=False):
frappe.flags.mute_emails = True
if not frappe.db:
frappe.connect()