Merge remote-tracking branch 'origin/main' into rh/2406-clipboard

This commit is contained in:
CocoByte 2024-08-01 13:04:02 -06:00
commit ae7ea346a4
No known key found for this signature in database
GPG key ID: BBFAA2526384C97F
19 changed files with 234 additions and 54 deletions

View file

@ -29,6 +29,7 @@ on:
- hotgov - hotgov
- litterbox - litterbox
- ms - ms
- ad
# GitHub Actions has no "good" way yet to dynamically input branches # GitHub Actions has no "good" way yet to dynamically input branches
branch: branch:
description: 'Branch to deploy' description: 'Branch to deploy'

View file

@ -29,6 +29,7 @@ jobs:
|| startsWith(github.head_ref, 'litterbox/') || startsWith(github.head_ref, 'litterbox/')
|| startsWith(github.head_ref, 'ag/') || startsWith(github.head_ref, 'ag/')
|| startsWith(github.head_ref, 'ms/') || startsWith(github.head_ref, 'ms/')
|| startsWith(github.head_ref, 'ad/')
outputs: outputs:
environment: ${{ steps.var.outputs.environment}} environment: ${{ steps.var.outputs.environment}}
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"

View file

@ -16,6 +16,7 @@ on:
- stable - stable
- staging - staging
- development - development
- ad
- ms - ms
- ag - ag
- litterbox - litterbox

View file

@ -16,6 +16,7 @@ on:
options: options:
- staging - staging
- development - development
- ad
- ms - ms
- ag - ag
- litterbox - litterbox

View file

@ -0,0 +1,32 @@
---
applications:
- name: getgov-ad
buildpacks:
- python_buildpack
path: ../../src
instances: 1
memory: 512M
stack: cflinuxfs4
timeout: 180
command: ./run.sh
health-check-type: http
health-check-http-endpoint: /health
health-check-invocation-timeout: 40
env:
# Send stdout and stderr straight to the terminal without buffering
PYTHONUNBUFFERED: yup
# Tell Django where to find its configuration
DJANGO_SETTINGS_MODULE: registrar.config.settings
# Tell Django where it is being hosted
DJANGO_BASE_URL: https://getgov-ad.app.cloud.gov
# Tell Django how much stuff to log
DJANGO_LOG_LEVEL: INFO
# default public site location
GETGOV_PUBLIC_SITE_URL: https://get.gov
# Flag to disable/enable features in prod environments
IS_PRODUCTION: False
routes:
- route: getgov-ad.app.cloud.gov
services:
- getgov-credentials
- getgov-ad-database

View file

@ -1169,6 +1169,8 @@ document.addEventListener('DOMContentLoaded', function() {
const statusIndicator = document.querySelector('.domain__filter-indicator'); const statusIndicator = document.querySelector('.domain__filter-indicator');
const statusToggle = document.querySelector('.usa-button--filter'); const statusToggle = document.querySelector('.usa-button--filter');
const noPortfolioFlag = document.getElementById('no-portfolio-js-flag'); const noPortfolioFlag = document.getElementById('no-portfolio-js-flag');
const portfolioElement = document.getElementById('portfolio-js-value');
const portfolioValue = portfolioElement ? portfolioElement.getAttribute('data-portfolio') : null;
/** /**
* Loads rows in the domains list, as well as updates pagination around the domains list * Loads rows in the domains list, as well as updates pagination around the domains list
@ -1178,10 +1180,15 @@ document.addEventListener('DOMContentLoaded', function() {
* @param {*} order - the sort order {asc, desc} * @param {*} order - the sort order {asc, desc}
* @param {*} scroll - control for the scrollToElement functionality * @param {*} scroll - control for the scrollToElement functionality
* @param {*} searchTerm - the search term * @param {*} searchTerm - the search term
* @param {*} portfolio - the portfolio id
*/ */
function loadDomains(page, sortBy = currentSortBy, order = currentOrder, scroll = scrollToTable, status = currentStatus, searchTerm = currentSearchTerm) { function loadDomains(page, sortBy = currentSortBy, order = currentOrder, scroll = scrollToTable, status = currentStatus, searchTerm = currentSearchTerm, portfolio = portfolioValue) {
// fetch json of page of domains, given params // fetch json of page of domains, given params
fetch(`/get-domains-json/?page=${page}&sort_by=${sortBy}&order=${order}&status=${status}&search_term=${searchTerm}`) let url = `/get-domains-json/?page=${page}&sort_by=${sortBy}&order=${order}&status=${status}&search_term=${searchTerm}`
if (portfolio)
url += `&portfolio=${portfolio}`
fetch(url)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.error) { if (data.error) {

View file

@ -665,6 +665,7 @@ ALLOWED_HOSTS = [
"getgov-stable.app.cloud.gov", "getgov-stable.app.cloud.gov",
"getgov-staging.app.cloud.gov", "getgov-staging.app.cloud.gov",
"getgov-development.app.cloud.gov", "getgov-development.app.cloud.gov",
"getgov-ad.app.cloud.gov",
"getgov-ms.app.cloud.gov", "getgov-ms.app.cloud.gov",
"getgov-ag.app.cloud.gov", "getgov-ag.app.cloud.gov",
"getgov-litterbox.app.cloud.gov", "getgov-litterbox.app.cloud.gov",

View file

@ -22,6 +22,11 @@ class UserFixture:
""" """
ADMINS = [ ADMINS = [
{
"username": "aad084c3-66cc-4632-80eb-41cdf5c5bcbf",
"first_name": "Aditi",
"last_name": "Green",
},
{ {
"username": "be17c826-e200-4999-9389-2ded48c43691", "username": "be17c826-e200-4999-9389-2ded48c43691",
"first_name": "Matthew", "first_name": "Matthew",
@ -120,6 +125,11 @@ class UserFixture:
] ]
STAFF = [ STAFF = [
{
"username": "ffec5987-aa84-411b-a05a-a7ee5cbcde54",
"first_name": "Aditi-Analyst",
"last_name": "Green-Analyst",
},
{ {
"username": "d6bf296b-fac5-47ff-9c12-f88ccc5c1b99", "username": "d6bf296b-fac5-47ff-9c12-f88ccc5c1b99",
"first_name": "Matthew-Analyst", "first_name": "Matthew-Analyst",

View file

@ -0,0 +1,38 @@
# Generated by Django 4.2.10 on 2024-07-25 12:45
import django.contrib.postgres.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("registrar", "0113_user_portfolio_user_portfolio_additional_permissions_and_more"),
]
operations = [
migrations.AlterField(
model_name="user",
name="portfolio_additional_permissions",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
choices=[
("view_all_domains", "View all domains and domain reports"),
("view_managed_domains", "View managed domains"),
("view_member", "View members"),
("edit_member", "Create and edit members"),
("view_all_requests", "View all requests"),
("view_created_requests", "View created requests"),
("edit_requests", "Create and edit requests"),
("view_portfolio", "View organization"),
("edit_portfolio", "Edit organization"),
],
max_length=50,
),
blank=True,
help_text="Select one or more additional permissions.",
null=True,
size=None,
),
),
]

View file

@ -215,6 +215,11 @@ class DomainRequest(TimeStampedModel):
} }
return org_election_map return org_election_map
@classmethod
def get_org_label(cls, org_name: str):
# Translating the key that is given to the direct readable value
return cls(org_name).label if org_name else None
class OrganizationChoicesVerbose(models.TextChoices): class OrganizationChoicesVerbose(models.TextChoices):
""" """
Tertiary organization choices Tertiary organization choices

View file

@ -76,11 +76,6 @@ class User(AbstractUser):
VIEW_ALL_DOMAINS = "view_all_domains", "View all domains and domain reports" VIEW_ALL_DOMAINS = "view_all_domains", "View all domains and domain reports"
VIEW_MANAGED_DOMAINS = "view_managed_domains", "View managed domains" VIEW_MANAGED_DOMAINS = "view_managed_domains", "View managed domains"
# EDIT_DOMAINS is really self.domains. We add is hear and leverage it in has_permission
# so we have one way to test for portfolio and domain edit permissions
# Do we need to check for portfolio domains specifically?
# NOTE: A user on an org can currently invite a user outside the org
EDIT_DOMAINS = "edit_domains", "User is a manager on a domain"
VIEW_MEMBER = "view_member", "View members" VIEW_MEMBER = "view_member", "View members"
EDIT_MEMBER = "edit_member", "Create and edit members" EDIT_MEMBER = "edit_member", "Create and edit members"
@ -268,11 +263,6 @@ class User(AbstractUser):
def _has_portfolio_permission(self, portfolio_permission): def _has_portfolio_permission(self, portfolio_permission):
"""The views should only call this function when testing for perms and not rely on roles.""" """The views should only call this function when testing for perms and not rely on roles."""
# EDIT_DOMAINS === user is a manager on a domain (has UserDomainRole)
# NOTE: Should we check whether the domain is in the portfolio?
if portfolio_permission == self.UserPortfolioPermissionChoices.EDIT_DOMAINS and self.domains.exists():
return True
if not self.portfolio: if not self.portfolio:
return False return False
@ -286,21 +276,14 @@ class User(AbstractUser):
return self._has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_PORTFOLIO) return self._has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_PORTFOLIO)
def has_domains_portfolio_permission(self): def has_domains_portfolio_permission(self):
return ( return self._has_portfolio_permission(
self._has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS) User.UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS
or self._has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS) ) or self._has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_MANAGED_DOMAINS)
# or self._has_portfolio_permission(User.UserPortfolioPermissionChoices.EDIT_DOMAINS)
)
def has_edit_domains_portfolio_permission(self):
return self._has_portfolio_permission(User.UserPortfolioPermissionChoices.EDIT_DOMAINS)
def has_domain_requests_portfolio_permission(self): def has_domain_requests_portfolio_permission(self):
return ( return self._has_portfolio_permission(
self._has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS) User.UserPortfolioPermissionChoices.VIEW_ALL_REQUESTS
or self._has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_CREATED_REQUESTS) ) or self._has_portfolio_permission(User.UserPortfolioPermissionChoices.VIEW_CREATED_REQUESTS)
# or self._has_portfolio_permission(User.UserPortfolioPermissionChoices.EDIT_REQUESTS)
)
@classmethod @classmethod
def needs_identity_verification(cls, email, uuid): def needs_identity_verification(cls, email, uuid):

View file

@ -5,8 +5,8 @@ accept and become a domain manager.
</p> </p>
<p> <p>
An “invited” status indicates that the recipient has not logged in to the registrar since the invitation was sent. An “invited” status indicates that the recipient has not logged in to the registrar since the invitation was sent. Deleting an invitation with an "invited" status will prevent the user from signing in.
A “received” status indicates that the recipient has logged in. A “received” status indicates that the recipient has logged in. Deleting an invitation with a "received" status will not revoke that user's access from the domain. To remove a user who has already signed in, go to <a class="text-underline" href="{% url 'admin:registrar_userdomainrole_changelist' %}">User domain roles</a> and delete the role for the correct domain/manager combination.
</p> </p>
<p> <p>

View file

@ -2,7 +2,6 @@
<section class="section--outlined domain-requests" id="domain-requests"> <section class="section--outlined domain-requests" id="domain-requests">
<div class="grid-row"> <div class="grid-row">
<!-- Use portfolio_base_permission when merging into 2366 and then delete this comment -->
{% if portfolio is None %} {% if portfolio is None %}
<div class="mobile:grid-col-12 desktop:grid-col-6"> <div class="mobile:grid-col-12 desktop:grid-col-6">
<h2 id="domain-requests-header" class="flex-6">Domain requests</h2> <h2 id="domain-requests-header" class="flex-6">Domain requests</h2>

View file

