Merge pull request #2766 from manassolanki/newsletter

[Fix] Newsletter
This commit is contained in:
Nabin Hait 2017-03-07 10:56:50 +05:30 committed by GitHub
commit 86da93ca24
10 changed files with 368 additions and 87 deletions

View file

@ -81,7 +81,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-17 17:01:07.647800",
"modified": "2017-02-27 19:01:17.203845",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Group",

View file

@ -24,6 +24,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Sender",
@ -52,6 +53,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Recipient",
@ -80,6 +82,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Show as cc",
@ -108,6 +111,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Message",
@ -136,6 +140,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
@ -164,6 +169,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Error",
@ -191,6 +197,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Message ID",
@ -219,6 +226,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference DocType",
@ -247,6 +255,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Reference DocName",
@ -274,6 +283,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Communication",
@ -303,6 +313,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Send After",
@ -332,6 +343,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Priority",
@ -349,6 +361,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "add_unsubscribe_link",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Add Unsubscribe Link",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -360,6 +402,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Unsubscribe Param",
@ -388,6 +431,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Unsubscribe Method",
@ -416,6 +460,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Expose Recipients",
@ -445,7 +490,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-01-19 00:47:10.751497",
"modified": "2017-02-24 17:42:10.878546",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Queue",
@ -461,7 +506,6 @@
"export": 0,
"if_owner": 0,
"import": 0,
"is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@ -476,6 +520,7 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0

View file

@ -54,4 +54,4 @@ cur_frm.cscript.setup_dashboard = function() {
]);
}
}
}
}

View file

@ -13,13 +13,42 @@
"editable_grid": 0,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "receipient",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Receipient",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "email_group",
"fieldtype": "Link",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -30,7 +59,7 @@
"label": "Email Group",
"length": 0,
"no_copy": 0,
"options": "Email Group",
"options": "Newsletter Email Group",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -38,35 +67,7 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "subject",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Subject",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
@ -128,35 +129,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "scheduled_to_send",
"fieldtype": "Int",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Scheduled To Send",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -185,6 +157,34 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "subject",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Subject",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -213,6 +213,66 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "send_unsubscribe_link",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Send Unsubscribe Link",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "send_attachements",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Send Attachements",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -299,6 +359,35 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "scheduled_to_send",
"fieldtype": "Int",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Scheduled To Send",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
@ -311,9 +400,9 @@
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"max_attachments": 3,
"menu_index": 0,
"modified": "2017-02-17 17:01:20.598041",
"modified": "2017-02-28 18:42:52.346905",
"modified_by": "Administrator",
"module": "Email",
"name": "Newsletter",

View file

@ -13,6 +13,8 @@ from frappe.utils.background_jobs import enqueue
from frappe.utils.scheduler import log
from frappe.email.queue import send
from frappe.email.doctype.email_group.email_group import add_subscribers
from frappe.utils.file_manager import get_file
class Newsletter(Document):
def autoname(self):
@ -63,11 +65,24 @@ class Newsletter(Document):
if not frappe.flags.in_test:
frappe.db.auto_commit_on_many_writes = True
attachments = []
if self.send_attachements:
files = frappe.get_all("File", fields = ["name"], filters = {"attached_to_doctype": "Newsletter",
"attached_to_name":self.name}, order_by="creation desc")
for file in files:
try:
file = get_file(file.name)
attachments.append({"fname": file[0], "fcontent": file[1]})
except IOError:
frappe.throw(_("Unable to find attachment {0}").format(a))
send(recipients = self.recipients, sender = sender,
subject = self.subject, message = self.message,
reference_doctype = self.doctype, reference_name = self.name,
add_unsubscribe_link = self.send_unsubscribe_link, attachments=attachments,
unsubscribe_method = "/api/method/frappe.email.doctype.newsletter.newsletter.unsubscribe",
unsubscribe_params = {"name": self.email_group},
unsubscribe_params = {"name": self.name},
send_priority = 0, queue_separately=True)
if not frappe.flags.in_test:
@ -75,8 +90,12 @@ class Newsletter(Document):
def get_recipients(self):
"""Get recipients from Email Group"""
return [d.email for d in frappe.db.get_all("Email Group Member", ["email"],
{"unsubscribed": 0, "email_group": self.email_group})]
recipients_list = []
for email_group in get_email_groups(self.name):
for d in frappe.db.get_all("Email Group Member", ["email"],
{"unsubscribed": 0, "email_group": email_group.email_group}):
recipients_list.append(d.email)
return list(set(recipients_list))
def validate_send(self):
if self.get("__islocal"):
@ -84,24 +103,38 @@ class Newsletter(Document):
check_email_limit(self.recipients)
def get_email_groups(name):
return frappe.db.get_all("Newsletter Email Group", ["email_group"],{"parent":name, "parenttype":"Newsletter"})
@frappe.whitelist(allow_guest=True)
def unsubscribe(email, name):
if not verify_request():
return
subs_id = frappe.db.get_value("Email Group Member", {"email": email, "email_group": name})
if subs_id:
subscriber = frappe.get_doc("Email Group Member", subs_id)
subscriber.unsubscribed = 1
subscriber.save(ignore_permissions=True)
primary_action = frappe.utils.get_url() + "/api/method/frappe.email.doctype.newsletter.newsletter.confirmed_unsubscribe"+\
"?" + get_signed_params({"email": email, "name":name})
return_confirmation_page(email, name, primary_action)
@frappe.whitelist(allow_guest=True)
def confirmed_unsubscribe(email, name):
if not verify_request():
return
for email_group in get_email_groups(name):
frappe.db.sql('''update `tabEmail Group Member` set unsubscribed=1 where email=%s and email_group=%s''',(email, email_group.email_group))
frappe.db.commit()
return_unsubscribed_page(email, name)
return_unsubscribed_page(email)
def return_confirmation_page(email, name, primary_action):
frappe.respond_as_web_page(_("Unsubscribe from Newsletter"),_("Do you want to unsubscribe from this mailing list?"),
indicator_color="blue", primary_label = _("Unsubscribe"), primary_action=primary_action)
def return_unsubscribed_page(email):
frappe.respond_as_web_page(_("Unsubscribed"),
_("{0} has been successfully unsubscribed from this list.").format(email), indicator_color='green')
def return_unsubscribed_page(email, name):
frappe.respond_as_web_page(_("Unsubscribed from Newsletter"),
_("<b>{0}</b> has been successfully unsubscribed from this mailing list.").format(email, name), indicator_color='green')
def create_lead(email_id):
"""create a lead if it does not exist"""

View file

@ -4,7 +4,7 @@ from __future__ import unicode_literals
import frappe, unittest
from frappe.email.doctype.newsletter.newsletter import unsubscribe
from frappe.email.doctype.newsletter.newsletter import confirmed_unsubscribe
from urllib import unquote
emails = ["test_subscriber1@example.com", "test_subscriber2@example.com",
@ -18,7 +18,7 @@ class TestNewsletter(unittest.TestCase):
"doctype": "Email Group Member",
"email": email,
"email_group": "_Test Email Group"
}).insert()
}).insert()
def test_send(self):
self.send_newsletter()
@ -36,7 +36,7 @@ class TestNewsletter(unittest.TestCase):
flush(from_test=True)
to_unsubscribe = unquote(frappe.local.flags.signed_query_string.split("email=")[1].split("&")[0])
unsubscribe(to_unsubscribe, "_Test Email Group")
confirmed_unsubscribe(to_unsubscribe, "_Test Newsletter")
self.send_newsletter()
@ -54,11 +54,12 @@ class TestNewsletter(unittest.TestCase):
newsletter = frappe.get_doc({
"doctype": "Newsletter",
"subject": "_Test Newsletter",
"email_group": "_Test Email Group",
"send_from": "Test Sender <test_sender@example.com>",
"message": "Testing my news."
"message": "Testing my news.",
}).insert(ignore_permissions=True)
newsletter.append("email_group", {"email_group": "_Test Email Group"})
newsletter.save()
newsletter.send_emails()
test_dependencies = ["Email Group"]

View file

@ -0,0 +1,100 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2017-02-26 16:20:52.654136",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "email_group",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Email Group",
"length": 0,
"no_copy": 0,
"options": "Email Group",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "total_subscribers",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Total Subscribers",
"length": 0,
"no_copy": 0,
"options": "email_group.total_subscribers",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-02-26 16:23:57.351167",
"modified_by": "Administrator",
"module": "Email",
"name": "Newsletter Email Group",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View file

@ -0,0 +1,10 @@
# -*- 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
class NewsletterEmailGroup(Document):
pass

View file

@ -20,7 +20,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
attachments=None, reply_to=None, cc=[], message_id=None, in_reply_to=None, send_after=None,
expose_recipients=None, send_priority=1, communication=None, now=False, read_receipt=None,
queue_separately=False):
queue_separately=False, add_unsubscribe_link=1):
"""Add email to sending queue (Email Queue)
:param recipients: List of recipients.
@ -39,6 +39,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
:param communication: Communication link to be set in Email Queue record
:param now: Send immediately (don't send in the background)
:param queue_separately: Queue each email separately
:param add_unsubscribe_link: Send unsubscribe link in the footer of the Email, default 1.
"""
if not unsubscribe_method:
unsubscribe_method = "/api/method/frappe.email.queue.unsubscribe"
@ -82,7 +83,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
email_content = formatted
email_text_context = text_content
if reference_doctype and (unsubscribe_message or reference_doctype=="Newsletter"):
if add_unsubscribe_link and reference_doctype and (unsubscribe_message or reference_doctype=="Newsletter") and add_unsubscribe_link==1:
unsubscribe_link = get_unsubscribe_message(unsubscribe_message, expose_recipients)
email_content = email_content.replace("<!--unsubscribe link here-->", unsubscribe_link.html)
email_text_context += unsubscribe_link.text
@ -102,6 +103,7 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
send_priority=send_priority,
email_account=email_account,
communication=communication,
add_unsubscribe_link=add_unsubscribe_link,
unsubscribe_method=unsubscribe_method,
unsubscribe_params=unsubscribe_params,
expose_recipients=expose_recipients,
@ -167,6 +169,7 @@ def get_email_queue(recipients, sender, subject, **kwargs):
e.set_recipients(recipients + kwargs.get('cc', []))
e.reference_doctype = kwargs.get('reference_doctype')
e.reference_name = kwargs.get('reference_name')
e.add_unsubscribe_link = kwargs.get("add_unsubscribe_link")
e.unsubscribe_method = kwargs.get('unsubscribe_method')
e.unsubscribe_params = kwargs.get('unsubscribe_params')
e.expose_recipients = kwargs.get('expose_recipients')
@ -322,7 +325,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals
email = frappe.db.sql('''select
name, status, communication, message, sender, reference_doctype,
reference_name, unsubscribe_param, unsubscribe_method, expose_recipients,
show_as_cc
show_as_cc, add_unsubscribe_link
from
`tabEmail Queue`
where
@ -423,7 +426,7 @@ where name=%s""", (unicode(e), email.name), auto_commit=auto_commit)
def prepare_message(email, recipient, recipients_list):
message = email.message
if email.reference_doctype: # is missing the check for unsubscribe message but will not add as there will be no unsubscribe url
if email.add_unsubscribe_link and email.reference_doctype: # is missing the check for unsubscribe message but will not add as there will be no unsubscribe url
unsubscribe_url = get_unsubcribed_url(email.reference_doctype, email.reference_name, recipient,
email.unsubscribe_method, email.unsubscribe_params)
message = message.replace("<!--unsubscribe url-->", quopri.encodestring(unsubscribe_url))