From e5f428ab8b4b2a11b0506acbfa47023392cd73cc Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 24 Nov 2023 19:36:24 +0100 Subject: [PATCH] feat(Newsletter): redirect to welcome URL --- .../email/doctype/email_group/email_group.js | 159 ++++++++++-------- .../doctype/email_group/email_group.json | 33 +++- .../email/doctype/email_group/email_group.py | 34 ++++ .../doctype/email_group/test_email_group.py | 25 ++- frappe/email/doctype/newsletter/newsletter.py | 24 ++- 5 files changed, 197 insertions(+), 78 deletions(-) diff --git a/frappe/email/doctype/email_group/email_group.js b/frappe/email/doctype/email_group/email_group.js index 5ad4a39dd9..bfe1577594 100644 --- a/frappe/email/doctype/email_group/email_group.js +++ b/frappe/email/doctype/email_group/email_group.js @@ -1,74 +1,97 @@ // Copyright (c) 2016, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on("Email Group", "refresh", function (frm) { - if (!frm.is_new()) { - frm.add_custom_button( - __("Import Subscribers"), - function () { - frappe.prompt( - { - fieldtype: "Select", - options: frm.doc.__onload.import_types, - label: __("Import Email From"), - fieldname: "doctype", - reqd: 1, - }, - function (data) { - frappe.call({ - method: "frappe.email.doctype.email_group.email_group.import_from", - args: { - name: frm.doc.name, - doctype: data.doctype, - }, - callback: function (r) { - frm.set_value("total_subscribers", r.message); - }, - }); - }, - __("Import Subscribers"), - __("Import") - ); - }, - __("Action") - ); +frappe.ui.form.on("Email Group", { + refresh: function (frm) { + if (!frm.is_new()) { + frm.add_custom_button( + __("Import Subscribers"), + function () { + frappe.prompt( + { + fieldtype: "Select", + options: frm.doc.__onload.import_types, + label: __("Import Email From"), + fieldname: "doctype", + reqd: 1, + }, + function (data) { + frappe.call({ + method: "frappe.email.doctype.email_group.email_group.import_from", + args: { + name: frm.doc.name, + doctype: data.doctype, + }, + callback: function (r) { + frm.set_value("total_subscribers", r.message); + }, + }); + }, + __("Import Subscribers"), + __("Import") + ); + }, + __("Action") + ); - frm.add_custom_button( - __("Add Subscribers"), - function () { - frappe.prompt( - { - fieldtype: "Text", - label: __("Email Addresses"), - fieldname: "email_list", - reqd: 1, - }, - function (data) { - frappe.call({ - method: "frappe.email.doctype.email_group.email_group.add_subscribers", - args: { - name: frm.doc.name, - email_list: data.email_list, - }, - callback: function (r) { - frm.set_value("total_subscribers", r.message); - }, - }); - }, - __("Add Subscribers"), - __("Add") - ); - }, - __("Action") - ); + frm.add_custom_button( + __("Add Subscribers"), + function () { + frappe.prompt( + { + fieldtype: "Text", + label: __("Email Addresses"), + fieldname: "email_list", + reqd: 1, + }, + function (data) { + frappe.call({ + method: "frappe.email.doctype.email_group.email_group.add_subscribers", + args: { + name: frm.doc.name, + email_list: data.email_list, + }, + callback: function (r) { + frm.set_value("total_subscribers", r.message); + }, + }); + }, + __("Add Subscribers"), + __("Add") + ); + }, + __("Action") + ); - frm.add_custom_button( - __("New Newsletter"), - function () { - frappe.route_options = { email_group: frm.doc.name }; - frappe.new_doc("Newsletter"); - }, - __("Action") - ); - } + frm.add_custom_button( + __("New Newsletter"), + function () { + frappe.route_options = { email_group: frm.doc.name }; + frappe.new_doc("Newsletter"); + }, + __("Action") + ); + } + + frm.trigger("preview_welcome_url"); + }, + welcome_url(frm) { + frm.trigger("preview_welcome_url"); + }, + add_query_parameters: function (frm) { + frm.trigger("preview_welcome_url"); + }, + preview_welcome_url: function (frm) { + if (frm.doc.add_query_parameters && frm.doc.welcome_url) { + frm.call("preview_welcome_url", { email: "mail@example.org" }).then((r) => { + frm.set_df_property( + "add_query_parameters", + "description", + `${__("Preview:")} ${r.message}` + ); + }); + } else { + frm.set_df_property("add_query_parameters", "description", ""); + } + }, }); diff --git a/frappe/email/doctype/email_group/email_group.json b/frappe/email/doctype/email_group/email_group.json index cb74249143..1e90beaecb 100644 --- a/frappe/email/doctype/email_group/email_group.json +++ b/frappe/email/doctype/email_group/email_group.json @@ -9,9 +9,13 @@ "engine": "InnoDB", "field_order": [ "title", + "column_break_oyyj", "total_subscribers", + "sign_up_and_confirmation_section", "confirmation_email_template", - "welcome_email_template" + "welcome_email_template", + "welcome_url", + "add_query_parameters" ], "fields": [ { @@ -41,6 +45,29 @@ "fieldtype": "Link", "label": "Welcome Email Template", "options": "Email Template" + }, + { + "fieldname": "column_break_oyyj", + "fieldtype": "Column Break" + }, + { + "fieldname": "sign_up_and_confirmation_section", + "fieldtype": "Section Break", + "label": "Sign Up and Confirmation" + }, + { + "description": "Redirect to this URL after successful confirmation.", + "fieldname": "welcome_url", + "fieldtype": "Data", + "label": "Welcome URL", + "options": "URL" + }, + { + "default": "0", + "depends_on": "welcome_url", + "fieldname": "add_query_parameters", + "fieldtype": "Check", + "label": "Add Query Parameters" } ], "index_web_pages_for_search": 1, @@ -51,10 +78,11 @@ "link_fieldname": "email_group" } ], - "modified": "2021-06-15 11:25:13.556201", + "modified": "2023-11-24 18:35:17.268492", "modified_by": "Administrator", "module": "Email", "name": "Email Group", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { @@ -75,5 +103,6 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/frappe/email/doctype/email_group/email_group.py b/frappe/email/doctype/email_group/email_group.py index c866beebee..f00388a8c8 100755 --- a/frappe/email/doctype/email_group/email_group.py +++ b/frappe/email/doctype/email_group/email_group.py @@ -16,10 +16,12 @@ class EmailGroup(Document): if TYPE_CHECKING: from frappe.types import DF + add_query_parameters: DF.Check confirmation_email_template: DF.Link | None title: DF.Data total_subscribers: DF.Int welcome_email_template: DF.Link | None + welcome_url: DF.Data | None # end: auto-generated types def onload(self): singles = [d.name for d in frappe.get_all("DocType", "name", {"issingle": 1})] @@ -73,6 +75,22 @@ class EmailGroup(Document): self.name, )[0][0] + @frappe.whitelist() + def preview_welcome_url(self, email: str | None = None) -> str | None: + """Get Welcome URL for the email group.""" + return self.get_welcome_url(email) + + def get_welcome_url(self, email: str | None = None) -> str | None: + """Get Welcome URL for the email group.""" + if not self.welcome_url: + return None + + return ( + add_query_params(self.welcome_url, {"email": email, "email_group": self.name}) + if self.add_query_parameters + else self.welcome_url + ) + def on_trash(self): for d in frappe.get_all("Email Group Member", "name", {"email_group": self.name}): frappe.delete_doc("Email Group Member", d.name) @@ -125,3 +143,19 @@ def send_welcome_email(welcome_email, email, email_group): args = dict(email=email, email_group=email_group) message = frappe.render_template(welcome_email.response_, args) frappe.sendmail(email, subject=welcome_email.subject, message=message) + + +def add_query_params(url: str, params: dict) -> str: + from urllib.parse import urlencode, urlparse, urlunparse + + if not params: + return url + + query_string = urlencode(params) + parsed = list(urlparse(url)) + if parsed[4]: + parsed[4] += f"&{query_string}" + else: + parsed[4] = query_string + + return urlunparse(parsed) diff --git a/frappe/email/doctype/email_group/test_email_group.py b/frappe/email/doctype/email_group/test_email_group.py index ffc325a6bd..fdb36825f5 100644 --- a/frappe/email/doctype/email_group/test_email_group.py +++ b/frappe/email/doctype/email_group/test_email_group.py @@ -1,9 +1,32 @@ # Copyright (c) 2015, Frappe Technologies and Contributors # License: MIT. See LICENSE +import frappe from frappe.tests.utils import FrappeTestCase +from frappe.utils import validate_url # test_records = frappe.get_test_records('Email Group') class TestEmailGroup(FrappeTestCase): - pass + def test_welcome_url(self): + email_group = frappe.new_doc("Email Group") + email_group.title = "Test" + email_group.welcome_url = "http://example.com/welcome?hello=world" + email_group.add_query_parameters = 1 + email_group.insert() + + welcome_url = email_group.get_welcome_url("mail@example.org") + self.assertTrue(validate_url(welcome_url)) + self.assertIn(email_group.welcome_url, welcome_url) + self.assertIn("email_group=Test", welcome_url) + self.assertIn("email=mail%40example.org", welcome_url) + + email_group.add_query_parameters = 0 + welcome_url = email_group.get_welcome_url("mail@example.org") + self.assertTrue(validate_url(welcome_url)) + self.assertIn(email_group.welcome_url, welcome_url) + self.assertNotIn("email_group=Test", welcome_url) + self.assertNotIn("email=mail%40example.org", welcome_url) + + email_group.welcome_url = "" + self.assertEqual(email_group.get_welcome_url(), None) diff --git a/frappe/email/doctype/newsletter/newsletter.py b/frappe/email/doctype/newsletter/newsletter.py index efe93bf5ed..b8be390036 100644 --- a/frappe/email/doctype/newsletter/newsletter.py +++ b/frappe/email/doctype/newsletter/newsletter.py @@ -365,19 +365,29 @@ def confirm_subscription(email, email_group=None): if email_group is None: email_group = get_default_email_group() - if not frappe.db.exists("Email Group", email_group): - frappe.get_doc({"doctype": "Email Group", "title": email_group}).insert(ignore_permissions=True) + try: + group = frappe.get_doc("Email Group", email_group) + except frappe.DoesNotExistError: + group = frappe.get_doc({"doctype": "Email Group", "title": email_group}).insert( + ignore_permissions=True + ) frappe.flags.ignore_permissions = True add_subscribers(email_group, email) frappe.db.commit() - frappe.respond_as_web_page( - _("Confirmed"), - _("{0} has been successfully added to the Email Group.").format(email), - indicator_color="green", - ) + welcome_url = group.get_welcome_url(email) + + if welcome_url: + frappe.local.response["type"] = "redirect" + frappe.local.response["location"] = welcome_url + else: + frappe.respond_as_web_page( + _("Confirmed"), + _("{0} has been successfully added to the Email Group.").format(email), + indicator_color="green", + ) def get_list_context(context=None):