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):
|
||||
"""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),
|
||||
),
|
||||
]
|
|
@ -54,6 +54,11 @@ class DomainRequest(TimeStampedModel):
|
|||
"""Returns the associated label for a given status name"""
|
||||
return cls(status_name).label if status_name else None
|
||||
|
||||
class FEBPurposeChoices(models.TextChoices):
|
||||
WEBSITE = "website"
|
||||
REDIRECT = "redirect"
|
||||
OTHER = "other"
|
||||
|
||||
class StateTerritoryChoices(models.TextChoices):
|
||||
ALABAMA = "AL", "Alabama (AL)"
|
||||
ALASKA = "AK", "Alaska (AK)"
|
||||
|
@ -501,6 +506,7 @@ class DomainRequest(TimeStampedModel):
|
|||
on_delete=models.PROTECT,
|
||||
)
|
||||
|
||||
# Fields specific to Federal Executive Branch agencies, used by OMB for reviewing requests
|
||||
feb_naming_requirements = models.BooleanField(
|
||||
null=True,
|
||||
blank=True,
|
||||
|
@ -511,6 +517,40 @@ class DomainRequest(TimeStampedModel):
|
|||
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(
|
||||
"registrar.Website",
|
||||
blank=True,
|
||||
|
@ -518,10 +558,7 @@ class DomainRequest(TimeStampedModel):
|
|||
help_text="Other domain names the creator provided for consideration",
|
||||
)
|
||||
|
||||
purpose = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
)
|
||||
|
||||
|
||||
other_contacts = models.ManyToManyField(
|
||||
"registrar.Contact",
|
||||
|
|
|
@ -3,17 +3,90 @@
|
|||
|
||||
{% 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>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>
|
||||
<p>Describe how you’ll use your .gov domain. Will it be used for a website, email, or something else?</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>{{requires_feb_questions}}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% 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.0.purpose %}
|
||||
{% if requires_feb_questions %}
|
||||
{% block feb_fields %}
|
||||
<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 %}
|
||||
{% endblock %}
|
||||
|
||||
<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 %}
|
||||
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from django.views.generic import TemplateView
|
||||
from django.contrib import messages
|
||||
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.models import DomainRequest
|
||||
from registrar.models.contact import Contact
|
||||
|
@ -182,7 +183,9 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
|
|||
return PortfolioDomainRequestStep if self.is_portfolio else Step
|
||||
|
||||
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
|
||||
def prefix(self):
|
||||
|
@ -709,7 +712,64 @@ class DotgovDomain(DomainRequestWizard):
|
|||
|
||||
class Purpose(DomainRequestWizard):
|
||||
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):
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue