diff --git a/src/registrar/config/urls.py b/src/registrar/config/urls.py index d8d61ac87..72c0604a0 100644 --- a/src/registrar/config/urls.py +++ b/src/registrar/config/urls.py @@ -24,6 +24,7 @@ application_urls = [ for step, view in [ # add/remove steps here (Step.ORGANIZATION_TYPE, views.OrganizationType), + (Step.TRIBAL_GOVERNMENT, views.TribalGovernment), (Step.ORGANIZATION_FEDERAL, views.OrganizationFederal), (Step.ORGANIZATION_ELECTION, views.OrganizationElection), (Step.ORGANIZATION_CONTACT, views.OrganizationContact), diff --git a/src/registrar/forms/application_wizard.py b/src/registrar/forms/application_wizard.py index 32143cd66..1b7f02e2d 100644 --- a/src/registrar/forms/application_wizard.py +++ b/src/registrar/forms/application_wizard.py @@ -66,6 +66,30 @@ class OrganizationTypeForm(RegistrarForm): ) +class TribalGovernmentForm(RegistrarForm): + federally_recognized_tribe = forms.BooleanField( + label="Our organization is a federally-recognized tribe. ", + required=False, + ) + + state_recognized_tribe = forms.BooleanField( + label="Our organization is a state-recognized tribe ", + required=False, + ) + + tribe_name = forms.CharField( + label="Enter the tribe that you represent", + label_suffix=REQUIRED_SUFFIX, + error_messages={"required": "Enter the tribe you represent."}, + ) + + def clean(self): + """Needs to be either state or federally recognized.""" + if not (self.cleaned_data["federally_recognized_tribe"] or + self.cleaned_data["state_recognized_tribe"]): + raise forms.ValidationError("Only tribes recognized by the U.S. federal government or by a U.S. state government are eligible for .gov domains.", code="invalid") + + class OrganizationFederalForm(RegistrarForm): federal_type = forms.ChoiceField( choices=DomainApplication.BranchChoices.choices, diff --git a/src/registrar/migrations/0008_domainapplication_federally_recognized_tribe_and_more.py b/src/registrar/migrations/0008_domainapplication_federally_recognized_tribe_and_more.py new file mode 100644 index 000000000..6fd059128 --- /dev/null +++ b/src/registrar/migrations/0008_domainapplication_federally_recognized_tribe_and_more.py @@ -0,0 +1,32 @@ +# Generated by Django 4.1.5 on 2023-01-18 21:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0007_domainapplication_more_organization_information_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="domainapplication", + name="federally_recognized_tribe", + field=models.BooleanField( + help_text="Is the tribe federally recognized", null=True + ), + ), + migrations.AddField( + model_name="domainapplication", + name="state_recognized_tribe", + field=models.BooleanField( + help_text="Is the tribe recognized by a state", null=True + ), + ), + migrations.AddField( + model_name="domainapplication", + name="tribe_name", + field=models.TextField(blank=True, help_text="Name of tribe", null=True), + ), + ] diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 6218a1ecc..7db923a92 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -289,6 +289,22 @@ class DomainApplication(TimeStampedModel): help_text="Type of Organization", ) + federally_recognized_tribe = models.BooleanField( + null=True, + help_text="Is the tribe federally recognized", + ) + + state_recognized_tribe = models.BooleanField( + null=True, + help_text="Is the tribe recognized by a state", + ) + + tribe_name = models.TextField( + null=True, + blank=True, + help_text="Name of tribe", + ) + federal_agency = models.TextField( null=True, blank=True, @@ -474,6 +490,11 @@ class DomainApplication(TimeStampedModel): user_choice = self.organization_type return user_choice == DomainApplication.OrganizationChoices.FEDERAL + def show_tribal_government(self) -> bool: + """Show this step if the answer to the first question was "tribal".""" + user_choice = self.organization_type + return user_choice == DomainApplication.OrganizationChoices.TRIBAL + def show_organization_election(self) -> bool: """Show this step if the answer to the first question implies it. diff --git a/src/registrar/templates/application_review.html b/src/registrar/templates/application_review.html index 2c8bf874c..3cda0cbd1 100644 --- a/src/registrar/templates/application_review.html +++ b/src/registrar/templates/application_review.html @@ -17,6 +17,11 @@ {% if step == Step.ORGANIZATION_TYPE %} {{ application.get_organization_type_display|default:"Incomplete" }} {% endif %} + {% if step == Step.TRIBAL_GOVERNMENT %} + {{ application.tribe_name|default:"Incomplete" }} + {% if application.federally_recognized_tribe %}

Federally-recognized tribe

{% endif %} + {% if application.state_recognized_tribe %}

State-recognized tribe

{% endif %} + {% endif %} {% if step == Step.ORGANIZATION_FEDERAL %} {{ application.get_federal_type_display|default:"Incomplete" }} {% endif %} diff --git a/src/registrar/templates/application_tribal_government.html b/src/registrar/templates/application_tribal_government.html new file mode 100644 index 000000000..972baba7a --- /dev/null +++ b/src/registrar/templates/application_tribal_government.html @@ -0,0 +1,27 @@ + +{% extends 'application_form.html' %} + +{% block form_content %} +{% load widget_tweaks dynamic_question_tags field_helpers %} + +
+ {% input_with_errors forms.0.tribe_name %} + +

Please check all that apply.

+
+ {% csrf_token %} + +
+ {{ forms.0.federally_recognized_tribe|add_class:"usa-checkbox__input"|attr:"required"}} + {{ forms.0.federally_recognized_tribe|add_label_class:"usa-checkbox__label" }} +
+
+ {{ forms.0.state_recognized_tribe|add_class:"usa-checkbox__input"|attr:"required"}} + {{ forms.0.state_recognized_tribe|add_label_class:"usa-checkbox__label" }} +
+
+ + {{ block.super }} + +
+{% endblock %} diff --git a/src/registrar/tests/test_views.py b/src/registrar/tests/test_views.py index 378341295..98f637828 100644 --- a/src/registrar/tests/test_views.py +++ b/src/registrar/tests/test_views.py @@ -120,7 +120,7 @@ class DomainApplicationTests(TestWithUser, WebTest): this test work. """ num_pages_tested = 0 - SKIPPED_PAGES = 2 # elections, type_of_work + SKIPPED_PAGES = 3 # elections, type_of_work, tribal_government num_pages = len(self.TITLES) - SKIPPED_PAGES type_page = self.app.get(reverse("application:")).follow() @@ -740,6 +740,23 @@ class DomainApplicationTests(TestWithUser, WebTest): self.assertContains(contact_page, self.TITLES[Step.TYPE_OF_WORK]) + def test_application_tribal_government(self): + """Tribal organizations have to answer an additional question.""" + type_form = type_page.form + type_form[ + "organization_type-organization_type" + ] = DomainApplication.OrganizationChoices.TRIBAL + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + type_result = type_page.form.submit() + # the tribal government page comes immediately afterwards + self.assertIn("/tribal_government", type_result.headers["Location"]) + # follow first redirect + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + tribal_government_page = type_result.follow() + + # and the step is on the sidebar list. + self.assertContains(tribal_government_page, self.TITLES[Step.TRIBAL_GOVERNMENT]) + def test_application_ao_dynamic_text(self): type_page = self.app.get(reverse("application:")).follow() # django-webtest does not handle cookie-based sessions well because it keeps diff --git a/src/registrar/views/application.py b/src/registrar/views/application.py index a53fb51cf..02e1d2923 100644 --- a/src/registrar/views/application.py +++ b/src/registrar/views/application.py @@ -24,6 +24,7 @@ class Step(StrEnum): """ ORGANIZATION_TYPE = "organization_type" + TRIBAL_GOVERNMENT = "tribal_government" ORGANIZATION_FEDERAL = "organization_federal" ORGANIZATION_ELECTION = "organization_election" ORGANIZATION_CONTACT = "organization_contact" @@ -68,6 +69,7 @@ class ApplicationWizard(LoginRequiredMixin, TemplateView): # We need to pass our human-readable step titles as context to the templates. TITLES = { Step.ORGANIZATION_TYPE: _("Type of organization"), + Step.TRIBAL_GOVERNMENT: _("Tribal government"), Step.ORGANIZATION_FEDERAL: _("Type of organization — Federal"), Step.ORGANIZATION_ELECTION: _("Type of organization — Election board"), Step.ORGANIZATION_CONTACT: _("Organization name and mailing address"), @@ -92,6 +94,9 @@ class ApplicationWizard(LoginRequiredMixin, TemplateView): Step.ORGANIZATION_FEDERAL: lambda w: w.from_model( "show_organization_federal", False ), + Step.TRIBAL_GOVERNMENT: lambda w: w.from_model( + "show_tribal_government", False + ), Step.ORGANIZATION_ELECTION: lambda w: w.from_model( "show_organization_election", False ), @@ -336,6 +341,11 @@ class OrganizationType(ApplicationWizard): forms = [forms.OrganizationTypeForm] +class TribalGovernment(ApplicationWizard): + template_name = "application_tribal_government.html" + forms = [forms.TribalGovernmentForm] + + class OrganizationFederal(ApplicationWizard): template_name = "application_org_federal.html" forms = [forms.OrganizationFederalForm]