diff --git a/frappe/__init__.py b/frappe/__init__.py index 83c3506efa..636c06c52c 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -274,7 +274,7 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message as_markdown=False, bulk=False, reference_doctype=None, reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, attachments=None, content=None, doctype=None, name=None, reply_to=None, - cc=(), message_id=None, as_bulk=False): + cc=(), message_id=None, as_bulk=False, send_after=None): """Send email using user's default **Email Account** or global default **Email Account**. @@ -291,6 +291,7 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message :param attachments: List of attachments. :param reply_to: Reply-To email id. :param message_id: Used for threading. If a reply is received to this email, Message-Id is sent back as In-Reply-To in received email. + :param send_after: Send after the given datetime. """ if bulk or as_bulk: @@ -299,7 +300,7 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message subject=subject, message=content or message, reference_doctype = doctype or reference_doctype, reference_name = name or reference_name, unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message, - attachments=attachments, reply_to=reply_to, cc=cc, message_id=message_id) + attachments=attachments, reply_to=reply_to, cc=cc, message_id=message_id, send_after=send_after) else: import frappe.email if as_markdown: diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index d862aa3475..998d5918d2 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -161,7 +161,8 @@ class User(Document): self.db_set("reset_password_key", key) link = get_url("/update-password?key=" + key) - self.send_login_mail(_("Verify Your Account"), "templates/emails/new_user.html", {"link": link}) + self.send_login_mail(_("Verify Your Account"), "templates/emails/new_user.html", + {"link": link}) def send_login_mail(self, subject, template, add_args): """send mail with login details""" diff --git a/frappe/email/bulk.py b/frappe/email/bulk.py index bba3f86d3c..f2f317c3ce 100644 --- a/frappe/email/bulk.py +++ b/frappe/email/bulk.py @@ -9,13 +9,13 @@ from frappe.email.smtp import SMTPServer, get_outgoing_email_account from frappe.email.email_body import get_email, get_formatted_html from frappe.utils.verified_command import get_signed_params, verify_request from html2text import html2text -from frappe.utils import get_url, nowdate, encode +from frappe.utils import get_url, nowdate, encode, now_datetime class BulkLimitCrossedError(frappe.ValidationError): pass def send(recipients=None, sender=None, subject=None, message=None, reference_doctype=None, reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None, - attachments=None, reply_to=None, cc=(), message_id=None): + attachments=None, reply_to=None, cc=(), message_id=None, send_after=None): """Add email to sending queue (Bulk Email) :param recipients: List of recipients. @@ -29,15 +29,17 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc :param attachments: Attachments to be sent. :param reply_to: Reply to be captured here (default inbox) :param message_id: Used for threading. If a reply is received to this email, Message-Id is sent back as In-Reply-To in received email. + :param send_after: Send this email after the given datetime. """ - - if not unsubscribe_method: unsubscribe_method = "/api/method/frappe.email.bulk.unsubscribe" if not recipients: return + if isinstance(recipients, basestring): + recipients = recipients.split(",") + if not sender or sender == "Administrator": email_account = get_outgoing_email_account() sender = email_account.get("sender") or email_account.email_id @@ -51,8 +53,11 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc except HTMLParser.HTMLParseError: text_content = "See html attachment" - unsubscribed = [d.email for d in frappe.db.get_all("Email Unsubscribe", "email", - {"reference_doctype": reference_doctype, "reference_name": reference_name})] + if reference_doctype and reference_name: + unsubscribed = [d.email for d in frappe.db.get_all("Email Unsubscribe", "email", + {"reference_doctype": reference_doctype, "reference_name": reference_name})] + else: + unsubscribed = [] for email in filter(None, list(set(recipients))): if email not in unsubscribed: @@ -70,11 +75,11 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc email_text_context += "\n" + _("Unsubscribe link: {0}").format(unsubscribe_url) add(email, sender, subject, email_content, email_text_context, reference_doctype, reference_name, attachments, reply_to, - cc, message_id) + cc, message_id, send_after) def add(email, sender, subject, formatted, text_content=None, reference_doctype=None, reference_name=None, attachments=None, reply_to=None, - cc=(), message_id=None): + cc=(), message_id=None, send_after=None): """add to bulk mail queue""" e = frappe.new_doc('Bulk Email') e.sender = sender @@ -95,6 +100,7 @@ def add(email, sender, subject, formatted, text_content=None, e.reference_doctype = reference_doctype e.reference_name = reference_name + e.send_after = send_after e.insert(ignore_permissions=True) def check_bulk_limit(recipients): @@ -169,7 +175,8 @@ def flush(from_test=False): for i in xrange(500): email = frappe.db.sql("""select * from `tabBulk Email` where - status='Not Sent' order by creation asc limit 1 for update""", as_dict=1) + status='Not Sent' and ifnull(send_after, "2000-01-01") > %s + order by creation asc limit 1 for update""", now_datetime(), as_dict=1) if email: email = email[0] else: diff --git a/frappe/email/doctype/bulk_email/bulk_email.json b/frappe/email/doctype/bulk_email/bulk_email.json index 37be147dce..bff64320cd 100644 --- a/frappe/email/doctype/bulk_email/bulk_email.json +++ b/frappe/email/doctype/bulk_email/bulk_email.json @@ -59,19 +59,27 @@ "permlevel": 0, "read_only": 1, "reqd": 0 + }, + { + "fieldname": "send_after", + "fieldtype": "Datetime", + "label": "Send After", + "no_copy": 1, + "permlevel": 0, + "precision": "", + "read_only": 1 } ], "icon": "icon-envelope", "idx": 1, "in_create": 1, - "modified": "2015-03-31 15:10:57.553836", + "modified": "2015-04-01 10:00:20.892939", "modified_by": "Administrator", "module": "Email", "name": "Bulk Email", "owner": "Administrator", "permissions": [ { - "delete": 1, "email": 1, "permlevel": 0, "print": 1, diff --git a/frappe/public/js/frappe/router.js b/frappe/public/js/frappe/router.js index 0b9462cd56..0b728686db 100644 --- a/frappe/public/js/frappe/router.js +++ b/frappe/public/js/frappe/router.js @@ -103,7 +103,7 @@ frappe.set_route = function() { window.location.hash = route; // Set favicon (app.js) - frappe.app.set_favicon(); + frappe.app.set_favicon && frappe.app.set_favicon(); } frappe.set_re_route = function() { diff --git a/frappe/utils/__init__.py b/frappe/utils/__init__.py index f447ac92bc..319a5bcc26 100644 --- a/frappe/utils/__init__.py +++ b/frappe/utils/__init__.py @@ -30,8 +30,11 @@ def getCSVelement(v): return '"'+v+'"' else: return v or '' -def get_fullname(user): +def get_fullname(user=None): """get the full name (first name + last name) of the user from User""" + if not user: + user = frappe.session.user + if not hasattr(frappe.local, "fullnames"): frappe.local.fullnames = {}