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")
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):
"""

View file

@ -40,7 +40,7 @@ def send_domain_invitation_email(
EmailSendingError: If there is an error while sending the email.
"""
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)
@ -99,17 +99,22 @@ def normalize_domains(domains: Domain | list[Domain]) -> list[Domain]:
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.
If the requestor is staff, default email is returned.
Raises:
MissingEmailError
"""
if requestor.is_staff:
return settings.DEFAULT_FROM_EMAIL
if not requestor.email or requestor.email.strip() == "":
domain_names = ", ".join([domain.name for domain in domains])
raise MissingEmailError(email=requestor.email, domain=domain_names)
domain_names = None
if domains:
domain_names = ", ".join([domain.name for domain in domains])
raise MissingEmailError(email=requestor.email, domain=domain_names, portfolio=portfolio)
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.
"""
# Default email address for staff
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
requestor_email = get_requestor_email(requestor, portfolio=portfolio)
try:
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.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
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.enums import DefaultUserValues
from registrar.views.utility.mixins import PortfolioMemberPermission
@ -405,6 +405,19 @@ class PortfolioInvitedMemberEditView(PortfolioMemberEditPermissionView, View):
portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk)
form = self.form_class(request.POST, instance=portfolio_invitation)
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()
messages.success(self.request, "The member access and permission changes have been saved.")
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):