Browse Source

Merge pull request #1808 from rmehta/email-queue

[rename] Bulk Email to Email Queue
version-14
Rushabh Mehta 9 years ago
committed by GitHub
parent
commit
64fdb2da43
34 changed files with 483 additions and 482 deletions
  1. +11
    -13
      frappe/__init__.py
  2. +5
    -5
      frappe/commands/utils.py
  3. +1
    -1
      frappe/config/core.py
  4. +1
    -2
      frappe/core/doctype/communication/comment.py
  5. +3
    -3
      frappe/core/doctype/communication/communication.py
  6. +5
    -5
      frappe/core/doctype/communication/email.py
  7. +9
    -6
      frappe/core/doctype/user/user.py
  8. +1
    -1
      frappe/desk/doctype/event/event.py
  9. +1
    -2
      frappe/desk/page/chat/chat.py
  10. +0
    -1
      frappe/email/doctype/bulk_email/README.md
  11. +0
    -4
      frappe/email/doctype/bulk_email/__init__.py
  12. +0
    -307
      frappe/email/doctype/bulk_email/bulk_email.json
  13. +0
    -12
      frappe/email/doctype/bulk_email/test_bulk_email.py
  14. +2
    -3
      frappe/email/doctype/email_account/email_account.py
  15. +6
    -6
      frappe/email/doctype/email_account/test_email_account.py
  16. +2
    -3
      frappe/email/doctype/email_alert/email_alert.py
  17. +13
    -13
      frappe/email/doctype/email_alert/test_email_alert.py
  18. +0
    -0
      frappe/email/doctype/email_queue/__init__.py
  19. +4
    -1
      frappe/email/doctype/email_queue/email_queue.js
  20. +308
    -0
      frappe/email/doctype/email_queue/email_queue.json
  21. +8
    -8
      frappe/email/doctype/email_queue/email_queue.py
  22. +1
    -1
      frappe/email/doctype/email_queue/email_queue_list.js
  23. +12
    -0
      frappe/email/doctype/email_queue/test_email_queue.py
  24. +33
    -33
      frappe/email/queue.py
  25. +4
    -4
      frappe/hooks.py
  26. +2
    -2
      frappe/limits.py
  27. +1
    -0
      frappe/patches.txt
  28. +3
    -3
      frappe/patches/v5_0/rename_ref_type_fieldnames.py
  29. +1
    -1
      frappe/patches/v5_0/v4_to_v5.py
  30. +4
    -0
      frappe/patches/v7_0/rename_bulk_email_to_email_queue.py
  31. +1
    -1
      frappe/patches/v7_0/update_send_after_in_bulk_email.py
  32. +1
    -1
      frappe/templates/includes/comments/comments.py
  33. +1
    -1
      frappe/tests/test_db.py
  34. +39
    -39
      frappe/tests/test_email.py

+ 11
- 13
frappe/__init__.py View File

@@ -7,7 +7,6 @@ globals attached to frappe module
from __future__ import unicode_literals from __future__ import unicode_literals


from werkzeug.local import Local, release_local from werkzeug.local import Local, release_local
from functools import wraps
import os, importlib, inspect, json import os, importlib, inspect, json


# public # 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 raise_exception: [optional] Raise given exception and show message.
:param as_table: [optional] If `msg` is a list of lists, render as HTML table. :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) out = _dict(message=msg)


@@ -355,11 +354,11 @@ def get_request_header(key, default=None):
return request.headers.get(key, default) return request.headers.get(key, default)


