diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 5c1ab37bf..2707a1dff 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1483,7 +1483,7 @@ class PortfolioInvitationAdmin(ListHeaderAdmin): extra_context["tabtitle"] = "Portfolio invitations" # Get the filtered values return super().changelist_view(request, extra_context=extra_context) - + def save_model(self, request, obj, form, change): """ Override the save_model method to send an email only on creation of the PortfolioInvitation object. @@ -1494,7 +1494,9 @@ class PortfolioInvitationAdmin(ListHeaderAdmin): requestor = request.user requested_user = User.objects.filter(email=requested_email).first() - permission_exists = UserPortfolioPermission.objects.filter(user=requested_user, portfolio=portfolio).exists() + 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) @@ -1511,14 +1513,21 @@ class PortfolioInvitationAdmin(ListHeaderAdmin): def _handle_exceptions(self, exception, request, obj): """Handle exceptions raised during the process.""" if isinstance(exception, EmailSendingError): - logger.warning("Could not sent email invitation to %s for portfolio %s (EmailSendingError)", obj.email, obj.portfolio, exc_info=True) + logger.warning( + "Could not sent email invitation to %s for portfolio %s (EmailSendingError)", + obj.email, + obj.portfolio, + exc_info=True, + ) messages.error(request, "Could not send email invitation. Portfolio invitation not saved.") elif isinstance(exception, MissingEmailError): messages.error(request, str(exception)) logger.error( - f"Can't send email to '{obj.email}' for portfolio '{obj.portfolio}'. No email exists for the requestor.", + f"Can't send email to '{obj.email}' for portfolio '{obj.portfolio}'. " + f"No email exists for the requestor.", exc_info=True, ) + else: logger.warning("Could not send email invitation (Other Exception)", obj.portfolio, exc_info=True) messages.error(request, "Could not send email invitation. Portfolio invitation not saved.") diff --git a/src/registrar/forms/portfolio.py b/src/registrar/forms/portfolio.py index 888340d40..b055985d1 100644 --- a/src/registrar/forms/portfolio.py +++ b/src/registrar/forms/portfolio.py @@ -12,7 +12,6 @@ from registrar.models import ( DomainInformation, Portfolio, SeniorOfficial, - User, ) from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices @@ -111,9 +110,9 @@ class PortfolioSeniorOfficialForm(forms.ModelForm): return cleaned_data - class BasePortfolioMemberForm(forms.ModelForm): """Base form for the PortfolioMemberForm and PortfolioInvitedMemberForm""" + required_star = '*' role = forms.ChoiceField( choices=[ @@ -180,7 +179,7 @@ class BasePortfolioMemberForm(forms.ModelForm): class Meta: model = None - fields = ["roles", "additional_permissions" ] + fields = ["roles", "additional_permissions"] def __init__(self, *args, **kwargs): """ @@ -242,7 +241,7 @@ class BasePortfolioMemberForm(forms.ModelForm): logger.info(cleaned_data) return cleaned_data - + def map_instance_to_initial(self): """ Maps self.instance to self.initial, handling roles and permissions. @@ -301,7 +300,7 @@ class PortfolioMemberForm(BasePortfolioMemberForm): class Meta: model = UserPortfolioPermission - fields = ["roles", "additional_permissions" ] + fields = ["roles", "additional_permissions"] class PortfolioInvitedMemberForm(BasePortfolioMemberForm): @@ -311,8 +310,7 @@ class PortfolioInvitedMemberForm(BasePortfolioMemberForm): class Meta: model = PortfolioInvitation - fields = ["roles", "additional_permissions" ] - + fields = ["roles", "additional_permissions"] class PortfolioNewMemberForm(BasePortfolioMemberForm): @@ -336,4 +334,3 @@ class PortfolioNewMemberForm(BasePortfolioMemberForm): class Meta: model = PortfolioInvitation fields = ["portfolio", "email", "roles", "additional_permissions"] - diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 5252ed605..19e96719f 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -7,7 +7,6 @@ from typing import Optional from django_fsm import FSMField, transition, TransitionNotAllowed # type: ignore from django.db import models -from django.urls import reverse from django.utils import timezone from typing import Any from registrar.models.host import Host diff --git a/src/registrar/models/portfolio.py b/src/registrar/models/portfolio.py index 633f27126..e7730230e 100644 --- a/src/registrar/models/portfolio.py +++ b/src/registrar/models/portfolio.py @@ -10,6 +10,7 @@ import logging logger = logging.getLogger(__name__) + class Portfolio(TimeStampedModel): """ Portfolio is used for organizing domains/domain-requests into @@ -165,7 +166,7 @@ class Portfolio(TimeStampedModel): def get_suborganizations(self): """Returns all suborganizations associated with this portfolio""" return self.portfolio_suborganizations.all() - + def full_clean(self, exclude=None, validate_unique=True): logger.info("portfolio full clean") super().full_clean(exclude, validate_unique) diff --git a/src/registrar/models/utility/portfolio_helper.py b/src/registrar/models/utility/portfolio_helper.py index 24c1a7810..cde28e4bd 100644 --- a/src/registrar/models/utility/portfolio_helper.py +++ b/src/registrar/models/utility/portfolio_helper.py @@ -8,6 +8,7 @@ import logging logger = logging.getLogger(__name__) + class UserPortfolioRoleChoices(models.TextChoices): """ Roles make it easier for admins to look at diff --git a/src/registrar/utility/email_invitations.py b/src/registrar/utility/email_invitations.py index c0077c196..a3b93f5d5 100644 --- a/src/registrar/utility/email_invitations.py +++ b/src/registrar/utility/email_invitations.py @@ -1,7 +1,5 @@ from django.conf import settings from registrar.models import DomainInvitation -from registrar.models.portfolio_invitation import PortfolioInvitation -from registrar.models.user_portfolio_permission import UserPortfolioPermission from registrar.utility.errors import ( AlreadyDomainInvitedError, AlreadyDomainManagerError, @@ -9,7 +7,7 @@ from registrar.utility.errors import ( OutsideOrgMemberError, ) from registrar.utility.waffle import flag_is_active_for_user -from registrar.utility.email import send_templated_email, EmailSendingError +from registrar.utility.email import send_templated_email import logging logger = logging.getLogger(__name__) diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index f52688e1a..60fb9b7b1 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -1128,7 +1128,10 @@ class DomainUsersView(DomainBaseView): for portfolio_invitation in portfolio_invitations: logger.info(portfolio_invitation) logger.info(portfolio_invitation.roles) - if portfolio_invitation.roles and UserPortfolioRoleChoices.ORGANIZATION_ADMIN in portfolio_invitation.roles: + if ( + portfolio_invitation.roles + and UserPortfolioRoleChoices.ORGANIZATION_ADMIN in portfolio_invitation.roles + ): has_admin_flag = True break # Once we find one match, no need to check further @@ -1210,18 +1213,16 @@ class DomainAddUserView(DomainFormBaseView): # Determine membership in a different organization member_of_a_different_org = ( - (existing_org_permission and existing_org_permission.portfolio != requestor_org) or - (existing_org_invitation and existing_org_invitation.portfolio != requestor_org) - ) + existing_org_permission and existing_org_permission.portfolio != requestor_org + ) or (existing_org_invitation and existing_org_invitation.portfolio != requestor_org) # Determine membership in the same organization - member_of_this_org = ( - (existing_org_permission and existing_org_permission.portfolio == requestor_org) or - (existing_org_invitation and existing_org_invitation.portfolio == requestor_org) + member_of_this_org = (existing_org_permission and existing_org_permission.portfolio == requestor_org) or ( + existing_org_invitation and existing_org_invitation.portfolio == requestor_org ) return member_of_a_different_org, member_of_this_org - + def form_valid(self, form): """Add the specified user to this domain.""" requested_email = form.cleaned_data["email"] @@ -1232,7 +1233,9 @@ class DomainAddUserView(DomainFormBaseView): # Get the requestor's organization requestor_org = UserPortfolioPermission.objects.filter(user=requestor).first().portfolio - member_of_a_different_org, member_of_this_org = self._get_org_membership(requestor_org, requested_email, requested_user) + member_of_a_different_org, member_of_this_org = self._get_org_membership( + requestor_org, requested_email, requested_user + ) # determine portfolio of the domain (code currently is looking at requestor's portfolio) # if requested_email/user is not member or invited member of this portfolio @@ -1299,7 +1302,12 @@ class DomainAddUserView(DomainFormBaseView): def _handle_exceptions(self, exception, email): """Handle exceptions raised during the process.""" if isinstance(exception, EmailSendingError): - logger.warning("Could not send email invitation to %s for domain %s (EmailSendingError)", email, self.object, exc_info=True) + logger.warning( + "Could not send email invitation to %s for domain %s (EmailSendingError)", + email, + self.object, + exc_info=True, + ) messages.warning(self.request, "Could not send email invitation.") elif isinstance(exception, OutsideOrgMemberError): logger.warning( @@ -1342,6 +1350,7 @@ class DomainAddUserView(DomainFormBaseView): logger.warning("Could not send email invitation (Other Exception)", portfolio, exc_info=True) messages.warning(self.request, "Could not send email invitation.") + class DomainInvitationCancelView(SuccessMessageMixin, DomainInvitationPermissionCancelView): object: DomainInvitation fields = [] diff --git a/src/registrar/views/portfolios.py b/src/registrar/views/portfolios.py index a0e21532f..bce668665 100644 --- a/src/registrar/views/portfolios.py +++ b/src/registrar/views/portfolios.py @@ -1,5 +1,4 @@ import logging -from django.conf import settings from django.http import Http404, JsonResponse from django.shortcuts import get_object_or_404, redirect, render @@ -299,7 +298,7 @@ class PortfolioInvitedMemberEditView(PortfolioMemberEditPermissionView, View): "invitation": portfolio_invitation, }, ) - + def post(self, request, pk): portfolio_invitation = get_object_or_404(PortfolioInvitation, pk=pk) form = self.form_class(request.POST, instance=portfolio_invitation) @@ -520,7 +519,7 @@ class PortfolioAddMemberView(PortfolioMembersPermissionView, FormMixin): self.object = None # No existing PortfolioInvitation instance form = self.get_form() return self.render_to_response(self.get_context_data(form=form)) - + def post(self, request, *args, **kwargs): """Handle POST requests to process form submission.""" self.object = None # For a new invitation, there's no existing model instance @@ -536,16 +535,16 @@ class PortfolioAddMemberView(PortfolioMembersPermissionView, FormMixin): return self.form_valid(form) else: return self.form_invalid(form) - + def is_ajax(self): return self.request.headers.get("X-Requested-With") == "XMLHttpRequest" - + def form_invalid(self, form): if self.is_ajax(): return JsonResponse({"is_valid": False}) # Return a JSON response else: return super().form_invalid(form) # Handle non-AJAX requests normally - + def form_valid(self, form): super().form_valid(form) if self.is_ajax(): @@ -577,14 +576,15 @@ class PortfolioAddMemberView(PortfolioMembersPermissionView, FormMixin): self._handle_exceptions(e, portfolio, requested_email) return redirect(self.get_success_url()) - def get_success_url(self): - """Redirect to the members page.""" - return reverse("members") - def _handle_exceptions(self, exception, portfolio, email): """Handle exceptions raised during the process.""" if isinstance(exception, EmailSendingError): - logger.warning("Could not sent email invitation to %s for portfolio %s (EmailSendingError)", email, portfolio, exc_info=True) + logger.warning( + "Could not sent email invitation to %s for portfolio %s (EmailSendingError)", + email, + portfolio, + exc_info=True, + ) messages.warning(self.request, "Could not send email invitation.") elif isinstance(exception, MissingEmailError): messages.error(self.request, str(exception))