mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-03 09:43:33 +02:00
Merge pull request #3270 from cisagov/rh/3142-expiring-soon
#3142: Domain Expiring Filter + CTA Banner - [RH]
This commit is contained in:
commit
5909a7cb49
13 changed files with 442 additions and 35 deletions
|
@ -31,6 +31,9 @@ export class DomainsTable extends BaseTable {
|
||||||
</td>
|
</td>
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
|
const isExpiring = domain.state_display === "Expiring soon"
|
||||||
|
const iconType = isExpiring ? "error_outline" : "info_outline";
|
||||||
|
const iconColor = isExpiring ? "text-secondary-vivid" : "text-accent-cool"
|
||||||
row.innerHTML = `
|
row.innerHTML = `
|
||||||
<th scope="row" role="rowheader" data-label="Domain name">
|
<th scope="row" role="rowheader" data-label="Domain name">
|
||||||
${domain.name}
|
${domain.name}
|
||||||
|
@ -41,14 +44,14 @@ export class DomainsTable extends BaseTable {
|
||||||
<td data-label="Status">
|
<td data-label="Status">
|
||||||
${domain.state_display}
|
${domain.state_display}
|
||||||
<svg
|
<svg
|
||||||
class="usa-icon usa-tooltip usa-tooltip--registrar text-middle margin-bottom-05 text-accent-cool no-click-outline-and-cursor-help"
|
class="usa-icon usa-tooltip usa-tooltip--registrar text-middle margin-bottom-05 ${iconColor} no-click-outline-and-cursor-help"
|
||||||
data-position="top"
|
data-position="top"
|
||||||
title="${domain.get_state_help_text}"
|
title="${domain.get_state_help_text}"
|
||||||
focusable="true"
|
focusable="true"
|
||||||
aria-label="${domain.get_state_help_text}"
|
aria-label="${domain.get_state_help_text}"
|
||||||
role="tooltip"
|
role="tooltip"
|
||||||
>
|
>
|
||||||
<use aria-hidden="true" xlink:href="/public/img/sprite.svg#info_outline"></use>
|
<use aria-hidden="true" xlink:href="/public/img/sprite.svg#${iconType}"></use>
|
||||||
</svg>
|
</svg>
|
||||||
</td>
|
</td>
|
||||||
${markupForSuborganizationRow}
|
${markupForSuborganizationRow}
|
||||||
|
@ -77,3 +80,30 @@ export function initDomainsTable() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For clicking the "Expiring" checkbox
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const expiringLink = document.getElementById('link-expiring-domains');
|
||||||
|
|
||||||
|
if (expiringLink) {
|
||||||
|
// Grab the selection for the status filter by
|
||||||
|
const statusCheckboxes = document.querySelectorAll('input[name="filter-status"]');
|
||||||
|
|
||||||
|
expiringLink.addEventListener('click', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
// Loop through all statuses
|
||||||
|
statusCheckboxes.forEach(checkbox => {
|
||||||
|
// To find the for checkbox for "Expiring soon"
|
||||||
|
if (checkbox.value === "expiring") {
|
||||||
|
// If the checkbox is not already checked, check it
|
||||||
|
if (!checkbox.checked) {
|
||||||
|
checkbox.checked = true;
|
||||||
|
// Do the checkbox action
|
||||||
|
let event = new Event('change');
|
||||||
|
checkbox.dispatchEvent(event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
|
@ -69,9 +69,19 @@ def portfolio_permissions(request):
|
||||||
"has_organization_requests_flag": False,
|
"has_organization_requests_flag": False,
|
||||||
"has_organization_members_flag": False,
|
"has_organization_members_flag": False,
|
||||||
"is_portfolio_admin": False,
|
"is_portfolio_admin": False,
|
||||||
|
"has_domain_renewal_flag": False,
|
||||||
}
|
}
|
||||||
try:
|
try:
|
||||||
portfolio = request.session.get("portfolio")
|
portfolio = request.session.get("portfolio")
|
||||||
|
|
||||||
|
# These feature flags will display and doesn't depend on portfolio
|
||||||
|
portfolio_context.update(
|
||||||
|
{
|
||||||
|
"has_organization_feature_flag": True,
|
||||||
|
"has_domain_renewal_flag": request.user.has_domain_renewal_flag(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
# Linting: line too long
|
# Linting: line too long
|
||||||
view_suborg = request.user.has_view_suborganization_portfolio_permission(portfolio)
|
view_suborg = request.user.has_view_suborganization_portfolio_permission(portfolio)
|
||||||
edit_suborg = request.user.has_edit_suborganization_portfolio_permission(portfolio)
|
edit_suborg = request.user.has_edit_suborganization_portfolio_permission(portfolio)
|
||||||
|
@ -90,6 +100,7 @@ def portfolio_permissions(request):
|
||||||
"has_organization_requests_flag": request.user.has_organization_requests_flag(),
|
"has_organization_requests_flag": request.user.has_organization_requests_flag(),
|
||||||
"has_organization_members_flag": request.user.has_organization_members_flag(),
|
"has_organization_members_flag": request.user.has_organization_members_flag(),
|
||||||
"is_portfolio_admin": request.user.is_portfolio_admin(portfolio),
|
"is_portfolio_admin": request.user.is_portfolio_admin(portfolio),
|
||||||
|
"has_domain_renewal_flag": request.user.has_domain_renewal_flag(),
|
||||||
}
|
}
|
||||||
return portfolio_context
|
return portfolio_context
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ from itertools import zip_longest
|
||||||
import logging
|
import logging
|
||||||
import ipaddress
|
import ipaddress
|
||||||
import re
|
import re
|
||||||
from datetime import date
|
from datetime import date, timedelta
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
from django_fsm import FSMField, transition, TransitionNotAllowed # type: ignore
|
from django_fsm import FSMField, transition, TransitionNotAllowed # type: ignore
|
||||||
|
|
||||||
|
@ -40,6 +40,7 @@ from .utility.time_stamped_model import TimeStampedModel
|
||||||
from .public_contact import PublicContact
|
from .public_contact import PublicContact
|
||||||
|
|
||||||
from .user_domain_role import UserDomainRole
|
from .user_domain_role import UserDomainRole
|
||||||
|
from waffle.decorators import flag_is_active
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -1152,13 +1153,28 @@ 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):
|
def is_expiring(self):
|
||||||
|
"""
|
||||||
|
Check if the domain's expiration date is within 60 days.
|
||||||
|
Return True if domain expiration date exists and within 60 days
|
||||||
|
and otherwise False bc there's no expiration date meaning so not expiring
|
||||||
|
"""
|
||||||
|
if self.expiration_date is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
now = timezone.now().date()
|
||||||
|
|
||||||
|
threshold_date = now + timedelta(days=60)
|
||||||
|
return now < self.expiration_date <= threshold_date
|
||||||
|
|
||||||
|
def state_display(self, request=None):
|
||||||
"""Return the display status of the domain."""
|
"""Return the display status of the domain."""
|
||||||
if self.is_expired() and self.state != self.State.UNKNOWN:
|
if self.is_expired() and (self.state != self.State.UNKNOWN):
|
||||||
return "Expired"
|
return "Expired"
|
||||||
|
elif flag_is_active(request, "domain_renewal") and self.is_expiring():
|
||||||
|
return "Expiring soon"
|
||||||
elif self.state == self.State.UNKNOWN or self.state == self.State.DNS_NEEDED:
|
elif self.state == self.State.UNKNOWN or self.state == self.State.DNS_NEEDED:
|
||||||
return "DNS needed"
|
return "DNS needed"
|
||||||
else:
|
|
||||||
return self.state.capitalize()
|
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):
|
||||||
|
|
|
@ -14,6 +14,8 @@ from .domain import Domain
|
||||||
from .domain_request import DomainRequest
|
from .domain_request import DomainRequest
|
||||||
from registrar.utility.waffle import flag_is_active_for_user
|
from registrar.utility.waffle import flag_is_active_for_user
|
||||||
from waffle.decorators import flag_is_active
|
from waffle.decorators import flag_is_active
|
||||||
|
from django.utils import timezone
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
from phonenumber_field.modelfields import PhoneNumberField # type: ignore
|
from phonenumber_field.modelfields import PhoneNumberField # type: ignore
|
||||||
|
|
||||||
|
@ -163,6 +165,20 @@ class User(AbstractUser):
|
||||||
active_requests_count = self.domain_requests_created.filter(status__in=allowed_states).count()
|
active_requests_count = self.domain_requests_created.filter(status__in=allowed_states).count()
|
||||||
return active_requests_count
|
return active_requests_count
|
||||||
|
|
||||||
|
def get_num_expiring_domains(self, request):
|
||||||
|
"""Return number of expiring domains"""
|
||||||
|
domain_ids = self.get_user_domain_ids(request)
|
||||||
|
now = timezone.now().date()
|
||||||
|
expiration_window = 60
|
||||||
|
threshold_date = now + timedelta(days=expiration_window)
|
||||||
|
num_of_expiring_domains = Domain.objects.filter(
|
||||||
|
id__in=domain_ids,
|
||||||
|
expiration_date__isnull=False,
|
||||||
|
expiration_date__lte=threshold_date,
|
||||||
|
expiration_date__gt=now,
|
||||||
|
).count()
|
||||||
|
return num_of_expiring_domains
|
||||||
|
|
||||||
def get_rejected_requests_count(self):
|
def get_rejected_requests_count(self):
|
||||||
"""Return count of rejected requests"""
|
"""Return count of rejected requests"""
|
||||||
return self.domain_requests_created.filter(status=DomainRequest.DomainRequestStatus.REJECTED).count()
|
return self.domain_requests_created.filter(status=DomainRequest.DomainRequestStatus.REJECTED).count()
|
||||||
|
@ -259,6 +275,9 @@ class User(AbstractUser):
|
||||||
def is_portfolio_admin(self, portfolio):
|
def is_portfolio_admin(self, portfolio):
|
||||||
return "Admin" in self.portfolio_role_summary(portfolio)
|
return "Admin" in self.portfolio_role_summary(portfolio)
|
||||||
|
|
||||||
|
def has_domain_renewal_flag(self):
|
||||||
|
return flag_is_active_for_user(self, "domain_renewal")
|
||||||
|
|
||||||
def get_first_portfolio(self):
|
def get_first_portfolio(self):
|
||||||
permission = self.portfolio_permissions.first()
|
permission = self.portfolio_permissions.first()
|
||||||
if permission:
|
if permission:
|
||||||
|
|
|
@ -35,9 +35,12 @@
|
||||||
Status:
|
Status:
|
||||||
</span>
|
</span>
|
||||||
<span class="text-primary-darker">
|
<span class="text-primary-darker">
|
||||||
|
|
||||||
{# UNKNOWN domains would not have an expiration date and thus would show 'Expired' #}
|
{# UNKNOWN domains would not have an expiration date and thus would show 'Expired' #}
|
||||||
{% if domain.is_expired and domain.state != domain.State.UNKNOWN %}
|
{% if domain.is_expired and domain.state != domain.State.UNKNOWN %}
|
||||||
Expired
|
Expired
|
||||||
|
{% elif has_domain_renewal_flag and domain.is_expiring %}
|
||||||
|
Expiring soon
|
||||||
{% elif domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED %}
|
{% elif domain.state == domain.State.UNKNOWN or domain.state == domain.State.DNS_NEEDED %}
|
||||||
DNS needed
|
DNS needed
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -46,7 +49,13 @@
|
||||||
</span>
|
</span>
|
||||||
{% if domain.get_state_help_text %}
|
{% if domain.get_state_help_text %}
|
||||||
<div class="padding-top-1 text-primary-darker">
|
<div class="padding-top-1 text-primary-darker">
|
||||||
|
{% if has_domain_renewal_flag and domain.is_expiring and is_domain_manager %}
|
||||||
|
This domain will expire soon. <a href="/not-available-yet">Renew to maintain access.</a>
|
||||||
|
{% elif has_domain_renewal_flag and domain.is_expiring and is_portfolio_user %}
|
||||||
|
This domain will expire soon. Contact one of the listed domain managers to renew the domain.
|
||||||
|
{% else %}
|
||||||
{{ domain.get_state_help_text }}
|
{{ domain.get_state_help_text }}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,10 +1,30 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% comment %} Stores the json endpoint in a url for easier access {% endcomment %}
|
{% comment %} Stores the json endpoint in a url for easier access {% endcomment %}
|
||||||
{% url 'get_domains_json' as url %}
|
{% url 'get_domains_json' as url %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<span id="get_domains_json_url" class="display-none">{{url}}</span>
|
<span id="get_domains_json_url" class="display-none">{{url}}</span>
|
||||||
|
|
||||||
|
<!-- Org model banner (org manager can view, domain manager can edit) -->
|
||||||
|
{% if has_domain_renewal_flag and num_expiring_domains > 0 and has_any_domains_portfolio_permission %}
|
||||||
|
<section class="usa-site-alert usa-site-alert--info margin-bottom-2 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
|
<div class="usa-alert">
|
||||||
|
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
||||||
|
<p class="usa-alert__text maxw-none">
|
||||||
|
{% if num_expiring_domains == 1%}
|
||||||
|
One domain will expire soon. Go to "Manage" to renew the domain. <a href="#" id="link-expiring-domains" class="usa-link">Show expiring domain.</a>
|
||||||
|
{% else%}
|
||||||
|
Multiple domains will expire soon. Go to "Manage" to renew the domains. <a href="#" id="link-expiring-domains" class="usa-link">Show expiring domains.</a>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<section class="section-outlined domains margin-top-0{% if portfolio %} section-outlined--border-base-light{% endif %}" id="domains">
|
<section class="section-outlined domains margin-top-0{% if portfolio %} section-outlined--border-base-light{% endif %}" id="domains">
|
||||||
<div class="section-outlined__header margin-bottom-3 {% if not portfolio %} section-outlined__header--no-portfolio justify-content-space-between{% else %} grid-row{% endif %}">
|
<div class="section-outlined__header margin-bottom-3 {% if not portfolio %} section-outlined__header--no-portfolio justify-content-space-between{% else %} grid-row{% endif %}">
|
||||||
{% if not portfolio %}
|
{% if not portfolio %}
|
||||||
|
@ -53,7 +73,24 @@
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% if portfolio %}
|
|
||||||
|
<!-- Non org model banner -->
|
||||||
|
{% if has_domain_renewal_flag and num_expiring_domains > 0 and not portfolio %}
|
||||||
|
<section class="usa-site-alert usa-site-alert--info margin-bottom-2 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
|
<div class="usa-alert">
|
||||||
|
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
||||||
|
<p class="usa-alert__text maxw-none">
|
||||||
|
{% if num_expiring_domains == 1%}
|
||||||
|
One domain will expire soon. Go to "Manage" to renew the domain. <a href="#" id="link-expiring-domains" class="usa-link">Show expiring domain.</a>
|
||||||
|
{% else%}
|
||||||
|
Multiple domains will expire soon. Go to "Manage" to renew the domains. <a href="#" id="link-expiring-domains" class="usa-link">Show expiring domains.</a>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<div class="display-flex flex-align-center">
|
<div class="display-flex flex-align-center">
|
||||||
<span class="margin-right-2 margin-top-neg-1 usa-prose text-base-darker">Filter by</span>
|
<span class="margin-right-2 margin-top-neg-1 usa-prose text-base-darker">Filter by</span>
|
||||||
<div class="usa-accordion usa-accordion--select margin-right-2">
|
<div class="usa-accordion usa-accordion--select margin-right-2">
|
||||||
|
@ -135,6 +172,19 @@
|
||||||
>Deleted</label
|
>Deleted</label
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
{% if has_domain_renewal_flag and num_expiring_domains > 0 %}
|
||||||
|
<div class="usa-checkbox">
|
||||||
|
<input
|
||||||
|
class="usa-checkbox__input"
|
||||||
|
id="filter-status-expiring"
|
||||||
|
type="checkbox"
|
||||||
|
name="filter-status"
|
||||||
|
value="expiring"
|
||||||
|
/>
|
||||||
|
<label class="usa-checkbox__label" for="filter-status-expiring"
|
||||||
|
>Expiring soon</label>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -149,7 +199,6 @@
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
<div class="display-none usa-table-container--scrollable margin-top-0" tabindex="0" id="domains__table-wrapper">
|
<div class="display-none usa-table-container--scrollable margin-top-0" tabindex="0" id="domains__table-wrapper">
|
||||||
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked">
|
<table class="usa-table usa-table--borderless usa-table--stacked dotgov-table dotgov-table--stacked">
|
||||||
<caption class="sr-only">Your registered domains</caption>
|
<caption class="sr-only">Your registered domains</caption>
|
||||||
|
|
|
@ -15,6 +15,6 @@
|
||||||
|
|
||||||
<div id="main-content">
|
<div id="main-content">
|
||||||
<h1 id="domains-header">Domains</h1>
|
<h1 id="domains-header">Domains</h1>
|
||||||
{% include "includes/domains_table.html" with portfolio=portfolio user_domain_count=user_domain_count %}
|
{% include "includes/domains_table.html" with portfolio=portfolio user_domain_count=user_domain_count num_expiring_domains=num_expiring_domains%}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -7,7 +7,7 @@ This file tests the various ways in which the registrar interacts with the regis
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.db.utils import IntegrityError
|
from django.db.utils import IntegrityError
|
||||||
from unittest.mock import MagicMock, patch, call
|
from unittest.mock import MagicMock, patch, call
|
||||||
import datetime
|
from datetime import datetime, date, timedelta
|
||||||
from django.utils.timezone import make_aware
|
from django.utils.timezone import make_aware
|
||||||
from api.tests.common import less_console_noise_decorator
|
from api.tests.common import less_console_noise_decorator
|
||||||
from registrar.models import Domain, Host, HostIP
|
from registrar.models import Domain, Host, HostIP
|
||||||
|
@ -2267,13 +2267,13 @@ class TestExpirationDate(MockEppLib):
|
||||||
"""assert that the setter for expiration date is not implemented and will raise error"""
|
"""assert that the setter for expiration date is not implemented and will raise error"""
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
with self.assertRaises(NotImplementedError):
|
with self.assertRaises(NotImplementedError):
|
||||||
self.domain.registry_expiration_date = datetime.date.today()
|
self.domain.registry_expiration_date = date.today()
|
||||||
|
|
||||||
def test_renew_domain(self):
|
def test_renew_domain(self):
|
||||||
"""assert that the renew_domain sets new expiration date in cache and saves to registrar"""
|
"""assert that the renew_domain sets new expiration date in cache and saves to registrar"""
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
self.domain.renew_domain()
|
self.domain.renew_domain()
|
||||||
test_date = datetime.date(2023, 5, 25)
|
test_date = date(2023, 5, 25)
|
||||||
self.assertEquals(self.domain._cache["ex_date"], test_date)
|
self.assertEquals(self.domain._cache["ex_date"], test_date)
|
||||||
self.assertEquals(self.domain.expiration_date, test_date)
|
self.assertEquals(self.domain.expiration_date, test_date)
|
||||||
|
|
||||||
|
@ -2295,18 +2295,42 @@ class TestExpirationDate(MockEppLib):
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
# to do this, need to mock value returned from timezone.now
|
# to do this, need to mock value returned from timezone.now
|
||||||
# set now to 2023-01-01
|
# set now to 2023-01-01
|
||||||
mocked_datetime = datetime.datetime(2023, 1, 1, 12, 0, 0)
|
mocked_datetime = datetime(2023, 1, 1, 12, 0, 0)
|
||||||
# force fetch_cache which sets the expiration date to 2023-05-25
|
# force fetch_cache which sets the expiration date to 2023-05-25
|
||||||
self.domain.statuses
|
self.domain.statuses
|
||||||
with patch("registrar.models.domain.timezone.now", return_value=mocked_datetime):
|
with patch("registrar.models.domain.timezone.now", return_value=mocked_datetime):
|
||||||
self.assertFalse(self.domain.is_expired())
|
self.assertFalse(self.domain.is_expired())
|
||||||
|
|
||||||
|
def test_is_expiring_within_threshold(self):
|
||||||
|
"""assert that is_expiring returns true when expiration date is within 60 days"""
|
||||||
|
with less_console_noise():
|
||||||
|
mocked_datetime = datetime(2023, 1, 1, 12, 0, 0)
|
||||||
|
expiration_date = mocked_datetime.date() + timedelta(days=30)
|
||||||
|
|
||||||
|
# set domain's expiration date
|
||||||
|
self.domain.expiration_date = expiration_date
|
||||||
|
|
||||||
|
with patch("registrar.models.domain.timezone.now", return_value=mocked_datetime):
|
||||||
|
self.assertTrue(self.domain.is_expiring())
|
||||||
|
|
||||||
|
def test_is_not_expiring_outside_threshold(self):
|
||||||
|
"""assert that is_expiring returns false when expiration date is outside 60 days"""
|
||||||
|
with less_console_noise():
|
||||||
|
mocked_datetime = datetime(2023, 1, 1, 12, 0, 0)
|
||||||
|
expiration_date = mocked_datetime.date() + timedelta(days=61)
|
||||||
|
|
||||||
|
# set domain's expiration date
|
||||||
|
self.domain.expiration_date = expiration_date
|
||||||
|
|
||||||
|
with patch("registrar.models.domain.timezone.now", return_value=mocked_datetime):
|
||||||
|
self.assertFalse(self.domain.is_expiring())
|
||||||
|
|
||||||
def test_expiration_date_updated_on_info_domain_call(self):
|
def test_expiration_date_updated_on_info_domain_call(self):
|
||||||
"""assert that expiration date in db is updated on info domain call"""
|
"""assert that expiration date in db is updated on info domain call"""
|
||||||
with less_console_noise():
|
with less_console_noise():
|
||||||
# force fetch_cache to be called
|
# force fetch_cache to be called
|
||||||
self.domain.statuses
|
self.domain.statuses
|
||||||
test_date = datetime.date(2023, 5, 25)
|
test_date = date(2023, 5, 25)
|
||||||
self.assertEquals(self.domain.expiration_date, test_date)
|
self.assertEquals(self.domain.expiration_date, test_date)
|
||||||
|
|
||||||
|
|
||||||
|
@ -2322,7 +2346,7 @@ class TestCreationDate(MockEppLib):
|
||||||
self.domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY)
|
self.domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY)
|
||||||
# creation_date returned from mockDataInfoDomain with creation date:
|
# creation_date returned from mockDataInfoDomain with creation date:
|
||||||
# cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35)
|
# cr_date=datetime.datetime(2023, 5, 25, 19, 45, 35)
|
||||||
self.creation_date = make_aware(datetime.datetime(2023, 5, 25, 19, 45, 35))
|
self.creation_date = make_aware(datetime(2023, 5, 25, 19, 45, 35))
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
Domain.objects.all().delete()
|
Domain.objects.all().delete()
|
||||||
|
@ -2331,7 +2355,7 @@ class TestCreationDate(MockEppLib):
|
||||||
def test_creation_date_setter_not_implemented(self):
|
def test_creation_date_setter_not_implemented(self):
|
||||||
"""assert that the setter for creation date is not implemented and will raise error"""
|
"""assert that the setter for creation date is not implemented and will raise error"""
|
||||||
with self.assertRaises(NotImplementedError):
|
with self.assertRaises(NotImplementedError):
|
||||||
self.domain.creation_date = datetime.date.today()
|
self.domain.creation_date = date.today()
|
||||||
|
|
||||||
def test_creation_date_updated_on_info_domain_call(self):
|
def test_creation_date_updated_on_info_domain_call(self):
|
||||||
"""assert that creation date in db is updated on info domain call"""
|
"""assert that creation date in db is updated on info domain call"""
|
||||||
|
|
|
@ -424,6 +424,112 @@ class TestDomainDetail(TestDomainOverview):
|
||||||
self.assertContains(detail_page, "invited@example.com")
|
self.assertContains(detail_page, "invited@example.com")
|
||||||
|
|
||||||
|
|
||||||
|
class TestDomainDetailDomainRenewal(TestDomainOverview):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
|
||||||
|
self.user = get_user_model().objects.create(
|
||||||
|
first_name="User",
|
||||||
|
last_name="Test",
|
||||||
|
email="bogus@example.gov",
|
||||||
|
phone="8003111234",
|
||||||
|
title="test title",
|
||||||
|
username="usertest",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.expiringdomain, _ = Domain.objects.get_or_create(
|
||||||
|
name="expiringdomain.gov",
|
||||||
|
)
|
||||||
|
|
||||||
|
UserDomainRole.objects.get_or_create(
|
||||||
|
user=self.user, domain=self.expiringdomain, role=UserDomainRole.Roles.MANAGER
|
||||||
|
)
|
||||||
|
|
||||||
|
DomainInformation.objects.get_or_create(creator=self.user, domain=self.expiringdomain)
|
||||||
|
|
||||||
|
self.portfolio, _ = Portfolio.objects.get_or_create(organization_name="Test org", creator=self.user)
|
||||||
|
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
def custom_is_expired(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def custom_is_expiring(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
@override_flag("domain_renewal", active=True)
|
||||||
|
def test_expiring_domain_on_detail_page_as_domain_manager(self):
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
with patch.object(Domain, "is_expiring", self.custom_is_expiring), patch.object(
|
||||||
|
Domain, "is_expired", self.custom_is_expired
|
||||||
|
):
|
||||||
|
self.assertEquals(self.expiringdomain.state, Domain.State.UNKNOWN)
|
||||||
|
detail_page = self.client.get(
|
||||||
|
reverse("domain", kwargs={"pk": self.expiringdomain.id}),
|
||||||
|
)
|
||||||
|
self.assertContains(detail_page, "Expiring soon")
|
||||||
|
|
||||||
|
self.assertContains(detail_page, "Renew to maintain access")
|
||||||
|
|
||||||
|
self.assertNotContains(detail_page, "DNS needed")
|
||||||
|
self.assertNotContains(detail_page, "Expired")
|
||||||
|
|
||||||
|
@override_flag("domain_renewal", active=True)
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
def test_expiring_domain_on_detail_page_in_org_model_as_a_non_domain_manager(self):
|
||||||
|
portfolio, _ = Portfolio.objects.get_or_create(organization_name="Test org", creator=self.user)
|
||||||
|
non_dom_manage_user = get_user_model().objects.create(
|
||||||
|
first_name="Non Domain",
|
||||||
|
last_name="Manager",
|
||||||
|
email="verybogus@example.gov",
|
||||||
|
phone="8003111234",
|
||||||
|
title="test title again",
|
||||||
|
username="nondomain",
|
||||||
|
)
|
||||||
|
|
||||||
|
non_dom_manage_user.save()
|
||||||
|
UserPortfolioPermission.objects.get_or_create(
|
||||||
|
user=non_dom_manage_user,
|
||||||
|
portfolio=portfolio,
|
||||||
|
roles=[UserPortfolioRoleChoices.ORGANIZATION_MEMBER],
|
||||||
|
additional_permissions=[
|
||||||
|
UserPortfolioPermissionChoices.VIEW_ALL_DOMAINS,
|
||||||
|
],
|
||||||
|
)
|
||||||
|
expiringdomain2, _ = Domain.objects.get_or_create(name="bogusdomain2.gov")
|
||||||
|
DomainInformation.objects.get_or_create(
|
||||||
|
creator=non_dom_manage_user, domain=expiringdomain2, portfolio=self.portfolio
|
||||||
|
)
|
||||||
|
non_dom_manage_user.refresh_from_db()
|
||||||
|
self.client.force_login(non_dom_manage_user)
|
||||||
|
with patch.object(Domain, "is_expiring", self.custom_is_expiring), patch.object(
|
||||||
|
Domain, "is_expired", self.custom_is_expired
|
||||||
|
):
|
||||||
|
detail_page = self.client.get(
|
||||||
|
reverse("domain", kwargs={"pk": expiringdomain2.id}),
|
||||||
|
)
|
||||||
|
self.assertContains(detail_page, "Contact one of the listed domain managers to renew the domain.")
|
||||||
|
|
||||||
|
@override_flag("domain_renewal", active=True)
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
def test_expiring_domain_on_detail_page_in_org_model_as_a_domain_manager(self):
|
||||||
|
portfolio, _ = Portfolio.objects.get_or_create(organization_name="Test org2", creator=self.user)
|
||||||
|
|
||||||
|
expiringdomain3, _ = Domain.objects.get_or_create(name="bogusdomain3.gov")
|
||||||
|
|
||||||
|
UserDomainRole.objects.get_or_create(user=self.user, domain=expiringdomain3, role=UserDomainRole.Roles.MANAGER)
|
||||||
|
DomainInformation.objects.get_or_create(creator=self.user, domain=expiringdomain3, portfolio=portfolio)
|
||||||
|
self.user.refresh_from_db()
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
with patch.object(Domain, "is_expiring", self.custom_is_expiring), patch.object(
|
||||||
|
Domain, "is_expired", self.custom_is_expired
|
||||||
|
):
|
||||||
|
detail_page = self.client.get(
|
||||||
|
reverse("domain", kwargs={"pk": expiringdomain3.id}),
|
||||||
|
)
|
||||||
|
self.assertContains(detail_page, "Renew to maintain access")
|
||||||
|
|
||||||
|
|
||||||
class TestDomainManagers(TestDomainOverview):
|
class TestDomainManagers(TestDomainOverview):
|
||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
|
@ -2348,3 +2454,125 @@ class TestDomainChangeNotifications(TestDomainOverview):
|
||||||
|
|
||||||
# Check that an email was not sent
|
# Check that an email was not sent
|
||||||
self.assertFalse(self.mock_client.send_email.called)
|
self.assertFalse(self.mock_client.send_email.called)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDomainRenewal(TestWithUser):
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
today = datetime.now()
|
||||||
|
expiring_date = (today + timedelta(days=30)).strftime("%Y-%m-%d")
|
||||||
|
expiring_date_current = (today + timedelta(days=70)).strftime("%Y-%m-%d")
|
||||||
|
expired_date = (today - timedelta(days=30)).strftime("%Y-%m-%d")
|
||||||
|
|
||||||
|
self.domain_with_expiring_soon_date, _ = Domain.objects.get_or_create(
|
||||||
|
name="igorville.gov", expiration_date=expiring_date
|
||||||
|
)
|
||||||
|
self.domain_with_expired_date, _ = Domain.objects.get_or_create(
|
||||||
|
name="domainwithexpireddate.com", expiration_date=expired_date
|
||||||
|
)
|
||||||
|
|
||||||
|
self.domain_with_current_date, _ = Domain.objects.get_or_create(
|
||||||
|
name="domainwithfarexpireddate.com", expiration_date=expiring_date_current
|
||||||
|
)
|
||||||
|
|
||||||
|
UserDomainRole.objects.get_or_create(
|
||||||
|
user=self.user, domain=self.domain_with_current_date, role=UserDomainRole.Roles.MANAGER
|
||||||
|
)
|
||||||
|
|
||||||
|
UserDomainRole.objects.get_or_create(
|
||||||
|
user=self.user, domain=self.domain_with_expired_date, role=UserDomainRole.Roles.MANAGER
|
||||||
|
)
|
||||||
|
|
||||||
|
UserDomainRole.objects.get_or_create(
|
||||||
|
user=self.user, domain=self.domain_with_expiring_soon_date, role=UserDomainRole.Roles.MANAGER
|
||||||
|
)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
try:
|
||||||
|
UserDomainRole.objects.all().delete()
|
||||||
|
Domain.objects.all().delete()
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
super().tearDown()
|
||||||
|
|
||||||
|
# Remove test_without_domain_renewal_flag when domain renewal is released as a feature
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("domain_renewal", active=False)
|
||||||
|
def test_without_domain_renewal_flag(self):
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
domains_page = self.client.get("/")
|
||||||
|
self.assertNotContains(domains_page, "will expire soon")
|
||||||
|
self.assertNotContains(domains_page, "Expiring soon")
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("domain_renewal", active=True)
|
||||||
|
def test_domain_renewal_flag_single_domain(self):
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
domains_page = self.client.get("/")
|
||||||
|
self.assertContains(domains_page, "One domain will expire soon")
|
||||||
|
self.assertContains(domains_page, "Expiring soon")
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("domain_renewal", active=True)
|
||||||
|
def test_with_domain_renewal_flag_mulitple_domains(self):
|
||||||
|
today = datetime.now()
|
||||||
|
expiring_date = (today + timedelta(days=30)).strftime("%Y-%m-%d")
|
||||||
|
self.domain_with_another_expiring, _ = Domain.objects.get_or_create(
|
||||||
|
name="domainwithanotherexpiringdate.com", expiration_date=expiring_date
|
||||||
|
)
|
||||||
|
|
||||||
|
UserDomainRole.objects.get_or_create(
|
||||||
|
user=self.user, domain=self.domain_with_another_expiring, role=UserDomainRole.Roles.MANAGER
|
||||||
|
)
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
domains_page = self.client.get("/")
|
||||||
|
self.assertContains(domains_page, "Multiple domains will expire soon")
|
||||||
|
self.assertContains(domains_page, "Expiring soon")
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("domain_renewal", active=True)
|
||||||
|
def test_with_domain_renewal_flag_no_expiring_domains(self):
|
||||||
|
UserDomainRole.objects.filter(user=self.user, domain=self.domain_with_expired_date).delete()
|
||||||
|
UserDomainRole.objects.filter(user=self.user, domain=self.domain_with_expiring_soon_date).delete()
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
domains_page = self.client.get("/")
|
||||||
|
self.assertNotContains(domains_page, "Expiring soon")
|
||||||
|
self.assertNotContains(domains_page, "will expire soon")
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("domain_renewal", active=True)
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
def test_domain_renewal_flag_single_domain_w_org_feature_flag(self):
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
domains_page = self.client.get("/")
|
||||||
|
self.assertContains(domains_page, "One domain will expire soon")
|
||||||
|
self.assertContains(domains_page, "Expiring soon")
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("domain_renewal", active=True)
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
def test_with_domain_renewal_flag_mulitple_domains_w_org_feature_flag(self):
|
||||||
|
today = datetime.now()
|
||||||
|
expiring_date = (today + timedelta(days=31)).strftime("%Y-%m-%d")
|
||||||
|
self.domain_with_another_expiring_org_model, _ = Domain.objects.get_or_create(
|
||||||
|
name="domainwithanotherexpiringdate_orgmodel.com", expiration_date=expiring_date
|
||||||
|
)
|
||||||
|
|
||||||
|
UserDomainRole.objects.get_or_create(
|
||||||
|
user=self.user, domain=self.domain_with_another_expiring_org_model, role=UserDomainRole.Roles.MANAGER
|
||||||
|
)
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
domains_page = self.client.get("/")
|
||||||
|
self.assertContains(domains_page, "Multiple domains will expire soon")
|
||||||
|
self.assertContains(domains_page, "Expiring soon")
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("domain_renewal", active=True)
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
def test_with_domain_renewal_flag_no_expiring_domains_w_org_feature_flag(self):
|
||||||
|
UserDomainRole.objects.filter(user=self.user, domain=self.domain_with_expired_date).delete()
|
||||||
|
UserDomainRole.objects.filter(user=self.user, domain=self.domain_with_expiring_soon_date).delete()
|
||||||
|
self.client.force_login(self.user)
|
||||||
|
domains_page = self.client.get("/")
|
||||||
|
self.assertNotContains(domains_page, "Expiring soon")
|
||||||
|
self.assertNotContains(domains_page, "will expire soon")
|
||||||
|
|
|
@ -8,24 +8,34 @@ from django_webtest import WebTest # type: ignore
|
||||||
from django.utils.dateparse import parse_date
|
from django.utils.dateparse import parse_date
|
||||||
from api.tests.common import less_console_noise_decorator
|
from api.tests.common import less_console_noise_decorator
|
||||||
from waffle.testutils import override_flag
|
from waffle.testutils import override_flag
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
|
||||||
class GetDomainsJsonTest(TestWithUser, WebTest):
|
class GetDomainsJsonTest(TestWithUser, WebTest):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.app.set_user(self.user.username)
|
self.app.set_user(self.user.username)
|
||||||
|
today = datetime.now()
|
||||||
|
expiring_date = (today + timedelta(days=30)).strftime("%Y-%m-%d")
|
||||||
|
expiring_date_2 = (today + timedelta(days=31)).strftime("%Y-%m-%d")
|
||||||
|
|
||||||
# Create test domains
|
# Create test domains
|
||||||
self.domain1 = Domain.objects.create(name="example1.com", expiration_date="2024-01-01", state="unknown")
|
self.domain1 = Domain.objects.create(name="example1.com", expiration_date="2024-01-01", state="unknown")
|
||||||
self.domain2 = Domain.objects.create(name="example2.com", expiration_date="2024-02-01", state="dns needed")
|
self.domain2 = Domain.objects.create(name="example2.com", expiration_date="2024-02-01", state="dns needed")
|
||||||
self.domain3 = Domain.objects.create(name="example3.com", expiration_date="2024-03-01", state="ready")
|
self.domain3 = Domain.objects.create(name="example3.com", expiration_date="2024-03-01", state="ready")
|
||||||
self.domain4 = Domain.objects.create(name="example4.com", expiration_date="2024-03-01", state="ready")
|
self.domain4 = Domain.objects.create(name="example4.com", expiration_date="2024-03-01", state="ready")
|
||||||
|
self.domain5 = Domain.objects.create(name="example5.com", expiration_date=expiring_date, state="expiring soon")
|
||||||
|
self.domain6 = Domain.objects.create(
|
||||||
|
name="example6.com", expiration_date=expiring_date_2, state="expiring soon"
|
||||||
|
)
|
||||||
# Create UserDomainRoles
|
# Create UserDomainRoles
|
||||||
UserDomainRole.objects.create(user=self.user, domain=self.domain1)
|
UserDomainRole.objects.create(user=self.user, domain=self.domain1)
|
||||||
UserDomainRole.objects.create(user=self.user, domain=self.domain2)
|
UserDomainRole.objects.create(user=self.user, domain=self.domain2)
|
||||||
UserDomainRole.objects.create(user=self.user, domain=self.domain3)
|
UserDomainRole.objects.create(user=self.user, domain=self.domain3)
|
||||||
|
|
||||||
|
UserDomainRole.objects.create(user=self.user, domain=self.domain5)
|
||||||
|
UserDomainRole.objects.create(user=self.user, domain=self.domain6)
|
||||||
|
|
||||||
# Create Portfolio
|
# Create Portfolio
|
||||||
self.portfolio = Portfolio.objects.create(creator=self.user, organization_name="Example org")
|
self.portfolio = Portfolio.objects.create(creator=self.user, organization_name="Example org")
|
||||||
|
|
||||||
|
@ -63,7 +73,7 @@ class GetDomainsJsonTest(TestWithUser, WebTest):
|
||||||
self.assertEqual(data["num_pages"], 1)
|
self.assertEqual(data["num_pages"], 1)
|
||||||
|
|
||||||
# Check the number of domains
|
# Check the number of domains
|
||||||
self.assertEqual(len(data["domains"]), 3)
|
self.assertEqual(len(data["domains"]), 5)
|
||||||
|
|
||||||
# Expected domains
|
# Expected domains
|
||||||
expected_domains = [self.domain1, self.domain2, self.domain3]
|
expected_domains = [self.domain1, self.domain2, self.domain3]
|
||||||
|
@ -310,7 +320,7 @@ class GetDomainsJsonTest(TestWithUser, WebTest):
|
||||||
self.assertFalse(data["has_previous"])
|
self.assertFalse(data["has_previous"])
|
||||||
self.assertEqual(data["num_pages"], 1)
|
self.assertEqual(data["num_pages"], 1)
|
||||||
self.assertEqual(data["total"], 1)
|
self.assertEqual(data["total"], 1)
|
||||||
self.assertEqual(data["unfiltered_total"], 3)
|
self.assertEqual(data["unfiltered_total"], 5)
|
||||||
|
|
||||||
# Check the number of domain requests
|
# Check the number of domain requests
|
||||||
self.assertEqual(len(data["domains"]), 1)
|
self.assertEqual(len(data["domains"]), 1)
|
||||||
|
@ -377,14 +387,15 @@ class GetDomainsJsonTest(TestWithUser, WebTest):
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_state_filtering(self):
|
def test_state_filtering(self):
|
||||||
"""Test that different states in request get expected responses."""
|
"""Test that different states in request get expected responses."""
|
||||||
|
|
||||||
expected_values = [
|
expected_values = [
|
||||||
("unknown", 1),
|
("unknown", 1),
|
||||||
("ready", 0),
|
("ready", 0),
|
||||||
("expired", 2),
|
("expired", 2),
|
||||||
("ready,expired", 2),
|
("ready,expired", 2),
|
||||||
("unknown,expired", 3),
|
("unknown,expired", 3),
|
||||||
|
("expiring", 2),
|
||||||
]
|
]
|
||||||
|
|
||||||
for state, num_domains in expected_values:
|
for state, num_domains in expected_values:
|
||||||
with self.subTest(state=state, num_domains=num_domains):
|
with self.subTest(state=state, num_domains=num_domains):
|
||||||
response = self.app.get(reverse("get_domains_json"), {"status": state})
|
response = self.app.get(reverse("get_domains_json"), {"status": state})
|
||||||
|
|
|
@ -27,7 +27,7 @@ def get_domains_json(request):
|
||||||
page_number = request.GET.get("page")
|
page_number = request.GET.get("page")
|
||||||
page_obj = paginator.get_page(page_number)
|
page_obj = paginator.get_page(page_number)
|
||||||
|
|
||||||
domains = [serialize_domain(domain, request.user) for domain in page_obj.object_list]
|
domains = [serialize_domain(domain, request) for domain in page_obj.object_list]
|
||||||
|
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{
|
{
|
||||||
|
@ -80,21 +80,27 @@ def apply_state_filter(queryset, request):
|
||||||
status_list.append("dns needed")
|
status_list.append("dns needed")
|
||||||
# Split the status list into normal states and custom states
|
# Split the status list into normal states and custom states
|
||||||
normal_states = [state for state in status_list if state in Domain.State.values]
|
normal_states = [state for state in status_list if state in Domain.State.values]
|
||||||
custom_states = [state for state in status_list if state == "expired"]
|
custom_states = [state for state in status_list if (state == "expired" or state == "expiring")]
|
||||||
# Construct Q objects for normal states that can be queried through ORM
|
# Construct Q objects for normal states that can be queried through ORM
|
||||||
state_query = Q()
|
state_query = Q()
|
||||||
if normal_states:
|
if normal_states:
|
||||||
state_query |= Q(state__in=normal_states)
|
state_query |= Q(state__in=normal_states)
|
||||||
# Handle custom states in Python, as expired can not be queried through ORM
|
# Handle custom states in Python, as expired can not be queried through ORM
|
||||||
if "expired" in custom_states:
|
if "expired" in custom_states:
|
||||||
expired_domain_ids = [domain.id for domain in queryset if domain.state_display() == "Expired"]
|
expired_domain_ids = [domain.id for domain in queryset if domain.state_display(request) == "Expired"]
|
||||||
state_query |= Q(id__in=expired_domain_ids)
|
state_query |= Q(id__in=expired_domain_ids)
|
||||||
|
if "expiring" in custom_states:
|
||||||
|
expiring_domain_ids = [domain.id for domain in queryset if domain.state_display(request) == "Expiring soon"]
|
||||||
|
state_query |= Q(id__in=expiring_domain_ids)
|
||||||
# Apply the combined query
|
# Apply the combined query
|
||||||
queryset = queryset.filter(state_query)
|
queryset = queryset.filter(state_query)
|
||||||
# If there are filtered states, and expired is not one of them, domains with
|
# If there are filtered states, and expired is not one of them, domains with
|
||||||
# state_display of 'Expired' must be removed
|
# state_display of 'Expired' must be removed
|
||||||
if "expired" not in custom_states:
|
if "expired" not in custom_states:
|
||||||
expired_domain_ids = [domain.id for domain in queryset if domain.state_display() == "Expired"]
|
expired_domain_ids = [domain.id for domain in queryset if domain.state_display(request) == "Expired"]
|
||||||
|
queryset = queryset.exclude(id__in=expired_domain_ids)
|
||||||
|
if "expiring" not in custom_states:
|
||||||
|
expired_domain_ids = [domain.id for domain in queryset if domain.state_display(request) == "Expiring soon"]
|
||||||
queryset = queryset.exclude(id__in=expired_domain_ids)
|
queryset = queryset.exclude(id__in=expired_domain_ids)
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
@ -105,7 +111,7 @@ def apply_sorting(queryset, request):
|
||||||
order = request.GET.get("order", "asc")
|
order = request.GET.get("order", "asc")
|
||||||
if sort_by == "state_display":
|
if sort_by == "state_display":
|
||||||
objects = list(queryset)
|
objects = list(queryset)
|
||||||
objects.sort(key=lambda domain: domain.state_display(), reverse=(order == "desc"))
|
objects.sort(key=lambda domain: domain.state_display(request), reverse=(order == "desc"))
|
||||||
return objects
|
return objects
|
||||||
else:
|
else:
|
||||||
if order == "desc":
|
if order == "desc":
|
||||||
|
@ -113,7 +119,8 @@ def apply_sorting(queryset, request):
|
||||||
return queryset.order_by(sort_by)
|
return queryset.order_by(sort_by)
|
||||||
|
|
||||||
|
|
||||||
def serialize_domain(domain, user):
|
def serialize_domain(domain, request):
|
||||||
|
user = request.user
|
||||||
suborganization_name = None
|
suborganization_name = None
|
||||||
try:
|
try:
|
||||||
domain_info = domain.domain_info
|
domain_info = domain.domain_info
|
||||||
|
@ -133,7 +140,7 @@ def serialize_domain(domain, user):
|
||||||
"name": domain.name,
|
"name": domain.name,
|
||||||
"expiration_date": domain.expiration_date,
|
"expiration_date": domain.expiration_date,
|
||||||
"state": domain.state,
|
"state": domain.state,
|
||||||
"state_display": domain.state_display(),
|
"state_display": domain.state_display(request),
|
||||||
"get_state_help_text": domain.get_state_help_text(),
|
"get_state_help_text": domain.get_state_help_text(),
|
||||||
"action_url": reverse("domain", kwargs={"pk": domain.id}),
|
"action_url": reverse("domain", kwargs={"pk": domain.id}),
|
||||||
"action_label": ("View" if view_only else "Manage"),
|
"action_label": ("View" if view_only else "Manage"),
|
||||||
|
|
|
@ -8,5 +8,6 @@ def index(request):
|
||||||
if request and request.user and request.user.is_authenticated:
|
if request and request.user and request.user.is_authenticated:
|
||||||
# This controls the creation of a new domain request in the wizard
|
# This controls the creation of a new domain request in the wizard
|
||||||
context["user_domain_count"] = request.user.get_user_domain_ids(request).count()
|
context["user_domain_count"] = request.user.get_user_domain_ids(request).count()
|
||||||
|
context["num_expiring_domains"] = request.user.get_num_expiring_domains(request)
|
||||||
|
|
||||||
return render(request, "home.html", context)
|
return render(request, "home.html", context)
|
||||||
|
|
|
@ -39,6 +39,8 @@ class PortfolioDomainsView(PortfolioDomainsPermissionView, View):
|
||||||
context = {}
|
context = {}
|
||||||
if self.request and self.request.user and self.request.user.is_authenticated:
|
if self.request and self.request.user and self.request.user.is_authenticated:
|
||||||
context["user_domain_count"] = self.request.user.get_user_domain_ids(request).count()
|
context["user_domain_count"] = self.request.user.get_user_domain_ids(request).count()
|
||||||
|
context["num_expiring_domains"] = request.user.get_num_expiring_domains(request)
|
||||||
|
|
||||||
return render(request, "portfolio_domains.html", context)
|
return render(request, "portfolio_domains.html", context)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue