mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-19 10:59:21 +02:00
Finish work on view, template and unit tests
This commit is contained in:
commit
e5aeefe451
23 changed files with 8387 additions and 403 deletions
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from dateutil.relativedelta import relativedelta # type: ignore
|
from dateutil.relativedelta import relativedelta # type: ignore
|
||||||
from epplibwrapper.errors import ErrorCode, RegistryError
|
from epplibwrapper.errors import ErrorCode, RegistryError
|
||||||
|
from registrar.models.user_domain_role import UserDomainRole
|
||||||
from waffle.admin import FlagAdmin
|
from waffle.admin import FlagAdmin
|
||||||
from waffle.models import Sample, Switch
|
from waffle.models import Sample, Switch
|
||||||
from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website
|
from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website
|
||||||
|
@ -753,6 +754,23 @@ class MyUserAdmin(BaseUserAdmin, ImportExportModelAdmin):
|
||||||
# users who might not belong to groups
|
# users who might not belong to groups
|
||||||
return self.analyst_readonly_fields
|
return self.analyst_readonly_fields
|
||||||
|
|
||||||
|
def change_view(self, request, object_id, form_url="", extra_context=None):
|
||||||
|
"""Add user's related domains and requests to context"""
|
||||||
|
obj = self.get_object(request, object_id)
|
||||||
|
|
||||||
|
domain_requests = DomainRequest.objects.filter(creator=obj).exclude(
|
||||||
|
Q(status=DomainRequest.DomainRequestStatus.STARTED) | Q(status=DomainRequest.DomainRequestStatus.WITHDRAWN)
|
||||||
|
)
|
||||||
|
sort_by = request.GET.get("sort_by", "requested_domain__name")
|
||||||
|
domain_requests = domain_requests.order_by(sort_by)
|
||||||
|
|
||||||
|
user_domain_roles = UserDomainRole.objects.filter(user=obj)
|
||||||
|
domain_ids = user_domain_roles.values_list("domain_id", flat=True)
|
||||||
|
domains = Domain.objects.filter(id__in=domain_ids).exclude(state=Domain.State.DELETED)
|
||||||
|
|
||||||
|
extra_context = {"domain_requests": domain_requests, "domains": domains}
|
||||||
|
return super().change_view(request, object_id, form_url, extra_context)
|
||||||
|
|
||||||
|
|
||||||
class HostIPInline(admin.StackedInline):
|
class HostIPInline(admin.StackedInline):
|
||||||
"""Edit an ip address on the host page."""
|
"""Edit an ip address on the host page."""
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
7042
src/registrar/assets/js/uswds-edited.js
Normal file
7042
src/registrar/assets/js/uswds-edited.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -126,6 +126,12 @@ html[data-theme="light"] {
|
||||||
.usa-table td {
|
.usa-table td {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets darker color on delete page links.
|
||||||
|
// Remove when dark mode successfully applies to Django delete page.
|
||||||
|
.delete-confirmation .content a:not(.button) {
|
||||||
|
color: color('primary');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firefox needs this to be specifically set
|
// Firefox needs this to be specifically set
|
||||||
|
@ -149,6 +155,12 @@ html[data-theme="dark"] {
|
||||||
.usa-table td {
|
.usa-table td {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets darker color on delete page links.
|
||||||
|
// Remove when dark mode successfully applies to Django delete page.
|
||||||
|
.delete-confirmation .content a:not(.button) {
|
||||||
|
color: color('primary');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#branding h1 a:link, #branding h1 a:visited {
|
#branding h1 a:link, #branding h1 a:visited {
|
||||||
|
@ -173,6 +185,14 @@ div#content > h2 {
|
||||||
margin: units(2) 0 units(1) 0;
|
margin: units(2) 0 units(1) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.module ul.padding-0 {
|
||||||
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module ul.margin-0 {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.change-list {
|
.change-list {
|
||||||
.usa-table--striped tbody tr:nth-child(odd) td,
|
.usa-table--striped tbody tr:nth-child(odd) td,
|
||||||
.usa-table--striped tbody tr:nth-child(odd) th,
|
.usa-table--striped tbody tr:nth-child(odd) th,
|
||||||
|
@ -707,7 +727,7 @@ div.dja__model-description{
|
||||||
|
|
||||||
a, a:link, a:visited {
|
a, a:link, a:visited {
|
||||||
font-size: medium;
|
font-size: medium;
|
||||||
color: #005288 !important;
|
color: color('primary') !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.dja__model-description--no-overflow {
|
&.dja__model-description--no-overflow {
|
||||||
|
@ -724,3 +744,15 @@ div.dja__model-description{
|
||||||
.text-underline {
|
.text-underline {
|
||||||
text-decoration: underline !important;
|
text-decoration: underline !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//-- Override some styling for the USWDS summary box (per design quidance for ticket #2055
|
||||||
|
.usa-summary-box {
|
||||||
|
background: #{$dhs-blue-10};
|
||||||
|
border-color: #{$dhs-blue-30};
|
||||||
|
max-width: 72ex;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usa-summary-box h3 {
|
||||||
|
color: #{$dhs-blue-60};
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -2,28 +2,35 @@
|
||||||
{% load i18n static %}
|
{% load i18n static %}
|
||||||
|
|
||||||
{% block after_related_objects %}
|
{% block after_related_objects %}
|
||||||
|
<div class="module aligned padding-3">
|
||||||
|
<h2>Associated requests and domains</h2>
|
||||||
<div class="module aligned padding-3">
|
<div class="grid-row grid-gap mobile:padding-x-1 desktop:padding-x-4">
|
||||||
<h2>Associated requests and domains</h2>
|
<div class="mobile:grid-col-12 tablet:grid-col-6 desktop:grid-col-4">
|
||||||
|
<h3>Domain requests</h3>
|
||||||
<div class="grid-row grid-gap">
|
<ul class="margin-0 padding-0">
|
||||||
<div class="mobile:grid-col-12 tablet:grid-col-6 desktop:grid-col-4">
|
{% for domain_request in domain_requests %}
|
||||||
<h3>Domain requests</h3>
|
<li>
|
||||||
<p>asfasdsd</p>
|
<a href="{% url 'admin:registrar_domainrequest_change' domain_request.pk %}">
|
||||||
</div>
|
{{ domain_request.requested_domain }}
|
||||||
<div class="mobile:grid-col-12 tablet:grid-col-6 desktop:grid-col-4">
|
</a>
|
||||||
|
({{ domain_request.status }})
|
||||||
<h3>Domains</h3>
|
</li>
|
||||||
<p>asfasdsd</p>
|
{% endfor %}
|
||||||
</div>
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="mobile:grid-col-12 tablet:grid-col-6 desktop:grid-col-4">
|
||||||
|
<h3>Domains</h3>
|
||||||
|
<ul class="margin-0 padding-0">
|
||||||
|
{% for domain in domains %}
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'admin:registrar_domain_change' domain.pk %}">
|
||||||
|
{{ domain.name }}
|
||||||
|
</a>
|
||||||
|
({{ domain.state }})
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -14,6 +14,11 @@
|
||||||
<h2>Time to complete the form</h2>
|
<h2>Time to complete the form</h2>
|
||||||
<p>If you have <a href="{% public_site_url 'domains/before/#information-you%E2%80%99ll-need-to-complete-the-domain-request-form' %}" target="_blank" class="usa-link">all the information you need</a>,
|
<p>If you have <a href="{% public_site_url 'domains/before/#information-you%E2%80%99ll-need-to-complete-the-domain-request-form' %}" target="_blank" class="usa-link">all the information you need</a>,
|
||||||
completing your domain request might take around 15 minutes.</p>
|
completing your domain request might take around 15 minutes.</p>
|
||||||
|
{% if has_profile_feature_flag %}
|
||||||
|
<h2>How we’ll reach you</h2>
|
||||||
|
<p>While reviewing your domain request, we may need to reach out with questions. We’ll also email you when we complete our review If the contact information below is not correct, visit <a href="" target="_blank" class="usa-link">your profile</a> to make updates.</p>
|
||||||
|
{% include "includes/profile_information.html" with user=user%}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% block form_buttons %}
|
{% block form_buttons %}
|
||||||
|
|
|
@ -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 #}
|
||||||
<!--
|
<!--
|
||||||
|
|
25
src/registrar/templates/includes/profile_information.html
Normal file
25
src/registrar/templates/includes/profile_information.html
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
{% load static field_helpers %}
|
||||||
|
|
||||||
|
{% block domain_content %}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="usa-summary-box"
|
||||||
|
role="region"
|
||||||
|
aria-labelledby="summary-box-key-information"
|
||||||
|
>
|
||||||
|
<div class="usa-summary-box__body">
|
||||||
|
<h3 class="usa-summary-box__heading" id="summary-box-key-information">
|
||||||
|
Your contact information
|
||||||
|
</h3>
|
||||||
|
<div class="usa-summary-box__text">
|
||||||
|
<ul>
|
||||||
|
<li>Full name: <b>{{ user.contact.get_formatted_name }}</b></li>
|
||||||
|
<li>Organization email: <b>{{ user.email }}</b></li>
|
||||||
|
<li>Title or role in your organization: <b>{{ user.contact.title }}</b></li>
|
||||||
|
<li>Phone: <b>{{ user.contact.phone.as_national }}</b></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -667,7 +667,7 @@ class MockDb(TestCase):
|
||||||
is_election_board=False,
|
is_election_board=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
meoward_user = get_user_model().objects.create(
|
self.meoward_user = get_user_model().objects.create(
|
||||||
username="meoward_username", first_name="first_meoward", last_name="last_meoward", email="meoward@rocks.com"
|
username="meoward_username", first_name="first_meoward", last_name="last_meoward", email="meoward@rocks.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -676,7 +676,7 @@ class MockDb(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
_, created = UserDomainRole.objects.get_or_create(
|
_, created = UserDomainRole.objects.get_or_create(
|
||||||
user=meoward_user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
|
user=self.meoward_user, domain=self.domain_1, role=UserDomainRole.Roles.MANAGER
|
||||||
)
|
)
|
||||||
|
|
||||||
_, created = UserDomainRole.objects.get_or_create(
|
_, created = UserDomainRole.objects.get_or_create(
|
||||||
|
@ -688,19 +688,21 @@ class MockDb(TestCase):
|
||||||
)
|
)
|
||||||
|
|
||||||
_, created = UserDomainRole.objects.get_or_create(
|
_, created = UserDomainRole.objects.get_or_create(
|
||||||
user=meoward_user, domain=self.domain_2, role=UserDomainRole.Roles.MANAGER
|
user=self.meoward_user, domain=self.domain_2, role=UserDomainRole.Roles.MANAGER
|
||||||
)
|
)
|
||||||
|
|
||||||
_, created = UserDomainRole.objects.get_or_create(
|
_, created = UserDomainRole.objects.get_or_create(
|
||||||
user=meoward_user, domain=self.domain_11, role=UserDomainRole.Roles.MANAGER
|
user=self.meoward_user, domain=self.domain_11, role=UserDomainRole.Roles.MANAGER
|
||||||
)
|
)
|
||||||
|
|
||||||
_, created = UserDomainRole.objects.get_or_create(
|
_, created = UserDomainRole.objects.get_or_create(
|
||||||
user=meoward_user, domain=self.domain_12, role=UserDomainRole.Roles.MANAGER
|
user=self.meoward_user, domain=self.domain_12, role=UserDomainRole.Roles.MANAGER
|
||||||
)
|
)
|
||||||
|
|
||||||
_, created = DomainInvitation.objects.get_or_create(
|
_, created = DomainInvitation.objects.get_or_create(
|
||||||
email=meoward_user.email, domain=self.domain_1, status=DomainInvitation.DomainInvitationStatus.RETRIEVED
|
email=self.meoward_user.email,
|
||||||
|
domain=self.domain_1,
|
||||||
|
status=DomainInvitation.DomainInvitationStatus.RETRIEVED,
|
||||||
)
|
)
|
||||||
|
|
||||||
_, created = DomainInvitation.objects.get_or_create(
|
_, created = DomainInvitation.objects.get_or_create(
|
||||||
|
|
|
@ -47,6 +47,7 @@ from registrar.models import (
|
||||||
from registrar.models.user_domain_role import UserDomainRole
|
from registrar.models.user_domain_role import UserDomainRole
|
||||||
from registrar.models.verified_by_staff import VerifiedByStaff
|
from registrar.models.verified_by_staff import VerifiedByStaff
|
||||||
from .common import (
|
from .common import (
|
||||||
|
MockDb,
|
||||||
MockSESClient,
|
MockSESClient,
|
||||||
AuditedAdminMockData,
|
AuditedAdminMockData,
|
||||||
completed_domain_request,
|
completed_domain_request,
|
||||||
|
@ -3438,16 +3439,19 @@ class TestListHeaderAdmin(TestCase):
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
|
|
||||||
|
|
||||||
class TestMyUserAdmin(TestCase):
|
class TestMyUserAdmin(MockDb):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
admin_site = AdminSite()
|
admin_site = AdminSite()
|
||||||
self.admin = MyUserAdmin(model=get_user_model(), admin_site=admin_site)
|
self.admin = MyUserAdmin(model=get_user_model(), admin_site=admin_site)
|
||||||
self.client = Client(HTTP_HOST="localhost:8080")
|
self.client = Client(HTTP_HOST="localhost:8080")
|
||||||
self.superuser = create_superuser()
|
self.superuser = create_superuser()
|
||||||
|
self.staffuser = create_user()
|
||||||
self.test_helper = GenericTestHelper(admin=self.admin)
|
self.test_helper = GenericTestHelper(admin=self.admin)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
|
DomainRequest.objects.all().delete()
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
|
@ -3472,7 +3476,7 @@ class TestMyUserAdmin(TestCase):
|
||||||
"""
|
"""
|
||||||
Tests for the correct helper text on this page
|
Tests for the correct helper text on this page
|
||||||
"""
|
"""
|
||||||
user = create_user()
|
user = self.staffuser
|
||||||
|
|
||||||
p = "adminpass"
|
p = "adminpass"
|
||||||
self.client.login(username="superuser", password=p)
|
self.client.login(username="superuser", password=p)
|
||||||
|
@ -3493,10 +3497,11 @@ class TestMyUserAdmin(TestCase):
|
||||||
]
|
]
|
||||||
self.test_helper.assert_response_contains_distinct_values(response, expected_values)
|
self.test_helper.assert_response_contains_distinct_values(response, expected_values)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
def test_list_display_without_username(self):
|
def test_list_display_without_username(self):
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
request = self.client.request().wsgi_request
|
request = self.client.request().wsgi_request
|
||||||
request.user = create_user()
|
request.user = self.staffuser
|
||||||
|
|
||||||
list_display = self.admin.get_list_display(request)
|
list_display = self.admin.get_list_display(request)
|
||||||
expected_list_display = [
|
expected_list_display = [
|
||||||
|
@ -3522,7 +3527,7 @@ class TestMyUserAdmin(TestCase):
|
||||||
def test_get_fieldsets_cisa_analyst(self):
|
def test_get_fieldsets_cisa_analyst(self):
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
request = self.client.request().wsgi_request
|
request = self.client.request().wsgi_request
|
||||||
request.user = create_user()
|
request.user = self.staffuser
|
||||||
fieldsets = self.admin.get_fieldsets(request)
|
fieldsets = self.admin.get_fieldsets(request)
|
||||||
expected_fieldsets = (
|
expected_fieldsets = (
|
||||||
(
|
(
|
||||||
|
@ -3540,6 +3545,97 @@ class TestMyUserAdmin(TestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(fieldsets, expected_fieldsets)
|
self.assertEqual(fieldsets, expected_fieldsets)
|
||||||
|
|
||||||
|
def test_analyst_can_see_related_domains_and_requests_in_user_form(self):
|
||||||
|
"""Tests if an analyst can see the related domains and domain requests for a user in that user's form"""
|
||||||
|
|
||||||
|
# From MockDb, we have self.meoward_user which we'll use as creator
|
||||||
|
# Create fake domain requests
|
||||||
|
domain_request_started = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.STARTED, user=self.meoward_user, name="started.gov"
|
||||||
|
)
|
||||||
|
domain_request_submitted = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.SUBMITTED, user=self.meoward_user, name="submitted.gov"
|
||||||
|
)
|
||||||
|
domain_request_in_review = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=self.meoward_user, name="in-review.gov"
|
||||||
|
)
|
||||||
|
domain_request_withdrawn = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.WITHDRAWN, user=self.meoward_user, name="withdrawn.gov"
|
||||||
|
)
|
||||||
|
domain_request_approved = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.APPROVED, user=self.meoward_user, name="approved.gov"
|
||||||
|
)
|
||||||
|
domain_request_rejected = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.REJECTED, user=self.meoward_user, name="rejected.gov"
|
||||||
|
)
|
||||||
|
domain_request_ineligible = completed_domain_request(
|
||||||
|
status=DomainRequest.DomainRequestStatus.INELIGIBLE, user=self.meoward_user, name="ineligible.gov"
|
||||||
|
)
|
||||||
|
|
||||||
|
# From MockDb, we have sel.meoward_user who's admin on
|
||||||
|
# self.domain_1 - READY
|
||||||
|
# self.domain_2 - DNS_NEEDED
|
||||||
|
# self.domain_11 - READY
|
||||||
|
# self.domain_12 - READY
|
||||||
|
# DELETED:
|
||||||
|
domain_deleted, _ = Domain.objects.get_or_create(
|
||||||
|
name="domain_deleted.gov", state=Domain.State.DELETED, deleted=timezone.make_aware(datetime(2024, 4, 2))
|
||||||
|
)
|
||||||
|
_, created = UserDomainRole.objects.get_or_create(
|
||||||
|
user=self.meoward_user, domain=domain_deleted, role=UserDomainRole.Roles.MANAGER
|
||||||
|
)
|
||||||
|
|
||||||
|
p = "userpass"
|
||||||
|
self.client.login(username="staffuser", password=p)
|
||||||
|
response = self.client.get(
|
||||||
|
"/admin/registrar/user/{}/change/".format(self.meoward_user.id),
|
||||||
|
follow=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make sure the page loaded and contains the expected domain request names and links to the domain requests
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
self.assertContains(response, domain_request_submitted.requested_domain.name)
|
||||||
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_submitted.pk])
|
||||||
|
self.assertContains(response, expected_href)
|
||||||
|
|
||||||
|
self.assertContains(response, domain_request_in_review.requested_domain.name)
|
||||||
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_in_review.pk])
|
||||||
|
self.assertContains(response, expected_href)
|
||||||
|
|
||||||
|
self.assertContains(response, domain_request_approved.requested_domain.name)
|
||||||
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_approved.pk])
|
||||||
|
self.assertContains(response, expected_href)
|
||||||
|
|
||||||
|
self.assertContains(response, domain_request_rejected.requested_domain.name)
|
||||||
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_rejected.pk])
|
||||||
|
self.assertContains(response, expected_href)
|
||||||
|
|
||||||
|
self.assertContains(response, domain_request_ineligible.requested_domain.name)
|
||||||
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_ineligible.pk])
|
||||||
|
self.assertContains(response, expected_href)
|
||||||
|
|
||||||
|
# We filter out those requests
|
||||||
|
# STARTED
|
||||||
|
self.assertNotContains(response, domain_request_started.requested_domain.name)
|
||||||
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_started.pk])
|
||||||
|
self.assertNotContains(response, expected_href)
|
||||||
|
|
||||||
|
# WITHDRAWN
|
||||||
|
self.assertNotContains(response, domain_request_withdrawn.requested_domain.name)
|
||||||
|
expected_href = reverse("admin:registrar_domainrequest_change", args=[domain_request_withdrawn.pk])
|
||||||
|
self.assertNotContains(response, expected_href)
|
||||||
|
|
||||||
|
# Make sure the page contains the expected domain names and links to the domains
|
||||||
|
self.assertContains(response, self.domain_1.name)
|
||||||
|
expected_href = reverse("admin:registrar_domain_change", args=[self.domain_1.pk])
|
||||||
|
self.assertContains(response, expected_href)
|
||||||
|
|
||||||
|
# We filter out DELETED
|
||||||
|
self.assertNotContains(response, domain_deleted.name)
|
||||||
|
expected_href = reverse("admin:registrar_domain_change", args=[domain_deleted.pk])
|
||||||
|
self.assertNotContains(response, expected_href)
|
||||||
|
|
||||||
|
|
||||||
class AuditedAdminTest(TestCase):
|
class AuditedAdminTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
|
@ -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, " "you’ll 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 can’t be edited and won’t 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, " "you’ll 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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
151
src/registrar/tests/test_views_domains_json.py
Normal file
151
src/registrar/tests/test_views_domains_json.py
Normal 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))
|
|
@ -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
|
||||||
|
@ -2402,10 +2399,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")
|
||||||
|
@ -2422,10 +2416,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")
|
||||||
|
@ -2437,10 +2428,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")
|
||||||
|
@ -2462,8 +2450,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."""
|
||||||
|
@ -2472,10 +2460,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")
|
||||||
|
@ -2545,9 +2530,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
|
||||||
|
@ -2555,13 +2540,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
|
||||||
|
@ -2612,8 +2590,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
|
||||||
|
@ -2621,13 +2598,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
|
||||||
|
|
248
src/registrar/tests/test_views_requests_json.py
Normal file
248
src/registrar/tests/test_views_requests_json.py
Normal 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)
|
|
@ -230,7 +230,8 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
||||||
context = self.get_context_data()
|
context = self.get_context_data()
|
||||||
if self.__class__ == DomainRequestWizard:
|
if self.__class__ == DomainRequestWizard:
|
||||||
if request.path_info == self.NEW_URL_NAME:
|
if request.path_info == self.NEW_URL_NAME:
|
||||||
return render(request, "domain_request_intro.html", context)
|
context = self.get_context_data()
|
||||||
|
return render(request, "domain_request_intro.html", context=context)
|
||||||
else:
|
else:
|
||||||
return self.goto(self.steps.first)
|
return self.goto(self.steps.first)
|
||||||
|
|
||||||
|
@ -386,7 +387,10 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
||||||
else:
|
else:
|
||||||
modal_heading = "You are about to submit an incomplete request"
|
modal_heading = "You are about to submit an incomplete request"
|
||||||
|
|
||||||
return {
|
has_profile_flag = flag_is_active(self.request, "profile_feature")
|
||||||
|
logger.debug("PROFILE FLAG is %s" % has_profile_flag)
|
||||||
|
|
||||||
|
context = {
|
||||||
"form_titles": self.TITLES,
|
"form_titles": self.TITLES,
|
||||||
"steps": self.steps,
|
"steps": self.steps,
|
||||||
# Add information about which steps should be unlocked
|
# Add information about which steps should be unlocked
|
||||||
|
@ -394,8 +398,11 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
||||||
"is_federal": self.domain_request.is_federal(),
|
"is_federal": self.domain_request.is_federal(),
|
||||||
"modal_button": modal_button,
|
"modal_button": modal_button,
|
||||||
"modal_heading": modal_heading,
|
"modal_heading": modal_heading,
|
||||||
"has_profile_feature_flag": flag_is_active(self.request, "profile_feature"),
|
# Use the profile waffle feature flag to toggle profile features throughout domain requests
|
||||||
|
"has_profile_feature_flag": has_profile_flag,
|
||||||
|
"user": self.request.user,
|
||||||
}
|
}
|
||||||
|
return context
|
||||||
|
|
||||||
def get_step_list(self) -> list:
|
def get_step_list(self) -> list:
|
||||||
"""Dynamically generated list of steps in the form wizard."""
|
"""Dynamically generated list of steps in the form wizard."""
|
||||||
|
|
79
src/registrar/views/domain_requests_json.py
Normal file
79
src/registrar/views/domain_requests_json.py
Normal 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,
|
||||||
|
}
|
||||||
|
)
|
60
src/registrar/views/domains_json.py
Normal file
60
src/registrar/views/domains_json.py
Normal 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,
|
||||||
|
}
|
||||||
|
)
|
|
@ -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)
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue