Skip to content

Commit

Permalink
✨ [#4650] Using variable value as email recipient
Browse files Browse the repository at this point in the history
The variable chosen in the email registration (`to_email_from_variable`) will now actually be used for the mailing. `to_emails` is used as fallback, in case that the variable doesn't return a valid email address.

The variable will also be used for the payment status update mailing. If `payment_emails` is defined, these will be used as recipients. Otherwise it will use `to_email_from_variable`, and as a last resort the `to_emails`
  • Loading branch information
robinmolen committed Dec 24, 2024
1 parent 473b0bb commit 575b635
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 2 deletions.
37 changes: 35 additions & 2 deletions src/openforms/registrations/contrib/email/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from typing import Any

from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import get_language_info, gettext_lazy as _
Expand Down Expand Up @@ -42,11 +44,41 @@ class EmailRegistration(BasePlugin[Options]):
verbose_name = _("Email registration")
configuration_options = EmailOptionsSerializer

def get_recipients(self, submission: Submission, options: Options) -> list[str]:
state = submission.load_submission_value_variables_state()
recipients = []

# If the 'recipients from variable' is used and it exists
if (
(variable_key := options.get("to_emails_from_variable"))
and variable_key in state.variables
and (variable_value := state.variables[variable_key].value)
):
# To simplify things, treat all variable values as lists
if type(variable_value) != list:
variable_value = [variable_value]

# Only if all email addresses are valid, they will be used as recipients
try:
for value in variable_value:
validate_email(value)
except ValidationError:
pass
else:
recipients = variable_value

# If the variable cannot be used, fallback to the general email addresses
if not recipients and "to_emails" in options:
recipients = options["to_emails"]

return recipients

def register_submission(self, submission: Submission, options: Options) -> None:
config = EmailConfig.get_solo()
config.apply_defaults_to(options)

self.send_registration_email(options["to_emails"], submission, options)
recipients = self.get_recipients(submission, options)
self.send_registration_email(recipients, submission, options)

# ensure that the payment email is also sent if registration is deferred until
# payment is completed
Expand Down Expand Up @@ -187,8 +219,9 @@ def send_registration_email(

def update_payment_status(self, submission: "Submission", options: Options):
recipients = options.get("payment_emails")

if not recipients:
recipients = options["to_emails"]
recipients = self.get_recipients(submission, options)

order_ids = submission.payments.get_completed_public_order_ids()
extra_context = {
Expand Down
217 changes: 217 additions & 0 deletions src/openforms/registrations/contrib/email/tests/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,158 @@ def test_submission_with_email_backend(self):
self.assertIn(f"{expected_download_url_1} (my-foo.bin)", message_text)
self.assertIn(f"{expected_download_url_2} (my-bar.txt)", message_text)

def test_submission_with_email_backend_using_to_emails_from_variable(self):
submission = SubmissionFactory.from_components(
completed=True,
components_list=[
{"key": "foo", "type": "textfield", "label": "foo"},
],
submitted_data={"foo": "bar"},
form__registration_backend="email",
)
SubmissionValueVariableFactory.create(
form_variable__source=FormVariableSources.user_defined,
form_variable__name="User defined var 1",
submission=submission,
key="email_recipient_variable",
value="[email protected]",
)
email_form_options = dict(
to_emails_from_variable="email_recipient_variable",
)
email_submission = EmailRegistration("email")

set_submission_reference(submission)

with patch("openforms.registrations.contrib.email.utils.EmailConfig.get_solo"):
email_submission.register_submission(submission, email_form_options)

# Verify that email was sent
self.assertEqual(len(mail.outbox), 1)

message = mail.outbox[0]
self.assertEqual(message.to, ["[email protected]"])

def test_submission_with_email_backend_using_to_emails_from_variable_with_multiple_email_addresses(
self,
):
submission = SubmissionFactory.from_components(
completed=True,
components_list=[
{"key": "foo", "type": "textfield", "label": "foo"},
],
submitted_data={"foo": "bar"},
form__registration_backend="email",
)
SubmissionValueVariableFactory.create(
form_variable__source=FormVariableSources.user_defined,
form_variable__name="User defined var 1",
submission=submission,
key="email_recipient_variable",
value=["[email protected]", "[email protected]"],
)
email_form_options = dict(
to_emails_from_variable="email_recipient_variable",
)
email_submission = EmailRegistration("email")

set_submission_reference(submission)

with patch("openforms.registrations.contrib.email.utils.EmailConfig.get_solo"):
email_submission.register_submission(submission, email_form_options)

# Verify that email was sent
self.assertEqual(len(mail.outbox), 1)

message = mail.outbox[0]
self.assertEqual(message.to, ["[email protected]", "[email protected]"])

def test_submission_with_email_backend_unknown_to_emails_from_variable(self):
submission = SubmissionFactory.from_components(
completed=True,
components_list=[
{"key": "foo", "type": "textfield", "label": "foo"},
],
submitted_data={"foo": "bar"},
form__registration_backend="email",
)
email_form_options = dict(
to_emails_from_variable="email_recipient_variable",
)
email_submission = EmailRegistration("email")

set_submission_reference(submission)

with patch("openforms.registrations.contrib.email.utils.EmailConfig.get_solo"):
email_submission.register_submission(submission, email_form_options)

# Verify that email wasn't sent
self.assertEqual(len(mail.outbox), 0)

def test_submission_with_email_backend_invalid_to_emails_from_variable(self):
submission = SubmissionFactory.from_components(
completed=True,
components_list=[
{"key": "foo", "type": "textfield", "label": "foo"},
],
submitted_data={"foo": "bar"},
form__registration_backend="email",
)
SubmissionValueVariableFactory.create(
form_variable__source=FormVariableSources.user_defined,
form_variable__name="User defined var 1",
submission=submission,
key="email_recipient_variable",
value="foo.com",
)
email_form_options = dict(
to_emails_from_variable="email_recipient_variable",
)
email_submission = EmailRegistration("email")

set_submission_reference(submission)

with patch("openforms.registrations.contrib.email.utils.EmailConfig.get_solo"):
email_submission.register_submission(submission, email_form_options)

# Verify that email wasn't sent
self.assertEqual(len(mail.outbox), 0)

def test_submission_with_email_backend_invalid_to_emails_from_variable_with_fallback(
self,
):
submission = SubmissionFactory.from_components(
completed=True,
components_list=[
{"key": "foo", "type": "textfield", "label": "foo"},
],
submitted_data={"foo": "bar"},
form__registration_backend="email",
)
SubmissionValueVariableFactory.create(
form_variable__source=FormVariableSources.user_defined,
form_variable__name="User defined var 1",
submission=submission,
key="email_recipient_variable",
value="foo.com",
)
email_form_options = dict(
to_emails_from_variable="email_recipient_variable",
to_emails=["[email protected]"],
)
email_submission = EmailRegistration("email")

set_submission_reference(submission)

with patch("openforms.registrations.contrib.email.utils.EmailConfig.get_solo"):
email_submission.register_submission(submission, email_form_options)

# Verify that email was sent
self.assertEqual(len(mail.outbox), 1)

message = mail.outbox[0]
self.assertEqual(message.to, ["[email protected]"])

def test_submission_with_email_backend_strip_out_urls(self):
config = GlobalConfiguration.get_solo()
config.email_template_netloc_allowlist = []
Expand Down Expand Up @@ -477,6 +629,71 @@ def test_register_and_update_paid_product_with_payment_email_recipient(self):
# check we used the payment_emails
self.assertEqual(message.to, ["[email protected]", "[email protected]"])

def test_register_and_update_paid_product_with_payment_email_recipient_and_variable_email_recipient(
self,
):
submission = SubmissionFactory.from_data(
{"voornaam": "Foo"},
form__product__price=Decimal("11.35"),
form__payment_backend="demo",
registration_success=True,
public_registration_reference="XYZ",
)
SubmissionValueVariableFactory.create(
form_variable__source=FormVariableSources.user_defined,
form_variable__name="User defined var 1",
submission=submission,
key="email_recipient_variable",
value="[email protected]",
)

email_form_options = dict(
to_emails=["[email protected]", "[email protected]"],
to_emails_from_variable="email_recipient_variable",
# payment_emails would override to_emails and to_emails_from_variable
payment_emails=["[email protected]", "[email protected]"],
)
email_submission = EmailRegistration("email")
email_submission.update_payment_status(submission, email_form_options)

self.assertEqual(len(mail.outbox), 1)

message = mail.outbox[0]
# check we used the payment_emails
self.assertEqual(message.to, ["[email protected]", "[email protected]"])

def test_register_and_update_paid_product_with_variable_email_recipient(
self,
):
submission = SubmissionFactory.from_data(
{"voornaam": "Foo"},
form__product__price=Decimal("11.35"),
form__payment_backend="demo",
registration_success=True,
public_registration_reference="XYZ",
)
SubmissionValueVariableFactory.create(
form_variable__source=FormVariableSources.user_defined,
form_variable__name="User defined var 1",
submission=submission,
key="email_recipient_variable",
value="[email protected]",
)

email_form_options = dict(
to_emails=["[email protected]", "[email protected]"],
# to_emails_from_variable would override to_emails
to_emails_from_variable="email_recipient_variable",
)
email_submission = EmailRegistration("email")
email_submission.update_payment_status(submission, email_form_options)

self.assertEqual(len(mail.outbox), 1)

message = mail.outbox[0]
# check we used the payment_emails
self.assertEqual(message.to, ["[email protected]"])

@override_settings(DEFAULT_FROM_EMAIL="[email protected]")
def test_submission_with_email_backend_export_csv_xlsx(self):
email_form_options = dict(
Expand Down

0 comments on commit 575b635

Please sign in to comment.