def sendmail(recipients=(), sender="", subject="No Subject", message="No Message", 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, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
attachments=None, content=None, doctype=None, name=None, reply_to=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**. """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 subject: Email Subject.
:param message: (or `content`) Email Content. :param message: (or `content`) Email Content.
:param as_markdown: Convert content markdown to HTML. :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_doctype: (or `doctype`) Append as communication to this DocType.
:param reference_name: (or `name`) Append as communication to this document name. :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` :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 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 send_after: Send after the given datetime.
:param expose_recipients: Display all recipients in the footer message - "This email was sent to" :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, subject=subject, message=content or message,
reference_doctype = doctype or reference_doctype, reference_name = name or reference_name, reference_doctype = doctype or reference_doctype, reference_name = name or reference_name,
unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, unsubscribe_message=unsubscribe_message, 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, 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: else:
import frappe.email import frappe.email
if as_markdown: 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): def copy_doc(doc, ignore_no_copy=True):
""" No_copy fields also get copied.""" """ No_copy fields also get copied."""
import copy import copy
from frappe.model import optional_fields, default_fields


def remove_no_copy_fields(d): def remove_no_copy_fields(d):
for df in d.meta.get("fields", {"no_copy": 1}): for df in d.meta.get("fields", {"no_copy": 1}):


+ 5
- 5
frappe/commands/utils.py View File

@@ -146,16 +146,16 @@ def execute(context, method, args=None, kwargs=None):
print json.dumps(ret) print json.dumps(ret)




@click.command('add-bulk-email')
@click.command('add-to-email-queue')
@click.argument('email') @click.argument('email')
@pass_context @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) site = get_site(context)
with frappe.init_site(site): with frappe.init_site(site):
frappe.connect() frappe.connect()
kwargs = json.loads(email) kwargs = json.loads(email)
kwargs['as_bulk'] = True
kwargs['delayed'] = True
frappe.sendmail(**kwargs) frappe.sendmail(**kwargs)
frappe.db.commit() frappe.db.commit()


@@ -408,7 +408,6 @@ def get_version():
print "{0} {1}".format(m, module.__version__) print "{0} {1}".format(m, module.__version__)


commands = [ commands = [
add_bulk_email,
build, build,
build_website, build_website,
clear_cache, clear_cache,
@@ -433,4 +432,5 @@ commands = [
sync_www, sync_www,
watch, watch,
_bulk_rename, _bulk_rename,
add_to_email_queue,
] ]

+ 1
- 1
frappe/config/core.py View File

@@ -48,7 +48,7 @@ def get_data():
}, },
{ {
"type": "doctype", "type": "doctype",
"name": "Bulk Email",
"name": "Email Queue",
"description": _("Background Email Queue"), "description": _("Background Email Queue"),
}, },
{ {


+ 1
- 2
frappe/core/doctype/communication/comment.py View File

@@ -104,8 +104,7 @@ def notify_mentions(doc):
recipients=recipients, recipients=recipients,
sender=frappe.session.user, sender=frappe.session.user,
subject=subject, subject=subject,
message=message,
bulk=True
message=message
) )


def get_comments_from_parent(doc): def get_comments_from_parent(doc):


+ 3
- 3
frappe/core/doctype/communication/communication.py View File

@@ -161,7 +161,7 @@ class Communication(Document):


def notify(self, print_html=None, print_format=None, attachments=None, def notify(self, print_html=None, print_format=None, attachments=None,
recipients=None, cc=None, fetched_from_email_account=False): 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_html: Send given value as HTML attachment
:param print_format: Attach print format of parent document :param print_format: Attach print format of parent document
@@ -193,9 +193,9 @@ class Communication(Document):
frappe.local.flags.commit = True frappe.local.flags.commit = True


def set_delivery_status(self, commit=False): 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 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'): if status_counts.get('Not Sent') or status_counts.get('Sending'):
delivery_status = 'Sending' delivery_status = 'Sending'


+ 5
- 5
frappe/core/doctype/communication/email.py View File

@@ -8,7 +8,7 @@ from email.utils import formataddr, parseaddr
from frappe.utils import (get_url, get_formatted_email, cint, from frappe.utils import (get_url, get_formatted_email, cint,
validate_email_add, split_emails, time_diff_in_seconds) validate_email_add, split_emails, time_diff_in_seconds)
from frappe.utils.file_manager import get_file 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 from frappe.utils.scheduler import log
import frappe.email.smtp import frappe.email.smtp
import MySQLdb import MySQLdb
@@ -89,7 +89,7 @@ def validate_email(doc):


def notify(doc, print_html=None, print_format=None, attachments=None, def notify(doc, print_html=None, print_format=None, attachments=None,
recipients=None, cc=None, fetched_from_email_account=False): 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_html: Send given value as HTML attachment
:param print_format: Attach print format of parent document :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, doc._notify(print_html=print_html, print_format=print_format, attachments=attachments,
recipients=recipients, cc=cc) recipients=recipients, cc=cc)
else: 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", enqueue(sendmail, queue="default", timeout=300, event="sendmail",
communication_name=doc.name, communication_name=doc.name,
print_html=print_html, print_format=print_format, attachments=attachments, 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, attachments=doc.attachments,
message_id=doc.name, message_id=doc.name,
unsubscribe_message=_("Leave this conversation"), unsubscribe_message=_("Leave this conversation"),
bulk=True,
delayed=True,
communication=doc.name 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_html: Send given value as HTML attachment.
:param print_format: Attach print format of parent document.""" :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")) view_link = frappe.utils.cint(frappe.db.get_value("Print Settings", "Print Settings", "attach_view_link"))


if print_format and view_link: if print_format and view_link:


+ 9
- 6
frappe/core/doctype/user/user.py View File

@@ -189,19 +189,21 @@ class User(Document):
(self.first_name and " " or '') + (self.last_name or '') (self.first_name and " " or '') + (self.last_name or '')


def password_reset_mail(self, link): 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): 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): 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() link = self.reset_password()
self.send_login_mail(_("Verify Your Account"), "templates/emails/new_user.html", self.send_login_mail(_("Verify Your Account"), "templates/emails/new_user.html",
{"link": link, "site_url": get_url()}) {"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""" """send mail with login details"""
from frappe.utils.user import get_user_fullname from frappe.utils.user import get_user_fullname
from frappe.utils import get_url 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 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, 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): def a_system_manager_should_exist(self):
if not self.get_other_system_managers(): 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"), frappe.sendmail(recipients=get_system_managers(), subject=_("Administrator Logged In"),
message=message, bulk=True)
message=message)


def extract_mentions(txt): def extract_mentions(txt):
"""Find all instances of @username in the string. """Find all instances of @username in the string.


+ 1
- 1
frappe/desk/doctype/event/event.py View File

@@ -66,7 +66,7 @@ def send_event_digest():
+ frappe._("Daily Event Digest is sent for Calendar Events where reminders are set.")+'</p>' + frappe._("Daily Event Digest is sent for Calendar Events where reminders are set.")+'</p>'


frappe.sendmail(recipients=user.email, subject=frappe._("Upcoming Events for Today"), frappe.sendmail(recipients=user.email, subject=frappe._("Upcoming Events for Today"),
content = text, bulk=True)
content = text)


@frappe.whitelist() @frappe.whitelist()
def get_events(start, end, user=None, for_reminder=False): def get_events(start, end, user=None, for_reminder=False):


+ 1
- 2
frappe/desk/page/chat/chat.py View File

@@ -131,7 +131,6 @@ def _notify(contact, txt, subject=None):
"from": get_fullname(frappe.session.user), "from": get_fullname(frappe.session.user),
"message": txt, "message": txt,
"link": get_url() "link": get_url()
}),
bulk=True)
}))
except frappe.OutgoingEmailError: except frappe.OutgoingEmailError:
pass pass

+ 0
- 1
frappe/email/doctype/bulk_email/README.md View File

@@ -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)

+ 0
- 4
frappe/email/doctype/bulk_email/__init__.py View File

@@ -1,4 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt

from __future__ import unicode_literals

+ 0
- 307
frappe/email/doctype/bulk_email/bulk_email.json View File

@@ -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
}

+ 0
- 12
frappe/email/doctype/bulk_email/test_bulk_email.py View File

@@ -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

+ 2
- 3
frappe/email/doctype/email_account/email_account.py View File

@@ -355,8 +355,7 @@ class EmailAccount(Document):
reference_name = communication.reference_name, reference_name = communication.reference_name,
message_id = communication.name, message_id = communication.name,
in_reply_to = email.mail.get("Message-Id"), # send back the Message-Id as In-Reply-To 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): def get_unreplied_notification_emails(self):
"""Return list of emails listed""" """Return list of emails listed"""
@@ -398,7 +397,7 @@ def notify_unreplied():
# if status is still open # if status is still open
frappe.sendmail(recipients=email_account.get_unreplied_notification_emails(), frappe.sendmail(recipients=email_account.get_unreplied_notification_emails(),
content=comm.content, subject=comm.subject, doctype= comm.reference_doctype, content=comm.content, subject=comm.subject, doctype= comm.reference_doctype,
name=comm.reference_name, bulk=True)
name=comm.reference_name)


