mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-13 04:59:59 +02:00
merge main
This commit is contained in:
commit
d2f1d1f5a8
66 changed files with 1204 additions and 852 deletions
|
@ -629,6 +629,51 @@ export function initRejectedEmail() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A function that handles the suborganzation and requested suborganization fields and buttons.
|
||||||
|
* - Fieldwise: Hooks to the sub_organization, suborganization_city, and suborganization_state_territory fields.
|
||||||
|
* On change, this function checks if any of these fields are not empty:
|
||||||
|
* sub_organization, suborganization_city, and suborganization_state_territory.
|
||||||
|
* If they aren't, then we show the "clear" button. If they are, then we hide it because we don't need it.
|
||||||
|
*
|
||||||
|
* - Buttonwise: Hooks to the #clear-requested-suborganization button.
|
||||||
|
* On click, this will clear the input value of sub_organization, suborganization_city, and suborganization_state_territory.
|
||||||
|
*/
|
||||||
|
function handleSuborgFieldsAndButtons() {
|
||||||
|
const requestedSuborganizationField = document.getElementById("id_requested_suborganization");
|
||||||
|
const suborganizationCity = document.getElementById("id_suborganization_city");
|
||||||
|
const suborganizationStateTerritory = document.getElementById("id_suborganization_state_territory");
|
||||||
|
const rejectButton = document.querySelector("#clear-requested-suborganization");
|
||||||
|
|
||||||
|
// Ensure that every variable is present before proceeding
|
||||||
|
if (!requestedSuborganizationField || !suborganizationCity || !suborganizationStateTerritory || !rejectButton) {
|
||||||
|
console.warn("handleSuborganizationSelection() => Could not find required fields.")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRejectButtonVisibility() {
|
||||||
|
if (requestedSuborganizationField.value || suborganizationCity.value || suborganizationStateTerritory.value) {
|
||||||
|
showElement(rejectButton);
|
||||||
|
}else {
|
||||||
|
hideElement(rejectButton)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleRejectButton() {
|
||||||
|
// Clear the text fields
|
||||||
|
requestedSuborganizationField.value = "";
|
||||||
|
suborganizationCity.value = "";
|
||||||
|
suborganizationStateTerritory.value = "";
|
||||||
|
// Update button visibility after clearing
|
||||||
|
handleRejectButtonVisibility();
|
||||||
|
}
|
||||||
|
rejectButton.addEventListener("click", handleRejectButton)
|
||||||
|
requestedSuborganizationField.addEventListener("blur", handleRejectButtonVisibility);
|
||||||
|
suborganizationCity.addEventListener("blur", handleRejectButtonVisibility);
|
||||||
|
suborganizationStateTerritory.addEventListener("change", handleRejectButtonVisibility);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A function for dynamic DomainRequest fields
|
* A function for dynamic DomainRequest fields
|
||||||
*/
|
*/
|
||||||
|
@ -636,5 +681,6 @@ export function initDynamicDomainRequestFields(){
|
||||||
const domainRequestPage = document.getElementById("domainrequest_form");
|
const domainRequestPage = document.getElementById("domainrequest_form");
|
||||||
if (domainRequestPage) {
|
if (domainRequestPage) {
|
||||||
handlePortfolioSelection();
|
handlePortfolioSelection();
|
||||||
|
handleSuborgFieldsAndButtons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,13 @@ export function handlePortfolioSelection(
|
||||||
const portfolioUrbanizationField = document.querySelector(".field-portfolio_urbanization");
|
const portfolioUrbanizationField = document.querySelector(".field-portfolio_urbanization");
|
||||||
const portfolioUrbanization = portfolioUrbanizationField.querySelector(".readonly");
|
const portfolioUrbanization = portfolioUrbanizationField.querySelector(".readonly");
|
||||||
const portfolioJsonUrl = document.getElementById("portfolio_json_url")?.value || null;
|
const portfolioJsonUrl = document.getElementById("portfolio_json_url")?.value || null;
|
||||||
|
// These requested suborganization fields only exist on the domain request page
|
||||||
|
const rejectSuborganizationButton = document.querySelector("#clear-requested-suborganization");
|
||||||
|
const requestedSuborganizationFieldInput = document.getElementById("id_requested_suborganization");
|
||||||
|
const suborganizationCityInput = document.getElementById("id_suborganization_city");
|
||||||
|
const suborganizationStateTerritoryInput = document.getElementById("id_suborganization_state_territory");
|
||||||
|
|
||||||
|
// Global var to track page load
|
||||||
let isPageLoading = true;
|
let isPageLoading = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -469,11 +476,28 @@ export function handlePortfolioSelection(
|
||||||
if (requestedSuborganizationField) showElement(requestedSuborganizationField);
|
if (requestedSuborganizationField) showElement(requestedSuborganizationField);
|
||||||
if (suborganizationCity) showElement(suborganizationCity);
|
if (suborganizationCity) showElement(suborganizationCity);
|
||||||
if (suborganizationStateTerritory) showElement(suborganizationStateTerritory);
|
if (suborganizationStateTerritory) showElement(suborganizationStateTerritory);
|
||||||
|
|
||||||
|
// == LOGIC FOR THE DOMAIN REQUEST PAGE == //
|
||||||
|
// Handle rejectSuborganizationButton (display of the clear requested suborg button).
|
||||||
|
// Basically, this button should only be visible when we have data for suborg, city, and state_territory.
|
||||||
|
// The function handleSuborgFieldsAndButtons() in domain-request-form.js handles doing this same logic
|
||||||
|
// but on field input for city, state_territory, and the suborg field.
|
||||||
|
// If it doesn't exist, don't do anything.
|
||||||
|
if (rejectSuborganizationButton){
|
||||||
|
if (requestedSuborganizationFieldInput?.value || suborganizationCityInput?.value || suborganizationStateTerritoryInput?.value) {
|
||||||
|
showElement(rejectSuborganizationButton);
|
||||||
|
}else {
|
||||||
|
hideElement(rejectSuborganizationButton);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Hide suborganization request fields if suborganization is selected
|
// Hide suborganization request fields if suborganization is selected
|
||||||
if (requestedSuborganizationField) hideElement(requestedSuborganizationField);
|
if (requestedSuborganizationField) hideElement(requestedSuborganizationField);
|
||||||
if (suborganizationCity) hideElement(suborganizationCity);
|
if (suborganizationCity) hideElement(suborganizationCity);
|
||||||
if (suborganizationStateTerritory) hideElement(suborganizationStateTerritory);
|
if (suborganizationStateTerritory) hideElement(suborganizationStateTerritory);
|
||||||
|
|
||||||
|
// == LOGIC FOR THE DOMAIN REQUEST PAGE == //
|
||||||
|
if (rejectSuborganizationButton) hideElement(rejectSuborganizationButton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -176,7 +176,16 @@ html[data-theme="dark"] {
|
||||||
color: var(--primary-fg);
|
color: var(--primary-fg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset the USWDS styles for alerts
|
||||||
|
@include at-media(desktop) {
|
||||||
|
.dashboard .usa-alert__body--widescreen {
|
||||||
|
padding-left: 4rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard .usa-alert__body--widescreen::before {
|
||||||
|
left: 1.5rem !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#branding h1,
|
#branding h1,
|
||||||
h1, h2, h3,
|
h1, h2, h3,
|
||||||
|
|
|
@ -1,21 +1,18 @@
|
||||||
@use "uswds-core" as *;
|
@use "uswds-core" as *;
|
||||||
@use "base" as *;
|
@use "base" as *;
|
||||||
|
|
||||||
// Fixes some font size disparities with the Figma
|
|
||||||
// for usa-alert alert elements
|
|
||||||
.usa-alert {
|
|
||||||
.usa-alert__heading.larger-font-sizing {
|
|
||||||
font-size: units(3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.usa-alert__text.measure-none {
|
/*----------------
|
||||||
max-width: measure(none);
|
Alert Layout
|
||||||
}
|
-----------------*/
|
||||||
|
|
||||||
// The icon was off center for some reason
|
// The icon was off center for some reason
|
||||||
// Fixes that issue
|
// Fixes that issue
|
||||||
@media (min-width: 64em){
|
@include at-media(desktop) {
|
||||||
|
// NOTE: !important is used because _font.scss overrides this
|
||||||
|
.usa-alert__body {
|
||||||
|
max-width: $widescreen-max-width !important;
|
||||||
|
}
|
||||||
.usa-alert--warning{
|
.usa-alert--warning{
|
||||||
.usa-alert__body::before {
|
.usa-alert__body::before {
|
||||||
left: 1rem !important;
|
left: 1rem !important;
|
||||||
|
@ -24,13 +21,29 @@
|
||||||
.usa-alert__body.margin-left-1 {
|
.usa-alert__body.margin-left-1 {
|
||||||
margin-left: 0.5rem!important;
|
margin-left: 0.5rem!important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.usa-alert__body--widescreen::before {
|
||||||
|
left: 4rem !important;
|
||||||
|
}
|
||||||
|
.usa-alert__body--widescreen {
|
||||||
|
padding-left: 7rem!important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: !important is used because _font.scss overrides this
|
/*----------------
|
||||||
.usa-alert__body--widescreen {
|
Alert Fonts
|
||||||
max-width: $widescreen-max-width !important;
|
-----------------*/
|
||||||
|
// Fixes some font size disparities with the Figma
|
||||||
|
// for usa-alert alert elements
|
||||||
|
.usa-alert {
|
||||||
|
.usa-alert__heading.larger-font-sizing {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*----------------
|
||||||
|
Alert Coloring
|
||||||
|
-----------------*/
|
||||||
.usa-site-alert--hot-pink {
|
.usa-site-alert--hot-pink {
|
||||||
.usa-alert {
|
.usa-alert {
|
||||||
background-color: $hot-pink;
|
background-color: $hot-pink;
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
@use "cisa_colors" as *;
|
@use "cisa_colors" as *;
|
||||||
|
|
||||||
$widescreen-max-width: 1920px;
|
$widescreen-max-width: 1920px;
|
||||||
|
$widescreen-x-padding: 4.5rem;
|
||||||
|
|
||||||
$hot-pink: #FFC3F9;
|
$hot-pink: #FFC3F9;
|
||||||
|
|
||||||
/* Styles for making visible to screen reader / AT users only. */
|
/* Styles for making visible to screen reader / AT users only. */
|
||||||
|
@ -253,6 +255,15 @@ abbr[title] {
|
||||||
max-width: $widescreen-max-width;
|
max-width: $widescreen-max-width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is used in cases where we want to align content to widescreen margins
|
||||||
|
// but we don't want the content itself to have widescreen widths
|
||||||
|
@include at-media(desktop) {
|
||||||
|
.padding-x--widescreen {
|
||||||
|
padding-left: $widescreen-x-padding !important;
|
||||||
|
padding-right: $widescreen-x-padding !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.margin-right-neg-4px {
|
.margin-right-neg-4px {
|
||||||
margin-right: -4px;
|
margin-right: -4px;
|
||||||
}
|
}
|
||||||
|
@ -267,3 +278,7 @@ abbr[title] {
|
||||||
height: 1.5em;
|
height: 1.5em;
|
||||||
width: 1.5em;
|
width: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.maxw-fit-content {
|
||||||
|
max-width: fit-content;
|
||||||
|
}
|
|
@ -6,3 +6,21 @@
|
||||||
.usa-identifier__container--widescreen {
|
.usa-identifier__container--widescreen {
|
||||||
max-width: $widescreen-max-width !important;
|
max-width: $widescreen-max-width !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE: !important is used because we are overriding default
|
||||||
|
// USWDS paddings in a few locations
|
||||||
|
@include at-media(desktop) {
|
||||||
|
.grid-container--widescreen {
|
||||||
|
padding-left: $widescreen-x-padding !important;
|
||||||
|
padding-right: $widescreen-x-padding !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// matches max-width to equal the max-width of .grid-container
|
||||||
|
// used to trick the eye into thinking we have left-aligned a
|
||||||
|
// regular grid-container within a widescreen (see instances
|
||||||
|
// where is_widescreen_centered is used in the html).
|
||||||
|
.max-width--grid-container {
|
||||||
|
max-width: 960px;
|
||||||
|
}
|
|
@ -110,8 +110,8 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.usa-nav__secondary {
|
.usa-nav__secondary {
|
||||||
// I don't know why USWDS has this at 2 rem, which puts it out of alignment
|
right: 1rem;
|
||||||
right: 3rem;
|
padding-right: $widescreen-x-padding;
|
||||||
color: color('white');
|
color: color('white');
|
||||||
bottom: 4.3rem;
|
bottom: 4.3rem;
|
||||||
.usa-nav-link,
|
.usa-nav-link,
|
||||||
|
|
|
@ -251,7 +251,7 @@ TEMPLATES = [
|
||||||
"registrar.context_processors.org_user_status",
|
"registrar.context_processors.org_user_status",
|
||||||
"registrar.context_processors.add_path_to_context",
|
"registrar.context_processors.add_path_to_context",
|
||||||
"registrar.context_processors.portfolio_permissions",
|
"registrar.context_processors.portfolio_permissions",
|
||||||
"registrar.context_processors.is_widescreen_mode",
|
"registrar.context_processors.is_widescreen_centered",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -109,31 +109,21 @@ def portfolio_permissions(request):
|
||||||
return portfolio_context
|
return portfolio_context
|
||||||
|
|
||||||
|
|
||||||
def is_widescreen_mode(request):
|
def is_widescreen_centered(request):
|
||||||
widescreen_paths = [] # If this list is meant to include specific paths, populate it.
|
include_paths = [
|
||||||
portfolio_widescreen_paths = [
|
|
||||||
"/domains/",
|
"/domains/",
|
||||||
"/requests/",
|
"/requests/",
|
||||||
"/request/",
|
"/members/",
|
||||||
"/no-organization-requests/",
|
|
||||||
"/no-organization-domains/",
|
|
||||||
"/domain-request/",
|
|
||||||
]
|
]
|
||||||
# widescreen_paths can be a bear as it trickles down sub-urls. exclude_paths gives us a way out.
|
|
||||||
exclude_paths = [
|
exclude_paths = [
|
||||||
"/domains/edit",
|
"/domains/edit",
|
||||||
|
"members/new-member/",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Check if the current path matches a widescreen path or the root path.
|
is_excluded = any(exclude_path in request.path for exclude_path in exclude_paths)
|
||||||
is_widescreen = any(path in request.path for path in widescreen_paths) or request.path == "/"
|
|
||||||
|
|
||||||
# Check if the user is an organization user and the path matches portfolio paths.
|
# Check if the current path matches a path in included_paths or the root path.
|
||||||
is_portfolio_widescreen = (
|
is_widescreen_centered = any(path in request.path for path in include_paths) or request.path == "/"
|
||||||
hasattr(request.user, "is_org_user")
|
|
||||||
and request.user.is_org_user(request)
|
|
||||||
and any(path in request.path for path in portfolio_widescreen_paths)
|
|
||||||
and not any(exclude_path in request.path for exclude_path in exclude_paths)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Return a dictionary with the widescreen mode status.
|
# Return a dictionary with the widescreen mode status.
|
||||||
return {"is_widescreen_mode": is_widescreen or is_portfolio_widescreen}
|
return {"is_widescreen_centered": is_widescreen_centered and not is_excluded}
|
||||||
|
|
|
@ -17,6 +17,7 @@ from registrar.models import Contact, DomainRequest, DraftDomain, Domain, Federa
|
||||||
from registrar.templatetags.url_helpers import public_site_url
|
from registrar.templatetags.url_helpers import public_site_url
|
||||||
from registrar.utility.enums import ValidationReturnType
|
from registrar.utility.enums import ValidationReturnType
|
||||||
from registrar.utility.constants import BranchChoices
|
from registrar.utility.constants import BranchChoices
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -78,6 +79,20 @@ class RequestingEntityForm(RegistrarForm):
|
||||||
# Otherwise just return the suborg as normal
|
# Otherwise just return the suborg as normal
|
||||||
return self.cleaned_data.get("sub_organization")
|
return self.cleaned_data.get("sub_organization")
|
||||||
|
|
||||||
|
def clean_requested_suborganization(self):
|
||||||
|
name = self.cleaned_data.get("requested_suborganization")
|
||||||
|
if (
|
||||||
|
name
|
||||||
|
and Suborganization.objects.filter(
|
||||||
|
name__iexact=name, portfolio=self.domain_request.portfolio, name__isnull=False, portfolio__isnull=False
|
||||||
|
).exists()
|
||||||
|
):
|
||||||
|
raise ValidationError(
|
||||||
|
"This suborganization already exists. "
|
||||||
|
"Choose a new name, or select it directly if you would like to use it."
|
||||||
|
)
|
||||||
|
return name
|
||||||
|
|
||||||
def full_clean(self):
|
def full_clean(self):
|
||||||
"""Validation logic to remove the custom suborganization value before clean is triggered.
|
"""Validation logic to remove the custom suborganization value before clean is triggered.
|
||||||
Without this override, the form will throw an 'invalid option' error."""
|
Without this override, the form will throw an 'invalid option' error."""
|
||||||
|
@ -114,7 +129,7 @@ class RequestingEntityForm(RegistrarForm):
|
||||||
if requesting_entity_is_suborganization == "True":
|
if requesting_entity_is_suborganization == "True":
|
||||||
if is_requesting_new_suborganization:
|
if is_requesting_new_suborganization:
|
||||||
# Validate custom suborganization fields
|
# Validate custom suborganization fields
|
||||||
if not cleaned_data.get("requested_suborganization"):
|
if not cleaned_data.get("requested_suborganization") and "requested_suborganization" not in self.errors:
|
||||||
self.add_error("requested_suborganization", "Enter the name of your suborganization.")
|
self.add_error("requested_suborganization", "Enter the name of your suborganization.")
|
||||||
if not cleaned_data.get("suborganization_city"):
|
if not cleaned_data.get("suborganization_city"):
|
||||||
self.add_error("suborganization_city", "Enter the city where your suborganization is located.")
|
self.add_error("suborganization_city", "Enter the city where your suborganization is located.")
|
||||||
|
|
|
@ -12,6 +12,7 @@ from registrar.models.utility.generic_helper import CreateOrUpdateOrganizationTy
|
||||||
from registrar.utility.errors import FSMDomainRequestError, FSMErrorCodes
|
from registrar.utility.errors import FSMDomainRequestError, FSMErrorCodes
|
||||||
from registrar.utility.constants import BranchChoices
|
from registrar.utility.constants import BranchChoices
|
||||||
from auditlog.models import LogEntry
|
from auditlog.models import LogEntry
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
|
||||||
from registrar.utility.waffle import flag_is_active_for_user
|
from registrar.utility.waffle import flag_is_active_for_user
|
||||||
|
|
||||||
|
@ -671,6 +672,59 @@ class DomainRequest(TimeStampedModel):
|
||||||
# Store original values for caching purposes. Used to compare them on save.
|
# Store original values for caching purposes. Used to compare them on save.
|
||||||
self._cache_status_and_status_reasons()
|
self._cache_status_and_status_reasons()
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
"""
|
||||||
|
Validates suborganization-related fields in two scenarios:
|
||||||
|
1. New suborganization request: Prevents duplicate names within same portfolio
|
||||||
|
2. Partial suborganization data: Enforces a all-or-nothing rule for city/state/name fields
|
||||||
|
when portfolio exists without selected suborganization
|
||||||
|
|
||||||
|
Add new domain request validation rules here to ensure they're
|
||||||
|
enforced during both model save and form submission.
|
||||||
|
Not presently used on the domain request wizard, though.
|
||||||
|
"""
|
||||||
|
super().clean()
|
||||||
|
# Validation logic for a suborganization request
|
||||||
|
if self.is_requesting_new_suborganization():
|
||||||
|
# Raise an error if this suborganization already exists
|
||||||
|
Suborganization = apps.get_model("registrar.Suborganization")
|
||||||
|
if (
|
||||||
|
self.requested_suborganization
|
||||||
|
and Suborganization.objects.filter(
|
||||||
|
name__iexact=self.requested_suborganization,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
name__isnull=False,
|
||||||
|
portfolio__isnull=False,
|
||||||
|
).exists()
|
||||||
|
):
|
||||||
|
# Add a field-level error to requested_suborganization.
|
||||||
|
# To pass in field-specific errors, we need to embed a dict of
|
||||||
|
# field: validationerror then pass that into a validation error itself.
|
||||||
|
# This is slightly confusing, but it just adds it at that level.
|
||||||
|
msg = (
|
||||||
|
"This suborganization already exists. "
|
||||||
|
"Choose a new name, or select it directly if you would like to use it."
|
||||||
|
)
|
||||||
|
errors = {"requested_suborganization": ValidationError(msg)}
|
||||||
|
raise ValidationError(errors)
|
||||||
|
elif self.portfolio and not self.sub_organization:
|
||||||
|
# You cannot create a new suborganization without these fields
|
||||||
|
required_suborg_fields = {
|
||||||
|
"requested_suborganization": self.requested_suborganization,
|
||||||
|
"suborganization_city": self.suborganization_city,
|
||||||
|
"suborganization_state_territory": self.suborganization_state_territory,
|
||||||
|
}
|
||||||
|
# If at least one value is populated, enforce a all-or-nothing rule
|
||||||
|
if any(bool(value) for value in required_suborg_fields.values()):
|
||||||
|
# Find which fields are empty and throw an error on the field
|
||||||
|
errors = {}
|
||||||
|
for field_name, value in required_suborg_fields.items():
|
||||||
|
if not value:
|
||||||
|
errors[field_name] = ValidationError(
|
||||||
|
"This field is required when creating a new suborganization.",
|
||||||
|
)
|
||||||
|
raise ValidationError(errors)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""Save override for custom properties"""
|
"""Save override for custom properties"""
|
||||||
self.sync_organization_type()
|
self.sync_organization_type()
|
||||||
|
@ -690,6 +744,18 @@ class DomainRequest(TimeStampedModel):
|
||||||
# Update the cached values after saving
|
# Update the cached values after saving
|
||||||
self._cache_status_and_status_reasons()
|
self._cache_status_and_status_reasons()
|
||||||
|
|
||||||
|
def create_requested_suborganization(self):
|
||||||
|
"""Creates the requested suborganization.
|
||||||
|
Adds the name, portfolio, city, and state_territory fields.
|
||||||
|
Returns the created suborganization."""
|
||||||
|
Suborganization = apps.get_model("registrar.Suborganization")
|
||||||
|
return Suborganization.objects.create(
|
||||||
|
name=self.requested_suborganization,
|
||||||
|
portfolio=self.portfolio,
|
||||||
|
city=self.suborganization_city,
|
||||||
|
state_territory=self.suborganization_state_territory,
|
||||||
|
)
|
||||||
|
|
||||||
def send_custom_status_update_email(self, status):
|
def send_custom_status_update_email(self, status):
|
||||||
"""Helper function to send out a second status email when the status remains the same,
|
"""Helper function to send out a second status email when the status remains the same,
|
||||||
but the reason has changed."""
|
but the reason has changed."""
|
||||||
|
@ -784,7 +850,9 @@ class DomainRequest(TimeStampedModel):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def delete_and_clean_up_domain(self, called_from):
|
def delete_and_clean_up_domain(self, called_from):
|
||||||
|
# Delete the approved domain
|
||||||
try:
|
try:
|
||||||
|
# Clean up the approved domain
|
||||||
domain_state = self.approved_domain.state
|
domain_state = self.approved_domain.state
|
||||||
# Only reject if it exists on EPP
|
# Only reject if it exists on EPP
|
||||||
if domain_state != Domain.State.UNKNOWN:
|
if domain_state != Domain.State.UNKNOWN:
|
||||||
|
@ -796,6 +864,39 @@ class DomainRequest(TimeStampedModel):
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
logger.error(f"Can't query an approved domain while attempting {called_from}")
|
logger.error(f"Can't query an approved domain while attempting {called_from}")
|
||||||
|
|
||||||
|
# Delete the suborg as long as this is the only place it is used
|
||||||
|
self._cleanup_dangling_suborg()
|
||||||
|
|
||||||
|
def _cleanup_dangling_suborg(self):
|
||||||
|
"""Deletes the existing suborg if its only being used by the deleted record"""
|
||||||
|
# Nothing to delete, so we just smile and walk away
|
||||||
|
if self.sub_organization is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
Suborganization = apps.get_model("registrar.Suborganization")
|
||||||
|
|
||||||
|
# Stored as so because we need to set the reference to none first,
|
||||||
|
# so we can't just use the self.sub_organization property
|
||||||
|
suborg = Suborganization.objects.get(id=self.sub_organization.id)
|
||||||
|
requests = suborg.request_sub_organization
|
||||||
|
domain_infos = suborg.information_sub_organization
|
||||||
|
|
||||||
|
# Check if this is the only reference to the suborganization
|
||||||
|
if requests.count() != 1 or domain_infos.count() > 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Remove the suborganization reference from request.
|
||||||
|
self.sub_organization = None
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
# Remove the suborganization reference from domain if it exists.
|
||||||
|
if domain_infos.count() == 1:
|
||||||
|
domain_infos.update(sub_organization=None)
|
||||||
|
|
||||||
|
# Delete the now-orphaned suborganization
|
||||||
|
logger.info(f"_cleanup_dangling_suborg() -> Deleting orphan suborganization: {suborg}")
|
||||||
|
suborg.delete()
|
||||||
|
|
||||||
def _send_status_update_email(
|
def _send_status_update_email(
|
||||||
self,
|
self,
|
||||||
new_status,
|
new_status,
|
||||||
|
@ -984,6 +1085,7 @@ class DomainRequest(TimeStampedModel):
|
||||||
|
|
||||||
if self.status == self.DomainRequestStatus.APPROVED:
|
if self.status == self.DomainRequestStatus.APPROVED:
|
||||||
self.delete_and_clean_up_domain("action_needed")
|
self.delete_and_clean_up_domain("action_needed")
|
||||||
|
|
||||||
elif self.status == self.DomainRequestStatus.REJECTED:
|
elif self.status == self.DomainRequestStatus.REJECTED:
|
||||||
self.rejection_reason = None
|
self.rejection_reason = None
|
||||||
|
|
||||||
|
@ -1014,8 +1116,16 @@ class DomainRequest(TimeStampedModel):
|
||||||
domain request into an admin on that domain. It also triggers an email
|
domain request into an admin on that domain. It also triggers an email
|
||||||
notification."""
|
notification."""
|
||||||
|
|
||||||
|
should_save = False
|
||||||
if self.federal_agency is None:
|
if self.federal_agency is None:
|
||||||
self.federal_agency = FederalAgency.objects.filter(agency="Non-Federal Agency").first()
|
self.federal_agency = FederalAgency.objects.filter(agency="Non-Federal Agency").first()
|
||||||
|
should_save = True
|
||||||
|
|
||||||
|
if self.is_requesting_new_suborganization():
|
||||||
|
self.sub_organization = self.create_requested_suborganization()
|
||||||
|
should_save = True
|
||||||
|
|
||||||
|
if should_save:
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
# create the domain
|
# create the domain
|
||||||
|
@ -1148,7 +1258,7 @@ class DomainRequest(TimeStampedModel):
|
||||||
def is_requesting_new_suborganization(self) -> bool:
|
def is_requesting_new_suborganization(self) -> bool:
|
||||||
"""Determines if a user is trying to request
|
"""Determines if a user is trying to request
|
||||||
a new suborganization using the domain request form, rather than one that already exists.
|
a new suborganization using the domain request form, rather than one that already exists.
|
||||||
Used for the RequestingEntity page.
|
Used for the RequestingEntity page and on DomainInformation.create_from_da().
|
||||||
|
|
||||||
Returns True if a sub_organization does not exist and if requested_suborganization,
|
Returns True if a sub_organization does not exist and if requested_suborganization,
|
||||||
suborganization_city, and suborganization_state_territory all exist.
|
suborganization_city, and suborganization_state_territory all exist.
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
{% block title %}{% translate "Unauthorized | " %}{% endblock %}
|
{% block title %}{% translate "Unauthorized | " %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="main-content" class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||||
<div class="grid-row grow-gap">
|
<div class="grid-row grow-gap {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
||||||
<h1>
|
<h1>
|
||||||
{% translate "You are not authorized to view this page" %}
|
{% translate "You are not authorized to view this page" %}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
{% block title %}{% translate "Forbidden | " %}{% endblock %}
|
{% block title %}{% translate "Forbidden | " %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="main-content" class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||||
<div class="grid-row grow-gap">
|
<div class="grid-row grow-gap {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
||||||
<h1>
|
<h1>
|
||||||
{% translate "You're not authorized to view this page." %}
|
{% translate "You're not authorized to view this page." %}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
{% block title %}{% translate "Page not found | " %}{% endblock %}
|
{% block title %}{% translate "Page not found | " %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="main-content" class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||||
<div class="grid-row grid-gap">
|
<div class="grid-row grid-gap {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
||||||
<h1>
|
<h1>
|
||||||
{% translate "We couldn’t find that page" %}
|
{% translate "We couldn’t find that page" %}
|
||||||
|
|
|
@ -5,8 +5,8 @@
|
||||||
{% block title %}{% translate "Server error | " %}{% endblock %}
|
{% block title %}{% translate "Server error | " %}{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="main-content" class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||||
<div class="grid-row grid-gap">
|
<div class="grid-row grid-gap {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
<div class="tablet:grid-col-6 usa-prose margin-bottom-3">
|
||||||
<h1>
|
<h1>
|
||||||
{% translate "We're having some trouble." %}
|
{% translate "We're having some trouble." %}
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
{% for model in app.models %}
|
{% for model in app.models %}
|
||||||
<tr class="model-{{ model.object_name|lower }}{% if model.admin_url in request.path|urlencode %} current-model{% endif %}">
|
<tr class="model-{{ model.object_name|lower }}{% if model.admin_url in request.path|urlencode %} current-model{% endif %}">
|
||||||
{% if model.admin_url %}
|
{% if model.admin_url %}
|
||||||
<th scope="row"><a href="{{ model.admin_url }}"{% if model.admin_url in request.path|urlencode %} aria-current="page"{% endif %}>{{ model.name }}</a></th>
|
<th scope="row"><a href="{{ model.admin_url }}"{% if model.admin_url in request.path|urlencode %} aria-current="page"{% endif %}">{{ model.name }}</a></th>
|
||||||
{% else %}
|
{% else %}
|
||||||
<th scope="row">{{ model.name }}</th>
|
<th scope="row">{{ model.name }}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -61,7 +61,7 @@ https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/
|
||||||
{% if field.field.help_text %}
|
{% if field.field.help_text %}
|
||||||
{# .gov override #}
|
{# .gov override #}
|
||||||
{% block help_text %}
|
{% block help_text %}
|
||||||
<div class="help"{% if field.field.id_for_label %} id="{{ field.field.id_for_label }}_helptext"{% endif %}>
|
<div class="help"{% if field.field.id_for_label %} id="{{ field.field.id_for_label }}_helptext"{% endif %}">
|
||||||
<div>{{ field.field.help_text|safe }}</div>
|
<div>{{ field.field.help_text|safe }}</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock help_text %}
|
{% endblock help_text %}
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<select name="selected_user" id="selected_user" class="admin-combobox margin-top-0" onchange="this.form.submit()">
|
<select name="selected_user" id="selected_user" class="admin-combobox margin-top-0" onchange="this.form.submit()">
|
||||||
<option value="">Select a user</option>
|
<option value="">Select a user</option>
|
||||||
{% for user in other_users %}
|
{% for user in other_users %}
|
||||||
<option value="{{ user.pk }}" {% if selected_user and user.pk == selected_user.pk %}selected{% endif %}>
|
<option value="{{ user.pk }}" {% if selected_user and user.pk == selected_user.pk %}selected{% endif %}">
|
||||||
{{ user.first_name }} {{ user.last_name }}
|
{{ user.first_name }} {{ user.last_name }}
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -97,7 +97,7 @@
|
||||||
<section class="usa-banner" aria-label="Official website of the United States government">
|
<section class="usa-banner" aria-label="Official website of the United States government">
|
||||||
<div class="usa-accordion">
|
<div class="usa-accordion">
|
||||||
<header class="usa-banner__header">
|
<header class="usa-banner__header">
|
||||||
<div class="usa-banner__inner {% if is_widescreen_mode %} usa-banner__inner--widescreen {% endif %}">
|
<div class="usa-banner__inner usa-banner__inner--widescreen padding-x--widescreen">
|
||||||
<div class="grid-col-auto">
|
<div class="grid-col-auto">
|
||||||
<img class="usa-banner__header-flag" src="{% static 'img/us_flag_small.png' %}" alt="U.S. flag" />
|
<img class="usa-banner__header-flag" src="{% static 'img/us_flag_small.png' %}" alt="U.S. flag" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -113,7 +113,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="usa-banner__content usa-accordion__content" id="gov-banner-default">
|
<div class="usa-banner__content usa-accordion__content padding-x--widescreen margin-x-0" id="gov-banner-default">
|
||||||
<div class="grid-row grid-gap-lg">
|
<div class="grid-row grid-gap-lg">
|
||||||
<div class="usa-banner__guidance tablet:grid-col-6">
|
<div class="usa-banner__guidance tablet:grid-col-6">
|
||||||
<img class="usa-banner__icon usa-media-block__img" src="{% static 'img/icon-dot-gov.svg' %}" role="img"
|
<img class="usa-banner__icon usa-media-block__img" src="{% static 'img/icon-dot-gov.svg' %}" role="img"
|
||||||
|
@ -159,14 +159,14 @@
|
||||||
|
|
||||||
{% block wrapper %}
|
{% block wrapper %}
|
||||||
{% block wrapperdiv %}
|
{% block wrapperdiv %}
|
||||||
<div id="wrapper">
|
<div id="wrapper" class="wrapper--padding-top-6">
|
||||||
{% endblock wrapperdiv %}
|
{% endblock wrapperdiv %}
|
||||||
{% block messages %}
|
{% block messages %}
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
<ul class="messages">
|
<ul class="messages">
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
{% if 'base' in message.extra_tags %}
|
{% if 'base' in message.extra_tags %}
|
||||||
<li{% if message.tags %} class="{{ message.tags }}" {% endif %}>
|
<li{% if message.tags %} class="{{ message.tags }}" {% endif %}">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
<ul class="messages">
|
<ul class="messages">
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<li {% if message.tags %} class="{{ message.tags }}" {% endif %}>
|
<li {% if message.tags %} class="{{ message.tags }}" {% endif %}">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</li>
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
|
@ -321,6 +321,22 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
{% else %}
|
{% else %}
|
||||||
<input id="last-sent-rejection-email-content" class="display-none" value="None">
|
<input id="last-sent-rejection-email-content" class="display-none" value="None">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% elif field.field.name == "requested_suborganization" %}
|
||||||
|
{{ field.field }}
|
||||||
|
<div class="requested-suborganization--clear-button">
|
||||||
|
<button
|
||||||
|
id="clear-requested-suborganization"
|
||||||
|
class="usa-button--dja usa-button usa-button__small-text usa-button--unstyled"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="usa-icon"
|
||||||
|
>
|
||||||
|
<use aria-hidden="true" xlink:href="{%static 'img/sprite.svg'%}#close"></use>
|
||||||
|
</svg>
|
||||||
|
Clear requested suborganization
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ field.field }}
|
{{ field.field }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
|
||||||
{% comment %} This view provides a detail button that can be used to show/hide content {% endcomment %}
|
{% comment %} This view provides a detail button that can be used to show/hide content {% endcomment %}
|
||||||
<details class="margin-top-1 dja-detail-table" aria-role="button" {% if start_open %}open{% else %}closed{% endif %}>
|
<details class="margin-top-1 dja-detail-table" aria-role="button" {% if start_open %}open{% else %}closed{% endif %}">
|
||||||
<summary class="padding-1 padding-left-0 dja-details-summary">Details</summary>
|
<summary class="padding-1 padding-left-0 dja-details-summary">Details</summary>
|
||||||
<div class="grid-container margin-left-0 padding-left-0 padding-right-0 dja-details-contents">
|
<div class="grid-container margin-left-0 padding-left-0 padding-right-0 dja-details-contents">
|
||||||
{% block detail_content %}
|
{% block detail_content %}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block help_text %}
|
{% block help_text %}
|
||||||
<div class="help margin-bottom-1" {% if field.field.id_for_label %} id="{{ field.field.id_for_label }}_helptext"{% endif %}>
|
<div class="help margin-bottom-1" {% if field.field.id_for_label %} id="{{ field.field.id_for_label }}_helptext"{% endif %}">
|
||||||
{% if field.field.name == "state" %}
|
{% if field.field.name == "state" %}
|
||||||
<div>{{ state_help_message }}</div>
|
<div>{{ state_help_message }}</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
|
@ -6,14 +6,14 @@
|
||||||
<ul class="mulitple-choice">
|
<ul class="mulitple-choice">
|
||||||
{% for choice in choices %}
|
{% for choice in choices %}
|
||||||
{% if choice.reset %}
|
{% if choice.reset %}
|
||||||
<li{% if choice.selected %} class="selected"{% endif %}>
|
<li{% if choice.selected %} class="selected"{% endif %}">
|
||||||
<a href="{{ choice.query_string|iriencode }}" title="{{ choice.display }}">{{ choice.display }}</a>
|
<a href="{{ choice.query_string|iriencode }}" title="{{ choice.display }}">{{ choice.display }}</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for choice in choices %}
|
{% for choice in choices %}
|
||||||
{% if not choice.reset %}
|
{% if not choice.reset %}
|
||||||
<li{% if choice.selected %} class="selected"{% endif %}>
|
<li{% if choice.selected %} class="selected"{% endif %}">
|
||||||
{% if choice.selected and choice.exclude_query_string %}
|
{% if choice.selected and choice.exclude_query_string %}
|
||||||
<a class="choice-filter choice-filter--checked" href="{{ choice.exclude_query_string|iriencode }}">{{ choice.display }}
|
<a class="choice-filter choice-filter--checked" href="{{ choice.exclude_query_string|iriencode }}">{{ choice.display }}
|
||||||
<svg class="usa-icon position-absolute z-0 left-0" aria-hidden="true" focusable="false" role="img" width="24" height="24">
|
<svg class="usa-icon position-absolute z-0 left-0" aria-hidden="true" focusable="false" role="img" width="24" height="24">
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
{% block title %}{{ domain.name }} | {% endblock %}
|
{% block title %}{{ domain.name }} | {% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="grid-container">
|
<div class="grid-container grid-container--widescreen">
|
||||||
|
|
||||||
<div class="grid-row grid-gap">
|
<div class="grid-row grid-gap {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
<div class="tablet:grid-col-3">
|
<div class="tablet:grid-col-3 ">
|
||||||
<p class="font-body-md margin-top-0 margin-bottom-2
|
<p class="font-body-md margin-top-0 margin-bottom-2
|
||||||
text-primary-darker text-semibold domain-name-wrap"
|
text-primary-darker text-semibold domain-name-wrap"
|
||||||
>
|
>
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
{% block title %}{{form_titles|get_item:steps.current}} | Request a .gov | {% endblock %}
|
{% block title %}{{form_titles|get_item:steps.current}} | Request a .gov | {% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="grid-container">
|
<div class="grid-container grid-container--widescreen">
|
||||||
<div class="grid-row grid-gap">
|
<div class="grid-row grid-gap {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
<div class="tablet:grid-col-3">
|
<div class="tablet:grid-col-3">
|
||||||
{% include 'domain_request_sidebar.html' %}
|
{% include 'domain_request_sidebar.html' %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,7 +4,8 @@
|
||||||
{% block title %} Start a request | {% endblock %}
|
{% block title %} Start a request | {% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="main-content" class="grid-container">
|
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||||
|
<div class="grid-row {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
|
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
|
||||||
|
|
||||||
<form class="usa-form usa-form--extra-large" method="post" novalidate>
|
<form class="usa-form usa-form--extra-large" method="post" novalidate>
|
||||||
|
@ -25,7 +26,7 @@
|
||||||
{% include "includes/profile_information.html" with user=user%}
|
{% include "includes/profile_information.html" with user=user%}
|
||||||
|
|
||||||
|
|
||||||
{% block form_buttons %}
|
{% block form_buttons %}
|
||||||
<div class="stepnav">
|
<div class="stepnav">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
@ -34,7 +35,7 @@
|
||||||
class="usa-button"
|
class="usa-button"
|
||||||
>Continue</button>
|
>Continue</button>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -8,18 +8,20 @@
|
||||||
{% endblock wrapperdiv %}
|
{% endblock wrapperdiv %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="grid-container">
|
<div class="grid-container grid-container--widescreen">
|
||||||
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
|
<div class="grid-row {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
|
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
|
||||||
|
|
||||||
|
|
||||||
<h1>Withdraw request for {{ DomainRequest.requested_domain.name }}?</h1>
|
<h1>Withdraw request for {{ DomainRequest.requested_domain.name }}?</h1>
|
||||||
|
|
||||||
<p>If you withdraw your request, we won't review it. Once you withdraw your request, you can edit it and submit it again. </p>
|
<p>If you withdraw your request, we won't review it. Once you withdraw your request, you can edit it and submit it again. </p>
|
||||||
|
|
||||||
<p><a href="{% url 'domain-request-withdrawn' DomainRequest.id %}" class="usa-button withdraw">Withdraw request</a>
|
<p><a href="{% url 'domain-request-withdrawn' DomainRequest.id %}" class="usa-button withdraw">Withdraw request</a>
|
||||||
<a href="{% url 'domain-request-status' DomainRequest.id %}">Cancel</a></p>
|
<a href="{% url 'domain-request-status' DomainRequest.id %}">Cancel</a></p>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
<li class="usa-sidenav__item">
|
<li class="usa-sidenav__item">
|
||||||
{% url 'domain-dns' pk=domain.id as url %}
|
{% url 'domain-dns' pk=domain.id as url %}
|
||||||
<a href="{{ url }}" {% if request.path|startswith:url %}class="usa-current"{% endif %}>
|
<a href="{{ url }}" {% if request.path|startswith:url %}class="usa-current"{% endif %}">
|
||||||
DNS
|
DNS
|
||||||
</a>
|
</a>
|
||||||
{% if request.path|startswith:url %}
|
{% if request.path|startswith:url %}
|
||||||
|
|
|
@ -5,12 +5,12 @@
|
||||||
{% block title %} Home | {% endblock %}
|
{% block title %} Home | {% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="main-content" class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
{# the entire logged in page goes here #}
|
{# the entire logged in page goes here #}
|
||||||
|
|
||||||
{% block homepage_content %}
|
{% block homepage_content %}
|
||||||
<div class="tablet:grid-col-11 desktop:grid-col-10 tablet:grid-offset-1">
|
<div class="tablet:grid-col-11 desktop:grid-col-10 {% if is_widescreen_centered %}tablet:grid-offset-1{% endif %}">
|
||||||
{% block messages %}
|
{% block messages %}
|
||||||
{% include "includes/form_messages.html" %}
|
{% include "includes/form_messages.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
<div class="margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
<div class="usa-alert usa-alert--error">
|
<div class="usa-alert usa-alert--error">
|
||||||
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||||
<h4 class="usa-alert__heading">
|
<h4 class="usa-alert__heading">
|
||||||
Header
|
Header
|
||||||
</h4>
|
</h4>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<section class="usa-site-alert usa-site-alert--info margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
<section class="usa-site-alert usa-site-alert--info margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
<div class="usa-alert">
|
<div class="usa-alert">
|
||||||
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||||
<h4 class="usa-alert__heading">
|
<h4 class="usa-alert__heading">
|
||||||
Header
|
Header
|
||||||
</h4>
|
</h4>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<section class="usa-site-alert usa-site-alert--emergency usa-site-alert--hot-pink margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
<section class="usa-site-alert usa-site-alert--emergency usa-site-alert--hot-pink margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
<div class="usa-alert">
|
<div class="usa-alert">
|
||||||
<div class="usa-alert__body {% if add_body_class %}{{ add_body_class }}{% endif %} {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
<div class="usa-alert__body {% if add_body_class %}{{ add_body_class }}{% endif %} usa-alert__body--widescreen">
|
||||||
<p class="usa-alert__text maxw-none">
|
<p class="usa-alert__text maxw-none">
|
||||||
<strong>Attention:</strong> You are on a test site.
|
<strong>Attention:</strong> You are on a test site.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<section class="usa-site-alert usa-site-alert--emergency margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
<section class="usa-site-alert usa-site-alert--emergency margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
<div class="usa-alert">
|
<div class="usa-alert">
|
||||||
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||||
<h3 class="usa-alert__heading">
|
<h3 class="usa-alert__heading">
|
||||||
Service disruption
|
Service disruption
|
||||||
</h3>
|
</h3>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<section class="usa-site-alert usa-site-alert--emergency margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
<section class="usa-site-alert usa-site-alert--emergency margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
<div class="usa-alert">
|
<div class="usa-alert">
|
||||||
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||||
<h3 class="usa-alert__heading">
|
<h3 class="usa-alert__heading">
|
||||||
Header here
|
Header here
|
||||||
</h3>
|
</h3>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<section class="usa-site-alert usa-site-alert--emergency margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
<section class="usa-site-alert usa-site-alert--emergency margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
<div class="usa-alert">
|
<div class="usa-alert">
|
||||||
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||||
<h3 class="usa-alert__heading">
|
<h3 class="usa-alert__heading">
|
||||||
System outage
|
System outage
|
||||||
</h3>
|
</h3>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<div class="margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
<div class="margin-y-0 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||||
<div class="usa-alert usa-alert--warning">
|
<div class="usa-alert usa-alert--warning">
|
||||||
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||||
<h4 class="usa-alert__heading">
|
<h4 class="usa-alert__heading">
|
||||||
Header
|
Header
|
||||||
</h4>
|
</h4>
|
||||||
|
|
|
@ -1,236 +0,0 @@
|
||||||
{% load custom_filters %}
|
|
||||||
{% load static url_helpers %}
|
|
||||||
<main id="main-content" class="grid-container">
|
|
||||||
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
|
|
||||||
{% block breadcrumb %}
|
|
||||||
{% if portfolio %}
|
|
||||||
{% url 'domain-requests' as url %}
|
|
||||||
{% else %}
|
|
||||||
{% url 'home' as url %}
|
|
||||||
{% endif %}
|
|
||||||
<nav class="usa-breadcrumb padding-top-0" aria-label="Domain request breadcrumb">
|
|
||||||
<ol class="usa-breadcrumb__list">
|
|
||||||
<li class="usa-breadcrumb__list-item">
|
|
||||||
{% if portfolio %}
|
|
||||||
<a href="{{ url }}" class="usa-breadcrumb__link"><span>Domain requests</span></a>
|
|
||||||
{% else %}
|
|
||||||
<a href="{{ url }}" class="usa-breadcrumb__link"><span>Manage your domains</span></a>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
<li class="usa-breadcrumb__list-item usa-current" aria-current="page">
|
|
||||||
{% if not DomainRequest.requested_domain and DomainRequest.status == DomainRequest.DomainRequestStatus.STARTED %}
|
|
||||||
<span>New domain request</span>
|
|
||||||
{% else %}
|
|
||||||
<span>{{ DomainRequest.requested_domain.name }}</span>
|
|
||||||
{% endif %}
|
|
||||||
</li>
|
|
||||||
</ol>
|
|
||||||
</nav>
|
|
||||||
{% endblock breadcrumb %}
|
|
||||||
|
|
||||||
{% block header %}
|
|
||||||
{% if not DomainRequest.requested_domain and DomainRequest.status == DomainRequest.DomainRequestStatus.STARTED %}
|
|
||||||
<h1>New domain request</h1>
|
|
||||||
{% else %}
|
|
||||||
<h1>Domain request for {{ DomainRequest.requested_domain.name }}</h1>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock header %}
|
|
||||||
|
|
||||||
{% block status_summary %}
|
|
||||||
<div
|
|
||||||
class="usa-summary-box dotgov-status-box margin-top-3 padding-left-2"
|
|
||||||
role="region"
|
|
||||||
aria-labelledby="summary-box-key-information"
|
|
||||||
>
|
|
||||||
<div class="usa-summary-box__body">
|
|
||||||
<p class="usa-summary-box__heading font-sans-md margin-bottom-0"
|
|
||||||
id="summary-box-key-information"
|
|
||||||
>
|
|
||||||
<span class="text-bold text-primary-darker">
|
|
||||||
Status:
|
|
||||||
</span>
|
|
||||||
{{ DomainRequest.get_status_display|default:"ERROR Please contact technical support/dev" }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
{% endblock status_summary %}
|
|
||||||
|
|
||||||
{% block status_metadata %}
|
|
||||||
|
|
||||||
{% if portfolio %}
|
|
||||||
{% if DomainRequest.creator %}
|
|
||||||
<p class="margin-top-1 margin-bottom-1">
|
|
||||||
<b class="review__step__name">Created by:</b> {{DomainRequest.creator.email|default:DomainRequest.creator.get_formatted_name }}
|
|
||||||
</p>
|
|
||||||
{% else %}
|
|
||||||
<p class="margin-top-1 margin-bottom-1">
|
|
||||||
<b class="review__step__name">No creator found:</b> this is an error, please email <a href="mailto:help@get.gov" class="usa-link">help@get.gov</a>.
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% with statuses=DomainRequest.DomainRequestStatus last_submitted=DomainRequest.last_submitted_date|date:"F j, Y" first_submitted=DomainRequest.first_submitted_date|date:"F j, Y" last_status_update=DomainRequest.last_status_update|date:"F j, Y" %}
|
|
||||||
{% comment %}
|
|
||||||
These are intentionally seperated this way.
|
|
||||||
There is some code repetition, but it gives us more flexibility rather than a dense reduction.
|
|
||||||
Leave it this way until we've solidified our requirements.
|
|
||||||
{% endcomment %}
|
|
||||||
{% if DomainRequest.status == statuses.STARTED %}
|
|
||||||
{% with first_started_date=DomainRequest.get_first_status_started_date|date:"F j, Y" %}
|
|
||||||
<p class="margin-top-1">
|
|
||||||
{% comment %}
|
|
||||||
A newly created domain request will not have a value for last_status update.
|
|
||||||
This is because the status never really updated.
|
|
||||||
However, if this somehow goes back to started we can default to displaying that new date.
|
|
||||||
{% endcomment %}
|
|
||||||
<b class="review__step__name">Started on:</b> {{last_status_update|default:first_started_date}}
|
|
||||||
</p>
|
|
||||||
{% endwith %}
|
|
||||||
{% elif DomainRequest.status == statuses.SUBMITTED %}
|
|
||||||
<p class="margin-top-1 margin-bottom-1">
|
|
||||||
<b class="review__step__name">Submitted on:</b> {{last_submitted|default:first_submitted }}
|
|
||||||
</p>
|
|
||||||
<p class="margin-top-1">
|
|
||||||
<b class="review__step__name">Last updated on:</b> {{DomainRequest.updated_at|date:"F j, Y"}}
|
|
||||||
</p>
|
|
||||||
{% elif DomainRequest.status == statuses.ACTION_NEEDED %}
|
|
||||||
<p class="margin-top-1 margin-bottom-1">
|
|
||||||
<b class="review__step__name">Submitted on:</b> {{last_submitted|default:first_submitted }}
|
|
||||||
</p>
|
|
||||||
<p class="margin-top-1">
|
|
||||||
<b class="review__step__name">Last updated on:</b> {{DomainRequest.updated_at|date:"F j, Y"}}
|
|
||||||
</p>
|
|
||||||
{% elif DomainRequest.status == statuses.REJECTED %}
|
|
||||||
<p class="margin-top-1 margin-bottom-1">
|
|
||||||
<b class="review__step__name">Submitted on:</b> {{last_submitted|default:first_submitted }}
|
|
||||||
</p>
|
|
||||||
<p class="margin-top-1">
|
|
||||||
<b class="review__step__name">Rejected on:</b> {{last_status_update}}
|
|
||||||
</p>
|
|
||||||
{% elif DomainRequest.status == statuses.WITHDRAWN %}
|
|
||||||
<p class="margin-top-1 margin-bottom-1">
|
|
||||||
<b class="review__step__name">Submitted on:</b> {{last_submitted|default:first_submitted }}
|
|
||||||
</p>
|
|
||||||
<p class="margin-top-1">
|
|
||||||
<b class="review__step__name">Withdrawn on:</b> {{last_status_update}}
|
|
||||||
</p>
|
|
||||||
{% else %}
|
|
||||||
{% comment %} Shown for in_review, approved, ineligible {% endcomment %}
|
|
||||||
<p class="margin-top-1">
|
|
||||||
<b class="review__step__name">Last updated on:</b> {{DomainRequest.updated_at|date:"F j, Y"}}
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endblock status_metadata %}
|
|
||||||
|
|
||||||
{% block status_blurb %}
|
|
||||||
{% if DomainRequest.is_awaiting_review %}
|
|
||||||
<p>{% include "includes/domain_request_awaiting_review.html" with show_withdraw_text=DomainRequest.is_withdrawable %}</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock status_blurb %}
|
|
||||||
|
|
||||||
{% block modify_request %}
|
|
||||||
{% if DomainRequest.is_withdrawable %}
|
|
||||||
<p><a href="{% url 'domain-request-withdraw-confirmation' pk=DomainRequest.id %}" class="usa-button usa-button--outline withdraw_outline">
|
|
||||||
Withdraw request</a>
|
|
||||||
</p>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock modify_request %}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grid-col desktop:grid-offset-2 maxw-tablet">
|
|
||||||
{% block request_summary_header %}
|
|
||||||
<h2 class="text-primary-darker"> Summary of your domain request </h2>
|
|
||||||
{% endblock request_summary_header%}
|
|
||||||
|
|
||||||
{% block request_summary %}
|
|
||||||
{% with heading_level='h3' %}
|
|
||||||
{% with org_type=DomainRequest.get_generic_org_type_display %}
|
|
||||||
{% include "includes/summary_item.html" with title='Type of organization' value=org_type heading_level=heading_level %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
{% if DomainRequest.tribe_name %}
|
|
||||||
{% include "includes/summary_item.html" with title='Tribal government' value=DomainRequest.tribe_name heading_level=heading_level %}
|
|
||||||
|
|
||||||
{% if DomainRequest.federally_recognized_tribe %}
|
|
||||||
<p>Federally-recognized tribe</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.state_recognized_tribe %}
|
|
||||||
<p>State-recognized tribe</p>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.get_federal_type_display %}
|
|
||||||
{% include "includes/summary_item.html" with title='Federal government branch' value=DomainRequest.get_federal_type_display heading_level=heading_level %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.is_election_board %}
|
|
||||||
{% with value=DomainRequest.is_election_board|yesno:"Yes,No,Incomplete" %}
|
|
||||||
{% include "includes/summary_item.html" with title='Election office' value=value heading_level=heading_level %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.organization_name %}
|
|
||||||
{% include "includes/summary_item.html" with title='Organization' value=DomainRequest address='true' heading_level=heading_level %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.about_your_organization %}
|
|
||||||
{% include "includes/summary_item.html" with title='About your organization' value=DomainRequest.about_your_organization heading_level=heading_level %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.senior_official %}
|
|
||||||
{% include "includes/summary_item.html" with title='Senior official' value=DomainRequest.senior_official contact='true' heading_level=heading_level %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.current_websites.all %}
|
|
||||||
{% include "includes/summary_item.html" with title='Current websites' value=DomainRequest.current_websites.all list='true' heading_level=heading_level %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.requested_domain %}
|
|
||||||
{% include "includes/summary_item.html" with title='.gov domain' value=DomainRequest.requested_domain heading_level=heading_level %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.alternative_domains.all %}
|
|
||||||
{% include "includes/summary_item.html" with title='Alternative domains' value=DomainRequest.alternative_domains.all list='true' heading_level=heading_level %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.purpose %}
|
|
||||||
{% include "includes/summary_item.html" with title='Purpose of your domain' value=DomainRequest.purpose heading_level=heading_level %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.creator %}
|
|
||||||
{% include "includes/summary_item.html" with title='Your contact information' value=DomainRequest.creator contact='true' heading_level=heading_level %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if DomainRequest.other_contacts.all %}
|
|
||||||
{% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.other_contacts.all contact='true' list='true' heading_level=heading_level %}
|
|
||||||
{% else %}
|
|
||||||
{% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.no_other_contacts_rationale heading_level=heading_level %}
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{# We always show this field even if None #}
|
|
||||||
{% if DomainRequest %}
|
|
||||||
<h3 class="header--body text-primary-dark margin-bottom-0">CISA Regional Representative</h3>
|
|
||||||
<ul class="usa-list usa-list--unstyled margin-top-0">
|
|
||||||
{% if DomainRequest.cisa_representative_first_name %}
|
|
||||||
{{ DomainRequest.get_formatted_cisa_rep_name }}
|
|
||||||
{% else %}
|
|
||||||
No
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
<h3 class="header--body text-primary-dark margin-bottom-0">Anything else</h3>
|
|
||||||
<ul class="usa-list usa-list--unstyled margin-top-0">
|
|
||||||
{% if DomainRequest.anything_else %}
|
|
||||||
{{DomainRequest.anything_else}}
|
|
||||||
{% else %}
|
|
||||||
No
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endblock request_summary%}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
|
@ -4,8 +4,8 @@
|
||||||
{% url 'get_domain_requests_json' as url %}
|
{% url 'get_domain_requests_json' as url %}
|
||||||
<span id="get_domain_requests_json_url" class="display-none">{{url}}</span>
|
<span id="get_domain_requests_json_url" class="display-none">{{url}}</span>
|
||||||
|
|
||||||
<section class="section-outlined domain-requests{% if portfolio %} section-outlined--border-base-light{% endif %}" id="domain-requests">
|
<section class="section-outlined domain-requests {% if portfolio %}section-outlined--border-base-light{% endif %}" id="domain-requests">
|
||||||
<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 %}
|
||||||
<h2 id="domain-requests-header" class="display-inline-block">Domain requests</h2>
|
<h2 id="domain-requests-header" class="display-inline-block">Domain requests</h2>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="section-outlined__search {% if portfolio %} mobile:grid-col-12 desktop:grid-col-6{% endif %} {% if is_widescreen_mode %} section-outlined__search--widescreen {% endif %}">
|
<div class="section-outlined__search section-outlined__search--widescreen {% if portfolio %}mobile:grid-col-12 desktop:grid-col-6{% endif %}">
|
||||||
<section aria-label="Domain requests search component" class="margin-top-2">
|
<section aria-label="Domain requests search component" class="margin-top-2">
|
||||||
<form class="usa-search usa-search--small" method="POST" role="search">
|
<form class="usa-search usa-search--small" method="POST" role="search">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
{% if has_domain_renewal_flag and num_expiring_domains > 0 and has_any_domains_portfolio_permission %}
|
{% 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">
|
<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">
|
||||||
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||||
<p class="usa-alert__text maxw-none">
|
<p class="usa-alert__text maxw-none">
|
||||||
{% if num_expiring_domains == 1%}
|
{% 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>
|
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>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
<!-- Embedding the portfolio value in a data attribute -->
|
<!-- Embedding the portfolio value in a data attribute -->
|
||||||
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="section-outlined__search {% if portfolio %} mobile:grid-col-12 desktop:grid-col-6{% endif %} {% if is_widescreen_mode %} section-outlined__search--widescreen {% endif %}">
|
<div class="section-outlined__search section-outlined__search--widescreen {% if portfolio %}mobile:grid-col-12 desktop:grid-col-6{% endif %}">
|
||||||
<section aria-label="Domains search component" class="margin-top-2">
|
<section aria-label="Domains search component" class="margin-top-2">
|
||||||
<form class="usa-search usa-search--small" method="POST" role="search">
|
<form class="usa-search usa-search--small" method="POST" role="search">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
@ -78,7 +78,7 @@
|
||||||
{% if has_domain_renewal_flag and num_expiring_domains > 0 and not portfolio %}
|
{% 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">
|
<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">
|
||||||
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||||
<p class="usa-alert__text maxw-none">
|
<p class="usa-alert__text maxw-none">
|
||||||
{% if num_expiring_domains == 1%}
|
{% 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>
|
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>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
<footer class="usa-footer">
|
<footer class="usa-footer">
|
||||||
<div class="usa-footer__secondary-section">
|
<div class="usa-footer__secondary-section">
|
||||||
<div class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
<div class="grid-container grid-container--widescreen">
|
||||||
<div class="grid-row grid-gap">
|
<div class="grid-row grid-gap">
|
||||||
<div
|
<div
|
||||||
class="
|
class="
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<div class="usa-footer__contact-links
|
<div class="usa-footer__contact-links
|
||||||
mobile-lg:grid-col-6 flex-align-self-center"
|
mobile-lg:grid-col-6 flex-align-self-center"
|
||||||
>
|
>
|
||||||
<address class="usa-footer__address">
|
<address class="usa-footer__address maxw-none">
|
||||||
<div class="usa-footer__contact-info grid-row grid-gap-md">
|
<div class="usa-footer__contact-info grid-row grid-gap-md">
|
||||||
{% if show_manage_your_domains %}
|
{% if show_manage_your_domains %}
|
||||||
<div class="grid-col-auto">
|
<div class="grid-col-auto">
|
||||||
|
@ -51,7 +51,7 @@
|
||||||
class="usa-identifier__section usa-identifier__section--masthead"
|
class="usa-identifier__section usa-identifier__section--masthead"
|
||||||
aria-label="Agency identifier"
|
aria-label="Agency identifier"
|
||||||
>
|
>
|
||||||
<div class="usa-identifier__container {% if is_widescreen_mode %} usa-identifier__container--widescreen {% endif %}">
|
<div class="usa-identifier__container usa-identifier__container--widescreen padding-x--widescreen">
|
||||||
<div class="usa-identifier__logos">
|
<div class="usa-identifier__logos">
|
||||||
<a rel="noopener noreferrer" target="_blank" href="https://www.cisa.gov" class="usa-identifier__logo"
|
<a rel="noopener noreferrer" target="_blank" href="https://www.cisa.gov" class="usa-identifier__logo"
|
||||||
><img
|
><img
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
class="usa-identifier__section usa-identifier__section--required-links"
|
class="usa-identifier__section usa-identifier__section--required-links"
|
||||||
aria-label="Important links"
|
aria-label="Important links"
|
||||||
>
|
>
|
||||||
<div class="usa-identifier__container {% if is_widescreen_mode %} usa-identifier__container--widescreen {% endif %}">
|
<div class="usa-identifier__container usa-identifier__container--widescreen padding-x--widescreen">
|
||||||
<ul class="usa-identifier__required-links-list">
|
<ul class="usa-identifier__required-links-list">
|
||||||
<li class="usa-identifier__required-links-item">
|
<li class="usa-identifier__required-links-item">
|
||||||
<a rel="noopener noreferrer" target="_blank" href="{% public_site_url 'about/' %}"
|
<a rel="noopener noreferrer" target="_blank" href="{% public_site_url 'about/' %}"
|
||||||
|
@ -119,7 +119,7 @@
|
||||||
class="usa-identifier__section usa-identifier__section--usagov"
|
class="usa-identifier__section usa-identifier__section--usagov"
|
||||||
aria-label="U.S. government information and services"
|
aria-label="U.S. government information and services"
|
||||||
>
|
>
|
||||||
<div class="usa-identifier__container {% if is_widescreen_mode %} usa-identifier__container--widescreen {% endif %}">
|
<div class="usa-identifier__container usa-identifier__container--widescreen padding-x--widescreen">
|
||||||
<div class="usa-identifier__usagov-description">
|
<div class="usa-identifier__usagov-description">
|
||||||
Looking for U.S. government information and services?
|
Looking for U.S. government information and services?
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
<header class="usa-header usa-header--basic">
|
<header class="usa-header usa-header--basic">
|
||||||
<div class="usa-nav-container {% if is_widescreen_mode %} usa-nav-container--widescreen {% endif %}">
|
<div class="usa-nav-container usa-nav-container--widescreen padding-x--widescreen">
|
||||||
<div class="usa-navbar">
|
<div class="usa-navbar">
|
||||||
{% include "includes/gov_extended_logo.html" with logo_clickable=logo_clickable %}
|
{% include "includes/gov_extended_logo.html" with logo_clickable=logo_clickable %}
|
||||||
<button type="button" class="usa-menu-btn">Menu</button>
|
<button type="button" class="usa-menu-btn">Menu</button>
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
{% load custom_filters %}
|
{% load custom_filters %}
|
||||||
|
|
||||||
<header class="usa-header usa-header--extended">
|
<header class="usa-header usa-header--extended">
|
||||||
<div class="usa-navbar {% if is_widescreen_mode %} usa-navbar--widescreen {% endif %}">
|
<div class="usa-navbar usa-navbar--widescreen padding-x--widescreen">
|
||||||
{% include "includes/gov_extended_logo.html" with logo_clickable=logo_clickable %}
|
{% include "includes/gov_extended_logo.html" with logo_clickable=logo_clickable %}
|
||||||
<button type="button" class="usa-menu-btn">Menu</button>
|
<button type="button" class="usa-menu-btn">Menu</button>
|
||||||
</div>
|
</div>
|
||||||
{% block usa_nav %}
|
{% block usa_nav %}
|
||||||
<nav class="usa-nav" aria-label="Primary navigation">
|
<nav class="usa-nav" aria-label="Primary navigation">
|
||||||
<div class="usa-nav__inner {% if is_widescreen_mode %} usa-nav__inner--widescreen {% endif %}">
|
<div class="usa-nav__inner usa-nav__inner--widescreen padding-x--widescreen">
|
||||||
<button type="button" class="usa-nav__close">
|
<button type="button" class="usa-nav__close">
|
||||||
<img src="{%static 'img/usa-icons/close.svg'%}" role="img" alt="Close" />
|
<img src="{%static 'img/usa-icons/close.svg'%}" role="img" alt="Close" />
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<section class="section-outlined members margin-top-0 section-outlined--border-base-light" id="members">
|
<section class="section-outlined members margin-top-0 section-outlined--border-base-light" id="members">
|
||||||
<div class="section-outlined__header margin-bottom-3 grid-row">
|
<div class="section-outlined__header margin-bottom-3 grid-row">
|
||||||
<!-- ---------- SEARCH ---------- -->
|
<!-- ---------- SEARCH ---------- -->
|
||||||
<div class="section-outlined__search mobile:grid-col-12 desktop:grid-col-6 {% if is_widescreen_mode %} section-outlined__search--widescreen {% endif %}">
|
<div class="section-outlined__search mobile:grid-col-12 desktop:grid-col-6 section-outlined__search--widescreen">
|
||||||
<section aria-label="Members search component" class="margin-top-2">
|
<section aria-label="Members search component" class="margin-top-2">
|
||||||
<form class="usa-search usa-search--small" method="POST" role="search">
|
<form class="usa-search usa-search--small" method="POST" role="search">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
{% load custom_filters %}
|
{% load custom_filters %}
|
||||||
{% load static url_helpers %}
|
{% load static url_helpers %}
|
||||||
<main id="main-content" class="grid-container">
|
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||||
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
|
<div class="{% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
|
<div class="grid-col desktop:grid-col-8 desktop:grid-offset-2 ">
|
||||||
{% block breadcrumb %}
|
{% block breadcrumb %}
|
||||||
{% if portfolio %}
|
{% if portfolio %}
|
||||||
{% url 'domain-requests' as url %}
|
{% url 'domain-requests' as url %}
|
||||||
|
@ -139,7 +140,7 @@
|
||||||
{% endblock modify_request %}
|
{% endblock modify_request %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid-col desktop:grid-offset-2 maxw-tablet">
|
<div class="grid-col maxw-fit-content desktop:grid-offset-2 ">
|
||||||
{% block request_summary_header %}
|
{% block request_summary_header %}
|
||||||
<h2 class="text-primary-darker"> Summary of your domain request </h2>
|
<h2 class="text-primary-darker"> Summary of your domain request </h2>
|
||||||
{% endblock request_summary_header%}
|
{% endblock request_summary_header%}
|
||||||
|
@ -237,4 +238,5 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock request_summary%}
|
{% endblock request_summary%}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
|
@ -1,3 +1,3 @@
|
||||||
<p class="{% if not remove_margin_top %}margin-top-3 {% endif %}">
|
<p class="{% if not remove_margin_top %}margin-top-3{% endif %}">
|
||||||
<em>Required fields are marked with an asterisk (<abbr class="usa-hint usa-hint--required" title="required">*</abbr>).</em>
|
<em>Required fields are marked with an asterisk (<abbr class="usa-hint usa-hint--required" title="required">*</abbr>).</em>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
{% load static field_helpers url_helpers %}
|
{% load static field_helpers url_helpers %}
|
||||||
|
|
||||||
|
<!-- Form messages -->
|
||||||
|
{% block messages %}
|
||||||
|
{% include "includes/form_messages.html" %}
|
||||||
|
{% endblock messages%}
|
||||||
{% if can_edit %}
|
{% if can_edit %}
|
||||||
{% include "includes/form_errors.html" with form=form %}
|
{% include "includes/form_errors.html" with form=form %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -4,14 +4,12 @@
|
||||||
<div id="wrapper" class="{% block wrapper_class %}wrapper--padding-top-6{% endblock %}">
|
<div id="wrapper" class="{% block wrapper_class %}wrapper--padding-top-6{% endblock %}">
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<main class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
<main class="grid-container grid-container--widescreen">
|
||||||
{% if user.is_authenticated %}
|
{% if user.is_authenticated %}
|
||||||
{# the entire logged in page goes here #}
|
{# the entire logged in page goes here #}
|
||||||
|
|
||||||
<div class="tablet:grid-col-11 desktop:grid-col-10 tablet:grid-offset-1">
|
<div class="grid-row {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
{% block messages %}
|
<div class="tablet:grid-col-11 desktop:grid-col-10 {% if is_widescreen_centered %}tablet:grid-offset-1{% endif %}">
|
||||||
{% include "includes/form_messages.html" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block portfolio_content %}{% endblock %}
|
{% block portfolio_content %}{% endblock %}
|
||||||
|
|
||||||
|
@ -24,6 +22,8 @@
|
||||||
</a></p>
|
</a></p>
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{% endblock content%}
|
{% endblock content%}
|
||||||
|
|
|
@ -9,9 +9,11 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
|
|
||||||
|
<!-- Form messages -->
|
||||||
{% block messages %}
|
{% block messages %}
|
||||||
{% include "includes/form_messages.html" %}
|
{% include "includes/form_messages.html" %}
|
||||||
{% endblock %}
|
{% endblock messages%}
|
||||||
|
|
||||||
<div id="main-content">
|
<div id="main-content">
|
||||||
<h1 id="domains-header">Domains</h1>
|
<h1 id="domains-header">Domains</h1>
|
||||||
|
|
|
@ -8,7 +8,12 @@ Organization member
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
<div id="main-content">
|
<div id="main-content" class="{% if not is_widescreen_centered %}desktop:grid-offset-2{% endif %}">
|
||||||
|
<!-- Form messages -->
|
||||||
|
{% include "includes/form_errors.html" with form=form %}
|
||||||
|
{% block messages %}
|
||||||
|
{% include "includes/form_messages.html" %}
|
||||||
|
{% endblock messages%}
|
||||||
|
|
||||||
{% url 'members' as url %}
|
{% url 'members' as url %}
|
||||||
<nav class="usa-breadcrumb padding-top-0 margin-bottom-3" aria-label="Portfolio member breadcrumb">
|
<nav class="usa-breadcrumb padding-top-0 margin-bottom-3" aria-label="Portfolio member breadcrumb">
|
||||||
|
|
|
@ -6,7 +6,12 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
<div id="main-content">
|
<div id="main-content" class=" {% if not is_widescreen_centered %}desktop:grid-offset-2{% endif %}">
|
||||||
|
<!-- Form messages -->
|
||||||
|
{% include "includes/form_errors.html" with form=form %}
|
||||||
|
{% block messages %}
|
||||||
|
{% include "includes/form_messages.html" %}
|
||||||
|
{% endblock messages%}
|
||||||
|
|
||||||
{% url 'members' as url %}
|
{% url 'members' as url %}
|
||||||
{% if portfolio_permission %}
|
{% if portfolio_permission %}
|
||||||
|
|
|
@ -6,7 +6,12 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
<div id="main-content">
|
<div id="main-content" class=" {% if not is_widescreen_centered %}desktop:grid-offset-2{% endif %}">
|
||||||
|
<!-- Form messages -->
|
||||||
|
{% include "includes/form_errors.html" with form=form %}
|
||||||
|
{% block messages %}
|
||||||
|
{% include "includes/form_messages.html" %}
|
||||||
|
{% endblock messages%}
|
||||||
|
|
||||||
{% url 'members' as url %}
|
{% url 'members' as url %}
|
||||||
{% if portfolio_permission %}
|
{% if portfolio_permission %}
|
||||||
|
|
|
@ -11,8 +11,15 @@
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
{% include "includes/form_errors.html" with form=form %}
|
{% include "includes/form_errors.html" with form=form %}
|
||||||
|
|
||||||
<!-- Navigation breadcrumbs -->
|
<div id="main-content" class=" {% if not is_widescreen_centered %}desktop:grid-offset-2{% endif %}">
|
||||||
<nav class="usa-breadcrumb padding-top-0 bg-gray-1" aria-label="Domain request breadcrumb">
|
<!-- Form messages -->
|
||||||
|
{% include "includes/form_errors.html" with form=form %}
|
||||||
|
{% block messages %}
|
||||||
|
{% include "includes/form_messages.html" %}
|
||||||
|
{% endblock messages%}
|
||||||
|
|
||||||
|
<!-- Navigation breadcrumbs -->
|
||||||
|
<nav class="usa-breadcrumb padding-top-0 bg-gray-1" aria-label="Domain request breadcrumb">
|
||||||
<ol class="usa-breadcrumb__list">
|
<ol class="usa-breadcrumb__list">
|
||||||
<li class="usa-breadcrumb__list-item">
|
<li class="usa-breadcrumb__list-item">
|
||||||
<a href="{% url 'members' %}" class="usa-breadcrumb__link"><span>Members</span></a>
|
<a href="{% url 'members' %}" class="usa-breadcrumb__link"><span>Members</span></a>
|
||||||
|
@ -30,14 +37,14 @@
|
||||||
<span>Member access and permissions</span>
|
<span>Member access and permissions</span>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Page header -->
|
<!-- Page header -->
|
||||||
<h1>Member access and permissions</h1>
|
<h1>Member access and permissions</h1>
|
||||||
|
|
||||||
{% include "includes/required_fields.html" with remove_margin_top=True %}
|
{% include "includes/required_fields.html" with remove_margin_top=True %}
|
||||||
|
|
||||||
<form class="usa-form usa-form--large" method="post" id="member_form" novalidate>
|
<form class="usa-form usa-form--large" method="post" id="member_form" novalidate>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<fieldset class="usa-fieldset">
|
<fieldset class="usa-fieldset">
|
||||||
<legend>
|
<legend>
|
||||||
|
@ -69,8 +76,8 @@
|
||||||
<!-- Member email -->
|
<!-- Member email -->
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<!-- Member access radio buttons (Toggles other sections) -->
|
<!-- Member access radio buttons (Toggles other sections) -->
|
||||||
<fieldset class="usa-fieldset">
|
<fieldset class="usa-fieldset">
|
||||||
<legend>
|
<legend>
|
||||||
<h2 class="margin-top-0">Member Access</h2>
|
<h2 class="margin-top-0">Member Access</h2>
|
||||||
</legend>
|
</legend>
|
||||||
|
@ -81,10 +88,10 @@
|
||||||
{% input_with_errors form.role %}
|
{% input_with_errors form.role %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<!-- Admin access form -->
|
<!-- Admin access form -->
|
||||||
<div id="member-admin-permissions" class="margin-top-2">
|
<div id="member-admin-permissions" class="margin-top-2">
|
||||||
<h2>Admin access permissions</h2>
|
<h2>Admin access permissions</h2>
|
||||||
<p>Member permissions available for admin-level acccess.</p>
|
<p>Member permissions available for admin-level acccess.</p>
|
||||||
|
|
||||||
|
@ -128,5 +135,6 @@
|
||||||
</a>
|
</a>
|
||||||
<button type="submit" class="usa-button">Update Member</button>
|
<button type="submit" class="usa-button">Update Member</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
{% endblock portfolio_content%}
|
{% endblock portfolio_content%}
|
||||||
|
|
|
@ -10,6 +10,11 @@
|
||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
|
|
||||||
|
<!-- Form messages -->
|
||||||
|
{% block messages %}
|
||||||
|
{% include "includes/form_messages.html" %}
|
||||||
|
{% endblock messages%}
|
||||||
|
|
||||||
<div id="main-content">
|
<div id="main-content">
|
||||||
<div id="toggleable-alert" class="usa-alert usa-alert--slim margin-bottom-2 display-none">
|
<div id="toggleable-alert" class="usa-alert usa-alert--slim margin-bottom-2 display-none">
|
||||||
<div class="usa-alert__body usa-alert__body--widescreen">
|
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||||
|
|
|
@ -10,11 +10,12 @@
|
||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
|
|
||||||
<!-- Form mesages -->
|
<div id="main-content" class=" {% if not is_widescreen_centered %}desktop:grid-offset-2{% endif %}">
|
||||||
{% include "includes/form_errors.html" with form=form %}
|
<!-- Form messages -->
|
||||||
{% block messages %}
|
{% include "includes/form_errors.html" with form=form %}
|
||||||
|
{% block messages %}
|
||||||
{% include "includes/form_messages.html" %}
|
{% include "includes/form_messages.html" %}
|
||||||
{% endblock messages%}
|
{% endblock messages%}
|
||||||
|
|
||||||
<!-- Navigation breadcrumbs -->
|
<!-- Navigation breadcrumbs -->
|
||||||
<nav class="usa-breadcrumb padding-top-0 bg-gray-1" aria-label="Domain request breadcrumb">
|
<nav class="usa-breadcrumb padding-top-0 bg-gray-1" aria-label="Domain request breadcrumb">
|
||||||
|
@ -28,16 +29,16 @@
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Page header -->
|
<!-- Page header -->
|
||||||
{% block new_member_header %}
|
{% block new_member_header %}
|
||||||
<h1>Add a new member</h1>
|
<h1>Add a new member</h1>
|
||||||
{% endblock new_member_header %}
|
{% endblock new_member_header %}
|
||||||
|
|
||||||
{% include "includes/required_fields.html" %}
|
{% include "includes/required_fields.html" %}
|
||||||
|
|
||||||
<form class="usa-form usa-form--large" method="post" id="add_member_form" novalidate>
|
<form class="usa-form usa-form--large" method="post" id="add_member_form" novalidate>
|
||||||
|
|
||||||
<fieldset class="usa-fieldset margin-top-2">
|
<fieldset class="usa-fieldset margin-top-2">
|
||||||
<legend>
|
<legend>
|
||||||
<h2>Email</h2>
|
<h2>Email</h2>
|
||||||
</legend>
|
</legend>
|
||||||
|
@ -46,10 +47,10 @@
|
||||||
{% with group_classes="usa-form-editable usa-form-editable--no-border padding-top-0" %}
|
{% with group_classes="usa-form-editable usa-form-editable--no-border padding-top-0" %}
|
||||||
{% input_with_errors form.email %}
|
{% input_with_errors form.email %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<!-- Member access radio buttons (Toggles other sections) -->
|
<!-- Member access radio buttons (Toggles other sections) -->
|
||||||
<fieldset class="usa-fieldset margin-top-2">
|
<fieldset class="usa-fieldset margin-top-2">
|
||||||
<legend>
|
<legend>
|
||||||
<h2>Member Access</h2>
|
<h2>Member Access</h2>
|
||||||
</legend>
|
</legend>
|
||||||
|
@ -60,10 +61,10 @@
|
||||||
{% input_with_errors form.role %}
|
{% input_with_errors form.role %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
|
||||||
<!-- Admin access form -->
|
<!-- Admin access form -->
|
||||||
<div id="new-member-admin-permissions" class="margin-top-2">
|
<div id="new-member-admin-permissions" class="margin-top-2">
|
||||||
<h2>Admin access permissions</h2>
|
<h2>Admin access permissions</h2>
|
||||||
<p>Member permissions available for admin-level acccess.</p>
|
<p>Member permissions available for admin-level acccess.</p>
|
||||||
|
|
||||||
|
@ -83,8 +84,8 @@
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Basic access form -->
|
<!-- Basic access form -->
|
||||||
<div id="new-member-basic-permissions" class="margin-top-2">
|
<div id="new-member-basic-permissions" class="margin-top-2">
|
||||||
<h2>Basic member permissions</h2>
|
<h2>Basic member permissions</h2>
|
||||||
<p>Member permissions available for basic-level acccess.</p>
|
<p>Member permissions available for basic-level acccess.</p>
|
||||||
|
|
||||||
|
@ -94,8 +95,8 @@
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Submit/cancel buttons -->
|
<!-- Submit/cancel buttons -->
|
||||||
<div class="margin-top-3">
|
<div class="margin-top-3">
|
||||||
<a
|
<a
|
||||||
type="button"
|
type="button"
|
||||||
href="{% url 'members' %}"
|
href="{% url 'members' %}"
|
||||||
|
@ -103,7 +104,7 @@
|
||||||
name="btn-cancel-click"
|
name="btn-cancel-click"
|
||||||
aria-label="Cancel adding new member"
|
aria-label="Cancel adding new member"
|
||||||
>Cancel
|
>Cancel
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
id="invite_member_trigger"
|
id="invite_member_trigger"
|
||||||
href="#invite-member-modal"
|
href="#invite-member-modal"
|
||||||
|
@ -112,8 +113,8 @@
|
||||||
data-open-modal
|
data-open-modal
|
||||||
>Trigger invite member modal</a>
|
>Trigger invite member modal</a>
|
||||||
<button id="invite_new_member_submit" type="submit" class="usa-button">Invite Member</button>
|
<button id="invite_new_member_submit" type="submit" class="usa-button">Invite Member</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="usa-modal"
|
class="usa-modal"
|
||||||
|
@ -170,6 +171,7 @@
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,8 +4,13 @@
|
||||||
|
|
||||||
{% block title %} Domains | {% endblock %}
|
{% block title %} Domains | {% endblock %}
|
||||||
|
|
||||||
|
<!-- Form messages -->
|
||||||
|
{% block messages %}
|
||||||
|
{% include "includes/form_messages.html" %}
|
||||||
|
{% endblock messages%}
|
||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
<div id="main-content">
|
<div id="main-content" class="{% if not is_widescreen_centered %}desktop:grid-offset-2{% endif %}">
|
||||||
<h1 id="domains-header">Domains</h1>
|
<h1 id="domains-header">Domains</h1>
|
||||||
<section class="section-outlined">
|
<section class="section-outlined">
|
||||||
<div class="section-outlined__header margin-bottom-3">
|
<div class="section-outlined__header margin-bottom-3">
|
||||||
|
|
|
@ -4,7 +4,13 @@
|
||||||
|
|
||||||
{% block title %} Domain Requests | {% endblock %}
|
{% block title %} Domain Requests | {% endblock %}
|
||||||
|
|
||||||
|
<!-- Form messages -->
|
||||||
|
{% block messages %}
|
||||||
|
{% include "includes/form_messages.html" %}
|
||||||
|
{% endblock messages%}
|
||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
|
<div id="main-content" class="{% if not is_widescreen_centered %}desktop:grid-offset-2{% endif %}">
|
||||||
<h1 id="domains-header">Domain requests</h1>
|
<h1 id="domains-header">Domain requests</h1>
|
||||||
<section class="section-outlined">
|
<section class="section-outlined">
|
||||||
<div class="section-outlined__header margin-bottom-3">
|
<div class="section-outlined__header margin-bottom-3">
|
||||||
|
@ -27,4 +33,5 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -18,12 +18,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tablet:grid-col-9" id="main-content">
|
<div class="tablet:grid-col-9" id="main-content">
|
||||||
|
<!-- Form messages -->
|
||||||
|
{% include "includes/form_errors.html" with form=form %}
|
||||||
{% block messages %}
|
{% block messages %}
|
||||||
{% include "includes/form_messages.html" %}
|
{% include "includes/form_messages.html" %}
|
||||||
{% endblock %}
|
{% endblock messages%}
|
||||||
|
|
||||||
{% include "includes/form_errors.html" with form=form %}
|
|
||||||
|
|
||||||
<h1>Organization</h1>
|
<h1>Organization</h1>
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,11 @@
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
|
|
||||||
|
<!-- Form messages -->
|
||||||
{% block messages %}
|
{% block messages %}
|
||||||
{% include "includes/form_messages.html" %}
|
{% include "includes/form_messages.html" %}
|
||||||
{% endblock %}
|
{% endblock messages%}
|
||||||
|
|
||||||
<div id="main-content">
|
<div id="main-content">
|
||||||
<h1 id="domain-requests-header" class="margin-bottom-1">Domain requests</h1>
|
<h1 id="domain-requests-header" class="margin-bottom-1">Domain requests</h1>
|
||||||
|
|
|
@ -6,9 +6,6 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
|
|
||||||
{% block portfolio_content %}
|
{% block portfolio_content %}
|
||||||
{% block messages %}
|
|
||||||
{% include "includes/form_messages.html" %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
<div class="grid-row grid-gap">
|
<div class="grid-row grid-gap">
|
||||||
<div class="tablet:grid-col-3">
|
<div class="tablet:grid-col-3">
|
||||||
|
|
|
@ -11,8 +11,9 @@ Edit your User Profile |
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main id="main-content" class="grid-container">
|
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||||
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
|
<div class="{% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||||
|
<div class="desktop:grid-col-8 desktop:grid-offset-2">
|
||||||
{% if messages %}
|
{% if messages %}
|
||||||
{% for message in messages %}
|
{% for message in messages %}
|
||||||
<div class="usa-alert usa-alert--{{ message.tags }} usa-alert--slim margin-bottom-3">
|
<div class="usa-alert usa-alert--{{ message.tags }} usa-alert--slim margin-bottom-3">
|
||||||
|
@ -90,11 +91,12 @@ Edit your User Profile |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
{% block content_bottom %}
|
{% block content_bottom %}
|
||||||
{% include "includes/profile_form.html" with form=form %}
|
{% include "includes/profile_form.html" with form=form %}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
{% endblock content_bottom %}
|
{% endblock content_bottom %}
|
||||||
|
|
||||||
|
|
|
@ -1034,6 +1034,10 @@ def completed_domain_request( # noqa
|
||||||
action_needed_reason=None,
|
action_needed_reason=None,
|
||||||
portfolio=None,
|
portfolio=None,
|
||||||
organization_name=None,
|
organization_name=None,
|
||||||
|
sub_organization=None,
|
||||||
|
requested_suborganization=None,
|
||||||
|
suborganization_city=None,
|
||||||
|
suborganization_state_territory=None,
|
||||||
):
|
):
|
||||||
"""A completed domain request."""
|
"""A completed domain request."""
|
||||||
if not user:
|
if not user:
|
||||||
|
@ -1098,6 +1102,18 @@ def completed_domain_request( # noqa
|
||||||
if portfolio:
|
if portfolio:
|
||||||
domain_request_kwargs["portfolio"] = portfolio
|
domain_request_kwargs["portfolio"] = portfolio
|
||||||
|
|
||||||
|
if sub_organization:
|
||||||
|
domain_request_kwargs["sub_organization"] = sub_organization
|
||||||
|
|
||||||
|
if requested_suborganization:
|
||||||
|
domain_request_kwargs["requested_suborganization"] = requested_suborganization
|
||||||
|
|
||||||
|
if suborganization_city:
|
||||||
|
domain_request_kwargs["suborganization_city"] = suborganization_city
|
||||||
|
|
||||||
|
if suborganization_state_territory:
|
||||||
|
domain_request_kwargs["suborganization_state_territory"] = suborganization_state_territory
|
||||||
|
|
||||||
domain_request, _ = DomainRequest.objects.get_or_create(**domain_request_kwargs)
|
domain_request, _ = DomainRequest.objects.get_or_create(**domain_request_kwargs)
|
||||||
|
|
||||||
if has_other_contacts:
|
if has_other_contacts:
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from django.forms import ValidationError
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
|
from waffle.testutils import override_flag
|
||||||
import re
|
import re
|
||||||
from django.test import RequestFactory, Client, TestCase, override_settings
|
from django.test import RequestFactory, Client, TestCase, override_settings
|
||||||
from django.contrib.admin.sites import AdminSite
|
from django.contrib.admin.sites import AdminSite
|
||||||
|
@ -24,6 +26,7 @@ from registrar.models import (
|
||||||
SeniorOfficial,
|
SeniorOfficial,
|
||||||
Portfolio,
|
Portfolio,
|
||||||
AllowedEmail,
|
AllowedEmail,
|
||||||
|
Suborganization,
|
||||||
)
|
)
|
||||||
from .common import (
|
from .common import (
|
||||||
MockSESClient,
|
MockSESClient,
|
||||||
|
@ -82,6 +85,7 @@ class TestDomainRequestAdmin(MockEppLib):
|
||||||
Contact.objects.all().delete()
|
Contact.objects.all().delete()
|
||||||
Website.objects.all().delete()
|
Website.objects.all().delete()
|
||||||
SeniorOfficial.objects.all().delete()
|
SeniorOfficial.objects.all().delete()
|
||||||
|
Suborganization.objects.all().delete()
|
||||||
Portfolio.objects.all().delete()
|
Portfolio.objects.all().delete()
|
||||||
self.mock_client.EMAILS_SENT.clear()
|
self.mock_client.EMAILS_SENT.clear()
|
||||||
|
|
||||||
|
@ -91,6 +95,83 @@ class TestDomainRequestAdmin(MockEppLib):
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
AllowedEmail.objects.all().delete()
|
AllowedEmail.objects.all().delete()
|
||||||
|
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_clean_validates_duplicate_suborganization(self):
|
||||||
|
"""Tests that clean() prevents duplicate suborganization names within the same portfolio"""
|
||||||
|
# Create a portfolio and existing suborganization
|
||||||
|
portfolio = Portfolio.objects.create(organization_name="Test Portfolio", creator=self.superuser)
|
||||||
|
|
||||||
|
# Create an existing suborganization
|
||||||
|
Suborganization.objects.create(name="Existing Suborg", portfolio=portfolio)
|
||||||
|
|
||||||
|
# Create a domain request trying to use the same suborganization name
|
||||||
|
# (intentionally lowercase)
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
name="test1234.gov",
|
||||||
|
portfolio=portfolio,
|
||||||
|
requested_suborganization="existing suborg",
|
||||||
|
suborganization_city="Rome",
|
||||||
|
suborganization_state_territory=DomainRequest.StateTerritoryChoices.OHIO,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert that the validation error is raised
|
||||||
|
with self.assertRaises(ValidationError) as err:
|
||||||
|
domain_request.clean()
|
||||||
|
|
||||||
|
self.assertIn("This suborganization already exists", str(err.exception))
|
||||||
|
|
||||||
|
# Test that a different name is allowed. Should not raise a error.
|
||||||
|
domain_request.requested_suborganization = "New Suborg"
|
||||||
|
domain_request.clean()
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
def test_clean_validates_partial_suborganization_fields(self):
|
||||||
|
"""Tests that clean() enforces all-or-nothing rule for suborganization fields"""
|
||||||
|
portfolio = Portfolio.objects.create(organization_name="Test Portfolio", creator=self.superuser)
|
||||||
|
|
||||||
|
# Create domain request with only city filled out
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
name="test1234.gov",
|
||||||
|
portfolio=portfolio,
|
||||||
|
suborganization_city="Test City",
|
||||||
|
)
|
||||||
|
|
||||||
|
# Assert validation error is raised with correct missing fields
|
||||||
|
with self.assertRaises(ValidationError) as err:
|
||||||
|
domain_request.clean()
|
||||||
|
|
||||||
|
error_dict = err.exception.error_dict
|
||||||
|
expected_missing = ["requested_suborganization", "suborganization_state_territory"]
|
||||||
|
|
||||||
|
# Verify correct fields are flagged as required
|
||||||
|
self.assertEqual(sorted(error_dict.keys()), sorted(expected_missing))
|
||||||
|
|
||||||
|
# Verify error message
|
||||||
|
for field in expected_missing:
|
||||||
|
self.assertEqual(
|
||||||
|
str(error_dict[field][0].message), "This field is required when creating a new suborganization."
|
||||||
|
)
|
||||||
|
|
||||||
|
# When all data is passed in, this should validate correctly
|
||||||
|
domain_request.requested_suborganization = "Complete Suborg"
|
||||||
|
domain_request.suborganization_state_territory = DomainRequest.StateTerritoryChoices.OHIO
|
||||||
|
# Assert that no ValidationError is raised
|
||||||
|
try:
|
||||||
|
domain_request.clean()
|
||||||
|
except ValidationError as e:
|
||||||
|
self.fail(f"ValidationError was raised unexpectedly: {e}")
|
||||||
|
|
||||||
|
# Also ensure that no validation error is raised if nothing is passed in at all
|
||||||
|
domain_request.suborganization_city = None
|
||||||
|
domain_request.requested_suborganization = None
|
||||||
|
domain_request.suborganization_state_territory = None
|
||||||
|
try:
|
||||||
|
domain_request.clean()
|
||||||
|
except ValidationError as e:
|
||||||
|
self.fail(f"ValidationError was raised unexpectedly: {e}")
|
||||||
|
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_domain_request_senior_official_is_alphabetically_sorted(self):
|
def test_domain_request_senior_official_is_alphabetically_sorted(self):
|
||||||
"""Tests if the senior offical dropdown is alphanetically sorted in the django admin display"""
|
"""Tests if the senior offical dropdown is alphanetically sorted in the django admin display"""
|
||||||
|
|
|
@ -15,6 +15,7 @@ from registrar.models import (
|
||||||
FederalAgency,
|
FederalAgency,
|
||||||
AllowedEmail,
|
AllowedEmail,
|
||||||
Portfolio,
|
Portfolio,
|
||||||
|
Suborganization,
|
||||||
)
|
)
|
||||||
|
|
||||||
import boto3_mocking
|
import boto3_mocking
|
||||||
|
@ -23,6 +24,8 @@ from registrar.utility.errors import FSMDomainRequestError
|
||||||
|
|
||||||
from .common import (
|
from .common import (
|
||||||
MockSESClient,
|
MockSESClient,
|
||||||
|
create_user,
|
||||||
|
create_superuser,
|
||||||
less_console_noise,
|
less_console_noise,
|
||||||
completed_domain_request,
|
completed_domain_request,
|
||||||
set_domain_request_investigators,
|
set_domain_request_investigators,
|
||||||
|
@ -1070,3 +1073,142 @@ class TestDomainRequest(TestCase):
|
||||||
)
|
)
|
||||||
self.assertEqual(domain_request2.generic_org_type, domain_request2.converted_generic_org_type)
|
self.assertEqual(domain_request2.generic_org_type, domain_request2.converted_generic_org_type)
|
||||||
self.assertEqual(domain_request2.federal_agency, domain_request2.converted_federal_agency)
|
self.assertEqual(domain_request2.federal_agency, domain_request2.converted_federal_agency)
|
||||||
|
|
||||||
|
|
||||||
|
class TestDomainRequestSuborganization(TestCase):
|
||||||
|
"""Tests for the suborganization fields on domain requests"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super().setUp()
|
||||||
|
self.user = create_user()
|
||||||
|
self.superuser = create_superuser()
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super().tearDown()
|
||||||
|
DomainInformation.objects.all().delete()
|
||||||
|
DomainRequest.objects.all().delete()
|
||||||
|
Domain.objects.all().delete()
|
||||||
|
Suborganization.objects.all().delete()
|
||||||
|
Portfolio.objects.all().delete()
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_approve_creates_requested_suborganization(self):
|
||||||
|
"""Test that approving a domain request with a requested suborganization creates it"""
|
||||||
|
portfolio = Portfolio.objects.create(organization_name="Test Org", creator=self.user)
|
||||||
|
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
name="test.gov",
|
||||||
|
portfolio=portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
||||||
|
requested_suborganization="Boom",
|
||||||
|
suborganization_city="Explody town",
|
||||||
|
suborganization_state_territory=DomainRequest.StateTerritoryChoices.OHIO,
|
||||||
|
)
|
||||||
|
domain_request.investigator = self.superuser
|
||||||
|
domain_request.save()
|
||||||
|
|
||||||
|
domain_request.approve()
|
||||||
|
|
||||||
|
created_suborg = Suborganization.objects.filter(
|
||||||
|
name="Boom",
|
||||||
|
city="Explody town",
|
||||||
|
state_territory=DomainRequest.StateTerritoryChoices.OHIO,
|
||||||
|
portfolio=portfolio,
|
||||||
|
).first()
|
||||||
|
|
||||||
|
self.assertIsNotNone(created_suborg)
|
||||||
|
self.assertEqual(domain_request.sub_organization, created_suborg)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_approve_without_requested_suborganization_makes_no_changes(self):
|
||||||
|
"""Test that approving without a requested suborganization doesn't create one"""
|
||||||
|
portfolio = Portfolio.objects.create(organization_name="Test Org", creator=self.user)
|
||||||
|
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
name="test.gov",
|
||||||
|
portfolio=portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
||||||
|
)
|
||||||
|
domain_request.investigator = self.superuser
|
||||||
|
domain_request.save()
|
||||||
|
|
||||||
|
initial_suborg_count = Suborganization.objects.count()
|
||||||
|
domain_request.approve()
|
||||||
|
|
||||||
|
self.assertEqual(Suborganization.objects.count(), initial_suborg_count)
|
||||||
|
self.assertIsNone(domain_request.sub_organization)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_approve_with_existing_suborganization_makes_no_changes(self):
|
||||||
|
"""Test that approving with an existing suborganization doesn't create a new one"""
|
||||||
|
portfolio = Portfolio.objects.create(organization_name="Test Org", creator=self.user)
|
||||||
|
existing_suborg = Suborganization.objects.create(name="Existing Division", portfolio=portfolio)
|
||||||
|
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
name="test.gov",
|
||||||
|
portfolio=portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
||||||
|
sub_organization=existing_suborg,
|
||||||
|
)
|
||||||
|
domain_request.investigator = self.superuser
|
||||||
|
domain_request.save()
|
||||||
|
|
||||||
|
initial_suborg_count = Suborganization.objects.count()
|
||||||
|
domain_request.approve()
|
||||||
|
|
||||||
|
self.assertEqual(Suborganization.objects.count(), initial_suborg_count)
|
||||||
|
self.assertEqual(domain_request.sub_organization, existing_suborg)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_cleanup_dangling_suborg_with_single_reference(self):
|
||||||
|
"""Test that a suborganization is deleted when it's only referenced once"""
|
||||||
|
portfolio = Portfolio.objects.create(organization_name="Test Org", creator=self.user)
|
||||||
|
suborg = Suborganization.objects.create(name="Test Division", portfolio=portfolio)
|
||||||
|
|
||||||
|
domain_request = completed_domain_request(
|
||||||
|
name="test.gov",
|
||||||
|
portfolio=portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
||||||
|
sub_organization=suborg,
|
||||||
|
)
|
||||||
|
domain_request.approve()
|
||||||
|
|
||||||
|
# set it back to in review
|
||||||
|
domain_request.in_review()
|
||||||
|
domain_request.refresh_from_db()
|
||||||
|
|
||||||
|
# Verify the suborganization was deleted
|
||||||
|
self.assertFalse(Suborganization.objects.filter(id=suborg.id).exists())
|
||||||
|
self.assertIsNone(domain_request.sub_organization)
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
def test_cleanup_dangling_suborg_with_multiple_references(self):
|
||||||
|
"""Test that a suborganization is preserved when it has multiple references"""
|
||||||
|
portfolio = Portfolio.objects.create(organization_name="Test Org", creator=self.user)
|
||||||
|
suborg = Suborganization.objects.create(name="Test Division", portfolio=portfolio)
|
||||||
|
|
||||||
|
# Create two domain requests using the same suborganization
|
||||||
|
domain_request1 = completed_domain_request(
|
||||||
|
name="test1.gov",
|
||||||
|
portfolio=portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
||||||
|
sub_organization=suborg,
|
||||||
|
)
|
||||||
|
domain_request2 = completed_domain_request(
|
||||||
|
name="test2.gov",
|
||||||
|
portfolio=portfolio,
|
||||||
|
status=DomainRequest.DomainRequestStatus.IN_REVIEW,
|
||||||
|
sub_organization=suborg,
|
||||||
|
)
|
||||||
|
|
||||||
|
domain_request1.approve()
|
||||||
|
domain_request2.approve()
|
||||||
|
|
||||||
|
# set one back to in review
|
||||||
|
domain_request1.in_review()
|
||||||
|
domain_request1.refresh_from_db()
|
||||||
|
|
||||||
|
# Verify the suborganization still exists
|
||||||
|
self.assertTrue(Suborganization.objects.filter(id=suborg.id).exists())
|
||||||
|
self.assertEqual(domain_request1.sub_organization, suborg)
|
||||||
|
self.assertEqual(domain_request2.sub_organization, suborg)
|
||||||
|
|
|
@ -2576,6 +2576,46 @@ class TestRequestingEntity(WebTest):
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
super().tearDown()
|
super().tearDown()
|
||||||
|
|
||||||
|
@less_console_noise_decorator
|
||||||
|
@override_flag("organization_feature", active=True)
|
||||||
|
@override_flag("organization_requests", active=True)
|
||||||
|
def test_form_validates_duplicate_suborganization(self):
|
||||||
|
"""Tests that form validation prevents duplicate suborganization names within the same portfolio"""
|
||||||
|
# Create an existing suborganization
|
||||||
|
suborganization = Suborganization.objects.create(name="Existing Suborg", portfolio=self.portfolio)
|
||||||
|
|
||||||
|
# Start the domain request process
|
||||||
|
response = self.app.get(reverse("domain-request:start"))
|
||||||
|
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
|
|
||||||
|
# Navigate past the intro page
|
||||||
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
form = response.forms[0]
|
||||||
|
response = form.submit().follow()
|
||||||
|
|
||||||
|
# Fill out the requesting entity form
|
||||||
|
form = response.forms[0]
|
||||||
|
form["portfolio_requesting_entity-requesting_entity_is_suborganization"] = "True"
|
||||||
|
form["portfolio_requesting_entity-is_requesting_new_suborganization"] = "True"
|
||||||
|
form["portfolio_requesting_entity-requested_suborganization"] = suborganization.name.lower()
|
||||||
|
form["portfolio_requesting_entity-suborganization_city"] = "Eggnog"
|
||||||
|
form["portfolio_requesting_entity-suborganization_state_territory"] = DomainRequest.StateTerritoryChoices.OHIO
|
||||||
|
|
||||||
|
# Submit form and verify error
|
||||||
|
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
response = form.submit()
|
||||||
|
self.assertContains(response, "This suborganization already exists")
|
||||||
|
|
||||||
|
# Test that a different name is allowed
|
||||||
|
form["portfolio_requesting_entity-requested_suborganization"] = "New Suborg"
|
||||||
|
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
response = form.submit().follow()
|
||||||
|
|
||||||
|
# Verify successful submission by checking we're on the next page
|
||||||
|
self.assertContains(response, "Current websites")
|
||||||
|
|
||||||
@override_flag("organization_feature", active=True)
|
@override_flag("organization_feature", active=True)
|
||||||
@override_flag("organization_requests", active=True)
|
@override_flag("organization_requests", active=True)
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
|
|
|
@ -417,7 +417,7 @@ class MemberExport(BaseExport):
|
||||||
# Adding a order_by increases output predictability.
|
# Adding a order_by increases output predictability.
|
||||||
# Doesn't matter as much for normal use, but makes tests easier.
|
# Doesn't matter as much for normal use, but makes tests easier.
|
||||||
# We should also just be ordering by default anyway.
|
# We should also just be ordering by default anyway.
|
||||||
members = permissions.union(invitations).order_by("email_display")
|
members = permissions.union(invitations).order_by("email_display", "member_display", "first_name", "last_name")
|
||||||
return convert_queryset_to_dict(members, is_model=False)
|
return convert_queryset_to_dict(members, is_model=False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue