mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-25 03:58:39 +02:00
Merge pull request #695 from cisagov/ik/org-mail-address
Add organization name and mailing address page for domains
This commit is contained in:
commit
e8f39f3b9e
11 changed files with 272 additions and 6 deletions
|
@ -88,6 +88,11 @@ urlpatterns = [
|
||||||
views.DomainYourContactInformationView.as_view(),
|
views.DomainYourContactInformationView.as_view(),
|
||||||
name="domain-your-contact-information",
|
name="domain-your-contact-information",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"domain/<int:pk>/org-name-address",
|
||||||
|
views.DomainOrgNameAddressView.as_view(),
|
||||||
|
name="domain-org-name-address",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"domain/<int:pk>/authorizing-official",
|
"domain/<int:pk>/authorizing-official",
|
||||||
views.DomainAuthorizingOfficialView.as_view(),
|
views.DomainAuthorizingOfficialView.as_view(),
|
||||||
|
|
|
@ -3,5 +3,6 @@ from .domain import (
|
||||||
DomainAddUserForm,
|
DomainAddUserForm,
|
||||||
NameserverFormset,
|
NameserverFormset,
|
||||||
DomainSecurityEmailForm,
|
DomainSecurityEmailForm,
|
||||||
|
DomainOrgNameAddressForm,
|
||||||
ContactForm,
|
ContactForm,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
"""Forms for domain management."""
|
"""Forms for domain management."""
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.core.validators import RegexValidator
|
||||||
from django.forms import formset_factory
|
from django.forms import formset_factory
|
||||||
|
|
||||||
from phonenumber_field.widgets import RegionalPhoneNumberWidget
|
from phonenumber_field.widgets import RegionalPhoneNumberWidget
|
||||||
|
|
||||||
from ..models import Contact
|
from ..models import Contact, DomainInformation
|
||||||
|
|
||||||
|
|
||||||
class DomainAddUserForm(forms.Form):
|
class DomainAddUserForm(forms.Form):
|
||||||
|
@ -64,3 +65,77 @@ class DomainSecurityEmailForm(forms.Form):
|
||||||
"""Form for adding or editing a security email to a domain."""
|
"""Form for adding or editing a security email to a domain."""
|
||||||
|
|
||||||
security_email = forms.EmailField(label="Security email")
|
security_email = forms.EmailField(label="Security email")
|
||||||
|
|
||||||
|
|
||||||
|
class DomainOrgNameAddressForm(forms.ModelForm):
|
||||||
|
|
||||||
|
"""Form for updating the organization name and mailing address."""
|
||||||
|
|
||||||
|
zipcode = forms.CharField(
|
||||||
|
label="Zip code",
|
||||||
|
validators=[
|
||||||
|
RegexValidator(
|
||||||
|
"^[0-9]{5}(?:-[0-9]{4})?$|^$",
|
||||||
|
message="Enter a zip code in the form of 12345 or 12345-6789.",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = DomainInformation
|
||||||
|
fields = [
|
||||||
|
"federal_agency",
|
||||||
|
"organization_name",
|
||||||
|
"address_line1",
|
||||||
|
"address_line2",
|
||||||
|
"city",
|
||||||
|
"state_territory",
|
||||||
|
"zipcode",
|
||||||
|
"urbanization",
|
||||||
|
]
|
||||||
|
error_messages = {
|
||||||
|
"federal_agency": {
|
||||||
|
"required": "Select the federal agency for your organization."
|
||||||
|
},
|
||||||
|
"organization_name": {"required": "Enter the name of your organization."},
|
||||||
|
"address_line1": {
|
||||||
|
"required": "Enter the street address of your organization."
|
||||||
|
},
|
||||||
|
"city": {"required": "Enter the city where your organization is located."},
|
||||||
|
"state_territory": {
|
||||||
|
"required": "Select the state, territory, or military post where your"
|
||||||
|
"organization is located."
|
||||||
|
},
|
||||||
|
}
|
||||||
|
widgets = {
|
||||||
|
# We need to set the required attributed for federal_agency and
|
||||||
|
# state/territory because for these fields we are creating an individual
|
||||||
|
# instance of the Select. For the other fields we use the for loop to set
|
||||||
|
# the class's required attribute to true.
|
||||||
|
"federal_agency": forms.Select(
|
||||||
|
attrs={"required": True}, choices=DomainInformation.AGENCY_CHOICES
|
||||||
|
),
|
||||||
|
"organization_name": forms.TextInput,
|
||||||
|
"address_line1": forms.TextInput,
|
||||||
|
"address_line2": forms.TextInput,
|
||||||
|
"city": forms.TextInput,
|
||||||
|
"state_territory": forms.Select(
|
||||||
|
attrs={
|
||||||
|
"required": True,
|
||||||
|
},
|
||||||
|
choices=DomainInformation.StateTerritoryChoices.choices,
|
||||||
|
),
|
||||||
|
"urbanization": forms.TextInput,
|
||||||
|
}
|
||||||
|
|
||||||
|
# the database fields have blank=True so ModelForm doesn't create
|
||||||
|
# required fields by default. Use this list in __init__ to mark each
|
||||||
|
# of these fields as required
|
||||||
|
required = ["organization_name", "address_line1", "city", "zipcode"]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
for field_name in self.required:
|
||||||
|
self.fields[field_name].required = True
|
||||||
|
self.fields["state_territory"].widget.attrs.pop("maxlength", None)
|
||||||
|
self.fields["zipcode"].widget.attrs.pop("maxlength", None)
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Generated by Django 4.2.1 on 2023-06-09 16:38
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("registrar", "0026_alter_domainapplication_address_line2_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domaininformation",
|
||||||
|
name="address_line1",
|
||||||
|
field=models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Street address",
|
||||||
|
null=True,
|
||||||
|
verbose_name="Street address",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domaininformation",
|
||||||
|
name="address_line2",
|
||||||
|
field=models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Street address line 2",
|
||||||
|
null=True,
|
||||||
|
verbose_name="Street address line 2",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domaininformation",
|
||||||
|
name="state_territory",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
help_text="State, territory, or military post",
|
||||||
|
max_length=2,
|
||||||
|
null=True,
|
||||||
|
verbose_name="State, territory, or military post",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domaininformation",
|
||||||
|
name="urbanization",
|
||||||
|
field=models.TextField(
|
||||||
|
blank=True,
|
||||||
|
help_text="Urbanization (Puerto Rico only)",
|
||||||
|
null=True,
|
||||||
|
verbose_name="Urbanization (Puerto Rico only)",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
|
@ -100,11 +100,13 @@ class DomainInformation(TimeStampedModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Street address",
|
help_text="Street address",
|
||||||
|
verbose_name="Street address",
|
||||||
)
|
)
|
||||||
address_line2 = models.TextField(
|
address_line2 = models.TextField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Street address line 2",
|
help_text="Street address line 2",
|
||||||
|
verbose_name="Street address line 2",
|
||||||
)
|
)
|
||||||
city = models.TextField(
|
city = models.TextField(
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -116,6 +118,7 @@ class DomainInformation(TimeStampedModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="State, territory, or military post",
|
help_text="State, territory, or military post",
|
||||||
|
verbose_name="State, territory, or military post",
|
||||||
)
|
)
|
||||||
zipcode = models.CharField(
|
zipcode = models.CharField(
|
||||||
max_length=10,
|
max_length=10,
|
||||||
|
@ -128,6 +131,7 @@ class DomainInformation(TimeStampedModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Urbanization (Puerto Rico only)",
|
help_text="Urbanization (Puerto Rico only)",
|
||||||
|
verbose_name="Urbanization (Puerto Rico only)",
|
||||||
)
|
)
|
||||||
|
|
||||||
type_of_work = models.TextField(
|
type_of_work = models.TextField(
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
<a class="usa-button margin-bottom-1" href="{{url}}"> Add DNS name servers </a>
|
<a class="usa-button margin-bottom-1" href="{{url}}"> Add DNS name servers </a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% url 'todo' as url %}
|
{% url 'domain-org-name-address' pk=domain.id as url %}
|
||||||
{% include "includes/summary_item.html" with title='Organization name and mailing address' value=domain.domain_info address='true' edit_link=url %}
|
{% include "includes/summary_item.html" with title='Organization name and mailing address' value=domain.domain_info address='true' edit_link=url %}
|
||||||
|
|
||||||
{% url 'domain-authorizing-official' pk=domain.id as url %}
|
{% url 'domain-authorizing-official' pk=domain.id as url %}
|
||||||
|
|
47
src/registrar/templates/domain_org_name_address.html
Normal file
47
src/registrar/templates/domain_org_name_address.html
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{% extends "domain_base.html" %}
|
||||||
|
{% load static field_helpers%}
|
||||||
|
|
||||||
|
{% block title %}Organization name and mailing address | {{ domain.name }} | {% endblock %}
|
||||||
|
|
||||||
|
{% block domain_content %}
|
||||||
|
{# this is right after the messages block in the parent template #}
|
||||||
|
{% include "includes/form_errors.html" with form=form %}
|
||||||
|
|
||||||
|
<h1>Organization name and mailing address </h1>
|
||||||
|
|
||||||
|
<p>The name of your organization will be publicly listed as the domain registrant.</p>
|
||||||
|
|
||||||
|
{% include "includes/required_fields.html" %}
|
||||||
|
|
||||||
|
<form class="usa-form usa-form--large" method="post" novalidate id="form-container">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{% if domain.domain_info.organization_type == 'federal' %}
|
||||||
|
{% input_with_errors form.federal_agency %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% input_with_errors form.organization_name %}
|
||||||
|
|
||||||
|
{% input_with_errors form.address_line1 %}
|
||||||
|
|
||||||
|
{% input_with_errors form.address_line2 %}
|
||||||
|
|
||||||
|
{% input_with_errors form.city %}
|
||||||
|
|
||||||
|
{% input_with_errors form.state_territory %}
|
||||||
|
|
||||||
|
{% with add_class="usa-input--small" %}
|
||||||
|
{% input_with_errors form.zipcode %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% input_with_errors form.urbanization %}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="usa-button"
|
||||||
|
>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %} {# domain_content #}
|
|
@ -22,7 +22,7 @@
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="usa-sidenav__item">
|
<li class="usa-sidenav__item">
|
||||||
{% url 'todo' as url %}
|
{% url 'domain-org-name-address' pk=domain.id as url %}
|
||||||
<a href="{{ url }}"
|
<a href="{{ url }}"
|
||||||
{% if request.path == url %}class="usa-current"{% endif %}
|
{% if request.path == url %}class="usa-current"{% endif %}
|
||||||
>
|
>
|
||||||
|
|
|
@ -1057,6 +1057,7 @@ class TestDomainPermissions(TestWithDomainPermissions):
|
||||||
"domain-users",
|
"domain-users",
|
||||||
"domain-users-add",
|
"domain-users-add",
|
||||||
"domain-nameservers",
|
"domain-nameservers",
|
||||||
|
"domain-org-name-address",
|
||||||
"domain-authorizing-official",
|
"domain-authorizing-official",
|
||||||
"domain-your-contact-information",
|
"domain-your-contact-information",
|
||||||
"domain-security-email",
|
"domain-security-email",
|
||||||
|
@ -1077,6 +1078,7 @@ class TestDomainPermissions(TestWithDomainPermissions):
|
||||||
"domain-users",
|
"domain-users",
|
||||||
"domain-users-add",
|
"domain-users-add",
|
||||||
"domain-nameservers",
|
"domain-nameservers",
|
||||||
|
"domain-org-name-address",
|
||||||
"domain-authorizing-official",
|
"domain-authorizing-official",
|
||||||
"domain-your-contact-information",
|
"domain-your-contact-information",
|
||||||
"domain-security-email",
|
"domain-security-email",
|
||||||
|
@ -1314,6 +1316,42 @@ class TestDomainDetail(TestWithDomainPermissions, WebTest):
|
||||||
)
|
)
|
||||||
self.assertContains(page, "Testy")
|
self.assertContains(page, "Testy")
|
||||||
|
|
||||||
|
def test_domain_org_name_address(self):
|
||||||
|
"""Can load domain's org name and mailing address page."""
|
||||||
|
page = self.client.get(
|
||||||
|
reverse("domain-org-name-address", kwargs={"pk": self.domain.id})
|
||||||
|
)
|
||||||
|
# once on the sidebar, once in the page title, once as H1
|
||||||
|
self.assertContains(page, "Organization name and mailing address", count=3)
|
||||||
|
|
||||||
|
def test_domain_org_name_address_content(self):
|
||||||
|
"""Org name and address information appears on the page."""
|
||||||
|
self.domain_information.organization_name = "Town of Igorville"
|
||||||
|
self.domain_information.save()
|
||||||
|
page = self.app.get(
|
||||||
|
reverse("domain-org-name-address", kwargs={"pk": self.domain.id})
|
||||||
|
)
|
||||||
|
self.assertContains(page, "Town of Igorville")
|
||||||
|
|
||||||
|
def test_domain_org_name_address_form(self):
|
||||||
|
"""Submitting changes works on the org name address page."""
|
||||||
|
self.domain_information.organization_name = "Town of Igorville"
|
||||||
|
self.domain_information.save()
|
||||||
|
org_name_page = self.app.get(
|
||||||
|
reverse("domain-org-name-address", kwargs={"pk": self.domain.id})
|
||||||
|
)
|
||||||
|
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
|
|
||||||
|
org_name_page.form["organization_name"] = "Not igorville"
|
||||||
|
org_name_page.form["city"] = "Faketown"
|
||||||
|
|
||||||
|
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
|
||||||
|
success_result_page = org_name_page.form.submit()
|
||||||
|
self.assertEqual(success_result_page.status_code, 200)
|
||||||
|
|
||||||
|
self.assertContains(success_result_page, "Not igorville")
|
||||||
|
self.assertContains(success_result_page, "Faketown")
|
||||||
|
|
||||||
def test_domain_your_contact_information(self):
|
def test_domain_your_contact_information(self):
|
||||||
"""Can load domain's your contact information page."""
|
"""Can load domain's your contact information page."""
|
||||||
page = self.client.get(
|
page = self.client.get(
|
||||||
|
|
|
@ -2,6 +2,7 @@ from .application import *
|
||||||
from .domain import (
|
from .domain import (
|
||||||
DomainView,
|
DomainView,
|
||||||
DomainAuthorizingOfficialView,
|
DomainAuthorizingOfficialView,
|
||||||
|
DomainOrgNameAddressView,
|
||||||
DomainNameserversView,
|
DomainNameserversView,
|
||||||
DomainYourContactInformationView,
|
DomainYourContactInformationView,
|
||||||
DomainSecurityEmailView,
|
DomainSecurityEmailView,
|
||||||
|
|
|
@ -22,10 +22,11 @@ from registrar.models import (
|
||||||
)
|
)
|
||||||
|
|
||||||
from ..forms import (
|
from ..forms import (
|
||||||
DomainAddUserForm,
|
|
||||||
NameserverFormset,
|
|
||||||
DomainSecurityEmailForm,
|
|
||||||
ContactForm,
|
ContactForm,
|
||||||
|
DomainOrgNameAddressForm,
|
||||||
|
DomainAddUserForm,
|
||||||
|
DomainSecurityEmailForm,
|
||||||
|
NameserverFormset,
|
||||||
)
|
)
|
||||||
from ..utility.email import send_templated_email, EmailSendingError
|
from ..utility.email import send_templated_email, EmailSendingError
|
||||||
from .utility import DomainPermissionView, DomainInvitationPermissionDeleteView
|
from .utility import DomainPermissionView, DomainInvitationPermissionDeleteView
|
||||||
|
@ -41,6 +42,47 @@ class DomainView(DomainPermissionView):
|
||||||
template_name = "domain_detail.html"
|
template_name = "domain_detail.html"
|
||||||
|
|
||||||
|
|
||||||
|
class DomainOrgNameAddressView(DomainPermissionView, FormMixin):
|
||||||
|
"""Organization name and mailing address view"""
|
||||||
|
|
||||||
|
model = Domain
|
||||||
|
template_name = "domain_org_name_address.html"
|
||||||
|
context_object_name = "domain"
|
||||||
|
form_class = DomainOrgNameAddressForm
|
||||||
|
|
||||||
|
def get_form_kwargs(self, *args, **kwargs):
|
||||||
|
"""Add domain_info.organization_name instance to make a bound form."""
|
||||||
|
form_kwargs = super().get_form_kwargs(*args, **kwargs)
|
||||||
|
form_kwargs["instance"] = self.get_object().domain_info
|
||||||
|
return form_kwargs
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
"""Redirect to the overview page for the domain."""
|
||||||
|
return reverse("domain-org-name-address", kwargs={"pk": self.object.pk})
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
"""Form submission posts to this view.
|
||||||
|
|
||||||
|
This post method harmonizes using DetailView and FormMixin together.
|
||||||
|
"""
|
||||||
|
self.object = self.get_object()
|
||||||
|
form = self.get_form()
|
||||||
|
if form.is_valid():
|
||||||
|
return self.form_valid(form)
|
||||||
|
else:
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
"""The form is valid, save the organization name and mailing address."""
|
||||||
|
form.save()
|
||||||
|
|
||||||
|
messages.success(
|
||||||
|
self.request, "The organization name and mailing address has been updated."
|
||||||
|
)
|
||||||
|
# superclass has the redirect
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class DomainAuthorizingOfficialView(DomainPermissionView, FormMixin):
|
class DomainAuthorizingOfficialView(DomainPermissionView, FormMixin):
|
||||||
|
|
||||||
"""Domain authorizing official editing view."""
|
"""Domain authorizing official editing view."""
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue