From f1131f080e9621081c66810b4f72208efbed5327 Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Tue, 23 Sep 2025 10:11:53 -0400 Subject: [PATCH 01/15] initial changes (need to review, as this seems incomplete) --- src/registrar/tests/test_admin.py | 2 +- src/registrar/utility/email_invitations.py | 4 ++-- src/registrar/utility/errors.py | 10 ++-------- src/registrar/views/portfolios.py | 2 +- src/registrar/views/utility/invitation_helper.py | 2 +- 5 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 53d4bb774f..c5c2888fe6 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1586,7 +1586,7 @@ def test_save_exception_generic_error(self, mock_messages_error, mock_send_email admin_instance.save_model(request, portfolio_invitation, None, None) # Assert that messages.error was called with the correct message - mock_messages_error.assert_called_once_with(request, "Could not send email invitation.") + mock_messages_error.assert_called_once_with(request, "An unexpected error occurred: {email} could not be added to this domain. Try again and contact us if the problem persists.") @less_console_noise_decorator @patch("registrar.admin.send_portfolio_admin_addition_emails") diff --git a/src/registrar/utility/email_invitations.py b/src/registrar/utility/email_invitations.py index ebd99f1a21..35be6323a5 100644 --- a/src/registrar/utility/email_invitations.py +++ b/src/registrar/utility/email_invitations.py @@ -103,7 +103,7 @@ def _send_domain_invitation_email(email, requestor_email, domains, requested_use f" Error: {err}", exc_info=True, ) - raise EmailSendingError(f"Could not send email invitation to {email} for domains: {domain_names}") from err + raise EmailSendingError(f"An unexpected error occurred: {email} could not be added to this domain. Try again and contact us if the problem persists.") from err def send_domain_invitation_email( @@ -290,7 +290,7 @@ def send_portfolio_invitation_email(email: str, requestor, portfolio, is_admin_i exc_info=True, ) raise EmailSendingError( - f"Could not sent email invitation to {email} for portfolio {portfolio}. Portfolio invitation not saved." + f"An unexpected error occurred: {email} could not be added to this domain. Try again and contact us if the problem persists." ) from err all_admin_emails_sent = True diff --git a/src/registrar/utility/errors.py b/src/registrar/utility/errors.py index 9446d84f72..cf40dfc413 100644 --- a/src/registrar/utility/errors.py +++ b/src/registrar/utility/errors.py @@ -53,13 +53,7 @@ class MissingEmailError(InvitationError): def __init__(self, email=None, domain=None, portfolio=None): # Default message if no additional info is provided message = "Can't send invitation email. No email is associated with your user account." - - # Customize message based on provided arguments - if email and domain: - message = f"Can't send email to '{email}' on domain '{domain}'. No email exists for the requestor." - elif email and portfolio: - message = f"Can't send email to '{email}' for portfolio '{portfolio}'. No email exists for the requestor." - + super().__init__(message) @@ -73,7 +67,7 @@ def __init__(self, email=None): # Default message if no additional info is provided message = "Can not invite member of a .gov organization to a different organization." if email: - message = f"{email} is already a member of another .gov organization." + message = f"{email} is already a member of another organization." super().__init__(message) diff --git a/src/registrar/views/portfolios.py b/src/registrar/views/portfolios.py index abc2c2194a..ed3934a7d3 100644 --- a/src/registrar/views/portfolios.py +++ b/src/registrar/views/portfolios.py @@ -1179,7 +1179,7 @@ def _handle_exceptions(self, exception, portfolio, email): elif isinstance(exception, MissingEmailError): messages.error(self.request, str(exception)) logger.error( - f"Can't send email to '{email}' for portfolio '{portfolio}'. No email exists for the requestor.", + f"Can't send invitation email. No email is associated with your account.", exc_info=True, ) else: diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index 18c4279404..410e3b5815 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -75,4 +75,4 @@ def handle_invitation_exceptions(request, exception, email): messages.error(request, f"{email} is already a manager for this domain") else: logger.warning("Could not send email invitation (Other Exception)", exc_info=True) - messages.error(request, "Could not send email invitation.") + messages.error(request, "An unexpected error occurred: {email} could not be added to this domain. Try again and contact us if the problem persists.") From 9afe1b829cf35f0bb6d4791a87c3de0d6d5b176a Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:41:20 -0400 Subject: [PATCH 02/15] correction to error messaging V2 --- src/registrar/admin.py | 2 +- src/registrar/tests/test_views_domain.py | 2 +- src/registrar/utility/errors.py | 2 +- src/registrar/views/domain.py | 4 ++-- src/registrar/views/portfolios.py | 4 ++-- src/registrar/views/utility/invitation_helper.py | 13 ++++++++++--- 6 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index fa7c8d740e..b49cc8a2f2 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1893,7 +1893,7 @@ def save_model(self, request, obj, form, change): is_member_of_different_org=member_of_a_different_org, requested_user=requested_user, ): - messages.warning(request, "Could not send email confirmation to existing domain managers.") + messages.warning(request, "Could not send email notification to existing domain managers.") if requested_user is not None: # Domain Invitation creation for an existing User obj.retrieve() diff --git a/src/registrar/tests/test_views_domain.py b/src/registrar/tests/test_views_domain.py index 38ba48e1ad..3c4906f5f6 100644 --- a/src/registrar/tests/test_views_domain.py +++ b/src/registrar/tests/test_views_domain.py @@ -980,7 +980,7 @@ def test_domain_user_add_form_fails_to_send_to_some_managers( self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) success_page = success_result.follow() - self.assertContains(success_page, "Could not send email confirmation to existing domain managers.") + self.assertContains(success_page, "Could not send email notification to existing domain managers.") @GenericTestHelper.switch_to_enterprise_mode_wrapper @boto3_mocking.patching diff --git a/src/registrar/utility/errors.py b/src/registrar/utility/errors.py index cf40dfc413..0291e894cd 100644 --- a/src/registrar/utility/errors.py +++ b/src/registrar/utility/errors.py @@ -37,7 +37,7 @@ class AlreadyDomainManagerError(InvitationError): """Raised when the user is already a manager for the domain.""" def __init__(self, email): - super().__init__(f"{email} is already a manager for this domain.") + super().__init__(f"An unexpected error occurred: {email} could not be added to this domain.") class AlreadyDomainInvitedError(InvitationError): diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index 7e83862fcc..c07434bf68 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -1284,7 +1284,7 @@ def _handle_new_user_invitation(self, email, requestor, member_of_different_org) domains=self.object, is_member_of_different_org=member_of_different_org, ): - messages.warning(self.request, "Could not send email confirmation to existing domain managers.") + messages.warning(self.request, "Could not send email notification to existing domain managers.") DomainInvitation.objects.get_or_create(email=email, domain=self.object) messages.success(self.request, f"{email} has been invited to the domain: {self.object}") @@ -1297,7 +1297,7 @@ def _handle_existing_user(self, email, requestor, requested_user, member_of_diff is_member_of_different_org=member_of_different_org, requested_user=requested_user, ): - messages.warning(self.request, "Could not send email confirmation to existing domain managers.") + messages.warning(self.request, "Could not send email notification to existing domain managers.") UserDomainRole.objects.create( user=requested_user, domain=self.object, diff --git a/src/registrar/views/portfolios.py b/src/registrar/views/portfolios.py index ed3934a7d3..e69ea4d265 100644 --- a/src/registrar/views/portfolios.py +++ b/src/registrar/views/portfolios.py @@ -446,7 +446,7 @@ def _process_added_domains(self, added_domain_ids, member, requestor, portfolio) is_member_of_different_org=member_of_a_different_org, requested_user=member, ): - messages.warning(self.request, "Could not send email confirmation to existing domain managers.") + messages.warning(self.request, "Could not send email notification to existing domain managers.") # Bulk create UserDomainRole instances for added domains UserDomainRole.objects.bulk_create( [ @@ -777,7 +777,7 @@ def _process_added_domains(self, added_domain_ids, email, requestor, portfolio): domains=added_domains, is_member_of_different_org=member_of_a_different_org, ): - messages.warning(self.request, "Could not send email confirmation to existing domain managers.") + messages.warning(self.request, "Could not send email notification to existing domain managers.") # Update existing invitations from CANCELED to INVITED existing_invitations = DomainInvitation.objects.filter(domain__in=added_domains, email=email) diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index 410e3b5815..c10ad36953 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -9,6 +9,7 @@ MissingEmailError, OutsideOrgMemberError, ) +from django.utils.safestring import mark_safe logger = logging.getLogger(__name__) @@ -61,18 +62,24 @@ def handle_invitation_exceptions(request, exception, email): """Handle exceptions raised during the process.""" if isinstance(exception, EmailSendingError): logger.warning(exception, exc_info=True) - messages.error(request, str(exception)) + messages.error(request, with_contact_link(str(exception))) elif isinstance(exception, MissingEmailError): messages.error(request, str(exception)) logger.error(exception, exc_info=True) elif isinstance(exception, OutsideOrgMemberError): messages.error(request, str(exception)) elif isinstance(exception, AlreadyDomainManagerError): - messages.error(request, str(exception)) + messages.error(request, with_contact_link(str(exception))) elif isinstance(exception, AlreadyDomainInvitedError): messages.error(request, str(exception)) elif isinstance(exception, IntegrityError): messages.error(request, f"{email} is already a manager for this domain") else: logger.warning("Could not send email invitation (Other Exception)", exc_info=True) - messages.error(request, "An unexpected error occurred: {email} could not be added to this domain. Try again and contact us if the problem persists.") + messages.error(request, "Could not send email invitation.") + + +def with_contact_link(msg): + return mark_safe( + f'{msg} Try again and contact us if the problem persists.' + ) From 45c90775a2d5129e047db7ffbbd80f35949e0005 Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:14:49 -0400 Subject: [PATCH 03/15] Test corrections and edits --- src/registrar/tests/test_admin.py | 12 +++++++++--- src/registrar/tests/test_email_invitations.py | 2 +- src/registrar/utility/email_invitations.py | 6 ++++-- src/registrar/utility/errors.py | 2 +- src/registrar/views/utility/invitation_helper.py | 4 +++- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index c5c2888fe6..acd999a5c3 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1503,7 +1503,7 @@ def test_save_exception_email_sending_error(self, mock_messages_error, mock_send self.client.force_login(self.superuser) # Mock the email sending function to raise EmailSendingError - mock_send_email.side_effect = EmailSendingError("Email service unavailable") + mock_send_email.side_effect = EmailSendingError("Email service unavailable.") # Create an instance of the admin class admin_instance = PortfolioInvitationAdmin(PortfolioInvitation, admin_site=None) @@ -1523,7 +1523,10 @@ def test_save_exception_email_sending_error(self, mock_messages_error, mock_send admin_instance.save_model(request, portfolio_invitation, None, None) # Assert that messages.error was called with the correct message - mock_messages_error.assert_called_once_with(request, "Email service unavailable") + mock_messages_error.assert_called_once_with( + request, + 'Email service unavailable. Try again and contact us if the problem persists.', + ) @less_console_noise_decorator @patch("registrar.admin.send_portfolio_invitation_email") @@ -1586,7 +1589,10 @@ def test_save_exception_generic_error(self, mock_messages_error, mock_send_email admin_instance.save_model(request, portfolio_invitation, None, None) # Assert that messages.error was called with the correct message - mock_messages_error.assert_called_once_with(request, "An unexpected error occurred: {email} could not be added to this domain. Try again and contact us if the problem persists.") + mock_messages_error.assert_called_once_with( + request, + 'An unexpected error occurred: {email} could not be added to this domain. Try again and contact us if the problem persists.', + ) @less_console_noise_decorator @patch("registrar.admin.send_portfolio_admin_addition_emails") diff --git a/src/registrar/tests/test_email_invitations.py b/src/registrar/tests/test_email_invitations.py index b6bab72984..09e14c5de7 100644 --- a/src/registrar/tests/test_email_invitations.py +++ b/src/registrar/tests/test_email_invitations.py @@ -523,7 +523,7 @@ def test_send_portfolio_invitation_email_failure(self, mock_send_templated_email with self.assertRaises(EmailSendingError) as context: send_portfolio_invitation_email(self.email, self.requestor, self.portfolio, is_admin_invitation) - self.assertIn("Could not sent email invitation to", str(context.exception)) + self.assertIn("An unexpected error occurred: invitee@example.com could not be added to this domain.", str(context.exception)) @less_console_noise_decorator @patch( diff --git a/src/registrar/utility/email_invitations.py b/src/registrar/utility/email_invitations.py index 35be6323a5..b714118e59 100644 --- a/src/registrar/utility/email_invitations.py +++ b/src/registrar/utility/email_invitations.py @@ -103,7 +103,9 @@ def _send_domain_invitation_email(email, requestor_email, domains, requested_use f" Error: {err}", exc_info=True, ) - raise EmailSendingError(f"An unexpected error occurred: {email} could not be added to this domain. Try again and contact us if the problem persists.") from err + raise EmailSendingError( + f"An unexpected error occurred: {email} could not be added to this domain." + ) from err def send_domain_invitation_email( @@ -290,7 +292,7 @@ def send_portfolio_invitation_email(email: str, requestor, portfolio, is_admin_i exc_info=True, ) raise EmailSendingError( - f"An unexpected error occurred: {email} could not be added to this domain. Try again and contact us if the problem persists." + f"An unexpected error occurred: {email} could not be added to this domain." ) from err all_admin_emails_sent = True diff --git a/src/registrar/utility/errors.py b/src/registrar/utility/errors.py index 0291e894cd..4ef9a4bad6 100644 --- a/src/registrar/utility/errors.py +++ b/src/registrar/utility/errors.py @@ -53,7 +53,7 @@ class MissingEmailError(InvitationError): def __init__(self, email=None, domain=None, portfolio=None): # Default message if no additional info is provided message = "Can't send invitation email. No email is associated with your user account." - + super().__init__(message) diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index c10ad36953..56ccacbaa1 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -76,7 +76,9 @@ def handle_invitation_exceptions(request, exception, email): messages.error(request, f"{email} is already a manager for this domain") else: logger.warning("Could not send email invitation (Other Exception)", exc_info=True) - messages.error(request, "Could not send email invitation.") + messages.error( + request, with_contact_link("An unexpected error occurred: {email} could not be added to this domain.") + ) def with_contact_link(msg): From 60f35c4336188ea3c162a03e5ced6b22aa4d6204 Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:21:36 -0400 Subject: [PATCH 04/15] removal of mark_safe and linter corrections --- src/registrar/tests/test_email_invitations.py | 5 ++++- src/registrar/utility/email_invitations.py | 8 ++------ src/registrar/views/utility/invitation_helper.py | 5 +---- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/registrar/tests/test_email_invitations.py b/src/registrar/tests/test_email_invitations.py index 09e14c5de7..e6f454667f 100644 --- a/src/registrar/tests/test_email_invitations.py +++ b/src/registrar/tests/test_email_invitations.py @@ -523,7 +523,10 @@ def test_send_portfolio_invitation_email_failure(self, mock_send_templated_email with self.assertRaises(EmailSendingError) as context: send_portfolio_invitation_email(self.email, self.requestor, self.portfolio, is_admin_invitation) - self.assertIn("An unexpected error occurred: invitee@example.com could not be added to this domain.", str(context.exception)) + self.assertIn( + "An unexpected error occurred: invitee@example.com could not be added to this domain.", + str(context.exception), + ) @less_console_noise_decorator @patch( diff --git a/src/registrar/utility/email_invitations.py b/src/registrar/utility/email_invitations.py index b714118e59..5387f37d53 100644 --- a/src/registrar/utility/email_invitations.py +++ b/src/registrar/utility/email_invitations.py @@ -103,9 +103,7 @@ def _send_domain_invitation_email(email, requestor_email, domains, requested_use f" Error: {err}", exc_info=True, ) - raise EmailSendingError( - f"An unexpected error occurred: {email} could not be added to this domain." - ) from err + raise EmailSendingError(f"An unexpected error occurred: {email} could not be added to this domain.") from err def send_domain_invitation_email( @@ -291,9 +289,7 @@ def send_portfolio_invitation_email(email: str, requestor, portfolio, is_admin_i f" Error: {err}", exc_info=True, ) - raise EmailSendingError( - f"An unexpected error occurred: {email} could not be added to this domain." - ) from err + raise EmailSendingError(f"An unexpected error occurred: {email} could not be added to this domain.") from err all_admin_emails_sent = True # send emails to portfolio admins diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index 56ccacbaa1..c1ee0c5b48 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -9,7 +9,6 @@ MissingEmailError, OutsideOrgMemberError, ) -from django.utils.safestring import mark_safe logger = logging.getLogger(__name__) @@ -82,6 +81,4 @@ def handle_invitation_exceptions(request, exception, email): def with_contact_link(msg): - return mark_safe( - f'{msg} Try again and contact us if the problem persists.' - ) + return f'{msg} Try again and contact us if the problem persists.' From 9ca35d4bc681c60bc9cfea440fa4b88e11b022e8 Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:49:20 -0400 Subject: [PATCH 05/15] linter fixes V2 --- src/registrar/tests/test_admin.py | 14 ++++++++++++-- src/registrar/views/portfolios.py | 2 +- src/registrar/views/utility/invitation_helper.py | 9 +++++++-- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index acd999a5c3..406b0559d8 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1521,11 +1521,15 @@ def test_save_exception_email_sending_error(self, mock_messages_error, mock_send # Call the save_model method admin_instance.save_model(request, portfolio_invitation, None, None) + msg = ( + 'Email service unavailable. Try again and contact us if the problem persists.' + ) # Assert that messages.error was called with the correct message mock_messages_error.assert_called_once_with( request, - 'Email service unavailable. Try again and contact us if the problem persists.', + msg, ) @less_console_noise_decorator @@ -1588,10 +1592,16 @@ def test_save_exception_generic_error(self, mock_messages_error, mock_send_email # Call the save_model method admin_instance.save_model(request, portfolio_invitation, None, None) + msg = ( + "An unexpected error occurred: {email} could not be added to this domain. " + 'Try again and ' + "contact us if the problem persists." + ) + # Assert that messages.error was called with the correct message mock_messages_error.assert_called_once_with( request, - 'An unexpected error occurred: {email} could not be added to this domain. Try again and contact us if the problem persists.', + msg, ) @less_console_noise_decorator diff --git a/src/registrar/views/portfolios.py b/src/registrar/views/portfolios.py index e69ea4d265..15eab4bd9b 100644 --- a/src/registrar/views/portfolios.py +++ b/src/registrar/views/portfolios.py @@ -1179,7 +1179,7 @@ def _handle_exceptions(self, exception, portfolio, email): elif isinstance(exception, MissingEmailError): messages.error(self.request, str(exception)) logger.error( - f"Can't send invitation email. No email is associated with your account.", + "Can't send invitation email. No email is associated with your account.", exc_info=True, ) else: diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index c1ee0c5b48..52762328e7 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -80,5 +80,10 @@ def handle_invitation_exceptions(request, exception, email): ) -def with_contact_link(msg): - return f'{msg} Try again and contact us if the problem persists.' +def with_contact_link(error_message): + contact_msg = ( + 'Try again and ' + "docker compose exec app ./manage.py lint contact us" + " if the problem persists." + ) + return f"{error_message} {contact_msg}" From d3cf495af9b7998b658675b66561ffb027d1f7b3 Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Thu, 25 Sep 2025 15:57:18 -0400 Subject: [PATCH 06/15] correcting rogue docker compose call in test --- src/registrar/views/utility/invitation_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index 52762328e7..e3aabdd2f3 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -83,7 +83,7 @@ def handle_invitation_exceptions(request, exception, email): def with_contact_link(error_message): contact_msg = ( 'Try again and ' - "docker compose exec app ./manage.py lint contact us" + "contact us" " if the problem persists." ) return f"{error_message} {contact_msg}" From 3f1803b806f3c85cb43bc8a64575287e4d3e4fbe Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Fri, 26 Sep 2025 09:43:38 -0400 Subject: [PATCH 07/15] changes to success messaging --- src/registrar/admin.py | 2 +- src/registrar/tests/test_admin.py | 8 ++++---- src/registrar/views/domain.py | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index b49cc8a2f2..90a55fde2b 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1884,7 +1884,7 @@ def save_model(self, request, obj, form, change): if requested_user is not None: portfolio_invitation.retrieve() portfolio_invitation.save() - messages.success(request, f"{requested_email} has been invited to the organization: {domain_org}") + messages.success(request, f"{requested_email} has been invited to become a member of {domain_org}") if not send_domain_invitation_email( email=requested_email, diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 406b0559d8..4fdedf1d9c 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -399,7 +399,7 @@ def test_add_domain_invitation_success_when_user_not_portfolio_member( # Assert success message mock_messages_success.assert_has_calls( [ - call(request, "test@example.com has been invited to the organization: new portfolio"), + call(request, "test@example.com has been invited to become a member of new portfolio"), call(request, "test@example.com has been invited to the domain: example.com"), ] ) @@ -656,7 +656,7 @@ def test_add_domain_invitation_when_user_not_portfolio_member_raises_exception_s # Assert success message mock_messages_success.assert_called_once_with( - request, "test@example.com has been invited to the organization: new portfolio" + request, "test@example.com has been invited to become a member of new portfolio" ) # Assert error message @@ -784,7 +784,7 @@ def test_add_domain_invitation_success_when_email_not_portfolio_member( # Assert success message mock_messages_success.assert_has_calls( [ - call(request, "nonexistent@example.com has been invited to the organization: new portfolio"), + call(request, "nonexistent@example.com has been invited to become a member of new portfolio"), call(request, "nonexistent@example.com has been invited to the domain: example.com"), ] ) @@ -1018,7 +1018,7 @@ def test_add_domain_invitation_when_user_not_portfolio_email_raises_exception_se # Assert success message mock_messages_success.assert_called_once_with( - request, "nonexistent@example.com has been invited to the organization: new portfolio" + request, "nonexistent@example.com has been invited to become a member of new portfolio" ) # Assert error message diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index c07434bf68..01a740779c 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -1265,7 +1265,7 @@ def form_valid(self, form): if requested_user is not None: portfolio_invitation.retrieve() portfolio_invitation.save() - messages.success(self.request, f"{requested_email} has been invited to the organization: {domain_org}") + messages.success(self.request, f"{requested_email} has been invited to become a member of {domain_org}") if requested_user is None: self._handle_new_user_invitation(requested_email, requestor, member_of_a_different_org) From c6b5187851ab31279fc133440e5388cabf6bc17b Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Fri, 26 Sep 2025 13:03:03 -0400 Subject: [PATCH 08/15] updates for SafeString --- src/registrar/tests/test_admin.py | 2 +- src/registrar/views/utility/invitation_helper.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 4fdedf1d9c..4e768a704a 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1593,7 +1593,7 @@ def test_save_exception_generic_error(self, mock_messages_error, mock_send_email admin_instance.save_model(request, portfolio_invitation, None, None) msg = ( - "An unexpected error occurred: {email} could not be added to this domain. " + "An unexpected error occurred: james.gordon@gotham.gov could not be added to this domain. " 'Try again and ' "contact us if the problem persists." ) diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index e3aabdd2f3..ab69d48430 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -9,6 +9,7 @@ MissingEmailError, OutsideOrgMemberError, ) +from django.utils.html import format_html logger = logging.getLogger(__name__) @@ -76,14 +77,13 @@ def handle_invitation_exceptions(request, exception, email): else: logger.warning("Could not send email invitation (Other Exception)", exc_info=True) messages.error( - request, with_contact_link("An unexpected error occurred: {email} could not be added to this domain.") + request, with_contact_link(f"An unexpected error occurred: {email} could not be added to this domain.") ) -def with_contact_link(error_message): - contact_msg = ( - 'Try again and ' - "contact us" - " if the problem persists." +def with_contact_link(error_message: str, contact_url: str = "https://get.gov/contact") -> str: + return format_html( + '{} Try again and contact us if the problem persists.', + error_message, + contact_url, ) - return f"{error_message} {contact_msg}" From 09d9f36df5595c13de9ff2fadd2562ea387838f9 Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:22:44 -0400 Subject: [PATCH 09/15] updated language --- src/registrar/tests/test_admin.py | 8 ++++---- src/registrar/views/utility/invitation_helper.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 4e768a704a..dcf33d5073 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1522,8 +1522,8 @@ def test_save_exception_email_sending_error(self, mock_messages_error, mock_send # Call the save_model method admin_instance.save_model(request, portfolio_invitation, None, None) msg = ( - 'Email service unavailable. Try again and contact us if the problem persists.' + 'Email service unavailable. Please try again. If the problem persists, ' + 'contact us.' ) # Assert that messages.error was called with the correct message @@ -1594,8 +1594,8 @@ def test_save_exception_generic_error(self, mock_messages_error, mock_send_email msg = ( "An unexpected error occurred: james.gordon@gotham.gov could not be added to this domain. " - 'Try again and ' - "contact us if the problem persists." + 'Please try again. If the problem persists, contact us if the problem persists.' ) # Assert that messages.error was called with the correct message diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index ab69d48430..ddfebff447 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -83,7 +83,7 @@ def handle_invitation_exceptions(request, exception, email): def with_contact_link(error_message: str, contact_url: str = "https://get.gov/contact") -> str: return format_html( - '{} Try again and contact us if the problem persists.', + '{} Please try again. If the problem persists, contact us.', error_message, contact_url, ) From 9832cf9762ccd4681bd6a3ed27d63754f320c11f Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Fri, 26 Sep 2025 16:30:21 -0400 Subject: [PATCH 10/15] repeated linting and test fix --- src/registrar/tests/test_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index dcf33d5073..a71890924d 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1522,7 +1522,7 @@ def test_save_exception_email_sending_error(self, mock_messages_error, mock_send # Call the save_model method admin_instance.save_model(request, portfolio_invitation, None, None) msg = ( - 'Email service unavailable. Please try again. If the problem persists, ' + "Email service unavailable. Please try again. If the problem persists, " 'contact us.' ) @@ -1595,7 +1595,7 @@ def test_save_exception_generic_error(self, mock_messages_error, mock_send_email msg = ( "An unexpected error occurred: james.gordon@gotham.gov could not be added to this domain. " 'Please try again. If the problem persists, contact us if the problem persists.' + 'class="usa-link" target="_blank">contact us.' ) # Assert that messages.error was called with the correct message From da07008f0064812418ca7c46bee404cb43b8f2e0 Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Mon, 29 Sep 2025 09:24:35 -0400 Subject: [PATCH 11/15] Updated language (removed please) --- src/registrar/tests/test_admin.py | 4 ++-- src/registrar/views/utility/invitation_helper.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index a71890924d..16301f08ed 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1522,7 +1522,7 @@ def test_save_exception_email_sending_error(self, mock_messages_error, mock_send # Call the save_model method admin_instance.save_model(request, portfolio_invitation, None, None) msg = ( - "Email service unavailable. Please try again. If the problem persists, " + "Email service unavailable. Try again, and if the problem persists, " 'contact us.' ) @@ -1594,7 +1594,7 @@ def test_save_exception_generic_error(self, mock_messages_error, mock_send_email msg = ( "An unexpected error occurred: james.gordon@gotham.gov could not be added to this domain. " - 'Please try again. If the problem persists, contact us.' ) diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index ddfebff447..7cbb4cce17 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -83,7 +83,7 @@ def handle_invitation_exceptions(request, exception, email): def with_contact_link(error_message: str, contact_url: str = "https://get.gov/contact") -> str: return format_html( - '{} Please try again. If the problem persists, contact us.', + '{} Try again, and if the problem persists, contact us.', error_message, contact_url, ) From 2103075994a17c6612a5acabc7787b3790831b0f Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:40:24 -0400 Subject: [PATCH 12/15] updated language for OutsideOrgMemberError --- src/registrar/utility/errors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/utility/errors.py b/src/registrar/utility/errors.py index 4ef9a4bad6..345ec1bcc6 100644 --- a/src/registrar/utility/errors.py +++ b/src/registrar/utility/errors.py @@ -65,9 +65,9 @@ class OutsideOrgMemberError(InvitationError): def __init__(self, email=None): # Default message if no additional info is provided - message = "Can not invite member of a .gov organization to a different organization." + message = "Can not invite member to this organization." if email: - message = f"{email} is already a member of another organization." + message = f"{email} is not a member of this organization." super().__init__(message) From 9ac54241a38b7c101533ad1a14a57ee30bbf29dd Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Wed, 1 Oct 2025 12:11:04 -0400 Subject: [PATCH 13/15] correcting AlreadyDomainManagerError logic --- src/registrar/utility/errors.py | 2 +- src/registrar/views/utility/invitation_helper.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/utility/errors.py b/src/registrar/utility/errors.py index 345ec1bcc6..d317199617 100644 --- a/src/registrar/utility/errors.py +++ b/src/registrar/utility/errors.py @@ -37,7 +37,7 @@ class AlreadyDomainManagerError(InvitationError): """Raised when the user is already a manager for the domain.""" def __init__(self, email): - super().__init__(f"An unexpected error occurred: {email} could not be added to this domain.") + super().__init__(f"{email} is already a manager for this domain.") class AlreadyDomainInvitedError(InvitationError): diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index 7cbb4cce17..acbd0c6ae0 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -73,7 +73,7 @@ def handle_invitation_exceptions(request, exception, email): elif isinstance(exception, AlreadyDomainInvitedError): messages.error(request, str(exception)) elif isinstance(exception, IntegrityError): - messages.error(request, f"{email} is already a manager for this domain") + messages.error(request, f"An unexpected error occurred: {email} could not be added to this domain.") else: logger.warning("Could not send email invitation (Other Exception)", exc_info=True) messages.error( From 458c8bfb4efa4fe77de7035a80b1d2fac35ee96d Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Tue, 14 Oct 2025 10:32:14 -0400 Subject: [PATCH 14/15] edits to already manager error messaging --- src/registrar/views/utility/invitation_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/views/utility/invitation_helper.py b/src/registrar/views/utility/invitation_helper.py index acbd0c6ae0..390467a8c5 100644 --- a/src/registrar/views/utility/invitation_helper.py +++ b/src/registrar/views/utility/invitation_helper.py @@ -69,7 +69,7 @@ def handle_invitation_exceptions(request, exception, email): elif isinstance(exception, OutsideOrgMemberError): messages.error(request, str(exception)) elif isinstance(exception, AlreadyDomainManagerError): - messages.error(request, with_contact_link(str(exception))) + messages.error(request, str(exception)) elif isinstance(exception, AlreadyDomainInvitedError): messages.error(request, str(exception)) elif isinstance(exception, IntegrityError): From 7f5cd185b8e1c30eec51d57ff2037b9328808cf8 Mon Sep 17 00:00:00 2001 From: Abe Alam <143724440+abe-alam-ecs@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:03:03 -0400 Subject: [PATCH 15/15] correcting domain invitation success message --- src/registrar/views/domain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index 01a740779c..fec3f2537f 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -1286,7 +1286,7 @@ def _handle_new_user_invitation(self, email, requestor, member_of_different_org) ): messages.warning(self.request, "Could not send email notification to existing domain managers.") DomainInvitation.objects.get_or_create(email=email, domain=self.object) - messages.success(self.request, f"{email} has been invited to the domain: {self.object}") + messages.success(self.request, f"{email} has been invited to this domain.") def _handle_existing_user(self, email, requestor, requested_user, member_of_different_org): """Handle adding an existing user to the domain."""