Merge remote-tracking branch 'origin/main' into nl/2136-CISA-rep-additional-details

This commit is contained in:
CocoByte 2024-06-03 11:38:00 -06:00
commit 21f6f0a508
No known key found for this signature in database
GPG key ID: BBFAA2526384C97F
15 changed files with 8159 additions and 367 deletions

View file

@ -291,13 +291,13 @@ We use the [CSS Block Element Modifier (BEM)](https://getbem.com/naming/) naming
### Upgrading USWDS and other JavaScript packages ### Upgrading USWDS and other JavaScript packages
Version numbers can be manually controlled in `package.json`. Edit that, if desired. 1. Version numbers can be manually controlled in `package.json`. Edit that, if desired.
2. Now run `docker-compose run node npm update`.
Now run `docker-compose run node npm update`. 3. Then run `docker-compose up` to recompile and recopy the assets, or run `docker-compose updateUswds` if your docker is already up.
4. Make note of the dotgov changes in uswds-edited.js.
Then run `docker-compose up` to recompile and recopy the assets. 5. Copy over the newly compiled code from uswds.js into uswds-edited.js.
6. Put back the dotgov changes you made note of into uswds-edited.js.
Examine the results in the running application (remember to empty your cache!) and commit `package.json` and `package-lock.json` if all is well. 7. Examine the results in the running application (remember to empty your cache!) and commit `package.json` and `package-lock.json` if all is well.
## Finite State Machines ## Finite State Machines

View file

@ -834,3 +834,417 @@ function hideDeletedForms() {
(function cisaRepresentativesFormListener() { (function cisaRepresentativesFormListener() {
HookupYesNoListener("additional_details-has_cisa_representative",'cisa-representative', null) HookupYesNoListener("additional_details-has_cisa_representative",'cisa-representative', null)
})(); })();
/**
* Initialize USWDS tooltips by calling initialization method. Requires that uswds-edited.js
* be loaded before get-gov.js. uswds-edited.js adds the tooltip module to the window to be
* accessible directly in get-gov.js
*
*/
function initializeTooltips() {
function checkTooltip() {
// Check that the tooltip library is loaded, and if not, wait and retry
if (window.tooltip && typeof window.tooltip.init === 'function') {
window.tooltip.init();
} else {
// Retry after a short delay
setTimeout(checkTooltip, 100);
}
}
checkTooltip();
}
/**
* Initialize USWDS modals by calling on method. Requires that uswds-edited.js be loaded
* before get-gov.js. uswds-edited.js adds the modal module to the window to be accessible
* directly in get-gov.js.
* initializeModals adds modal-related DOM elements, based on other DOM elements existing in
* the page. It needs to be called only once for any particular DOM element; otherwise, it
* will initialize improperly. Therefore, if DOM elements change dynamically and include
* DOM elements with modal classes, unloadModals needs to be called before initializeModals.
*
*/
function initializeModals() {
window.modal.on();
}
/**
* Unload existing USWDS modals by calling off method. Requires that uswds-edited.js be
* loaded before get-gov.js. uswds-edited.js adds the modal module to the window to be
* accessible directly in get-gov.js.
* See note above with regards to calling this method relative to initializeModals.
*
*/
function unloadModals() {
window.modal.off();
}
/**
* An IIFE that listens for DOM Content to be loaded, then executes. This function
* initializes the domains list and associated functionality on the home page of the app.
*
*/
document.addEventListener('DOMContentLoaded', function() {
let domainsWrapper = document.querySelector('.domains-wrapper');
if (domainsWrapper) {
let currentSortBy = 'id';
let currentOrder = 'asc';
let noDomainsWrapper = document.querySelector('.no-domains-wrapper');
/**
* Loads rows in the domains list, as well as updates pagination around the domains list
* based on the supplied attributes.
* @param {*} page - the page number of the results (starts with 1)
* @param {*} sortBy - the sort column option
* @param {*} order - the sort order {asc, desc}
*/
function loadDomains(page, sortBy = currentSortBy, order = currentOrder) {
//fetch json of page of domains, given page # and sort
fetch(`/get-domains-json/?page=${page}&sort_by=${sortBy}&order=${order}`)
.then(response => response.json())
.then(data => {
if (data.error) {
console.log('Error in AJAX call: ' + data.error);
return;
}
// handle the display of proper messaging in the event that no domains exist in the list
if (data.domains.length) {
domainsWrapper.classList.remove('display-none');
noDomainsWrapper.classList.add('display-none');
} else {
domainsWrapper.classList.add('display-none');
noDomainsWrapper.classList.remove('display-none');
}
// identify the DOM element where the domain list will be inserted into the DOM
const domainList = document.querySelector('.dotgov-table__registered-domains tbody');
domainList.innerHTML = '';
data.domains.forEach(domain => {
const expirationDate = domain.expiration_date ? new Date(domain.expiration_date) : null;
const expirationDateSortValue = expirationDate ? expirationDate.getTime() : '';
const actionUrl = domain.action_url;
const row = document.createElement('tr');
row.innerHTML = `
<th scope="row" role="rowheader" data-label="Domain name">
${domain.name}
</th>
<td data-sort-value="${expirationDateSortValue}" data-label="Expires">
${expirationDate ? expirationDate.toLocaleDateString() : ''}
</td>
<td data-label="Status">
${domain.state_display}
<svg
class="usa-icon usa-tooltip usa-tooltip--registrar text-middle margin-bottom-05 text-accent-cool no-click-outline-and-cursor-help"
data-position="top"
title="${domain.get_state_help_text}"
focusable="true"
aria-label="Status Information"
role="tooltip"
>
<use aria-hidden="true" xlink:href="/public/img/sprite.svg#info_outline"></use>
</svg>
</td>
<td>
<a href="${actionUrl}">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="/public/img/sprite.svg#${domain.svg_icon}"></use>
</svg>
${domain.action_label} <span class="usa-sr-only">${domain.name}</span>
</a>
</td>
`;
domainList.appendChild(row);
});
// initialize tool tips immediately after the associated DOM elements are added
initializeTooltips();
hasLoaded = true;
// update pagination
updateDomainsPagination(data.page, data.num_pages, data.has_previous, data.has_next, data.total);
currentSortBy = sortBy;
currentOrder = order;
})
.catch(error => console.error('Error fetching domains:', error));
}
/**
* Update the pagination below the domains list.
* @param {*} currentPage - the current page number (starting with 1)
* @param {*} numPages - the number of pages indicated by the domains list response
* @param {*} hasPrevious - if there is a page of results prior to the current page
* @param {*} hasNext - if there is a page of results after the current page
*/
function updateDomainsPagination(currentPage, numPages, hasPrevious, hasNext, totalItems) {
// identify the DOM element where the pagination will be inserted
const paginationContainer = document.querySelector('#domains-pagination');
const paginationCounter = document.querySelector('#domains-pagination .usa-pagination__counter');
const paginationButtons = document.querySelector('#domains-pagination .usa-pagination__list');
paginationCounter.innerHTML = '';
paginationButtons.innerHTML = '';
// Buttons should only be displayed if there are more than one pages of results
paginationButtons.classList.toggle('display-none', numPages <= 1);
// Counter should only be displayed if there is more than 1 item
paginationContainer.classList.toggle('display-none', totalItems < 1);
paginationCounter.innerHTML = `${totalItems} domain${totalItems > 1 ? 's' : ''}`;
if (hasPrevious) {
const prevPageItem = document.createElement('li');
prevPageItem.className = 'usa-pagination__item usa-pagination__arrow';
prevPageItem.innerHTML = `
<a href="#domains-header" class="usa-pagination__link usa-pagination__previous-page" aria-label="Domains previous page">
<svg class="usa-icon" aria-hidden="true" role="img">
<use xlink:href="/public/img/sprite.svg#navigate_before"></use>
</svg>
<span class="usa-pagination__link-text">Previous</span>
</a>
`;
prevPageItem.querySelector('a').addEventListener('click', () => loadDomains(currentPage - 1));
paginationButtons.appendChild(prevPageItem);
}
for (let i = 1; i <= numPages; i++) {
const pageItem = document.createElement('li');
pageItem.className = 'usa-pagination__item usa-pagination__page-no';
pageItem.innerHTML = `
<a href="#domains-header" class="usa-pagination__button" aria-label="Domains page ${i}">${i}</a>
`;
if (i === currentPage) {
pageItem.querySelector('a').classList.add('usa-current');
pageItem.querySelector('a').setAttribute('aria-current', 'page');
}
pageItem.querySelector('a').addEventListener('click', () => loadDomains(i));
paginationButtons.appendChild(pageItem);
}
if (hasNext) {
const nextPageItem = document.createElement('li');
nextPageItem.className = 'usa-pagination__item usa-pagination__arrow';
nextPageItem.innerHTML = `
<a href="#domains-header" class="usa-pagination__link usa-pagination__next-page" aria-label="Domains next page">
<span class="usa-pagination__link-text">Next</span>
<svg class="usa-icon" aria-hidden="true" role="img">
<use xlink:href="/public/img/sprite.svg#navigate_next"></use>
</svg>
</a>
`;
nextPageItem.querySelector('a').addEventListener('click', () => loadDomains(currentPage + 1));
paginationButtons.appendChild(nextPageItem);
}
}
// Add event listeners to table headers for sorting
document.querySelectorAll('.dotgov-table__registered-domains th[data-sortable]').forEach(header => {
header.addEventListener('click', function() {
const sortBy = this.getAttribute('data-sortable');
let order = 'asc';
// sort order will be ascending, unless the currently sorted column is ascending, and the user
// is selecting the same column to sort in descending order
if (sortBy === currentSortBy) {
order = currentOrder === 'asc' ? 'desc' : 'asc';
}
// load the results with the updated sort
loadDomains(1, sortBy, order);
});
});
// Load the first page initially
loadDomains(1);
}
});
/**
* An IIFE that listens for DOM Content to be loaded, then executes. This function
* initializes the domain requests list and associated functionality on the home page of the app.
*
*/
document.addEventListener('DOMContentLoaded', function() {
let domainRequestsWrapper = document.querySelector('.domain-requests-wrapper');
if (domainRequestsWrapper) {
let currentSortBy = 'id';
let currentOrder = 'asc';
let noDomainRequestsWrapper = document.querySelector('.no-domain-requests-wrapper');
/**
* Loads rows in the domain requests list, as well as updates pagination around the domain requests list
* based on the supplied attributes.
* @param {*} page - the page number of the results (starts with 1)
* @param {*} sortBy - the sort column option
* @param {*} order - the sort order {asc, desc}
*/
function loadDomainRequests(page, sortBy = currentSortBy, order = currentOrder) {
//fetch json of page of domain requests, given page # and sort
fetch(`/get-domain-requests-json/?page=${page}&sort_by=${sortBy}&order=${order}`)
.then(response => response.json())
.then(data => {
if (data.error) {
console.log('Error in AJAX call: ' + data.error);
return;
}
// handle the display of proper messaging in the event that no domain requests exist in the list
if (data.domain_requests.length) {
domainRequestsWrapper.classList.remove('display-none');
noDomainRequestsWrapper.classList.add('display-none');
} else {
domainRequestsWrapper.classList.add('display-none');
noDomainRequestsWrapper.classList.remove('display-none');
}
// identify the DOM element where the domain request list will be inserted into the DOM
const tbody = document.querySelector('.dotgov-table__domain-requests tbody');
tbody.innerHTML = '';
// remove any existing modal elements from the DOM so they can be properly re-initialized
// after the DOM content changes and there are new delete modal buttons added
unloadModals();
data.domain_requests.forEach(request => {
const domainName = request.requested_domain ? request.requested_domain : `New domain request <span class="text-base font-body-xs">(${new Date(request.created_at).toLocaleString()} UTC)</span>`;
const actionUrl = request.action_url;
const actionLabel = request.action_label;
const options = { year: 'numeric', month: 'short', day: 'numeric' };
const submissionDate = request.submission_date ? new Date(request.submission_date).toLocaleDateString('en-US', options) : `<span class="text-base">Not submitted</span>`;
const deleteButton = request.is_deletable ? `
<a
role="button"
id="button-toggle-delete-domain-alert-${request.id}"
href="#toggle-delete-domain-alert-${request.id}"
class="usa-button--unstyled text-no-underline late-loading-modal-trigger"
aria-controls="toggle-delete-domain-alert-${request.id}"
data-open-modal
>
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="/public/img/sprite.svg#delete"></use>
</svg> Delete <span class="usa-sr-only">${domainName}</span>
</a>` : '';
const row = document.createElement('tr');
row.innerHTML = `
<th scope="row" role="rowheader" data-label="Domain name">
${domainName}
</th>
<td data-sort-value="${new Date(request.submission_date).getTime()}" data-label="Date submitted">
${submissionDate}
</td>
<td data-label="Status">
${request.status}
</td>
<td>
<a href="${actionUrl}">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="/public/img/sprite.svg#${request.svg_icon}"></use>
</svg>
${actionLabel} <span class="usa-sr-only">${request.requested_domain ? request.requested_domain : 'New domain request'}</span>
</a>
</td>
<td>${deleteButton}</td>
`;
tbody.appendChild(row);
});
// initialize modals immediately after the DOM content is updated
initializeModals();
hasLoaded = true;
// update the pagination after the domain requests list is updated
updateDomainRequestsPagination(data.page, data.num_pages, data.has_previous, data.has_next, data.total);
currentSortBy = sortBy;
currentOrder = order;
})
.catch(error => console.error('Error fetching domain requests:', error));
}
/**
* Update the pagination below the domain requests list.
* @param {*} currentPage - the current page number (starting with 1)
* @param {*} numPages - the number of pages indicated by the domain request list response
* @param {*} hasPrevious - if there is a page of results prior to the current page
* @param {*} hasNext - if there is a page of results after the current page
*/
function updateDomainRequestsPagination(currentPage, numPages, hasPrevious, hasNext, totalItems) {
// identify the DOM element where pagination is contained
const paginationContainer = document.querySelector('#domain-requests-pagination');
const paginationCounter = document.querySelector('#domain-requests-pagination .usa-pagination__counter');
const paginationButtons = document.querySelector('#domain-requests-pagination .usa-pagination__list');
paginationCounter.innerHTML = '';
paginationButtons.innerHTML = '';
// Buttons should only be displayed if there are more than one pages of results
paginationButtons.classList.toggle('display-none', numPages <= 1);
// Counter should only be displayed if there is more than 1 item
paginationContainer.classList.toggle('display-none', totalItems < 1);
paginationCounter.innerHTML = `${totalItems} domain request${totalItems > 1 ? 's' : ''}`;
if (hasPrevious) {
const prevPageItem = document.createElement('li');
prevPageItem.className = 'usa-pagination__item usa-pagination__arrow';
prevPageItem.innerHTML = `
<a href="#domain-requests-header" class="usa-pagination__link usa-pagination__previous-page" aria-label="Domain requests previous page">
<svg class="usa-icon" aria-hidden="true" role="img">
<use xlink:href="/public/img/sprite.svg#navigate_before"></use>
</svg>
<span class="usa-pagination__link-text">Previous</span>
</a>
`;
prevPageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(currentPage - 1));
paginationButtons.appendChild(prevPageItem);
}
for (let i = 1; i <= numPages; i++) {
const pageItem = document.createElement('li');
pageItem.className = 'usa-pagination__item usa-pagination__page-no';
pageItem.innerHTML = `
<a href="#domain-requests-header" class="usa-pagination__button" aria-label="Domain requests page ${i}">${i}</a>
`;
if (i === currentPage) {
pageItem.querySelector('a').classList.add('usa-current');
pageItem.querySelector('a').setAttribute('aria-current', 'page');
}
pageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(i));
paginationButtons.appendChild(pageItem);
}
if (hasNext) {
const nextPageItem = document.createElement('li');
nextPageItem.className = 'usa-pagination__item usa-pagination__arrow';
nextPageItem.innerHTML = `
<a href="#domain-requests-header" class="usa-pagination__link usa-pagination__next-page" aria-label="Domain requests next page">
<span class="usa-pagination__link-text">Next</span>
<svg class="usa-icon" aria-hidden="true" role="img">
<use xlink:href="/public/img/sprite.svg#navigate_next"></use>
</svg>
</a>
`;
nextPageItem.querySelector('a').addEventListener('click', () => loadDomainRequests(currentPage + 1));
paginationButtons.appendChild(nextPageItem);
}
}
// Add event listeners to table headers for sorting
document.querySelectorAll('.dotgov-table__domain-requests th[data-sortable]').forEach(header => {
header.addEventListener('click', function() {
const sortBy = this.getAttribute('data-sortable');
let order = 'asc';
// sort order will be ascending, unless the currently sorted column is ascending, and the user
// is selecting the same column to sort in descending order
if (sortBy === currentSortBy) {
order = currentOrder === 'asc' ? 'desc' : 'asc';
}
loadDomainRequests(1, sortBy, order);
});
});
// Load the first page initially
loadDomainRequests(1);
}
});

File diff suppressed because one or more lines are too long

View file

@ -21,6 +21,8 @@ from registrar.views.admin_views import (
) )
from registrar.views.domain_request import Step from registrar.views.domain_request import Step
from registrar.views.domain_requests_json import get_domain_requests_json
from registrar.views.domains_json import get_domains_json
from registrar.views.utility import always_404 from registrar.views.utility import always_404
from api.views import available, get_current_federal, get_current_full from api.views import available, get_current_federal, get_current_full
@ -198,6 +200,8 @@ urlpatterns = [
views.DomainDeleteUserView.as_view(http_method_names=["post"]), views.DomainDeleteUserView.as_view(http_method_names=["post"]),
name="domain-user-delete", name="domain-user-delete",
), ),
path("get-domains-json/", get_domains_json, name="get_domains_json"),
path("get-domain-requests-json/", get_domain_requests_json, name="get_domain_requests_json"),
] ]
# Djangooidc strips out context data from that context, so we define a custom error # Djangooidc strips out context data from that context, so we define a custom error

View file

@ -1062,6 +1062,15 @@ class Domain(TimeStampedModel, DomainHelper):
now = timezone.now().date() now = timezone.now().date()
return self.expiration_date < now return self.expiration_date < now
def state_display(self):
"""Return the display status of the domain."""
if self.is_expired() and self.state != self.State.UNKNOWN:
return "Expired"
elif self.state == self.State.UNKNOWN or self.state == self.State.DNS_NEEDED:
return "DNS needed"
else:
return self.state.capitalize()
def map_epp_contact_to_public_contact(self, contact: eppInfo.InfoContactResultData, contact_id, contact_type): def map_epp_contact_to_public_contact(self, contact: eppInfo.InfoContactResultData, contact_id, contact_type):
"""Maps the Epp contact representation to a PublicContact object. """Maps the Epp contact representation to a PublicContact object.

