mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-14 08:37:03 +02:00
Revise for multiple portfolios
This commit is contained in:
parent
fc2f4f08cc
commit
2af57c8aa8
4 changed files with 65 additions and 31 deletions
|
@ -10,6 +10,7 @@ from django.http import HttpResponseRedirect
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django_fsm import get_available_FIELD_transitions, FSMField
|
from django_fsm import get_available_FIELD_transitions, FSMField
|
||||||
from registrar.models.domain_information import DomainInformation
|
from registrar.models.domain_information import DomainInformation
|
||||||
|
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 waffle.decorators import flag_is_active
|
from waffle.decorators import flag_is_active
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
|
@ -3008,6 +3009,28 @@ class PortfolioAdmin(ListHeaderAdmin):
|
||||||
"creator",
|
"creator",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def get_admin_users(self, obj):
|
||||||
|
# Filter UserPortfolioPermission objects related to the portfolio
|
||||||
|
admin_permissions = UserPortfolioPermission.objects.filter(
|
||||||
|
portfolio=obj, roles__contains=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get the user objects associated with these permissions
|
||||||
|
admin_users = User.objects.filter(portfolio_permissions__in=admin_permissions)
|
||||||
|
|
||||||
|
return admin_users
|
||||||
|
|
||||||
|
def get_non_admin_users(self, obj):
|
||||||
|
# Filter UserPortfolioPermission objects related to the portfolio that do NOT have the "Admin" role
|
||||||
|
non_admin_permissions = UserPortfolioPermission.objects.filter(portfolio=obj).exclude(
|
||||||
|
roles__contains=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get the user objects associated with these permissions
|
||||||
|
non_admin_users = User.objects.filter(portfolio_permissions__in=non_admin_permissions)
|
||||||
|
|
||||||
|
return non_admin_users
|
||||||
|
|
||||||
def display_admins(self, obj):
|
def display_admins(self, obj):
|
||||||
"""Get joined users who are Admin, unpack and return an HTML block.
|
"""Get joined users who are Admin, unpack and return an HTML block.
|
||||||
|
|
||||||
|
@ -3016,7 +3039,7 @@ class PortfolioAdmin(ListHeaderAdmin):
|
||||||
data would display in a custom change form without extensive template customization.
|
data would display in a custom change form without extensive template customization.
|
||||||
|
|
||||||
Will be used in the field_readonly block"""
|
Will be used in the field_readonly block"""
|
||||||
admins = [user for user in obj.user.all() if "Admin" in user.portfolio_role_summary]
|
admins = self.get_admin_users(obj)
|
||||||
if not admins:
|
if not admins:
|
||||||
return format_html("<p>No admins found.</p>")
|
return format_html("<p>No admins found.</p>")
|
||||||
|
|
||||||
|
@ -3030,13 +3053,13 @@ class PortfolioAdmin(ListHeaderAdmin):
|
||||||
admin_details += "<div class='admin-icon-group admin-icon-group__clipboard-link'>"
|
admin_details += "<div class='admin-icon-group admin-icon-group__clipboard-link'>"
|
||||||
admin_details += f"<input aria-hidden='true' class='display-none' value='{portfolio_admin.email}'>"
|
admin_details += f"<input aria-hidden='true' class='display-none' value='{portfolio_admin.email}'>"
|
||||||
admin_details += (
|
admin_details += (
|
||||||
"<button class='usa-button usa-button--unstyled padding-right-1 usa-button--icon"
|
"<button class='usa-button usa-button--unstyled padding-right-1 usa-button--icon padding-left-05"
|
||||||
+ "button--clipboard copy-to-clipboard text-no-underline' type='button'>"
|
+ "button--clipboard copy-to-clipboard text-no-underline' type='button'>"
|
||||||
)
|
)
|
||||||
admin_details += "<svg class='usa-icon'>"
|
admin_details += "<svg class='usa-icon'>"
|
||||||
admin_details += "<use aria-hidden='true' xlink:href='/public/img/sprite.svg#content_copy'></use>"
|
admin_details += "<use aria-hidden='true' xlink:href='/public/img/sprite.svg#content_copy'></use>"
|
||||||
admin_details += "</svg>"
|
admin_details += "</svg>"
|
||||||
admin_details += "<span class='padding-left-05'>Copy</span>"
|
admin_details += "Copy"
|
||||||
admin_details += "</button>"
|
admin_details += "</button>"
|
||||||
admin_details += "</div><br>"
|
admin_details += "</div><br>"
|
||||||
admin_details += f"{portfolio_admin.phone}"
|
admin_details += f"{portfolio_admin.phone}"
|
||||||
|
@ -3053,7 +3076,7 @@ class PortfolioAdmin(ListHeaderAdmin):
|
||||||
data would display in a custom change form without extensive template customization.
|
data would display in a custom change form without extensive template customization.
|
||||||
|
|
||||||
Will be used in the after_help_text block."""
|
Will be used in the after_help_text block."""
|
||||||
members = [user for user in obj.user.all() if "Admin" not in user.portfolio_role_summary]
|
members = self.get_non_admin_users(obj)
|
||||||
if not members:
|
if not members:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
@ -3073,7 +3096,7 @@ class PortfolioAdmin(ListHeaderAdmin):
|
||||||
member_details += f"<td>{member.email}</td>"
|
member_details += f"<td>{member.email}</td>"
|
||||||
member_details += f"<td>{member.phone}</td>"
|
member_details += f"<td>{member.phone}</td>"
|
||||||
member_details += "<td>"
|
member_details += "<td>"
|
||||||
for role in member.portfolio_role_summary:
|
for role in member.portfolio_role_summary(obj):
|
||||||
member_details += f"<span class='usa-tag'>{role}</span> "
|
member_details += f"<span class='usa-tag'>{role}</span> "
|
||||||
member_details += "</td></tr>"
|
member_details += "</td></tr>"
|
||||||
member_details += "</tbody></table>"
|
member_details += "</tbody></table>"
|
||||||
|
@ -3083,7 +3106,7 @@ class PortfolioAdmin(ListHeaderAdmin):
|
||||||
|
|
||||||
def display_members_summary(self, obj):
|
def display_members_summary(self, obj):
|
||||||
"""Will be passed as context and used in the field_readonly block."""
|
"""Will be passed as context and used in the field_readonly block."""
|
||||||
members = [user for user in obj.user.all() if "Admin" not in user.portfolio_role_summary]
|
members = self.get_non_admin_users(obj)
|
||||||
if not members:
|
if not members:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
|
@ -245,39 +245,39 @@ class User(AbstractUser):
|
||||||
return permission.portfolio
|
return permission.portfolio
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def has_edit_requests(self):
|
def has_edit_requests(self, portfolio):
|
||||||
return self._has_portfolio_permission(UserPortfolioPermissionChoices.EDIT_REQUESTS)
|
return self._has_portfolio_permission(portfolio, UserPortfolioPermissionChoices.EDIT_REQUESTS)
|
||||||
|
|
||||||
@property
|
def portfolio_role_summary(self, portfolio):
|
||||||
def portfolio_role_summary(self):
|
|
||||||
"""Returns a list of roles based on the user's permissions."""
|
"""Returns a list of roles based on the user's permissions."""
|
||||||
roles = []
|
roles = []
|
||||||
|
|
||||||
# Define the conditions and their corresponding roles
|
# Define the conditions and their corresponding roles
|
||||||
conditions_roles = [
|
conditions_roles = [
|
||||||
(self.has_edit_suborganization(), ["Admin"]),
|
(self.has_edit_suborganization(portfolio), ["Admin"]),
|
||||||
(
|
(
|
||||||
self.has_view_all_domains_permission()
|
self.has_view_all_domains_permission(portfolio)
|
||||||
and self.has_domain_requests_portfolio_permission()
|
and self.has_domain_requests_portfolio_permission(portfolio)
|
||||||
and self.has_edit_requests(),
|
and self.has_edit_requests(portfolio),
|
||||||
["View-only admin", "Domain requestor"],
|
["View-only admin", "Domain requestor"],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
self.has_view_all_domains_permission() and self.has_domain_requests_portfolio_permission(),
|
self.has_view_all_domains_permission(portfolio)
|
||||||
|
and self.has_domain_requests_portfolio_permission(portfolio),
|
||||||
["View-only admin"],
|
["View-only admin"],
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
self.has_base_portfolio_permission()
|
self.has_base_portfolio_permission(portfolio)
|
||||||
and self.has_edit_requests()
|
and self.has_edit_requests(portfolio)
|
||||||
and self.has_domains_portfolio_permission(),
|
and self.has_domains_portfolio_permission(portfolio),
|
||||||
["Domain requestor", "Domain manager"],
|
["Domain requestor", "Domain manager"],
|
||||||
),
|
),
|
||||||
(self.has_base_portfolio_permission() and self.has_edit_requests(), ["Domain requestor"]),
|
(self.has_base_portfolio_permission(portfolio) and self.has_edit_requests(portfolio), ["Domain requestor"]),
|
||||||
(
|
(
|
||||||
self.has_base_portfolio_permission() and self.has_domains_portfolio_permission(),
|
self.has_base_portfolio_permission(portfolio) and self.has_domains_portfolio_permission(portfolio),
|
||||||
["Domain manager"],
|
["Domain manager"],
|
||||||
),
|
),
|
||||||
(self.has_base_portfolio_permission(), ["Member"]),
|
(self.has_base_portfolio_permission(portfolio), ["Member"]),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Evaluate conditions and add roles
|
# Evaluate conditions and add roles
|
||||||
|
|
|
@ -17,7 +17,7 @@ Template for an input field with a clipboard
|
||||||
>
|
>
|
||||||
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use>
|
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<span>Copy</span>
|
Copy
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,7 +25,7 @@ Template for an input field with a clipboard
|
||||||
<div class="admin-icon-group admin-icon-group__clipboard-link">
|
<div class="admin-icon-group admin-icon-group__clipboard-link">
|
||||||
<input aria-hidden="true" class="display-none" value="{{ field.email }}" />
|
<input aria-hidden="true" class="display-none" value="{{ field.email }}" />
|
||||||
<button
|
<button
|
||||||
class="usa-button usa-button--unstyled padding-right-1 usa-button--icon button--clipboard copy-to-clipboard text-no-underline"
|
class="usa-button usa-button--unstyled padding-right-1 usa-button--icon button--clipboard copy-to-clipboard text-no-underline padding-left-05"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
|
@ -33,7 +33,7 @@ Template for an input field with a clipboard
|
||||||
>
|
>
|
||||||
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use>
|
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#content_copy"></use>
|
||||||
</svg>
|
</svg>
|
||||||
<span class="padding-left-05">Copy</span>
|
Copy
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -45,6 +45,7 @@ from registrar.models import (
|
||||||
from registrar.models.portfolio_invitation import PortfolioInvitation
|
from registrar.models.portfolio_invitation import PortfolioInvitation
|
||||||
from registrar.models.senior_official import SeniorOfficial
|
from registrar.models.senior_official import SeniorOfficial
|
||||||
from registrar.models.user_domain_role import UserDomainRole
|
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.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
|
||||||
from registrar.models.verified_by_staff import VerifiedByStaff
|
from registrar.models.verified_by_staff import VerifiedByStaff
|
||||||
from .common import (
|
from .common import (
|
||||||
|
@ -2129,8 +2130,10 @@ class TestPortfolioAdmin(TestCase):
|
||||||
last_name="Meoward",
|
last_name="Meoward",
|
||||||
title="Captain",
|
title="Captain",
|
||||||
email="meaoward@gov.gov",
|
email="meaoward@gov.gov",
|
||||||
portfolio=self.portfolio,
|
)
|
||||||
portfolio_roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
||||||
|
UserPortfolioPermission.objects.all().create(
|
||||||
|
user=admin_user_1, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
||||||
)
|
)
|
||||||
|
|
||||||
admin_user_2 = User.objects.create(
|
admin_user_2 = User.objects.create(
|
||||||
|
@ -2139,8 +2142,10 @@ class TestPortfolioAdmin(TestCase):
|
||||||
last_name="Poopy",
|
last_name="Poopy",
|
||||||
title="Major",
|
title="Major",
|
||||||
email="poopy@gov.gov",
|
email="poopy@gov.gov",
|
||||||
portfolio=self.portfolio,
|
)
|
||||||
portfolio_roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN],
|
|
||||||
|
UserPortfolioPermission.objects.all().create(
|
||||||
|
user=admin_user_2, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
|
||||||
)
|
)
|
||||||
|
|
||||||
admin_user_3 = User.objects.create(
|
admin_user_3 = User.objects.create(
|
||||||
|
@ -2149,8 +2154,10 @@ class TestPortfolioAdmin(TestCase):
|
||||||
last_name="Max",
|
last_name="Max",
|
||||||
title="Road warrior",
|
title="Road warrior",
|
||||||
email="madmax@gov.gov",
|
email="madmax@gov.gov",
|
||||||
portfolio=self.portfolio,
|
)
|
||||||
portfolio_roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
|
||||||
|
UserPortfolioPermission.objects.all().create(
|
||||||
|
user=admin_user_3, portfolio=self.portfolio, roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER]
|
||||||
)
|
)
|
||||||
|
|
||||||
admin_user_4 = User.objects.create(
|
admin_user_4 = User.objects.create(
|
||||||
|
@ -2159,8 +2166,12 @@ class TestPortfolioAdmin(TestCase):
|
||||||
last_name="Smith",
|
last_name="Smith",
|
||||||
title="Program",
|
title="Program",
|
||||||
email="thematrix@gov.gov",
|
email="thematrix@gov.gov",
|
||||||
|
)
|
||||||
|
|
||||||
|
UserPortfolioPermission.objects.all().create(
|
||||||
|
user=admin_user_4,
|
||||||
portfolio=self.portfolio,
|
portfolio=self.portfolio,
|
||||||
portfolio_additional_permissions=[
|
additional_permissions=[
|
||||||
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
|
UserPortfolioPermissionChoices.VIEW_PORTFOLIO,
|
||||||
UserPortfolioPermissionChoices.EDIT_REQUESTS,
|
UserPortfolioPermissionChoices.EDIT_REQUESTS,
|
||||||
],
|
],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue