diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 7777245fe..fbb8a82bf 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1,5 +1,6 @@ import logging from django import forms +from django.db.models.functions import Concat from django.http import HttpResponse from django.shortcuts import redirect from django_fsm import get_available_FIELD_transitions @@ -11,7 +12,7 @@ from django.http.response import HttpResponseRedirect from django.urls import reverse from epplibwrapper.errors import ErrorCode, RegistryError from registrar.models.domain import Domain -from registrar.models.utility.admin_sort_fields import AdminSortFields +from registrar.models.user import User from registrar.utility import csv_export from . import models from auditlog.models import LogEntry # type: ignore @@ -45,7 +46,44 @@ class CustomLogEntryAdmin(LogEntryAdmin): add_form_template = "admin/change_form_no_submit.html" -class AuditedAdmin(admin.ModelAdmin, AdminSortFields): +class AdminSortFields: + def get_queryset(db_field): + """This is a helper function for formfield_for_manytomany and formfield_for_foreignkey""" + # customize sorting + if db_field.name in ( + "other_contacts", + "authorizing_official", + "submitter", + ): + # Sort contacts by first_name, then last_name, then email + return models.Contact.objects.all().order_by(Concat("first_name", "last_name", "email")) + elif db_field.name in ("current_websites", "alternative_domains"): + # sort web sites + return models.Website.objects.all().order_by("website") + elif db_field.name in ( + "creator", + "user", + "investigator", + ): + # Sort users by first_name, then last_name, then email + return models.User.objects.all().order_by(Concat("first_name", "last_name", "email")) + elif db_field.name in ( + "domain", + "approved_domain", + ): + # Sort domains by name + return models.Domain.objects.all().order_by("name") + elif db_field.name in ("requested_domain",): + # Sort draft domains by name + return models.DraftDomain.objects.all().order_by("name") + elif db_field.name in ("domain_application",): + # Sort domain applications by name + return models.DomainApplication.objects.all().order_by("requested_domain__name") + else: + return None + + +class AuditedAdmin(admin.ModelAdmin): """Custom admin to make auditing easier.""" def history_view(self, request, object_id, extra_context=None): @@ -58,10 +96,27 @@ class AuditedAdmin(admin.ModelAdmin, AdminSortFields): ) ) + def formfield_for_manytomany(self, db_field, request, **kwargs): + """customize the behavior of formfields with manytomany relationships. the customized + behavior includes sorting of objects in lists as well as customizing helper text""" + queryset = AdminSortFields.get_queryset(db_field) + if queryset: + kwargs["queryset"] = queryset + formfield = super().formfield_for_manytomany(db_field, request, **kwargs) + # customize the help text for all formfields for manytomany + formfield.help_text = ( + formfield.help_text + + " If more than one value is selected, the change/delete/view actions will be disabled." + ) + return formfield + def formfield_for_foreignkey(self, db_field, request, **kwargs): - """Used to sort dropdown fields alphabetically but can be expanded upon""" - form_field = super().formfield_for_foreignkey(db_field, request, **kwargs) - return self.form_field_order_helper(form_field, db_field) + """customize the behavior of formfields with foreign key relationships. this will customize + the behavior of selects. customized behavior includes sorting of objects in list""" + queryset = AdminSortFields.get_queryset(db_field) + if queryset: + kwargs["queryset"] = queryset + return super().formfield_for_foreignkey(db_field, request, **kwargs) class ListHeaderAdmin(AuditedAdmin): @@ -118,15 +173,6 @@ class ListHeaderAdmin(AuditedAdmin): ) return filters - # customize the help_text for all formfields for manytomany - def formfield_for_manytomany(self, db_field, request, **kwargs): - formfield = super().formfield_for_manytomany(db_field, request, **kwargs) - formfield.help_text = ( - formfield.help_text - + " If more than one value is selected, the change/delete/view actions will be disabled." - ) - return formfield - class UserContactInline(admin.StackedInline): """Edit a user's profile on the user page.""" @@ -221,6 +267,10 @@ class MyUserAdmin(BaseUserAdmin): "groups", ) + # this ordering effects the ordering of results + # in autocomplete_fields for user + ordering = ["first_name", "last_name", "email"] + # Let's define First group # (which should in theory be the ONLY group) def group(self, obj): @@ -489,12 +539,8 @@ class DomainInformationAdmin(ListHeaderAdmin): # to activate the edit/delete/view buttons filter_horizontal = ("other_contacts",) - # lists in filter_horizontal are not sorted properly, sort them - # by first_name - def formfield_for_manytomany(self, db_field, request, **kwargs): - if db_field.name in ("other_contacts",): - kwargs["queryset"] = models.Contact.objects.all().order_by("first_name") # Sort contacts - return super().formfield_for_manytomany(db_field, request, **kwargs) + # Table ordering + ordering = ["domain__name"] def get_readonly_fields(self, request, obj=None): """Set the read-only state on form elements. @@ -547,6 +593,27 @@ class DomainApplicationAdmin(ListHeaderAdmin): """Custom domain applications admin class.""" + class InvestigatorFilter(admin.SimpleListFilter): + """Custom investigator filter that only displays users with the manager role""" + + title = "investigator" + # Match the old param name to avoid unnecessary refactoring + parameter_name = "investigator__id__exact" + + def lookups(self, request, model_admin): + """Lookup reimplementation, gets users of is_staff. + Returns a list of tuples consisting of (user.id, user) + """ + privileged_users = User.objects.filter(is_staff=True).order_by("first_name", "last_name", "email") + return [(user.id, user) for user in privileged_users] + + def queryset(self, request, queryset): + """Custom queryset implementation, filters by investigator""" + if self.value() is None: + return queryset + else: + return queryset.filter(investigator__id__exact=self.value()) + # Columns list_display = [ "requested_domain", @@ -558,7 +625,7 @@ class DomainApplicationAdmin(ListHeaderAdmin): ] # Filters - list_filter = ("status", "organization_type", "investigator") + list_filter = ("status", "organization_type", InvestigatorFilter) # Search search_fields = [ @@ -634,6 +701,9 @@ class DomainApplicationAdmin(ListHeaderAdmin): filter_horizontal = ("current_websites", "alternative_domains", "other_contacts") + # Table ordering + ordering = ["requested_domain__name"] + # lists in filter_horizontal are not sorted properly, sort them # by website def formfield_for_manytomany(self, db_field, request, **kwargs): @@ -641,6 +711,13 @@ class DomainApplicationAdmin(ListHeaderAdmin): kwargs["queryset"] = models.Website.objects.all().order_by("website") # Sort websites return super().formfield_for_manytomany(db_field, request, **kwargs) + def formfield_for_foreignkey(self, db_field, request, **kwargs): + # Removes invalid investigator options from the investigator dropdown + if db_field.name == "investigator": + kwargs["queryset"] = User.objects.filter(is_staff=True) + return db_field.formfield(**kwargs) + return super().formfield_for_foreignkey(db_field, request, **kwargs) + # Trigger action when a fieldset is changed def save_model(self, request, obj, form, change): if obj and obj.creator.status != models.User.RESTRICTED: @@ -774,12 +851,27 @@ class DomainInformationInline(admin.StackedInline): # to activate the edit/delete/view buttons filter_horizontal = ("other_contacts",) - # lists in filter_horizontal are not sorted properly, sort them - # by first_name def formfield_for_manytomany(self, db_field, request, **kwargs): - if db_field.name in ("other_contacts",): - kwargs["queryset"] = models.Contact.objects.all().order_by("first_name") # Sort contacts - return super().formfield_for_manytomany(db_field, request, **kwargs) + """customize the behavior of formfields with manytomany relationships. the customized + behavior includes sorting of objects in lists as well as customizing helper text""" + queryset = AdminSortFields.get_queryset(db_field) + if queryset: + kwargs["queryset"] = queryset + formfield = super().formfield_for_manytomany(db_field, request, **kwargs) + # customize the help text for all formfields for manytomany + formfield.help_text = ( + formfield.help_text + + " If more than one value is selected, the change/delete/view actions will be disabled." + ) + return formfield + + def formfield_for_foreignkey(self, db_field, request, **kwargs): + """customize the behavior of formfields with foreign key relationships. this will customize + the behavior of selects. customized behavior includes sorting of objects in list""" + queryset = AdminSortFields.get_queryset(db_field) + if queryset: + kwargs["queryset"] = queryset + return super().formfield_for_foreignkey(db_field, request, **kwargs) def get_readonly_fields(self, request, obj=None): return DomainInformationAdmin.get_readonly_fields(self, request, obj=None) @@ -797,6 +889,10 @@ class DomainAdmin(ListHeaderAdmin): "state", ] + # this ordering effects the ordering of results + # in autocomplete_fields for domain + ordering = ["name"] + def organization_type(self, obj): return obj.domain_info.get_organization_type_display() @@ -811,6 +907,9 @@ class DomainAdmin(ListHeaderAdmin): change_list_template = "django/admin/domain_change_list.html" readonly_fields = ["state", "expiration_date"] + # Table ordering + ordering = ["name"] + def export_data_type(self, request): # match the CSV example with all the fields response = HttpResponse(content_type="text/csv") diff --git a/src/registrar/forms/application_wizard.py b/src/registrar/forms/application_wizard.py index 5310c4610..e0bc5fe52 100644 --- a/src/registrar/forms/application_wizard.py +++ b/src/registrar/forms/application_wizard.py @@ -329,10 +329,6 @@ class AuthorizingOfficialForm(RegistrarForm): label="First name / given name", error_messages={"required": ("Enter the first name / given name of your authorizing official.")}, ) - middle_name = forms.CharField( - required=False, - label="Middle name (optional)", - ) last_name = forms.CharField( label="Last name / family name", error_messages={"required": ("Enter the last name / family name of your authorizing official.")}, @@ -350,10 +346,6 @@ class AuthorizingOfficialForm(RegistrarForm): label="Email", error_messages={"invalid": ("Enter an email address in the required format, like name@example.com.")}, ) - phone = PhoneNumberField( - label="Phone", - error_messages={"required": "Enter the phone number for your authorizing official."}, - ) class CurrentSitesForm(RegistrarForm): @@ -618,6 +610,12 @@ class NoOtherContactsForm(RegistrarForm): "we can contact to help us assess your eligibility for a .gov domain." ), widget=forms.Textarea(), + validators=[ + MaxLengthValidator( + 1000, + message="Response must be less than 1000 characters.", + ) + ], ) diff --git a/src/registrar/forms/domain.py b/src/registrar/forms/domain.py index ac96393b4..5977449c3 100644 --- a/src/registrar/forms/domain.py +++ b/src/registrar/forms/domain.py @@ -213,6 +213,9 @@ class AuthorizingOfficialContactForm(ContactForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + # Overriding bc phone not required in this form + self.fields["phone"] = forms.IntegerField(required=False) + # Set custom error messages self.fields["first_name"].error_messages = { "required": "Enter the first name / given name of your authorizing official." @@ -227,7 +230,6 @@ class AuthorizingOfficialContactForm(ContactForm): self.fields["email"].error_messages = { "required": "Enter an email address in the required format, like name@example.com." } - self.fields["phone"].error_messages["required"] = "Enter a phone number for your authorizing official." class DomainSecurityEmailForm(forms.Form): diff --git a/src/registrar/models/contact.py b/src/registrar/models/contact.py index 6b3b6ddb2..06cf83887 100644 --- a/src/registrar/models/contact.py +++ b/src/registrar/models/contact.py @@ -64,7 +64,7 @@ class Contact(TimeStampedModel): super().save(*args, **kwargs) # Update the related User object's first_name and last_name - if self.user: + if self.user and (not self.user.first_name or not self.user.last_name): self.user.first_name = self.first_name self.user.last_name = self.last_name self.user.save() diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 7c0566774..51c074a7d 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -203,7 +203,7 @@ class Domain(TimeStampedModel, DomainHelper): return self._get_property("cr_date") @creation_date.setter # type: ignore - def creation_date(self, ex_date: date): + def creation_date(self, cr_date: date): """ Direct setting of the creation date in the registry is not implemented. diff --git a/src/registrar/models/domain_application.py b/src/registrar/models/domain_application.py index 9dcafdad5..95caa41a1 100644 --- a/src/registrar/models/domain_application.py +++ b/src/registrar/models/domain_application.py @@ -594,7 +594,12 @@ class DomainApplication(TimeStampedModel): @transition( field="status", - source=[ApplicationStatus.STARTED, ApplicationStatus.ACTION_NEEDED, ApplicationStatus.WITHDRAWN], + source=[ + ApplicationStatus.STARTED, + ApplicationStatus.IN_REVIEW, + ApplicationStatus.ACTION_NEEDED, + ApplicationStatus.WITHDRAWN, + ], target=ApplicationStatus.SUBMITTED, ) def submit(self): @@ -626,7 +631,17 @@ class DomainApplication(TimeStampedModel): "emails/submission_confirmation_subject.txt", ) - @transition(field="status", source=ApplicationStatus.SUBMITTED, target=ApplicationStatus.IN_REVIEW) + @transition( + field="status", + source=[ + ApplicationStatus.SUBMITTED, + ApplicationStatus.ACTION_NEEDED, + ApplicationStatus.APPROVED, + ApplicationStatus.REJECTED, + ApplicationStatus.INELIGIBLE, + ], + target=ApplicationStatus.IN_REVIEW, + ) def in_review(self): """Investigate an application that has been submitted. @@ -640,7 +655,12 @@ class DomainApplication(TimeStampedModel): @transition( field="status", - source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.REJECTED], + source=[ + ApplicationStatus.IN_REVIEW, + ApplicationStatus.APPROVED, + ApplicationStatus.REJECTED, + ApplicationStatus.INELIGIBLE, + ], target=ApplicationStatus.ACTION_NEEDED, ) def action_needed(self): @@ -659,8 +679,8 @@ class DomainApplication(TimeStampedModel): source=[ ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW, + ApplicationStatus.ACTION_NEEDED, ApplicationStatus.REJECTED, - ApplicationStatus.INELIGIBLE, ], target=ApplicationStatus.APPROVED, ) @@ -697,7 +717,7 @@ class DomainApplication(TimeStampedModel): @transition( field="status", - source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW], + source=[ApplicationStatus.SUBMITTED, ApplicationStatus.IN_REVIEW, ApplicationStatus.ACTION_NEEDED], target=ApplicationStatus.WITHDRAWN, ) def withdraw(self): @@ -710,7 +730,7 @@ class DomainApplication(TimeStampedModel): @transition( field="status", - source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.APPROVED], + source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.ACTION_NEEDED, ApplicationStatus.APPROVED], target=ApplicationStatus.REJECTED, conditions=[domain_is_not_active], ) @@ -720,12 +740,16 @@ class DomainApplication(TimeStampedModel): As side effects this will delete the domain and domain_information (will cascade), and send an email notification.""" if self.status == self.ApplicationStatus.APPROVED: - domain_state = self.approved_domain.state - # Only reject if it exists on EPP - if domain_state != Domain.State.UNKNOWN: - self.approved_domain.deletedInEpp() - self.approved_domain.delete() - self.approved_domain = None + try: + domain_state = self.approved_domain.state + # Only reject if it exists on EPP + if domain_state != Domain.State.UNKNOWN: + self.approved_domain.deletedInEpp() + self.approved_domain.delete() + self.approved_domain = None + except Exception as err: + logger.error(err) + logger.error("Can't query an approved domain while attempting a DA reject()") self._send_status_update_email( "action needed", @@ -735,7 +759,12 @@ class DomainApplication(TimeStampedModel): @transition( field="status", - source=[ApplicationStatus.IN_REVIEW, ApplicationStatus.APPROVED], + source=[ + ApplicationStatus.IN_REVIEW, + ApplicationStatus.ACTION_NEEDED, + ApplicationStatus.APPROVED, + ApplicationStatus.REJECTED, + ], target=ApplicationStatus.INELIGIBLE, conditions=[domain_is_not_active], ) @@ -749,12 +778,16 @@ class DomainApplication(TimeStampedModel): and domain_information (will cascade) when they exist.""" if self.status == self.ApplicationStatus.APPROVED: - domain_state = self.approved_domain.state - # Only reject if it exists on EPP - if domain_state != Domain.State.UNKNOWN: - self.approved_domain.deletedInEpp() - self.approved_domain.delete() - self.approved_domain = None + try: + domain_state = self.approved_domain.state + # Only reject if it exists on EPP + if domain_state != Domain.State.UNKNOWN: + self.approved_domain.deletedInEpp() + self.approved_domain.delete() + self.approved_domain = None + except Exception as err: + logger.error(err) + logger.error("Can't query an approved domain while attempting a DA reject_with_prejudice()") self.creator.restrict_user() diff --git a/src/registrar/models/user_group.py b/src/registrar/models/user_group.py index 0f12a2e84..a733239c7 100644 --- a/src/registrar/models/user_group.py +++ b/src/registrar/models/user_group.py @@ -114,7 +114,7 @@ class UserGroup(Group): ) cisa_analysts_group.save() - logger.debug("CISA Analyt permissions added to group " + cisa_analysts_group.name) + logger.debug("CISA Analyst permissions added to group " + cisa_analysts_group.name) except Exception as e: logger.error(f"Error creating analyst permissions group: {e}") diff --git a/src/registrar/models/utility/admin_form_order_helper.py b/src/registrar/models/utility/admin_form_order_helper.py deleted file mode 100644 index acc26db11..000000000 --- a/src/registrar/models/utility/admin_form_order_helper.py +++ /dev/null @@ -1,63 +0,0 @@ -import logging -from typing import Dict -from django.forms import ModelChoiceField - -logger = logging.getLogger(__name__) - - -class SortingDict: - """Stores a sorting dictionary object""" - - _sorting_dict: Dict[type, type] = {} - - def __init__(self, model_list, sort_list): - self._sorting_dict = { - "dropDownSelected": self.convert_list_to_dict(model_list), - "sortBy": sort_list, - } - - # Used in __init__ for model_list for performance reasons - def convert_list_to_dict(self, value_list): - """Used internally to convert model_list to a dictionary""" - return {item: item for item in value_list} - - def get_dict(self): - """Grabs the associated dictionary item, - has two fields: 'dropDownSelected': model_list and 'sortBy': sort_list""" - # This should never happen so we need to log this - if self._sorting_dict is None: - raise ValueError("_sorting_dict was None") - return self._sorting_dict - - -class AdminFormOrderHelper: - """A helper class to order a dropdown field in Django Admin, - takes the fields you want to order by as an array""" - - # Used to keep track of how we want to order_by certain FKs - _sorting_list: list[SortingDict] = [] - - def __init__(self, sort: list[SortingDict]): - self._sorting_list = sort - - def get_ordered_form_field(self, form_field, db_field) -> ModelChoiceField | None: - """Orders the queryset for a ModelChoiceField - based on the order_by_dict dictionary""" - _order_by_list = [] - - for item in self._sorting_list: - item_dict = item.get_dict() - drop_down_selected = item_dict.get("dropDownSelected") - sort_by = item_dict.get("sortBy") - - if db_field.name in drop_down_selected: - _order_by_list = sort_by - # Exit loop when order_by_list is found - break - - # Only order if we choose to do so - # noqa for the linter... reduces readability otherwise - if _order_by_list is not None and _order_by_list != []: # noqa - form_field.queryset = form_field.queryset.order_by(*_order_by_list) - - return form_field diff --git a/src/registrar/models/utility/admin_sort_fields.py b/src/registrar/models/utility/admin_sort_fields.py deleted file mode 100644 index 8037c6df0..000000000 --- a/src/registrar/models/utility/admin_sort_fields.py +++ /dev/null @@ -1,27 +0,0 @@ -from registrar.models.utility.admin_form_order_helper import ( - AdminFormOrderHelper, - SortingDict, -) - - -class AdminSortFields: - # Used to keep track of how we want to order_by certain FKs - foreignkey_orderby_dict: list[SortingDict] = [ - # foreign_key - order_by - # Handles fields that are sorted by 'first_name / last_name - SortingDict( - ["submitter", "authorizing_official", "investigator", "creator", "user"], - ["first_name", "last_name"], - ), - # Handles fields that are sorted by 'name' - SortingDict(["domain", "requested_domain"], ["name"]), - SortingDict(["domain_application"], ["requested_domain__name"]), - ] - - # For readability purposes, but can be replaced with a one liner - def form_field_order_helper(self, form_field, db_field): - """A shorthand for AdminFormOrderHelper(foreignkey_orderby_dict) - .get_ordered_form_field(form_field, db_field)""" - - form = AdminFormOrderHelper(self.foreignkey_orderby_dict) - return form.get_ordered_form_field(form_field, db_field) diff --git a/src/registrar/templates/403.html b/src/registrar/templates/403.html index 08057dfba..91652d807 100644 --- a/src/registrar/templates/403.html +++ b/src/registrar/templates/403.html @@ -23,7 +23,7 @@ {% endif %}

You must be an authorized user and need to be signed in to view this page. - Would you like to try logging in again? + Try logging in again.

If you'd like help with this error contact us. diff --git a/src/registrar/templates/application_authorizing_official.html b/src/registrar/templates/application_authorizing_official.html index 08df404a3..9c364f612 100644 --- a/src/registrar/templates/application_authorizing_official.html +++ b/src/registrar/templates/application_authorizing_official.html @@ -25,17 +25,11 @@ {% input_with_errors forms.0.first_name %} - {% input_with_errors forms.0.middle_name %} - {% input_with_errors forms.0.last_name %} {% input_with_errors forms.0.title %} {% input_with_errors forms.0.email %} - {% with add_class="usa-input--medium" %} - {% input_with_errors forms.0.phone %} - {% endwith %} - {% endblock %} diff --git a/src/registrar/templates/application_done.html b/src/registrar/templates/application_done.html index 91f9588b7..6d7bf6f43 100644 --- a/src/registrar/templates/application_done.html +++ b/src/registrar/templates/application_done.html @@ -1,5 +1,6 @@ {% extends 'base.html' %} {% load static %} +{% load url_helpers %} {% block title %}Thanks for your domain request! | {% endblock %} @@ -14,21 +15,22 @@ />

Thanks for your domain request!

-

We'll email a copy of your request to you.

+

We’ll email a copy of your request to you.

Next steps in this process

-

We'll review your request. This usually takes 20 business days. During - this review we'll verify that your:

+

We’ll review your request. This usually takes 20 business days. During + this review we’ll verify that:

-

You can check the status - of your request at any time. We'll email you with any questions or when we - complete our review.

+

We’ll email you if we have questions and when we complete our review. You can check the status + of your request at any time on the registrar homepage.

+ +

Contact us if you need help during this process.

{% endblock %} diff --git a/src/registrar/templates/application_form.html b/src/registrar/templates/application_form.html index cec2416fb..c34ddf5bc 100644 --- a/src/registrar/templates/application_form.html +++ b/src/registrar/templates/application_form.html @@ -105,7 +105,7 @@ aria-describedby="Are you sure you want to submit a domain request?" data-force-action > - {% include 'includes/modal.html' with modal_heading=modal_heading|safe modal_description="Once you submit this request, you won’t be able to make further edits until it’s reviewed by our staff. You’ll only be able to withdraw your request." modal_button=modal_button|safe %} + {% include 'includes/modal.html' with modal_heading=modal_heading|safe modal_description="Once you submit this request, you won’t be able to edit it until we review it. You’ll only be able to withdraw your request." modal_button=modal_button|safe %} {% block after_form_content %}{% endblock %} diff --git a/src/registrar/templates/application_no_other_contacts.html b/src/registrar/templates/application_no_other_contacts.html index 736454831..69217bfc1 100644 --- a/src/registrar/templates/application_no_other_contacts.html +++ b/src/registrar/templates/application_no_other_contacts.html @@ -2,5 +2,7 @@ {% load static field_helpers %} {% block form_fields %} - {% input_with_errors forms.0.no_other_contacts_rationale %} + {% with attr_maxlength=1000 %} + {% input_with_errors forms.0.no_other_contacts_rationale %} + {% endwith %} {% endblock %} diff --git a/src/registrar/templates/domain_authorizing_official.html b/src/registrar/templates/domain_authorizing_official.html index 57a315e86..e7fc12a5e 100644 --- a/src/registrar/templates/domain_authorizing_official.html +++ b/src/registrar/templates/domain_authorizing_official.html @@ -19,18 +19,12 @@ {% input_with_errors form.first_name %} - {% input_with_errors form.middle_name %} - {% input_with_errors form.last_name %} {% input_with_errors form.title %} {% input_with_errors form.email %} - {% input_with_errors form.phone %} - - -