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
|
||||
*/
|
||||
|
@ -636,5 +681,6 @@ export function initDynamicDomainRequestFields(){
|
|||
const domainRequestPage = document.getElementById("domainrequest_form");
|
||||
if (domainRequestPage) {
|
||||
handlePortfolioSelection();
|
||||
handleSuborgFieldsAndButtons();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,13 @@ export function handlePortfolioSelection(
|
|||
const portfolioUrbanizationField = document.querySelector(".field-portfolio_urbanization");
|
||||
const portfolioUrbanization = portfolioUrbanizationField.querySelector(".readonly");
|
||||
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;
|
||||
|
||||
/**
|
||||
|
@ -469,11 +476,28 @@ export function handlePortfolioSelection(
|
|||
if (requestedSuborganizationField) showElement(requestedSuborganizationField);
|
||||
if (suborganizationCity) showElement(suborganizationCity);
|
||||
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 {
|
||||
// Hide suborganization request fields if suborganization is selected
|
||||
if (requestedSuborganizationField) hideElement(requestedSuborganizationField);
|
||||
if (suborganizationCity) hideElement(suborganizationCity);
|
||||
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);
|
||||
}
|
||||
|
||||
// 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,
|
||||
h1, h2, h3,
|
||||
|
|
|
@ -1,21 +1,18 @@
|
|||
@use "uswds-core" 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
|
||||
// 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__body::before {
|
||||
left: 1rem !important;
|
||||
|
@ -24,13 +21,29 @@
|
|||
.usa-alert__body.margin-left-1 {
|
||||
margin-left: 0.5rem!important;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: !important is used because _font.scss overrides this
|
||||
.usa-alert__body--widescreen::before {
|
||||
left: 4rem !important;
|
||||
}
|
||||
.usa-alert__body--widescreen {
|
||||
max-width: $widescreen-max-width !important;
|
||||
padding-left: 7rem!important;
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------
|
||||
Alert Fonts
|
||||
-----------------*/
|
||||
// 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-alert {
|
||||
background-color: $hot-pink;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
@use "cisa_colors" as *;
|
||||
|
||||
$widescreen-max-width: 1920px;
|
||||
$widescreen-x-padding: 4.5rem;
|
||||
|
||||
$hot-pink: #FFC3F9;
|
||||
|
||||
/* Styles for making visible to screen reader / AT users only. */
|
||||
|
@ -253,6 +255,15 @@ abbr[title] {
|
|||
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: -4px;
|
||||
}
|
||||
|
@ -267,3 +278,7 @@ abbr[title] {
|
|||
height: 1.5em;
|
||||
width: 1.5em;
|
||||
}
|
||||
|
||||
.maxw-fit-content {
|
||||
max-width: fit-content;
|
||||
}
|
|
@ -6,3 +6,21 @@
|
|||
.usa-identifier__container--widescreen {
|
||||
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 {
|
||||
// I don't know why USWDS has this at 2 rem, which puts it out of alignment
|
||||
right: 3rem;
|
||||
right: 1rem;
|
||||
padding-right: $widescreen-x-padding;
|
||||
color: color('white');
|
||||
bottom: 4.3rem;
|
||||
.usa-nav-link,
|
||||
|
|
|
@ -251,7 +251,7 @@ TEMPLATES = [
|
|||
"registrar.context_processors.org_user_status",
|
||||
"registrar.context_processors.add_path_to_context",
|
||||
"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
|
||||
|
||||
|
||||
def is_widescreen_mode(request):
|
||||
widescreen_paths = [] # If this list is meant to include specific paths, populate it.
|
||||
portfolio_widescreen_paths = [
|
||||
def is_widescreen_centered(request):
|
||||
include_paths = [
|
||||
"/domains/",
|
||||
"/requests/",
|
||||
"/request/",
|
||||
"/no-organization-requests/",
|
||||
"/no-organization-domains/",
|
||||
"/domain-request/",
|
||||
"/members/",
|
||||
]
|
||||
# widescreen_paths can be a bear as it trickles down sub-urls. exclude_paths gives us a way out.
|
||||
exclude_paths = [
|
||||
"/domains/edit",
|
||||
"members/new-member/",
|
||||
]
|
||||
|
||||
# Check if the current path matches a widescreen path or the root path.
|
||||
is_widescreen = any(path in request.path for path in widescreen_paths) or request.path == "/"
|
||||
is_excluded = any(exclude_path in request.path for exclude_path in exclude_paths)
|
||||
|
||||
# Check if the user is an organization user and the path matches portfolio paths.
|
||||
is_portfolio_widescreen = (
|
||||
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)
|
||||
)
|
||||
# Check if the current path matches a path in included_paths or the root path.
|
||||
is_widescreen_centered = any(path in request.path for path in include_paths) or request.path == "/"
|
||||
|
||||
# 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.utility.enums import ValidationReturnType
|
||||
from registrar.utility.constants import BranchChoices
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -78,6 +79,20 @@ class RequestingEntityForm(RegistrarForm):
|
|||
# Otherwise just return the suborg as normal
|
||||
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):
|
||||
"""Validation logic to remove the custom suborganization value before clean is triggered.
|
||||
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 is_requesting_new_suborganization:
|
||||
# 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.")
|
||||
if not cleaned_data.get("suborganization_city"):
|
||||
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.constants import BranchChoices
|
||||
from auditlog.models import LogEntry
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
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.
|
||||
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):
|
||||
"""Save override for custom properties"""
|
||||
self.sync_organization_type()
|
||||
|
@ -690,6 +744,18 @@ class DomainRequest(TimeStampedModel):
|
|||
# Update the cached values after saving
|
||||
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):
|
||||
"""Helper function to send out a second status email when the status remains the same,
|
||||
but the reason has changed."""
|
||||
|
@ -784,7 +850,9 @@ class DomainRequest(TimeStampedModel):
|
|||
return True
|
||||
|
||||
def delete_and_clean_up_domain(self, called_from):
|
||||
# Delete the approved domain
|
||||
try:
|
||||
# Clean up the approved domain
|
||||
domain_state = self.approved_domain.state
|
||||
# Only reject if it exists on EPP
|
||||
if domain_state != Domain.State.UNKNOWN:
|
||||
|
@ -796,6 +864,39 @@ class DomainRequest(TimeStampedModel):
|
|||
logger.error(err)
|
||||
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(
|
||||
self,
|
||||
new_status,
|
||||
|
@ -984,6 +1085,7 @@ class DomainRequest(TimeStampedModel):
|
|||
|
||||
if self.status == self.DomainRequestStatus.APPROVED:
|
||||
self.delete_and_clean_up_domain("action_needed")
|
||||
|
||||
elif self.status == self.DomainRequestStatus.REJECTED:
|
||||
self.rejection_reason = None
|
||||
|
||||
|
@ -1014,8 +1116,16 @@ class DomainRequest(TimeStampedModel):
|
|||
domain request into an admin on that domain. It also triggers an email
|
||||
notification."""
|
||||
|
||||
should_save = False
|
||||
if self.federal_agency is None:
|
||||
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()
|
||||
|
||||
# create the domain
|
||||
|
@ -1148,7 +1258,7 @@ class DomainRequest(TimeStampedModel):
|
|||
def is_requesting_new_suborganization(self) -> bool:
|
||||
"""Determines if a user is trying to request
|
||||
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,
|
||||
suborganization_city, and suborganization_state_territory all exist.
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
{% block title %}{% translate "Unauthorized | " %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
||||
<div class="grid-row grow-gap">
|
||||
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||
<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">
|
||||
<h1>
|
||||
{% translate "You are not authorized to view this page" %}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
{% block title %}{% translate "Forbidden | " %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
||||
<div class="grid-row grow-gap">
|
||||
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||
<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">
|
||||
<h1>
|
||||
{% translate "You're not authorized to view this page." %}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
{% block title %}{% translate "Page not found | " %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
||||
<div class="grid-row grid-gap">
|
||||
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||
<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">
|
||||
<h1>
|
||||
{% translate "We couldn’t find that page" %}
|
||||
|
|
|
@ -5,8 +5,8 @@
|
|||
{% block title %}{% translate "Server error | " %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container {% if is_widescreen_mode %} grid-container--widescreen{% endif %}">
|
||||
<div class="grid-row grid-gap">
|
||||
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||
<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">
|
||||
<h1>
|
||||
{% translate "We're having some trouble." %}
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
{% for model in app.models %}
|
||||
<tr class="model-{{ model.object_name|lower }}{% if model.admin_url in request.path|urlencode %} current-model{% endif %}">
|
||||
{% 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 %}
|
||||
<th scope="row">{{ model.name }}</th>
|
||||
{% endif %}
|
||||
|
|
|
@ -61,7 +61,7 @@ https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/
|
|||
{% if field.field.help_text %}
|
||||
{# .gov override #}
|
||||
{% 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>
|
||||
{% endblock help_text %}
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<select name="selected_user" id="selected_user" class="admin-combobox margin-top-0" onchange="this.form.submit()">
|
||||
<option value="">Select a user</option>
|
||||
{% 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 }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
|
|
|
@ -97,7 +97,7 @@
|
|||
<section class="usa-banner" aria-label="Official website of the United States government">
|
||||
<div class="usa-accordion">
|
||||
<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">
|
||||
<img class="usa-banner__header-flag" src="{% static 'img/us_flag_small.png' %}" alt="U.S. flag" />
|
||||
</div>
|
||||
|
@ -113,7 +113,7 @@
|
|||
</button>
|
||||
</div>
|
||||
</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="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"
|
||||
|
@ -159,14 +159,14 @@
|
|||
|
||||
{% block wrapper %}
|
||||
{% block wrapperdiv %}
|
||||
<div id="wrapper">
|
||||
<div id="wrapper" class="wrapper--padding-top-6">
|
||||
{% endblock wrapperdiv %}
|
||||
{% block messages %}
|
||||
{% if messages %}
|
||||
<ul class="messages">
|
||||
{% for message in messages %}
|
||||
{% if 'base' in message.extra_tags %}
|
||||
<li{% if message.tags %} class="{{ message.tags }}" {% endif %}>
|
||||
<li{% if message.tags %} class="{{ message.tags }}" {% endif %}">
|
||||
{{ message }}
|
||||
</li>
|
||||
{% endif %}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
{% if messages %}
|
||||
<ul class="messages">
|
||||
{% for message in messages %}
|
||||
<li {% if message.tags %} class="{{ message.tags }}" {% endif %}>
|
||||
<li {% if message.tags %} class="{{ message.tags }}" {% endif %}">
|
||||
{{ message }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
|
|
@ -321,6 +321,22 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
|||
{% else %}
|
||||
<input id="last-sent-rejection-email-content" class="display-none" value="None">
|
||||
{% 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 %}
|
||||
{{ field.field }}
|
||||
{% endif %}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
{% 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>
|
||||
<div class="grid-container margin-left-0 padding-left-0 padding-right-0 dja-details-contents">
|
||||
{% block detail_content %}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% 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" %}
|
||||
<div>{{ state_help_message }}</div>
|
||||
{% else %}
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
<ul class="mulitple-choice">
|
||||
{% for choice in choices %}
|
||||
{% 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>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% for choice in choices %}
|
||||
{% 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 %}
|
||||
<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">
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
{% block title %}{{ domain.name }} | {% endblock %}
|
||||
|
||||
{% 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 ">
|
||||
<p class="font-body-md margin-top-0 margin-bottom-2
|
||||
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 content %}
|
||||
<div class="grid-container">
|
||||
<div class="grid-row grid-gap">
|
||||
<div class="grid-container grid-container--widescreen">
|
||||
<div class="grid-row grid-gap {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||
<div class="tablet:grid-col-3">
|
||||
{% include 'domain_request_sidebar.html' %}
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
{% block title %} Start a request | {% endblock %}
|
||||
|
||||
{% 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">
|
||||
|
||||
<form class="usa-form usa-form--extra-large" method="post" novalidate>
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
{% endblock wrapperdiv %}
|
||||
|
||||
{% block content %}
|
||||
<div class="grid-container">
|
||||
<div 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">
|
||||
|
||||
|
||||
|
@ -20,6 +21,7 @@
|
|||
<a href="{% url 'domain-request-status' DomainRequest.id %}">Cancel</a></p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<li class="usa-sidenav__item">
|
||||
{% 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
|
||||
</a>
|
||||
{% if request.path|startswith:url %}
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
{% block title %} Home | {% endblock %}
|
||||
|
||||
{% 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 %}
|
||||
{# the entire logged in page goes here #}
|
||||
|
||||
{% 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 %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<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__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
||||
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||
<h4 class="usa-alert__heading">
|
||||
Header
|
||||
</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">
|
||||
<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">
|
||||
Header
|
||||
</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">
|
||||
<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">
|
||||
<strong>Attention:</strong> You are on a test site.
|
||||
</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">
|
||||
<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">
|
||||
Service disruption
|
||||
</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">
|
||||
<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">
|
||||
Header here
|
||||
</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">
|
||||
<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">
|
||||
System outage
|
||||
</h3>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<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__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
||||
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||
<h4 class="usa-alert__heading">
|
||||
Header
|
||||
</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>
|
|
@ -13,7 +13,7 @@
|
|||
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
||||
{% 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">
|
||||
<form class="usa-search usa-search--small" method="POST" role="search">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
{% if has_domain_renewal_flag and num_expiring_domains > 0 and has_any_domains_portfolio_permission %}
|
||||
<section class="usa-site-alert usa-site-alert--info margin-bottom-2 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||
<div class="usa-alert">
|
||||
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
||||
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||
<p class="usa-alert__text maxw-none">
|
||||
{% if num_expiring_domains == 1%}
|
||||
One domain will expire soon. Go to "Manage" to renew the domain. <a href="#" id="link-expiring-domains" class="usa-link">Show expiring domain.</a>
|
||||
|
@ -33,7 +33,7 @@
|
|||
<!-- Embedding the portfolio value in a data attribute -->
|
||||
<span id="portfolio-js-value" data-portfolio="{{ portfolio.id }}"></span>
|
||||
{% 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">
|
||||
<form class="usa-search usa-search--small" method="POST" role="search">
|
||||
{% csrf_token %}
|
||||
|
@ -78,7 +78,7 @@
|
|||
{% if has_domain_renewal_flag and num_expiring_domains > 0 and not portfolio %}
|
||||
<section class="usa-site-alert usa-site-alert--info margin-bottom-2 {% if add_class %}{{ add_class }}{% endif %}" aria-label="Site alert">
|
||||
<div class="usa-alert">
|
||||
<div class="usa-alert__body {% if is_widescreen_mode %}usa-alert__body--widescreen{% endif %}">
|
||||
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||
<p class="usa-alert__text maxw-none">
|
||||
{% if num_expiring_domains == 1%}
|
||||
One domain will expire soon. Go to "Manage" to renew the domain. <a href="#" id="link-expiring-domains" class="usa-link">Show expiring domain.</a>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
<footer class="usa-footer">
|
||||
<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="
|
||||
|
@ -24,7 +24,7 @@
|
|||
<div class="usa-footer__contact-links
|
||||
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">
|
||||
{% if show_manage_your_domains %}
|
||||
<div class="grid-col-auto">
|
||||
|
@ -51,7 +51,7 @@
|
|||
class="usa-identifier__section usa-identifier__section--masthead"
|
||||
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">
|
||||
<a rel="noopener noreferrer" target="_blank" href="https://www.cisa.gov" class="usa-identifier__logo"
|
||||
><img
|
||||
|
@ -77,7 +77,7 @@
|
|||
class="usa-identifier__section usa-identifier__section--required-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">
|
||||
<li class="usa-identifier__required-links-item">
|
||||
<a rel="noopener noreferrer" target="_blank" href="{% public_site_url 'about/' %}"
|
||||
|
@ -119,7 +119,7 @@
|
|||
class="usa-identifier__section usa-identifier__section--usagov"
|
||||
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">
|
||||
Looking for U.S. government information and services?
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% load static %}
|
||||
|
||||
<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">
|
||||
{% include "includes/gov_extended_logo.html" with logo_clickable=logo_clickable %}
|
||||
<button type="button" class="usa-menu-btn">Menu</button>
|
||||
|
|
|
@ -2,13 +2,13 @@
|
|||
{% load custom_filters %}
|
||||
|
||||
<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 %}
|
||||
<button type="button" class="usa-menu-btn">Menu</button>
|
||||
</div>
|
||||
{% block usa_nav %}
|
||||
<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">
|
||||
<img src="{%static 'img/usa-icons/close.svg'%}" role="img" alt="Close" />
|
||||
</button>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
<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">
|
||||
<!-- ---------- 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">
|
||||
<form class="usa-search usa-search--small" method="POST" role="search">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
{% 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">
|
||||
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||
<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 %}
|
||||
{% if portfolio %}
|
||||
{% url 'domain-requests' as url %}
|
||||
|
@ -139,7 +140,7 @@
|
|||
{% endblock modify_request %}
|
||||
</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 %}
|
||||
<h2 class="text-primary-darker"> Summary of your domain request </h2>
|
||||
{% endblock request_summary_header%}
|
||||
|
@ -237,4 +238,5 @@
|
|||
{% endif %}
|
||||
{% endblock request_summary%}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
|
@ -1,5 +1,9 @@
|
|||
{% load static field_helpers url_helpers %}
|
||||
|
||||
<!-- Form messages -->
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock messages%}
|
||||
{% if can_edit %}
|
||||
{% include "includes/form_errors.html" with form=form %}
|
||||
{% endif %}
|
||||
|
|
|
@ -4,14 +4,12 @@
|
|||
<div id="wrapper" class="{% block wrapper_class %}wrapper--padding-top-6{% endblock %}">
|
||||
{% 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 %}
|
||||
{# the entire logged in page goes here #}
|
||||
|
||||
<div class="tablet:grid-col-11 desktop:grid-col-10 tablet:grid-offset-1">
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock %}
|
||||
<div class="grid-row {% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||
<div class="tablet:grid-col-11 desktop:grid-col-10 {% if is_widescreen_centered %}tablet:grid-offset-1{% endif %}">
|
||||
|
||||
{% block portfolio_content %}{% endblock %}
|
||||
|
||||
|
@ -24,6 +22,8 @@
|
|||
</a></p>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</main>
|
||||
|
||||
{% endblock content%}
|
||||
|
|
|
@ -9,9 +9,11 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block portfolio_content %}
|
||||
|
||||
<!-- Form messages -->
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock %}
|
||||
{% endblock messages%}
|
||||
|
||||
<div id="main-content">
|
||||
<h1 id="domains-header">Domains</h1>
|
||||
|
|
|
@ -8,7 +8,12 @@ Organization member
|
|||
{% load static %}
|
||||
|
||||
{% 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 %}
|
||||
<nav class="usa-breadcrumb padding-top-0 margin-bottom-3" aria-label="Portfolio member breadcrumb">
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
{% load static %}
|
||||
|
||||
{% 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 %}
|
||||
{% if portfolio_permission %}
|
||||
|
|
|
@ -6,7 +6,12 @@
|
|||
{% load static %}
|
||||
|
||||
{% 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 %}
|
||||
{% if portfolio_permission %}
|
||||
|
|
|
@ -11,6 +11,13 @@
|
|||
{% block portfolio_content %}
|
||||
{% include "includes/form_errors.html" with form=form %}
|
||||
|
||||
<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%}
|
||||
|
||||
<!-- Navigation breadcrumbs -->
|
||||
<nav class="usa-breadcrumb padding-top-0 bg-gray-1" aria-label="Domain request breadcrumb">
|
||||
<ol class="usa-breadcrumb__list">
|
||||
|
@ -129,4 +136,5 @@
|
|||
<button type="submit" class="usa-button">Update Member</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock portfolio_content%}
|
||||
|
|
|
@ -10,6 +10,11 @@
|
|||
|
||||
{% block portfolio_content %}
|
||||
|
||||
<!-- Form messages -->
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock messages%}
|
||||
|
||||
<div id="main-content">
|
||||
<div id="toggleable-alert" class="usa-alert usa-alert--slim margin-bottom-2 display-none">
|
||||
<div class="usa-alert__body usa-alert__body--widescreen">
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
|
||||
{% block portfolio_content %}
|
||||
|
||||
<!-- Form mesages -->
|
||||
<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" %}
|
||||
|
@ -171,6 +172,7 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock portfolio_content%}
|
||||
|
|
|
@ -4,8 +4,13 @@
|
|||
|
||||
{% block title %} Domains | {% endblock %}
|
||||
|
||||
<!-- Form messages -->
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock messages%}
|
||||
|
||||
{% 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>
|
||||
<section class="section-outlined">
|
||||
<div class="section-outlined__header margin-bottom-3">
|
||||
|
|
|
@ -4,7 +4,13 @@
|
|||
|
||||
{% block title %} Domain Requests | {% endblock %}
|
||||
|
||||
<!-- Form messages -->
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock messages%}
|
||||
|
||||
{% 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>
|
||||
<section class="section-outlined">
|
||||
<div class="section-outlined__header margin-bottom-3">
|
||||
|
@ -27,4 +33,5 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -18,12 +18,11 @@
|
|||
</div>
|
||||
|
||||
<div class="tablet:grid-col-9" id="main-content">
|
||||
|
||||
<!-- Form messages -->
|
||||
{% include "includes/form_errors.html" with form=form %}
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock %}
|
||||
|
||||
{% include "includes/form_errors.html" with form=form %}
|
||||
{% endblock messages%}
|
||||
|
||||
<h1>Organization</h1>
|
||||
|
||||
|
|
|
@ -9,9 +9,11 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block portfolio_content %}
|
||||
|
||||
<!-- Form messages -->
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock %}
|
||||
{% endblock messages%}
|
||||
|
||||
<div id="main-content">
|
||||
<h1 id="domain-requests-header" class="margin-bottom-1">Domain requests</h1>
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
{% load static %}
|
||||
|
||||
{% block portfolio_content %}
|
||||
{% block messages %}
|
||||
{% include "includes/form_messages.html" %}
|
||||
{% endblock %}
|
||||
|
||||
<div class="grid-row grid-gap">
|
||||
<div class="tablet:grid-col-3">
|
||||
|
|
|
@ -11,8 +11,9 @@ Edit your User Profile |
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<main id="main-content" class="grid-container">
|
||||
<div class="grid-col desktop:grid-offset-2 desktop:grid-col-8">
|
||||
<main id="main-content" class="grid-container grid-container--widescreen">
|
||||
<div class="{% if not is_widescreen_centered %}max-width--grid-container{% endif %}">
|
||||
<div class="desktop:grid-col-8 desktop:grid-offset-2">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="usa-alert usa-alert--{{ message.tags }} usa-alert--slim margin-bottom-3">
|
||||
|
@ -95,6 +96,7 @@ Edit your User Profile |
|
|||
{% block content_bottom %}
|
||||
{% include "includes/profile_form.html" with form=form %}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
{% endblock content_bottom %}
|
||||
|
||||
|
|
|
@ -1034,6 +1034,10 @@ def completed_domain_request( # noqa
|
|||
action_needed_reason=None,
|
||||
portfolio=None,
|
||||
organization_name=None,
|
||||
sub_organization=None,
|
||||
requested_suborganization=None,
|
||||
suborganization_city=None,
|
||||
suborganization_state_territory=None,
|
||||
):
|
||||
"""A completed domain request."""
|
||||
if not user:
|
||||
|
@ -1098,6 +1102,18 @@ def completed_domain_request( # noqa
|
|||
if 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)
|
||||
|
||||
if has_other_contacts:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
from datetime import datetime
|
||||
from django.forms import ValidationError
|
||||
from django.utils import timezone
|
||||
from waffle.testutils import override_flag
|
||||
import re
|
||||
from django.test import RequestFactory, Client, TestCase, override_settings
|
||||
from django.contrib.admin.sites import AdminSite
|
||||
|
@ -24,6 +26,7 @@ from registrar.models import (
|
|||
SeniorOfficial,
|
||||
Portfolio,
|
||||
AllowedEmail,
|
||||
Suborganization,
|
||||
)
|
||||
from .common import (
|
||||
MockSESClient,
|
||||
|
@ -82,6 +85,7 @@ class TestDomainRequestAdmin(MockEppLib):
|
|||
Contact.objects.all().delete()
|
||||
Website.objects.all().delete()
|
||||
SeniorOfficial.objects.all().delete()
|
||||
Suborganization.objects.all().delete()
|
||||
Portfolio.objects.all().delete()
|
||||
self.mock_client.EMAILS_SENT.clear()
|
||||
|
||||
|
@ -91,6 +95,83 @@ class TestDomainRequestAdmin(MockEppLib):
|
|||
User.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
|
||||
def test_domain_request_senior_official_is_alphabetically_sorted(self):
|
||||
"""Tests if the senior offical dropdown is alphanetically sorted in the django admin display"""
|
||||
|
|
|
@ -15,6 +15,7 @@ from registrar.models import (
|
|||
FederalAgency,
|
||||
AllowedEmail,
|
||||
Portfolio,
|
||||
Suborganization,
|
||||
)
|
||||
|
||||
import boto3_mocking
|
||||
|
@ -23,6 +24,8 @@ from registrar.utility.errors import FSMDomainRequestError
|
|||
|
||||
from .common import (
|
||||
MockSESClient,
|
||||
create_user,
|
||||
create_superuser,
|
||||
less_console_noise,
|
||||
completed_domain_request,
|
||||
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.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()
|
||||
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_requests", active=True)
|
||||
@less_console_noise_decorator
|
||||
|
|
|
@ -417,7 +417,7 @@ class MemberExport(BaseExport):
|
|||
# Adding a order_by increases output predictability.
|
||||
# Doesn't matter as much for normal use, but makes tests easier.
|
||||
# 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)
|
||||
|
||||
@classmethod
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue