diff --git a/frappe/__init__.py b/frappe/__init__.py
index 781f850a86..d526a24bcc 100644
--- a/frappe/__init__.py
+++ b/frappe/__init__.py
@@ -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 = []
diff --git a/frappe/cli.py b/frappe/cli.py
index 9c3f1055d4..0fa3c515d9 100755
--- a/frappe/cli.py
+++ b/frappe/cli.py
@@ -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:
diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py
index 7cf5a48edd..8bdce2c506 100644
--- a/frappe/core/doctype/communication/communication.py
+++ b/frappe/core/doctype/communication/communication.py
@@ -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 = "
" + 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)
diff --git a/frappe/email/__init__.py b/frappe/email/__init__.py
index c77b420eeb..08c892684e 100644
--- a/frappe/email/__init__.py
+++ b/frappe/email/__init__.py
@@ -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)""")
diff --git a/frappe/email/bulk.py b/frappe/email/bulk.py
index 1b5b970ea9..4f2727102f 100644
--- a/frappe/email/bulk.py
+++ b/frappe/email/bulk.py
@@ -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)
diff --git a/frappe/email/doctype/email_account/email_account.json b/frappe/email/doctype/email_account/email_account.json
index 9c81435c9f..65c81023e9 100644
--- a/frappe/email/doctype/email_account/email_account.json
+++ b/frappe/email/doctype/email_account/email_account.json
@@ -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",
diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py
index ca70aedc66..50e2048c85 100644
--- a/frappe/email/doctype/email_account/email_account.py
+++ b/frappe/email/doctype/email_account/email_account.py
@@ -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)
diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py
index c077005649..28e30fbcb5 100644
--- a/frappe/email/doctype/email_account/test_email_account.py
+++ b/frappe/email/doctype/email_account/test_email_account.py
@@ -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)
+
+
diff --git a/frappe/email/doctype/email_account/test_records.json b/frappe/email/doctype/email_account/test_records.json
index 9213e730c2..1cec4dc1be 100644
--- a/frappe/email/doctype/email_account/test_records.json
+++ b/frappe/email/doctype/email_account/test_records.json
@@ -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"
}
]
diff --git a/frappe/email/doctype/outgoing_email_settings/__init__.py b/frappe/email/doctype/outgoing_email_settings/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/frappe/email/doctype/outgoing_email_settings/outgoing_email_settings.json b/frappe/email/doctype/outgoing_email_settings/outgoing_email_settings.json
deleted file mode 100644
index 717367a977..0000000000
--- a/frappe/email/doctype/outgoing_email_settings/outgoing_email_settings.json
+++ /dev/null
@@ -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": "[?]",
- "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": "",
- "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
- }
- ]
-}
diff --git a/frappe/email/doctype/outgoing_email_settings/outgoing_email_settings.py b/frappe/email/doctype/outgoing_email_settings/outgoing_email_settings.py
deleted file mode 100644
index fda44b6154..0000000000
--- a/frappe/email/doctype/outgoing_email_settings/outgoing_email_settings.py
+++ /dev/null
@@ -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 ""
diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py
index faceb84540..2ef2f45ef9 100644
--- a/frappe/email/email_body.py
+++ b/frappe/email/email_body.py
@@ -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 += ""
diff --git a/frappe/email/receive.py b/frappe/email/receive.py
index 41c9dc0829..250a0bf473 100644
--- a/frappe/email/receive.py
+++ b/frappe/email/receive.py
@@ -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
diff --git a/frappe/email/smtp.py b/frappe/email/smtp.py
index 6c04e0cb5f..fcc177e3e7 100644
--- a/frappe/email/smtp.py
+++ b/frappe/email/smtp.py
@@ -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
diff --git a/frappe/hooks.py b/frappe/hooks.py
index fadf9b760b..6d4a6ff3a0 100644
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -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"
diff --git a/frappe/model/document.py b/frappe/model/document.py
index 0097d392d6..814f435720 100644
--- a/frappe/model/document.py
+++ b/frappe/model/document.py
@@ -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)
diff --git a/frappe/patches/v4_1/enable_outgoing_email_settings.py b/frappe/patches/v4_1/enable_outgoing_email_settings.py
index 472a8109e3..fadf2b85e8 100644
--- a/frappe/patches/v4_1/enable_outgoing_email_settings.py
+++ b/frappe/patches/v4_1/enable_outgoing_email_settings.py
@@ -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)
diff --git a/frappe/patches/v4_1/enable_print_as_pdf.py b/frappe/patches/v4_1/enable_print_as_pdf.py
index 877a2b61f7..5d96c0030b 100644
--- a/frappe/patches/v4_1/enable_print_as_pdf.py
+++ b/frappe/patches/v4_1/enable_print_as_pdf.py
@@ -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()
diff --git a/frappe/patches/v5_0/v4_to_v5.py b/frappe/patches/v5_0/v4_to_v5.py
index 666991e3ac..d70c6abef7 100644
--- a/frappe/patches/v5_0/v4_to_v5.py
+++ b/frappe/patches/v5_0/v4_to_v5.py
@@ -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"))
)
diff --git a/frappe/public/js/frappe/form/footer/comments.js b/frappe/public/js/frappe/form/footer/comments.js
index dfc0400ba3..a95af2bf61 100644
--- a/frappe/public/js/frappe/form/footer/comments.js
+++ b/frappe/public/js/frappe/form/footer/comments.js
@@ -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;";
diff --git a/frappe/tasks.py b/frappe/tasks.py
index 1c9dcf0e5e..bd9add86c8 100644
--- a/frappe/tasks.py
+++ b/frappe/tasks.py
@@ -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()
diff --git a/frappe/templates/includes/comments.py b/frappe/templates/includes/comments.py
index 2b7e1e13e2..9ca9dc6e33 100644
--- a/frappe/templates/includes/comments.py
+++ b/frappe/templates/includes/comments.py
@@ -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',
diff --git a/frappe/test_runner.py b/frappe/test_runner.py
index 7010258130..7f15c29c87 100644
--- a/frappe/test_runner.py
+++ b/frappe/test_runner.py
@@ -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()