mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-27 04:58:42 +02:00
Merge pull request #3706 from cisagov/backup/3622-warn-org-admins
#3622: Members page, warn org admins when they are modifying themselves - [BACKUP]
This commit is contained in:
commit
d07fab387a
9 changed files with 189 additions and 41 deletions
|
@ -11,7 +11,7 @@ import { initDomainRequestsTable } from './table-domain-requests.js';
|
||||||
import { initMembersTable } from './table-members.js';
|
import { initMembersTable } from './table-members.js';
|
||||||
import { initMemberDomainsTable } from './table-member-domains.js';
|
import { initMemberDomainsTable } from './table-member-domains.js';
|
||||||
import { initEditMemberDomainsTable } from './table-edit-member-domains.js';
|
import { initEditMemberDomainsTable } from './table-edit-member-domains.js';
|
||||||
import { initPortfolioNewMemberPageToggle, initAddNewMemberPageListeners, initPortfolioMemberPageRadio } from './portfolio-member-page.js';
|
import { initPortfolioNewMemberPageToggle, initAddNewMemberPageListeners, initPortfolioMemberPage } from './portfolio-member-page.js';
|
||||||
import { initDomainRequestForm } from './domain-request-form.js';
|
import { initDomainRequestForm } from './domain-request-form.js';
|
||||||
import { initDomainManagersPage } from './domain-managers.js';
|
import { initDomainManagersPage } from './domain-managers.js';
|
||||||
import { initDomainDNSSEC } from './domain-dnssec.js';
|
import { initDomainDNSSEC } from './domain-dnssec.js';
|
||||||
|
@ -56,8 +56,10 @@ initDomainDNSSEC();
|
||||||
|
|
||||||
initFormErrorHandling();
|
initFormErrorHandling();
|
||||||
|
|
||||||
|
// Init the portfolio member page
|
||||||
|
initPortfolioMemberPage();
|
||||||
|
|
||||||
// Init the portfolio new member page
|
// Init the portfolio new member page
|
||||||
initPortfolioMemberPageRadio();
|
|
||||||
initPortfolioNewMemberPageToggle();
|
initPortfolioNewMemberPageToggle();
|
||||||
initAddNewMemberPageListeners();
|
initAddNewMemberPageListeners();
|
||||||
|
|
||||||
|
|
|
@ -193,10 +193,14 @@ export function initAddNewMemberPageListeners() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initalize the radio for the member pages
|
// Initalize the radio for the member pages
|
||||||
export function initPortfolioMemberPageRadio() {
|
export function initPortfolioMemberPage() {
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
let memberForm = document.getElementById("member_form");
|
let memberForm = document.getElementById("member_form");
|
||||||
let newMemberForm = document.getElementById("add_member_form")
|
let newMemberForm = document.getElementById("add_member_form");
|
||||||
|
let editSelfWarningModal = document.getElementById("toggle-member-permissions-edit-self");
|
||||||
|
let editSelfWarningModalConfirm = document.getElementById("member-permissions-edit-self");
|
||||||
|
|
||||||
|
// Init the radio
|
||||||
if (memberForm || newMemberForm) {
|
if (memberForm || newMemberForm) {
|
||||||
hookupRadioTogglerListener(
|
hookupRadioTogglerListener(
|
||||||
'role',
|
'role',
|
||||||
|
@ -206,5 +210,36 @@ export function initPortfolioMemberPageRadio() {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Init the "edit self" warning modal, which triggers when the user is trying to edit themselves.
|
||||||
|
// The dom will include these elements when this occurs.
|
||||||
|
// NOTE: This logic does not trigger when the user is the ONLY admin in the portfolio.
|
||||||
|
// This is because info alerts are used rather than modals in this case.
|
||||||
|
if (memberForm && editSelfWarningModal) {
|
||||||
|
// Only show the warning modal when the user is changing their ROLE.
|
||||||
|
var canSubmit = document.querySelector(`input[name="role"]:checked`)?.value != "organization_member";
|
||||||
|
let radioButtons = document.querySelectorAll(`input[name="role"]`);
|
||||||
|
radioButtons.forEach(function (radioButton) {
|
||||||
|
radioButton.addEventListener("change", function() {
|
||||||
|
let selectedValue = radioButton.checked ? radioButton.value : null;
|
||||||
|
canSubmit = selectedValue != "organization_member";
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prevent form submission assuming org member is selected for role, and open the modal.
|
||||||
|
memberForm.addEventListener("submit", function(e) {
|
||||||
|
if (!canSubmit) {
|
||||||
|
e.preventDefault();
|
||||||
|
editSelfWarningModal.click();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hook the confirm button on the modal to form submission.
|
||||||
|
editSelfWarningModalConfirm.addEventListener("click", function() {
|
||||||
|
canSubmit = true;
|
||||||
|
memberForm.submit();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -411,6 +411,27 @@ class PortfolioMemberForm(BasePortfolioMemberForm):
|
||||||
model = UserPortfolioPermission
|
model = UserPortfolioPermission
|
||||||
fields = ["roles", "additional_permissions"]
|
fields = ["roles", "additional_permissions"]
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""
|
||||||
|
Override of clean to ensure that the user isn't removing themselves
|
||||||
|
if they're the only portfolio admin
|
||||||
|
"""
|
||||||
|
super().clean()
|
||||||
|
role = self.cleaned_data.get("role")
|
||||||
|
if self.instance and hasattr(self.instance, "user") and hasattr(self.instance, "portfolio"):
|
||||||
|
if role and self.instance.user.is_only_admin_of_portfolio(self.instance.portfolio):
|
||||||
|
# This is how you associate a validation error to a particular field.
|
||||||
|
# The alternative is to do this in clean_role, but execution order matters.
|
||||||
|
raise forms.ValidationError(
|
||||||
|
{
|
||||||
|
"role": forms.ValidationError(
|
||||||
|
"You can't change your member access because you're "
|
||||||
|
"the only admin for this organization. "
|
||||||
|
"To change your access, you'll need to add another admin."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class PortfolioInvitedMemberForm(BasePortfolioMemberForm):
|
class PortfolioInvitedMemberForm(BasePortfolioMemberForm):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -474,7 +474,7 @@ class User(AbstractUser):
|
||||||
admin_count = admins.count()
|
admin_count = admins.count()
|
||||||
|
|
||||||
# Check if the current user is in the list of admins
|
# Check if the current user is in the list of admins
|
||||||
if admin_count == 1 and admins.first().user == self:
|
if admin_count == 1 and admins.first() and admins.first().user == self:
|
||||||
return True # The user is the only admin
|
return True # The user is the only admin
|
||||||
|
|
||||||
# If there are other admins or the user is not the only one
|
# If there are other admins or the user is not the only one
|
||||||
|
|
|
@ -98,6 +98,18 @@ Organization member
|
||||||
</address>
|
</address>
|
||||||
|
|
||||||
{% if portfolio_permission %}
|
{% if portfolio_permission %}
|
||||||
|
{% if member and member.id == request.user.id and is_only_admin %}
|
||||||
|
<div class="usa-alert usa-alert--info usa-alert--slim">
|
||||||
|
<div class="usa-alert__body">
|
||||||
|
<p class="usa-alert__text ">
|
||||||
|
You're the only admin for this organization.
|
||||||
|
Organizations must have at least one admin.
|
||||||
|
To remove yourself or change your member access,
|
||||||
|
you'll need to add another admin.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% include "includes/summary_item.html" with title='Member access and permissions' permissions=True value=portfolio_permission edit_link=edit_url editable=has_edit_members_portfolio_permission %}
|
{% include "includes/summary_item.html" with title='Member access and permissions' permissions=True value=portfolio_permission edit_link=edit_url editable=has_edit_members_portfolio_permission %}
|
||||||
{% elif portfolio_invitation %}
|
{% elif portfolio_invitation %}
|
||||||
{% include "includes/summary_item.html" with title='Member access and permissions' permissions=True value=portfolio_invitation edit_link=edit_url editable=has_edit_members_portfolio_permission %}
|
{% include "includes/summary_item.html" with title='Member access and permissions' permissions=True value=portfolio_invitation edit_link=edit_url editable=has_edit_members_portfolio_permission %}
|
||||||
|
|
|
@ -75,12 +75,26 @@
|
||||||
<!-- Member email -->
|
<!-- Member email -->
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
|
|
||||||
<!-- Member access radio buttons (Toggles other sections) -->
|
<!-- Member access radio buttons (Toggles other sections) -->
|
||||||
<fieldset class="usa-fieldset">
|
<fieldset class="usa-fieldset">
|
||||||
<legend>
|
<legend>
|
||||||
<h2 class="margin-top-0">Member access</h2>
|
<h2 class="margin-top-0">Member access</h2>
|
||||||
</legend>
|
</legend>
|
||||||
|
|
||||||
|
{% if member and member.id == request.user.id and is_only_admin %}
|
||||||
|
<div class="usa-alert usa-alert--info usa-alert--slim margin-top-1 margin-bottom-1">
|
||||||
|
<div class="usa-alert__body">
|
||||||
|
<p class="usa-alert__text ">
|
||||||
|
You're the only admin for this organization.
|
||||||
|
Organizations must have at least one admin.
|
||||||
|
To remove yourself or change your member access,
|
||||||
|
you'll need to add another admin.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<em>Select the level of access for this member. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
<em>Select the level of access for this member. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||||
|
|
||||||
{% with group_classes="margin-top-0" add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
{% with group_classes="margin-top-0" add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||||
|
@ -109,4 +123,22 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% comment %} If an admin is trying to edit themselves, show a modal {% endcomment %}
|
||||||
|
{% if member and member.id == request.user.id and not is_only_admin %}
|
||||||
|
<a
|
||||||
|
id="toggle-member-permissions-edit-self"
|
||||||
|
href="#modal-member-permissions-edit-self"
|
||||||
|
class="display-none"
|
||||||
|
aria-controls="modal-member-permissions-edit-self"
|
||||||
|
data-open-modal
|
||||||
|
>Edit self</a>
|
||||||
|
<div
|
||||||
|
class="usa-modal"
|
||||||
|
id="modal-member-permissions-edit-self"
|
||||||
|
data-force-action
|
||||||
|
>
|
||||||
|
{% include 'includes/modal.html' with modal_heading="Are you sure you want to change your member access?" modal_description="You’ve selected basic access, which means you’ll no longer be able to manage member permissions. This action cannot be undone." modal_button_id="member-permissions-edit-self" modal_button_text="Yes, change my access" %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
{% endblock portfolio_content%}
|
{% endblock portfolio_content%}
|
||||||
|
|
|
@ -477,8 +477,8 @@ class TestBasePortfolioMemberForms(TestCase):
|
||||||
self.assertTrue(form.is_valid(), f"Form {form_class.__name__} failed validation with data: {data}")
|
self.assertTrue(form.is_valid(), f"Form {form_class.__name__} failed validation with data: {data}")
|
||||||
return form
|
return form
|
||||||
|
|
||||||
def _assert_form_has_error(self, form_class, data, field_name):
|
def _assert_form_has_error(self, form_class, data, field_name, instance=None):
|
||||||
form = form_class(data=data)
|
form = form_class(data=data, instance=instance)
|
||||||
self.assertFalse(form.is_valid())
|
self.assertFalse(form.is_valid())
|
||||||
self.assertIn(field_name, form.errors)
|
self.assertIn(field_name, form.errors)
|
||||||
|
|
||||||
|
@ -504,17 +504,23 @@ class TestBasePortfolioMemberForms(TestCase):
|
||||||
"domain_permissions": "", # Simulate missing field
|
"domain_permissions": "", # Simulate missing field
|
||||||
"member_permissions": "", # Simulate missing field
|
"member_permissions": "", # Simulate missing field
|
||||||
}
|
}
|
||||||
|
user_portfolio_permission, _ = UserPortfolioPermission.objects.get_or_create(
|
||||||
|
portfolio=self.portfolio, user=self.user
|
||||||
|
)
|
||||||
|
portfolio_invitation, _ = PortfolioInvitation.objects.get_or_create(portfolio=self.portfolio, email="hi@ho")
|
||||||
|
|
||||||
# Check required fields for all forms
|
# Check required fields for all forms
|
||||||
self._assert_form_has_error(PortfolioMemberForm, data, "domain_request_permissions")
|
self._assert_form_has_error(PortfolioMemberForm, data, "domain_request_permissions", user_portfolio_permission)
|
||||||
self._assert_form_has_error(PortfolioMemberForm, data, "domain_permissions")
|
self._assert_form_has_error(PortfolioMemberForm, data, "domain_permissions", user_portfolio_permission)
|
||||||
self._assert_form_has_error(PortfolioMemberForm, data, "member_permissions")
|
self._assert_form_has_error(PortfolioMemberForm, data, "member_permissions", user_portfolio_permission)
|
||||||
self._assert_form_has_error(PortfolioInvitedMemberForm, data, "domain_request_permissions")
|
self._assert_form_has_error(
|
||||||
self._assert_form_has_error(PortfolioInvitedMemberForm, data, "domain_permissions")
|
PortfolioInvitedMemberForm, data, "domain_request_permissions", portfolio_invitation
|
||||||
self._assert_form_has_error(PortfolioInvitedMemberForm, data, "member_permissions")
|
)
|
||||||
self._assert_form_has_error(PortfolioNewMemberForm, data, "domain_request_permissions")
|
self._assert_form_has_error(PortfolioInvitedMemberForm, data, "domain_permissions", portfolio_invitation)
|
||||||
self._assert_form_has_error(PortfolioNewMemberForm, data, "domain_permissions")
|
self._assert_form_has_error(PortfolioInvitedMemberForm, data, "member_permissions", portfolio_invitation)
|
||||||
self._assert_form_has_error(PortfolioNewMemberForm, data, "member_permissions")
|
self._assert_form_has_error(PortfolioNewMemberForm, data, "domain_request_permissions", portfolio_invitation)
|
||||||
|
self._assert_form_has_error(PortfolioNewMemberForm, data, "domain_permissions", portfolio_invitation)
|
||||||
|
self._assert_form_has_error(PortfolioNewMemberForm, data, "member_permissions", portfolio_invitation)
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_clean_validates_required_fields_for_admin_role(self):
|
def test_clean_validates_required_fields_for_admin_role(self):
|
||||||
|
@ -529,7 +535,6 @@ class TestBasePortfolioMemberForms(TestCase):
|
||||||
portfolio=self.portfolio, user=self.user
|
portfolio=self.portfolio, user=self.user
|
||||||
)
|
)
|
||||||
portfolio_invitation, _ = PortfolioInvitation.objects.get_or_create(portfolio=self.portfolio, email="hi@ho")
|
portfolio_invitation, _ = PortfolioInvitation.objects.get_or_create(portfolio=self.portfolio, email="hi@ho")
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN.value,
|
"role": UserPortfolioRoleChoices.ORGANIZATION_ADMIN.value,
|
||||||
}
|
}
|
||||||
|
@ -677,6 +682,7 @@ class TestBasePortfolioMemberForms(TestCase):
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_invalid_data_for_member(self):
|
def test_invalid_data_for_member(self):
|
||||||
"""Test invalid form submission for a member role with missing permissions."""
|
"""Test invalid form submission for a member role with missing permissions."""
|
||||||
|
portfolio_invitation, _ = PortfolioInvitation.objects.get_or_create(portfolio=self.portfolio, email="hi@ho")
|
||||||
data = {
|
data = {
|
||||||
"email": "hi@ho.com",
|
"email": "hi@ho.com",
|
||||||
"portfolio": self.portfolio.id,
|
"portfolio": self.portfolio.id,
|
||||||
|
@ -685,5 +691,5 @@ class TestBasePortfolioMemberForms(TestCase):
|
||||||
"member_permissions": "", # Missing field
|
"member_permissions": "", # Missing field
|
||||||
"domain_permissions": "", # Missing field
|
"domain_permissions": "", # Missing field
|
||||||
}
|
}
|
||||||
self._assert_form_has_error(PortfolioMemberForm, data, "domain_request_permissions")
|
self._assert_form_has_error(PortfolioMemberForm, data, "domain_request_permissions", portfolio_invitation)
|
||||||
self._assert_form_has_error(PortfolioInvitedMemberForm, data, "member_permissions")
|
self._assert_form_has_error(PortfolioInvitedMemberForm, data, "member_permissions", portfolio_invitation)
|
||||||
|
|
|
@ -1826,10 +1826,7 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 400)
|
self.assertEqual(response.status_code, 400)
|
||||||
expected_error_message = (
|
expected_error_message = "the only admin for this organization"
|
||||||
"There must be at least one admin in your organization. Give another member admin "
|
|
||||||
"permissions, make sure they log into the registrar, and then remove this member."
|
|
||||||
)
|
|
||||||
self.assertContains(response, expected_error_message, status_code=400)
|
self.assertContains(response, expected_error_message, status_code=400)
|
||||||
|
|
||||||
# assert that send_portfolio_admin_removal_emails is not called
|
# assert that send_portfolio_admin_removal_emails is not called
|
||||||
|
@ -2155,17 +2152,14 @@ class TestPortfolioMemberDeleteView(WebTest):
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
|
|
||||||
expected_error_message = (
|
expected_error_message = "the only admin for this organization."
|
||||||
"There must be at least one admin in your organization. Give another member admin "
|
|
||||||
"permissions, make sure they log into the registrar, and then remove this member."
|
|
||||||
)
|
|
||||||
|
|
||||||
args, kwargs = mock_error.call_args
|
args, kwargs = mock_error.call_args
|
||||||
# Check if first arg is a WSGIRequest, confirms request object passed correctly
|
# Check if first arg is a WSGIRequest, confirms request object passed correctly
|
||||||
# WSGIRequest protocol is basically the HTTPRequest but in Django form (ie POST '/member/1/delete')
|
# WSGIRequest protocol is basically the HTTPRequest but in Django form (ie POST '/member/1/delete')
|
||||||
self.assertIsInstance(args[0], WSGIRequest)
|
self.assertIsInstance(args[0], WSGIRequest)
|
||||||
# Check that the error message matches the expected error message
|
# Check that the error message matches the expected error message
|
||||||
self.assertEqual(args[1], expected_error_message)
|
self.assertIn(expected_error_message, args[1])
|
||||||
|
|
||||||
# Location is used for a 3xx HTTP status code to indicate that the URL was redirected
|
# Location is used for a 3xx HTTP status code to indicate that the URL was redirected
|
||||||
# and then confirm that we're still on the Manage Members page
|
# and then confirm that we're still on the Manage Members page
|
||||||
|
@ -4639,6 +4633,17 @@ class TestPortfolioMemberEditView(WebTest):
|
||||||
# Get the user's admin permission
|
# Get the user's admin permission
|
||||||
admin_permission = UserPortfolioPermission.objects.get(user=self.user, portfolio=self.portfolio)
|
admin_permission = UserPortfolioPermission.objects.get(user=self.user, portfolio=self.portfolio)
|
||||||
|
|
||||||
|
# Create a second permission so the user isn't just deleting themselves
|
||||||
|
member = create_test_user()
|
||||||
|
UserPortfolioPermission.objects.create(
|
||||||
|
user=member, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
||||||
|
)
|
||||||
|
|
||||||
|
# First, verify that the change modal is on the page
|
||||||
|
response = self.client.get(reverse("member-permissions", kwargs={"member_pk": admin_permission.id}))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, "Yes, change my access")
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse("member-permissions", kwargs={"member_pk": admin_permission.id}),
|
reverse("member-permissions", kwargs={"member_pk": admin_permission.id}),
|
||||||
{
|
{
|
||||||
|
@ -4652,6 +4657,39 @@ class TestPortfolioMemberEditView(WebTest):
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response["Location"], reverse("home"))
|
self.assertEqual(response["Location"], reverse("home"))
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@override_flag("organization_members", active=True)
|
||||||
|
def test_admin_removing_own_admin_role_only_admin(self):
|
||||||
|
"""Tests that admin removing their own admin role when they are the only admin
|
||||||
|
throws a validation error.
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
|
||||||
|
# Get the user's admin permission
|
||||||
|
admin_permission = UserPortfolioPermission.objects.get(user=self.user, portfolio=self.portfolio)
|
||||||
|
|
||||||
|
# First, verify that the info alert is present on the page
|
||||||
|
response = self.client.get(reverse("member-permissions", kwargs={"member_pk": admin_permission.id}))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertContains(response, "To remove yourself or change your member access")
|
||||||
|
|
||||||
|
# Then, verify that the right form error is shown
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("member-permissions", kwargs={"member_pk": admin_permission.id}),
|
||||||
|
{
|
||||||
|
"role": UserPortfolioRoleChoices.ORGANIZATION_MEMBER,
|
||||||
|
"domain_permissions": UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS,
|
||||||
|
"member_permissions": "no_access",
|
||||||
|
"domain_request_permissions": "no_access",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
error_message = "the only admin for this organization"
|
||||||
|
self.assertIn(error_message, str(response.context["form"].errors))
|
||||||
|
|
||||||
|
|
||||||
class TestPortfolioInvitedMemberEditView(WebTest):
|
class TestPortfolioInvitedMemberEditView(WebTest):
|
||||||
"""Tests for the edit invited member page on portfolios"""
|
"""Tests for the edit invited member page on portfolios"""
|
||||||
|
|
|
@ -114,6 +114,7 @@ class PortfolioMemberView(DetailView, View):
|
||||||
"member_has_view_members_portfolio_permission": member_has_view_members_portfolio_permission,
|
"member_has_view_members_portfolio_permission": member_has_view_members_portfolio_permission,
|
||||||
"member_has_edit_members_portfolio_permission": member_has_edit_members_portfolio_permission,
|
"member_has_edit_members_portfolio_permission": member_has_edit_members_portfolio_permission,
|
||||||
"member_has_view_all_domains_portfolio_permission": member_has_view_all_domains_portfolio_permission,
|
"member_has_view_all_domains_portfolio_permission": member_has_view_all_domains_portfolio_permission,
|
||||||
|
"is_only_admin": request.user.is_only_admin_of_portfolio(portfolio_permission.portfolio),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -160,8 +161,8 @@ class PortfolioMemberDeleteView(View):
|
||||||
)
|
)
|
||||||
if member.is_only_admin_of_portfolio(portfolio):
|
if member.is_only_admin_of_portfolio(portfolio):
|
||||||
return (
|
return (
|
||||||
"There must be at least one admin in your organization. Give another member admin "
|
"You can't remove yourself because you're the only admin for this organization. "
|
||||||
"permissions, make sure they log into the registrar, and then remove this member."
|
"To remove yourself, you'll need to add another admin."
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@ -257,9 +258,7 @@ class PortfolioMemberEditView(DetailView, View):
|
||||||
def get(self, request, member_pk):
|
def get(self, request, member_pk):
|
||||||
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=member_pk)
|
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=member_pk)
|
||||||
user = portfolio_permission.user
|
user = portfolio_permission.user
|
||||||
|
|
||||||
form = self.form_class(instance=portfolio_permission)
|
form = self.form_class(instance=portfolio_permission)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
self.template_name,
|
self.template_name,
|
||||||
|
@ -267,6 +266,7 @@ class PortfolioMemberEditView(DetailView, View):
|
||||||
"form": form,
|
"form": form,
|
||||||
"member": user,
|
"member": user,
|
||||||
"portfolio_permission": portfolio_permission,
|
"portfolio_permission": portfolio_permission,
|
||||||
|
"is_only_admin": request.user.is_only_admin_of_portfolio(portfolio_permission.portfolio),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -307,15 +307,17 @@ class PortfolioMemberEditView(DetailView, View):
|
||||||
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("member", member_pk=member_pk) if not removing_admin_role_on_self else redirect("home")
|
return redirect("member", member_pk=member_pk) if not removing_admin_role_on_self else redirect("home")
|
||||||
|
else:
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
self.template_name,
|
self.template_name,
|
||||||
{
|
{
|
||||||
"form": form,
|
"form": form,
|
||||||
"member": user, # Pass the user object again to the template
|
"member": user,
|
||||||
},
|
"portfolio_permission": portfolio_permission,
|
||||||
)
|
"is_only_admin": request.user.is_only_admin_of_portfolio(portfolio_permission.portfolio),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def _handle_exceptions(self, exception):
|
def _handle_exceptions(self, exception):
|
||||||
"""Handle exceptions raised during the process."""
|
"""Handle exceptions raised during the process."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue