mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-29 05:56:30 +02:00
initial work on feb purpose questions
This commit is contained in:
parent
b2c57b5aa1
commit
df303fa66f
6 changed files with 341 additions and 36 deletions
|
@ -642,25 +642,6 @@ class ExecutiveNamingRequirementsDetailsForm(BaseDeletableRegistrarForm):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class PurposeForm(RegistrarForm):
|
|
||||||
purpose = forms.CharField(
|
|
||||||
label="Purpose",
|
|
||||||
widget=forms.Textarea(
|
|
||||||
attrs={
|
|
||||||
"aria-label": "What is the purpose of your requested domain? Describe how you’ll use your .gov domain. \
|
|
||||||
Will it be used for a website, email, or something else?"
|
|
||||||
}
|
|
||||||
),
|
|
||||||
validators=[
|
|
||||||
MaxLengthValidator(
|
|
||||||
2000,
|
|
||||||
message="Response must be less than 2000 characters.",
|
|
||||||
)
|
|
||||||
],
|
|
||||||
error_messages={"required": "Describe how you’ll use the .gov domain you’re requesting."},
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OtherContactsYesNoForm(BaseYesNoForm):
|
class OtherContactsYesNoForm(BaseYesNoForm):
|
||||||
"""The yes/no field for the OtherContacts form."""
|
"""The yes/no field for the OtherContacts form."""
|
||||||
|
|
||||||
|
|
114
src/registrar/forms/domainrequestwizard/purpose.py
Normal file
114
src/registrar/forms/domainrequestwizard/purpose.py
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
from django import forms
|
||||||
|
from django.core.validators import MaxLengthValidator
|
||||||
|
from registrar.forms.utility.wizard_form_helper import BaseDeletableRegistrarForm, BaseYesNoForm, RegistrarForm
|
||||||
|
|
||||||
|
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"))
|
||||||
|
|
||||||
|
feb_purpose_choice = forms.ChoiceField(
|
||||||
|
required=True,
|
||||||
|
choices=form_choices,
|
||||||
|
widget=forms.RadioSelect,
|
||||||
|
error_messages={
|
||||||
|
"required": "This question is required.",
|
||||||
|
},
|
||||||
|
label = "Select one"
|
||||||
|
)
|
||||||
|
|
||||||
|
class PurposeDetailsForm(BaseDeletableRegistrarForm):
|
||||||
|
|
||||||
|
labels = {
|
||||||
|
"new": "Explain why a new domain is required and why a subdomain of an existing domain doesn't meet your needs. \
|
||||||
|
Include any data that supports a clear public benefit or evident user need for this new domain.",
|
||||||
|
"redirect": "Explain why a redirect is necessary",
|
||||||
|
"other": "Describe how this domain will be used",
|
||||||
|
}
|
||||||
|
|
||||||
|
field_name="purpose"
|
||||||
|
|
||||||
|
purpose = forms.CharField(
|
||||||
|
label="Purpose",
|
||||||
|
widget=forms.Textarea(
|
||||||
|
attrs={
|
||||||
|
"aria-label": "What is the purpose of your requested domain? Describe how you’ll use your .gov domain. \
|
||||||
|
Will it be used for a website, email, or something else?"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
validators=[
|
||||||
|
MaxLengthValidator(
|
||||||
|
2000,
|
||||||
|
message="Response must be less than 2000 characters.",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
error_messages={"required": "Describe how you’ll use the .gov domain you’re requesting."},
|
||||||
|
)
|
||||||
|
|
||||||
|
class FEBTimeFrameYesNoForm(BaseDeletableRegistrarForm, BaseYesNoForm):
|
||||||
|
"""
|
||||||
|
Form for determining whether the domain request comes with a target timeframe for launch.
|
||||||
|
If the "no" option is selected, details must be provided via the separate details form.
|
||||||
|
"""
|
||||||
|
|
||||||
|
field_name = "has_timeframe"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def form_is_checked(self):
|
||||||
|
"""
|
||||||
|
Determines the initial checked state of the form based on the domain_request's attributes.
|
||||||
|
"""
|
||||||
|
return self.domain_request.has_timeframe
|
||||||
|
|
||||||
|
|
||||||
|
class FEBTimeFrameDetailsForm(BaseDeletableRegistrarForm):
|
||||||
|
time_frame_details = forms.CharField(
|
||||||
|
label="time_frame_details",
|
||||||
|
widget=forms.Textarea(
|
||||||
|
attrs={
|
||||||
|
"aria-label": "Provide details on your target timeframe. \
|
||||||
|
Is there a special significance to this date (legal requirement, announcement, event, etc)?"
|
||||||
|
}
|
||||||
|
),
|
||||||
|
validators=[
|
||||||
|
MaxLengthValidator(
|
||||||
|
2000,
|
||||||
|
message="Response must be less than 2000 characters.",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
error_messages={"required": "Provide details on your target timeframe."},
|
||||||
|
)
|
||||||
|
|
||||||
|
class FEBInteragencyInitiativeYesNoForm(BaseDeletableRegistrarForm, BaseYesNoForm):
|
||||||
|
"""
|
||||||
|
Form for determining whether the domain request is part of an interagency initative.
|
||||||
|
If the "no" option is selected, details must be provided via the separate details form.
|
||||||
|
"""
|
||||||
|
|
||||||
|
field_name = "is_interagency_initiative"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def form_is_checked(self):
|
||||||
|
"""
|
||||||
|
Determines the initial checked state of the form based on the domain_request's attributes.
|
||||||
|
"""
|
||||||
|
return self.domain_request.is_interagency_initiative
|
||||||
|
|
||||||
|
|
||||||
|
class FEBInteragencyInitiativeDetailsForm(BaseDeletableRegistrarForm):
|
||||||
|
interagency_initiative_details = forms.CharField(
|
||||||
|
label="interagency_initiative_details",
|
||||||
|
widget=forms.Textarea(
|
||||||
|
attrs={
|
||||||
|
"aria-label": "Name the agencies that will be involved in this initiative."
|
||||||
|
}
|
||||||
|
),
|
||||||
|
validators=[
|
||||||
|
MaxLengthValidator(
|
||||||
|
2000,
|
||||||
|
message="Response must be less than 2000 characters.",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
error_messages={"required": "Name the agencies that will be involved in this initiative."},
|
||||||
|
)
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Generated by Django 4.2.17 on 2025-02-25 23:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("registrar", "0141_domainrequest_feb_naming_requirements_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="domainrequest",
|
||||||
|
name="feb_purpose_choice",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True, choices=[("website", "Website"), ("redirect", "Redirect"), ("other", "Other")], null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="domainrequest",
|
||||||
|
name="has_timeframe",
|
||||||
|
field=models.BooleanField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="domainrequest",
|
||||||
|
name="interagency_initiative_details",
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="domainrequest",
|
||||||
|
name="is_interagency_initiative",
|
||||||
|
field=models.BooleanField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="domainrequest",
|
||||||
|
name="time_frame_details",
|
||||||
|
field=models.TextField(blank=True, null=True),
|
||||||
|
),
|
||||||
|
]
|
|
@ -53,6 +53,11 @@ class DomainRequest(TimeStampedModel):
|
||||||
def get_status_label(cls, status_name: str):
|
def get_status_label(cls, status_name: str):
|
||||||
"""Returns the associated label for a given status name"""
|
"""Returns the associated label for a given status name"""
|
||||||
return cls(status_name).label if status_name else None
|
return cls(status_name).label if status_name else None
|
||||||
|
|
||||||
|
class FEBPurposeChoices(models.TextChoices):
|
||||||
|
WEBSITE = "website"
|
||||||
|
REDIRECT = "redirect"
|
||||||
|
OTHER = "other"
|
||||||
|
|
||||||
class StateTerritoryChoices(models.TextChoices):
|
class StateTerritoryChoices(models.TextChoices):
|
||||||
ALABAMA = "AL", "Alabama (AL)"
|
ALABAMA = "AL", "Alabama (AL)"
|
||||||
|
@ -501,6 +506,7 @@ class DomainRequest(TimeStampedModel):
|
||||||
on_delete=models.PROTECT,
|
on_delete=models.PROTECT,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Fields specific to Federal Executive Branch agencies, used by OMB for reviewing requests
|
||||||
feb_naming_requirements = models.BooleanField(
|
feb_naming_requirements = models.BooleanField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
|
@ -511,6 +517,40 @@ class DomainRequest(TimeStampedModel):
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
feb_purpose_choice = models.CharField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
choices=FEBPurposeChoices.choices,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 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,
|
||||||
|
)
|
||||||
|
|
||||||
|
has_timeframe = models.BooleanField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
time_frame_details = models.TextField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
is_interagency_initiative = models.BooleanField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
interagency_initiative_details = models.TextField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
alternative_domains = models.ManyToManyField(
|
alternative_domains = models.ManyToManyField(
|
||||||
"registrar.Website",
|
"registrar.Website",
|
||||||
blank=True,
|
blank=True,
|
||||||
|
@ -518,10 +558,7 @@ class DomainRequest(TimeStampedModel):
|
||||||
help_text="Other domain names the creator provided for consideration",
|
help_text="Other domain names the creator provided for consideration",
|
||||||
)
|
)
|
||||||
|
|
||||||
purpose = models.TextField(
|
|
||||||
null=True,
|
|
||||||
blank=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
other_contacts = models.ManyToManyField(
|
other_contacts = models.ManyToManyField(
|
||||||
"registrar.Contact",
|
"registrar.Contact",
|
||||||
|
|
|
@ -3,17 +3,90 @@
|
||||||
|
|
||||||
{% block form_instructions %}
|
{% block form_instructions %}
|
||||||
<p>.Gov domains are intended for public use. Domains will not be given to organizations that only want to reserve a domain name (defensive registration) or that only intend to use the domain internally (as for an intranet).</p>
|
<p>.Gov domains are intended for public use. Domains will not be given to organizations that only want to reserve a domain name (defensive registration) or that only intend to use the domain internally (as for an intranet).</p>
|
||||||
<p>Read about <a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% public_site_url 'domains/requirements/' %}">activities that are prohibited on .gov domains.</a></p>
|
<p>Read about <a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% public_site_url 'domains/requirements/' %}">activities that are prohibited on .gov domains.</a></p>
|
||||||
<h2>What is the purpose of your requested domain?</h2>
|
<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>
|
<h2>{{requires_feb_questions}}</h2>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block form_required_fields_help_text %}
|
{% if requires_feb_questions %}
|
||||||
{# commented out so it does not appear on this page #}
|
{% block feb_fields %}
|
||||||
{% endblock %}
|
<fieldset class="usa-fieldset margin-top-0 dotgov-domain-form">
|
||||||
|
{{forms.0.management_form}}
|
||||||
|
{{forms.1.management_form}}
|
||||||
|
{{forms.2.management_form}}
|
||||||
|
{{forms.3.management_form}}
|
||||||
|
{{forms.4.management_form}}
|
||||||
|
{{forms.5.management_form}}
|
||||||
|
<p class="usa-label">
|
||||||
|
Select One <span class="usa-label--required">*</span>
|
||||||
|
</p>
|
||||||
|
{% with add_label_class="usa-sr-only" attr_required="required" maxlength="2000" %}
|
||||||
|
{% input_with_errors forms.0.feb_purpose_choice %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<div id="domain-purpose-details-container" class="conditional-panel" style="display: none;">
|
||||||
|
<p class="usa-label">
|
||||||
|
Provide details below <span class="usa-label--required">*</span>
|
||||||
|
</p>
|
||||||
|
{% with add_label_class="usa-sr-only" attr_required="required" maxlength="2000" %}
|
||||||
|
{% input_with_errors forms.1.purpose %}
|
||||||
|
{% endwith %}
|
||||||
|
<p class="usa-hint">Maximum 2000 characters allowed.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<legend>
|
||||||
|
<h3>Do you have a target time frame for launching this domain?</h3>
|
||||||
|
</legend>
|
||||||
|
<p class="usa-label">
|
||||||
|
Select One <span class="usa-label--required">*</span>
|
||||||
|
</p>
|
||||||
|
{% with add_label_class="usa-sr-only" attr_required="required" maxlength="2000" %}
|
||||||
|
{% input_with_errors forms.2.has_timeframe %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<div id="domain-timeframe-details-container" class="conditional-panel" style="display: none;">
|
||||||
|
<p class="usa-label">
|
||||||
|
Provide details below <span class="usa-label--required">*</span>
|
||||||
|
</p>
|
||||||
|
{% with add_label_class="usa-sr-only" attr_required="required" maxlength="2000" %}
|
||||||
|
{% input_with_errors forms.3.time_frame_details %}
|
||||||
|
{% endwith %}
|
||||||
|
<p class="usa-hint">Maximum 2000 characters allowed.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<legend>
|
||||||
|
<h3>Will the domain name be used for an interagency initiative?</h3>
|
||||||
|
</legend>
|
||||||
|
<p class="usa-label">
|
||||||
|
Select One <span class="usa-label--required">*</span>
|
||||||
|
</p>
|
||||||
|
{% with add_label_class="usa-sr-only" attr_required="required" maxlength="2000" %}
|
||||||
|
{% input_with_errors forms.4.is_interagency_initiative %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<div id="domain-interagency-initaitive-details-container" class="conditional-panel" style="display: none;">
|
||||||
|
<p class="usa-label">
|
||||||
|
Provide details below <span class="usa-label--required">*</span>
|
||||||
|
</p>
|
||||||
|
{% with add_label_class="usa-sr-only" attr_required="required" maxlength="2000" %}
|
||||||
|
{% input_with_errors forms.5.interagency_initiative_details %}
|
||||||
|
{% endwith %}
|
||||||
|
<p class="usa-hint">Maximum 2000 characters allowed.</p>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
{% endblock %}
|
||||||
|
{% else %}
|
||||||
|
<p>Describe how you’ll use your .gov domain. Will it be used for a website, email, or something else?</p>
|
||||||
|
|
||||||
|
{% block form_required_fields_help_text %}
|
||||||
|
{# commented out so it does not appear on this page #}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block form_fields %}
|
||||||
|
{% with attr_maxlength=2000 add_label_class="usa-sr-only" %}
|
||||||
|
{% input_with_errors forms.1.purpose %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endblock %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% block form_fields %}
|
|
||||||
{% with attr_maxlength=2000 add_label_class="usa-sr-only" %}
|
|
||||||
{% input_with_errors forms.0.purpose %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from registrar.forms import domain_request_wizard as forms
|
from registrar.forms import domain_request_wizard as forms
|
||||||
|
from registrar.forms.domainrequestwizard import purpose
|
||||||
from registrar.forms.utility.wizard_form_helper import request_step_list
|
from registrar.forms.utility.wizard_form_helper import request_step_list
|
||||||
from registrar.models import DomainRequest
|
from registrar.models import DomainRequest
|
||||||
from registrar.models.contact import Contact
|
from registrar.models.contact import Contact
|
||||||
|
@ -182,7 +183,9 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
||||||
return PortfolioDomainRequestStep if self.is_portfolio else Step
|
return PortfolioDomainRequestStep if self.is_portfolio else Step
|
||||||
|
|
||||||
def requires_feb_questions(self) -> bool:
|
def requires_feb_questions(self) -> bool:
|
||||||
return self.domain_request.is_feb() and flag_is_active_for_user(self.request.user, "organization_feature")
|
# TODO: this is for testing, revert later
|
||||||
|
return False
|
||||||
|
# return self.domain_request.is_feb() and flag_is_active_for_user(self.request.user, "organization_feature")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def prefix(self):
|
def prefix(self):
|
||||||
|
@ -709,7 +712,64 @@ class DotgovDomain(DomainRequestWizard):
|
||||||
|
|
||||||
class Purpose(DomainRequestWizard):
|
class Purpose(DomainRequestWizard):
|
||||||
template_name = "domain_request_purpose.html"
|
template_name = "domain_request_purpose.html"
|
||||||
forms = [forms.PurposeForm]
|
|
||||||
|
forms = [purpose.FEBPurposeOptionsForm,
|
||||||
|
purpose.PurposeDetailsForm,
|
||||||
|
purpose.FEBTimeFrameYesNoForm,
|
||||||
|
purpose.FEBTimeFrameDetailsForm,
|
||||||
|
purpose.FEBInteragencyInitiativeYesNoForm,
|
||||||
|
purpose.FEBInteragencyInitiativeDetailsForm
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_context_data(self):
|
||||||
|
context= super().get_context_data()
|
||||||
|
context["requires_feb_questions"] = self.requires_feb_questions()
|
||||||
|
return context
|
||||||
|
|
||||||
|
def is_valid(self, forms_list: list) -> bool:
|
||||||
|
"""
|
||||||
|
Expected order of forms_list:
|
||||||
|
0: FEBPurposeOptionsForm
|
||||||
|
1: PurposeDetailsForm
|
||||||
|
2: FEBTimeFrameYesNoForm
|
||||||
|
3: FEBTimeFrameDetailsForm
|
||||||
|
4: FEBInteragencyInitiativeYesNoForm
|
||||||
|
5: FEBInteragencyInitiativeDetailsForm
|
||||||
|
"""
|
||||||
|
|
||||||
|
feb_purpose_options_form = forms_list[0]
|
||||||
|
purpose_details_form = forms_list[1]
|
||||||
|
feb_timeframe_yes_no_form = forms_list[2]
|
||||||
|
feb_timeframe_details_form = forms_list[3]
|
||||||
|
feb_initiative_yes_no_form = forms_list[4]
|
||||||
|
feb_initiative_details_form = forms_list[5]
|
||||||
|
|
||||||
|
if not self.requires_feb_questions():
|
||||||
|
# if FEB questions don't apply, mark all other forms for deletion
|
||||||
|
feb_purpose_options_form.mark_form_for_deletion()
|
||||||
|
feb_timeframe_yes_no_form.mark_form_for_deletion()
|
||||||
|
feb_timeframe_details_form.mark_form_for_deletion()
|
||||||
|
feb_initiative_yes_no_form.mark_form_for_deletion()
|
||||||
|
feb_initiative_details_form.mark_form_for_deletion()
|
||||||
|
# we only care about the purpose form in this case since it's used in both instances
|
||||||
|
return purpose_details_form.is_valid()
|
||||||
|
|
||||||
|
if not feb_purpose_options_form.id_valid():
|
||||||
|
# Ensure details form doesn't throw errors if it's not showing
|
||||||
|
purpose_details_form.mark_form_for_deletion()
|
||||||
|
|
||||||
|
if not feb_initiative_yes_no_form.is_valid() or not feb_timeframe_yes_no_form.cleaned_data.get("has_timeframe"):
|
||||||
|
# Ensure details form doesn't throw errors if it's not showing
|
||||||
|
feb_initiative_details_form.mark_form_for_deletion()
|
||||||
|
|
||||||
|
if not feb_timeframe_yes_no_form.is_valid() or not feb_initiative_yes_no_form.cleaned_data.get("is_interagency_initiative"):
|
||||||
|
# Ensure details form doesn't throw errors if it's not showing
|
||||||
|
feb_timeframe_details_form.mark_form_for_delation()
|
||||||
|
|
||||||
|
valid = all(form.is_valid() for form in forms_list if not form.form_data_marked_for_deletion)
|
||||||
|
|
||||||
|
return valid
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class OtherContacts(DomainRequestWizard):
|
class OtherContacts(DomainRequestWizard):
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue