Add in checks for if theyre only admin or have any in progress requests

This commit is contained in:
Rebecca Hsieh 2024-10-24 16:22:47 -07:00
parent 5f6e8968c7
commit cd517ae885
No known key found for this signature in database
3 changed files with 128 additions and 49 deletions

View file

@ -2074,9 +2074,14 @@ class MembersTable extends LoadTableBase {
modalHeading = `Are you sure you want to delete ${member_email}?`;
modalDescription = `They will no longer be able to access this organization. \n
This action cannot be undone.`;
} else if (num_domains === 1) {
modalHeading = `Are you sure you want to delete ${member_email}?`;
modalDescription = `<b>${member_email}</b> currently manages ${num_domains} domain in the organization. \n
Removing them from the organization will remove all of their domains. They will no longer be able to \n
access this organization. This action cannot be undone.`;
} else if (num_domains >= 1) {
modalHeading = `Are you sure you want to delete ${member_email}?`;
modalDescription = `${member_email} current manages ${num_domains} domains in the organization \n
modalDescription = `<b>${member_email}</b> currently manages ${num_domains} domains in the organization. \n
Removing them from the organization will remove all of their domains. They will no longer be able to \n
access this organization. This action cannot be undone.`;
}
@ -2136,7 +2141,6 @@ class MembersTable extends LoadTableBase {
</div>
`
this.tableWrapper.appendChild(modal);
console.log("modal", modal)
}
generateKebabHTML(member_id, member_name, last_active) {
@ -2195,9 +2199,44 @@ class MembersTable extends LoadTableBase {
* @param {*} domainRequestPk - the identifier for the request that we're deleting
* @param {*} pageToDisplay - If we're deleting the last item on a page that is not page 1, we'll need to display the previous page
*/
// This is what we originally have
// deleteMember(member_delete_url, pageToDisplay) {
// // Use to debug uswds modal issues
// console.log(member_delete_url)
// // Get csrf token
// const csrfToken = getCsrfToken();
// // Create FormData object and append the CSRF token
// const formData = `csrfmiddlewaretoken=${encodeURIComponent(csrfToken)}`;
// fetch(`${member_delete_url}`, {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/x-www-form-urlencoded',
// 'X-CSRFToken': csrfToken,
// },
// body: formData
// })
// .then(response => {
// if (!response.ok) {
// throw new Error(`HTTP error! status: ${response.status}`);
// }
// // Update data and UI
// this.loadTable(pageToDisplay, this.currentSortBy, this.currentOrder, this.scrollToTable, this.currentSearchTerm);
// })
// .catch(error => console.error('Error fetching domain requests:', error));
// }
deleteMember(member_delete_url, pageToDisplay) {
// Use to debug uswds modal issues
console.log(member_delete_url)
// Debugging
console.log(member_delete_url);
const inProgressResponse = "This member has an active domain request and can't \n"
"be removed from this organization. <Contact the .gov team link> to remove them."
const onlyAdminResponse = "There must be at least one admin in your organization. \n"
"Give another member admin permissions, make sure they log into the registrar, \n"
"and then remove this member."
// Get csrf token
const csrfToken = getCsrfToken();
@ -2211,44 +2250,40 @@ class MembersTable extends LoadTableBase {
'X-CSRFToken': csrfToken,
},
body: formData
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
if (response.status === 204) {
// TODO: Add success alert with "You've removed member.email from the organization." text
console.log('Member successfully deleted');
// Update data and UI
this.loadTable(pageToDisplay, this.currentSortBy, this.currentOrder, this.scrollToTable, this.currentSearchTerm);
} else {
// If the response isn't 204, handle the error response
return response.json().then(data => {
console.log("Member response not 204");
if (data.error) {
// TODO: We maybe don't need the consts above and have those
// responses in the portfolios.py JSON response. Formatting though?
// This should display the error given from backend for
// either only admin OR in progress requests
this.displayErrorMessage(data.error);
} else {
throw new Error(`Unexpected status: ${response.status}`);
}
});
}
})
.catch(error => console.error('Error fetching domain requests:', error));
.catch(error => {
console.error('Error deleting member:', error);
this.displayErrorMessage(error.message);
});
}
// deleteDomainRequest(domainRequestPk, pageToDisplay) {
// // Use to debug uswds modal issues
// //console.log('deleteDomainRequest')
displayErrorMessage(errorMessage) {
alert(errorMessage); // Just debugging for now
}
// // Get csrf token
// const csrfToken = getCsrfToken();
// // Create FormData object and append the CSRF token
// const formData = `csrfmiddlewaretoken=${encodeURIComponent(csrfToken)}&delete-domain-request=`;
// fetch(`/domain-request/${domainRequestPk}/delete`, {
// method: 'POST',
// headers: {
// 'Content-Type': 'application/x-www-form-urlencoded',
// 'X-CSRFToken': csrfToken,
// },
// body: formData
// })
// .then(response => {
// if (!response.ok) {
// throw new Error(`HTTP error! status: ${response.status}`);
// }
// // Update data and UI
// this.loadTable(pageToDisplay, this.currentSortBy, this.currentOrder, this.scrollToTable, this.currentSearchTerm);
// })
// .catch(error => console.error('Error fetching domain requests:', error));
// }
/**
* Loads rows in the members list, as well as updates pagination around the members list

View file

@ -1,5 +1,6 @@
import logging
from django.apps import apps
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.db.models import Q
@ -472,10 +473,41 @@ class User(AbstractUser):
else:
return UserDomainRole.objects.filter(user=self).values_list("id", flat=True)
# def get_user_domain_count(self, request):
# """Returns the count of domains associated with this user on UserDomainRole or Portfolio"""
# portfolio = request.session.get("portfolio")
# if self.is_org_user(request) and self.has_view_all_domains_portfolio_permission(portfolio):
# return DomainInformation.objects.filter(portfolio=portfolio).count()
# else:
# return UserDomainRole.objects.filter(user=self).count()
def get_active_requests_count_in_portfolio(self, request):
"""Return count of active requests for the portfolio associated with the request."""
portfolio_id = request.session.get(
"portfolio_id"
) # Adjust based on how you store the portfolio ID in the session
if not portfolio_id:
return 0 # No portfolio ID found
allowed_states = [
DomainRequest.DomainRequestStatus.SUBMITTED,
DomainRequest.DomainRequestStatus.IN_REVIEW,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
]
# Assuming you have a way to filter domain requests by portfolio
active_requests_count = self.domain_requests_created.filter(
status__in=allowed_states, portfolio__id=portfolio_id # Ensure this field exists on the DomainRequest model
).count()
return active_requests_count
def is_only_admin_of_portfolio(self, portfolio):
"""Check if the user is the only admin of the given portfolio."""
UserPortfolioPermission = apps.get_model("registrar", "UserPortfolioPermission")
# Grab admin permission ability we want
admin_permission = UserPortfolioPermissionChoices.EDIT_PORTFOLIO
# Get all users with admin permission for this portfolio
admins = UserPortfolioPermission.objects.filter(portfolio=portfolio, roles__contains=[admin_permission])
# Check if there is more than one admin
if admins.count() == 1 and admins.first().user == self:
# The user is the only admin
return True
# There are other admins OR the user is not the only one
return False

View file

@ -1,5 +1,5 @@
import logging
from django.http import HttpResponse, Http404
from django.http import HttpResponse, Http404, JsonResponse
from django.shortcuts import render
from django.urls import reverse
from django.contrib import messages
@ -100,6 +100,7 @@ class PortfolioMemberView(PortfolioMemberPermissionView, View):
},
)
class PortfolioMemberDeleteView(PortfolioMemberPermission, View):
def post(self, request, pk):
@ -108,6 +109,17 @@ class PortfolioMemberDeleteView(PortfolioMemberPermission, View):
Redirect to a success page after deletion (or any other appropriate page).
"""
portfolio_member_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
member = portfolio_member_permission.user
active_requests_count = member.get_active_requests_count_in_portfolio(request)
print(f"Active requests count for member {member.id}: {active_requests_count}")
if active_requests_count > 0:
return JsonResponse({"error": "ERROR: Member has in-progress requests and cannot be removed."}, status=400)
# If they are the last manager of a domain
if member.is_only_admin_of_portfolio(portfolio_member_permission.portfolio):
return JsonResponse({"error": "ERROR: Member is the only admin."}, status=400)
portfolio_member_permission.delete()