This commit is contained in:
Rachid Mrad 2024-10-03 18:44:34 -04:00
parent 2ecbab459b
commit d933da326c
No known key found for this signature in database
6 changed files with 165 additions and 73 deletions

View file

@ -13,4 +13,5 @@ from .domain import (
)
from .portfolio import (
PortfolioOrgAddressForm,
PortfolioMemberForm,
)

View file

@ -4,6 +4,9 @@ import logging
from django import forms
from django.core.validators import RegexValidator
from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
from ..models import DomainInformation, Portfolio, SeniorOfficial
logger = logging.getLogger(__name__)
@ -95,3 +98,31 @@ class PortfolioSeniorOfficialForm(forms.ModelForm):
cleaned_data = super().clean()
cleaned_data.pop("full_name", None)
return cleaned_data
class PortfolioMemberForm(forms.ModelForm):
"""
Form for updating a portfolio member.
"""
roles = forms.MultipleChoiceField(
choices=UserPortfolioRoleChoices.choices,
widget=forms.SelectMultiple(attrs={'class': 'usa-select'}),
required=False,
label="Roles",
)
additional_permissions = forms.MultipleChoiceField(
choices=UserPortfolioPermissionChoices.choices,
widget=forms.SelectMultiple(attrs={'class': 'usa-select'}),
required=False,
label="Additional Permissions",
)
class Meta:
model = UserPortfolioPermission
fields = [
"roles",
"additional_permissions",
]

View file

@ -91,12 +91,14 @@
</li>
{% endif %}
{% if has_organization_members_flag and has_view_members_portfolio_permission %}
<li class="usa-nav__primary-item">
<a href="/members/" class="usa-nav-link {% if path|is_members_subpage %} usa-current{% endif %}">
Members
</a>
</li>
{% if has_organization_members_flag %}
{% if has_view_members_portfolio_permission or has_edit_members_portfolio_permission %}
<li class="usa-nav__primary-item">
<a href="/members/" class="usa-nav-link {% if path|is_members_subpage %} usa-current{% endif %}">
Members
</a>
</li>
{% endif %}
{% endif %}
<li class="usa-nav__primary-item">

View file

@ -7,14 +7,6 @@
{% block portfolio_content %}
<div class="grid-row grid-gap">
<div class="tablet:grid-col-3">
<p class="font-body-md margin-top-0 margin-bottom-2
text-primary-darker text-semibold"
>
<span class="usa-sr-only"> Portfolio name:</span> {{ portfolio }}
</p>
</div>
<div class="tablet:grid-col-9" id="main-content">
{% block messages %}
@ -23,7 +15,22 @@
<h1>Member</h1>
<p>The name of your organization will be publicly listed as the domain registrant.</p>
<p>{{ user.first_name }}</p>
<hr>
<form class="usa-form usa-form--large" method="post" novalidate>
{% csrf_token %}
{% input_with_errors form.roles %}
{% input_with_errors form.additional_permissions %}
<button
type="submit"
class="usa-button"
>Submit</button>
</form>

View file

@ -8,6 +8,7 @@ from registrar.models.portfolio_invitation import PortfolioInvitation
from registrar.models.user import User
from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.utility.portfolio_helper import UserPortfolioRoleChoices
from operator import itemgetter
@login_required
@ -15,31 +16,60 @@ def get_portfolio_members_json(request):
"""Given the current request,
get all members that are associated with the given portfolio"""
portfolio = request.GET.get("portfolio")
member_ids = get_member_ids_from_request(request, portfolio)
objects = User.objects.filter(id__in=member_ids)
# member_ids = get_member_ids_from_request(request, portfolio)
# members = User.objects.filter(id__in=member_ids)
admin_ids = UserPortfolioPermission.objects.filter(
portfolio=portfolio,
roles__overlap=[
UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
],
).values_list("user__id", flat=True)
portfolio_invitation_emails = PortfolioInvitation.objects.filter(portfolio=portfolio).values_list(
"email", flat=True
permissions = UserPortfolioPermission.objects.filter(portfolio=portfolio).select_related("user").values_list("pk", "user__first_name", "user__last_name", "user__email", "user__last_login", "roles")
invitations = PortfolioInvitation.objects.filter(portfolio=portfolio).values_list(
'pk', 'email', 'portfolio_roles', 'portfolio_additional_permissions', 'status'
)
unfiltered_total = objects.count()
# Convert the permissions queryset into a list of dictionaries
permission_list = [
{
'id': perm[0],
'first_name': perm[1],
'last_name': perm[2],
'email': perm[3],
'last_active': perm[4],
'roles': perm[5],
'source': 'permission' # Mark the source as permissions
}
for perm in permissions
]
objects = apply_search(objects, request)
# objects = apply_status_filter(objects, request)
objects = apply_sorting(objects, request)
# Convert the invitations queryset into a list of dictionaries
invitation_list = [
{
'id': invite[0],
'first_name': None, # No first name in invitations
'last_name': None, # No last name in invitations
'email': invite[1],
'roles': invite[2],
'additional_permissions': invite[3],
'status': invite[4],
'last_active': 'Invited',
'source': 'invitation' # Mark the source as invitations
}
for invite in invitations
]
paginator = Paginator(objects, 10)
# Combine both lists into one unified list
combined_list = permission_list + invitation_list
unfiltered_total = len(combined_list)
combined_list = apply_search(combined_list, request)
combined_list = apply_sorting(combined_list, request)
paginator = Paginator(combined_list, 10)
page_number = request.GET.get("page", 1)
page_obj = paginator.get_page(page_number)
members = [
serialize_members(request, portfolio, member, request.user, admin_ids, portfolio_invitation_emails)
for member in page_obj.object_list
serialize_members(request, portfolio, item, request.user)
for item in page_obj.object_list
]
return JsonResponse(
@ -55,44 +85,43 @@ def get_portfolio_members_json(request):
)
def get_member_ids_from_request(request, portfolio):
"""Given the current request,
get all members that are associated with the given portfolio"""
member_ids = []
if portfolio:
member_ids = UserPortfolioPermission.objects.filter(portfolio=portfolio).values_list("user__id", flat=True)
return member_ids
# def get_member_ids_from_request(request, portfolio):
# """Given the current request,
# get all members that are associated with the given portfolio"""
# member_ids = []
# if portfolio:
# member_ids = UserPortfolioPermission.objects.filter(portfolio=portfolio).values_list("user__id", flat=True)
# return member_ids
def apply_search(queryset, request):
search_term = request.GET.get("search_term")
def apply_search(data_list, request):
search_term = request.GET.get("search_term", "").lower()
if search_term:
queryset = queryset.filter(
Q(username__icontains=search_term)
| Q(first_name__icontains=search_term)
| Q(last_name__icontains=search_term)
| Q(email__icontains=search_term)
)
return queryset
# Filter the list based on the search term (case-insensitive)
data_list = [
item for item in data_list
if search_term in (item.get('first_name', '') or '').lower()
or search_term in (item.get('last_name', '') or '').lower()
or search_term in (item.get('email', '') or '').lower()
]
return data_list
def apply_sorting(queryset, request):
def apply_sorting(data_list, request):
sort_by = request.GET.get("sort_by", "id") # Default to 'id'
order = request.GET.get("order", "asc") # Default to 'asc'
if sort_by == "member":
sort_by = ["email", "first_name", "middle_name", "last_name"]
else:
sort_by = [sort_by]
sort_by = "email"
if order == "desc":
sort_by = [f"-{field}" for field in sort_by]
# Sort the list
data_list = sorted(data_list, key=itemgetter(sort_by), reverse=(order == "desc"))
return queryset.order_by(*sort_by)
return data_list
def serialize_members(request, portfolio, member, user, admin_ids, portfolio_invitation_emails):
def serialize_members(request, portfolio, item, user):
# ------- VIEW ONLY
# If not view_only (the user has permissions to edit/manage users), show the gear icon with "Manage" link.
# If view_only (the user only has view user permissions), show the "View" link (no gear icon).
@ -106,20 +135,20 @@ def serialize_members(request, portfolio, member, user, admin_ids, portfolio_inv
view_only = not user.has_edit_members_portfolio_permission(portfolio) or not user_can_edit_other_users
# ------- USER STATUSES
is_invited = member.email in portfolio_invitation_emails
last_active = "Invited" if is_invited else "Unknown"
if member.last_login:
last_active = member.last_login.strftime("%b. %d, %Y")
is_admin = member.id in admin_ids
is_admin = UserPortfolioRoleChoices.ORGANIZATION_ADMIN in item['roles']
action_url = '#'
if item['source'] == 'permission':
action_url = reverse("member", kwargs={"pk": item['id']})
# ------- SERIALIZE
member_json = {
"id": member.id,
"name": member.get_formatted_name(),
"email": member.email,
"id": item['id'],
"name": (item['first_name'] or '') + ' ' + (item['last_name'] or ''),
"email": item['email'],
"is_admin": is_admin,
"last_active": last_active,
"action_url": reverse("member", kwargs={"pk": member.id}), # TODO: Future ticket?
"last_active": item['last_active'],
"action_url": action_url,
"action_label": ("View" if view_only else "Manage"),
"svg_icon": ("visibility" if view_only else "settings"),
}

View file

@ -3,7 +3,7 @@ from django.http import Http404
from django.shortcuts import render
from django.urls import reverse
from django.contrib import messages
from registrar.forms.portfolio import PortfolioOrgAddressForm, PortfolioSeniorOfficialForm
from registrar.forms.portfolio import PortfolioMemberForm, PortfolioOrgAddressForm, PortfolioSeniorOfficialForm
from registrar.models import Portfolio, User
from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.utility.portfolio_helper import UserPortfolioRoleChoices
@ -17,7 +17,7 @@ from registrar.views.utility.permission_views import (
)
from django.views.generic import View
from django.views.generic.edit import FormMixin
from django.shortcuts import get_object_or_404, redirect
logger = logging.getLogger(__name__)
@ -55,11 +55,33 @@ class PortfolioMembersView(PortfolioMembersPermissionView, View):
class PortfolioMemberView(PortfolioMemberPermissionView, View):
template_name = "portfolio_member.html"
model = User
form_class = PortfolioMemberForm
# def get(self, request):
# """Add additional context data to the template."""
# return render(request, self.template_name, context=self.get_context_data())
def get(self, request, pk):
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
user = portfolio_permission.user
form = self.form_class(instance=portfolio_permission)
return render(request, self.template_name, {
'form': form,
'user': user,
})
def post(self, request, pk):
portfolio_permission = get_object_or_404(UserPortfolioPermission, pk=pk)
user = portfolio_permission.user
form = self.form_class(request.POST, instance=portfolio_permission)
if form.is_valid():
form.save()
return redirect('home')
return render(request, self.template_name, {
'form': form,
'user': user, # Pass the user object again to the template
})