View file

@ -45,6 +45,8 @@
{% block css %} {% block css %}
<link rel="stylesheet" href="{% static 'css/styles.css' %}"> <link rel="stylesheet" href="{% static 'css/styles.css' %}">
<script src="{% static 'js/uswds-init.min.js' %}" defer></script> <script src="{% static 'js/uswds-init.min.js' %}" defer></script>
<!-- We override with our own copy to make some classes accessible in our JS -->
<script src="{% static 'js/uswds-edited.js' %}" defer></script>
<script src="{% static 'js/get-gov.js' %}" defer></script> <script src="{% static 'js/get-gov.js' %}" defer></script>
{% endblock %} {% endblock %}
@ -67,7 +69,6 @@
</head> </head>
<body id="{% block body_id %}default{% endblock %}" class="{% block body_class %}section_front{% endblock %}"> <body id="{% block body_id %}default{% endblock %}" class="{% block body_class %}section_front{% endblock %}">
<script src="{% static 'js/uswds.min.js' %}" defer></script>
<a class="usa-skipnav" href="#main-content">Skip to main content</a> <a class="usa-skipnav" href="#main-content">Skip to main content</a>
{% if not IS_PRODUCTION %} {% if not IS_PRODUCTION %}

View file

@ -24,224 +24,123 @@
</p> </p>
<section class="section--outlined"> <section class="section--outlined">
<h2>Domains</h2> <h2 id="domains-header">Domains</h2>
{% if domains %} <div class="domains-wrapper display-none">
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked dotgov-table__registered-domains"> <table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked dotgov-table__registered-domains">
<caption class="sr-only">Your registered domains</caption> <caption class="sr-only">Your registered domains</caption>
<thead> <thead>
<tr> <tr>
<th data-sortable scope="col" role="columnheader">Domain name</th> <th data-sortable="name" scope="col" role="columnheader">Domain name</th>
<th data-sortable scope="col" role="columnheader">Expires</th> <th data-sortable="expiration_date" scope="col" role="columnheader">Expires</th>
<th data-sortable scope="col" role="columnheader">Status</th> <th data-sortable="state_display" scope="col" role="columnheader">Status</th>
<th <th
scope="col" scope="col"
role="columnheader" role="columnheader"
>
<span class="usa-sr-only">Action</span>
</th>
</tr>
</thead>
<tbody>
{% for domain in domains %}
<tr>
<th th scope="row" role="rowheader" data-label="Domain name">
{{ domain.name }}
</th>
<td data-sort-value="{{ domain.expiration_date|date:"U" }}" data-label="Expires">{{ domain.expiration_date|date }}</td>
<td data-label="Status">
{# UNKNOWN domains would not have an expiration date and thus would show 'Expired' #}
{% if domain.is_expired and domain.state != domain.State.UNKNOWN %}
Expired
{% elif domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED %}
DNS needed
{% else %}
{{ domain.state|capfirst }}
{% endif %}
<svg
class="usa-icon usa-tooltip usa-tooltip--registrar text-middle margin-bottom-05 text-accent-cool no-click-outline-and-cursor-help"
data-position="top"
title="{{domain.get_state_help_text}}"
focusable="true"
aria-label="Status Information"
role="tooltip"
> >
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#info_outline"></use> <span class="usa-sr-only">Action</span>
</svg> </th>
</td> </tr>
<td> </thead>
<a href="{% url "domain" pk=domain.pk %}"> <tbody>
<svg <!-- AJAX will populate this tbody -->
class="usa-icon" </tbody>
aria-hidden="true" </table>
focusable="false" <div
role="img" class="usa-sr-only usa-table__announcement-region"
width="24" aria-live="polite"
> ></div>
{% if domain.state == "deleted" or domain.state == "on hold" %} </div>
<use xlink:href="{%static 'img/sprite.svg'%}#visibility"></use> <div class="no-domains-wrapper display-none">
</svg> <p>You don't have any registered domains.</p>
View <span class="usa-sr-only">{{ domain.name }}</span> <p class="maxw-none clearfix">
{% else %} <a href="https://get.gov/help/faq/#do-not-see-my-domain" class="float-right-tablet display-flex flex-align-start usa-link" target="_blank">
<use xlink:href="{%static 'img/sprite.svg'%}#settings"></use> <svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
</svg> <use xlink:href="{%static 'img/sprite.svg'%}#help_outline"></use>
Manage <span class="usa-sr-only">{{ domain.name }}</span> </svg>
{% endif %} Why don't I see my domain when I sign in to the registrar?
</a> </a>
</td> </p>
</tr> </div>
{% endfor %}
</tbody>
</table>
<div
class="usa-sr-only usa-table__announcement-region"
aria-live="polite"
></div>
{% else %}
<p>You don't have any registered domains.</p>
<p class="maxw-none clearfix">
<a href="https://get.gov/help/faq/#do-not-see-my-domain" class="float-right-tablet display-flex flex-align-start usa-link" target="_blank">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="{%static 'img/sprite.svg'%}#help_outline"></use>
</svg>
Why don't I see my domain when I sign in to the registrar?
</a>
</p>
{% endif %}
</section> </section>
<nav aria-label="Pagination" class="usa-pagination flex-justify" id="domains-pagination">
<span class="usa-pagination__counter text-base-dark padding-left-2">
<!-- Count will be dynamically populated by JS -->
</span>
<ul class="usa-pagination__list">
<!-- Pagination links will be dynamically populated by JS -->
</ul>
</nav>
<section class="section--outlined"> <section class="section--outlined">
<h2>Domain requests</h2> <h2 id="domain-requests-header">Domain requests</h2>
{% if domain_requests %} <div class="domain-requests-wrapper display-none">
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked dotgov-table__domain-requests"> <table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked dotgov-table__domain-requests">
<caption class="sr-only">Your domain requests</caption> <caption class="sr-only">Your domain requests</caption>
<thead> <thead>
<tr> <tr>
<th data-sortable scope="col" role="columnheader">Domain name</th> <th data-sortable="requested_domain__name" scope="col" role="columnheader">Domain name</th>
<th data-sortable scope="col" role="columnheader">Date submitted</th> <th data-sortable="submission_date" scope="col" role="columnheader">Date submitted</th>
<th data-sortable scope="col" role="columnheader">Status</th> <th data-sortable="status" scope="col" role="columnheader">Status</th>
<th scope="col" role="columnheader"><span class="usa-sr-only">Action</span></th> <th scope="col" role="columnheader"><span class="usa-sr-only">Action</span></th>
{% if has_deletable_domain_requests %} <th scope="col" role="columnheader"><span class="usa-sr-only">Delete Action</span></th>
<th scope="col" role="columnheader"><span class="usa-sr-only">Delete Action</span></th> </tr>
{% endif %} </thead>
</tr> <tbody id="domain-requests-tbody">
</thead> <!-- AJAX will populate this tbody -->
<tbody> </tbody>
{% for domain_request in domain_requests %} </table>
<tr> <div
<th th scope="row" role="rowheader" data-label="Domain name"> class="usa-sr-only usa-table__announcement-region"
aria-live="polite"
></div>
{% for domain_request in domain_requests %}
{% if has_deletable_domain_requests %}
{% if domain_request.status == domain_request.DomainRequestStatus.STARTED or domain_request.status == domain_request.DomainRequestStatus.WITHDRAWN %}
<div
class="usa-modal"
id="toggle-delete-domain-alert-{{ domain_request.id }}"
aria-labelledby="Are you sure you want to continue?"
aria-describedby="Domain will be removed"
data-force-action
>
<form method="POST" action="{% url "domain-request-delete" pk=domain_request.id %}">
{% if domain_request.requested_domain is None %} {% if domain_request.requested_domain is None %}
New domain request {% if domain_request.created_at %}
{# Add a breakpoint #} {% with prefix="(created " %}
<div aria-hidden="true"></div> {% with formatted_date=domain_request.created_at|date:"DATETIME_FORMAT" %}
<span class="text-base font-body-xs">({{ domain_request.created_at }} UTC)</span> {% with modal_content=prefix|add:formatted_date|add:" UTC)" %}
{% else %} {% include 'includes/modal.html' with modal_heading="Are you sure you want to delete this domain request?" modal_description="This will remove the domain request "|add:modal_content|add:" from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
{{ domain_request.requested_domain.name }} {% endwith %}
{% endif %}
</th>
<td data-sort-value="{{ domain_request.submission_date|date:"U" }}" data-label="Date submitted">
{% if domain_request.submission_date %}
{{ domain_request.submission_date|date }}
{% else %}
<span class="text-base">Not submitted</span>
{% endif %}
</td>
<td data-label="Status">{{ domain_request.get_status_display }}</td>
<td>
{% with prefix="New domain request ("%}
{% with date=domain_request.created_at|date:"DATETIME_FORMAT"%}
{% with name_default=prefix|add:date|add:" UTC)"%}
{% if domain_request.status == domain_request.DomainRequestStatus.STARTED or domain_request.status == domain_request.DomainRequestStatus.ACTION_NEEDED or domain_request.status == domain_request.DomainRequestStatus.WITHDRAWN %}
<a href="{% url 'edit-domain-request' domain_request.pk %}">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="{%static 'img/sprite.svg'%}#edit"></use>
</svg>
{% if domain_request.requested_domain is not None%}
Edit <span class="usa-sr-only">{{ domain_request.requested_domain.name }}</span>
{% else %}
Edit <span class="usa-sr-only">{{ name_default }}</span>
{% endif %}
{% else %}
<a href="{% url 'domain-request-status' domain_request.pk %}">
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="{%static 'img/sprite.svg'%}#settings"></use>
</svg>
Manage <span class="usa-sr-only">{{ domain_request.requested_domain.name|default:name_default }}</span>
{% endif %}
{% endwith %}
{% endwith %}
{% endwith %}
</a>
</td>
{% if has_deletable_domain_requests %}
<td>
{% if domain_request.status == "started" or domain_request.status == "withdrawn" %}
<a
role="button"
id="button-toggle-delete-domain-alert-{{ forloop.counter }}"
href="#toggle-delete-domain-alert-{{ forloop.counter }}"
class="usa-button--unstyled text-no-underline"
aria-controls="toggle-delete-domain-alert-{{ forloop.counter }}"
data-open-modal
>
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img" width="24">
<use xlink:href="{%static 'img/sprite.svg'%}#delete"></use>
</svg>
{% with prefix="New domain request ("%}
{% with date=domain_request.created_at|date:"DATETIME_FORMAT"%}
{% with name_default=prefix|add:date|add:" UTC)"%}
{% if domain_request.requested_domain is not None %}
Delete <span class="usa-sr-only">{{ domain_request.requested_domain.name }}</span>
{% else %}
Delete <span class="usa-sr-only">{{ name_default }}</span>
{% endif %}
{% endwith %} {% endwith %}
{% endwith %} {% endwith %}
{% else %}
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete New domain request?" modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
{% endif %}
{% else %}
{% with modal_heading_value=domain_request.requested_domain.name|add:"?" %}
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete" heading_value=modal_heading_value modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
{% endwith %} {% endwith %}
</a> {% endif %}
</form>
<div </div>
class="usa-modal"
id="toggle-delete-domain-alert-{{ forloop.counter }}"
aria-labelledby="Are you sure you want to continue?"
aria-describedby="Domain will be removed"
data-force-action
>
<form method="POST" action="{% url "domain-request-delete" pk=domain_request.id %}">
{% if domain_request.requested_domain is None %}
{% if domain_request.created_at %}
{% with prefix="(created " %}
{% with formatted_date=domain_request.created_at|date:"DATETIME_FORMAT" %}
{% with modal_content=prefix|add:formatted_date|add:" UTC)" %}
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete this domain request?" modal_description="This will remove the domain request "|add:modal_content|add:" from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
{% endwith %}
{% endwith %}
{% endwith %}
{% else %}
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete New domain request?" modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
{% endif %}
{% else %}
{% with modal_heading_value=domain_request.requested_domain.name|add:"?" %}
{% include 'includes/modal.html' with modal_heading="Are you sure you want to delete" heading_value=modal_heading_value modal_description="This will remove the domain request from the .gov registrar. This action cannot be undone." modal_button=modal_button|safe %}
{% endwith %}
{% endif %}
</form>
</div>
{% endif %}
</td>
{% endif %} {% endif %}
</tr> {% endif %}
{% endfor %} {% endfor %}
</tbody>
</table> </div>
<div <div class="no-domain-requests-wrapper display-none">
class="usa-sr-only usa-table__announcement-region" <p>You haven't requested any domains.</p>
aria-live="polite" </div>
></div>
{% else %}
<p>You haven't requested any domains.</p>
<!-- <p><a href="{% url 'domain-request:' %}" class="usa-button">Start a new domain request</a></p> -->
{% endif %}
</section> </section>
<nav aria-label="Pagination" class="usa-pagination flex-justify" id="domain-requests-pagination">
<span class="usa-pagination__counter text-base-dark padding-left-2">
<!-- Count will be dynamically populated by JS -->
</span>
<ul class="usa-pagination__list">
<!-- Pagination links will be dynamically populated by JS -->
</ul>
</nav>
{# Note: Reimplement this after MVP #} {# Note: Reimplement this after MVP #}
<!-- <!--

View file

@ -161,31 +161,14 @@ class HomeTests(TestWithUser):
self.assertContains(response, "You don't have any registered domains.") self.assertContains(response, "You don't have any registered domains.")
self.assertContains(response, "Why don't I see my domain when I sign in to the registrar?") self.assertContains(response, "Why don't I see my domain when I sign in to the registrar?")
def test_home_lists_domain_requests(self):
response = self.client.get("/")
self.assertNotContains(response, "igorville.gov")
site = DraftDomain.objects.create(name="igorville.gov")
domain_request = DomainRequest.objects.create(creator=self.user, requested_domain=site)
response = self.client.get("/")
# count = 7 because of screenreader content
self.assertContains(response, "igorville.gov", count=7)
# clean up
domain_request.delete()
def test_state_help_text(self): def test_state_help_text(self):
"""Tests if each domain state has help text""" """Tests if each domain state has help text"""
# Get the expected text content of each state # Get the expected text content of each state
deleted_text = "This domain has been removed and " "is no longer registered to your organization." deleted_text = "This domain has been removed and " "is no longer registered to your organization."
dns_needed_text = "Before this domain can be used, " "youll need to add name server addresses." dns_needed_text = "Before this domain can be used, "
ready_text = "This domain has name servers and is ready for use." ready_text = "This domain has name servers and is ready for use."
on_hold_text = ( on_hold_text = "This domain is administratively paused, "
"This domain is administratively paused, "
"so it cant be edited and wont resolve in DNS. "
"Contact help@get.gov for details."
)
deleted_text = "This domain has been removed and " "is no longer registered to your organization." deleted_text = "This domain has been removed and " "is no longer registered to your organization."
# Generate a mapping of domain names, the state, and expected messages for the subtest # Generate a mapping of domain names, the state, and expected messages for the subtest
test_cases = [ test_cases = [
@ -206,12 +189,11 @@ class HomeTests(TestWithUser):
user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER
) )
# Grab the home page # Grab the json response for domain list
response = self.client.get("/") response = self.client.get("/get-domains-json/")
# Make sure the user can actually see the domain. # Make sure the domain is in the list.
# We expect two instances because of SR content. self.assertContains(response, domain_name, count=1)
self.assertContains(response, domain_name, count=2)
# Check that we have the right text content. # Check that we have the right text content.
self.assertContains(response, expected_message, count=1) self.assertContains(response, expected_message, count=1)
@ -222,19 +204,18 @@ class HomeTests(TestWithUser):
def test_state_help_text_expired(self): def test_state_help_text_expired(self):
"""Tests if each domain state has help text when expired""" """Tests if each domain state has help text when expired"""
expired_text = "This domain has expired, but it is still online. " "To renew this domain, contact help@get.gov." expired_text = "This domain has expired, but it is still online. "
test_domain, _ = Domain.objects.get_or_create(name="expired.gov", state=Domain.State.READY) test_domain, _ = Domain.objects.get_or_create(name="expired.gov", state=Domain.State.READY)
test_domain.expiration_date = date(2011, 10, 10) test_domain.expiration_date = date(2011, 10, 10)
test_domain.save() test_domain.save()
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER) UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER)
# Grab the home page # Grab the json response of the domains list
response = self.client.get("/") response = self.client.get("/get-domains-json/")
# Make sure the user can actually see the domain. # Make sure the domain is in the response
# We expect two instances because of SR content. self.assertContains(response, "expired.gov", count=1)
self.assertContains(response, "expired.gov", count=2)
# Check that we have the right text content. # Check that we have the right text content.
self.assertContains(response, expired_text, count=1) self.assertContains(response, expired_text, count=1)
@ -243,19 +224,18 @@ class HomeTests(TestWithUser):
"""Tests if each domain state has help text when expiration date is None""" """Tests if each domain state has help text when expiration date is None"""
# == Test a expiration of None for state ready. This should be expired. == # # == Test a expiration of None for state ready. This should be expired. == #
expired_text = "This domain has expired, but it is still online. " "To renew this domain, contact help@get.gov." expired_text = "This domain has expired, but it is still online. "
test_domain, _ = Domain.objects.get_or_create(name="imexpired.gov", state=Domain.State.READY) test_domain, _ = Domain.objects.get_or_create(name="imexpired.gov", state=Domain.State.READY)
test_domain.expiration_date = None test_domain.expiration_date = None
test_domain.save() test_domain.save()
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER) UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain, role=UserDomainRole.Roles.MANAGER)
# Grab the home page # Grab the json response of the domains list
response = self.client.get("/") response = self.client.get("/get-domains-json/")
# Make sure the user can actually see the domain. # Make sure domain is in the response
# We expect two instances because of SR content. self.assertContains(response, "imexpired.gov", count=1)
self.assertContains(response, "imexpired.gov", count=2)
# Make sure the expiration date is None # Make sure the expiration date is None
self.assertEqual(test_domain.expiration_date, None) self.assertEqual(test_domain.expiration_date, None)
@ -264,19 +244,18 @@ class HomeTests(TestWithUser):
self.assertContains(response, expired_text, count=1) self.assertContains(response, expired_text, count=1)
# == Test a expiration of None for state unknown. This should not display expired text. == # # == Test a expiration of None for state unknown. This should not display expired text. == #
unknown_text = "Before this domain can be used, " "youll need to add name server addresses." unknown_text = "Before this domain can be used, "
test_domain_2, _ = Domain.objects.get_or_create(name="notexpired.gov", state=Domain.State.UNKNOWN) test_domain_2, _ = Domain.objects.get_or_create(name="notexpired.gov", state=Domain.State.UNKNOWN)
test_domain_2.expiration_date = None test_domain_2.expiration_date = None
test_domain_2.save() test_domain_2.save()
UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain_2, role=UserDomainRole.Roles.MANAGER) UserDomainRole.objects.get_or_create(user=self.user, domain=test_domain_2, role=UserDomainRole.Roles.MANAGER)
# Grab the home page # Grab the json response of the domains list
response = self.client.get("/") response = self.client.get("/get-domains-json/")
# Make sure the user can actually see the domain. # Make sure the response contains the domain
# We expect two instances because of SR content. self.assertContains(response, "notexpired.gov", count=1)
self.assertContains(response, "notexpired.gov", count=2)
# Make sure the expiration date is None # Make sure the expiration date is None
self.assertEqual(test_domain_2.expiration_date, None) self.assertEqual(test_domain_2.expiration_date, None)
@ -292,14 +271,6 @@ class HomeTests(TestWithUser):
creator=self.user, requested_domain=site, status=DomainRequest.DomainRequestStatus.WITHDRAWN creator=self.user, requested_domain=site, status=DomainRequest.DomainRequestStatus.WITHDRAWN
) )
# Ensure that igorville.gov exists on the page
home_page = self.client.get("/")
self.assertContains(home_page, "igorville.gov")
# Check if the delete button exists. We can do this by checking for its id and text content.
self.assertContains(home_page, "Delete")
self.assertContains(home_page, "button-toggle-delete-domain-alert-1")
# Trigger the delete logic # Trigger the delete logic
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True) response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)
@ -316,14 +287,6 @@ class HomeTests(TestWithUser):
creator=self.user, requested_domain=site, status=DomainRequest.DomainRequestStatus.STARTED creator=self.user, requested_domain=site, status=DomainRequest.DomainRequestStatus.STARTED
) )
# Ensure that igorville.gov exists on the page
home_page = self.client.get("/")
self.assertContains(home_page, "igorville.gov")
# Check if the delete button exists. We can do this by checking for its id and text content.
self.assertContains(home_page, "Delete")
self.assertContains(home_page, "button-toggle-delete-domain-alert-1")
# Trigger the delete logic # Trigger the delete logic
response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True) response = self.client.post(reverse("domain-request-delete", kwargs={"pk": domain_request.pk}), follow=True)

View file

@ -222,27 +222,6 @@ class TestDomainDetail(TestDomainOverview):
self.assertContains(detail_page, "igorville.gov") self.assertContains(detail_page, "igorville.gov")
self.assertContains(detail_page, "Status") self.assertContains(detail_page, "Status")
def test_unknown_domain_does_not_show_as_expired_on_homepage(self):
"""An UNKNOWN domain does not show as expired on the homepage.
It shows as 'DNS needed'"""
# At the time of this test's writing, there are 6 UNKNOWN domains inherited
# from constructors. Let's reset.
with less_console_noise():
Domain.objects.all().delete()
UserDomainRole.objects.all().delete()
self.domain, _ = Domain.objects.get_or_create(name="igorville.gov")
home_page = self.app.get("/")
self.assertNotContains(home_page, "igorville.gov")
self.role, _ = UserDomainRole.objects.get_or_create(
user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER
)
home_page = self.app.get("/")
self.assertContains(home_page, "igorville.gov")
igorville = Domain.objects.get(name="igorville.gov")
self.assertEquals(igorville.state, Domain.State.UNKNOWN)
self.assertNotContains(home_page, "Expired")
self.assertContains(home_page, "DNS needed")
def test_unknown_domain_does_not_show_as_expired_on_detail_page(self): def test_unknown_domain_does_not_show_as_expired_on_detail_page(self):
"""An UNKNOWN domain should not exist on the detail_page anymore. """An UNKNOWN domain should not exist on the detail_page anymore.
It shows as 'DNS needed'""" It shows as 'DNS needed'"""
@ -258,11 +237,9 @@ class TestDomainDetail(TestDomainOverview):
user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER
) )
home_page = self.app.get("/")
self.assertContains(home_page, "igorville.gov")
igorville = Domain.objects.get(name="igorville.gov") igorville = Domain.objects.get(name="igorville.gov")
self.assertEquals(igorville.state, Domain.State.UNKNOWN) self.assertEquals(igorville.state, Domain.State.UNKNOWN)
detail_page = home_page.click("Manage", index=0) detail_page = self.app.get(f"/domain/{igorville.id}")
self.assertContains(detail_page, "Expired") self.assertContains(detail_page, "Expired")
self.assertNotContains(detail_page, "DNS needed") self.assertNotContains(detail_page, "DNS needed")
@ -274,26 +251,18 @@ class TestDomainDetail(TestDomainOverview):
with less_console_noise(): with less_console_noise():
self.user.status = User.RESTRICTED self.user.status = User.RESTRICTED
self.user.save() self.user.save()
home_page = self.app.get("/")
self.assertContains(home_page, "igorville.gov")
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id})) response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
def test_domain_detail_allowed_for_on_hold(self): def test_domain_detail_allowed_for_on_hold(self):
"""Test that the domain overview page displays for on hold domain""" """Test that the domain overview page displays for on hold domain"""
with less_console_noise(): with less_console_noise():
home_page = self.app.get("/")
self.assertContains(home_page, "on-hold.gov")
# View domain overview page # View domain overview page
detail_page = self.client.get(reverse("domain", kwargs={"pk": self.domain_on_hold.id})) detail_page = self.client.get(reverse("domain", kwargs={"pk": self.domain_on_hold.id}))
self.assertNotContains(detail_page, "Edit") self.assertNotContains(detail_page, "Edit")
def test_domain_detail_see_just_nameserver(self): def test_domain_detail_see_just_nameserver(self):
with less_console_noise(): with less_console_noise():
home_page = self.app.get("/")
self.assertContains(home_page, "justnameserver.com")
# View nameserver on Domain Overview page # View nameserver on Domain Overview page
detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_just_nameserver.id})) detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_just_nameserver.id}))
@ -303,9 +272,6 @@ class TestDomainDetail(TestDomainOverview):
def test_domain_detail_see_nameserver_and_ip(self): def test_domain_detail_see_nameserver_and_ip(self):
with less_console_noise(): with less_console_noise():
home_page = self.app.get("/")
self.assertContains(home_page, "nameserverwithip.gov")
# View nameserver on Domain Overview page # View nameserver on Domain Overview page
detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_with_ip.id})) detail_page = self.app.get(reverse("domain", kwargs={"pk": self.domain_with_ip.id}))
@ -1643,8 +1609,6 @@ class TestDomainSecurityEmail(TestDomainOverview):
management pages share the same permissions class""" management pages share the same permissions class"""
self.user.status = User.RESTRICTED self.user.status = User.RESTRICTED
self.user.save() self.user.save()
home_page = self.app.get("/")
self.assertContains(home_page, "igorville.gov")
with less_console_noise(): with less_console_noise():
response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id})) response = self.client.get(reverse("domain", kwargs={"pk": self.domain.id}))
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)

