added support in forms and views for sending email on change from member to admin

This commit is contained in:
David Kennedy 2025-01-30 08:35:22 -05:00
parent 37f52aa9d4
commit 1bd73b6794
No known key found for this signature in database
GPG key ID: 6528A5386E66B96B
3 changed files with 62 additions and 14 deletions

View file

@ -293,6 +293,32 @@ class BasePortfolioMemberForm(forms.ModelForm):
selected_domain_permission = next((perm for perm in domain_perms if perm in perms), "no_access") selected_domain_permission = next((perm for perm in domain_perms if perm in perms), "no_access")
self.initial["domain_request_permission_member"] = selected_domain_permission self.initial["domain_request_permission_member"] = selected_domain_permission
def is_change_from_member_to_admin(self) -> bool:
"""
Checks if the roles have changed from not containing ORGANIZATION_ADMIN
to containing ORGANIZATION_ADMIN.
"""
previous_roles = set(self.initial.get("roles", [])) # Initial roles before change
new_roles = set(self.cleaned_data.get("roles", [])) # New roles after change
return (
UserPortfolioRoleChoices.ORGANIZATION_ADMIN not in previous_roles
and UserPortfolioRoleChoices.ORGANIZATION_ADMIN in new_roles
)
def is_change_from_admin_to_member(self) -> bool:
"""
Checks if the roles have changed from containing ORGANIZATION_ADMIN
to not containing ORGANIZATION_ADMIN.
"""
previous_roles = set(self.initial.get("roles", [])) # Initial roles before change
new_roles = set(self.cleaned_data.get("roles", [])) # New roles after change
return (
UserPortfolioRoleChoices.ORGANIZATION_ADMIN in previous_roles
and UserPortfolioRoleChoices.ORGANIZATION_ADMIN not in new_roles
)
class PortfolioMemberForm(BasePortfolioMemberForm): class PortfolioMemberForm(BasePortfolioMemberForm):
""" """

View file

@ -40,7 +40,7 @@ def send_domain_invitation_email(
EmailSendingError: If there is an error while sending the email. EmailSendingError: If there is an error while sending the email.
""" """
domains = normalize_domains(domains) domains = normalize_domains(domains)
requestor_email = get_requestor_email(requestor, domains) requestor_email = get_requestor_email(requestor, domains=domains)
_validate_invitation(email, requested_user, domains, requestor, is_member_of_different_org) _validate_invitation(email, requested_user, domains, requestor, is_member_of_different_org)
@ -99,17 +99,22 @@ def normalize_domains(domains: Domain | list[Domain]) -> list[Domain]:
return [domains] if isinstance(domains, Domain) else domains return [domains] if isinstance(domains, Domain) else domains
def get_requestor_email(requestor, domains): def get_requestor_email(requestor, domains=None, portfolio=None):
"""Get the requestor's email or raise an error if it's missing. """Get the requestor's email or raise an error if it's missing.
If the requestor is staff, default email is returned. If the requestor is staff, default email is returned.
Raises:
MissingEmailError
""" """
if requestor.is_staff: if requestor.is_staff:
return settings.DEFAULT_FROM_EMAIL return settings.DEFAULT_FROM_EMAIL
if not requestor.email or requestor.email.strip() == "": if not requestor.email or requestor.email.strip() == "":
domain_names = None
if domains:
domain_names = ", ".join([domain.name for domain in domains]) domain_names = ", ".join([domain.name for domain in domains])
raise MissingEmailError(email=requestor.email, domain=domain_names) raise MissingEmailError(email=requestor.email, domain=domain_names, portfolio=portfolio)
return requestor.email return requestor.email
@ -191,15 +196,7 @@ def send_portfolio_invitation_email(email: str, requestor, portfolio, is_admin_i
EmailSendingError: If there is an error while sending the email. EmailSendingError: If there is an error while sending the email.
""" """
# Default email address for staff requestor_email = get_requestor_email(requestor, portfolio=portfolio)
requestor_email = settings.DEFAULT_FROM_EMAIL
# Check if the requestor is staff and has an email
if not requestor.is_staff:
if not requestor.email or requestor.email.strip() == "":
raise MissingEmailError(email=email, portfolio=portfolio)
else:
requestor_email = requestor.email
try: try:
send_templated_email( send_templated_email(

View file

@ -15,7 +15,7 @@ from registrar.models.user_domain_role import UserDomainRole
from registrar.models.user_portfolio_permission import UserPortfolioPermission from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
from registrar.utility.email import EmailSendingError from registrar.utility.email import EmailSendingError
from registrar.utility.email_invitations import send_domain_invitation_email, send_portfolio_invitation_email from registrar.utility.email_invitations import send_domain_invitation_email, send_portfolio_admin_addition_emails, send_portfolio_invitation_email
from registrar.utility.errors import MissingEmailError from registrar.utility.errors import MissingEmailError
from registrar.utility.enums import DefaultUserValues from registrar.utility.enums import DefaultUserValues
from registrar.views.utility.mixins import PortfolioMemberPermission from registrar.views.utility.mixins import PortfolioMemberPermission
@ -405,6 +405,19 @@ class PortfolioInvitedMemberEditView(PortfolioMemberEditPermissionView, View):
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk) portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk)
form = self.form_class(request.POST, instance=portfolio_invitation) form = self.form_class(request.POST, instance=portfolio_invitation)
if form.is_valid(): if form.is_valid():
try:
if form.is_change_from_member_to_admin():
if not send_portfolio_admin_addition_emails(
email=portfolio_invitation.email,
requestor=request.user,
portfolio=portfolio_invitation.portfolio
):
messages.warning(self.request, "Could not send email notification to existing organization admins.")
elif form.is_change_from_admin_to_member():
# NOTE: need to add portfolio_admin_removal_emails when ready
pass
except Exception as e:
self._handle_exceptions(e)
form.save() form.save()
messages.success(self.request, "The member access and permission changes have been saved.") messages.success(self.request, "The member access and permission changes have been saved.")
return redirect("invitedmember", pk=pk) return redirect("invitedmember", pk=pk)
@ -418,6 +431,18 @@ class PortfolioInvitedMemberEditView(PortfolioMemberEditPermissionView, View):
}, },
) )
def _handle_exceptions(self, exception):
"""Handle exceptions raised during the process."""
if isinstance(exception, MissingEmailError):
messages.warning(self.request, "Could not send email notification to existing organization admins.")
logger.warning(
f"Could not send email notification to existing organization admins.",
exc_info=True,
)
else:
logger.warning("Could not send email notification to existing organization admins.", exc_info=True)
messages.warning(self.request, "Could not send email notification to existing organization admins.")
class PortfolioInvitedMemberDomainsView(PortfolioMemberDomainsPermissionView, View): class PortfolioInvitedMemberDomainsView(PortfolioMemberDomainsPermissionView, View):