[rename] Bulk Email to Email Queueversion-14
@@ -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}): | |||
@@ -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, | |||
] |
@@ -48,7 +48,7 @@ def get_data(): | |||
}, | |||
{ | |||
"type": "doctype", | |||
"name": "Bulk Email", | |||
"name": "Email Queue", | |||
"description": _("Background Email Queue"), | |||
}, | |||
{ | |||
@@ -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): | |||
@@ -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' | |||
@@ -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: | |||
@@ -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. | |||
@@ -66,7 +66,7 @@ def send_event_digest(): | |||
+ 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"), | |||
content = text, bulk=True) | |||
content = text) | |||
@frappe.whitelist() | |||
def get_events(start, end, user=None, for_reminder=False): | |||
@@ -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 |
@@ -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) |
@@ -1,4 +0,0 @@ | |||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||
# MIT License. See license.txt | |||
from __future__ import unicode_literals |
@@ -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 | |||
} |
@@ -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 |
@@ -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) | |||
@@ -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() | |||
@@ -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): | |||
@@ -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"})) |
@@ -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() { |
@@ -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 | |||
} |
@@ -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') |
@@ -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]; |
@@ -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 |
@@ -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')) | |||
frappe.throw(_('Only Administrator can delete Email Queue')) |
@@ -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", | |||
@@ -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 | |||
@@ -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'") | |||
@@ -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") |
@@ -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")) | |||
) | |||
@@ -0,0 +1,4 @@ | |||
import frappe | |||
def execute(): | |||
frappe.rename_doc('DocType', 'Bulk Email', 'Email Queue') |
@@ -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()) | |||
frappe.db.sql('update `tabEmail Queue` set send_after=%s where send_after is null', now_datetime()) |
@@ -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(), | |||
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), | |||
@@ -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`""") |
@@ -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 | |||