View file

@ -0,0 +1,151 @@
from registrar.models import UserDomainRole, Domain
from django.urls import reverse
from .test_views import TestWithUser
from django_webtest import WebTest # type: ignore
from django.utils.dateparse import parse_date
class GetDomainsJsonTest(TestWithUser, WebTest):
def setUp(self):
super().setUp()
self.app.set_user(self.user.username)
# Create test domains
self.domain1 = Domain.objects.create(name="example1.com", expiration_date="2024-01-01", state="active")
self.domain2 = Domain.objects.create(name="example2.com", expiration_date="2024-02-01", state="inactive")
self.domain3 = Domain.objects.create(name="example3.com", expiration_date="2024-03-01", state="active")
# Create UserDomainRoles
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.domain3)
def tearDown(self):
super().tearDown()
UserDomainRole.objects.all().delete()
UserDomainRole.objects.all().delete()
def test_get_domains_json_unauthenticated(self):
"""for an unauthenticated user, test that the user is redirected for auth"""
self.app.reset()
response = self.client.get(reverse("get_domains_json"))
self.assertEqual(response.status_code, 302)
def test_get_domains_json_authenticated(self):
"""Test that an authenticated user gets the list of 3 domains."""
response = self.app.get(reverse("get_domains_json"))
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"]), 3)
# Expected domains
expected_domains = [self.domain1, self.domain2, self.domain3]
# 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
action_label_expected = (
"View"
if 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])
def test_pagination(self):
"""Test that pagination is correct in the response"""
response = self.app.get(reverse("get_domains_json"), {"page": 1})
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)
def test_sorting(self):
"""test that sorting works properly in the response"""
response = self.app.get(reverse("get_domains_json"), {"sort_by": "expiration_date", "order": "desc"})
self.assertEqual(response.status_code, 200)
data = response.json
# Check if sorted by expiration_date in descending order
expiration_dates = [domain["expiration_date"] for domain in data["domains"]]
self.assertEqual(expiration_dates, sorted(expiration_dates, reverse=True))
response = self.app.get(reverse("get_domains_json"), {"sort_by": "expiration_date", "order": "asc"})
self.assertEqual(response.status_code, 200)
data = response.json
# Check if sorted by expiration_date in ascending order
expiration_dates = [domain["expiration_date"] for domain in data["domains"]]
self.assertEqual(expiration_dates, sorted(expiration_dates))
def test_sorting_by_state_display(self):
"""test that the state_display sorting works properly"""
response = self.app.get(reverse("get_domains_json"), {"sort_by": "state_display", "order": "asc"})
self.assertEqual(response.status_code, 200)
data = response.json
# Check if sorted by state_display in ascending order
states = [domain["state_display"] for domain in data["domains"]]
self.assertEqual(states, sorted(states))
response = self.app.get(reverse("get_domains_json"), {"sort_by": "state_display", "order": "desc"})
self.assertEqual(response.status_code, 200)
data = response.json
# Check if sorted by state_display in descending order
states = [domain["state_display"] for domain in data["domains"]]
self.assertEqual(states, sorted(states, reverse=True))

View file

@ -47,11 +47,8 @@ class DomainRequestTests(TestWithUser, WebTest):
def test_domain_request_form_intro_is_skipped_when_edit_access(self): def test_domain_request_form_intro_is_skipped_when_edit_access(self):
"""Tests that user is NOT presented with intro acknowledgement page when accessed through 'edit'""" """Tests that user is NOT presented with intro acknowledgement page when accessed through 'edit'"""
completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
home_page = self.app.get("/") detail_page = self.app.get(f"/domain-request/{domain_request.id}/edit/")
self.assertContains(home_page, "city.gov")
# click the "Edit" link
detail_page = home_page.click("Edit", index=0)
# Check that the response is a redirect # Check that the response is a redirect
self.assertEqual(detail_page.status_code, 302) self.assertEqual(detail_page.status_code, 302)
# You can access the 'Location' header to get the redirect URL # You can access the 'Location' header to get the redirect URL
@ -2425,10 +2422,7 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
domain_request.save() domain_request.save()
home_page = self.app.get("/") detail_page = self.app.get(f"/domain-request/{domain_request.id}")
self.assertContains(home_page, "city.gov")
# click the "Manage" link
detail_page = home_page.click("Manage", index=0)
self.assertContains(detail_page, "city.gov") self.assertContains(detail_page, "city.gov")
self.assertContains(detail_page, "city1.gov") self.assertContains(detail_page, "city1.gov")
self.assertContains(detail_page, "Chief Tester") self.assertContains(detail_page, "Chief Tester")
@ -2445,10 +2439,7 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
domain_request.save() domain_request.save()
home_page = self.app.get("/") detail_page = self.app.get(f"/domain-request/{domain_request.id}")
self.assertContains(home_page, "city.gov")
# click the "Manage" link
detail_page = home_page.click("Manage", index=0)
self.assertContains(detail_page, "city.gov") self.assertContains(detail_page, "city.gov")
self.assertContains(detail_page, "Chief Tester") self.assertContains(detail_page, "Chief Tester")
self.assertContains(detail_page, "testy@town.com") self.assertContains(detail_page, "testy@town.com")
@ -2460,10 +2451,7 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
domain_request.save() domain_request.save()
home_page = self.app.get("/") detail_page = self.app.get(f"/domain-request/{domain_request.id}")
self.assertContains(home_page, "city.gov")
# click the "Manage" link
detail_page = home_page.click("Manage", index=0)
self.assertContains(detail_page, "city.gov") self.assertContains(detail_page, "city.gov")
self.assertContains(detail_page, "city1.gov") self.assertContains(detail_page, "city1.gov")
self.assertContains(detail_page, "Chief Tester") self.assertContains(detail_page, "Chief Tester")
@ -2485,8 +2473,8 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
target_status_code=200, target_status_code=200,
fetch_redirect_response=True, fetch_redirect_response=True,
) )
home_page = self.app.get("/") response = self.client.get("/get-domain-requests-json/")
self.assertContains(home_page, "Withdrawn") self.assertContains(response, "Withdrawn")
def test_domain_request_withdraw_no_permissions(self): def test_domain_request_withdraw_no_permissions(self):
"""Can't withdraw domain requests as a restricted user.""" """Can't withdraw domain requests as a restricted user."""
@ -2495,10 +2483,7 @@ class DomainRequestTestDifferentStatuses(TestWithUser, WebTest):
domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.user)
domain_request.save() domain_request.save()
home_page = self.app.get("/") detail_page = self.app.get(f"/domain-request/{domain_request.id}")
self.assertContains(home_page, "city.gov")
# click the "Manage" link
detail_page = home_page.click("Manage", index=0)
self.assertContains(detail_page, "city.gov") self.assertContains(detail_page, "city.gov")
self.assertContains(detail_page, "city1.gov") self.assertContains(detail_page, "city1.gov")
self.assertContains(detail_page, "Chief Tester") self.assertContains(detail_page, "Chief Tester")
@ -2568,9 +2553,9 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
def test_unlocked_steps_full_domain_request(self): def test_unlocked_steps_full_domain_request(self):
"""Test when all fields in the domain request are filled.""" """Test when all fields in the domain request are filled."""
completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user) domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.STARTED, user=self.user)
# Make a request to the home page
home_page = self.app.get("/") response = self.app.get(f"/domain-request/{domain_request.id}/edit/")
# django-webtest does not handle cookie-based sessions well because it keeps # django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept # resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here # of a "session". We are going to do it manually, saving the session ID here
@ -2578,13 +2563,6 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# Assert that the response contains "city.gov"
self.assertContains(home_page, "city.gov")
# Click the "Edit" link
response = home_page.click("Edit", index=0)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# Check if the response is a redirect # Check if the response is a redirect
if response.status_code == 302: if response.status_code == 302:
# Follow the redirect manually # Follow the redirect manually
@ -2635,8 +2613,7 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
) )
domain_request.other_contacts.set([contact_2]) domain_request.other_contacts.set([contact_2])
# Make a request to the home page response = self.app.get(f"/domain-request/{domain_request.id}/edit/")
home_page = self.app.get("/")
# django-webtest does not handle cookie-based sessions well because it keeps # django-webtest does not handle cookie-based sessions well because it keeps
# resetting the session key on each new request, thus destroying the concept # resetting the session key on each new request, thus destroying the concept
# of a "session". We are going to do it manually, saving the session ID here # of a "session". We are going to do it manually, saving the session ID here
@ -2644,13 +2621,6 @@ class TestWizardUnlockingSteps(TestWithUser, WebTest):
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# Assert that the response contains "city.gov"
self.assertContains(home_page, "igorville.gov")
# Click the "Edit" link
response = home_page.click("Edit", index=0)
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
# Check if the response is a redirect # Check if the response is a redirect
if response.status_code == 302: if response.status_code == 302:
# Follow the redirect manually # Follow the redirect manually

View file

@ -0,0 +1,248 @@
from registrar.models import DomainRequest
from django.urls import reverse
from .test_views import TestWithUser
from django_webtest import WebTest # type: ignore
from django.utils.dateparse import parse_datetime
class GetRequestsJsonTest(TestWithUser, WebTest):
def setUp(self):
super().setUp()
self.app.set_user(self.user.username)
# Create domain requests for the user
self.domain_requests = [
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-01-01",
status=DomainRequest.DomainRequestStatus.STARTED,
created_at="2024-01-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-02-01",
status=DomainRequest.DomainRequestStatus.WITHDRAWN,
created_at="2024-02-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-03-01",
status=DomainRequest.DomainRequestStatus.REJECTED,
created_at="2024-03-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-04-01",
status=DomainRequest.DomainRequestStatus.STARTED,
created_at="2024-04-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-05-01",
status=DomainRequest.DomainRequestStatus.STARTED,
created_at="2024-05-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-06-01",
status=DomainRequest.DomainRequestStatus.WITHDRAWN,
created_at="2024-06-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-07-01",
status=DomainRequest.DomainRequestStatus.REJECTED,
created_at="2024-07-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-08-01",
status=DomainRequest.DomainRequestStatus.STARTED,
created_at="2024-08-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-09-01",
status=DomainRequest.DomainRequestStatus.STARTED,
created_at="2024-09-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-10-01",
status=DomainRequest.DomainRequestStatus.WITHDRAWN,
created_at="2024-10-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-11-01",
status=DomainRequest.DomainRequestStatus.REJECTED,
created_at="2024-11-01",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-11-02",
status=DomainRequest.DomainRequestStatus.WITHDRAWN,
created_at="2024-11-02",
),
DomainRequest.objects.create(
creator=self.user,
requested_domain=None,
submission_date="2024-12-01",
status=DomainRequest.DomainRequestStatus.APPROVED,
created_at="2024-12-01",
),
]
def tearDown(self):
super().tearDown()
DomainRequest.objects.all().delete()
def test_get_domain_requests_json_authenticated(self):
"""Test that domain requests are returned properly for an authenticated user."""
response = self.app.get(reverse("get_domain_requests_json"))
self.assertEqual(response.status_code, 200)
data = response.json
# Check pagination info
self.assertEqual(data["page"], 1)
self.assertTrue(data["has_next"])
self.assertFalse(data["has_previous"])
self.assertEqual(data["num_pages"], 2)
# Check the number of domain requests
self.assertEqual(len(data["domain_requests"]), 10)
# Extract fields from response
requested_domains = [request["requested_domain"] for request in data["domain_requests"]]
submission_dates = [request["submission_date"] for request in data["domain_requests"]]
statuses = [request["status"] for request in data["domain_requests"]]
created_ats = [request["created_at"] for request in data["domain_requests"]]
ids = [request["id"] for request in data["domain_requests"]]
is_deletables = [request["is_deletable"] for request in data["domain_requests"]]
action_urls = [request["action_url"] for request in data["domain_requests"]]
action_labels = [request["action_label"] for request in data["domain_requests"]]
svg_icons = [request["svg_icon"] for request in data["domain_requests"]]
# Check fields for each domain request
for i in range(10):
self.assertNotEqual(data["domain_requests"][i]["status"], "Approved")
self.assertEqual(
self.domain_requests[i].requested_domain.name if self.domain_requests[i].requested_domain else None,
requested_domains[i],
)
self.assertEqual(self.domain_requests[i].submission_date, submission_dates[i])
self.assertEqual(self.domain_requests[i].get_status_display(), statuses[i])
self.assertEqual(
parse_datetime(self.domain_requests[i].created_at.isoformat()), parse_datetime(created_ats[i])
)
self.assertEqual(self.domain_requests[i].id, ids[i])
# Check is_deletable
is_deletable_expected = self.domain_requests[i].status in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
self.assertEqual(is_deletable_expected, is_deletables[i])
# Check action_url
action_url_expected = (
reverse("edit-domain-request", kwargs={"id": self.domain_requests[i].id})
if self.domain_requests[i].status
in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
else reverse("domain-request-status", kwargs={"pk": self.domain_requests[i].id})
)
self.assertEqual(action_url_expected, action_urls[i])
# Check action_label
action_label_expected = (
"Edit"
if self.domain_requests[i].status
in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
else "Manage"
)
self.assertEqual(action_label_expected, action_labels[i])
# Check svg_icon
svg_icon_expected = (
"edit"
if self.domain_requests[i].status
in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
else "settings"
)
self.assertEqual(svg_icon_expected, svg_icons[i])
def test_pagination(self):
"""Test that pagination works properly. There are 11 total non-approved requests and
a page size of 10"""
response = self.app.get(reverse("get_domain_requests_json"), {"page": 1})
self.assertEqual(response.status_code, 200)
data = response.json
# Check pagination info
self.assertEqual(data["page"], 1)
self.assertTrue(data["has_next"])
self.assertFalse(data["has_previous"])
self.assertEqual(data["num_pages"], 2)
response = self.app.get(reverse("get_domain_requests_json"), {"page": 2})
self.assertEqual(response.status_code, 200)
data = response.json
# Check pagination info
self.assertEqual(data["page"], 2)
self.assertFalse(data["has_next"])
self.assertTrue(data["has_previous"])
self.assertEqual(data["num_pages"], 2)
def test_sorting(self):
"""test that sorting works properly on the result set"""
response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "submission_date", "order": "desc"})
self.assertEqual(response.status_code, 200)
data = response.json
# Check if sorted by submission_date in descending order
submission_dates = [req["submission_date"] for req in data["domain_requests"]]
self.assertEqual(submission_dates, sorted(submission_dates, reverse=True))
response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "submission_date", "order": "asc"})
self.assertEqual(response.status_code, 200)
data = response.json
# Check if sorted by submission_date in ascending order
submission_dates = [req["submission_date"] for req in data["domain_requests"]]
self.assertEqual(submission_dates, sorted(submission_dates))
def test_filter_approved_excluded(self):
"""test that approved requests are excluded from result set."""
# sort in reverse chronological order of submission date, since most recent request is approved
response = self.app.get(reverse("get_domain_requests_json"), {"sort_by": "submission_date", "order": "desc"})
self.assertEqual(response.status_code, 200)
data = response.json
# Ensure no approved requests are included
for domain_request in data["domain_requests"]:
self.assertNotEqual(domain_request["status"], DomainRequest.DomainRequestStatus.APPROVED)

View file

@ -0,0 +1,79 @@
from django.http import JsonResponse
from django.core.paginator import Paginator
from registrar.models import DomainRequest
from django.utils.dateformat import format
from django.contrib.auth.decorators import login_required
from django.urls import reverse
@login_required
def get_domain_requests_json(request):
"""Given the current request,
get all domain requests that are associated with the request user and exclude the APPROVED ones"""
domain_requests = DomainRequest.objects.filter(creator=request.user).exclude(
status=DomainRequest.DomainRequestStatus.APPROVED
)
# Handle sorting
sort_by = request.GET.get("sort_by", "id") # Default to 'id'
order = request.GET.get("order", "asc") # Default to 'asc'
if order == "desc":
sort_by = f"-{sort_by}"
domain_requests = domain_requests.order_by(sort_by)
page_number = request.GET.get("page", 1)
paginator = Paginator(domain_requests, 10)
page_obj = paginator.get_page(page_number)
domain_requests_data = [
{
"requested_domain": domain_request.requested_domain.name if domain_request.requested_domain else None,
"submission_date": domain_request.submission_date,
"status": domain_request.get_status_display(),
"created_at": format(domain_request.created_at, "c"), # Serialize to ISO 8601
"id": domain_request.id,
"is_deletable": domain_request.status
in [DomainRequest.DomainRequestStatus.STARTED, DomainRequest.DomainRequestStatus.WITHDRAWN],
"action_url": (
reverse("edit-domain-request", kwargs={"id": domain_request.id})
if domain_request.status
in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
else reverse("domain-request-status", kwargs={"pk": domain_request.id})
),
"action_label": (
"Edit"
if domain_request.status
in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
else "Manage"
),
"svg_icon": (
"edit"
if domain_request.status
in [
DomainRequest.DomainRequestStatus.STARTED,
DomainRequest.DomainRequestStatus.ACTION_NEEDED,
DomainRequest.DomainRequestStatus.WITHDRAWN,
]
else "settings"
),
}
for domain_request in page_obj
]
return JsonResponse(
{
"domain_requests": domain_requests_data,
"has_next": page_obj.has_next(),
"has_previous": page_obj.has_previous(),
"page": page_obj.number,
"num_pages": paginator.num_pages,
"total": paginator.count,
}
)

View file

@ -0,0 +1,60 @@
from django.http import JsonResponse
from django.core.paginator import Paginator
from registrar.models import UserDomainRole, Domain
from django.contrib.auth.decorators import login_required
from django.urls import reverse
@login_required
def get_domains_json(request):
"""Given the current request,
get all domains that are associated with the UserDomainRole object"""
user_domain_roles = UserDomainRole.objects.filter(user=request.user)
domain_ids = user_domain_roles.values_list("domain_id", flat=True)
objects = Domain.objects.filter(id__in=domain_ids)
# Handle sorting
sort_by = request.GET.get("sort_by", "id") # Default to 'id'
order = request.GET.get("order", "asc") # Default to 'asc'
if sort_by == "state_display":
# Fetch the objects and sort them in Python
objects = list(objects) # Evaluate queryset to a list
objects.sort(key=lambda domain: domain.state_display(), reverse=(order == "desc"))
else:
if order == "desc":
sort_by = f"-{sort_by}"
objects = objects.order_by(sort_by)
paginator = Paginator(objects, 10)
page_number = request.GET.get("page")
page_obj = paginator.get_page(page_number)
# Convert objects to JSON-serializable format
domains = [
{
"id": domain.id,
"name": domain.name,
"expiration_date": domain.expiration_date,
"state": domain.state,
"state_display": domain.state_display(),
"get_state_help_text": domain.get_state_help_text(),
"action_url": reverse("domain", kwargs={"pk": domain.id}),
"action_label": ("View" if 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"),
}
for domain in page_obj.object_list
]
return JsonResponse(
{
"domains": domains,
"page": page_obj.number,
"num_pages": paginator.num_pages,
"has_previous": page_obj.has_previous(),
"has_next": page_obj.has_next(),
"total": paginator.count,
}
)

View file

@ -1,22 +1,18 @@
from django.shortcuts import render from django.shortcuts import render
from registrar.models import DomainRequest
from registrar.models import DomainRequest, Domain, UserDomainRole
from waffle.decorators import flag_is_active from waffle.decorators import flag_is_active
def index(request): def index(request):
"""This page is available to anyone without logging in.""" """This page is available to anyone without logging in."""
context = {} context = {}
if request.user.is_authenticated: if request.user.is_authenticated:
# Get all domain requests the user has access to # Get all domain requests the user has access to
domain_requests, deletable_domain_requests = _get_domain_requests(request) domain_requests, deletable_domain_requests = _get_domain_requests(request)
context["domain_requests"] = domain_requests context["domain_requests"] = domain_requests
# Get all domains the user has access to
domains = _get_domains(request)
context["domains"] = domains
# Determine if the user will see domain requests that they can delete # Determine if the user will see domain requests that they can delete
has_deletable_domain_requests = deletable_domain_requests.exists() has_deletable_domain_requests = deletable_domain_requests.exists()
context["has_deletable_domain_requests"] = has_deletable_domain_requests context["has_deletable_domain_requests"] = has_deletable_domain_requests
@ -55,11 +51,3 @@ def _get_domain_requests(request):
deletable_domain_requests = domain_requests.filter(status__in=valid_statuses) deletable_domain_requests = domain_requests.filter(status__in=valid_statuses)
return (domain_requests, deletable_domain_requests) return (domain_requests, deletable_domain_requests)
def _get_domains(request):
"""Given the current request,
get all domains that are associated with the UserDomainRole object"""
user_domain_roles = UserDomainRole.objects.filter(user=request.user)
domain_ids = user_domain_roles.values_list("domain_id", flat=True)
return Domain.objects.filter(id__in=domain_ids)