diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py index 78439188e..fa4c2d8dc 100644 --- a/src/registrar/config/settings.py +++ b/src/registrar/config/settings.py @@ -107,6 +107,7 @@ DEBUG = env_debug # Controls production specific feature toggles IS_PRODUCTION = env_is_production SECRET_ENCRYPT_METADATA = secret_encrypt_metadata +BASE_URL = env_base_url # Applications are modular pieces of code. # They are provided by Django, by third-parties, or by yourself. diff --git a/src/registrar/templates/emails/action_needed_reasons/bad_name.txt b/src/registrar/templates/emails/action_needed_reasons/bad_name.txt index ac563b549..40e5ed899 100644 --- a/src/registrar/templates/emails/action_needed_reasons/bad_name.txt +++ b/src/registrar/templates/emails/action_needed_reasons/bad_name.txt @@ -17,7 +17,7 @@ Domains should uniquely identify a government organization and be clear to the g ACTION NEEDED -First, we need you to identify a new domain name that meets our naming requirements for your type of organization. Then, log in to the registrar and update the name in your domain request. Once you submit your updated request, we’ll resume the adjudication process. +First, we need you to identify a new domain name that meets our naming requirements for your type of organization. Then, log in to the registrar and update the name in your domain request. <{{ manage_url }}> Once you submit your updated request, we’ll resume the adjudication process. If you have questions or want to discuss potential domain names, reply to this email. diff --git a/src/registrar/templates/emails/action_needed_reasons/questionable_senior_official.txt b/src/registrar/templates/emails/action_needed_reasons/questionable_senior_official.txt index ef05e17d7..40d068cd9 100644 --- a/src/registrar/templates/emails/action_needed_reasons/questionable_senior_official.txt +++ b/src/registrar/templates/emails/action_needed_reasons/questionable_senior_official.txt @@ -21,7 +21,7 @@ We expect a senior official to be someone in a role of significant, executive re ACTION NEEDED Reply to this email with a justification for naming {{ domain_request.senior_official.get_formatted_name }} as the senior official. If you have questions or comments, include those in your reply. -Alternatively, you can log in to the registrar and enter a different senior official for this domain request. Once you submit your updated request, we’ll resume the adjudication process. +Alternatively, you can log in to the registrar and enter a different senior official for this domain request. <{{ manage_url }}> Once you submit your updated request, we’ll resume the adjudication process. THANK YOU diff --git a/src/registrar/templates/emails/domain_invitation.txt b/src/registrar/templates/emails/domain_invitation.txt index a077bff26..270786a7a 100644 --- a/src/registrar/templates/emails/domain_invitation.txt +++ b/src/registrar/templates/emails/domain_invitation.txt @@ -4,7 +4,7 @@ Hi,{% if requested_user and requested_user.first_name %} {{ requested_user.first {{ requestor_email }} has invited you to manage: {% for domain in domains %}{{ domain.name }} {% endfor %} -To manage domain information, visit the .gov registrar . +To manage domain information, visit the .gov registrar <{{ manage_url }}>. ---------------------------------------------------------------- {% if not requested_user %} diff --git a/src/registrar/templates/emails/domain_manager_notification.txt b/src/registrar/templates/emails/domain_manager_notification.txt index c253937e4..b5096a9d8 100644 --- a/src/registrar/templates/emails/domain_manager_notification.txt +++ b/src/registrar/templates/emails/domain_manager_notification.txt @@ -15,7 +15,7 @@ The person who received the invitation will become a domain manager once they lo associated with the invited email address. If you need to cancel this invitation or remove the domain manager, you can do that by going to -this domain in the .gov registrar . +this domain in the .gov registrar <{{ manage_url }}>. WHY DID YOU RECEIVE THIS EMAIL? diff --git a/src/registrar/templates/emails/domain_request_withdrawn.txt b/src/registrar/templates/emails/domain_request_withdrawn.txt index fbdf5b4f1..fe026027b 100644 --- a/src/registrar/templates/emails/domain_request_withdrawn.txt +++ b/src/registrar/templates/emails/domain_request_withdrawn.txt @@ -11,7 +11,7 @@ STATUS: Withdrawn ---------------------------------------------------------------- YOU CAN EDIT YOUR WITHDRAWN REQUEST -You can edit and resubmit this request by signing in to the registrar . +You can edit and resubmit this request by signing in to the registrar <{{ manage_url }}>. SOMETHING WRONG? diff --git a/src/registrar/templates/emails/portfolio_admin_addition_notification.txt b/src/registrar/templates/emails/portfolio_admin_addition_notification.txt index b8953aa67..9e6da3985 100644 --- a/src/registrar/templates/emails/portfolio_admin_addition_notification.txt +++ b/src/registrar/templates/emails/portfolio_admin_addition_notification.txt @@ -16,7 +16,7 @@ The person who received the invitation will become an admin once they log in to associated with the invited email address. If you need to cancel this invitation or remove the admin, you can do that by going to -the Members section for your organization . +the Members section for your organization <{{ manage_url }}>. WHY DID YOU RECEIVE THIS EMAIL? diff --git a/src/registrar/templates/emails/portfolio_admin_removal_notification.txt b/src/registrar/templates/emails/portfolio_admin_removal_notification.txt index 6a536aa49..bf0338c03 100644 --- a/src/registrar/templates/emails/portfolio_admin_removal_notification.txt +++ b/src/registrar/templates/emails/portfolio_admin_removal_notification.txt @@ -8,7 +8,7 @@ REMOVED BY: {{ requestor_email }} REMOVED ON: {{date}} ADMIN REMOVED: {{ removed_email_address }} -You can view this update by going to the Members section for your .gov organization . +You can view this update by going to the Members section for your .gov organization <{{ manage_url }}>. ---------------------------------------------------------------- diff --git a/src/registrar/templates/emails/portfolio_invitation.txt b/src/registrar/templates/emails/portfolio_invitation.txt index 775b74c7c..893da153d 100644 --- a/src/registrar/templates/emails/portfolio_invitation.txt +++ b/src/registrar/templates/emails/portfolio_invitation.txt @@ -3,7 +3,7 @@ Hi. {{ requestor_email }} has invited you to {{ portfolio.organization_name }}. -You can view this organization on the .gov registrar . +You can view this organization on the .gov registrar <{{ manage_url }}>. ---------------------------------------------------------------- diff --git a/src/registrar/templates/emails/status_change_approved.txt b/src/registrar/templates/emails/status_change_approved.txt index 821e89e42..635b36cbd 100644 --- a/src/registrar/templates/emails/status_change_approved.txt +++ b/src/registrar/templates/emails/status_change_approved.txt @@ -8,7 +8,7 @@ REQUESTED BY: {{ domain_request.creator.email }} REQUEST RECEIVED ON: {{ domain_request.last_submitted_date|date }} STATUS: Approved -You can manage your approved domain on the .gov registrar . +You can manage your approved domain on the .gov registrar <{{ manage_url }}>. ---------------------------------------------------------------- diff --git a/src/registrar/templates/emails/submission_confirmation.txt b/src/registrar/templates/emails/submission_confirmation.txt index d9d01ec3e..afbde48d5 100644 --- a/src/registrar/templates/emails/submission_confirmation.txt +++ b/src/registrar/templates/emails/submission_confirmation.txt @@ -20,7 +20,7 @@ During our review, we’ll verify that: - You work at the organization and/or can make requests on its behalf - Your requested domain meets our naming requirements {% endif %} -We’ll email you if we have questions. We’ll also email you as soon as we complete our review. You can check the status of your request at any time on the registrar. . +We’ll email you if we have questions. We’ll also email you as soon as we complete our review. You can check the status of your request at any time on the registrar. <{{ manage_url }}>. NEED TO MAKE CHANGES? diff --git a/src/registrar/templates/emails/transition_domain_invitation.txt b/src/registrar/templates/emails/transition_domain_invitation.txt index b6773d9e9..14dd626dd 100644 --- a/src/registrar/templates/emails/transition_domain_invitation.txt +++ b/src/registrar/templates/emails/transition_domain_invitation.txt @@ -31,7 +31,7 @@ CHECK YOUR .GOV DOMAIN CONTACTS This is a good time to check who has access to your .gov domain{% if domains|length > 1 %}s{% endif %}. The admin, technical, and billing contacts listed for your domain{% if domains|length > 1 %}s{% endif %} in our old system also received this email. In our new registrar, these contacts are all considered “domain managers.” We no longer have the admin, technical, and billing roles, and you aren’t limited to three domain managers like in the old system. - 1. Once you have your Login.gov account, sign in to the new registrar at . + 1. Once you have your Login.gov account, sign in to the new registrar at <{{ manage_url }}>. 2. Click the “Manage” link next to your .gov domain, then click on “Domain managers” to see who has access to your domain. 3. If any of these users should not have access to your domain, let us know in a reply to this email. @@ -57,7 +57,7 @@ THANK YOU The .gov team .Gov blog -Domain management +Domain management <{{ manage_url }}}> Get.gov The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) diff --git a/src/registrar/templates/emails/update_to_approved_domain.txt b/src/registrar/templates/emails/update_to_approved_domain.txt index 99f86ea54..070096f62 100644 --- a/src/registrar/templates/emails/update_to_approved_domain.txt +++ b/src/registrar/templates/emails/update_to_approved_domain.txt @@ -8,7 +8,7 @@ UPDATED BY: {{user}} UPDATED ON: {{date}} INFORMATION UPDATED: {{changes}} -You can view this update in the .gov registrar . +You can view this update in the .gov registrar <{{ manage_url }}>. Get help with managing your .gov domain . diff --git a/src/registrar/tests/test_emails.py b/src/registrar/tests/test_emails.py index f39f11517..2b7f89ac9 100644 --- a/src/registrar/tests/test_emails.py +++ b/src/registrar/tests/test_emails.py @@ -108,6 +108,82 @@ class TestEmails(TestCase): self.assertEqual(["testy2@town.com", "mayor@igorville.gov"], kwargs["Destination"]["CcAddresses"]) + @boto3_mocking.patching + @override_settings(IS_PRODUCTION=True, BASE_URL="manage.get.gov") + def test_email_production_subject_and_url_check(self): + """Test sending an email in production that: + 1. Does not have a prefix in the email subject (no [MANAGE]) + 2. Uses the production URL in the email body of manage.get.gov still""" + with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): + send_templated_email( + "emails/update_to_approved_domain.txt", + "emails/update_to_approved_domain_subject.txt", + "doesnotexist@igorville.com", + context={"domain": "test", "user": "test", "date": 1, "changes": "test"}, + bcc_address=None, + cc_addresses=["testy2@town.com", "mayor@igorville.gov"], + ) + + # check that an email was sent + self.assertTrue(self.mock_client.send_email.called) + + # check the call sequence for the email + args, kwargs = self.mock_client.send_email.call_args + self.assertIn("Destination", kwargs) + self.assertIn("CcAddresses", kwargs["Destination"]) + + self.assertEqual(["testy2@town.com", "mayor@igorville.gov"], kwargs["Destination"]["CcAddresses"]) + + # Grab email subject + email_subject = kwargs["Content"]["Simple"]["Subject"]["Data"] + + # Check that the subject does NOT contain a prefix for production + self.assertNotIn("[MANAGE]", email_subject) + self.assertIn("An update was made to", email_subject) + + # Grab email body + email_body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] + + # Check that manage_url is correctly set for production + self.assertIn("https://manage.get.gov", email_body) + + @boto3_mocking.patching + @override_settings(IS_PRODUCTION=False, BASE_URL="https://getgov-rh.app.cloud.gov") + def test_email_non_production_subject_and_url_check(self): + """Test sending an email in production that: + 1. Does prefix in the email subject (ie [GETGOV-RH]) + 2. Uses the sandbox url in the email body (ie getgov-rh.app.cloud.gov)""" + with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class): + send_templated_email( + "emails/update_to_approved_domain.txt", + "emails/update_to_approved_domain_subject.txt", + "doesnotexist@igorville.com", + context={"domain": "test", "user": "test", "date": 1, "changes": "test"}, + bcc_address=None, + cc_addresses=["testy2@town.com", "mayor@igorville.gov"], + ) + + # check that an email was sent + self.assertTrue(self.mock_client.send_email.called) + + # check the call sequence for the email + args, kwargs = self.mock_client.send_email.call_args + self.assertIn("Destination", kwargs) + self.assertIn("CcAddresses", kwargs["Destination"]) + self.assertEqual(["testy2@town.com", "mayor@igorville.gov"], kwargs["Destination"]["CcAddresses"]) + + # Grab email subject + email_subject = kwargs["Content"]["Simple"]["Subject"]["Data"] + + # Check that the subject DOES contain a prefix of the current sandbox + self.assertIn("[GETGOV-RH]", email_subject) + + # Grab email body + email_body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"] + + # Check that manage_url is correctly set of the sandbox + self.assertIn("https://getgov-rh.app.cloud.gov", email_body) + @boto3_mocking.patching @less_console_noise_decorator def test_submission_confirmation(self): diff --git a/src/registrar/utility/email.py b/src/registrar/utility/email.py index 40601cdc7..797ad4aa9 100644 --- a/src/registrar/utility/email.py +++ b/src/registrar/utility/email.py @@ -3,6 +3,7 @@ import boto3 import logging import textwrap +import re from datetime import datetime from django.apps import apps from django.conf import settings @@ -48,6 +49,21 @@ def send_templated_email( # noqa No valid recipient addresses are provided """ + if context is None: + context = {} + + env_base_url = settings.BASE_URL + # The regular expression is to get both http (localhost) and https (everything else) + env_name = re.sub(r"^https?://", "", env_base_url).split(".")[0] + # If NOT in prod, add env to the subject line + # IE adds [GETGOV-RH] if we are in the -RH sandbox + prefix = f"[{env_name.upper()}] " if not settings.IS_PRODUCTION else "" + # If NOT in prod, update instances of "manage.get.gov" links to point to + # current environment, ie "getgov-rh.app.cloud.gov" + manage_url = env_base_url if not settings.IS_PRODUCTION else "https://manage.get.gov" + + context["manage_url"] = manage_url + # by default assume we can send to all addresses (prod has no whitelist) sendable_cc_addresses = cc_addresses @@ -70,8 +86,12 @@ def send_templated_email( # noqa if email_body: email_body.strip().lstrip("\n") + # Update the subject to have prefix here versus every email subject_template = get_template(subject_template_name) subject = subject_template.render(context=context) + subject = f"{prefix}{subject}" + + context["subject"] = subject try: ses_client = boto3.client(