@ -2,10 +2,12 @@
<section class="section--outlined domains{% if portfolio is not None %} margin-top-0{% endif %}" id="domains"> <section class="section--outlined domains{% if portfolio is not None %} margin-top-0{% endif %}" id="domains">
<div class="section--outlined__header margin-bottom-3 {% if portfolio is None %} section--outlined__header--no-portfolio justify-content-space-between{% else %} grid-row{% endif %}"> <div class="section--outlined__header margin-bottom-3 {% if portfolio is None %} section--outlined__header--no-portfolio justify-content-space-between{% else %} grid-row{% endif %}">
<!-- Use portfolio_base_permission when merging into 2366 then delete this comment -->
{% if portfolio is None %} {% if portfolio is None %}
<h2 id="domains-header" class="display-inline-block">Domains</h2> <h2 id="domains-header" class="display-inline-block">Domains</h2>
<span class="display-none" id="no-portfolio-js-flag"></span> <span class="display-none" id="no-portfolio-js-flag"></span>
{% else %}
<!-- Embedding the portfolio value in a data attribute -->
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
{% endif %} {% endif %}
<div class="section--outlined__search {% if portfolio %} mobile:grid-col-12 desktop:grid-col-6{% endif %}"> <div class="section--outlined__search {% if portfolio %} mobile:grid-col-12 desktop:grid-col-6{% endif %}">
<section aria-label="Domains search component" class="margin-top-2"> <section aria-label="Domains search component" class="margin-top-2">
@ -45,7 +47,6 @@
</section> </section>
</div> </div>
</div> </div>
<!-- Use portfolio_base_permission when merging into 2366 then delete this comment -->
{% if portfolio %} {% if portfolio %}
<div class="display-flex flex-align-center"> <div class="display-flex flex-align-center">
<span class="margin-right-2 margin-top-neg-1 usa-prose text-base-darker">Filter by</span> <span class="margin-right-2 margin-top-neg-1 usa-prose text-base-darker">Filter by</span>
@ -149,7 +150,6 @@
<th data-sortable="name" scope="col" role="columnheader">Domain name</th> <th data-sortable="name" scope="col" role="columnheader">Domain name</th>
<th data-sortable="expiration_date" scope="col" role="columnheader">Expires</th> <th data-sortable="expiration_date" scope="col" role="columnheader">Expires</th>
<th data-sortable="state_display" scope="col" role="columnheader">Status</th> <th data-sortable="state_display" scope="col" role="columnheader">Status</th>
<!-- Use portfolio_base_permission when merging into 2366 then delete this comment -->
{% if portfolio %} {% if portfolio %}
<th data-sortable="suborganization" scope="col" role="columnheader">Suborganization</th> <th data-sortable="suborganization" scope="col" role="columnheader">Suborganization</th>
{% endif %} {% endif %}

View file

@ -164,7 +164,7 @@ class TestDomainInvitationAdmin(TestCase):
) )
# Assert that the filters are added # Assert that the filters are added
self.assertContains(response, "invited", count=4) self.assertContains(response, "invited", count=5)
self.assertContains(response, "Invited", count=2) self.assertContains(response, "Invited", count=2)
self.assertContains(response, "retrieved", count=2) self.assertContains(response, "retrieved", count=2)
self.assertContains(response, "Retrieved", count=2) self.assertContains(response, "Retrieved", count=2)

View file

