Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
# For license information, please see license.txt

import frappe
from frappe.email.doctype.email_template.email_template import get_email_template
from frappe.model.document import Document
from frappe.utils import get_url

from buzz.payments import mark_payment_as_received
from buzz.utils import render_email_template


class SponsorshipEnquiry(Document):
Expand Down Expand Up @@ -91,7 +91,7 @@ def send_pitch_deck(self, now=False):
frappe.log_error("No sponsor deck email template configured", "Sponsorship Enquiry")
return

email_template = get_email_template(template_name, {"doc": self, "event": event})
email_template = render_email_template(template_name, {"doc": self, "event": event})

subject = email_template.get("subject")
content = email_template.get("message")
Expand Down
4 changes: 2 additions & 2 deletions buzz/ticketing/doctype/event_booking/event_booking.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.email.doctype.email_template.email_template import get_email_template
from frappe.model.document import Document
from frappe.utils import cstr, flt

Expand All @@ -13,6 +12,7 @@
get_committed_tickets,
record_gateway_refund,
)
from buzz.utils import render_email_template

RAZORPAY = "Razorpay"

Expand Down Expand Up @@ -222,7 +222,7 @@ def send_booking_confirmation_email(self):

content = None
if booking_template:
email_template = get_email_template(booking_template, args)
email_template = render_email_template(booking_template, args)
subject = email_template.get("subject") or subject
content = email_template.get("message")

Expand Down
11 changes: 7 additions & 4 deletions buzz/ticketing/doctype/event_ticket/event_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@
from frappe.core.api.user_invitation import invite_by_email
from frappe.model.document import Document

from buzz.utils import generate_ics_file, generate_qr_code_file, only_if_app_installed
from buzz.utils import (
generate_ics_file,
generate_qr_code_file,
only_if_app_installed,
render_email_template,
)


class EventTicket(Document):
Expand Down Expand Up @@ -122,9 +127,7 @@ def send_ticket_email(self, now: bool = False):
}

if ticket_template:
from frappe.email.doctype.email_template.email_template import get_email_template

email_template = get_email_template(ticket_template, args)
email_template = render_email_template(ticket_template, args)
subject = email_template.get("subject")
content = email_template.get("message")

Expand Down
86 changes: 85 additions & 1 deletion buzz/ticketing/doctype/event_ticket/test_event_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import frappe
from frappe.tests import IntegrationTestCase

from buzz.utils import generate_qr_code_file, make_qr_image
from buzz.utils import generate_qr_code_file, make_qr_image, render_email_template

EXTRA_TEST_RECORD_DEPENDENCIES = []
IGNORE_TEST_RECORD_DEPENDENCIES = ["Bulk Ticket Coupon"]
Expand Down Expand Up @@ -304,3 +304,87 @@ def test_ticket_details_expose_the_zoom_session_reference(self):
self.assertEqual(details.zoom_join_url, registrant["join_url"])
self.assertEqual(details.zoom_reference_doctype, "Zoom Meeting")
self.assertEqual(details.zoom_reference_name, meeting.name)


class TestRenderEmailTemplate(IntegrationTestCase):
"""Attendees and sponsors are Website Users; only Desk Users can read an Email Template."""

def tearDown(self):
frappe.db.rollback()

def test_renders_for_a_user_without_email_template_permission(self):
template = frappe.get_doc(
{
"doctype": "Email Template",
"name": "Unprivileged Render Template",
"subject": "NOPERM - {{ event_title }}",
"response": "<p>NOPERM content</p>",
}
).insert(ignore_permissions=True)

attendee = "attendee-render@example.com"
if not frappe.db.exists("User", attendee):
frappe.get_doc(
{
"doctype": "User",
"email": attendee,
"first_name": "Attendee",
"user_type": "Website User",
"send_welcome_email": 0,
}
).insert(ignore_permissions=True)
self.addCleanup(frappe.set_user, frappe.session.user)
frappe.set_user(attendee)
self.assertFalse(frappe.has_permission("Email Template", "read"))

rendered = render_email_template(template.name, {"event_title": "Buzz Conf"})

self.assertEqual(rendered["subject"], "NOPERM - Buzz Conf")
self.assertIn("NOPERM content", rendered["message"])


class TestGuestTicketEmail(IntegrationTestCase):
"""Public bookings submit their tickets as Guest."""

def setUp(self):
self.event = frappe.get_doc("Buzz Event", {"route": "test-route"})
self.ticket_type = frappe.get_doc(
{
"doctype": "Event Ticket Type",
"event": self.event.name,
"title": "Guest Email Ticket",
"price": 0,
}
).insert(ignore_permissions=True)
self.template = frappe.get_doc(
{
"doctype": "Email Template",
"name": "Guest Ticket Template",
"subject": "GUEST - {{ event_title }}",
"response": "<p>Guest content</p>",
}
).insert(ignore_permissions=True)
self.event.db_set("ticket_email_template", self.template.name)
self.addCleanup(frappe.set_user, frappe.session.user)

def tearDown(self):
frappe.db.rollback()

@patch("frappe.sendmail")
def test_guest_can_render_the_ticket_email_template(self, mock_sendmail):
ticket = frappe.get_doc(
{
"doctype": "Event Ticket",
"event": self.event.name,
"ticket_type": self.ticket_type.name,
"first_name": "Guest",
"attendee_email": "guest-booking@example.com",
}
).insert(ignore_permissions=True)

frappe.set_user("Guest")

ticket.send_ticket_email(now=True)

mock_sendmail.assert_called_once()
self.assertIn("GUEST", mock_sendmail.call_args[1]["subject"])
9 changes: 9 additions & 0 deletions buzz/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,12 @@ def get_time_zone_label(time_zone: str | None, reference_datetime: datetime | No
if minutes:
label += f":{minutes:02d}"
return label


def render_email_template(template_name: str, args: dict) -> dict:
"""Render an Email Template without a permission check.

Frappe's get_email_template() is whitelisted and reads the template as the session
user, which fails for the guest-facing booking and enquiry flows.
"""
return frappe.get_doc("Email Template", template_name).get_formatted_email(args)
Loading