# update flag # update flag
comm.db_set("unread_notification_sent", 1) comm.db_set("unread_notification_sent", 1)


+ 6
- 6
frappe/email/doctype/email_account/test_email_account.py View File

@@ -42,9 +42,9 @@ class TestEmailAccount(unittest.TestCase):
comm = frappe.get_doc("Communication", {"sender": "test_sender@example.com"}) comm = frappe.get_doc("Communication", {"sender": "test_sender@example.com"})
comm.db_set("creation", datetime.now() - timedelta(seconds = 30 * 60)) 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() 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"})) "reference_name": comm.reference_name, "status":"Not Sent"}))


def test_incoming_with_attach(self): 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", make(subject = "test-mail-000", content="test mail 000", recipients="test_receiver@example.com",
send_email=True, sender="test_sender@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")) self.assertTrue("test-mail-000" in mail.get("Subject"))


def test_sendmail(self): def test_sendmail(self):
frappe.flags.sent_mail = None frappe.flags.sent_mail = None
frappe.sendmail(sender="test_sender@example.com", recipients="test_recipient@example.com", 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) sent_mail = email.message_from_string(frappe.flags.sent_mail)
self.assertTrue("test-mail-001" in sent_mail.get("Subject")) 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", content="test mail 001", subject="test-mail-002", doctype="Email Account",
name="_Test Email Account 1", print_format="Standard", send_email=True) 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")) self.assertTrue("test-mail-002" in sent_mail.get("Subject"))


def test_threading(self): def test_threading(self):
@@ -131,7 +131,7 @@ class TestEmailAccount(unittest.TestCase):
recipients="test_receiver@example.com", sender="test@example.com", recipients="test_receiver@example.com", sender="test@example.com",
send_email=True)["name"] 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: with open(os.path.join(os.path.dirname(__file__), "test_mails", "reply-1.raw"), "r") as f:
raw = f.read() raw = f.read()


+ 2
- 3
frappe/email/doctype/email_alert/email_alert.py View File

@@ -32,7 +32,7 @@ class EmailAlert(Document):
frappe.throw(_("The Condition '{0}' is invalid").format(self.condition)) frappe.throw(_("The Condition '{0}' is invalid").format(self.condition))


def validate_forbidden_types(self): def validate_forbidden_types(self):
forbidden_document_types = ("Bulk Email",)
forbidden_document_types = ("Email Queue",)
if (self.document_type in forbidden_document_types if (self.document_type in forbidden_document_types
or frappe.get_meta(self.document_type).istable): 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 # 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) subject = frappe.render_template(alert.subject, context)


frappe.sendmail(recipients=recipients, subject=subject, 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) attachments = [frappe.attach_print(doc.doctype, doc.name)] if alert.attach_print else None)


def get_context(doc): def get_context(doc):


+ 13
- 13
frappe/email/doctype/email_alert/test_email_alert.py View File

@@ -9,7 +9,7 @@ test_records = frappe.get_test_records('Email Alert')


class TestEmailAlert(unittest.TestCase): class TestEmailAlert(unittest.TestCase):
def setUp(self): def setUp(self):
frappe.db.sql("""delete from `tabBulk Email`""")
frappe.db.sql("""delete from `tabEmail Queue`""")
frappe.set_user("test1@example.com") frappe.set_user("test1@example.com")


def tearDown(self): def tearDown(self):
@@ -22,14 +22,14 @@ class TestEmailAlert(unittest.TestCase):
communication.content = "test" communication.content = "test"
communication.insert(ignore_permissions=True) 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"})) "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.content = "test 2"
communication.save() 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"})) "reference_name": communication.name, "status":"Not Sent"}))


def test_condition(self): def test_condition(self):
@@ -39,13 +39,13 @@ class TestEmailAlert(unittest.TestCase):
event.starts_on = "2014-06-06 12:00:00" event.starts_on = "2014-06-06 12:00:00"
event.insert() 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"})) "reference_name": event.name, "status":"Not Sent"}))


event.event_type = "Public" event.event_type = "Public"
event.save() 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"})) "reference_name": event.name, "status":"Not Sent"}))


def test_invalid_condition(self): def test_invalid_condition(self):
@@ -72,19 +72,19 @@ class TestEmailAlert(unittest.TestCase):
event.starts_on = "2014-06-06 12:00:00" event.starts_on = "2014-06-06 12:00:00"
event.insert() 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"})) "reference_name": event.name, "status":"Not Sent"}))


event.subject = "test 1" event.subject = "test 1"
event.save() 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"})) "reference_name": event.name, "status":"Not Sent"}))


event.description = "test" event.description = "test"
event.save() 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"})) "reference_name": event.name, "status":"Not Sent"}))


def test_date_changed(self): def test_date_changed(self):
@@ -94,23 +94,23 @@ class TestEmailAlert(unittest.TestCase):
event.starts_on = "2014-01-01 12:00:00" event.starts_on = "2014-01-01 12:00:00"
event.insert() 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"})) "reference_name": event.name, "status":"Not Sent"}))


frappe.utils.scheduler.trigger(frappe.local.site, "daily", now=True) frappe.utils.scheduler.trigger(frappe.local.site, "daily", now=True)


# not today, so no alert # 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"})) "reference_name": event.name, "status":"Not Sent"}))


event.starts_on = frappe.utils.add_days(frappe.utils.nowdate(), 2) + " 12:00:00" event.starts_on = frappe.utils.add_days(frappe.utils.nowdate(), 2) + " 12:00:00"
event.save() 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"})) "reference_name": event.name, "status":"Not Sent"}))


frappe.utils.scheduler.trigger(frappe.local.site, "daily", now=True) frappe.utils.scheduler.trigger(frappe.local.site, "daily", now=True)


# today so show alert # 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"})) "reference_name": event.name, "status":"Not Sent"}))

+ 0
- 0
frappe/email/doctype/email_queue/__init__.py View File


frappe/email/doctype/bulk_email/bulk_email.js → frappe/email/doctype/email_queue/email_queue.js View File

