mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-29 17:00:02 +02:00
Save button for domain application
This commit is contained in:
parent
19c360f3bf
commit
07eb374d25
21 changed files with 979 additions and 312 deletions
|
@ -1,7 +1,12 @@
|
|||
[mypy]
|
||||
plugins =
|
||||
mypy_django_plugin.main
|
||||
# strict_optional: treat None as compatible with every type?
|
||||
# `var: int` is equal to `var: int|None`
|
||||
strict_optional = True
|
||||
# implicit_optional: treat arguments a None default value as implicitly Optional?
|
||||
# `var: int = None` is equal to `var: Optional[int] = None`
|
||||
implicit_optional = True
|
||||
|
||||
[mypy.plugins.django-stubs]
|
||||
django_settings_module = "registrar.config.settings"
|
||||
|
|
|
@ -24,6 +24,7 @@ urlpatterns = [
|
|||
path("", index.index, name="home"),
|
||||
path("whoami/", whoami.whoami, name="whoami"),
|
||||
path("admin/", admin.site.urls),
|
||||
path("application/<id>/edit/", application_wizard, name="edit-application"),
|
||||
path("health/", health.health),
|
||||
path("edit_profile/", profile.edit_profile, name="edit-profile"),
|
||||
path("openid/", include("djangooidc.urls")),
|
||||
|
|
|
@ -6,52 +6,46 @@ import logging
|
|||
|
||||
from django import forms
|
||||
from django.shortcuts import render
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.urls import resolve
|
||||
|
||||
from formtools.wizard.views import NamedUrlSessionWizardView # type: ignore
|
||||
|
||||
from registrar.models import DomainApplication, Domain
|
||||
from registrar.models import Contact, DomainApplication, Domain
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Subclass used to remove the default colon suffix from all fields
|
||||
class RegistrarForm(forms.Form):
|
||||
"""Subclass used to remove the default colon suffix from all fields."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault("label_suffix", "")
|
||||
super(RegistrarForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def to_database(self, obj: DomainApplication | Contact):
|
||||
"""
|
||||
Adds this form's cleaned data to `obj` and saves `obj`.
|
||||
|
||||
Does nothing if form is not valid.
|
||||
"""
|
||||
if not self.is_valid():
|
||||
return
|
||||
for name, value in self.cleaned_data.items():
|
||||
setattr(obj, name, value)
|
||||
obj.save()
|
||||
|
||||
def from_database(self, obj: DomainApplication | Contact):
|
||||
"""Initializes this form's fields with values gotten from `obj`."""
|
||||
for name in self.declared_fields.keys():
|
||||
self.initial[name] = getattr(obj, name) # type: ignore
|
||||
|
||||
|
||||
class OrganizationTypeForm(RegistrarForm):
|
||||
organization_type = forms.ChoiceField(
|
||||
required=True,
|
||||
choices=[
|
||||
("Federal", "Federal: a federal agency"),
|
||||
("Interstate", "Interstate: an organization of two or more states"),
|
||||
(
|
||||
"State_or_Territory",
|
||||
(
|
||||
"State or Territory: One of the 50 U.S. states, the District of "
|
||||
"Columbia, American Samoa, Guam, Northern Mariana Islands, "
|
||||
"Puerto Rico, or the U.S. Virgin Islands"
|
||||
),
|
||||
),
|
||||
(
|
||||
"Tribal",
|
||||
(
|
||||
"Tribal: a tribal government recognized by the federal or "
|
||||
"state government"
|
||||
),
|
||||
),
|
||||
("County", "County: a county, parish, or borough"),
|
||||
("City", "City: a city, town, township, village, etc."),
|
||||
(
|
||||
"Special_District",
|
||||
"Special District: an independent organization within a single state",
|
||||
),
|
||||
],
|
||||
choices=DomainApplication.OrganizationChoices.choices,
|
||||
widget=forms.RadioSelect,
|
||||
)
|
||||
|
||||
|
@ -59,20 +53,20 @@ class OrganizationTypeForm(RegistrarForm):
|
|||
class OrganizationFederalForm(RegistrarForm):
|
||||
federal_type = forms.ChoiceField(
|
||||
required=False,
|
||||
choices=DomainApplication.BRANCH_CHOICES,
|
||||
choices=DomainApplication.BranchChoices.choices,
|
||||
widget=forms.RadioSelect,
|
||||
)
|
||||
|
||||
|
||||
class OrganizationElectionForm(RegistrarForm):
|
||||
is_election_board = forms.BooleanField(
|
||||
required=False,
|
||||
widget=forms.RadioSelect(
|
||||
choices=[
|
||||
(True, "Yes"),
|
||||
(False, "No"),
|
||||
],
|
||||
),
|
||||
required=False,
|
||||
)
|
||||
|
||||
|
||||
|
@ -83,71 +77,32 @@ class OrganizationContactForm(RegistrarForm):
|
|||
required=False,
|
||||
label="Address line 2",
|
||||
)
|
||||
us_state = forms.ChoiceField(
|
||||
label="State",
|
||||
choices=[
|
||||
("AL", "Alabama"),
|
||||
("AK", "Alaska"),
|
||||
("AZ", "Arizona"),
|
||||
("AR", "Arkansas"),
|
||||
("CA", "California"),
|
||||
("CO", "Colorado"),
|
||||
("CT", "Connecticut"),
|
||||
("DE", "Delaware"),
|
||||
("DC", "District of Columbia"),
|
||||
("FL", "Florida"),
|
||||
("GA", "Georgia"),
|
||||
("HI", "Hawaii"),
|
||||
("ID", "Idaho"),
|
||||
("IL", "Illinois"),
|
||||
("IN", "Indiana"),
|
||||
("IA", "Iowa"),
|
||||
("KS", "Kansas"),
|
||||
("KY", "Kentucky"),
|
||||
("LA", "Louisiana"),
|
||||
("ME", "Maine"),
|
||||
("MD", "Maryland"),
|
||||
("MA", "Massachusetts"),
|
||||
("MI", "Michigan"),
|
||||
("MN", "Minnesota"),
|
||||
("MS", "Mississippi"),
|
||||
("MO", "Missouri"),
|
||||
("MT", "Montana"),
|
||||
("NE", "Nebraska"),
|
||||
("NV", "Nevada"),
|
||||
("NH", "New Hampshire"),
|
||||
("NJ", "New Jersey"),
|
||||
("NM", "New Mexico"),
|
||||
("NY", "New York"),
|
||||
("NC", "North Carolina"),
|
||||
("ND", "North Dakota"),
|
||||
("OH", "Ohio"),
|
||||
("OK", "Oklahoma"),
|
||||
("OR", "Oregon"),
|
||||
("PA", "Pennsylvania"),
|
||||
("RI", "Rhode Island"),
|
||||
("SC", "South Carolina"),
|
||||
("SD", "South Dakota"),
|
||||
("TN", "Tennessee"),
|
||||
("TX", "Texas"),
|
||||
("UT", "Utah"),
|
||||
("VT", "Vermont"),
|
||||
("VA", "Virginia"),
|
||||
("WA", "Washington"),
|
||||
("WV", "West Virginia"),
|
||||
("WI", "Wisconsin"),
|
||||
("WY", "Wyoming"),
|
||||
("AS", "American Samoa"),
|
||||
("GU", "Guam"),
|
||||
("MP", "Northern Mariana Islands"),
|
||||
("PR", "Puerto Rico"),
|
||||
("VI", "Virgin Islands"),
|
||||
],
|
||||
state_territory = forms.ChoiceField(
|
||||
label="State", choices=DomainApplication.StateTerritoryChoices.choices
|
||||
)
|
||||
zipcode = forms.CharField(label="ZIP code")
|
||||
|
||||
|
||||
class AuthorizingOfficialForm(RegistrarForm):
|
||||
def to_database(self, obj):
|
||||
"""Adds this form's cleaned data to `obj` and saves `obj`."""
|
||||
if not self.is_valid():
|
||||
return
|
||||
contact = getattr(obj, "authorizing_official", None)
|
||||
if contact is not None:
|
||||
super().to_database(contact)
|
||||
else:
|
||||
contact = Contact()
|
||||
super().to_database(contact)
|
||||
obj.authorizing_official = contact
|
||||
obj.save()
|
||||
|
||||
def from_database(self, obj):
|
||||
"""Initializes this form's fields with values gotten from `obj`."""
|
||||
contact = getattr(obj, "authorizing_official", None)
|
||||
if contact is not None:
|
||||
super().from_database(contact)
|
||||
|
||||
first_name = forms.CharField(label="First name/given name")
|
||||
middle_name = forms.CharField(
|
||||
required=False,
|
||||
|
@ -160,6 +115,21 @@ class AuthorizingOfficialForm(RegistrarForm):
|
|||
|
||||
|
||||
class CurrentSitesForm(RegistrarForm):
|
||||
def to_database(self, obj):
|
||||
"""Adds this form's cleaned data to `obj` and saves `obj`."""
|
||||
if not self.is_valid():
|
||||
return
|
||||
obj.save()
|
||||
normalized = Domain.normalize(self.cleaned_data["current_site"])
|
||||
# TODO: ability to update existing records
|
||||
obj.current_websites.create(website=normalized)
|
||||
|
||||
def from_database(self, obj):
|
||||
"""Initializes this form's fields with values gotten from `obj`."""
|
||||
current_website = obj.current_websites.first()
|
||||
if current_website is not None:
|
||||
self.initial["current_site"] = current_website.website
|
||||
|
||||
current_site = forms.CharField(
|
||||
required=False,
|
||||
label="Enter your organization’s public website, if you have one. For example, "
|
||||
|
@ -168,7 +138,36 @@ class CurrentSitesForm(RegistrarForm):
|
|||
|
||||
|
||||
class DotGovDomainForm(RegistrarForm):
|
||||
dotgov_domain = forms.CharField(label="What .gov domain do you want?")
|
||||
def to_database(self, obj):
|
||||
"""Adds this form's cleaned data to `obj` and saves `obj`."""
|
||||
if not self.is_valid():
|
||||
return
|
||||
normalized = Domain.normalize(self.cleaned_data["requested_domain"], "gov")
|
||||
requested_domain = getattr(obj, "requested_domain", None)
|
||||
if requested_domain is not None:
|
||||
requested_domain.name = normalized
|
||||
requested_domain.save()
|
||||
else:
|
||||
requested_domain = Domain.objects.create(name=normalized)
|
||||
obj.requested_domain = requested_domain
|
||||
obj.save()
|
||||
|
||||
obj.save()
|
||||
normalized = Domain.normalize(self.cleaned_data["alternative_domain"], "gov")
|
||||
# TODO: ability to update existing records
|
||||
obj.alternative_domains.create(website=normalized)
|
||||
|
||||
def from_database(self, obj):
|
||||
"""Initializes this form's fields with values gotten from `obj`."""
|
||||
requested_domain = getattr(obj, "requested_domain", None)
|
||||
if requested_domain is not None:
|
||||
self.initial["requested_domain"] = requested_domain.sld
|
||||
|
||||
alternative_domain = obj.alternative_domains.first()
|
||||
if alternative_domain is not None:
|
||||
self.initial["alternative_domain"] = alternative_domain.sld
|
||||
|
||||
requested_domain = forms.CharField(label="What .gov domain do you want?")
|
||||
alternative_domain = forms.CharField(
|
||||
required=False,
|
||||
label="Are there other domains you’d like if we can’t give you your first "
|
||||
|
@ -177,10 +176,29 @@ class DotGovDomainForm(RegistrarForm):
|
|||
|
||||
|
||||
class PurposeForm(RegistrarForm):
|
||||
purpose_field = forms.CharField(label="Purpose", widget=forms.Textarea())
|
||||
purpose = forms.CharField(label="Purpose", widget=forms.Textarea())
|
||||
|
||||
|
||||
class YourContactForm(RegistrarForm):
|
||||
def to_database(self, obj):
|
||||
"""Adds this form's cleaned data to `obj` and saves `obj`."""
|
||||
if not self.is_valid():
|
||||
return
|
||||
contact = getattr(obj, "submitter", None)
|
||||
if contact is not None:
|
||||
super().to_database(contact)
|
||||
else:
|
||||
contact = Contact()
|
||||
super().to_database(contact)
|
||||
obj.submitter = contact
|
||||
obj.save()
|
||||
|
||||
def from_database(self, obj):
|
||||
"""Initializes this form's fields with values gotten from `obj`."""
|
||||
contact = getattr(obj, "submitter", None)
|
||||
if contact is not None:
|
||||
super().from_database(contact)
|
||||
|
||||
first_name = forms.CharField(label="First name/given name")
|
||||
middle_name = forms.CharField(
|
||||
required=False,
|
||||
|
@ -193,6 +211,27 @@ class YourContactForm(RegistrarForm):
|
|||
|
||||
|
||||
class OtherContactsForm(RegistrarForm):
|
||||
def to_database(self, obj):
|
||||
"""Adds this form's cleaned data to `obj` and saves `obj`."""
|
||||
if not self.is_valid():
|
||||
return
|
||||
obj.save()
|
||||
|
||||
# TODO: ability to handle multiple contacts
|
||||
contact = obj.other_contacts.filter(email=self.cleaned_data["email"]).first()
|
||||
if contact is not None:
|
||||
super().to_database(contact)
|
||||
else:
|
||||
contact = Contact()
|
||||
super().to_database(contact)
|
||||
obj.other_contacts.add(contact)
|
||||
|
||||
def from_database(self, obj):
|
||||
"""Initializes this form's fields with values gotten from `obj`."""
|
||||
other_contacts = obj.other_contacts.first()
|
||||
if other_contacts is not None:
|
||||
super().from_database(other_contacts)
|
||||
|
||||
first_name = forms.CharField(label="First name/given name")
|
||||
middle_name = forms.CharField(
|
||||
required=False,
|
||||
|
@ -205,7 +244,7 @@ class OtherContactsForm(RegistrarForm):
|
|||
|
||||
|
||||
class SecurityEmailForm(RegistrarForm):
|
||||
email = forms.EmailField(
|
||||
security_email = forms.EmailField(
|
||||
required=False,
|
||||
label="Security email",
|
||||
)
|
||||
|
@ -218,72 +257,97 @@ class AnythingElseForm(RegistrarForm):
|
|||
|
||||
|
||||
class RequirementsForm(RegistrarForm):
|
||||
agree_check = forms.BooleanField(
|
||||
is_policy_acknowledged = forms.BooleanField(
|
||||
label="I read and agree to the .gov domain requirements."
|
||||
)
|
||||
|
||||
|
||||
# Empty class for the review page which gets included as part of the form, but does not
|
||||
# have any form fields itself
|
||||
class ReviewForm(RegistrarForm):
|
||||
"""
|
||||
Empty class for the review page.
|
||||
|
||||
It gets included as part of the form, but does not have any form fields itself.
|
||||
"""
|
||||
|
||||
def to_database(self, _):
|
||||
"""This form has no data. Do nothing."""
|
||||
pass
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# List of forms in our wizard. Each entry is a tuple of a name and a form
|
||||
# subclass
|
||||
class Step:
|
||||
"""Names for each page of the application wizard."""
|
||||
|
||||
ORGANIZATION_TYPE = "organization_type"
|
||||
ORGANIZATION_FEDERAL = "organization_federal"
|
||||
ORGANIZATION_ELECTION = "organization_election"
|
||||
ORGANIZATION_CONTACT = "organization_contact"
|
||||
AUTHORIZING_OFFICIAL = "authorizing_official"
|
||||
CURRENT_SITES = "current_sites"
|
||||
DOTGOV_DOMAIN = "dotgov_domain"
|
||||
PURPOSE = "purpose"
|
||||
YOUR_CONTACT = "your_contact"
|
||||
OTHER_CONTACTS = "other_contacts"
|
||||
SECURITY_EMAIL = "security_email"
|
||||
ANYTHING_ELSE = "anything_else"
|
||||
REQUIREMENTS = "requirements"
|
||||
REVIEW = "review"
|
||||
|
||||
|
||||
# List of forms in our wizard.
|
||||
# Each entry is a tuple of a name and a form subclass
|
||||
FORMS = [
|
||||
("organization_type", OrganizationTypeForm),
|
||||
("organization_federal", OrganizationFederalForm),
|
||||
("organization_election", OrganizationElectionForm),
|
||||
("organization_contact", OrganizationContactForm),
|
||||
("authorizing_official", AuthorizingOfficialForm),
|
||||
("current_sites", CurrentSitesForm),
|
||||
("dotgov_domain", DotGovDomainForm),
|
||||
("purpose", PurposeForm),
|
||||
("your_contact", YourContactForm),
|
||||
("other_contacts", OtherContactsForm),
|
||||
("security_email", SecurityEmailForm),
|
||||
("anything_else", AnythingElseForm),
|
||||
("requirements", RequirementsForm),
|
||||
("review", ReviewForm),
|
||||
(Step.ORGANIZATION_TYPE, OrganizationTypeForm),
|
||||
(Step.ORGANIZATION_FEDERAL, OrganizationFederalForm),
|
||||
(Step.ORGANIZATION_ELECTION, OrganizationElectionForm),
|
||||
(Step.ORGANIZATION_CONTACT, OrganizationContactForm),
|
||||
(Step.AUTHORIZING_OFFICIAL, AuthorizingOfficialForm),
|
||||
(Step.CURRENT_SITES, CurrentSitesForm),
|
||||
(Step.DOTGOV_DOMAIN, DotGovDomainForm),
|
||||
(Step.PURPOSE, PurposeForm),
|
||||
(Step.YOUR_CONTACT, YourContactForm),
|
||||
(Step.OTHER_CONTACTS, OtherContactsForm),
|
||||
(Step.SECURITY_EMAIL, SecurityEmailForm),
|
||||
(Step.ANYTHING_ELSE, AnythingElseForm),
|
||||
(Step.REQUIREMENTS, RequirementsForm),
|
||||
(Step.REVIEW, ReviewForm),
|
||||
]
|
||||
|
||||
# Dict to match up the right template with the right step. Keys here must
|
||||
# match the first elements of the tuples in FORMS
|
||||
# Dict to match up the right template with the right step.
|
||||
TEMPLATES = {
|
||||
"organization_type": "application_org_type.html",
|
||||
"organization_federal": "application_org_federal.html",
|
||||
"organization_election": "application_org_election.html",
|
||||
"organization_contact": "application_org_contact.html",
|
||||
"authorizing_official": "application_authorizing_official.html",
|
||||
"current_sites": "application_current_sites.html",
|
||||
"dotgov_domain": "application_dotgov_domain.html",
|
||||
"purpose": "application_purpose.html",
|
||||
"your_contact": "application_your_contact.html",
|
||||
"other_contacts": "application_other_contacts.html",
|
||||
"security_email": "application_security_email.html",
|
||||
"anything_else": "application_anything_else.html",
|
||||
"requirements": "application_requirements.html",
|
||||
"review": "application_review.html",
|
||||
Step.ORGANIZATION_TYPE: "application_org_type.html",
|
||||
Step.ORGANIZATION_FEDERAL: "application_org_federal.html",
|
||||
Step.ORGANIZATION_ELECTION: "application_org_election.html",
|
||||
Step.ORGANIZATION_CONTACT: "application_org_contact.html",
|
||||
Step.AUTHORIZING_OFFICIAL: "application_authorizing_official.html",
|
||||
Step.CURRENT_SITES: "application_current_sites.html",
|
||||
Step.DOTGOV_DOMAIN: "application_dotgov_domain.html",
|
||||
Step.PURPOSE: "application_purpose.html",
|
||||
Step.YOUR_CONTACT: "application_your_contact.html",
|
||||
Step.OTHER_CONTACTS: "application_other_contacts.html",
|
||||
Step.SECURITY_EMAIL: "application_security_email.html",
|
||||
Step.ANYTHING_ELSE: "application_anything_else.html",
|
||||
Step.REQUIREMENTS: "application_requirements.html",
|
||||
Step.REVIEW: "application_review.html",
|
||||
}
|
||||
|
||||
# We need to pass our page titles as context to the templates, indexed
|
||||
# by the step names
|
||||
# We need to pass our page titles as context to the templates
|
||||
TITLES = {
|
||||
"organization_type": "Type of organization",
|
||||
"organization_federal": "Type of organization — Federal",
|
||||
"organization_election": "Type of organization — Election board",
|
||||
"organization_contact": "Organization name and mailing address",
|
||||
"authorizing_official": "Authorizing official",
|
||||
"current_sites": "Organization website",
|
||||
"dotgov_domain": ".gov domain",
|
||||
"purpose": "Purpose of your domain",
|
||||
"your_contact": "Your contact information",
|
||||
"other_contacts": "Other contacts for your domain",
|
||||
"security_email": "Security email for public use",
|
||||
"anything_else": "Anything else we should know?",
|
||||
"requirements": "Requirements for registration and operation of .gov domains",
|
||||
"review": "Review and submit your domain request",
|
||||
Step.ORGANIZATION_TYPE: "Type of organization",
|
||||
Step.ORGANIZATION_FEDERAL: "Type of organization — Federal",
|
||||
Step.ORGANIZATION_ELECTION: "Type of organization — Election board",
|
||||
Step.ORGANIZATION_CONTACT: "Organization name and mailing address",
|
||||
Step.AUTHORIZING_OFFICIAL: "Authorizing official",
|
||||
Step.CURRENT_SITES: "Organization website",
|
||||
Step.DOTGOV_DOMAIN: ".gov domain",
|
||||
Step.PURPOSE: "Purpose of your domain",
|
||||
Step.YOUR_CONTACT: "Your contact information",
|
||||
Step.OTHER_CONTACTS: "Other contacts for your domain",
|
||||
Step.SECURITY_EMAIL: "Security email for public use",
|
||||
Step.ANYTHING_ELSE: "Anything else we should know?",
|
||||
Step.REQUIREMENTS: "Requirements for registration and operation of .gov domains",
|
||||
Step.REVIEW: "Review and submit your domain request",
|
||||
}
|
||||
|
||||
|
||||
|
@ -303,6 +367,10 @@ class ApplicationWizard(LoginRequiredMixin, NamedUrlSessionWizardView):
|
|||
domain applications. Each form in the sequence has its own URL and
|
||||
the progress through the form is stored in the Django session (thus
|
||||
"NamedUrlSessionWizardView").
|
||||
|
||||
Caution: due to the redirect performed by using NamedUrlSessionWizardView,
|
||||
many methods, such as `process_step`, are called TWICE per request. For
|
||||
this reason, methods in this class need to be idempotent.
|
||||
"""
|
||||
|
||||
form_list = FORMS
|
||||
|
@ -320,42 +388,97 @@ class ApplicationWizard(LoginRequiredMixin, NamedUrlSessionWizardView):
|
|||
context["form_titles"] = TITLES
|
||||
return context
|
||||
|
||||
def forms_to_object(self, form_dict: dict) -> DomainApplication:
|
||||
"""Unpack the form responses onto the model object properties."""
|
||||
def get_application_object(self) -> DomainApplication:
|
||||
"""
|
||||
Attempt to match the current wizard with a DomainApplication.
|
||||
|
||||
Will create an application if none exists.
|
||||
"""
|
||||
if "application_id" in self.storage.extra_data:
|
||||
id = self.storage.extra_data["application_id"]
|
||||
try:
|
||||
return DomainApplication.objects.get(
|
||||
creator=self.request.user,
|
||||
pk=id,
|
||||
)
|
||||
except DomainApplication.DoesNotExist:
|
||||
logger.debug("Application id %s did not have a DomainApplication" % id)
|
||||
|
||||
application = DomainApplication.objects.create(creator=self.request.user)
|
||||
|
||||
# organization type information
|
||||
organization_type_data = form_dict["organization_type"].cleaned_data
|
||||
application.organization_type = organization_type_data["organization_type"]
|
||||
|
||||
# federal branch information may not exist
|
||||
federal_branch_data = form_dict.get("organization_federal")
|
||||
if federal_branch_data is not None:
|
||||
federal_branch_data = federal_branch_data.cleaned_data
|
||||
application.federal_branch = federal_branch_data["federal_type"]
|
||||
|
||||
# election board information may not exist.
|
||||
election_board_data = form_dict.get("organization_election")
|
||||
if election_board_data is not None:
|
||||
election_board_data = election_board_data.cleaned_data
|
||||
application.is_election_office = election_board_data["is_election_board"]
|
||||
|
||||
# contact information
|
||||
contact_data = form_dict["organization_contact"].cleaned_data
|
||||
application.organization_name = contact_data["organization_name"]
|
||||
application.street_address = contact_data["address_line1"]
|
||||
# TODO: add the rest of these fields when they are created in the forms
|
||||
|
||||
# This isn't really the requested_domain field
|
||||
# but we need something in this field to make the form submittable
|
||||
requested_site, _ = Domain.objects.get_or_create(
|
||||
name=contact_data["organization_name"] + ".gov"
|
||||
)
|
||||
application.requested_domain = requested_site
|
||||
self.storage.extra_data["application_id"] = application.id
|
||||
return application
|
||||
|
||||
def forms_to_database(
|
||||
self, forms: dict = None, form: RegistrarForm = None
|
||||
) -> DomainApplication:
|
||||
"""
|
||||
Unpack the form responses onto the model object properties.
|
||||
|
||||
Saves the application to the database.
|
||||
"""
|
||||
application = self.get_application_object()
|
||||
|
||||
if forms:
|
||||
itr = forms
|
||||
elif form:
|
||||
itr = {"form": form}
|
||||
else:
|
||||
raise TypeError("forms and form cannot both be None")
|
||||
|
||||
for form in itr.values():
|
||||
if form is not None and hasattr(form, "to_database"):
|
||||
form.to_database(application)
|
||||
|
||||
return application
|
||||
|
||||
def process_step(self, form):
|
||||
"""
|
||||
Hook called on every POST request, if the form is valid.
|
||||
|
||||
Do not manipulate the form data here.
|
||||
"""
|
||||
# save progress
|
||||
self.forms_to_database(form=form)
|
||||
return self.get_form_step_data(form)
|
||||
|
||||
def get_form(self, step=None, data=None, files=None):
|
||||
"""This method constructs the form for a given step."""
|
||||
form = super().get_form(step, data, files)
|
||||
|
||||
# restore from database, but only if a record has already
|
||||
# been associated with this wizard instance
|
||||
if "application_id" in self.storage.extra_data:
|
||||
application = self.get_application_object()
|
||||
form.from_database(application)
|
||||
return form
|
||||
|
||||
def post(self, *args, **kwargs):
|
||||
"""This method handles POST requests."""
|
||||
step = self.steps.current
|
||||
# always call super() first, to do important pre-processing
|
||||
rendered = super().post(*args, **kwargs)
|
||||
# if user opted to save their progress,
|
||||
# return them to the page they were already on
|
||||
button = self.request.POST.get("submit_button", None)
|
||||
if button == "save":
|
||||
return self.render_goto_step(step)
|
||||
# otherwise, proceed as normal
|
||||
return rendered
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
"""This method handles GET requests."""
|
||||
current_url = resolve(self.request.path_info).url_name
|
||||
# always call super(), it handles important redirect logic
|
||||
rendered = super().get(*args, **kwargs)
|
||||
# if user visited via an "edit" url, associate the id of the
|
||||
# application they are trying to edit to this wizard instance
|
||||
if current_url == "edit-application" and "id" in kwargs:
|
||||
self.storage.extra_data["application_id"] = kwargs["id"]
|
||||
return rendered
|
||||
|
||||
def done(self, form_list, form_dict, **kwargs):
|
||||
application = self.forms_to_object(form_dict)
|
||||
"""Called when the data for every form is submitted and validated."""
|
||||
application = self.forms_to_database(forms=form_dict)
|
||||
application.submit() # change the status to submitted
|
||||
application.save()
|
||||
logger.debug("Application object saved: %s", application.id)
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
# Generated by Django 4.1.3 on 2022-12-02 21:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("registrar", "0002_domain_host_nameserver_hostip_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name="domainapplication",
|
||||
old_name="is_election_office",
|
||||
new_name="is_election_board",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="domainapplication",
|
||||
old_name="acknowledged_policy",
|
||||
new_name="is_policy_acknowledged",
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name="domainapplication",
|
||||
old_name="zip_code",
|
||||
new_name="zipcode",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="domainapplication",
|
||||
name="federal_branch",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="domainapplication",
|
||||
name="street_address",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="domainapplication",
|
||||
name="unit_number",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="domainapplication",
|
||||
name="unit_type",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="domainapplication",
|
||||
name="address_line1",
|
||||
field=models.TextField(blank=True, help_text="Address line 1", null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="domainapplication",
|
||||
name="address_line2",
|
||||
field=models.CharField(
|
||||
blank=True, help_text="Address line 2", max_length=15, null=True
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="domainapplication",
|
||||
name="federal_type",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("executive", "Executive"),
|
||||
("judicial", "Judicial"),
|
||||
("legislative", "Legislative"),
|
||||
],
|
||||
help_text="Branch of federal government",
|
||||
max_length=50,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domainapplication",
|
||||
name="organization_type",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("federal", "Federal: a federal agency"),
|
||||
("interstate", "Interstate: an organization of two or more states"),
|
||||
(
|
||||
"state_or_territory",
|
||||
"State or Territory: One of the 50 U.S. states, the District of Columbia, American Samoa, Guam, Northern Mariana Islands, Puerto Rico, or the U.S. Virgin Islands",
|
||||
),
|
||||
(
|
||||
"tribal",
|
||||
"Tribal: a tribal government recognized by the federal or state government",
|
||||
),
|
||||
("county", "County: a county, parish, or borough"),
|
||||
("city", "City: a city, town, township, village, etc."),
|
||||
(
|
||||
"special_district",
|
||||
"Special District: an independent organization within a single state",
|
||||
),
|
||||
],
|
||||
help_text="Type of Organization",
|
||||
max_length=255,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -1,14 +1,14 @@
|
|||
import logging
|
||||
import re
|
||||
|
||||
from django.apps import apps
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django_fsm import FSMField, transition # type: ignore
|
||||
|
||||
from epp.mock_epp import domain_info, domain_check
|
||||
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
from .domain_application import DomainApplication
|
||||
from .user import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
@ -93,11 +93,58 @@ class Domain(TimeStampedModel):
|
|||
DOMAIN_REGEX = re.compile(r"^(?!-)[A-Za-z0-9-]{1,63}(?<!-)\.[A-Za-z]{2,6}")
|
||||
|
||||
@classmethod
|
||||
def string_could_be_domain(cls, domain: str) -> bool:
|
||||
def normalize(cls, domain: str, tld=None) -> str: # noqa: C901
|
||||
"""Return `domain` in form `<second level>.<tld>`, if possible.
|
||||
|
||||
This does not guarantee the returned string is a valid domain name."""
|
||||
cleaned = domain.lower()
|
||||
# starts with https or http
|
||||
if cleaned.startswith("https://"):
|
||||
cleaned = cleaned[8:]
|
||||
if cleaned.startswith("http://"):
|
||||
cleaned = cleaned[7:]
|
||||
# has url parts
|
||||
if "/" in cleaned:
|
||||
cleaned = cleaned.split("/")[0]
|
||||
# has query parts
|
||||
if "?" in cleaned:
|
||||
cleaned = cleaned.split("?")[0]
|
||||
# has fragments
|
||||
if "#" in cleaned:
|
||||
cleaned = cleaned.split("#")[0]
|
||||
# replace disallowed chars
|
||||
re.sub(r"^[^A-Za-z0-9.-]+", "", cleaned)
|
||||
|
||||
parts = cleaned.split(".")
|
||||
# has subdomains or invalid repetitions
|
||||
if cleaned.count(".") > 0:
|
||||
# remove invalid repetitions
|
||||
while parts[-1] == parts[-2]:
|
||||
parts.pop()
|
||||
# remove subdomains
|
||||
parts = parts[-2:]
|
||||
hasTLD = len(parts) == 2
|
||||
if hasTLD:
|
||||
# set correct tld
|
||||
if tld is not None:
|
||||
parts[-1] = tld
|
||||
else:
|
||||
# add tld
|
||||
if tld is not None:
|
||||
parts.append(tld)
|
||||
else:
|
||||
raise ValueError("You must specify a tld for %s" % domain)
|
||||
|
||||
cleaned = ".".join(parts)
|
||||
|
||||
return cleaned
|
||||
|
||||
@classmethod
|
||||
def string_could_be_domain(cls, domain: str | None) -> bool:
|
||||
"""Return True if the string could be a domain name, otherwise False."""
|
||||
if cls.DOMAIN_REGEX.match(domain):
|
||||
return True
|
||||
return False
|
||||
if not isinstance(domain, str):
|
||||
return False
|
||||
return bool(cls.DOMAIN_REGEX.match(domain))
|
||||
|
||||
@classmethod
|
||||
def available(cls, domain: str) -> bool:
|
||||
|
@ -137,16 +184,10 @@ class Domain(TimeStampedModel):
|
|||
# TODO: return an error if registry cannot be contacted
|
||||
return None
|
||||
|
||||
def could_be_domain(self) -> bool:
|
||||
"""Could this instance be a domain?"""
|
||||
# short-circuit if self.website is null/None
|
||||
if not self.name:
|
||||
return False
|
||||
return self.string_could_be_domain(str(self.name))
|
||||
|
||||
@transition(field="is_active", source="*", target=True)
|
||||
def activate(self):
|
||||
"""This domain should be made live."""
|
||||
DomainApplication = apps.get_model("registrar.DomainApplication")
|
||||
if hasattr(self, "domain_application"):
|
||||
if self.domain_application.status != DomainApplication.APPROVED:
|
||||
raise ValueError("Cannot activate. Application must be approved.")
|
||||
|
@ -166,6 +207,34 @@ class Domain(TimeStampedModel):
|
|||
# if there is a feature request to implement this
|
||||
raise Exception("Cannot revoke, contact registry.")
|
||||
|
||||
@property
|
||||
def sld(self):
|
||||
"""Get or set the second level domain string."""
|
||||
return self.name.split(".")[0]
|
||||
|
||||
@sld.setter
|
||||
def sld(self, value: str):
|
||||
parts = self.name.split(".")
|
||||
tld = parts[1] if len(parts) > 1 else ""
|
||||
if Domain.string_could_be_domain(f"{value}.{tld}"):
|
||||
self.name = f"{value}.{tld}"
|
||||
else:
|
||||
raise ValidationError("%s is not a valid second level domain" % value)
|
||||
|
||||
@property
|
||||
def tld(self):
|
||||
"""Get or set the top level domain string."""
|
||||
parts = self.name.split(".")
|
||||
return parts[1] if len(parts) > 1 else ""
|
||||
|
||||
@tld.setter
|
||||
def tld(self, value: str):
|
||||
sld = self.name.split(".")[0]
|
||||
if Domain.string_could_be_domain(f"{sld}.{value}"):
|
||||
self.name = f"{sld}.{value}"
|
||||
else:
|
||||
raise ValidationError("%s is not a valid top level domain" % value)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
|
@ -232,6 +301,6 @@ class Domain(TimeStampedModel):
|
|||
# TODO: determine the relationship between this field
|
||||
# and the domain application's `creator` and `submitter`
|
||||
owners = models.ManyToManyField(
|
||||
User,
|
||||
"registrar.User",
|
||||
help_text="",
|
||||
)
|
||||
|
|
|
@ -1,14 +1,11 @@
|
|||
from __future__ import annotations
|
||||
from typing import TYPE_CHECKING, Union
|
||||
|
||||
from django.apps import apps
|
||||
from django.db import models
|
||||
from django_fsm import FSMField, transition # type: ignore
|
||||
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
from .contact import Contact
|
||||
from .user import User
|
||||
from .website import Website
|
||||
|
||||
from typing import TYPE_CHECKING, Union
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..forms.application_wizard import ApplicationWizard
|
||||
|
@ -30,35 +27,86 @@ class DomainApplication(TimeStampedModel):
|
|||
(APPROVED, APPROVED),
|
||||
]
|
||||
|
||||
FEDERAL = "federal"
|
||||
INTERSTATE = "interstate"
|
||||
STATE_OR_TERRITORY = "state_or_territory"
|
||||
TRIBAL = "tribal"
|
||||
COUNTY = "county"
|
||||
CITY = "city"
|
||||
SPECIAL_DISTRICT = "special_district"
|
||||
ORGANIZATION_CHOICES = [
|
||||
(FEDERAL, "a federal agency"),
|
||||
(INTERSTATE, "an organization of two or more states"),
|
||||
(
|
||||
STATE_OR_TERRITORY,
|
||||
"one of the 50 U.S. states, the District of "
|
||||
"Columbia, American Samoa, Guam, Northern Mariana Islands, "
|
||||
"Puerto Rico, or the U.S. Virgin Islands",
|
||||
),
|
||||
(
|
||||
TRIBAL,
|
||||
"a tribal government recognized by the federal or " "state government",
|
||||
),
|
||||
(COUNTY, "a county, parish, or borough"),
|
||||
(CITY, "a city, town, township, village, etc."),
|
||||
(SPECIAL_DISTRICT, "an independent organization within a single state"),
|
||||
]
|
||||
class StateTerritoryChoices(models.TextChoices):
|
||||
ALABAMA = "AL", "Alabama"
|
||||
ALASKA = "AK", "Alaska"
|
||||
ARIZONA = "AZ", "Arizona"
|
||||
ARKANSAS = "AR", "Arkansas"
|
||||
CALIFORNIA = "CA", "California"
|
||||
COLORADO = "CO", "Colorado"
|
||||
CONNECTICUT = "CT", "Connecticut"
|
||||
DELAWARE = "DE", "Delaware"
|
||||
DISTRICT_OF_COLUMBIA = "DC", "District of Columbia"
|
||||
FLORIDA = "FL", "Florida"
|
||||
GEORGIA = "GA", "Georgia"
|
||||
HAWAII = "HI", "Hawaii"
|
||||
IDAHO = "ID", "Idaho"
|
||||
ILLINOIS = "IL", "Illinois"
|
||||
INDIANA = "IN", "Indiana"
|
||||
IOWA = "IA", "Iowa"
|
||||
KANSAS = "KS", "Kansas"
|
||||
KENTUCKY = "KY", "Kentucky"
|
||||
LOUISIANA = "LA", "Louisiana"
|
||||
MAINE = "ME", "Maine"
|
||||
MARYLAND = "MD", "Maryland"
|
||||
MASSACHUSETTS = "MA", "Massachusetts"
|
||||
MICHIGAN = "MI", "Michigan"
|
||||
MINNESOTA = "MN", "Minnesota"
|
||||
MISSISSIPPI = "MS", "Mississippi"
|
||||
MISSOURI = "MO", "Missouri"
|
||||
MONTANA = "MT", "Montana"
|
||||
NEBRASKA = "NE", "Nebraska"
|
||||
NEVADA = "NV", "Nevada"
|
||||
NEW_HAMPSHIRE = "NH", "New Hampshire"
|
||||
NEW_JERSEY = "NJ", "New Jersey"
|
||||
NEW_MEXICO = "NM", "New Mexico"
|
||||
NEW_YORK = "NY", "New York"
|
||||
NORTH_CAROLINA = "NC", "North Carolina"
|
||||
NORTH_DAKOTA = "ND", "North Dakota"
|
||||
OHIO = "OH", "Ohio"
|
||||
OKLAHOMA = "OK", "Oklahoma"
|
||||
OREGON = "OR", "Oregon"
|
||||
PENNSYLVANIA = "PA", "Pennsylvania"
|
||||
RHODE_ISLAND = "RI", "Rhode Island"
|
||||
SOUTH_CAROLINA = "SC", "South Carolina"
|
||||
SOUTH_DAKOTA = "SD", "South Dakota"
|
||||
TENNESSEE = "TN", "Tennessee"
|
||||
TEXAS = "TX", "Texas"
|
||||
UTAH = "UT", "Utah"
|
||||
VERMONT = "VT", "Vermont"
|
||||
VIRGINIA = "VA", "Virginia"
|
||||
WASHINGTON = "WA", "Washington"
|
||||
WEST_VIRGINIA = "WV", "West Virginia"
|
||||
WISCONSIN = "WI", "Wisconsin"
|
||||
WYOMING = "WY", "Wyoming"
|
||||
AMERICAN_SAMOA = "AS", "American Samoa"
|
||||
GUAM = "GU", "Guam"
|
||||
NORTHERN_MARIANA_ISLANDS = "MP", "Northern Mariana Islands"
|
||||
PUERTO_RICO = "PR", "Puerto Rico"
|
||||
VIRGIN_ISLANDS = "VI", "Virgin Islands"
|
||||
|
||||
EXECUTIVE = "Executive"
|
||||
JUDICIAL = "Judicial"
|
||||
LEGISLATIVE = "Legislative"
|
||||
BRANCH_CHOICES = [(x, x) for x in (EXECUTIVE, JUDICIAL, LEGISLATIVE)]
|
||||
class OrganizationChoices(models.TextChoices):
|
||||
FEDERAL = "federal", "Federal: a federal agency"
|
||||
INTERSTATE = "interstate", "Interstate: an organization of two or more states"
|
||||
STATE_OR_TERRITORY = "state_or_territory", (
|
||||
"State or Territory: One of the 50 U.S. states, the District of "
|
||||
"Columbia, American Samoa, Guam, Northern Mariana Islands, "
|
||||
"Puerto Rico, or the U.S. Virgin Islands"
|
||||
)
|
||||
TRIBAL = "tribal", (
|
||||
"Tribal: a tribal government recognized by the federal or "
|
||||
"state government"
|
||||
)
|
||||
COUNTY = "county", "County: a county, parish, or borough"
|
||||
CITY = "city", "City: a city, town, township, village, etc."
|
||||
SPECIAL_DISTRICT = "special_district", (
|
||||
"Special District: an independent organization within a single state"
|
||||
)
|
||||
|
||||
class BranchChoices(models.TextChoices):
|
||||
EXECUTIVE = "executive", "Executive"
|
||||
JUDICIAL = "judicial", "Judicial"
|
||||
LEGISLATIVE = "legislative", "Legislative"
|
||||
|
||||
# #### Internal fields about the application #####
|
||||
status = FSMField(
|
||||
|
@ -69,10 +117,12 @@ class DomainApplication(TimeStampedModel):
|
|||
# This is the application user who created this application. The contact
|
||||
# information that they gave is in the `submitter` field
|
||||
creator = models.ForeignKey(
|
||||
User, on_delete=models.PROTECT, related_name="applications_created"
|
||||
"registrar.User",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="applications_created",
|
||||
)
|
||||
investigator = models.ForeignKey(
|
||||
User,
|
||||
"registrar.User",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
|
@ -82,21 +132,21 @@ class DomainApplication(TimeStampedModel):
|
|||
# ##### data fields from the initial form #####
|
||||
organization_type = models.CharField(
|
||||
max_length=255,
|
||||
choices=ORGANIZATION_CHOICES,
|
||||
choices=OrganizationChoices.choices,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Type of Organization",
|
||||
)
|
||||
|
||||
federal_branch = models.CharField(
|
||||
federal_type = models.CharField(
|
||||
max_length=50,
|
||||
choices=BRANCH_CHOICES,
|
||||
choices=BranchChoices.choices,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Branch of federal government",
|
||||
)
|
||||
|
||||
is_election_office = models.BooleanField(
|
||||
is_election_board = models.BooleanField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Is your ogranization an election office?",
|
||||
|
@ -108,22 +158,16 @@ class DomainApplication(TimeStampedModel):
|
|||
help_text="Organization name",
|
||||
db_index=True,
|
||||
)
|
||||
street_address = models.TextField(
|
||||
address_line1 = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Street Address",
|
||||
help_text="Address line 1",
|
||||
)
|
||||
unit_type = models.CharField(
|
||||
address_line2 = models.CharField(
|
||||
max_length=15,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Unit type",
|
||||
)
|
||||
unit_number = models.CharField(
|
||||
max_length=255,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Unit number",
|
||||
help_text="Address line 2",
|
||||
)
|
||||
state_territory = models.CharField(
|
||||
max_length=2,
|
||||
|
@ -131,7 +175,7 @@ class DomainApplication(TimeStampedModel):
|
|||
blank=True,
|
||||
help_text="State/Territory",
|
||||
)
|
||||
zip_code = models.CharField(
|
||||
zipcode = models.CharField(
|
||||
max_length=10,
|
||||
null=True,
|
||||
blank=True,
|
||||
|
@ -140,7 +184,7 @@ class DomainApplication(TimeStampedModel):
|
|||
)
|
||||
|
||||
authorizing_official = models.ForeignKey(
|
||||
Contact,
|
||||
"registrar.Contact",
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="authorizing_official",
|
||||
|
@ -149,7 +193,7 @@ class DomainApplication(TimeStampedModel):
|
|||
|
||||
# "+" means no reverse relation to lookup applications from Website
|
||||
current_websites = models.ManyToManyField(
|
||||
Website,
|
||||
"registrar.Website",
|
||||
blank=True,
|
||||
related_name="current+",
|
||||
)
|
||||
|
@ -163,7 +207,7 @@ class DomainApplication(TimeStampedModel):
|
|||
on_delete=models.PROTECT,
|
||||
)
|
||||
alternative_domains = models.ManyToManyField(
|
||||
Website,
|
||||
"registrar.Website",
|
||||
blank=True,
|
||||
related_name="alternatives+",
|
||||
)
|
||||
|
@ -171,7 +215,7 @@ class DomainApplication(TimeStampedModel):
|
|||
# This is the contact information provided by the applicant. The
|
||||
# application user who created it is in the `creator` field.
|
||||
submitter = models.ForeignKey(
|
||||
Contact,
|
||||
"registrar.Contact",
|
||||
null=True,
|
||||
blank=True,
|
||||
related_name="submitted_applications",
|
||||
|
@ -185,7 +229,7 @@ class DomainApplication(TimeStampedModel):
|
|||
)
|
||||
|
||||
other_contacts = models.ManyToManyField(
|
||||
Contact,
|
||||
"registrar.Contact",
|
||||
blank=True,
|
||||
related_name="contact_applications",
|
||||
)
|
||||
|
@ -203,7 +247,7 @@ class DomainApplication(TimeStampedModel):
|
|||
help_text="Anything else we should know?",
|
||||
)
|
||||
|
||||
acknowledged_policy = models.BooleanField(
|
||||
is_policy_acknowledged = models.BooleanField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Acknowledged .gov acceptable use policy",
|
||||
|
@ -226,8 +270,15 @@ class DomainApplication(TimeStampedModel):
|
|||
# can raise more informative exceptions
|
||||
|
||||
# requested_domain could be None here
|
||||
if (not self.requested_domain) or (not self.requested_domain.could_be_domain()):
|
||||
raise ValueError("Requested domain is not a legal domain name.")
|
||||
if not hasattr(self, "requested_domain"):
|
||||
raise ValueError("Requested domain is missing.")
|
||||
|
||||
if self.requested_domain is None:
|
||||
raise ValueError("Requested domain is missing.")
|
||||
|
||||
Domain = apps.get_model("registrar.Domain")
|
||||
if not Domain.string_could_be_domain(self.requested_domain.name):
|
||||
raise ValueError("Requested domain is not a valid domain name.")
|
||||
|
||||
# if no exception was raised, then we don't need to do anything
|
||||
# inside this method, keep the `pass` here to remind us of that
|
||||
|
@ -253,7 +304,8 @@ class DomainApplication(TimeStampedModel):
|
|||
@staticmethod
|
||||
def show_organization_federal(wizard: ApplicationWizard) -> bool:
|
||||
"""Show this step if the answer to the first question was "federal"."""
|
||||
return DomainApplication._get_organization_type(wizard) == "Federal"
|
||||
user_choice = DomainApplication._get_organization_type(wizard)
|
||||
return user_choice == DomainApplication.OrganizationChoices.FEDERAL
|
||||
|
||||
@staticmethod
|
||||
def show_organization_election(wizard: ApplicationWizard) -> bool:
|
||||
|
@ -261,7 +313,9 @@ class DomainApplication(TimeStampedModel):
|
|||
|
||||
This shows for answers that aren't "Federal" or "Interstate".
|
||||
"""
|
||||
type_answer = DomainApplication._get_organization_type(wizard)
|
||||
if type_answer and type_answer not in ("Federal", "Interstate"):
|
||||
return True
|
||||
return False
|
||||
user_choice = DomainApplication._get_organization_type(wizard)
|
||||
excluded = [
|
||||
DomainApplication.OrganizationChoices.FEDERAL,
|
||||
DomainApplication.OrganizationChoices.INTERSTATE,
|
||||
]
|
||||
return bool(user_choice and user_choice not in excluded)
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
from django.db import models
|
||||
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
from .domain import Domain
|
||||
|
||||
|
||||
class Host(TimeStampedModel):
|
||||
|
@ -26,7 +25,7 @@ class Host(TimeStampedModel):
|
|||
)
|
||||
|
||||
domain = models.ForeignKey(
|
||||
Domain,
|
||||
"registrar.Domain",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="host", # access this Host via the Domain as `domain.host`
|
||||
help_text="Domain to which this host belongs",
|
||||
|
|
|
@ -2,7 +2,6 @@ from django.db import models
|
|||
from django.core.validators import validate_ipv46_address
|
||||
|
||||
from .utility.time_stamped_model import TimeStampedModel
|
||||
from .host import Host
|
||||
|
||||
|
||||
class HostIP(TimeStampedModel):
|
||||
|
@ -25,7 +24,7 @@ class HostIP(TimeStampedModel):
|
|||
)
|
||||
|
||||
host = models.ForeignKey(
|
||||
Host,
|
||||
"registrar.Host",
|
||||
on_delete=models.PROTECT,
|
||||
related_name="ip", # access this HostIP via the Host as `host.ip`
|
||||
help_text="Host to which this IP address belongs",
|
||||
|
|
|
@ -4,7 +4,6 @@ from .utility.time_stamped_model import TimeStampedModel
|
|||
from .utility.address_model import AddressModel
|
||||
|
||||
from .contact import Contact
|
||||
from .user import User
|
||||
|
||||
|
||||
class UserProfile(TimeStampedModel, Contact, AddressModel):
|
||||
|
@ -12,7 +11,7 @@ class UserProfile(TimeStampedModel, Contact, AddressModel):
|
|||
"""User information, unrelated to their login/auth details."""
|
||||
|
||||
user = models.OneToOneField(
|
||||
User,
|
||||
"registrar.User",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.CASCADE,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from django.apps import apps
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
|
||||
|
||||
|
@ -14,5 +16,35 @@ class Website(models.Model):
|
|||
help_text="",
|
||||
)
|
||||
|
||||
@property
|
||||
def sld(self):
|
||||
"""Get or set the second level domain string."""
|
||||
return self.website.split(".")[0]
|
||||
|
||||
@sld.setter
|
||||
def sld(self, value: str):
|
||||
Domain = apps.get_model("registrar.Domain")
|
||||
parts = self.website.split(".")
|
||||
tld = parts[1] if len(parts) > 1 else ""
|
||||
if Domain.string_could_be_domain(f"{value}.{tld}"):
|
||||
self.website = f"{value}.{tld}"
|
||||
else:
|
||||
raise ValidationError("%s is not a valid second level domain" % value)
|
||||
|
||||
@property
|
||||
def tld(self):
|
||||
"""Get or set the top level domain string."""
|
||||
parts = self.website.split(".")
|
||||
return parts[1] if len(parts) > 1 else ""
|
||||
|
||||
@tld.setter
|
||||
def tld(self, value: str):
|
||||
Domain = apps.get_model("registrar.Domain")
|
||||
sld = self.website.split(".")[0]
|
||||
if Domain.string_could_be_domain(f"{sld}.{value}"):
|
||||
self.website = f"{sld}.{value}"
|
||||
else:
|
||||
raise ValidationError("%s is not a valid top level domain" % value)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.website)
|
||||
|
|
|
@ -27,10 +27,10 @@
|
|||
|
||||
{{ wizard.management_form }}
|
||||
{% csrf_token %}
|
||||
{{ wizard.form.dotgov_domain|add_label_class:"usa-label" }}
|
||||
{{ wizard.form.requested_domain|add_label_class:"usa-label" }}
|
||||
<div class="display-flex flex-align-center">
|
||||
<span class="padding-top-05 padding-right-2px">www.</span>
|
||||
{{ wizard.form.dotgov_domain|add_class:"usa-input"|attr:"aria-describedby:domain_instructions" }}
|
||||
{{ wizard.form.requested_domain|add_class:"usa-input"|attr:"aria-describedby:domain_instructions" }}
|
||||
<span class="padding-top-05 padding-left-2px">.gov </span>
|
||||
</div>
|
||||
<button type="button" class="usa-button">Check availability </button>
|
||||
|
|
|
@ -20,11 +20,11 @@
|
|||
<h1> {{form_titles|get_item:wizard.steps.current}} </h1>
|
||||
{% block form_content %}
|
||||
{% if wizard.steps.next %}
|
||||
<button type="submit" class="usa-button">Next</button>
|
||||
<button type="submit" name="submit_button" value="next" class="usa-button">Next</button>
|
||||
{% else %}
|
||||
<button type="submit" class="usa-button">Submit your domain request</button>
|
||||
{% endif %}
|
||||
<button type="button" class="usa-button usa-button--outline">Save</button>
|
||||
<button type="submit" name="submit_button" value="save" class="usa-button usa-button--outline">Save</button>
|
||||
</main>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
{{ wizard.form.address_line1|add_class:"usa-input" }}
|
||||
{{ wizard.form.address_line2|add_label_class:"usa-label" }}
|
||||
{{ wizard.form.address_line2|add_class:"usa-input" }}
|
||||
{{ wizard.form.us_state|add_label_class:"usa-label" }}
|
||||
<div class="usa-combo-box">
|
||||
{{ wizard.form.us_state|add_class:"usa-select" }}
|
||||
{{ wizard.form.state_territory|add_label_class:"usa-label" }}
|
||||
<div class="usa-combo-box" data-default-value>
|
||||
{{ wizard.form.state_territory|add_class:"usa-select" }}
|
||||
</div>
|
||||
{{ wizard.form.zipcode|add_label_class:"usa-label" }}
|
||||
{{ wizard.form.zipcode|add_class:"usa-input usa-input--small" }}
|
||||
|
|
|
@ -13,8 +13,9 @@
|
|||
<h2>Is your organization an election office?</h2>
|
||||
</legend>
|
||||
{% radio_buttons_by_value wizard.form.is_election_board as choices %}
|
||||
{% include "includes/radio_button.html" with choice=choices|get_item:True %}
|
||||
{% include "includes/radio_button.html" with choice=choices|get_item:False %}
|
||||
{% for choice in choices.values %}
|
||||
{% include "includes/radio_button.html" with choice=choice tile="true" %}
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
|
||||
{{ block.super }}
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
<legend>
|
||||
<h2>Which federal branch is your organization in?</h2>
|
||||
</legend>
|
||||
{% radio_buttons_by_value wizard.form.federal_type as federal_choices %}
|
||||
{% include "includes/radio_button.html" with choice=federal_choices.Executive%}
|
||||
{% include "includes/radio_button.html" with choice=federal_choices.Judicial%}
|
||||
{% include "includes/radio_button.html" with choice=federal_choices.Legislative%}
|
||||
{% radio_buttons_by_value wizard.form.federal_type as choices %}
|
||||
{% for choice in choices.values %}
|
||||
{% include "includes/radio_button.html" with choice=choice tile="true" %}
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
|
||||
{{ block.super }}
|
||||
|
|
|
@ -16,13 +16,9 @@
|
|||
<h2> What kind of government organization do you represent?</h2>
|
||||
</legend>
|
||||
{{ wizard.form.organization_type.errors }}
|
||||
{% include "includes/radio_button.html" with choice=choices.Federal tile="true" %}
|
||||
{% include "includes/radio_button.html" with choice=choices.Interstate tile="true" %}
|
||||
{% include "includes/radio_button.html" with choice=choices.State_or_Territory tile="true" %}
|
||||
{% include "includes/radio_button.html" with choice=choices.Tribal tile="true" %}
|
||||
{% include "includes/radio_button.html" with choice=choices.County tile="true" %}
|
||||
{% include "includes/radio_button.html" with choice=choices.City tile="true" %}
|
||||
{% include "includes/radio_button.html" with choice=choices.Special_District tile="true" %}
|
||||
{% for choice in choices.values %}
|
||||
{% include "includes/radio_button.html" with choice=choice tile="true" %}
|
||||
{% endfor %}
|
||||
</fieldset>
|
||||
|
||||
{{ block.super }}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
{% csrf_token %}
|
||||
|
||||
<div class="usa-character-count">
|
||||
{{ wizard.form.purpose_field|add_label_class:"usa-label usa-sr-only" }}
|
||||
{{ wizard.form.purpose_field|add_class:"usa-textarea usa-character-count__field"|attr:"aria-describedby:instructions"|attr:"maxlength=500" }}
|
||||
{{ wizard.form.purpose|add_label_class:"usa-label usa-sr-only" }}
|
||||
{{ wizard.form.purpose|add_class:"usa-textarea usa-character-count__field"|attr:"aria-describedby:instructions"|attr:"maxlength=500" }}
|
||||
<span class="usa-character-count__message" id="with-hint-textarea-info with-hint-textarea-hint"> You can enter up to 500 characters </span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -60,8 +60,8 @@
|
|||
{% csrf_token %}
|
||||
|
||||
<div class="usa-checkbox">
|
||||
{{ wizard.form.agree_check|add_class:"usa-checkbox__input"}}
|
||||
{{ wizard.form.agree_check|add_label_class:"usa-checkbox__label" }}
|
||||
{{ wizard.form.is_policy_acknowledged|add_class:"usa-checkbox__input"}}
|
||||
{{ wizard.form.is_policy_acknowledged|add_label_class:"usa-checkbox__label" }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -11,8 +11,8 @@
|
|||
{{ wizard.management_form }}
|
||||
{% csrf_token %}
|
||||
|
||||
{{ wizard.form.email|add_label_class:"usa-label" }}
|
||||
{{ wizard.form.email|add_class:"usa-input"|attr:"aria-describedby:instructions" }}
|
||||
{{ wizard.form.security_email|add_label_class:"usa-label" }}
|
||||
{{ wizard.form.security_email|add_class:"usa-input"|attr:"aria-describedby:instructions" }}
|
||||
|
||||
{{ block.super }}
|
||||
|
||||
|
|
|
@ -27,22 +27,21 @@ class TestDomainApplication(TestCase):
|
|||
application = DomainApplication.objects.create(
|
||||
creator=user,
|
||||
investigator=user,
|
||||
organization_type=DomainApplication.FEDERAL,
|
||||
federal_branch=DomainApplication.EXECUTIVE,
|
||||
is_election_office=False,
|
||||
organization_type=DomainApplication.OrganizationChoices.FEDERAL,
|
||||
federal_type=DomainApplication.BranchChoices.EXECUTIVE,
|
||||
is_election_board=False,
|
||||
organization_name="Test",
|
||||
street_address="100 Main St.",
|
||||
unit_type="APT",
|
||||
unit_number="1A",
|
||||
address_line1="100 Main St.",
|
||||
address_line2="APT 1A",
|
||||
state_territory="CA",
|
||||
zip_code="12345-6789",
|
||||
zipcode="12345-6789",
|
||||
authorizing_official=contact,
|
||||
requested_domain=domain,
|
||||
submitter=contact,
|
||||
purpose="Igorville rules!",
|
||||
security_email="security@igorville.gov",
|
||||
anything_else="All of Igorville loves the dotgov program.",
|
||||
acknowledged_policy=True,
|
||||
is_policy_acknowledged=True,
|
||||
)
|
||||
application.current_websites.add(com_website)
|
||||
application.alternative_domains.add(gov_website)
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
from unittest import skip
|
||||
|
||||
from django.conf import settings
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
|
@ -5,7 +7,7 @@ from django.contrib.auth import get_user_model
|
|||
|
||||
from django_webtest import WebTest # type: ignore
|
||||
|
||||
from registrar.models import DomainApplication, Domain
|
||||
from registrar.models import DomainApplication, Domain, Contact, Website
|
||||
from registrar.forms.application_wizard import TITLES
|
||||
|
||||
from .common import less_console_noise
|
||||
|
@ -87,9 +89,9 @@ class LoggedInTests(TestWithUser):
|
|||
)
|
||||
|
||||
|
||||
class FormTests(TestWithUser, WebTest):
|
||||
class DomainApplicationTests(TestWithUser, WebTest):
|
||||
|
||||
"""Webtests for forms to test filling and submitting."""
|
||||
"""Webtests for domain application to test filling and submitting."""
|
||||
|
||||
# Doesn't work with CSRF checking
|
||||
# hypothesis is that CSRF_USE_SESSIONS is incompatible with WebTest
|
||||
|
@ -100,7 +102,7 @@ class FormTests(TestWithUser, WebTest):
|
|||
self.app.set_user(self.user.username)
|
||||
|
||||
def tearDown(self):
|
||||
# delete any applications we made so that users can be deleted\
|
||||
# delete any applications we made so that users can be deleted
|
||||
DomainApplication.objects.all().delete()
|
||||
super().tearDown()
|
||||
|
||||
|
@ -116,6 +118,10 @@ class FormTests(TestWithUser, WebTest):
|
|||
As we add additional form pages, we need to include them here to make
|
||||
this test work.
|
||||
"""
|
||||
num_pages_tested = 0
|
||||
SKIPPED_PAGES = 1 # elections
|
||||
num_pages = len(TITLES) - SKIPPED_PAGES
|
||||
|
||||
type_page = self.app.get(reverse("application")).follow()
|
||||
# django-webtest does not handle cookie-based sessions well because it keeps
|
||||
# resetting the session key on each new request, thus destroying the concept
|
||||
|
@ -125,9 +131,18 @@ class FormTests(TestWithUser, WebTest):
|
|||
|
||||
# ---- TYPE PAGE ----
|
||||
type_form = type_page.form
|
||||
type_form["organization_type-organization_type"] = "Federal"
|
||||
type_form["organization_type-organization_type"] = "federal"
|
||||
|
||||
# set the session ID before .submit()
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = type_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/organization_type/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(application.organization_type, "federal")
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_result = type_page.form.submit()
|
||||
|
||||
|
@ -135,20 +150,31 @@ class FormTests(TestWithUser, WebTest):
|
|||
# the application
|
||||
self.assertEquals(type_result.status_code, 302)
|
||||
self.assertEquals(type_result["Location"], "/register/organization_federal/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- FEDERAL BRANCH PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
federal_page = type_result.follow()
|
||||
federal_form = federal_page.form
|
||||
federal_form["organization_federal-federal_type"] = "Executive"
|
||||
federal_form["organization_federal-federal_type"] = "executive"
|
||||
|
||||
# set the session ID before .submit()
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = federal_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/organization_federal/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(application.federal_type, "executive")
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
federal_result = federal_form.submit()
|
||||
|
||||
self.assertEquals(federal_result.status_code, 302)
|
||||
self.assertEquals(federal_result["Location"], "/register/organization_contact/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- ORG CONTACT PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
|
@ -156,9 +182,22 @@ class FormTests(TestWithUser, WebTest):
|
|||
org_contact_form = org_contact_page.form
|
||||
org_contact_form["organization_contact-organization_name"] = "Testorg"
|
||||
org_contact_form["organization_contact-address_line1"] = "address 1"
|
||||
org_contact_form["organization_contact-us_state"] = "NY"
|
||||
org_contact_form["organization_contact-state_territory"] = "NY"
|
||||
org_contact_form["organization_contact-zipcode"] = "10002"
|
||||
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = org_contact_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/organization_contact/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(application.organization_name, "Testorg")
|
||||
self.assertEquals(application.address_line1, "address 1")
|
||||
self.assertEquals(application.state_territory, "NY")
|
||||
self.assertEquals(application.zipcode, "10002")
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
org_contact_result = org_contact_form.submit()
|
||||
|
||||
|
@ -166,6 +205,8 @@ class FormTests(TestWithUser, WebTest):
|
|||
self.assertEquals(
|
||||
org_contact_result["Location"], "/register/authorizing_official/"
|
||||
)
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- AUTHORIZING OFFICIAL PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
ao_page = org_contact_result.follow()
|
||||
|
@ -176,11 +217,26 @@ class FormTests(TestWithUser, WebTest):
|
|||
ao_form["authorizing_official-email"] = "testy@town.com"
|
||||
ao_form["authorizing_official-phone"] = "(555) 555 5555"
|
||||
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = ao_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/authorizing_official/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(application.authorizing_official.first_name, "Testy")
|
||||
self.assertEquals(application.authorizing_official.last_name, "Tester")
|
||||
self.assertEquals(application.authorizing_official.title, "Chief Tester")
|
||||
self.assertEquals(application.authorizing_official.email, "testy@town.com")
|
||||
self.assertEquals(application.authorizing_official.phone, "(555) 555 5555")
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
ao_result = ao_form.submit()
|
||||
|
||||
self.assertEquals(ao_result.status_code, 302)
|
||||
self.assertEquals(ao_result["Location"], "/register/current_sites/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- CURRENT SITES PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
|
@ -188,35 +244,73 @@ class FormTests(TestWithUser, WebTest):
|
|||
current_sites_form = current_sites_page.form
|
||||
current_sites_form["current_sites-current_site"] = "www.city.com"
|
||||
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = current_sites_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/current_sites/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(
|
||||
application.current_websites.filter(website="city.com").count(), 1
|
||||
)
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
current_sites_result = current_sites_form.submit()
|
||||
|
||||
self.assertEquals(current_sites_result.status_code, 302)
|
||||
self.assertEquals(current_sites_result["Location"], "/register/dotgov_domain/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- DOTGOV DOMAIN PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
dotgov_page = current_sites_result.follow()
|
||||
dotgov_form = dotgov_page.form
|
||||
dotgov_form["dotgov_domain-dotgov_domain"] = "city"
|
||||
dotgov_form["dotgov_domain-requested_domain"] = "city"
|
||||
dotgov_form["dotgov_domain-alternative_domain"] = "city1"
|
||||
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = dotgov_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/dotgov_domain/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(application.requested_domain.name, "city.gov")
|
||||
self.assertEquals(
|
||||
application.alternative_domains.filter(website="city1.gov").count(), 1
|
||||
)
|
||||
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
dotgov_result = dotgov_form.submit()
|
||||
|
||||
self.assertEquals(dotgov_result.status_code, 302)
|
||||
self.assertEquals(dotgov_result["Location"], "/register/purpose/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- PURPOSE DOMAIN PAGE ----
|
||||
# ---- PURPOSE PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
purpose_page = dotgov_result.follow()
|
||||
purpose_form = purpose_page.form
|
||||
purpose_form["purpose-purpose_field"] = "Purpose of the site"
|
||||
purpose_form["purpose-purpose"] = "Purpose of the site"
|
||||
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = purpose_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/purpose/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(application.purpose, "Purpose of the site")
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
purpose_result = purpose_form.submit()
|
||||
|
||||
self.assertEquals(purpose_result.status_code, 302)
|
||||
self.assertEquals(purpose_result["Location"], "/register/your_contact/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- YOUR CONTACT INFO PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
|
@ -229,11 +323,26 @@ class FormTests(TestWithUser, WebTest):
|
|||
your_contact_form["your_contact-email"] = "testy-admin@town.com"
|
||||
your_contact_form["your_contact-phone"] = "(555) 555 5556"
|
||||
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = your_contact_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/your_contact/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(application.submitter.first_name, "Testy you")
|
||||
self.assertEquals(application.submitter.last_name, "Tester you")
|
||||
self.assertEquals(application.submitter.title, "Admin Tester")
|
||||
self.assertEquals(application.submitter.email, "testy-admin@town.com")
|
||||
self.assertEquals(application.submitter.phone, "(555) 555 5556")
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
your_contact_result = your_contact_form.submit()
|
||||
|
||||
self.assertEquals(your_contact_result.status_code, 302)
|
||||
self.assertEquals(your_contact_result["Location"], "/register/other_contacts/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- OTHER CONTACTS PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
|
@ -246,6 +355,25 @@ class FormTests(TestWithUser, WebTest):
|
|||
other_contacts_form["other_contacts-email"] = "testy2@town.com"
|
||||
other_contacts_form["other_contacts-phone"] = "(555) 555 5557"
|
||||
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = other_contacts_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/other_contacts/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(
|
||||
application.other_contacts.filter(
|
||||
first_name="Testy2",
|
||||
last_name="Tester2",
|
||||
title="Another Tester",
|
||||
email="testy2@town.com",
|
||||
phone="(555) 555 5557",
|
||||
).count(),
|
||||
1,
|
||||
)
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
other_contacts_result = other_contacts_form.submit()
|
||||
|
||||
|
@ -253,19 +381,31 @@ class FormTests(TestWithUser, WebTest):
|
|||
self.assertEquals(
|
||||
other_contacts_result["Location"], "/register/security_email/"
|
||||
)
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- SECURITY EMAIL PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
security_email_page = other_contacts_result.follow()
|
||||
security_email_form = security_email_page.form
|
||||
|
||||
security_email_form["security_email-email"] = "security@city.com"
|
||||
security_email_form["security_email-security_email"] = "security@city.com"
|
||||
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = security_email_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/security_email/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(application.security_email, "security@city.com")
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
security_email_result = security_email_form.submit()
|
||||
|
||||
self.assertEquals(security_email_result.status_code, 302)
|
||||
self.assertEquals(security_email_result["Location"], "/register/anything_else/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- ANYTHING ELSE PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
|
@ -274,37 +414,65 @@ class FormTests(TestWithUser, WebTest):
|
|||
|
||||
anything_else_form["anything_else-anything_else"] = "No"
|
||||
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = anything_else_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/anything_else/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(application.anything_else, "No")
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
anything_else_result = anything_else_form.submit()
|
||||
|
||||
self.assertEquals(anything_else_result.status_code, 302)
|
||||
self.assertEquals(anything_else_result["Location"], "/register/requirements/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- REQUIREMENTS PAGE ----
|
||||
# Follow the redirect to the next form page
|
||||
requirements_page = anything_else_result.follow()
|
||||
requirements_form = requirements_page.form
|
||||
|
||||
requirements_form["requirements-agree_check"] = True
|
||||
requirements_form["requirements-is_policy_acknowledged"] = True
|
||||
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = requirements_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/requirements/")
|
||||
# should see results in db
|
||||
application = DomainApplication.objects.get() # there's only one
|
||||
self.assertEquals(application.is_policy_acknowledged, True)
|
||||
|
||||
# test next button
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
requirements_result = requirements_form.submit()
|
||||
|
||||
self.assertEquals(requirements_result.status_code, 302)
|
||||
self.assertEquals(requirements_result["Location"], "/register/review/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# ---- REVIEW AND FINSIHED PAGES ----
|
||||
# Follow the redirect to the next form page
|
||||
review_page = requirements_result.follow()
|
||||
review_form = review_page.form
|
||||
|
||||
# final submission results in a redirect to the "finished" URL
|
||||
# test saving the page
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
result = review_page.form.submit("submit_button", value="save")
|
||||
# should remain on the same page
|
||||
self.assertEquals(result["Location"], "/register/review/")
|
||||
|
||||
# final submission results in a redirect to the "finished" URL
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
review_result = review_form.submit()
|
||||
|
||||
self.assertEquals(review_result.status_code, 302)
|
||||
self.assertEquals(review_result["Location"], "/register/finished/")
|
||||
num_pages_tested += 1
|
||||
|
||||
# following this redirect is a GET request, so include the cookie
|
||||
# here too.
|
||||
|
@ -313,6 +481,9 @@ class FormTests(TestWithUser, WebTest):
|
|||
final_result = review_result.follow()
|
||||
self.assertContains(final_result, "Thank you for your domain request")
|
||||
|
||||
# check that any new pages are added to this test
|
||||
self.assertEqual(num_pages, num_pages_tested)
|
||||
|
||||
def test_application_form_conditional_federal(self):
|
||||
"""Federal branch question is shown for federal organizations."""
|
||||
type_page = self.app.get(reverse("application")).follow()
|
||||
|
@ -328,7 +499,7 @@ class FormTests(TestWithUser, WebTest):
|
|||
self.assertNotContains(type_page, TITLES["organization_federal"])
|
||||
self.assertNotContains(type_page, TITLES["organization_election"])
|
||||
type_form = type_page.form
|
||||
type_form["organization_type-organization_type"] = "Federal"
|
||||
type_form["organization_type-organization_type"] = "federal"
|
||||
|
||||
# set the session ID before .submit()
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
|
@ -361,14 +532,13 @@ class FormTests(TestWithUser, WebTest):
|
|||
self.assertNotContains(type_page, TITLES["organization_federal"])
|
||||
self.assertNotContains(type_page, TITLES["organization_election"])
|
||||
type_form = type_page.form
|
||||
type_form["organization_type-organization_type"] = "County"
|
||||
type_form["organization_type-organization_type"] = "county"
|
||||
|
||||
# set the session ID before .submit()
|
||||
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
type_result = type_form.submit()
|
||||
|
||||
# the post request should return a redirect to the federal branch
|
||||
# question
|
||||
# the post request should return a redirect to the elections question
|
||||
self.assertEquals(type_result.status_code, 302)
|
||||
self.assertEquals(type_result["Location"], "/register/organization_election/")
|
||||
|
||||
|
@ -378,3 +548,124 @@ class FormTests(TestWithUser, WebTest):
|
|||
election_page = type_result.follow()
|
||||
self.assertContains(election_page, TITLES["organization_election"])
|
||||
self.assertNotContains(election_page, TITLES["organization_federal"])
|
||||
|
||||
@skip("WIP")
|
||||
def test_application_edit_restore(self):
|
||||
"""
|
||||
Test that a previously saved application is available at the /edit endpoint.
|
||||
"""
|
||||
ao, _ = Contact.objects.get_or_create(
|
||||
first_name="Testy",
|
||||
last_name="Tester",
|
||||
title="Chief Tester",
|
||||
email="testy@town.com",
|
||||
phone="(555) 555 5555",
|
||||
)
|
||||
domain, _ = Domain.objects.get_or_create(name="city.gov")
|
||||
alt, _ = Website.objects.get_or_create(website="city1.gov")
|
||||
current, _ = Website.objects.get_or_create(website="city.com")
|
||||
you, _ = Contact.objects.get_or_create(
|
||||
first_name="Testy you",
|
||||
last_name="Tester you",
|
||||
title="Admin Tester",
|
||||
email="testy-admin@town.com",
|
||||
phone="(555) 555 5556",
|
||||
)
|
||||
other, _ = Contact.objects.get_or_create(
|
||||
first_name="Testy2",
|
||||
last_name="Tester2",
|
||||
title="Another Tester",
|
||||
email="testy2@town.com",
|
||||
phone="(555) 555 5557",
|
||||
)
|
||||
application, _ = DomainApplication.objects.get_or_create(
|
||||
organization_type="federal",
|
||||
federal_type="executive",
|
||||
purpose="Purpose of the site",
|
||||
security_email="security@city.com",
|
||||
anything_else="No",
|
||||
is_policy_acknowledged=True,
|
||||
organization_name="Testorg",
|
||||
address_line1="address 1",
|
||||
state_territory="NY",
|
||||
zipcode="10002",
|
||||
authorizing_official=ao,
|
||||
requested_domain=domain,
|
||||
submitter=you,
|
||||
creator=self.user,
|
||||
)
|
||||
application.other_contacts.add(other)
|
||||
application.current_websites.add(current)
|
||||
application.alternative_domains.add(alt)
|
||||
|
||||
# prime the form by visiting /edit
|
||||
url = reverse("edit-application", kwargs={"id": application.pk})
|
||||
response = self.client.get(url)
|
||||
|
||||
url = reverse("application_step", kwargs={"step": "organization_type"})
|
||||
response = self.client.get(url, follow=True)
|
||||
self.assertContains(response, "<input>")
|
||||
# choices = response.context['wizard']['form']['organization_type'].subwidgets
|
||||
# radio = [ x for x in choices if x.data["value"] == "federal" ][0]
|
||||
# checked = radio.data["selected"]
|
||||
# self.assertTrue(checked)
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "organization_federal"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "organization_contact"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "authorizing_official"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "current_sites"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "dotgov_domain"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "purpose"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "your_contact"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "other_contacts"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "other_contacts"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "security_email"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "anything_else"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
||||
# url = reverse("application_step", kwargs={"step": "requirements"})
|
||||
# self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||
# page = self.app.get(url)
|
||||
# self.assertNotContains(page, "VALUE")
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue