diff --git a/frappe/__init__.py b/frappe/__init__.py index 0a4e86f42a..869dfc9b2c 100644 --- a/frappe/__init__.py +++ b/frappe/__init__.py @@ -7,7 +7,6 @@ globals attached to frappe module from __future__ import unicode_literals from werkzeug.local import Local, release_local -from functools import wraps import os, importlib, inspect, json # public @@ -252,7 +251,7 @@ def msgprint(msg, title=None, raise_exception=0, as_table=False, indicator=None, :param raise_exception: [optional] Raise given exception and show message. :param as_table: [optional] If `msg` is a list of lists, render as HTML table. """ - from utils import cstr, encode + from utils import encode out = _dict(message=msg) @@ -355,11 +354,11 @@ def get_request_header(key, default=None): return request.headers.get(key, default) def sendmail(recipients=(), sender="", subject="No Subject", message="No Message", - as_markdown=False, bulk=False, reference_doctype=None, reference_name=None, + as_markdown=False, delayed=True, 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=(), show_as_cc=(), message_id=None, in_reply_to=None, as_bulk=False, send_after=None, expose_recipients=False, - bulk_priority=1, communication=None): + cc=(), show_as_cc=(), message_id=None, in_reply_to=None, send_after=None, expose_recipients=False, + send_priority=1, communication=None): """Send email using user's default **Email Account** or global default **Email Account**. @@ -368,8 +367,8 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message :param subject: Email Subject. :param message: (or `content`) Email Content. :param as_markdown: Convert content markdown to HTML. - :param bulk: Send via scheduled email sender **Bulk Email**. Don't send immediately. - :param bulk_priority: Priority for bulk email, default 1. + :param delayed: Send via scheduled email sender **Email Queue**. Don't send immediately. Default is true + :param send_priority: Priority for Email Queue, default 1. :param reference_doctype: (or `doctype`) Append as communication to this DocType. :param reference_name: (or `name`) Append as communication to this document name. :param unsubscribe_method: Unsubscribe url with options email, doctype, name. e.g. `/api/method/unsubscribe` @@ -380,17 +379,17 @@ def sendmail(recipients=(), sender="", subject="No Subject", message="No Message :param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To. :param send_after: Send after the given datetime. :param expose_recipients: Display all recipients in the footer message - "This email was sent to" - :param communication: Communication link to be set in Bulk Email record + :param communication: Communication link to be set in Email Queue record """ - if bulk or as_bulk: - import frappe.email.bulk - frappe.email.bulk.send(recipients=recipients, sender=sender, + if delayed: + import frappe.email.queue + frappe.email.queue.send(recipients=recipients, sender=sender, 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, show_as_cc=show_as_cc, message_id=message_id, in_reply_to=in_reply_to, - send_after=send_after, expose_recipients=expose_recipients, bulk_priority=bulk_priority, communication=communication) + send_after=send_after, expose_recipients=expose_recipients, send_priority=send_priority, communication=communication) else: import frappe.email if as_markdown: @@ -918,7 +917,6 @@ def import_doc(path, ignore_links=False, ignore_insert=False, insert=False): def copy_doc(doc, ignore_no_copy=True): """ No_copy fields also get copied.""" import copy - from frappe.model import optional_fields, default_fields def remove_no_copy_fields(d): for df in d.meta.get("fields", {"no_copy": 1}): diff --git a/frappe/commands/utils.py b/frappe/commands/utils.py index d7aa6de44d..986c0ffec5 100644 --- a/frappe/commands/utils.py +++ b/frappe/commands/utils.py @@ -146,16 +146,16 @@ def execute(context, method, args=None, kwargs=None): print json.dumps(ret) -@click.command('add-bulk-email') +@click.command('add-to-email-queue') @click.argument('email') @pass_context -def add_bulk_email(context, email): - "Add an email to the Bulk Email queue" +def add_to_email_queue(context, email): + "Add an email to the Email Queue" site = get_site(context) with frappe.init_site(site): frappe.connect() kwargs = json.loads(email) - kwargs['as_bulk'] = True + kwargs['delayed'] = True frappe.sendmail(**kwargs) frappe.db.commit() @@ -408,7 +408,6 @@ def get_version(): print "{0} {1}".format(m, module.__version__) commands = [ - add_bulk_email, build, build_website, clear_cache, @@ -433,4 +432,5 @@ commands = [ sync_www, watch, _bulk_rename, + add_to_email_queue, ] diff --git a/frappe/config/core.py b/frappe/config/core.py index 6bb81b2e2d..a4a9ac860f 100644 --- a/frappe/config/core.py +++ b/frappe/config/core.py @@ -48,7 +48,7 @@ def get_data(): }, { "type": "doctype", - "name": "Bulk Email", + "name": "Email Queue", "description": _("Background Email Queue"), }, { diff --git a/frappe/core/doctype/communication/comment.py b/frappe/core/doctype/communication/comment.py index 3b8a266a99..5ac6d3053d 100644 --- a/frappe/core/doctype/communication/comment.py +++ b/frappe/core/doctype/communication/comment.py @@ -104,8 +104,7 @@ def notify_mentions(doc): recipients=recipients, sender=frappe.session.user, subject=subject, - message=message, - bulk=True + message=message ) def get_comments_from_parent(doc): diff --git a/frappe/core/doctype/communication/communication.py b/frappe/core/doctype/communication/communication.py index f6e6a1c176..1e43780b2f 100644 --- a/frappe/core/doctype/communication/communication.py +++ b/frappe/core/doctype/communication/communication.py @@ -161,7 +161,7 @@ class Communication(Document): def notify(self, print_html=None, print_format=None, attachments=None, recipients=None, cc=None, fetched_from_email_account=False): - """Calls a delayed task 'sendmail' that enqueus email in Bulk Email queue + """Calls a delayed task 'sendmail' that enqueus email in Email Queue queue :param print_html: Send given value as HTML attachment :param print_format: Attach print format of parent document @@ -193,9 +193,9 @@ class Communication(Document): frappe.local.flags.commit = True def set_delivery_status(self, commit=False): - '''Look into the status of Bulk Email linked to this Communication and set the Delivery Status of this Communication''' + '''Look into the status of Email Queue linked to this Communication and set the Delivery Status of this Communication''' delivery_status = None - status_counts = Counter(frappe.db.sql_list('''select status from `tabBulk Email` where communication=%s''', self.name)) + status_counts = Counter(frappe.db.sql_list('''select status from `tabEmail Queue` where communication=%s''', self.name)) if status_counts.get('Not Sent') or status_counts.get('Sending'): delivery_status = 'Sending' diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 3edb4858f7..9dc48edf41 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -8,7 +8,7 @@ from email.utils import formataddr, parseaddr from frappe.utils import (get_url, get_formatted_email, cint, validate_email_add, split_emails, time_diff_in_seconds) from frappe.utils.file_manager import get_file -from frappe.email.bulk import check_bulk_limit +from frappe.email.queue import check_email_limit from frappe.utils.scheduler import log import frappe.email.smtp import MySQLdb @@ -89,7 +89,7 @@ def validate_email(doc): def notify(doc, print_html=None, print_format=None, attachments=None, recipients=None, cc=None, fetched_from_email_account=False): - """Calls a delayed task 'sendmail' that enqueus email in Bulk Email queue + """Calls a delayed task 'sendmail' that enqueus email in Email Queue queue :param print_html: Send given value as HTML attachment :param print_format: Attach print format of parent document @@ -109,7 +109,7 @@ def notify(doc, print_html=None, print_format=None, attachments=None, doc._notify(print_html=print_html, print_format=print_format, attachments=attachments, recipients=recipients, cc=cc) else: - check_bulk_limit(list(set(doc.sent_email_addresses))) + check_email_limit(list(set(doc.sent_email_addresses))) enqueue(sendmail, queue="default", timeout=300, event="sendmail", communication_name=doc.name, print_html=print_html, print_format=print_format, attachments=attachments, @@ -133,7 +133,7 @@ def _notify(doc, print_html=None, print_format=None, attachments=None, attachments=doc.attachments, message_id=doc.name, unsubscribe_message=_("Leave this conversation"), - bulk=True, + delayed=True, communication=doc.name ) @@ -191,7 +191,7 @@ def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None) :param print_html: Send given value as HTML attachment. :param print_format: Attach print format of parent document.""" - + view_link = frappe.utils.cint(frappe.db.get_value("Print Settings", "Print Settings", "attach_view_link")) if print_format and view_link: diff --git a/frappe/core/doctype/user/user.py b/frappe/core/doctype/user/user.py index 2e3144c3ce..4362865828 100644 --- a/frappe/core/doctype/user/user.py +++ b/frappe/core/doctype/user/user.py @@ -189,19 +189,21 @@ class User(Document): (self.first_name and " " or '') + (self.last_name or '') def password_reset_mail(self, link): - self.send_login_mail(_("Password Reset"), "templates/emails/password_reset.html", {"link": link}) + self.send_login_mail(_("Password Reset"), + "templates/emails/password_reset.html", {"link": link}, now=True) def password_update_mail(self, password): - self.send_login_mail(_("Password Update"), "templates/emails/password_update.html", {"new_password": password}) + self.send_login_mail(_("Password Update"), + "templates/emails/password_update.html", {"new_password": password}, now=True) def send_welcome_mail_to_user(self): - from frappe.utils import random_string, get_url + from frappe.utils import get_url link = self.reset_password() self.send_login_mail(_("Verify Your Account"), "templates/emails/new_user.html", {"link": link, "site_url": get_url()}) - def send_login_mail(self, subject, template, add_args): + def send_login_mail(self, subject, template, add_args, now=None): """send mail with login details""" from frappe.utils.user import get_user_fullname from frappe.utils import get_url @@ -226,7 +228,8 @@ class User(Document): sender = frappe.session.user not in STANDARD_USERS and get_formatted_email(frappe.session.user) or None frappe.sendmail(recipients=self.email, sender=sender, subject=subject, - message=frappe.get_template(template).render(args), as_bulk=self.flags.delay_emails) + message=frappe.get_template(template).render(args), + delayed=now if now!=None else self.flags.delay_emails) def a_system_manager_should_exist(self): if not self.get_other_system_managers(): @@ -607,7 +610,7 @@ def notifify_admin_access_to_system_manager(login_manager=None): ) frappe.sendmail(recipients=get_system_managers(), subject=_("Administrator Logged In"), - message=message, bulk=True) + message=message) def extract_mentions(txt): """Find all instances of @username in the string. diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py index d290b13ad4..b425715a9e 100644 --- a/frappe/desk/doctype/event/event.py +++ b/frappe/desk/doctype/event/event.py @@ -66,7 +66,7 @@ def send_event_digest(): + frappe._("Daily Event Digest is sent for Calendar Events where reminders are set.")+'
' frappe.sendmail(recipients=user.email, subject=frappe._("Upcoming Events for Today"), - content = text, bulk=True) + content = text) @frappe.whitelist() def get_events(start, end, user=None, for_reminder=False): diff --git a/frappe/desk/page/chat/chat.py b/frappe/desk/page/chat/chat.py index 73be52e1b5..011f4f8a05 100644 --- a/frappe/desk/page/chat/chat.py +++ b/frappe/desk/page/chat/chat.py @@ -131,7 +131,6 @@ def _notify(contact, txt, subject=None): "from": get_fullname(frappe.session.user), "message": txt, "link": get_url() - }), - bulk=True) + })) except frappe.OutgoingEmailError: pass diff --git a/frappe/email/doctype/bulk_email/README.md b/frappe/email/doctype/bulk_email/README.md deleted file mode 100644 index f6911250d8..0000000000 --- a/frappe/email/doctype/bulk_email/README.md +++ /dev/null @@ -1 +0,0 @@ -Emails to be sent asynchronously via the scheduler. Emails remain in Bulk Email for a month before they are deleted (to check mail quotas) \ No newline at end of file diff --git a/frappe/email/doctype/bulk_email/__init__.py b/frappe/email/doctype/bulk_email/__init__.py deleted file mode 100644 index 4dbcd0d163..0000000000 --- a/frappe/email/doctype/bulk_email/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt - -from __future__ import unicode_literals diff --git a/frappe/email/doctype/bulk_email/bulk_email.json b/frappe/email/doctype/bulk_email/bulk_email.json deleted file mode 100644 index a9a07b5694..0000000000 --- a/frappe/email/doctype/bulk_email/bulk_email.json +++ /dev/null @@ -1,307 +0,0 @@ -{ - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "hash", - "beta": 0, - "creation": "2012-08-02 15:17:28", - "custom": 0, - "description": "Bulk Email records.", - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "fields": [ - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "sender", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Sender", - "length": 0, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "recipient", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Recipient", - "length": 0, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "message", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "default": "Not Sent", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "\nNot Sent\nSending\nSent\nError\nExpired", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "error", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Error", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "reference_doctype", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Reference DocType", - "length": 0, - "no_copy": 0, - "options": "DocType", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "reference_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Reference DocName", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "communication", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Communication", - "length": 0, - "no_copy": 0, - "options": "Communication", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "send_after", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Send After", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "default": "1", - "fieldname": "priority", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Priority", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-envelope", - "idx": 1, - "in_create": 1, - "in_dialog": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2016-05-26 06:00:18.596285", - "modified_by": "Administrator", - "module": "Email", - "name": "Bulk Email", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_order": "DESC", - "track_seen": 0 -} \ No newline at end of file diff --git a/frappe/email/doctype/bulk_email/test_bulk_email.py b/frappe/email/doctype/bulk_email/test_bulk_email.py deleted file mode 100644 index 89bc1034d0..0000000000 --- a/frappe/email/doctype/bulk_email/test_bulk_email.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest - -# test_records = frappe.get_test_records('Bulk Email') - -class TestBulkEmail(unittest.TestCase): - pass diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index dd988d49ec..4c71c31dcf 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -355,8 +355,7 @@ class EmailAccount(Document): reference_name = communication.reference_name, message_id = communication.name, in_reply_to = email.mail.get("Message-Id"), # send back the Message-Id as In-Reply-To - unsubscribe_message = _("Leave this conversation"), - bulk=True) + unsubscribe_message = _("Leave this conversation")) def get_unreplied_notification_emails(self): """Return list of emails listed""" @@ -398,7 +397,7 @@ def notify_unreplied(): # if status is still open frappe.sendmail(recipients=email_account.get_unreplied_notification_emails(), content=comm.content, subject=comm.subject, doctype= comm.reference_doctype, - name=comm.reference_name, bulk=True) + name=comm.reference_name) # update flag comm.db_set("unread_notification_sent", 1) diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py index 24f97b6df3..53e405a27b 100644 --- a/frappe/email/doctype/email_account/test_email_account.py +++ b/frappe/email/doctype/email_account/test_email_account.py @@ -42,9 +42,9 @@ class TestEmailAccount(unittest.TestCase): comm = frappe.get_doc("Communication", {"sender": "test_sender@example.com"}) comm.db_set("creation", datetime.now() - timedelta(seconds = 30 * 60)) - frappe.db.sql("delete from `tabBulk Email`") + frappe.db.sql("delete from `tabEmail Queue`") notify_unreplied() - self.assertTrue(frappe.db.get_value("Bulk Email", {"reference_doctype": comm.reference_doctype, + self.assertTrue(frappe.db.get_value("Email Queue", {"reference_doctype": comm.reference_doctype, "reference_name": comm.reference_name, "status":"Not Sent"})) def test_incoming_with_attach(self): @@ -102,13 +102,13 @@ class TestEmailAccount(unittest.TestCase): make(subject = "test-mail-000", content="test mail 000", recipients="test_receiver@example.com", send_email=True, sender="test_sender@example.com") - mail = email.message_from_string(frappe.get_last_doc("Bulk Email").message) + mail = email.message_from_string(frappe.get_last_doc("Email Queue").message) self.assertTrue("test-mail-000" in mail.get("Subject")) def test_sendmail(self): frappe.flags.sent_mail = None frappe.sendmail(sender="test_sender@example.com", recipients="test_recipient@example.com", - content="test mail 001", subject="test-mail-001") + content="test mail 001", subject="test-mail-001", delayed=False) sent_mail = email.message_from_string(frappe.flags.sent_mail) self.assertTrue("test-mail-001" in sent_mail.get("Subject")) @@ -119,7 +119,7 @@ class TestEmailAccount(unittest.TestCase): content="test mail 001", subject="test-mail-002", doctype="Email Account", name="_Test Email Account 1", print_format="Standard", send_email=True) - sent_mail = email.message_from_string(frappe.get_last_doc("Bulk Email").message) + sent_mail = email.message_from_string(frappe.get_last_doc("Email Queue").message) self.assertTrue("test-mail-002" in sent_mail.get("Subject")) def test_threading(self): @@ -131,7 +131,7 @@ class TestEmailAccount(unittest.TestCase): recipients="test_receiver@example.com", sender="test@example.com", send_email=True)["name"] - sent_mail = email.message_from_string(frappe.get_last_doc("Bulk Email").message) + sent_mail = email.message_from_string(frappe.get_last_doc("Email Queue").message) with open(os.path.join(os.path.dirname(__file__), "test_mails", "reply-1.raw"), "r") as f: raw = f.read() diff --git a/frappe/email/doctype/email_alert/email_alert.py b/frappe/email/doctype/email_alert/email_alert.py index 066f143357..7aaa5cea22 100755 --- a/frappe/email/doctype/email_alert/email_alert.py +++ b/frappe/email/doctype/email_alert/email_alert.py @@ -32,7 +32,7 @@ class EmailAlert(Document): frappe.throw(_("The Condition '{0}' is invalid").format(self.condition)) def validate_forbidden_types(self): - forbidden_document_types = ("Bulk Email",) + forbidden_document_types = ("Email Queue",) if (self.document_type in forbidden_document_types or frappe.get_meta(self.document_type).istable): # currently email alerts don't work on child tables as events are not fired for each record of child table @@ -151,8 +151,7 @@ def evaluate_alert(doc, alert, event): subject = frappe.render_template(alert.subject, context) frappe.sendmail(recipients=recipients, subject=subject, - message= frappe.render_template(alert.message, context), - bulk=True, reference_doctype = doc.doctype, reference_name = doc.name, + message= frappe.render_template(alert.message, context), reference_doctype = doc.doctype, reference_name = doc.name, attachments = [frappe.attach_print(doc.doctype, doc.name)] if alert.attach_print else None) def get_context(doc): diff --git a/frappe/email/doctype/email_alert/test_email_alert.py b/frappe/email/doctype/email_alert/test_email_alert.py index 421841a2a7..020d2a4bd9 100755 --- a/frappe/email/doctype/email_alert/test_email_alert.py +++ b/frappe/email/doctype/email_alert/test_email_alert.py @@ -9,7 +9,7 @@ test_records = frappe.get_test_records('Email Alert') class TestEmailAlert(unittest.TestCase): def setUp(self): - frappe.db.sql("""delete from `tabBulk Email`""") + frappe.db.sql("""delete from `tabEmail Queue`""") frappe.set_user("test1@example.com") def tearDown(self): @@ -22,14 +22,14 @@ class TestEmailAlert(unittest.TestCase): communication.content = "test" communication.insert(ignore_permissions=True) - self.assertTrue(frappe.db.get_value("Bulk Email", {"reference_doctype": "Communication", + self.assertTrue(frappe.db.get_value("Email Queue", {"reference_doctype": "Communication", "reference_name": communication.name, "status":"Not Sent"})) - frappe.db.sql("""delete from `tabBulk Email`""") + frappe.db.sql("""delete from `tabEmail Queue`""") communication.content = "test 2" communication.save() - self.assertTrue(frappe.db.get_value("Bulk Email", {"reference_doctype": "Communication", + self.assertTrue(frappe.db.get_value("Email Queue", {"reference_doctype": "Communication", "reference_name": communication.name, "status":"Not Sent"})) def test_condition(self): @@ -39,13 +39,13 @@ class TestEmailAlert(unittest.TestCase): event.starts_on = "2014-06-06 12:00:00" event.insert() - self.assertFalse(frappe.db.get_value("Bulk Email", {"reference_doctype": "Event", + self.assertFalse(frappe.db.get_value("Email Queue", {"reference_doctype": "Event", "reference_name": event.name, "status":"Not Sent"})) event.event_type = "Public" event.save() - self.assertTrue(frappe.db.get_value("Bulk Email", {"reference_doctype": "Event", + self.assertTrue(frappe.db.get_value("Email Queue", {"reference_doctype": "Event", "reference_name": event.name, "status":"Not Sent"})) def test_invalid_condition(self): @@ -72,19 +72,19 @@ class TestEmailAlert(unittest.TestCase): event.starts_on = "2014-06-06 12:00:00" event.insert() - self.assertFalse(frappe.db.get_value("Bulk Email", {"reference_doctype": "Event", + self.assertFalse(frappe.db.get_value("Email Queue", {"reference_doctype": "Event", "reference_name": event.name, "status":"Not Sent"})) event.subject = "test 1" event.save() - self.assertFalse(frappe.db.get_value("Bulk Email", {"reference_doctype": "Event", + self.assertFalse(frappe.db.get_value("Email Queue", {"reference_doctype": "Event", "reference_name": event.name, "status":"Not Sent"})) event.description = "test" event.save() - self.assertTrue(frappe.db.get_value("Bulk Email", {"reference_doctype": "Event", + self.assertTrue(frappe.db.get_value("Email Queue", {"reference_doctype": "Event", "reference_name": event.name, "status":"Not Sent"})) def test_date_changed(self): @@ -94,23 +94,23 @@ class TestEmailAlert(unittest.TestCase): event.starts_on = "2014-01-01 12:00:00" event.insert() - self.assertFalse(frappe.db.get_value("Bulk Email", {"reference_doctype": "Event", + self.assertFalse(frappe.db.get_value("Email Queue", {"reference_doctype": "Event", "reference_name": event.name, "status":"Not Sent"})) frappe.utils.scheduler.trigger(frappe.local.site, "daily", now=True) # not today, so no alert - self.assertFalse(frappe.db.get_value("Bulk Email", {"reference_doctype": "Event", + self.assertFalse(frappe.db.get_value("Email Queue", {"reference_doctype": "Event", "reference_name": event.name, "status":"Not Sent"})) event.starts_on = frappe.utils.add_days(frappe.utils.nowdate(), 2) + " 12:00:00" event.save() - self.assertFalse(frappe.db.get_value("Bulk Email", {"reference_doctype": "Event", + self.assertFalse(frappe.db.get_value("Email Queue", {"reference_doctype": "Event", "reference_name": event.name, "status":"Not Sent"})) frappe.utils.scheduler.trigger(frappe.local.site, "daily", now=True) # today so show alert - self.assertTrue(frappe.db.get_value("Bulk Email", {"reference_doctype": "Event", + self.assertTrue(frappe.db.get_value("Email Queue", {"reference_doctype": "Event", "reference_name": event.name, "status":"Not Sent"})) diff --git a/frappe/email/doctype/email_queue/__init__.py b/frappe/email/doctype/email_queue/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/email/doctype/bulk_email/bulk_email.js b/frappe/email/doctype/email_queue/email_queue.js similarity index 81% rename from frappe/email/doctype/bulk_email/bulk_email.js rename to frappe/email/doctype/email_queue/email_queue.js index 53caab8396..ad8983067e 100644 --- a/frappe/email/doctype/bulk_email/bulk_email.js +++ b/frappe/email/doctype/email_queue/email_queue.js @@ -1,4 +1,7 @@ -frappe.ui.form.on("Bulk Email", { +// Copyright (c) 2016, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Email Queue", { refresh: function(frm) { if (frm.doc.status==="Not Sent") { frm.add_custom_button("Send Now", function() { diff --git a/frappe/email/doctype/email_queue/email_queue.json b/frappe/email/doctype/email_queue/email_queue.json new file mode 100644 index 0000000000..7463ca5cdc --- /dev/null +++ b/frappe/email/doctype/email_queue/email_queue.json @@ -0,0 +1,308 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "hash", + "beta": 0, + "creation": "2012-08-02 15:17:28", + "custom": 0, + "description": "Email Queue records.", + "docstatus": 0, + "doctype": "DocType", + "document_type": "System", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "sender", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Sender", + "length": 0, + "no_copy": 0, + "options": "Email", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "recipient", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Recipient", + "length": 0, + "no_copy": 0, + "options": "Email", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "message", + "fieldtype": "Code", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Message", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "default": "Not Sent", + "fieldname": "status", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Status", + "length": 0, + "no_copy": 0, + "options": "\nNot Sent\nSending\nSent\nError\nExpired", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "error", + "fieldtype": "Code", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Error", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "reference_doctype", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference DocType", + "length": 0, + "no_copy": 0, + "options": "DocType", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "reference_name", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Reference DocName", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "communication", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Communication", + "length": 0, + "no_copy": 0, + "options": "Communication", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 1, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "send_after", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Send After", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "default": "1", + "fieldname": "priority", + "fieldtype": "Int", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Priority", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "icon": "icon-envelope", + "idx": 1, + "image_view": 0, + "in_create": 1, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2016-06-22 12:23:10.621244", + "modified_by": "Administrator", + "module": "Email", + "name": "Email Queue", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 0, + "delete": 1, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 0, + "submit": 0, + "write": 0 + } + ], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/frappe/email/doctype/bulk_email/bulk_email.py b/frappe/email/doctype/email_queue/email_queue.py similarity index 51% rename from frappe/email/doctype/bulk_email/bulk_email.py rename to frappe/email/doctype/email_queue/email_queue.py index 991a78482b..ceab0518db 100644 --- a/frappe/email/doctype/bulk_email/bulk_email.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -1,28 +1,28 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# MIT License. See license.txt +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies and contributors +# For license information, please see license.txt from __future__ import unicode_literals import frappe from frappe.model.document import Document -from frappe.email.bulk import send_one -from frappe.utils import now_datetime +from frappe.email.queue import send_one -class BulkEmail(Document): +class EmailQueue(Document): pass @frappe.whitelist() def retry_sending(name): - doc = frappe.get_doc("Bulk Email", name) + doc = frappe.get_doc("Email Queue", name) if doc and doc.status == "Error": doc.status = "Not Sent" doc.save(ignore_permissions=True) @frappe.whitelist() def send_now(name): - doc = frappe.get_doc("Bulk Email", name) + doc = frappe.get_doc("Email Queue", name) send_one(doc, now=True) def on_doctype_update(): """Add index in `tabCommunication` for `(reference_doctype, reference_name)`""" - frappe.db.add_index('Bulk Email', ('status', 'send_after', 'priority', 'creation'), 'index_bulk_flush') + frappe.db.add_index('Email Queue', ('status', 'send_after', 'priority', 'creation'), 'index_bulk_flush') diff --git a/frappe/email/doctype/bulk_email/bulk_email_list.js b/frappe/email/doctype/email_queue/email_queue_list.js similarity index 83% rename from frappe/email/doctype/bulk_email/bulk_email_list.js rename to frappe/email/doctype/email_queue/email_queue_list.js index f6abd19701..1249183467 100644 --- a/frappe/email/doctype/bulk_email/bulk_email_list.js +++ b/frappe/email/doctype/email_queue/email_queue_list.js @@ -1,4 +1,4 @@ -frappe.listview_settings['Bulk Email'] = { +frappe.listview_settings['Email Queue'] = { get_indicator: function(doc) { colour = {'Sent': 'green', 'Sending': 'blue', 'Not Sent': 'grey', 'Error': 'red', 'Expired': 'orange'}; return [__(doc.status), colour[doc.status], "status,=," + doc.status]; diff --git a/frappe/email/doctype/email_queue/test_email_queue.py b/frappe/email/doctype/email_queue/test_email_queue.py new file mode 100644 index 0000000000..7cd79f9259 --- /dev/null +++ b/frappe/email/doctype/email_queue/test_email_queue.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest + +# test_records = frappe.get_test_records('Email Queue') + +class TestEmailQueue(unittest.TestCase): + pass diff --git a/frappe/email/bulk.py b/frappe/email/queue.py similarity index 84% rename from frappe/email/bulk.py rename to frappe/email/queue.py index 48885237a4..76681ba471 100755 --- a/frappe/email/bulk.py +++ b/frappe/email/queue.py @@ -14,13 +14,13 @@ from frappe.utils import get_url, nowdate, encode, now_datetime, add_days, split from rq.timeouts import JobTimeoutException from frappe.utils.scheduler import log -class BulkLimitCrossedError(frappe.ValidationError): pass +class EmailLimitCrossedError(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=(), show_as_cc=(), message_id=None, in_reply_to=None, send_after=None, - expose_recipients=False, bulk_priority=1, communication=None): - """Add email to sending queue (Bulk Email) + expose_recipients=False, send_priority=1, communication=None): + """Add email to sending queue (Email Queue) :param recipients: List of recipients. :param sender: Email sender. @@ -28,18 +28,18 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc :param message: Email message. :param reference_doctype: Reference DocType of caller document. :param reference_name: Reference name of caller document. - :param bulk_priority: Priority for bulk email, default 1. - :param unsubscribe_method: URL method for unsubscribe. Default is `/api/method/frappe.email.bulk.unsubscribe`. + :param send_priority: Priority for Email Queue, default 1. + :param unsubscribe_method: URL method for unsubscribe. Default is `/api/method/frappe.email.queue.unsubscribe`. :param unsubscribe_params: additional params for unsubscribed links. default are name, doctype, email :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 in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To. :param send_after: Send this email after the given datetime. If value is in integer, then `send_after` will be the automatically set to no of days from current date. - :param communication: Communication link to be set in Bulk Email record + :param communication: Communication link to be set in Email Queue record """ if not unsubscribe_method: - unsubscribe_method = "/api/method/frappe.email.bulk.unsubscribe" + unsubscribe_method = "/api/method/frappe.email.queue.unsubscribe" if not recipients: return @@ -54,7 +54,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc if not sender or sender == "Administrator": sender = email_account.default_sender - check_bulk_limit(recipients) + check_email_limit(recipients) formatted = get_formatted_html(subject, message, email_account=email_account) @@ -104,15 +104,15 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc # add to queue add(email, sender, subject, email_content, email_text_context, reference_doctype, - reference_name, attachments, reply_to, cc, message_id, in_reply_to, send_after, bulk_priority, email_account=email_account, communication=communication) + reference_name, attachments, reply_to, cc, message_id, in_reply_to, send_after, send_priority, email_account=email_account, communication=communication) def add(email, sender, subject, formatted, text_content=None, reference_doctype=None, reference_name=None, attachments=None, reply_to=None, - cc=(), message_id=None, in_reply_to=None, send_after=None, bulk_priority=1, email_account=None, communication=None): - """add to bulk mail queue""" - e = frappe.new_doc('Bulk Email') + cc=(), message_id=None, in_reply_to=None, send_after=None, send_priority=1, email_account=None, communication=None): + """Add to Email Queue""" + e = frappe.new_doc('Email Queue') e.recipient = email - e.priority = bulk_priority + e.priority = send_priority try: mail = get_email(email, sender=sender, formatted=formatted, subject=subject, @@ -137,8 +137,8 @@ def add(email, sender, subject, formatted, text_content=None, e.send_after = send_after e.insert(ignore_permissions=True) -def check_bulk_limit(recipients): - # if using settings from site_config.json, check bulk limit +def check_email_limit(recipients): + # if using settings from site_config.json, check email limit # No limit for own email settings smtp_server = SMTPServer() @@ -147,14 +147,14 @@ def check_bulk_limit(recipients): or frappe.flags.in_test): # get count of mails sent this month - this_month = frappe.db.sql("""select count(name) from `tabBulk Email` where + this_month = frappe.db.sql("""select count(name) from `tabEmail Queue` where status='Sent' and MONTH(creation)=MONTH(CURDATE())""")[0][0] - monthly_bulk_mail_limit = frappe.conf.get('monthly_bulk_mail_limit') or 500 + monthly_email_limit = frappe.conf.get('monthly_email_limit') or 500 - if (this_month + len(recipients)) > monthly_bulk_mail_limit: - throw(_("Cannot send this email. You have crossed the sending limit of {0} emails for this month.").format(monthly_bulk_mail_limit), - BulkLimitCrossedError) + if (this_month + len(recipients)) > monthly_email_limit: + throw(_("Cannot send this email. You have crossed the sending limit of {0} emails for this month.").format(monthly_email_limit), + EmailLimitCrossedError) def get_unsubscribe_link(reference_doctype, reference_name, email, recipients, expose_recipients, show_as_cc, @@ -241,21 +241,21 @@ def return_unsubscribed_page(email, doctype, name): def flush(from_test=False): """flush email queue, every time: called from scheduler""" # additional check - check_bulk_limit([]) + check_email_limit([]) auto_commit = not from_test if frappe.are_emails_muted(): msgprint(_("Emails are muted")) from_test = True - frappe.db.sql("""update `tabBulk Email` set status='Expired' + frappe.db.sql("""update `tabEmail Queue` set status='Expired' where datediff(curdate(), creation) > 7 and status='Not Sent'""", auto_commit=auto_commit) smtpserver = SMTPServer() for i in xrange(500): # don't use for update here, as it leads deadlocks - email = frappe.db.sql('''select * from `tabBulk Email` + email = frappe.db.sql('''select * from `tabEmail Queue` where status='Not Sent' and (send_after is null or send_after < %(now)s) order by priority desc, creation asc limit 1''', { 'now': now_datetime() }, as_dict=True) @@ -272,15 +272,15 @@ def flush(from_test=False): # frappe.db.commit() def send_one(email, smtpserver=None, auto_commit=True, now=False): - '''Send bulk email with given smtpserver''' + '''Send Email Queue with given smtpserver''' - status = frappe.db.sql('''select status from `tabBulk Email` where name=%s for update''', email.name)[0][0] + status = frappe.db.sql('''select status from `tabEmail Queue` where name=%s for update''', email.name)[0][0] if status != 'Not Sent': # rollback to release lock and return frappe.db.rollback() return - frappe.db.sql("""update `tabBulk Email` set status='Sending', modified=%s where name=%s""", + frappe.db.sql("""update `tabEmail Queue` set status='Sending', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) if email.communication: @@ -292,7 +292,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False): smtpserver.setup_email_account(email.reference_doctype) smtpserver.sess.sendmail(email.sender, email.recipient, encode(email.message)) - frappe.db.sql("""update `tabBulk Email` set status='Sent', modified=%s where name=%s""", + frappe.db.sql("""update `tabEmail Queue` set status='Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) if email.communication: @@ -305,7 +305,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False): JobTimeoutException): # bad connection/timeout, retry later - frappe.db.sql("""update `tabBulk Email` set status='Not Sent', modified=%s where name=%s""", + frappe.db.sql("""update `tabEmail Queue` set status='Not Sent', modified=%s where name=%s""", (now_datetime(), email.name), auto_commit=auto_commit) if email.communication: @@ -317,7 +317,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False): except Exception, e: frappe.db.rollback() - frappe.db.sql("""update `tabBulk Email` set status='Error', error=%s + frappe.db.sql("""update `tabEmail Queue` set status='Error', error=%s where name=%s""", (unicode(e), email.name), auto_commit=auto_commit) if email.communication: @@ -328,14 +328,14 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False): else: # log to scheduler log - log('frappe.email.bulk.flush', unicode(e)) + log('frappe.email.queue.flush', unicode(e)) def clear_outbox(): """Remove mails older than 31 days in Outbox. Called daily via scheduler.""" - frappe.db.sql("""delete from `tabBulk Email` where + frappe.db.sql("""delete from `tabEmail Queue` where datediff(now(), creation) > 31""") -def prevent_bulk_email_delete(doc, method): +def prevent_email_queue_delete(doc, method): from frappe.limits import get_limits if frappe.session.user != 'Administrator' and get_limits().get('block_bulk_email_delete'): - frappe.throw(_('Only Administrator can delete Bulk Email')) \ No newline at end of file + frappe.throw(_('Only Administrator can delete Email Queue')) \ No newline at end of file diff --git a/frappe/hooks.py b/frappe/hooks.py index cd300d813c..cda7deb5da 100755 --- a/frappe/hooks.py +++ b/frappe/hooks.py @@ -99,8 +99,8 @@ doc_events = { "User": { "validate": "frappe.utils.user.validate_user_limit" }, - "Bulk Email": { - "on_trash": "frappe.email.bulk.prevent_bulk_email_delete" + "Email Queue": { + "on_trash": "frappe.email.queue.prevent_email_queue_delete" }, "*": { "after_insert": "frappe.email.doctype.email_alert.email_alert.trigger_email_alerts", @@ -124,7 +124,7 @@ doc_events = { scheduler_events = { "all": [ - "frappe.email.bulk.flush", + "frappe.email.queue.flush", "frappe.email.doctype.email_account.email_account.pull", "frappe.email.doctype.email_account.email_account.notify_unreplied", "frappe.utils.error.collect_error_snapshots", @@ -132,7 +132,7 @@ scheduler_events = { 'frappe.model.utils.list_settings.sync_list_settings' ], "daily": [ - "frappe.email.bulk.clear_outbox", + "frappe.email.queue.clear_outbox", "frappe.desk.notifications.clear_notifications", "frappe.core.doctype.scheduler_log.scheduler_log.set_old_logs_as_seen", "frappe.desk.doctype.event.event.send_event_digest", diff --git a/frappe/limits.py b/frappe/limits.py index 9f671902fa..f6148601d8 100755 --- a/frappe/limits.py +++ b/frappe/limits.py @@ -35,7 +35,7 @@ def check_if_expired(): # if expired, stop user from logging in expires_on = formatdate(get_limits().get("expiry")) support_email = get_limits().get("support_email") or _("your provider") - + frappe.throw(_("""Your subscription expired on {0}. To extend please send an email to {1}""").format(expires_on, support_email), SiteExpiredError) @@ -72,7 +72,7 @@ def get_expiry_message(): def get_limits(): limits = frappe.get_conf().get("limits") or {} day = frappe.utils.add_months(frappe.utils.today(), -1) - limits["bulk_count"] = frappe.db.count("Bulk Email", filters={'creation': ['>', day]}) + limits["emails_sent"] = frappe.db.count("Email Queue", filters={'creation': ['>', day]}) return limits diff --git a/frappe/patches.txt b/frappe/patches.txt index 028ea37bdd..269fad4823 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -15,6 +15,7 @@ execute:frappe.reload_doc('custom', 'doctype', 'property_setter') #2014-12-31-1 execute:frappe.reload_doc('core', 'doctype', 'patch_log') #2016-10-31 execute:frappe.reload_doctype("File") # 2015-10-19 execute:frappe.reload_doc('core', 'doctype', 'error_snapshot') +frappe.patches.v7_0.rename_bulk_email_to_email_queue execute:frappe.db.sql("alter table `tabSessions` modify `user` varchar(255), engine=InnoDB") execute:frappe.db.sql("delete from `tabDocField` where parent='0'") diff --git a/frappe/patches/v5_0/rename_ref_type_fieldnames.py b/frappe/patches/v5_0/rename_ref_type_fieldnames.py index bf84858025..d0c4d71fca 100644 --- a/frappe/patches/v5_0/rename_ref_type_fieldnames.py +++ b/frappe/patches/v5_0/rename_ref_type_fieldnames.py @@ -6,14 +6,14 @@ import frappe def execute(): try: - frappe.db.sql("alter table `tabBulk Email` change `ref_docname` `reference_name` varchar(255)") + frappe.db.sql("alter table `tabEmail Queue` change `ref_docname` `reference_name` varchar(255)") except Exception, e: if e.args[0] not in (1054, 1060): raise try: - frappe.db.sql("alter table `tabBulk Email` change `ref_doctype` `reference_doctype` varchar(255)") + frappe.db.sql("alter table `tabEmail Queue` change `ref_doctype` `reference_doctype` varchar(255)") except Exception, e: if e.args[0] not in (1054, 1060): raise - frappe.reload_doctype("Bulk Email") + frappe.reload_doctype("Email Queue") diff --git a/frappe/patches/v5_0/v4_to_v5.py b/frappe/patches/v5_0/v4_to_v5.py index 16ce4f8f39..e36b6ca4e6 100644 --- a/frappe/patches/v5_0/v4_to_v5.py +++ b/frappe/patches/v5_0/v4_to_v5.py @@ -9,7 +9,7 @@ def execute(): ("desk", ("feed", "event", "event_role", "todo", "note")), ("custom", ("custom_field", "custom_script", "customize_form", "customize_form_field", "property_setter")), - ("email", ("bulk_email", "email_alert", "email_alert_recipient", "standard_reply")), + ("email", ("email_queue", "email_alert", "email_alert_recipient", "standard_reply")), ("geo", ("country", "currency")), ("print", ("letter_head", "print_format", "print_settings")) ) diff --git a/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py b/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py new file mode 100644 index 0000000000..7be9aa6a72 --- /dev/null +++ b/frappe/patches/v7_0/rename_bulk_email_to_email_queue.py @@ -0,0 +1,4 @@ +import frappe + +def execute(): + frappe.rename_doc('DocType', 'Bulk Email', 'Email Queue') \ No newline at end of file diff --git a/frappe/patches/v7_0/update_send_after_in_bulk_email.py b/frappe/patches/v7_0/update_send_after_in_bulk_email.py index ca01f88897..157b084255 100644 --- a/frappe/patches/v7_0/update_send_after_in_bulk_email.py +++ b/frappe/patches/v7_0/update_send_after_in_bulk_email.py @@ -2,4 +2,4 @@ import frappe from frappe.utils import now_datetime def execute(): - frappe.db.sql('update `tabBulk Email` set send_after=%s where send_after is null', now_datetime()) \ No newline at end of file + frappe.db.sql('update `tabEmail Queue` set send_after=%s where send_after is null', now_datetime()) \ No newline at end of file diff --git a/frappe/templates/includes/comments/comments.py b/frappe/templates/includes/comments/comments.py index fe5b7f2c45..abc8013d07 100644 --- a/frappe/templates/includes/comments/comments.py +++ b/frappe/templates/includes/comments/comments.py @@ -49,7 +49,7 @@ def add_comment(args=None): message += "".format(frappe.utils.get_request_site_address(), page_name, _("View it in your browser")) - from frappe.email.bulk import send + from frappe.email.queue import send send(recipients=recipients, subject = _("New comment on {0} {1}").format(doc.doctype, doc.name), diff --git a/frappe/tests/test_db.py b/frappe/tests/test_db.py index 744f93067a..c0d34796a0 100644 --- a/frappe/tests/test_db.py +++ b/frappe/tests/test_db.py @@ -26,4 +26,4 @@ class TestDB(unittest.TestCase): def test_multiple_queries(self): # implicit commit - self.assertRaises(frappe.SQLError, frappe.db.sql, """select name from `tabUser`; truncate `tabBulk Email`""") + self.assertRaises(frappe.SQLError, frappe.db.sql, """select name from `tabUser`; truncate `tabEmail Queue`""") diff --git a/frappe/tests/test_email.py b/frappe/tests/test_email.py index a695b26598..8386663164 100644 --- a/frappe/tests/test_email.py +++ b/frappe/tests/test_email.py @@ -12,81 +12,81 @@ make_test_records("Email Account") class TestEmail(unittest.TestCase): def setUp(self): frappe.db.sql("""delete from `tabEmail Unsubscribe`""") - frappe.db.sql("""delete from `tabBulk Email`""") + frappe.db.sql("""delete from `tabEmail Queue`""") def test_send(self): from frappe.email import sendmail sendmail('test@example.com', subject='Test Mail', msg="Test Content") - def test_bulk(self, send_after=None): - from frappe.email.bulk import send + def test_email_queue(self, send_after=None): + from frappe.email.queue import send send(recipients = ['test@example.com', 'test1@example.com'], sender="admin@example.com", reference_doctype='User', reference_name='Administrator', - subject='Testing Bulk', message='This is a bulk mail!', send_after=send_after) + subject='Testing Queue', message='This mail is queued!', send_after=send_after) - bulk = frappe.db.sql("""select * from `tabBulk Email` where status='Not Sent'""", as_dict=1) - self.assertEquals(len(bulk), 2) - self.assertTrue('test@example.com' in [d['recipient'] for d in bulk]) - self.assertTrue('test1@example.com' in [d['recipient'] for d in bulk]) - self.assertTrue('Unsubscribe' in bulk[0]['message']) + email_queue = frappe.db.sql("""select * from `tabEmail Queue` where status='Not Sent'""", as_dict=1) + self.assertEquals(len(email_queue), 2) + self.assertTrue('test@example.com' in [d['recipient'] for d in email_queue]) + self.assertTrue('test1@example.com' in [d['recipient'] for d in email_queue]) + self.assertTrue('Unsubscribe' in email_queue[0]['message']) def test_flush(self): - self.test_bulk(send_after = 1) - from frappe.email.bulk import flush + self.test_email_queue(send_after = 1) + from frappe.email.queue import flush flush(from_test=True) - bulk = frappe.db.sql("""select * from `tabBulk Email` where status='Sent'""", as_dict=1) - self.assertEquals(len(bulk), 0) + email_queue = frappe.db.sql("""select * from `tabEmail Queue` where status='Sent'""", as_dict=1) + self.assertEquals(len(email_queue), 0) def test_send_after(self): - self.test_bulk() - from frappe.email.bulk import flush + self.test_email_queue() + from frappe.email.queue import flush flush(from_test=True) - bulk = frappe.db.sql("""select * from `tabBulk Email` where status='Sent'""", as_dict=1) - self.assertEquals(len(bulk), 2) - self.assertTrue('test@example.com' in [d['recipient'] for d in bulk]) - self.assertTrue('test1@example.com' in [d['recipient'] for d in bulk]) + email_queue = frappe.db.sql("""select * from `tabEmail Queue` where status='Sent'""", as_dict=1) + self.assertEquals(len(email_queue), 2) + self.assertTrue('test@example.com' in [d['recipient'] for d in email_queue]) + self.assertTrue('test1@example.com' in [d['recipient'] for d in email_queue]) def test_expired(self): - self.test_bulk() - frappe.db.sql("update `tabBulk Email` set creation='2010-01-01 12:00:00'") - from frappe.email.bulk import flush + self.test_email_queue() + frappe.db.sql("update `tabEmail Queue` set creation='2010-01-01 12:00:00'") + from frappe.email.queue import flush flush(from_test=True) - bulk = frappe.db.sql("""select * from `tabBulk Email` where status='Expired'""", as_dict=1) - self.assertEquals(len(bulk), 2) - self.assertTrue('test@example.com' in [d['recipient'] for d in bulk]) - self.assertTrue('test1@example.com' in [d['recipient'] for d in bulk]) + email_queue = frappe.db.sql("""select * from `tabEmail Queue` where status='Expired'""", as_dict=1) + self.assertEquals(len(email_queue), 2) + self.assertTrue('test@example.com' in [d['recipient'] for d in email_queue]) + self.assertTrue('test1@example.com' in [d['recipient'] for d in email_queue]) def test_unsubscribe(self): - from frappe.email.bulk import unsubscribe, send + from frappe.email.queue import unsubscribe, send unsubscribe(doctype="User", name="Administrator", email="test@example.com") self.assertTrue(frappe.db.get_value("Email Unsubscribe", {"reference_doctype": "User", "reference_name": "Administrator", "email": "test@example.com"})) - before = frappe.db.sql("""select count(name) from `tabBulk Email` where status='Not Sent'""")[0][0] + before = frappe.db.sql("""select count(name) from `tabEmail Queue` where status='Not Sent'""")[0][0] send(recipients = ['test@example.com', 'test1@example.com'], sender="admin@example.com", reference_doctype='User', reference_name= "Administrator", - subject='Testing Bulk', message='This is a bulk mail!') + subject='Testing Email Queue', message='This is mail is queued!') # this is sent async (?) - bulk = frappe.db.sql("""select * from `tabBulk Email` where status='Not Sent'""", + email_queue = frappe.db.sql("""select * from `tabEmail Queue` where status='Not Sent'""", as_dict=1) - self.assertEquals(len(bulk), before + 1) - self.assertFalse('test@example.com' in [d['recipient'] for d in bulk]) - self.assertTrue('test1@example.com' in [d['recipient'] for d in bulk]) - self.assertTrue('Unsubscribe' in bulk[0]['message']) - - def test_bulk_limit(self): - from frappe.email.bulk import send, BulkLimitCrossedError - self.assertRaises(BulkLimitCrossedError, send, + self.assertEquals(len(email_queue), before + 1) + self.assertFalse('test@example.com' in [d['recipient'] for d in email_queue]) + self.assertTrue('test1@example.com' in [d['recipient'] for d in email_queue]) + self.assertTrue('Unsubscribe' in email_queue[0]['message']) + + def test_email_queue_limit(self): + from frappe.email.queue import send, EmailLimitCrossedError + self.assertRaises(EmailLimitCrossedError, send, recipients=['test@example.com']*1000, sender="admin@example.com", reference_doctype = "User", reference_name="Administrator", - subject='Testing Bulk', message='This is a bulk mail!') + subject='Testing Email Queue', message='This email is queued!') def test_image_parsing(self): import re