@@ -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) { refresh: function(frm) {
if (frm.doc.status==="Not Sent") { if (frm.doc.status==="Not Sent") {
frm.add_custom_button("Send Now", function() { frm.add_custom_button("Send Now", function() {

+ 308
- 0
frappe/email/doctype/email_queue/email_queue.json View File

@@ -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
}

frappe/email/doctype/bulk_email/bulk_email.py → frappe/email/doctype/email_queue/email_queue.py View File

@@ -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 from __future__ import unicode_literals
import frappe import frappe


from frappe.model.document import Document 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 pass


@frappe.whitelist() @frappe.whitelist()
def retry_sending(name): def retry_sending(name):
doc = frappe.get_doc("Bulk Email", name)
doc = frappe.get_doc("Email Queue", name)
if doc and doc.status == "Error": if doc and doc.status == "Error":
doc.status = "Not Sent" doc.status = "Not Sent"
doc.save(ignore_permissions=True) doc.save(ignore_permissions=True)


@frappe.whitelist() @frappe.whitelist()
def send_now(name): def send_now(name):
doc = frappe.get_doc("Bulk Email", name)
doc = frappe.get_doc("Email Queue", name)
send_one(doc, now=True) send_one(doc, now=True)


def on_doctype_update(): def on_doctype_update():
"""Add index in `tabCommunication` for `(reference_doctype, reference_name)`""" """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')

frappe/email/doctype/bulk_email/bulk_email_list.js → frappe/email/doctype/email_queue/email_queue_list.js View File

@@ -1,4 +1,4 @@
frappe.listview_settings['Bulk Email'] = {
frappe.listview_settings['Email Queue'] = {
get_indicator: function(doc) { get_indicator: function(doc) {
colour = {'Sent': 'green', 'Sending': 'blue', 'Not Sent': 'grey', 'Error': 'red', 'Expired': 'orange'}; colour = {'Sent': 'green', 'Sending': 'blue', 'Not Sent': 'grey', 'Error': 'red', 'Expired': 'orange'};
return [__(doc.status), colour[doc.status], "status,=," + doc.status]; return [__(doc.status), colour[doc.status], "status,=," + doc.status];

+ 12
- 0
frappe/email/doctype/email_queue/test_email_queue.py View File

@@ -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

frappe/email/bulk.py → frappe/email/queue.py View File

@@ -14,13 +14,13 @@ from frappe.utils import get_url, nowdate, encode, now_datetime, add_days, split
from rq.timeouts import JobTimeoutException from rq.timeouts import JobTimeoutException
from frappe.utils.scheduler import log 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, 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, 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, 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 recipients: List of recipients.
:param sender: Email sender. :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 message: Email message.
:param reference_doctype: Reference DocType of caller document. :param reference_doctype: Reference DocType of caller document.
:param reference_name: Reference name 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 unsubscribe_params: additional params for unsubscribed links. default are name, doctype, email
:param attachments: Attachments to be sent. :param attachments: Attachments to be sent.
:param reply_to: Reply to be captured here (default inbox) :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 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 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 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: if not unsubscribe_method:
unsubscribe_method = "/api/method/frappe.email.bulk.unsubscribe"
unsubscribe_method = "/api/method/frappe.email.queue.unsubscribe"


if not recipients: if not recipients:
return return
@@ -54,7 +54,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
if not sender or sender == "Administrator": if not sender or sender == "Administrator":
sender = email_account.default_sender sender = email_account.default_sender


check_bulk_limit(recipients)
check_email_limit(recipients)


formatted = get_formatted_html(subject, message, email_account=email_account) 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 to queue
add(email, sender, subject, email_content, email_text_context, reference_doctype, 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, def add(email, sender, subject, formatted, text_content=None,
reference_doctype=None, reference_name=None, attachments=None, reply_to=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.recipient = email
e.priority = bulk_priority
e.priority = send_priority


try: try:
mail = get_email(email, sender=sender, formatted=formatted, subject=subject, 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.send_after = send_after
e.insert(ignore_permissions=True) 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 # No limit for own email settings
smtp_server = SMTPServer() smtp_server = SMTPServer()


@@ -147,14 +147,14 @@ def check_bulk_limit(recipients):
or frappe.flags.in_test): or frappe.flags.in_test):


# get count of mails sent this month # 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] 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, def get_unsubscribe_link(reference_doctype, reference_name,
email, recipients, expose_recipients, show_as_cc, email, recipients, expose_recipients, show_as_cc,
@@ -241,21 +241,21 @@ def return_unsubscribed_page(email, doctype, name):
def flush(from_test=False): def flush(from_test=False):
"""flush email queue, every time: called from scheduler""" """flush email queue, every time: called from scheduler"""
# additional check # additional check
check_bulk_limit([])
check_email_limit([])


auto_commit = not from_test auto_commit = not from_test
if frappe.are_emails_muted(): if frappe.are_emails_muted():
msgprint(_("Emails are muted")) msgprint(_("Emails are muted"))
from_test = True 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) where datediff(curdate(), creation) > 7 and status='Not Sent'""", auto_commit=auto_commit)


smtpserver = SMTPServer() smtpserver = SMTPServer()


for i in xrange(500): for i in xrange(500):
# don't use for update here, as it leads deadlocks # 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) where status='Not Sent' and (send_after is null or send_after < %(now)s)
order by priority desc, creation asc order by priority desc, creation asc
limit 1''', { 'now': now_datetime() }, as_dict=True) limit 1''', { 'now': now_datetime() }, as_dict=True)
@@ -272,15 +272,15 @@ def flush(from_test=False):
# frappe.db.commit() # frappe.db.commit()


def send_one(email, smtpserver=None, auto_commit=True, now=False): 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': if status != 'Not Sent':
# rollback to release lock and return # rollback to release lock and return
frappe.db.rollback() frappe.db.rollback()
return 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) (now_datetime(), email.name), auto_commit=auto_commit)


if email.communication: 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.setup_email_account(email.reference_doctype)
smtpserver.sess.sendmail(email.sender, email.recipient, encode(email.message)) 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) (now_datetime(), email.name), auto_commit=auto_commit)


if email.communication: if email.communication:
@@ -305,7 +305,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False):
JobTimeoutException): JobTimeoutException):


# bad connection/timeout, retry later # 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) (now_datetime(), email.name), auto_commit=auto_commit)


if email.communication: if email.communication:
@@ -317,7 +317,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False):
except Exception, e: except Exception, e:
frappe.db.rollback() 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) where name=%s""", (unicode(e), email.name), auto_commit=auto_commit)


if email.communication: if email.communication:
@@ -328,14 +328,14 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False):


else: else:
# log to scheduler log # log to scheduler log
log('frappe.email.bulk.flush', unicode(e))
log('frappe.email.queue.flush', unicode(e))


def clear_outbox(): def clear_outbox():
"""Remove mails older than 31 days in Outbox. Called daily via scheduler.""" """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""") datediff(now(), creation) > 31""")


def prevent_bulk_email_delete(doc, method):
def prevent_email_queue_delete(doc, method):
from frappe.limits import get_limits from frappe.limits import get_limits
if frappe.session.user != 'Administrator' and get_limits().get('block_bulk_email_delete'): if frappe.session.user != 'Administrator' and get_limits().get('block_bulk_email_delete'):
frappe.throw(_('Only Administrator can delete Bulk Email'))
frappe.throw(_('Only Administrator can delete Email Queue'))

+ 4
- 4
frappe/hooks.py View File

@@ -99,8 +99,8 @@ doc_events = {
"User": { "User": {
"validate": "frappe.utils.user.validate_user_limit" "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", "after_insert": "frappe.email.doctype.email_alert.email_alert.trigger_email_alerts",
@@ -124,7 +124,7 @@ doc_events = {


scheduler_events = { scheduler_events = {
"all": [ "all": [
"frappe.email.bulk.flush",
"frappe.email.queue.flush",
"frappe.email.doctype.email_account.email_account.pull", "frappe.email.doctype.email_account.email_account.pull",
"frappe.email.doctype.email_account.email_account.notify_unreplied", "frappe.email.doctype.email_account.email_account.notify_unreplied",
"frappe.utils.error.collect_error_snapshots", "frappe.utils.error.collect_error_snapshots",
@@ -132,7 +132,7 @@ scheduler_events = {
'frappe.model.utils.list_settings.sync_list_settings' 'frappe.model.utils.list_settings.sync_list_settings'
], ],
"daily": [ "daily": [
"frappe.email.bulk.clear_outbox",
"frappe.email.queue.clear_outbox",
"frappe.desk.notifications.clear_notifications", "frappe.desk.notifications.clear_notifications",
"frappe.core.doctype.scheduler_log.scheduler_log.set_old_logs_as_seen", "frappe.core.doctype.scheduler_log.scheduler_log.set_old_logs_as_seen",
"frappe.desk.doctype.event.event.send_event_digest", "frappe.desk.doctype.event.event.send_event_digest",


+ 2
- 2
frappe/limits.py View File

@@ -35,7 +35,7 @@ def check_if_expired():
# if expired, stop user from logging in # if expired, stop user from logging in
expires_on = formatdate(get_limits().get("expiry")) expires_on = formatdate(get_limits().get("expiry"))
support_email = get_limits().get("support_email") or _("your provider") support_email = get_limits().get("support_email") or _("your provider")
frappe.throw(_("""Your subscription expired on {0}. frappe.throw(_("""Your subscription expired on {0}.
To extend please send an email to {1}""").format(expires_on, support_email), To extend please send an email to {1}""").format(expires_on, support_email),
SiteExpiredError) SiteExpiredError)
@@ -72,7 +72,7 @@ def get_expiry_message():
def get_limits(): def get_limits():
limits = frappe.get_conf().get("limits") or {} limits = frappe.get_conf().get("limits") or {}
day = frappe.utils.add_months(frappe.utils.today(), -1) 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 return limits






+ 1
- 0
frappe/patches.txt View File

@@ -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_doc('core', 'doctype', 'patch_log') #2016-10-31
execute:frappe.reload_doctype("File") # 2015-10-19 execute:frappe.reload_doctype("File") # 2015-10-19
execute:frappe.reload_doc('core', 'doctype', 'error_snapshot') 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("alter table `tabSessions` modify `user` varchar(255), engine=InnoDB")
execute:frappe.db.sql("delete from `tabDocField` where parent='0'") execute:frappe.db.sql("delete from `tabDocField` where parent='0'")


+ 3
- 3
frappe/patches/v5_0/rename_ref_type_fieldnames.py View File

@@ -6,14 +6,14 @@ import frappe


def execute(): def execute():
try: 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: except Exception, e:
if e.args[0] not in (1054, 1060): if e.args[0] not in (1054, 1060):
raise raise


try: 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: except Exception, e:
if e.args[0] not in (1054, 1060): if e.args[0] not in (1054, 1060):
raise raise
frappe.reload_doctype("Bulk Email")
frappe.reload_doctype("Email Queue")

+ 1
- 1
frappe/patches/v5_0/v4_to_v5.py View File

@@ -9,7 +9,7 @@ def execute():
("desk", ("feed", "event", "event_role", "todo", "note")), ("desk", ("feed", "event", "event_role", "todo", "note")),
("custom", ("custom_field", "custom_script", "customize_form", ("custom", ("custom_field", "custom_script", "customize_form",
"customize_form_field", "property_setter")), "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")), ("geo", ("country", "currency")),
("print", ("letter_head", "print_format", "print_settings")) ("print", ("letter_head", "print_format", "print_settings"))
) )


+ 4
- 0
frappe/patches/v7_0/rename_bulk_email_to_email_queue.py View File

@@ -0,0 +1,4 @@
import frappe

def execute():
frappe.rename_doc('DocType', 'Bulk Email', 'Email Queue')

+ 1
- 1
frappe/patches/v7_0/update_send_after_in_bulk_email.py View File

@@ -2,4 +2,4 @@ import frappe
from frappe.utils import now_datetime from frappe.utils import now_datetime


def execute(): def execute():
frappe.db.sql('update `tabBulk Email` set send_after=%s where send_after is null', now_datetime())
frappe.db.sql('update `tabEmail Queue` set send_after=%s where send_after is null', now_datetime())

+ 1
- 1
frappe/templates/includes/comments/comments.py View File

@@ -49,7 +49,7 @@ def add_comment(args=None):
message += "<p><a href='{0}/{1}' style='font-size: 80%'>{2}</a></p>".format(frappe.utils.get_request_site_address(), message += "<p><a href='{0}/{1}' style='font-size: 80%'>{2}</a></p>".format(frappe.utils.get_request_site_address(),
page_name, _("View it in your browser")) page_name, _("View it in your browser"))


from frappe.email.bulk import send
from frappe.email.queue import send


send(recipients=recipients, send(recipients=recipients,
subject = _("New comment on {0} {1}").format(doc.doctype, doc.name), subject = _("New comment on {0} {1}").format(doc.doctype, doc.name),


+ 1
- 1
frappe/tests/test_db.py View File

@@ -26,4 +26,4 @@ class TestDB(unittest.TestCase):


def test_multiple_queries(self): def test_multiple_queries(self):
# implicit commit # 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`""")

+ 39
- 39
frappe/tests/test_email.py View File

@@ -12,81 +12,81 @@ make_test_records("Email Account")
class TestEmail(unittest.TestCase): class TestEmail(unittest.TestCase):
def setUp(self): def setUp(self):
frappe.db.sql("""delete from `tabEmail Unsubscribe`""") 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): def test_send(self):
from frappe.email import sendmail from frappe.email import sendmail
sendmail('test@example.com', subject='Test Mail', msg="Test Content") 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'], send(recipients = ['test@example.com', 'test1@example.com'],
sender="admin@example.com", sender="admin@example.com",
reference_doctype='User', reference_name='Administrator', 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): 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) 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): 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) 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): 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) 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): 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") unsubscribe(doctype="User", name="Administrator", email="test@example.com")


self.assertTrue(frappe.db.get_value("Email Unsubscribe", self.assertTrue(frappe.db.get_value("Email Unsubscribe",
{"reference_doctype": "User", "reference_name": "Administrator", "email": "test@example.com"})) {"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'], send(recipients = ['test@example.com', 'test1@example.com'],
sender="admin@example.com", sender="admin@example.com",
reference_doctype='User', reference_name= "Administrator", 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 (?) # 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) 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, recipients=['test@example.com']*1000,
sender="admin@example.com", sender="admin@example.com",
reference_doctype = "User", reference_name="Administrator", 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): def test_image_parsing(self):
import re import re


Loading…
Cancel
Save