@ -1292,7 +1292,6 @@ class TestUser(TestCase):
1. Returns False when a user does not have a portfolio 1. Returns False when a user does not have a portfolio
2. Returns True when user has direct permission 2. Returns True when user has direct permission
3. Returns True when user has permission through a role 3. Returns True when user has permission through a role
4. Returns True EDIT_DOMAINS when user does not have the perm but has UserDomainRole
Note: This tests _get_portfolio_permissions as a side effect Note: This tests _get_portfolio_permissions as a side effect
""" """
@ -1304,11 +1303,9 @@ class TestUser(TestCase):
user_can_view_all_domains = self.user.has_domains_portfolio_permission() user_can_view_all_domains = self.user.has_domains_portfolio_permission()
user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission() user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission()
user_can_edit_domains = self.user.has_edit_domains_portfolio_permission()
self.assertFalse(user_can_view_all_domains) self.assertFalse(user_can_view_all_domains)
self.assertFalse(user_can_view_all_requests) self.assertFalse(user_can_view_all_requests)
self.assertFalse(user_can_edit_domains)
self.user.portfolio = portfolio self.user.portfolio = portfolio
self.user.save() self.user.save()
@ -1316,11 +1313,9 @@ class TestUser(TestCase):
user_can_view_all_domains = self.user.has_domains_portfolio_permission() user_can_view_all_domains = self.user.has_domains_portfolio_permission()
user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission() user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission()
user_can_edit_domains = self.user.has_edit_domains_portfolio_permission()
self.assertTrue(user_can_view_all_domains) self.assertTrue(user_can_view_all_domains)
self.assertFalse(user_can_view_all_requests) self.assertFalse(user_can_view_all_requests)
self.assertFalse(user_can_edit_domains)
self.user.portfolio_roles = [User.UserPortfolioRoleChoices.ORGANIZATION_ADMIN] self.user.portfolio_roles = [User.UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
self.user.save() self.user.save()
@ -1328,11 +1323,9 @@ class TestUser(TestCase):
user_can_view_all_domains = self.user.has_domains_portfolio_permission() user_can_view_all_domains = self.user.has_domains_portfolio_permission()
user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission() user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission()
user_can_edit_domains = self.user.has_edit_domains_portfolio_permission()
self.assertTrue(user_can_view_all_domains) self.assertTrue(user_can_view_all_domains)
self.assertTrue(user_can_view_all_requests) self.assertTrue(user_can_view_all_requests)
self.assertFalse(user_can_edit_domains)
UserDomainRole.objects.all().get_or_create( UserDomainRole.objects.all().get_or_create(
user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER
@ -1340,11 +1333,9 @@ class TestUser(TestCase):
user_can_view_all_domains = self.user.has_domains_portfolio_permission() user_can_view_all_domains = self.user.has_domains_portfolio_permission()
user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission() user_can_view_all_requests = self.user.has_domain_requests_portfolio_permission()
user_can_edit_domains = self.user.has_edit_domains_portfolio_permission()
self.assertTrue(user_can_view_all_domains) self.assertTrue(user_can_view_all_domains)
self.assertTrue(user_can_view_all_requests) self.assertTrue(user_can_view_all_requests)
self.assertTrue(user_can_edit_domains)
Portfolio.objects.all().delete() Portfolio.objects.all().delete()

View file

@ -1,4 +1,4 @@
from registrar.models import UserDomainRole, Domain from registrar.models import UserDomainRole, Domain, DomainInformation, Portfolio
from django.urls import reverse from django.urls import reverse
from .test_views import TestWithUser from .test_views import TestWithUser
from django_webtest import WebTest # type: ignore from django_webtest import WebTest # type: ignore
@ -15,15 +15,25 @@ class GetDomainsJsonTest(TestWithUser, WebTest):
self.domain1 = Domain.objects.create(name="example1.com", expiration_date="2024-01-01", state="unknown") self.domain1 = Domain.objects.create(name="example1.com", expiration_date="2024-01-01", state="unknown")
self.domain2 = Domain.objects.create(name="example2.com", expiration_date="2024-02-01", state="dns needed") self.domain2 = Domain.objects.create(name="example2.com", expiration_date="2024-02-01", state="dns needed")
self.domain3 = Domain.objects.create(name="example3.com", expiration_date="2024-03-01", state="ready") self.domain3 = Domain.objects.create(name="example3.com", expiration_date="2024-03-01", state="ready")
self.domain4 = Domain.objects.create(name="example4.com", expiration_date="2024-03-01", state="ready")
# Create UserDomainRoles # Create UserDomainRoles
UserDomainRole.objects.create(user=self.user, domain=self.domain1) UserDomainRole.objects.create(user=self.user, domain=self.domain1)
UserDomainRole.objects.create(user=self.user, domain=self.domain2) UserDomainRole.objects.create(user=self.user, domain=self.domain2)
UserDomainRole.objects.create(user=self.user, domain=self.domain3) UserDomainRole.objects.create(user=self.user, domain=self.domain3)
# Create Portfolio
self.portfolio = Portfolio.objects.create(creator=self.user, organization_name="Example org")
# Add domain3 and domain4 to portfolio
DomainInformation.objects.create(creator=self.user, domain=self.domain3, portfolio=self.portfolio)
DomainInformation.objects.create(creator=self.user, domain=self.domain4, portfolio=self.portfolio)
def tearDown(self): def tearDown(self):
super().tearDown()
UserDomainRole.objects.all().delete() UserDomainRole.objects.all().delete()
DomainInformation.objects.all().delete()
Portfolio.objects.all().delete()
super().tearDown()
@less_console_noise_decorator @less_console_noise_decorator
def test_get_domains_json_unauthenticated(self): def test_get_domains_json_unauthenticated(self):
@ -104,6 +114,82 @@ class GetDomainsJsonTest(TestWithUser, WebTest):
) )
self.assertEqual(svg_icon_expected, svg_icons[i]) self.assertEqual(svg_icon_expected, svg_icons[i])
@less_console_noise_decorator
def test_get_domains_json_with_portfolio(self):
"""Test that an authenticated user gets the list of 2 domains for portfolio."""
response = self.app.get(reverse("get_domains_json"), {"portfolio": self.portfolio.id})
self.assertEqual(response.status_code, 200)
data = response.json
# Check pagination info
self.assertEqual(data["page"], 1)
self.assertFalse(data["has_next"])
self.assertFalse(data["has_previous"])
self.assertEqual(data["num_pages"], 1)
# Check the number of domains
self.assertEqual(len(data["domains"]), 2)
# Expected domains
expected_domains = [self.domain3, self.domain4]
# Extract fields from response
domain_ids = [domain["id"] for domain in data["domains"]]
names = [domain["name"] for domain in data["domains"]]
expiration_dates = [domain["expiration_date"] for domain in data["domains"]]
states = [domain["state"] for domain in data["domains"]]
state_displays = [domain["state_display"] for domain in data["domains"]]
get_state_help_texts = [domain["get_state_help_text"] for domain in data["domains"]]
action_urls = [domain["action_url"] for domain in data["domains"]]
action_labels = [domain["action_label"] for domain in data["domains"]]
svg_icons = [domain["svg_icon"] for domain in data["domains"]]
# Check fields for each domain
for i, expected_domain in enumerate(expected_domains):
self.assertEqual(expected_domain.id, domain_ids[i])
self.assertEqual(expected_domain.name, names[i])
self.assertEqual(expected_domain.expiration_date, expiration_dates[i])
self.assertEqual(expected_domain.state, states[i])
# Parsing the expiration date from string to date
parsed_expiration_date = parse_date(expiration_dates[i])
expected_domain.expiration_date = parsed_expiration_date
# Check state_display and get_state_help_text
self.assertEqual(expected_domain.state_display(), state_displays[i])
self.assertEqual(expected_domain.get_state_help_text(), get_state_help_texts[i])
self.assertEqual(reverse("domain", kwargs={"pk": expected_domain.id}), action_urls[i])
# Check action_label
user_domain_role_exists = UserDomainRole.objects.filter(
domain_id=expected_domains[i].id, user=self.user
).exists()
action_label_expected = (
"View"
if not user_domain_role_exists
or expected_domains[i].state
in [
Domain.State.DELETED,
Domain.State.ON_HOLD,
]
else "Manage"
)
self.assertEqual(action_label_expected, action_labels[i])
# Check svg_icon
svg_icon_expected = (
"visibility"
if expected_domains[i].state
in [
Domain.State.DELETED,
Domain.State.ON_HOLD,
]
else "settings"
)
self.assertEqual(svg_icon_expected, svg_icons[i])
@less_console_noise_decorator @less_console_noise_decorator
def test_get_domains_json_search(self): def test_get_domains_json_search(self):
"""Test search.""" """Test search."""

View file

@ -374,8 +374,9 @@ class DomainExport(BaseExport):
if first_ready_on is None: if first_ready_on is None:
first_ready_on = "(blank)" first_ready_on = "(blank)"
domain_org_type = model.get("generic_org_type") # organization_type has generic_org_type AND is_election
human_readable_domain_org_type = DomainRequest.OrganizationChoices.get_org_label(domain_org_type) domain_org_type = model.get("organization_type")
human_readable_domain_org_type = DomainRequest.OrgChoicesElectionOffice.get_org_label(domain_org_type)
domain_federal_type = model.get("federal_type") domain_federal_type = model.get("federal_type")
human_readable_domain_federal_type = BranchChoices.get_branch_label(domain_federal_type) human_readable_domain_federal_type = BranchChoices.get_branch_label(domain_federal_type)
domain_type = human_readable_domain_org_type domain_type = human_readable_domain_org_type

View file

@ -6,6 +6,8 @@ from django.contrib.auth.decorators import login_required
from django.urls import reverse from django.urls import reverse
from django.db.models import Q from django.db.models import Q
from registrar.models.domain_information import DomainInformation
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -14,10 +16,9 @@ def get_domains_json(request):
"""Given the current request, """Given the current request,
get all domains that are associated with the UserDomainRole object""" get all domains that are associated with the UserDomainRole object"""
user_domain_roles = UserDomainRole.objects.filter(user=request.user).select_related("domain_info__sub_organization") domain_ids = get_domain_ids_from_request(request)
domain_ids = user_domain_roles.values_list("domain_id", flat=True)
objects = Domain.objects.filter(id__in=domain_ids) objects = Domain.objects.filter(id__in=domain_ids).select_related("domain_info__sub_organization")
unfiltered_total = objects.count() unfiltered_total = objects.count()
objects = apply_search(objects, request) objects = apply_search(objects, request)
@ -28,7 +29,7 @@ def get_domains_json(request):
page_number = request.GET.get("page") page_number = request.GET.get("page")
page_obj = paginator.get_page(page_number) page_obj = paginator.get_page(page_number)
domains = [serialize_domain(domain) for domain in page_obj.object_list] domains = [serialize_domain(domain, request.user) for domain in page_obj.object_list]
return JsonResponse( return JsonResponse(
{ {
@ -43,6 +44,21 @@ def get_domains_json(request):
) )
def get_domain_ids_from_request(request):
"""Get domain ids from request.
If portfolio specified, return domain ids associated with portfolio.
Otherwise, return domain ids associated with request.user.
"""
portfolio = request.GET.get("portfolio")
if portfolio:
domain_infos = DomainInformation.objects.filter(portfolio=portfolio)
return domain_infos.values_list("domain_id", flat=True)
else:
user_domain_roles = UserDomainRole.objects.filter(user=request.user)
return user_domain_roles.values_list("domain_id", flat=True)
def apply_search(queryset, request): def apply_search(queryset, request):
search_term = request.GET.get("search_term") search_term = request.GET.get("search_term")
if search_term: if search_term:
@ -94,7 +110,7 @@ def apply_sorting(queryset, request):
return queryset.order_by(sort_by) return queryset.order_by(sort_by)
def serialize_domain(domain): def serialize_domain(domain, user):
suborganization_name = None suborganization_name = None
try: try:
domain_info = domain.domain_info domain_info = domain.domain_info
@ -106,6 +122,9 @@ def serialize_domain(domain):
domain_info = None domain_info = None
logger.debug(f"Issue in domains_json: We could not find domain_info for {domain}") logger.debug(f"Issue in domains_json: We could not find domain_info for {domain}")
# Check if there is a UserDomainRole for this domain and user
user_domain_role_exists = UserDomainRole.objects.filter(domain_id=domain.id, user=user).exists()
return { return {
"id": domain.id, "id": domain.id,
"name": domain.name, "name": domain.name,
@ -114,7 +133,11 @@ def serialize_domain(domain):
"state_display": domain.state_display(), "state_display": domain.state_display(),
"get_state_help_text": domain.get_state_help_text(), "get_state_help_text": domain.get_state_help_text(),
"action_url": reverse("domain", kwargs={"pk": domain.id}), "action_url": reverse("domain", kwargs={"pk": domain.id}),
"action_label": ("View" if domain.state in [Domain.State.DELETED, Domain.State.ON_HOLD] else "Manage"), "action_label": (
"View"
if not user_domain_role_exists or domain.state in [Domain.State.DELETED, Domain.State.ON_HOLD]
else "Manage"
),
"svg_icon": ("visibility" if domain.state in [Domain.State.DELETED, Domain.State.ON_HOLD] else "settings"), "svg_icon": ("visibility" if domain.state in [Domain.State.DELETED, Domain.State.ON_HOLD] else "settings"),
"suborganization": suborganization_name, "suborganization": suborganization_name,
} }