portfolio addition email send to admins on registrar invite

This commit is contained in:
David Kennedy 2025-01-30 07:34:20 -05:00
parent b4c697f32e
commit 37f52aa9d4
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
4 changed files with 107 additions and 3 deletions

View file

@ -0,0 +1,40 @@
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
Hi,{% if portfolio_admin and portfolio_admin.first_name %} {{ portfolio_admin.first_name }}.{% endif %}
An admin was invited to your .gov organization.
ORGANIZATION: {{ portfolio.organization_name }}
INVITED BY: {{ requestor_email }}
INVITED ON: {{date}}
ADMIN INVITED: {{ invited_email_address }}
----------------------------------------------------------------
NEXT STEPS
The person who received the invitation will become an admin once they log in to the
.gov registrar. They'll need to access the registrar using a Login.gov account that's
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 <https://manage.get.gov/>.
WHY DID YOU RECEIVE THIS EMAIL?
Youre listed as an admin for {{ portfolio.organization_name }}. That means you'll receive a notification
whenever a new admin is invited to that organization.
If you have questions or concerns, reach out to the person who sent the invitation or reply to this email.
THANK YOU
.Gov helps the public identify official, trusted information. Thank you for using a .gov domain.
----------------------------------------------------------------
The .gov team
Contact us: <https://get.gov/contact/>
Learn about .gov <https://get.gov>
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency
(CISA) <https://cisa.gov/>
{% endautoescape %}

View file

@ -0,0 +1 @@
An admin was invited to your .gov organization

View file

@ -1,6 +1,8 @@
from datetime import date
from django.conf import settings
from registrar.models import Domain, DomainInvitation, UserDomainRole
from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.utility.portfolio_helper import UserPortfolioRoleChoices
from registrar.utility.errors import (
AlreadyDomainInvitedError,
AlreadyDomainManagerError,
@ -169,7 +171,7 @@ def send_invitation_email(email, requestor_email, domains, requested_user):
raise EmailSendingError(f"Could not send email invitation to {email} for domains: {domain_names}") from err
def send_portfolio_invitation_email(email: str, requestor, portfolio):
def send_portfolio_invitation_email(email: str, requestor, portfolio, is_admin_invitation):
"""
Sends a portfolio member invitation email to the specified address.
@ -179,6 +181,10 @@ def send_portfolio_invitation_email(email: str, requestor, portfolio):
email (str): Email address of the recipient
requestor (User): The user initiating the invitation.
portfolio (Portfolio): The portfolio object for which the invitation is being sent.
is_admin_invitation (boolean): boolean indicating if the invitation is an admin invitation
Returns:
Boolean indicating if all messages were sent successfully.
Raises:
MissingEmailError: If the requestor has no email associated with their account.
@ -210,3 +216,53 @@ def send_portfolio_invitation_email(email: str, requestor, portfolio):
raise EmailSendingError(
f"Could not sent email invitation to {email} for portfolio {portfolio}. Portfolio invitation not saved."
) from err
all_admin_emails_sent = True
# send emails to portfolio admins
if is_admin_invitation:
all_admin_emails_sent = send_portfolio_admin_addition_emails_to_portfolio_admins(
email=email,
requestor_email=requestor_email,
portfolio=portfolio,
requested_user=None,
)
return all_admin_emails_sent
def send_portfolio_admin_addition_emails_to_portfolio_admins(
email: str, requestor_email, portfolio: Domain, requested_user=None
):
"""
Notifies all portfolio admins of the provided portfolio of a newly invited portfolio admin
Returns:
Boolean indicating if all messages were sent successfully.
"""
all_emails_sent = True
# Get each portfolio admin from list
user_portfolio_permissions = UserPortfolioPermission.objects.filter(
portfolio=portfolio, roles__contains=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
).exclude(user__email=email)
for user_portfolio_permission in user_portfolio_permissions:
# Send email to each portfolio_admin
user = user_portfolio_permission.user
try:
send_templated_email(
"emails/portfolio_admin_addition_notification.txt",
"emails/portfolio_admin_addition_notification_subject.txt",
to_address=user.email,
context={
"portfolio": portfolio,
"requestor_email": requestor_email,
"invited_email_address": email,
"portfolio_admin": user,
"date": date.today(),
},
)
except EmailSendingError:
logger.warning(
f"Could not send email organization admin notification to {user.email} for portfolio: {portfolio.name}",
exc_info=True,
)
all_emails_sent = False
return all_emails_sent

View file

@ -773,12 +773,19 @@ class PortfolioAddMemberView(PortfolioMembersPermissionView, FormMixin):
requested_email = form.cleaned_data["email"]
requestor = self.request.user
portfolio = form.cleaned_data["portfolio"]
is_admin_invitation = UserPortfolioRoleChoices.ORGANIZATION_ADMIN in form.cleaned_data["roles"]
requested_user = User.objects.filter(email=requested_email).first()
permission_exists = UserPortfolioPermission.objects.filter(user=requested_user, portfolio=portfolio).exists()
try:
if not requested_user or not permission_exists:
send_portfolio_invitation_email(email=requested_email, requestor=requestor, portfolio=portfolio)
if not send_portfolio_invitation_email(
email=requested_email,
requestor=requestor,
portfolio=portfolio,
is_admin_invitation=is_admin_invitation,
):
messages.warning(self.request, "Could not send email notification to existing organization admins.")
portfolio_invitation = form.save()
# if user exists for email, immediately retrieve portfolio invitation upon creation
if requested_user is not None:
@ -801,7 +808,7 @@ class PortfolioAddMemberView(PortfolioMembersPermissionView, FormMixin):
portfolio,
exc_info=True,
)
messages.warning(self.request, "Could not send portfolio email invitation.")
messages.error(self.request, "Could not send organization invitation email.")
elif isinstance(exception, MissingEmailError):
messages.error(self.request, str(exception))
logger.error(