correct form validation and dynamic elements

This commit is contained in:
Matt-Spence 2025-02-25 14:34:01 -05:00
parent 190bca2cac
commit 065febd496
No known key found for this signature in database
4 changed files with 32 additions and 83 deletions

View file

@ -607,7 +607,8 @@ class DotGovDomainForm(RegistrarForm):
}, },
) )
class ExecutiveNamingRequirementsYesNoForm(BaseYesNoForm):
class ExecutiveNamingRequirementsYesNoForm(BaseYesNoForm, BaseDeletableRegistrarForm):
""" """
Form for verifying if the domain request meets the Federal Executive Branch domain naming requirements. Form for verifying if the domain request meets the Federal Executive Branch domain naming requirements.
If the "no" option is selected, details must be provided via the separate details form. If the "no" option is selected, details must be provided via the separate details form.
@ -622,76 +623,24 @@ class ExecutiveNamingRequirementsYesNoForm(BaseYesNoForm):
""" """
return self.domain_request.feb_naming_requirements return self.domain_request.feb_naming_requirements
def clean(self):
# Skip validation if this form is not applicable.
if not (self.domain_request.is_federal() and self.domain_request.federal_type == "Executive"):
# Initialize cleaned_data if it doesn't exist
if not hasattr(self, 'cleaned_data'):
self.cleaned_data = {}
# If not executive, default to None
self.cleaned_data["feb_naming_requirements"] = None
return self.cleaned_data
# Only validate the yes/no field here; details are handled by the separate details form.
cleaned = super().clean()
return cleaned
def to_database(self, obj: DomainRequest):
"""
Saves the cleaned data from this form to the DomainRequest object.
"""
if not self.is_valid():
return
obj.feb_naming_requirements = (self.cleaned_data.get("feb_naming_requirements", None) == "yes")
obj.save()
@classmethod
def from_database(cls, obj):
"""
Retrieves initial data from the DomainRequest object to prepopulate the form.
"""
initial = {}
if hasattr(obj, "feb_naming_requirements"):
initial["feb_naming_requirements"] = "yes" if obj.feb_naming_requirements else "no"
return initial
class ExecutiveNamingRequirementsDetailsForm(BaseDeletableRegistrarForm): class ExecutiveNamingRequirementsDetailsForm(BaseDeletableRegistrarForm):
JOIN = "feb_naming_requirements_details"
# Text area for additional details; rendered conditionally when "no" is selected. # Text area for additional details; rendered conditionally when "no" is selected.
feb_naming_requirements_details = forms.CharField( feb_naming_requirements_details = forms.CharField(
widget=forms.Textarea(attrs={'maxlength': '2000'}), widget=forms.Textarea(attrs={"maxlength": "2000"}),
max_length=2000, max_length=2000,
required=True, required=True,
error_messages={"required": ("This field is required.")}, error_messages={"required": ("This field is required.")},
validators=[
MaxLengthValidator(
2000,
message="Response must be less than 2000 characters.",
)
],
label="", label="",
help_text="Maximum 2000 characters allowed.", help_text="Maximum 2000 characters allowed.",
) )
def to_database(self, obj: DomainRequest):
if not self.is_valid():
return
obj.feb_naming_requirements_details = self.cleaned_data["feb_naming_requirements_details"]
obj.save()
def is_valid(self):
"""
Validate that details are provided when required.
If the form is marked for deletion, bypass validation.
"""
if self.form_data_marked_for_deletion:
return True
is_valid = super().is_valid()
if not is_valid:
return False
# Check if the details field has content
details = self.cleaned_data.get('feb_naming_requirements_details', '').strip()
if not details:
return False
return True
class PurposeForm(RegistrarForm): class PurposeForm(RegistrarForm):
purpose = forms.CharField( purpose = forms.CharField(

View file

@ -2,7 +2,7 @@
{% load static field_helpers url_helpers %} {% load static field_helpers url_helpers %}
{% block form_instructions %} {% 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 is_feb %}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 <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:
<ul class="usa-list"> <ul class="usa-list">
<li>Be available </li> <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>
@ -101,7 +101,7 @@
{{ forms.2.management_form }} {{ forms.2.management_form }}
{{ forms.3.management_form }} {{ forms.3.management_form }}
{% if is_feb %} {% if requires_feb_questions %}
<fieldset class="usa-fieldset margin-top-0 dotgov-domain-form"> <fieldset class="usa-fieldset margin-top-0 dotgov-domain-form">
<legend> <legend>
<h2>Does this submission meet each domain naming requirement?</h2> <h2>Does this submission meet each domain naming requirement?</h2>

View file

@ -12,6 +12,7 @@ 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
from registrar.models.user import User from registrar.models.user import User
from registrar.utility.waffle import flag_is_active_for_user
from registrar.views.utility import StepsHelper from registrar.views.utility import StepsHelper
from registrar.views.utility.permission_views import DomainRequestPermissionDeleteView from registrar.views.utility.permission_views import DomainRequestPermissionDeleteView
from registrar.utility.enums import Step, PortfolioDomainRequestStep from registrar.utility.enums import Step, PortfolioDomainRequestStep
@ -180,6 +181,9 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
"""Determines which step enum we should use for the wizard""" """Determines which step enum we should use for the wizard"""
return PortfolioDomainRequestStep if self.is_portfolio else Step 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")
@property @property
def prefix(self): def prefix(self):
"""Namespace the wizard to avoid clashes in session variable names.""" """Namespace the wizard to avoid clashes in session variable names."""
@ -227,7 +231,12 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
if portfolio and not self._domain_request.generic_org_type: if portfolio and not self._domain_request.generic_org_type:
self._domain_request.generic_org_type = portfolio.organization_type self._domain_request.generic_org_type = portfolio.organization_type
self._domain_request.save() self._domain_request.save()
if portfolio and not self._domain_request.federal_type:
logger.debug(f"Setting fed type to {portfolio.federal_type}")
self._domain_request.federal_type = portfolio.federal_type
self._domain_request.save()
else: else:
logger.debug("Did not find domain request in DomainRequestWizard, creating new one.")
self._domain_request = DomainRequest.objects.create(creator=self.request.user) self._domain_request = DomainRequest.objects.create(creator=self.request.user)
return self._domain_request return self._domain_request
@ -662,8 +671,7 @@ class DotgovDomain(DomainRequestWizard):
def get_context_data(self): def get_context_data(self):
context = super().get_context_data() context = super().get_context_data()
context["generic_org_type"] = self.domain_request.generic_org_type context["requires_feb_questions"] = self.requires_feb_questions()
context["is_feb"] = self.domain_request.is_feb()
return context return context
def is_valid(self, forms_list: list) -> bool: def is_valid(self, forms_list: list) -> bool:
@ -675,34 +683,26 @@ class DotgovDomain(DomainRequestWizard):
3: ExecutiveNamingRequirementsDetailsForm 3: ExecutiveNamingRequirementsDetailsForm
""" """
logger.debug("Validating dotgov domain form") logger.debug("Validating dotgov domain form")
# If not a federal executive branch agency, mark executive-related forms for deletion. # If FEB questions aren't required, validate only non-FEB forms
if not (self.domain_request.is_feb()): if not self.requires_feb_questions():
forms_list[2].mark_form_for_deletion() forms_list[2].mark_form_for_deletion()
forms_list[3].mark_form_for_deletion() forms_list[3].mark_form_for_deletion()
return all(form.is_valid() for form in forms_list) return forms_list[0].is_valid() and forms_list[1].is_valid()
if not forms_list[2].is_valid(): if not forms_list[2].is_valid():
logger.debug("Dotgov domain form is invalid") logger.debug("Dotgov domain form is invalid")
if forms_list[2].cleaned_data.get("feb_naming_requirements", None) != "no": # mark details form for deletion so that its errors don't show up
forms_list[3].mark_form_for_deletion() forms_list[3].mark_form_for_deletion()
return False return False
logger.debug(f"feb_naming_requirements: {forms_list[2].cleaned_data.get('feb_naming_requirements', None)}") if forms_list[2].cleaned_data.get("feb_naming_requirements", None):
if forms_list[2].cleaned_data.get("feb_naming_requirements", None) != "no":
logger.debug("Marking details form for deletion") logger.debug("Marking details form for deletion")
# If the user selects "yes" or has made no selection, no details are needed. # If the user selects "yes" or has made no selection, no details are needed.
forms_list[3].mark_form_for_deletion() forms_list[3].mark_form_for_deletion()
valid = all( valid = all(form.is_valid() for i, form in enumerate(forms_list) if i != 3)
form.is_valid() for i, form in enumerate(forms_list) if i != 3
)
else: else:
# "No" was selected details are required. # "No" was selected details are required.
valid = ( valid = all(form.is_valid() for form in forms_list)
forms_list[2].is_valid() and
forms_list[3].is_valid() and
all(form.is_valid() for i, form in enumerate(forms_list) if i not in [2, 3])
)
return valid return valid