mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-20 17:56:11 +02:00
Merge pull request #3700 from cisagov/cb/3212-subissues
#3212: FEB final part - [CB]
This commit is contained in:
commit
60ed2fd0a5
18 changed files with 549 additions and 197 deletions
|
@ -2741,6 +2741,8 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportRegistrarModelAdmin):
|
|||
|
||||
portfolio_urbanization.short_description = "Urbanization" # type: ignore
|
||||
|
||||
# ------ FEB fields ------
|
||||
|
||||
# This is just a placeholder. This field will be populated in the detail_table_fieldset view.
|
||||
# This is not a field that exists on the model.
|
||||
def status_history(self, obj):
|
||||
|
@ -2821,7 +2823,16 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportRegistrarModelAdmin):
|
|||
]
|
||||
},
|
||||
),
|
||||
(".gov domain", {"fields": ["requested_domain", "alternative_domains"]}),
|
||||
(
|
||||
".gov domain",
|
||||
{
|
||||
"fields": [
|
||||
"requested_domain",
|
||||
"alternative_domains",
|
||||
"feb_naming_requirements_details",
|
||||
]
|
||||
},
|
||||
),
|
||||
(
|
||||
"Contacts",
|
||||
{
|
||||
|
@ -2833,10 +2844,24 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportRegistrarModelAdmin):
|
|||
"cisa_representative_first_name",
|
||||
"cisa_representative_last_name",
|
||||
"cisa_representative_email",
|
||||
"eop_stakeholder_first_name",
|
||||
"eop_stakeholder_last_name",
|
||||
]
|
||||
},
|
||||
),
|
||||
(
|
||||
"Background info",
|
||||
{
|
||||
"fields": [
|
||||
"feb_purpose_choice",
|
||||
"purpose",
|
||||
"time_frame_details",
|
||||
"interagency_initiative_details",
|
||||
"anything_else",
|
||||
"current_websites",
|
||||
]
|
||||
},
|
||||
),
|
||||
("Background info", {"fields": ["purpose", "anything_else", "current_websites"]}),
|
||||
(
|
||||
"Type of organization",
|
||||
{
|
||||
|
@ -3033,23 +3058,41 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportRegistrarModelAdmin):
|
|||
def get_fieldsets(self, request, obj=None):
|
||||
fieldsets = super().get_fieldsets(request, obj)
|
||||
|
||||
excluded_fields = set()
|
||||
feb_fields = [
|
||||
"feb_naming_requirements_details",
|
||||
"feb_purpose_choice",
|
||||
"time_frame_details",
|
||||
"interagency_initiative_details",
|
||||
"eop_stakeholder_first_name",
|
||||
"eop_stakeholder_last_name",
|
||||
]
|
||||
|
||||
org_fields = [
|
||||
"portfolio",
|
||||
"sub_organization",
|
||||
"requested_suborganization",
|
||||
"suborganization_city",
|
||||
"suborganization_state_territory",
|
||||
]
|
||||
|
||||
org_flag = flag_is_active_for_user(request.user, "organization_requests")
|
||||
# Hide FEB fields for non-FEB requests
|
||||
if not (obj and obj.portfolio and obj.is_feb()):
|
||||
excluded_fields.update(feb_fields)
|
||||
|
||||
# Hide certain portfolio and suborg fields behind the organization requests flag
|
||||
# if it is not enabled
|
||||
if not flag_is_active_for_user(request.user, "organization_requests"):
|
||||
excluded_fields = [
|
||||
"portfolio",
|
||||
"sub_organization",
|
||||
"requested_suborganization",
|
||||
"suborganization_city",
|
||||
"suborganization_state_territory",
|
||||
]
|
||||
modified_fieldsets = []
|
||||
for name, data in fieldsets:
|
||||
fields = data.get("fields", [])
|
||||
fields = tuple(field for field in fields if field not in excluded_fields)
|
||||
modified_fieldsets.append((name, {**data, "fields": fields}))
|
||||
return modified_fieldsets
|
||||
return fieldsets
|
||||
if not org_flag:
|
||||
excluded_fields.update(org_fields)
|
||||
excluded_fields.update(feb_fields)
|
||||
|
||||
modified_fieldsets = []
|
||||
for name, data in fieldsets:
|
||||
fields = data.get("fields", [])
|
||||
fields = tuple(field for field in fields if field not in excluded_fields)
|
||||
modified_fieldsets.append((name, {**data, "fields": fields}))
|
||||
return modified_fieldsets
|
||||
|
||||
# Trigger action when a fieldset is changed
|
||||
def save_model(self, request, obj, form, change):
|
||||
|
|
|
@ -1,41 +1,105 @@
|
|||
import { showElement } from './helpers.js';
|
||||
|
||||
// Flag to track if we're in the initial page load
|
||||
let isInitialLoad = true;
|
||||
|
||||
export const domain_purpose_choice_callbacks = {
|
||||
'new': {
|
||||
callback: function(value, element) {
|
||||
callback: function(value, element, event) {
|
||||
// Only clear errors if this is a user-initiated event (not initial page load)
|
||||
if (!isInitialLoad) {
|
||||
clearErrors(element);
|
||||
}
|
||||
|
||||
//show the purpose details container
|
||||
showElement(element);
|
||||
// change just the text inside the em tag
|
||||
const labelElement = element.querySelector('.usa-label em');
|
||||
labelElement.innerHTML = 'Explain why a new domain is required and why a ' +
|
||||
'subdomain of an existing domain doesn\'t meet your needs.' +
|
||||
const labelElement = element.querySelector('p em');
|
||||
labelElement.innerHTML = 'Explain why a new domain name is needed instead of using a ' +
|
||||
'subdomain of an existing website.' +
|
||||
'<br><br>' + // Adding double line break for spacing
|
||||
'Include any data that supports a clear public benefit or ' +
|
||||
'evidence user need for this new domain. ' +
|
||||
'Include any information or data that shows how the new domain would ' +
|
||||
'benefit the public or meet user needs. ' +
|
||||
'<span class="usa-label--required">*</span>';
|
||||
|
||||
// Mark that we're no longer in initial load
|
||||
isInitialLoad = false;
|
||||
},
|
||||
element: document.getElementById('purpose-details-container')
|
||||
},
|
||||
'redirect': {
|
||||
callback: function(value, element) {
|
||||
callback: function(value, element, event) {
|
||||
// Only clear errors if this is a user-initiated event (not initial page load)
|
||||
if (!isInitialLoad) {
|
||||
clearErrors(element);
|
||||
}
|
||||
|
||||
// show the purpose details container
|
||||
showElement(element);
|
||||
// change just the text inside the em tag
|
||||
const labelElement = element.querySelector('.usa-label em');
|
||||
const labelElement = element.querySelector('p em');
|
||||
labelElement.innerHTML = 'Explain why a redirect is necessary. ' +
|
||||
'<span class="usa-label--required">*</span>';
|
||||
|
||||
// Mark that we're no longer in initial load
|
||||
isInitialLoad = false;
|
||||
},
|
||||
element: document.getElementById('purpose-details-container')
|
||||
},
|
||||
'other': {
|
||||
callback: function(value, element) {
|
||||
callback: function(value, element, event) {
|
||||
// Only clear errors if this is a user-initiated event (not initial page load)
|
||||
if (!isInitialLoad) {
|
||||
clearErrors(element);
|
||||
}
|
||||
|
||||
// Show the purpose details container
|
||||
showElement(element);
|
||||
// change just the text inside the em tag
|
||||
const labelElement = element.querySelector('.usa-label em');
|
||||
const labelElement = element.querySelector('p em');
|
||||
labelElement.innerHTML = 'Describe how this domain will be used. ' +
|
||||
'<span class="usa-label--required">*</span>';
|
||||
|
||||
// Mark that we're no longer in initial load
|
||||
isInitialLoad = false;
|
||||
},
|
||||
element: document.getElementById('purpose-details-container')
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to clear error messages in a textarea
|
||||
function clearErrors(element) {
|
||||
// Find the error message div
|
||||
const errorMessage = element.querySelector('#id_purpose-purpose__error-message');
|
||||
if (errorMessage) {
|
||||
errorMessage.remove();
|
||||
}
|
||||
|
||||
// Find the form group and remove error class
|
||||
const formGroup = element.querySelector('.usa-form-group');
|
||||
if (formGroup) {
|
||||
formGroup.classList.remove('usa-form-group--error');
|
||||
}
|
||||
|
||||
// Find the textarea and remove error class
|
||||
const textarea = element.querySelector('#id_purpose-purpose');
|
||||
if (textarea) {
|
||||
textarea.classList.remove('usa-input--error');
|
||||
|
||||
// Also update aria attributes
|
||||
textarea.setAttribute('aria-invalid', 'false');
|
||||
|
||||
// Remove error message from aria-describedby
|
||||
const describedBy = textarea.getAttribute('aria-describedby');
|
||||
if (describedBy) {
|
||||
const newDescribedBy = describedBy.replace('id_purpose-purpose__error-message', '').trim();
|
||||
textarea.setAttribute('aria-describedby', newDescribedBy);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the label and remove error class
|
||||
const label = element.querySelector('label');
|
||||
if (label) {
|
||||
label.classList.remove('usa-label--error');
|
||||
}
|
||||
}
|
|
@ -22,6 +22,13 @@ class UserFixture:
|
|||
"""
|
||||
|
||||
ADMINS = [
|
||||
{
|
||||
"username": "4aa78480-6272-42f9-ac29-a034ebdd9231",
|
||||
"first_name": "Kaitlin",
|
||||
"last_name": "Abbitt",
|
||||
"email": "kaitlin.abbitt@cisa.dhs.gov",
|
||||
"title": "Captain pirate",
|
||||
},
|
||||
{
|
||||
"username": "aad084c3-66cc-4632-80eb-41cdf5c5bcbf",
|
||||
"first_name": "Aditi",
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from django import forms
|
||||
from django.core.validators import MaxLengthValidator
|
||||
from registrar.forms.utility.wizard_form_helper import BaseDeletableRegistrarForm, BaseYesNoForm
|
||||
from registrar.models.domain_request import DomainRequest
|
||||
|
||||
|
||||
class ExecutiveNamingRequirementsYesNoForm(BaseYesNoForm, BaseDeletableRegistrarForm):
|
||||
|
@ -11,6 +12,8 @@ class ExecutiveNamingRequirementsYesNoForm(BaseYesNoForm, BaseDeletableRegistrar
|
|||
|
||||
field_name = "feb_naming_requirements"
|
||||
|
||||
required_error_message = "Select “Yes” if your submission meets each domain naming requirement. Select “No” if it doesn’t meet each requirement." # noqa: E501
|
||||
|
||||
@property
|
||||
def form_is_checked(self):
|
||||
"""
|
||||
|
@ -25,7 +28,9 @@ class ExecutiveNamingRequirementsDetailsForm(BaseDeletableRegistrarForm):
|
|||
widget=forms.Textarea(attrs={"maxlength": "2000"}),
|
||||
max_length=2000,
|
||||
required=True,
|
||||
error_messages={"required": ("This field is required.")},
|
||||
error_messages={
|
||||
"required": ("Provide details on why your submission does not meet each domain naming requirement.")
|
||||
}, # noqa: E501
|
||||
validators=[
|
||||
MaxLengthValidator(
|
||||
2000,
|
||||
|
@ -41,18 +46,14 @@ class FEBPurposeOptionsForm(BaseDeletableRegistrarForm):
|
|||
|
||||
field_name = "feb_purpose_choice"
|
||||
|
||||
form_choices = (
|
||||
("new", "Used for a new website"),
|
||||
("redirect", "Used as a redirect for an existing website"),
|
||||
("other", "Not for a website"),
|
||||
)
|
||||
form_choices = DomainRequest.FEBPurposeChoices.choices
|
||||
|
||||
feb_purpose_choice = forms.ChoiceField(
|
||||
required=True,
|
||||
choices=form_choices,
|
||||
widget=forms.RadioSelect,
|
||||
error_messages={
|
||||
"required": "This question is required.",
|
||||
"required": "Select the purpose of your requested domain.",
|
||||
},
|
||||
label="Select one",
|
||||
)
|
||||
|
@ -65,6 +66,10 @@ class FEBTimeFrameYesNoForm(BaseDeletableRegistrarForm, BaseYesNoForm):
|
|||
"""
|
||||
|
||||
field_name = "has_timeframe"
|
||||
required_error_message = (
|
||||
"Select “Yes” if you have a target time frame for"
|
||||
" launching this domain. Select “No” if you don’t have a target time frame."
|
||||
)
|
||||
|
||||
@property
|
||||
def form_is_checked(self):
|
||||
|
@ -79,7 +84,7 @@ class FEBTimeFrameDetailsForm(BaseDeletableRegistrarForm):
|
|||
label="time_frame_details",
|
||||
widget=forms.Textarea(
|
||||
attrs={
|
||||
"aria-label": "Provide details on your target timeframe. \
|
||||
"aria-label": "Provide details on your target time frame. \
|
||||
Is there a special significance to this date (legal requirement, announcement, event, etc)?"
|
||||
}
|
||||
),
|
||||
|
@ -89,7 +94,7 @@ class FEBTimeFrameDetailsForm(BaseDeletableRegistrarForm):
|
|||
message="Response must be less than 2000 characters.",
|
||||
)
|
||||
],
|
||||
error_messages={"required": "Provide details on your target timeframe."},
|
||||
error_messages={"required": "Provide details on your target time frame."},
|
||||
)
|
||||
|
||||
|
||||
|
@ -100,6 +105,10 @@ class FEBInteragencyInitiativeYesNoForm(BaseDeletableRegistrarForm, BaseYesNoFor
|
|||
"""
|
||||
|
||||
field_name = "is_interagency_initiative"
|
||||
required_error_message = (
|
||||
"Select “Yes” if the domain will be used for an "
|
||||
"interagency initiative. Select “No” if it won’t be used for an interagency initiative."
|
||||
)
|
||||
|
||||
@property
|
||||
def form_is_checked(self):
|
||||
|
@ -156,29 +165,12 @@ class EOPContactForm(BaseDeletableRegistrarForm):
|
|||
error_messages={"required": "Enter the last name / family name of this contact."},
|
||||
required=True,
|
||||
)
|
||||
email = forms.EmailField(
|
||||
label="Email",
|
||||
max_length=None,
|
||||
error_messages={
|
||||
"required": ("Enter an email address in the required format, like name@example.com."),
|
||||
"invalid": ("Enter an email address in the required format, like name@example.com."),
|
||||
},
|
||||
validators=[
|
||||
MaxLengthValidator(
|
||||
320,
|
||||
message="Response must be less than 320 characters.",
|
||||
)
|
||||
],
|
||||
required=True,
|
||||
help_text="Enter an email address in the required format, like name@example.com.",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def from_database(cls, obj):
|
||||
return {
|
||||
"first_name": obj.eop_stakeholder_first_name,
|
||||
"last_name": obj.eop_stakeholder_last_name,
|
||||
"email": obj.eop_stakeholder_email,
|
||||
}
|
||||
|
||||
def to_database(self, obj):
|
||||
|
@ -192,7 +184,6 @@ class EOPContactForm(BaseDeletableRegistrarForm):
|
|||
return
|
||||
obj.eop_stakeholder_first_name = self.cleaned_data["first_name"]
|
||||
obj.eop_stakeholder_last_name = self.cleaned_data["last_name"]
|
||||
obj.eop_stakeholder_email = self.cleaned_data["email"]
|
||||
obj.save()
|
||||
|
||||
|
||||
|
|
|
@ -234,9 +234,12 @@ class BaseYesNoForm(RegistrarForm):
|
|||
# For instance, this could be "has_other_contacts"
|
||||
field_name: str
|
||||
|
||||
# This field can be overriden to show a custom error
|
||||
# message.
|
||||
required_error_message = "This question is required."
|
||||
|
||||
# Default form choice mapping. Default is suitable for most cases.
|
||||
# Override for more complex scenarios.
|
||||
form_choices = ((True, "Yes"), (False, "No"))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
@ -13,17 +13,17 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name="domainrequest",
|
||||
name="eop_stakeholder_email",
|
||||
field=models.EmailField(blank=True, max_length=254, null=True, verbose_name="EOP Stakeholder Email"),
|
||||
field=models.EmailField(blank=True, max_length=254, null=True, verbose_name="EOP contact email"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="domainrequest",
|
||||
name="eop_stakeholder_first_name",
|
||||
field=models.CharField(blank=True, null=True, verbose_name="EOP Stakeholder First Name"),
|
||||
field=models.CharField(blank=True, null=True, verbose_name="EOP contact first name"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="domainrequest",
|
||||
name="eop_stakeholder_last_name",
|
||||
field=models.CharField(blank=True, null=True, verbose_name="EOP Stakeholder Last Name"),
|
||||
field=models.CharField(blank=True, null=True, verbose_name="EOP contact last name"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="domainrequest",
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
# Generated by Django 4.2.20 on 2025-03-26 19:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("registrar", "0145_create_groups_v19"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="domainrequest",
|
||||
name="eop_stakeholder_email",
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domainrequest",
|
||||
name="feb_naming_requirements",
|
||||
field=models.BooleanField(blank=True, null=True, verbose_name="Meets naming requirements"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domainrequest",
|
||||
name="feb_naming_requirements_details",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
help_text="Required if requested domain that doesn't meet naming requirements",
|
||||
null=True,
|
||||
verbose_name="Domain name rationale",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domainrequest",
|
||||
name="feb_purpose_choice",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("new", "Used for a new website"),
|
||||
("redirect", "Used as a redirect for an existing website"),
|
||||
("other", "Not for a website"),
|
||||
],
|
||||
null=True,
|
||||
verbose_name="Purpose type",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domainrequest",
|
||||
name="interagency_initiative_details",
|
||||
field=models.TextField(blank=True, null=True, verbose_name="Interagency initiative"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domainrequest",
|
||||
name="time_frame_details",
|
||||
field=models.TextField(blank=True, null=True, verbose_name="Target time frame"),
|
||||
),
|
||||
]
|
|
@ -55,9 +55,14 @@ class DomainRequest(TimeStampedModel):
|
|||
return cls(status_name).label if status_name else None
|
||||
|
||||
class FEBPurposeChoices(models.TextChoices):
|
||||
WEBSITE = "website"
|
||||
REDIRECT = "redirect"
|
||||
OTHER = "other"
|
||||
WEBSITE = "new", "Used for a new website"
|
||||
REDIRECT = "redirect", "Used as a redirect for an existing website"
|
||||
OTHER = "other", "Not for a website"
|
||||
|
||||
@classmethod
|
||||
def get_purpose_label(cls, purpose_name: str | None):
|
||||
"""Returns the associated label for a given purpose name"""
|
||||
return cls(purpose_name).label if purpose_name else None
|
||||
|
||||
class StateTerritoryChoices(models.TextChoices):
|
||||
ALABAMA = "AL", "Alabama (AL)"
|
||||
|
@ -510,17 +515,21 @@ class DomainRequest(TimeStampedModel):
|
|||
feb_naming_requirements = models.BooleanField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="Meets naming requirements",
|
||||
)
|
||||
|
||||
feb_naming_requirements_details = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Required if requested domain that doesn't meet naming requirements",
|
||||
verbose_name="Domain name rationale",
|
||||
)
|
||||
|
||||
feb_purpose_choice = models.CharField(
|
||||
null=True,
|
||||
blank=True,
|
||||
choices=FEBPurposeChoices.choices,
|
||||
verbose_name="Purpose type",
|
||||
)
|
||||
|
||||
working_with_eop = models.BooleanField(
|
||||
|
@ -531,24 +540,17 @@ class DomainRequest(TimeStampedModel):
|
|||
eop_stakeholder_first_name = models.CharField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="EOP Stakeholder First Name",
|
||||
verbose_name="EOP contact first name",
|
||||
)
|
||||
|
||||
eop_stakeholder_last_name = models.CharField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="EOP Stakeholder Last Name",
|
||||
)
|
||||
|
||||
eop_stakeholder_email = models.EmailField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="EOP Stakeholder Email",
|
||||
verbose_name="EOP contact last name",
|
||||
)
|
||||
|
||||
# This field is alternately used for generic domain purpose explanations
|
||||
# and for explanations of the specific purpose chosen with feb_purpose_choice
|
||||
# by a Federal Executive Branch agency.
|
||||
purpose = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
|
@ -562,6 +564,7 @@ class DomainRequest(TimeStampedModel):
|
|||
time_frame_details = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="Target time frame",
|
||||
)
|
||||
|
||||
is_interagency_initiative = models.BooleanField(
|
||||
|
@ -572,6 +575,7 @@ class DomainRequest(TimeStampedModel):
|
|||
interagency_initiative_details = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
verbose_name="Interagency initiative",
|
||||
)
|
||||
|
||||
alternative_domains = models.ManyToManyField(
|
||||
|
@ -1015,11 +1019,15 @@ class DomainRequest(TimeStampedModel):
|
|||
if not context:
|
||||
has_organization_feature_flag = flag_is_active_for_user(recipient, "organization_feature")
|
||||
is_org_user = has_organization_feature_flag and recipient.has_view_portfolio_permission(self.portfolio)
|
||||
requires_feb_questions = self.is_feb() and is_org_user
|
||||
purpose_label = DomainRequest.FEBPurposeChoices.get_purpose_label(self.feb_purpose_choice)
|
||||
context = {
|
||||
"domain_request": self,
|
||||
# This is the user that we refer to in the email
|
||||
"recipient": recipient,
|
||||
"is_org_user": is_org_user,
|
||||
"requires_feb_questions": requires_feb_questions,
|
||||
"purpose_label": purpose_label,
|
||||
}
|
||||
|
||||
if custom_email_content:
|
||||
|
|
|
@ -2,19 +2,25 @@
|
|||
{% load static field_helpers url_helpers %}
|
||||
|
||||
{% block form_instructions %}
|
||||
<p>Before requesting a .gov domain, please make sure it meets <a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% if requires_feb_questions %}https://get.gov/domains/executive-branch-guidance/{% else %}{% public_site_url 'domains/choosing' %}{% endif %}">our naming requirements</a>. Your domain name must:
|
||||
<p>
|
||||
Before requesting a .gov domain, please make sure it meets
|
||||
{% if requires_feb_questions %}
|
||||
<a class="usa-link" rel="noopener noreferrer" target="_blank" href="https://get.gov/domains/executive-branch-guidance/">our naming requirements for executive branch agencies</a>. Your domain name must:
|
||||
{% else %}
|
||||
<a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% public_site_url 'domains/choosing' %}">our naming requirements</a>. Your domain name must:
|
||||
{% endif %}
|
||||
<ul class="usa-list">
|
||||
<li>Be available </li>
|
||||
<li>Relate to your organization's name, location, and/or services </li>
|
||||
<li>Relate to your organization’s name, location, and/or services </li>
|
||||
<li>Be unlikely to mislead or confuse the general public (even if your domain is only intended for a specific audience) </li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>Names that <em>uniquely apply to your organization</em> are likely to be approved over names that could also apply to other organizations.
|
||||
{% if not is_federal %}In most instances, this requires including your state's two-letter abbreviation.{% endif %}</p>
|
||||
{% if not is_federal %}In most instances, this requires including your state’s two-letter abbreviation.{% endif %}</p>
|
||||
|
||||
{% if not portfolio %}
|
||||
<p>Requests for your organization's initials or an abbreviated name might not be approved, but we encourage you to request the name you want.</p>
|
||||
<p>Requests for your organization’s initials or an abbreviated name might not be approved, but we encourage you to request the name you want.</p>
|
||||
{% endif %}
|
||||
|
||||
<p>Note that <strong>only federal agencies can request generic terms</strong> like
|
||||
|
@ -29,7 +35,7 @@
|
|||
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
{# empty this block so it doesn't show on this page #}
|
||||
{# empty this block so it doesn’t show on this page #}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
@ -42,8 +48,8 @@
|
|||
<h2>What .gov domain do you want?</h2>
|
||||
</legend>
|
||||
<p id="domain_instructions" class="margin-top-05">
|
||||
After you enter your domain, we'll make sure it's available and that it meets some of our naming requirements.
|
||||
If your domain passes these initial checks, we'll verify that it meets all our requirements after you complete the rest of this form.
|
||||
After you enter your domain, we’ll make sure it’s available and that it meets some of our naming requirements.
|
||||
If your domain passes these initial checks, we’ll verify that it meets all our requirements after you complete the rest of this form.
|
||||
</p>
|
||||
{% with attr_aria_labelledby="domain_instructions domain_instructions2" attr_aria_describedby="id_dotgov_domain-requested_domain--toast" %}
|
||||
{# attr_validate / validate="domain" invokes code in getgov.min.js #}
|
||||
|
@ -65,7 +71,7 @@
|
|||
<h2 id="alternative-domains-title">Alternative domains (optional)</h2>
|
||||
</legend>
|
||||
<p id="alt_domain_instructions" class="margin-top-05">
|
||||
Are there other domains you'd like if we can't give you your first choice?
|
||||
Are there other domains you’d like if we can’t give you your first choice?
|
||||
</p>
|
||||
{% with attr_aria_labelledby="alt_domain_instructions" %}
|
||||
{# Will probably want to remove blank-ok and do related cleanup when we implement delete #}
|
||||
|
@ -99,7 +105,7 @@
|
|||
>Check availability</button>
|
||||
</div>
|
||||
<p class="margin-top-05">
|
||||
If you're not sure this is the domain you want, that's ok. You can change the domain later.
|
||||
If you’re not sure this is the domain you want, that’s ok. You can change the domain later.
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
|
@ -114,9 +120,10 @@
|
|||
<p id="dotgov-domain-naming-requirements" class="margin-top-05">
|
||||
OMB will review each request against the domain
|
||||
<a class="usa-link" rel="noopener noreferrer" target="_blank" href="https://get.gov/domains/executive-branch-guidance/">
|
||||
naming requirements for executive branch agencies
|
||||
</a>.
|
||||
Agency submissions are expected to meet each requirement.
|
||||
naming requirements for executive branch agencies.</a> Agency submissions are expected to meet each requirement.
|
||||
</p>
|
||||
<p class="usa-label margin-top-0 margin-bottom-0">
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.2.feb_naming_requirements %}
|
||||
|
@ -124,13 +131,12 @@
|
|||
|
||||
{# Conditional Details Field – only shown when the executive naming requirements radio is "False" #}
|
||||
<div id="domain-naming-requirements-details-container" class="conditional-panel" style="display: none;">
|
||||
<p class="usa-label">
|
||||
Provide details below <span class="usa-label--required">*</span>
|
||||
<p class="margin-bottom-3 margin-top-3">
|
||||
<em>Provide details below. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_label_class="usa-sr-only" attr_required="required" maxlength="2000" %}
|
||||
{% input_with_errors forms.3.feb_naming_requirements_details %}
|
||||
{% endwith %}
|
||||
<p class="usa-hint">Maximum 2000 characters allowed.</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
{% endif %}
|
||||
|
|
|
@ -12,14 +12,12 @@
|
|||
|
||||
{% block form_fields %}
|
||||
{% if requires_feb_questions %}
|
||||
<fieldset class="usa-fieldset margin-top-0 dotgov-domain-form">
|
||||
<fieldset class="usa-fieldset margin-top-0">
|
||||
{{forms.0.management_form}}
|
||||
{{forms.1.management_form}}
|
||||
{{forms.2.management_form}}
|
||||
{{forms.3.management_form}}
|
||||
{{forms.4.management_form}}
|
||||
{{forms.5.management_form}}
|
||||
<h2>What is the purpose of your requested domain?</h2>
|
||||
<legend>
|
||||
<h2>What is the purpose of your requested domain?</h2>
|
||||
</legend>
|
||||
<p class="margin-bottom-0 margin-top-1">
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
|
@ -28,51 +26,58 @@
|
|||
{% endwith %}
|
||||
|
||||
<div id="purpose-details-container" class="conditional-panel display-none">
|
||||
<p class="usa-label">
|
||||
<em>Provide details below <span class="usa-label--required">*</span></em>
|
||||
<p class="margin-bottom-3 margin-top-3">
|
||||
<em>Provide details below. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_label_class="usa-sr-only" attr_required="required" attr_maxlength="2000" %}
|
||||
{% input_with_errors forms.1.purpose %}
|
||||
{% endwith %}
|
||||
<p class="usa-hint margin-top-0">Maximum 2000 characters allowed.</p>
|
||||
</div>
|
||||
|
||||
<h2>Do you have a target time frame for launching this domain?</h2>
|
||||
<p class="margin-bottom-0 margin-top-1">
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.2.has_timeframe %}
|
||||
{% endwith %}
|
||||
|
||||
<div id="purpose-timeframe-details-container" class="conditional-panel">
|
||||
</fieldset>
|
||||
<fieldset class="usa-fieldset margin-top-2">
|
||||
{{forms.2.management_form}}
|
||||
{{forms.3.management_form}}
|
||||
<legend>
|
||||
<h2>Do you have a target time frame for launching this domain?</h2>
|
||||
</legend>
|
||||
<p class="margin-bottom-0 margin-top-1">
|
||||
<em>Provide details below. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_label_class="usa-sr-only" attr_required="required" attr_maxlength="2000" %}
|
||||
{% input_with_errors forms.3.time_frame_details %}
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.2.has_timeframe %}
|
||||
{% endwith %}
|
||||
<p class="usa-hint margin-top-0">Maximum 2000 characters allowed.</p>
|
||||
</div>
|
||||
|
||||
<h2>Will the domain name be used for an interagency initiative?</h2>
|
||||
<p class="margin-bottom-0 margin-top-1">
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.4.is_interagency_initiative %}
|
||||
{% endwith %}
|
||||
|
||||
<div id="purpose-interagency-initaitive-details-container" class="conditional-panel">
|
||||
<div id="purpose-timeframe-details-container" class="conditional-panel">
|
||||
<p class="margin-bottom-3 margin-top-3">
|
||||
<em>Provide details on your target time frame. Is there special significance of this day (legal requirement, announcement, event, etc.)? <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_label_class="usa-sr-only" attr_required="required" attr_maxlength="2000" %}
|
||||
{% input_with_errors forms.3.time_frame_details %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset class="usa-fieldset margin-top-2" aria-labelledby="interagency-initiative-question">
|
||||
{{forms.4.management_form}}
|
||||
{{forms.5.management_form}}
|
||||
<legend>
|
||||
<h2 id="interagency-initiative-question">Will the domain name be used for an interagency initiative?</h2>
|
||||
</legend>
|
||||
<p class="margin-bottom-0 margin-top-1">
|
||||
<em>Provide details below. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_label_class="usa-sr-only" attr_required="required" attr_maxlength="2000" %}
|
||||
{% input_with_errors forms.5.interagency_initiative_details %}
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.4.is_interagency_initiative %}
|
||||
{% endwith %}
|
||||
<p class="usa-hint margin-top-0">Maximum 2000 characters allowed.</p>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<div id="purpose-interagency-initaitive-details-container" class="conditional-panel">
|
||||
<p class="margin-bottom-3 margin-top-3">
|
||||
<em>Name the agencies that will be involved in this initiative. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_label_class="usa-sr-only" attr_required="required" attr_maxlength="2000" %}
|
||||
{% input_with_errors forms.5.interagency_initiative_details %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</fieldset>
|
||||
{% else %}
|
||||
<h2>What is the purpose of your requested domain?</h2>
|
||||
<p>Describe how you’ll use your .gov domain. Will it be used for a website, email, or something else?</p>
|
||||
|
|
|
@ -8,12 +8,47 @@ Current websites: {% for site in domain_request.current_websites.all %}
|
|||
{% endfor %}{% endif %}
|
||||
.gov domain:
|
||||
{{ domain_request.requested_domain.name }}
|
||||
{% if requires_feb_questions %}
|
||||
Meets naming requirements
|
||||
{% if domain_request.feb_naming_requirements %}
|
||||
{{ domain_request.feb_naming_requirements }}
|
||||
{% else %}
|
||||
No
|
||||
{{ domain_request.feb_naming_requirements_details }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if domain_request.alternative_domains.all %}
|
||||
Alternative domains:
|
||||
{% for site in domain_request.alternative_domains.all %}{% spaceless %}{{ site.website }}{% endspaceless %}
|
||||
{% endfor %}{% endif %}
|
||||
Purpose of your domain:
|
||||
{% if requires_feb_questions %}
|
||||
{{ purpose_label }}
|
||||
{{ domain_request.purpose }}
|
||||
Target time frame:
|
||||
{% if domain_request.has_target_time_frame %}
|
||||
{{ domain_request.time_frame_details }}
|
||||
{% else %}
|
||||
No
|
||||
{% endif %}
|
||||
Interagency initiative:
|
||||
{% if domain_request.is_interagency_initiative %}
|
||||
{{ domain_request.interagency_initiative_details }}
|
||||
{% else %}
|
||||
No
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ domain_request.purpose }}
|
||||
{% endif %}
|
||||
{% if requires_feb_questions %}
|
||||
EOP contact:
|
||||
{% if domain_request.working_with_eop %}
|
||||
{{ domain_request.eop_contact.first_name }} {{ domain_request.eop_contact.last_name }}
|
||||
{{ domain_request.eop_contact.email }}
|
||||
{% else %}
|
||||
No
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if domain_request.anything_else %}
|
||||
Additional details:
|
||||
{{ domain_request.anything_else }}
|
||||
|
|
|
@ -10,19 +10,19 @@
|
|||
{% if step == Step.REQUESTING_ENTITY %}
|
||||
{% with title=form_titles|get_item:step %}
|
||||
{% if domain_request.sub_organization %}
|
||||
{% include "includes/summary_item.html" with value=domain_request.sub_organization edit_link=domain_request_url %}
|
||||
{% include "includes/summary_item.html" with title=title value=domain_request.sub_organization editable=is_editable edit_link=domain_request_url %}
|
||||
{% comment %} We don't have city or state_territory for suborganizations yet, so no data should display {% endcomment %}
|
||||
{% elif domain_request.requesting_entity_is_suborganization %}
|
||||
{% include "includes/summary_item.html" with value=domain_request.requested_suborganization edit_link=domain_request_url %}
|
||||
{% include "includes/summary_item.html" with title=title value=domain_request.requested_suborganization editable=is_editable edit_link=domain_request_url %}
|
||||
<p class="margin-y-0">{{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}</p>
|
||||
{% elif domain_request.requesting_entity_is_portfolio %}
|
||||
{% include "includes/summary_item.html" with value=domain_request.portfolio.organization_name edit_link=domain_request_url %}
|
||||
{% include "includes/summary_item.html" with title=title value=domain_request.portfolio.organization_name editable=is_editable edit_link=domain_request_url %}
|
||||
{% if domain_request.portfolio.city and domain_request.portfolio.state_territory %}
|
||||
<p class="margin-y-0">{{domain_request.portfolio.city}}, {{domain_request.portfolio.state_territory}}</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% with value="<span class='text-bold text-secondary-dark'>Incomplete</span>"|safe %}
|
||||
{% include "includes/summary_item.html" with edit_link=domain_request_url %}
|
||||
{% include "includes/summary_item.html" with title=title editable=is_editable edit_link=domain_request_url %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
@ -53,18 +53,78 @@
|
|||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if requires_feb_questions %}
|
||||
<h4 class="margin-bottom-0">Meets naming requirements</h4>
|
||||
{% if domain_request.feb_naming_requirements is None %}
|
||||
<p class="margin-y-0"><span class='text-bold text-secondary-dark'>Incomplete</span></p>
|
||||
{% elif domain_request.feb_naming_requirements %}
|
||||
<p class="margin-y-0">Yes</p>
|
||||
{% else %}
|
||||
<p class="margin-y-0">No</p>
|
||||
<p class="margin-y-0">{{domain_request.feb_naming_requirements_details}}</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if step == Step.PURPOSE %}
|
||||
{% with title=form_titles|get_item:step value=domain_request.purpose|default:"<span class='text-bold text-secondary-dark'>Incomplete</span>"|safe %}
|
||||
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
|
||||
{% endwith %}
|
||||
{% if requires_feb_questions %}
|
||||
{% with title=form_titles|get_item:step %}
|
||||
{% include "includes/summary_item.html" with title=title value=" " heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
|
||||
{% endwith %}
|
||||
<h4 class="margin-bottom-0">Purpose</h4>
|
||||
{% if domain_request.feb_purpose_choice %}
|
||||
<p class="margin-y-0">{{purpose_label}}</p>
|
||||
<p class="margin-y-0">{{domain_request.purpose}}</p>
|
||||
{% else %}
|
||||
<p class="margin-y-0"><span class='text-bold text-secondary-dark'>Incomplete</span></p>
|
||||
{% endif %}
|
||||
<h4 class="margin-bottom-0">Target time frame</h4>
|
||||
{% if domain_request.has_timeframe is None %}
|
||||
<p class="margin-y-0"><span class='text-bold text-secondary-dark'>Incomplete</span></p>
|
||||
{% elif domain_request.has_timeframe %}
|
||||
<p class="margin-y-0">{{domain_request.time_frame_details}}</p>
|
||||
{% else %}
|
||||
<p class="margin-y-0">No</p>
|
||||
{% endif %}
|
||||
<h4 class="margin-bottom-0">Interagency initiative</h4>
|
||||
{% if domain_request.is_interagency_initiative is None %}
|
||||
<p class="margin-y-0"><span class='text-bold text-secondary-dark'>Incomplete</span></p>
|
||||
{% elif domain_request.is_interagency_initiative %}
|
||||
<p class="margin-y-0">{{domain_request.interagency_initiative_details}}</p>
|
||||
{% else %}
|
||||
<p class="margin-y-0">No</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% with title=form_titles|get_item:step value=domain_request.purpose|default:"<span class='text-bold text-secondary-dark'>Incomplete</span>"|safe %}
|
||||
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if step == Step.ADDITIONAL_DETAILS %}
|
||||
{% with title=form_titles|get_item:step value=domain_request.anything_else|default:"None" %}
|
||||
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
|
||||
{% endwith %}
|
||||
{% if requires_feb_questions %}
|
||||
{% with title=form_titles|get_item:step %}
|
||||
{% include "includes/summary_item.html" with title=title value=" " heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
|
||||
{% endwith %}
|
||||
<h4 class="margin-bottom-0">EOP contact</h4>
|
||||
{% if domain_request.working_with_eop is None %}
|
||||
<p class="margin-y-0"><span class='text-bold text-secondary-dark'>Incomplete</span></p>
|
||||
{% elif domain_request.working_with_eop %}
|
||||
<p class="margin-y-0">{{domain_request.eop_stakeholder_first_name}} {{domain_request.eop_stakeholder_last_name}}</p>
|
||||
{% else %}
|
||||
<p class="margin-y-0">No</p>
|
||||
{% endif %}
|
||||
<h4 class="margin-bottom-0">Anything else</h4>
|
||||
{% if domain_request.anything_else %}
|
||||
<p class="margin-y-0">{{domain_request.anything_else}}</p>
|
||||
{% else %}
|
||||
<p class="margin-y-0">None</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% with title=form_titles|get_item:step value=domain_request.anything_else|default:"None" %}
|
||||
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if step == Step.REQUIREMENTS %}
|
||||
|
|
|
@ -6,60 +6,62 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block form_fields %}
|
||||
{% if requires_feb_questions %}
|
||||
{% include "includes/required_fields.html" %}
|
||||
{% if requires_feb_questions %}
|
||||
<fieldset class="usa-fieldset">
|
||||
{{forms.0.management_form}}
|
||||
{{forms.1.management_form}}
|
||||
<h2 id="working-with-eop--question" class="margin-top-0 margin-bottom-0">Are you working with someone in the Executive Office of the President (EOP) on this request?</h2>
|
||||
<p id="working-with-eop--requirement" class="margin-bottom-2 margin-top-2">Working with the EOP is not required to request a .gov domain.</p>
|
||||
<p class="margin-bottom-0 margin-top-1">
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" add_aria_describedby="working-with-eop--requirement" add_legend_heading="Are you working with someone in the Executive Office of the President (EOP) on this request?" %}
|
||||
{% input_with_errors forms.0.working_with_eop %}
|
||||
{% endwith %}
|
||||
|
||||
<div id="eop-contact-container" class="conditional-panel display-none">
|
||||
<p class="margin-bottom-3 margin-top-3">
|
||||
<em>Provide the name of the person you're working with. <span class="usa-label--required">*</span></em>
|
||||
</p>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.1.first_name %}
|
||||
{% input_with_errors forms.1.last_name %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="usa-fieldset" aria-labelledby="anything-else-question">
|
||||
{{forms.2.management_form}}
|
||||
{{forms.3.management_form}}
|
||||
{{forms.4.management_form}}
|
||||
{{forms.5.management_form}}
|
||||
<fieldset class="usa-fieldset">
|
||||
<h2 class="margin-top-0 margin-bottom-0">Are you working with someone in the Executive Office of the President (EOP) on this request?</h2>
|
||||
|
||||
<p class="margin-bottom-0 margin-top-1">
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.0.working_with_eop %}
|
||||
{% endwith %}
|
||||
<h2 id="anything-else-question">Is there anything else you'd like us to know about your domain request?</h2>
|
||||
<p class="margin-bottom-0 margin-top-1">
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" add_legend_heading="Is there anything else you'd like us to know about your domain request?"%}
|
||||
{% input_with_errors forms.2.has_anything_else_text %}
|
||||
{% endwith %}
|
||||
|
||||
<div id="eop-contact-container" class="conditional-panel display-none">
|
||||
<p class="margin-bottom-0 margin-top-1">
|
||||
Provide the name and email of the person you're working with.<span class="usa-label--required">*</span>
|
||||
</p>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.1.first_name %}
|
||||
{% input_with_errors forms.1.last_name %}
|
||||
{% input_with_errors forms.1.email %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
<h2 class="margin-top-0 margin-bottom-0">Is there anything else you'd like us to know about your domain request?</h2>
|
||||
<p class="margin-bottom-0 margin-top-1">
|
||||
<em>Select one. <abbr class="usa-hint usa-hint--required" title="required">*</abbr></em>
|
||||
</p>
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.2.has_anything_else_text %}
|
||||
{% endwith %}
|
||||
|
||||
<div id="anything-else-details-container" class="conditional-panel display-none">
|
||||
<p class="usa-label">
|
||||
<em>Provide details below <span class="usa-label--required">*</span></em>
|
||||
</p>
|
||||
{% with add_label_class="usa-sr-only" attr_required="required" attr_maxlength="2000" %}
|
||||
{% input_with_errors forms.3.anything_else %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
</fieldset>
|
||||
{% else %}
|
||||
<fieldset class="usa-fieldset">
|
||||
<h2 class="margin-top-0 margin-bottom-0">Is there anything else you’d like us to know about your domain request?</h2>
|
||||
</legend>
|
||||
</fieldset>
|
||||
|
||||
<div id="anything-else">
|
||||
<p><em>This question is optional.</em></p>
|
||||
{% with attr_maxlength=2000 add_label_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.0.anything_else %}
|
||||
{% endwith %}
|
||||
<div id="anything-else-details-container" class="conditional-panel display-none">
|
||||
<p class="margin-bottom-3 margin-top-3">
|
||||
<em>Provide details below. <span class="usa-label--required">*</span></em>
|
||||
</p>
|
||||
{% with add_label_class="usa-sr-only" attr_required="required" attr_maxlength="2000" %}
|
||||
{% input_with_errors forms.3.anything_else %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</fieldset>
|
||||
{% else %}
|
||||
<fieldset class="usa-fieldset" aria-labelledby="anything-else-question">
|
||||
<h2 id="anything-else-question" class="margin-top-0 margin-bottom-0">Is there anything else you’d like us to know about your domain request?</h2>
|
||||
</legend>
|
||||
</fieldset>
|
||||
|
||||
<div id="anything-else">
|
||||
<p><em>This question is optional.</em></p>
|
||||
{% with attr_maxlength=2000 add_label_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.3.anything_else %}
|
||||
{% endwith %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -23,8 +23,10 @@ def input_with_errors(context, field=None): # noqa: C901
|
|||
add_required_class: like `add_class` but only if field is required
|
||||
add_label_class: append to input element's label's `class` attribute
|
||||
add_legend_class: append to input element's legend's `class` attribute
|
||||
add_legend_heading: sets the text for the legend associated with this element
|
||||
add_group_class: append to input element's surrounding tag's `class` attribute
|
||||
add_aria_label: append to input element's `aria_label` attribute
|
||||
add_aria_describedby: appends to input element's `aria-describedby` attribute
|
||||
attr_* - adds or replaces any single html attribute for the input
|
||||
add_error_attr_* - like `attr_*` but only if field.errors is not empty
|
||||
toggleable_input: shows a simple edit button, and adds display-none to the input field.
|
||||
|
@ -106,6 +108,8 @@ def input_with_errors(context, field=None): # noqa: C901
|
|||
|
||||
elif key == "add_aria_label":
|
||||
aria_labels.append(value)
|
||||
elif key == "add_aria_describedby":
|
||||
described_by.append(value)
|
||||
|
||||
elif key == "sublabel_text":
|
||||
sublabel_text.append(value)
|
||||
|
|
|
@ -2064,7 +2064,6 @@ class TestDomainRequestAdmin(MockEppLib):
|
|||
"working_with_eop",
|
||||
"eop_stakeholder_first_name",
|
||||
"eop_stakeholder_last_name",
|
||||
"eop_stakeholder_email",
|
||||
"purpose",
|
||||
"has_timeframe",
|
||||
"time_frame_details",
|
||||
|
|
|
@ -2615,8 +2615,8 @@ class DomainRequestTests(TestWithUser, WebTest):
|
|||
domain_form = dotgov_page.forms[0]
|
||||
domain = "test.gov"
|
||||
domain_form["dotgov_domain-requested_domain"] = domain
|
||||
domain_form["dotgov_domain-feb_naming_requirements"] = "True"
|
||||
domain_form["dotgov_domain-feb_naming_requirements_details"] = "test"
|
||||
domain_form["dotgov_domain-feb_naming_requirements"] = "False"
|
||||
domain_form["dotgov_domain-feb_naming_requirements_details"] = "Because this is a test"
|
||||
with patch(
|
||||
"registrar.forms.domain_request_wizard.DotGovDomainForm.clean_requested_domain", return_value=domain
|
||||
): # noqa
|
||||
|
@ -2631,11 +2631,11 @@ class DomainRequestTests(TestWithUser, WebTest):
|
|||
|
||||
purpose_form = purpose_page.forms[0]
|
||||
purpose_form["purpose-feb_purpose_choice"] = "redirect"
|
||||
purpose_form["purpose-purpose"] = "test"
|
||||
purpose_form["purpose-purpose"] = "testPurpose123"
|
||||
purpose_form["purpose-has_timeframe"] = "True"
|
||||
purpose_form["purpose-time_frame_details"] = "test"
|
||||
purpose_form["purpose-time_frame_details"] = "1/2/2025 - 1/2/2026"
|
||||
purpose_form["purpose-is_interagency_initiative"] = "True"
|
||||
purpose_form["purpose-interagency_initiative_details"] = "test"
|
||||
purpose_form["purpose-interagency_initiative_details"] = "FakeInteragencyInitiative"
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
purpose_result = purpose_form.submit()
|
||||
|
||||
|
@ -2646,9 +2646,8 @@ class DomainRequestTests(TestWithUser, WebTest):
|
|||
|
||||
additional_details_form = additional_details_page.forms[0]
|
||||
additional_details_form["portfolio_additional_details-working_with_eop"] = "True"
|
||||
additional_details_form["portfolio_additional_details-first_name"] = "Testy"
|
||||
additional_details_form["portfolio_additional_details-last_name"] = "Tester"
|
||||
additional_details_form["portfolio_additional_details-email"] = "testy@town.com"
|
||||
additional_details_form["portfolio_additional_details-first_name"] = "TesterFirstName"
|
||||
additional_details_form["portfolio_additional_details-last_name"] = "TesterLastName"
|
||||
additional_details_form["portfolio_additional_details-has_anything_else_text"] = "True"
|
||||
additional_details_form["portfolio_additional_details-anything_else"] = "test"
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
@ -2659,6 +2658,16 @@ class DomainRequestTests(TestWithUser, WebTest):
|
|||
requirements_page = additional_details_result.follow()
|
||||
self.feb_requirements_page_tests(requirements_page)
|
||||
|
||||
requirements_form = requirements_page.forms[0]
|
||||
requirements_form["requirements-is_policy_acknowledged"] = "True"
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
requirements_result = requirements_form.submit()
|
||||
|
||||
# ---- REVIEW PAGE ----
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
review_page = requirements_result.follow()
|
||||
self.feb_review_page_tests(review_page)
|
||||
|
||||
def feb_purpose_page_tests(self, purpose_page):
|
||||
self.assertContains(purpose_page, "What is the purpose of your requested domain?")
|
||||
|
||||
|
@ -2710,7 +2719,6 @@ class DomainRequestTests(TestWithUser, WebTest):
|
|||
self.assertContains(additional_details_page, "eop-contact-container")
|
||||
self.assertContains(additional_details_page, "additional_details-first_name")
|
||||
self.assertContains(additional_details_page, "additional_details-last_name")
|
||||
self.assertContains(additional_details_page, "additional_details-email")
|
||||
|
||||
# Make sure the additional details form is present
|
||||
self.assertContains(additional_details_page, "additional_details-has_anything_else_text")
|
||||
|
@ -2733,6 +2741,25 @@ class DomainRequestTests(TestWithUser, WebTest):
|
|||
"I read and understand the guidance outlined in the DOTGOV Act for operating a .gov domain.",
|
||||
)
|
||||
|
||||
def feb_review_page_tests(self, review_page):
|
||||
# Meets naming requirements
|
||||
self.assertContains(review_page, "Meets naming requirements")
|
||||
self.assertContains(review_page, "No")
|
||||
self.assertContains(review_page, "Because this is a test")
|
||||
# Purpose
|
||||
self.assertContains(review_page, "Purpose")
|
||||
self.assertContains(review_page, "Used as a redirect for an existing website")
|
||||
self.assertContains(review_page, "testPurpose123")
|
||||
# Target time frame
|
||||
self.assertContains(review_page, "Target time frame")
|
||||
self.assertContains(review_page, "1/2/2025 - 1/2/2026")
|
||||
# Interagency initiative
|
||||
self.assertContains(review_page, "Interagency initiative")
|
||||
self.assertContains(review_page, "FakeInteragencyInitiative")
|
||||
# EOP contact
|
||||
self.assertContains(review_page, "EOP contact")
|
||||
self.assertContains(review_page, "TesterFirstName TesterLastName")
|
||||
|
||||
@less_console_noise_decorator
|
||||
def test_domain_request_formsets(self):
|
||||
"""Users are able to add more than one of some fields."""
|
||||
|
|
|
@ -1841,6 +1841,18 @@ class DomainRequestExport(BaseExport):
|
|||
details = [cisa_rep, model.get("anything_else")]
|
||||
additional_details = " | ".join([field for field in details if field])
|
||||
|
||||
# FEB fields
|
||||
purpose_type = model.get("feb_purpose_choice")
|
||||
purpose_type_display = (
|
||||
DomainRequest.FEBPurposeChoices.get_purpose_label(purpose_type) if purpose_type else "N/A"
|
||||
)
|
||||
eop_stakeholder_first_name = model.get("eop_stakeholder_first_name")
|
||||
eop_stakeholder_last_name = model.get("eop_stakeholder_last_name")
|
||||
if not eop_stakeholder_first_name or not eop_stakeholder_last_name:
|
||||
eop_stakeholder_name = None
|
||||
else:
|
||||
eop_stakeholder_name = f"{eop_stakeholder_first_name} {eop_stakeholder_last_name}"
|
||||
|
||||
# create a dictionary of fields which can be included in output.
|
||||
# "extra_fields" are precomputed fields (generated in the DB or parsed).
|
||||
FIELDS = {
|
||||
|
@ -1882,6 +1894,12 @@ class DomainRequestExport(BaseExport):
|
|||
"Last submitted date": model.get("last_submitted_date"),
|
||||
"First submitted date": model.get("first_submitted_date"),
|
||||
"Last status update": model.get("last_status_update"),
|
||||
# FEB only fields
|
||||
"Purpose": purpose_type_display,
|
||||
"Domain name rationale": model.get("feb_naming_requirements_details", None),
|
||||
"Target time frame": model.get("time_frame_details", None),
|
||||
"Interagency initiative": model.get("interagency_initiative_details", None),
|
||||
"EOP stakeholder name": eop_stakeholder_name,
|
||||
}
|
||||
|
||||
row = [FIELDS.get(column, "") for column in columns]
|
||||
|
@ -1927,6 +1945,12 @@ class DomainRequestDataType(DomainRequestExport):
|
|||
"Last submitted date",
|
||||
"First submitted date",
|
||||
"Last status update",
|
||||
"Purpose",
|
||||
"Domain name rationale",
|
||||
"Target time frame",
|
||||
"Interagency initiative",
|
||||
"EOP stakeholder name",
|
||||
"EOP stakeholder email",
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
@ -2071,6 +2095,12 @@ class DomainRequestDataFull(DomainRequestExport):
|
|||
"CISA regional representative",
|
||||
"Current websites",
|
||||
"Investigator",
|
||||
"Purpose",
|
||||
"Domain name rationale",
|
||||
"Target time frame",
|
||||
"Interagency initiative",
|
||||
"EOP stakeholder name",
|
||||
"EOP stakeholder email",
|
||||
]
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -625,6 +625,11 @@ class PortfolioAdditionalDetails(DomainRequestWizard):
|
|||
2: FEBAnythingElseYesNoForm
|
||||
3: PortfolioAnythingElseForm
|
||||
"""
|
||||
if not self.requires_feb_questions():
|
||||
for i in range(3):
|
||||
forms[i].mark_form_for_deletion()
|
||||
# If FEB questions aren't required, validate only the anything else form
|
||||
return forms[3].is_valid()
|
||||
eop_forms_valid = True
|
||||
if not forms[0].is_valid():
|
||||
# If the user isn't working with EOP, don't validate the EOP contact form
|
||||
|
@ -792,11 +797,11 @@ class Purpose(DomainRequestWizard):
|
|||
option = feb_purpose_options_form.cleaned_data.get("feb_purpose_choice")
|
||||
if option == "new":
|
||||
purpose_details_form.fields["purpose"].error_messages = {
|
||||
"required": "Explain why a new domain is required."
|
||||
"required": "Provide details on why a new domain is required."
|
||||
}
|
||||
elif option == "redirect":
|
||||
purpose_details_form.fields["purpose"].error_messages = {
|
||||
"required": "Explain why a redirect is needed."
|
||||
"required": "Provide details on why a redirect is necessary."
|
||||
}
|
||||
elif option == "other":
|
||||
purpose_details_form.fields["purpose"].error_messages = {
|
||||
|
@ -965,6 +970,9 @@ class Review(DomainRequestWizard):
|
|||
context["Step"] = self.get_step_enum().__members__
|
||||
context["domain_request"] = self.domain_request
|
||||
context["requires_feb_questions"] = self.requires_feb_questions()
|
||||
context["purpose_label"] = DomainRequest.FEBPurposeChoices.get_purpose_label(
|
||||
self.domain_request.feb_purpose_choice
|
||||
)
|
||||
return context
|
||||
|
||||
def goto_next_step(self):
|
||||
|
@ -1178,6 +1186,10 @@ class PortfolioDomainRequestStatusViewOnly(DetailView):
|
|||
context["Step"] = PortfolioDomainRequestStep.__members__
|
||||
context["steps"] = request_step_list(wizard, PortfolioDomainRequestStep)
|
||||
context["form_titles"] = wizard.titles
|
||||
context["requires_feb_questions"] = self.object.is_feb() and flag_is_active_for_user(
|
||||
self.request.user, "organization_feature"
|
||||
)
|
||||
context["purpose_label"] = DomainRequest.FEBPurposeChoices.get_purpose_label(self.object.feb_purpose_choice)
|
||||
return context
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue