diff --git a/src/registrar/admin.py b/src/registrar/admin.py index b0399096a..d07551a01 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -48,6 +48,34 @@ class MyUserAdminForm(UserChangeForm): "user_permissions": NoAutocompleteFilteredSelectMultiple("user_permissions", False), } + def __init__(self, *args, **kwargs): + """Custom init to modify the user form""" + super(MyUserAdminForm, self).__init__(*args, **kwargs) + self._override_base_help_texts() + + def _override_base_help_texts(self): + """ + Used to override pre-existing help texts in AbstractUser. + This is done to avoid modifying the base AbstractUser class. + """ + is_superuser = self.fields.get("is_superuser") + is_staff = self.fields.get("is_staff") + password = self.fields.get("password") + + if is_superuser is not None: + is_superuser.help_text = "For development purposes only; provides superuser access on the database level." + + if is_staff is not None: + is_staff.help_text = "Designates whether the user can log in to this admin site." + + if password is not None: + # Link is copied from the base implementation of UserChangeForm. + link = f"../../{self.instance.pk}/password/" + password.help_text = ( + "Raw passwords are not stored, so they will not display here. " + f'You can change the password using this form.' + ) + class DomainInformationAdminForm(forms.ModelForm): """This form utilizes the custom widget for its class's ManyToMany UIs.""" @@ -535,7 +563,7 @@ class MyUserAdmin(BaseUserAdmin): analyst_fieldsets = ( ( None, - {"fields": ("password", "status", "verification_type")}, + {"fields": ("status", "verification_type",)}, ), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ( @@ -561,7 +589,6 @@ class MyUserAdmin(BaseUserAdmin): # NOT all fields are readonly for admin, otherwise we would have # set this at the permissions level. The exception is 'status' analyst_readonly_fields = [ - "password", "Personal Info", "first_name", "last_name", @@ -1727,21 +1754,27 @@ class DomainAdmin(ListHeaderAdmin): if domain is not None and hasattr(domain, "domain_info"): extra_context["original_object"] = domain.domain_info + extra_context["state_help_message"] = Domain.State.get_admin_help_text(domain.state) + extra_context["domain_state"] = domain.get_state_display() + # Pass in what the an extended expiration date would be for the expiration date modal - years_to_extend_by = self._get_calculated_years_for_exp_date(domain) - try: - curr_exp_date = domain.registry_expiration_date - except KeyError: - # No expiration date was found. Return none. - extra_context["extended_expiration_date"] = None - return super().changeform_view(request, object_id, form_url, extra_context) - new_date = curr_exp_date + relativedelta(years=years_to_extend_by) - extra_context["extended_expiration_date"] = new_date - else: - extra_context["extended_expiration_date"] = None + self._set_expiration_date_context(domain, extra_context) return super().changeform_view(request, object_id, form_url, extra_context) + def _set_expiration_date_context(self, domain, extra_context): + """Given a domain, calculate the an extended expiration date + from the current registry expiration date.""" + years_to_extend_by = self._get_calculated_years_for_exp_date(domain) + try: + curr_exp_date = domain.registry_expiration_date + except KeyError: + # No expiration date was found. Return none. + extra_context["extended_expiration_date"] = None + else: + new_date = curr_exp_date + relativedelta(years=years_to_extend_by) + extra_context["extended_expiration_date"] = new_date + def response_change(self, request, obj): # Create dictionary of action functions ACTION_FUNCTIONS = { diff --git a/src/registrar/migrations/0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py b/src/registrar/migrations/0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py new file mode 100644 index 000000000..edbadcb4e --- /dev/null +++ b/src/registrar/migrations/0087_alter_domain_deleted_alter_domain_expiration_date_and_more.py @@ -0,0 +1,1233 @@ +# Generated by Django 4.2.10 on 2024-04-23 15:03 + +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import django_fsm + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0086_domaininformation_updated_federal_agency_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="domain", + name="deleted", + field=models.DateField( + editable=False, + help_text='Will appear blank unless the domain is in "deleted" state', + null=True, + verbose_name="deleted on", + ), + ), + migrations.AlterField( + model_name="domain", + name="expiration_date", + field=models.DateField(help_text="Date the domain expires in the registry", null=True), + ), + migrations.AlterField( + model_name="domain", + name="first_ready", + field=models.DateField( + editable=False, + help_text='Date when this domain first moved into "ready" state; date will never change', + null=True, + verbose_name="first ready on", + ), + ), + migrations.AlterField( + model_name="domain", + name="state", + field=django_fsm.FSMField( + choices=[ + ("unknown", "Unknown"), + ("dns needed", "Dns needed"), + ("ready", "Ready"), + ("on hold", "On hold"), + ("deleted", "Deleted"), + ], + default="unknown", + help_text=" ", + max_length=21, + protected=True, + verbose_name="domain state", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="about_your_organization", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="address_line1", + field=models.CharField(blank=True, null=True, verbose_name="address line 1"), + ), + migrations.AlterField( + model_name="domaininformation", + name="address_line2", + field=models.CharField(blank=True, null=True, verbose_name="address line 2"), + ), + migrations.AlterField( + model_name="domaininformation", + name="anything_else", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="city", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="creator", + field=models.ForeignKey( + help_text="Person who submitted the domain request", + on_delete=django.db.models.deletion.PROTECT, + related_name="information_created", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="domain", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="domain_info", + to="registrar.domain", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="domain_request", + field=models.OneToOneField( + blank=True, + help_text="Request associated with this domain", + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="DomainRequest_info", + to="registrar.domainrequest", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="federal_agency", + field=models.CharField( + blank=True, + choices=[ + ( + "Administrative Conference of the United States", + "Administrative Conference of the United States", + ), + ("Advisory Council on Historic Preservation", "Advisory Council on Historic Preservation"), + ("American Battle Monuments Commission", "American Battle Monuments Commission"), + ("AMTRAK", "AMTRAK"), + ("Appalachian Regional Commission", "Appalachian Regional Commission"), + ( + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + ), + ("Appraisal Subcommittee", "Appraisal Subcommittee"), + ("Architect of the Capitol", "Architect of the Capitol"), + ("Armed Forces Retirement Home", "Armed Forces Retirement Home"), + ( + "Barry Goldwater Scholarship and Excellence in Education Foundation", + "Barry Goldwater Scholarship and Excellence in Education Foundation", + ), + ( + "Barry Goldwater Scholarship and Excellence in Education Program", + "Barry Goldwater Scholarship and Excellence in Education Program", + ), + ("Central Intelligence Agency", "Central Intelligence Agency"), + ("Chemical Safety Board", "Chemical Safety Board"), + ("Christopher Columbus Fellowship Foundation", "Christopher Columbus Fellowship Foundation"), + ("Civil Rights Cold Case Records Review Board", "Civil Rights Cold Case Records Review Board"), + ( + "Commission for the Preservation of America's Heritage Abroad", + "Commission for the Preservation of America's Heritage Abroad", + ), + ("Commission of Fine Arts", "Commission of Fine Arts"), + ( + "Committee for Purchase From People Who Are Blind or Severely Disabled", + "Committee for Purchase From People Who Are Blind or Severely Disabled", + ), + ("Commodity Futures Trading Commission", "Commodity Futures Trading Commission"), + ("Congressional Budget Office", "Congressional Budget Office"), + ("Consumer Financial Protection Bureau", "Consumer Financial Protection Bureau"), + ("Consumer Product Safety Commission", "Consumer Product Safety Commission"), + ("Corporation for National & Community Service", "Corporation for National & Community Service"), + ( + "Corporation for National and Community Service", + "Corporation for National and Community Service", + ), + ( + "Council of Inspectors General on Integrity and Efficiency", + "Council of Inspectors General on Integrity and Efficiency", + ), + ("Court Services and Offender Supervision", "Court Services and Offender Supervision"), + ("Cyberspace Solarium Commission", "Cyberspace Solarium Commission"), + ( + "DC Court Services and Offender Supervision Agency", + "DC Court Services and Offender Supervision Agency", + ), + ("DC Pre-trial Services", "DC Pre-trial Services"), + ("Defense Nuclear Facilities Safety Board", "Defense Nuclear Facilities Safety Board"), + ("Delta Regional Authority", "Delta Regional Authority"), + ("Denali Commission", "Denali Commission"), + ("Department of Agriculture", "Department of Agriculture"), + ("Department of Commerce", "Department of Commerce"), + ("Department of Defense", "Department of Defense"), + ("Department of Education", "Department of Education"), + ("Department of Energy", "Department of Energy"), + ("Department of Health and Human Services", "Department of Health and Human Services"), + ("Department of Homeland Security", "Department of Homeland Security"), + ("Department of Housing and Urban Development", "Department of Housing and Urban Development"), + ("Department of Justice", "Department of Justice"), + ("Department of Labor", "Department of Labor"), + ("Department of State", "Department of State"), + ("Department of the Interior", "Department of the Interior"), + ("Department of the Treasury", "Department of the Treasury"), + ("Department of Transportation", "Department of Transportation"), + ("Department of Veterans Affairs", "Department of Veterans Affairs"), + ("Director of National Intelligence", "Director of National Intelligence"), + ("Dwight D. Eisenhower Memorial Commission", "Dwight D. Eisenhower Memorial Commission"), + ("Election Assistance Commission", "Election Assistance Commission"), + ("Environmental Protection Agency", "Environmental Protection Agency"), + ("Equal Employment Opportunity Commission", "Equal Employment Opportunity Commission"), + ("Executive Office of the President", "Executive Office of the President"), + ("Export-Import Bank of the United States", "Export-Import Bank of the United States"), + ("Export/Import Bank of the U.S.", "Export/Import Bank of the U.S."), + ("Farm Credit Administration", "Farm Credit Administration"), + ("Farm Credit System Insurance Corporation", "Farm Credit System Insurance Corporation"), + ("Federal Communications Commission", "Federal Communications Commission"), + ("Federal Deposit Insurance Corporation", "Federal Deposit Insurance Corporation"), + ("Federal Election Commission", "Federal Election Commission"), + ("Federal Energy Regulatory Commission", "Federal Energy Regulatory Commission"), + ( + "Federal Financial Institutions Examination Council", + "Federal Financial Institutions Examination Council", + ), + ("Federal Housing Finance Agency", "Federal Housing Finance Agency"), + ("Federal Judiciary", "Federal Judiciary"), + ("Federal Labor Relations Authority", "Federal Labor Relations Authority"), + ("Federal Maritime Commission", "Federal Maritime Commission"), + ("Federal Mediation and Conciliation Service", "Federal Mediation and Conciliation Service"), + ( + "Federal Mine Safety and Health Review Commission", + "Federal Mine Safety and Health Review Commission", + ), + ( + "Federal Permitting Improvement Steering Council", + "Federal Permitting Improvement Steering Council", + ), + ("Federal Reserve Board of Governors", "Federal Reserve Board of Governors"), + ("Federal Reserve System", "Federal Reserve System"), + ("Federal Trade Commission", "Federal Trade Commission"), + ("General Services Administration", "General Services Administration"), + ("gov Administration", "gov Administration"), + ("Government Accountability Office", "Government Accountability Office"), + ("Government Publishing Office", "Government Publishing Office"), + ("Gulf Coast Ecosystem Restoration Council", "Gulf Coast Ecosystem Restoration Council"), + ("Harry S Truman Scholarship Foundation", "Harry S Truman Scholarship Foundation"), + ("Harry S. Truman Scholarship Foundation", "Harry S. Truman Scholarship Foundation"), + ("Institute of Museum and Library Services", "Institute of Museum and Library Services"), + ("Institute of Peace", "Institute of Peace"), + ("Inter-American Foundation", "Inter-American Foundation"), + ( + "International Boundary and Water Commission: United States and Mexico", + "International Boundary and Water Commission: United States and Mexico", + ), + ( + "International Boundary Commission: United States and Canada", + "International Boundary Commission: United States and Canada", + ), + ( + "International Joint Commission: United States and Canada", + "International Joint Commission: United States and Canada", + ), + ("James Madison Memorial Fellowship Foundation", "James Madison Memorial Fellowship Foundation"), + ("Japan-United States Friendship Commission", "Japan-United States Friendship Commission"), + ("Japan-US Friendship Commission", "Japan-US Friendship Commission"), + ("John F. Kennedy Center for Performing Arts", "John F. Kennedy Center for Performing Arts"), + ( + "John F. Kennedy Center for the Performing Arts", + "John F. Kennedy Center for the Performing Arts", + ), + ("Legal Services Corporation", "Legal Services Corporation"), + ("Legislative Branch", "Legislative Branch"), + ("Library of Congress", "Library of Congress"), + ("Marine Mammal Commission", "Marine Mammal Commission"), + ( + "Medicaid and CHIP Payment and Access Commission", + "Medicaid and CHIP Payment and Access Commission", + ), + ("Medical Payment Advisory Commission", "Medical Payment Advisory Commission"), + ("Medicare Payment Advisory Commission", "Medicare Payment Advisory Commission"), + ("Merit Systems Protection Board", "Merit Systems Protection Board"), + ("Millennium Challenge Corporation", "Millennium Challenge Corporation"), + ( + "Morris K. Udall and Stewart L. Udall Foundation", + "Morris K. Udall and Stewart L. Udall Foundation", + ), + ("National Aeronautics and Space Administration", "National Aeronautics and Space Administration"), + ("National Archives and Records Administration", "National Archives and Records Administration"), + ("National Capital Planning Commission", "National Capital Planning Commission"), + ("National Council on Disability", "National Council on Disability"), + ("National Credit Union Administration", "National Credit Union Administration"), + ("National Endowment for the Arts", "National Endowment for the Arts"), + ("National Endowment for the Humanities", "National Endowment for the Humanities"), + ( + "National Foundation on the Arts and the Humanities", + "National Foundation on the Arts and the Humanities", + ), + ("National Gallery of Art", "National Gallery of Art"), + ("National Indian Gaming Commission", "National Indian Gaming Commission"), + ("National Labor Relations Board", "National Labor Relations Board"), + ("National Mediation Board", "National Mediation Board"), + ("National Science Foundation", "National Science Foundation"), + ( + "National Security Commission on Artificial Intelligence", + "National Security Commission on Artificial Intelligence", + ), + ("National Transportation Safety Board", "National Transportation Safety Board"), + ( + "Networking Information Technology Research and Development", + "Networking Information Technology Research and Development", + ), + ("Non-Federal Agency", "Non-Federal Agency"), + ("Northern Border Regional Commission", "Northern Border Regional Commission"), + ("Nuclear Regulatory Commission", "Nuclear Regulatory Commission"), + ("Nuclear Safety Oversight Committee", "Nuclear Safety Oversight Committee"), + ("Nuclear Waste Technical Review Board", "Nuclear Waste Technical Review Board"), + ( + "Occupational Safety & Health Review Commission", + "Occupational Safety & Health Review Commission", + ), + ( + "Occupational Safety and Health Review Commission", + "Occupational Safety and Health Review Commission", + ), + ("Office of Compliance", "Office of Compliance"), + ("Office of Congressional Workplace Rights", "Office of Congressional Workplace Rights"), + ("Office of Government Ethics", "Office of Government Ethics"), + ("Office of Navajo and Hopi Indian Relocation", "Office of Navajo and Hopi Indian Relocation"), + ("Office of Personnel Management", "Office of Personnel Management"), + ("Open World Leadership Center", "Open World Leadership Center"), + ("Overseas Private Investment Corporation", "Overseas Private Investment Corporation"), + ("Peace Corps", "Peace Corps"), + ("Pension Benefit Guaranty Corporation", "Pension Benefit Guaranty Corporation"), + ("Postal Regulatory Commission", "Postal Regulatory Commission"), + ("Presidio Trust", "Presidio Trust"), + ("Privacy and Civil Liberties Oversight Board", "Privacy and Civil Liberties Oversight Board"), + ("Public Buildings Reform Board", "Public Buildings Reform Board"), + ( + "Public Defender Service for the District of Columbia", + "Public Defender Service for the District of Columbia", + ), + ("Railroad Retirement Board", "Railroad Retirement Board"), + ("Securities and Exchange Commission", "Securities and Exchange Commission"), + ("Selective Service System", "Selective Service System"), + ("Small Business Administration", "Small Business Administration"), + ("Smithsonian Institution", "Smithsonian Institution"), + ("Social Security Administration", "Social Security Administration"), + ("Social Security Advisory Board", "Social Security Advisory Board"), + ("Southeast Crescent Regional Commission", "Southeast Crescent Regional Commission"), + ("Southwest Border Regional Commission", "Southwest Border Regional Commission"), + ("State Justice Institute", "State Justice Institute"), + ("State, Local, and Tribal Government", "State, Local, and Tribal Government"), + ("Stennis Center for Public Service", "Stennis Center for Public Service"), + ("Surface Transportation Board", "Surface Transportation Board"), + ("Tennessee Valley Authority", "Tennessee Valley Authority"), + ("The Executive Office of the President", "The Executive Office of the President"), + ("The Intelligence Community", "The Intelligence Community"), + ("The Legislative Branch", "The Legislative Branch"), + ("The Supreme Court", "The Supreme Court"), + ( + "The United States World War One Centennial Commission", + "The United States World War One Centennial Commission", + ), + ("U.S. Access Board", "U.S. Access Board"), + ("U.S. Agency for Global Media", "U.S. Agency for Global Media"), + ("U.S. Agency for International Development", "U.S. Agency for International Development"), + ("U.S. Capitol Police", "U.S. Capitol Police"), + ("U.S. Chemical Safety Board", "U.S. Chemical Safety Board"), + ( + "U.S. China Economic and Security Review Commission", + "U.S. China Economic and Security Review Commission", + ), + ( + "U.S. Commission for the Preservation of Americas Heritage Abroad", + "U.S. Commission for the Preservation of Americas Heritage Abroad", + ), + ("U.S. Commission of Fine Arts", "U.S. Commission of Fine Arts"), + ("U.S. Commission on Civil Rights", "U.S. Commission on Civil Rights"), + ( + "U.S. Commission on International Religious Freedom", + "U.S. Commission on International Religious Freedom", + ), + ("U.S. Courts", "U.S. Courts"), + ("U.S. Department of Agriculture", "U.S. Department of Agriculture"), + ("U.S. Interagency Council on Homelessness", "U.S. Interagency Council on Homelessness"), + ("U.S. International Trade Commission", "U.S. International Trade Commission"), + ("U.S. Nuclear Waste Technical Review Board", "U.S. Nuclear Waste Technical Review Board"), + ("U.S. Office of Special Counsel", "U.S. Office of Special Counsel"), + ("U.S. Peace Corps", "U.S. Peace Corps"), + ("U.S. Postal Service", "U.S. Postal Service"), + ("U.S. Semiquincentennial Commission", "U.S. Semiquincentennial Commission"), + ("U.S. Trade and Development Agency", "U.S. Trade and Development Agency"), + ( + "U.S.-China Economic and Security Review Commission", + "U.S.-China Economic and Security Review Commission", + ), + ("Udall Foundation", "Udall Foundation"), + ("United States AbilityOne", "United States AbilityOne"), + ("United States Access Board", "United States Access Board"), + ("United States African Development Foundation", "United States African Development Foundation"), + ("United States Agency for Global Media", "United States Agency for Global Media"), + ("United States Arctic Research Commission", "United States Arctic Research Commission"), + ("United States Global Change Research Program", "United States Global Change Research Program"), + ("United States Holocaust Memorial Museum", "United States Holocaust Memorial Museum"), + ("United States Institute of Peace", "United States Institute of Peace"), + ( + "United States Interagency Council on Homelessness", + "United States Interagency Council on Homelessness", + ), + ( + "United States International Development Finance Corporation", + "United States International Development Finance Corporation", + ), + ("United States International Trade Commission", "United States International Trade Commission"), + ("United States Postal Service", "United States Postal Service"), + ("United States Senate", "United States Senate"), + ("United States Trade and Development Agency", "United States Trade and Development Agency"), + ( + "Utah Reclamation Mitigation and Conservation Commission", + "Utah Reclamation Mitigation and Conservation Commission", + ), + ("Vietnam Education Foundation", "Vietnam Education Foundation"), + ("Western Hemisphere Drug Policy Commission", "Western Hemisphere Drug Policy Commission"), + ( + "Woodrow Wilson International Center for Scholars", + "Woodrow Wilson International Center for Scholars", + ), + ("World War I Centennial Commission", "World War I Centennial Commission"), + ], + null=True, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="federal_type", + field=models.CharField( + blank=True, + choices=[("executive", "Executive"), ("judicial", "Judicial"), ("legislative", "Legislative")], + max_length=50, + null=True, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="federally_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="is_election_board", + field=models.BooleanField(blank=True, null=True, verbose_name="election office"), + ), + migrations.AlterField( + model_name="domaininformation", + name="no_other_contacts_rationale", + field=models.TextField( + blank=True, help_text="Required if creator does not list other employees", null=True + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="notes", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="organization_name", + field=models.CharField(blank=True, db_index=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="organization_type", + field=models.CharField( + blank=True, + choices=[ + ("federal", "Federal"), + ("interstate", "Interstate"), + ("state_or_territory", "State or territory"), + ("tribal", "Tribal"), + ("county", "County"), + ("city", "City"), + ("special_district", "Special district"), + ("school_district", "School district"), + ("state_or_territory_election", "State or territory - Election"), + ("tribal_election", "Tribal - Election"), + ("county_election", "County - Election"), + ("city_election", "City - Election"), + ("special_district_election", "Special district - Election"), + ], + help_text='"Election" appears after the org type if it\'s an election office.', + max_length=255, + null=True, + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="state_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="state_territory", + field=models.CharField( + blank=True, + choices=[ + ("AL", "Alabama (AL)"), + ("AK", "Alaska (AK)"), + ("AS", "American Samoa (AS)"), + ("AZ", "Arizona (AZ)"), + ("AR", "Arkansas (AR)"), + ("CA", "California (CA)"), + ("CO", "Colorado (CO)"), + ("CT", "Connecticut (CT)"), + ("DE", "Delaware (DE)"), + ("DC", "District of Columbia (DC)"), + ("FL", "Florida (FL)"), + ("GA", "Georgia (GA)"), + ("GU", "Guam (GU)"), + ("HI", "Hawaii (HI)"), + ("ID", "Idaho (ID)"), + ("IL", "Illinois (IL)"), + ("IN", "Indiana (IN)"), + ("IA", "Iowa (IA)"), + ("KS", "Kansas (KS)"), + ("KY", "Kentucky (KY)"), + ("LA", "Louisiana (LA)"), + ("ME", "Maine (ME)"), + ("MD", "Maryland (MD)"), + ("MA", "Massachusetts (MA)"), + ("MI", "Michigan (MI)"), + ("MN", "Minnesota (MN)"), + ("MS", "Mississippi (MS)"), + ("MO", "Missouri (MO)"), + ("MT", "Montana (MT)"), + ("NE", "Nebraska (NE)"), + ("NV", "Nevada (NV)"), + ("NH", "New Hampshire (NH)"), + ("NJ", "New Jersey (NJ)"), + ("NM", "New Mexico (NM)"), + ("NY", "New York (NY)"), + ("NC", "North Carolina (NC)"), + ("ND", "North Dakota (ND)"), + ("MP", "Northern Mariana Islands (MP)"), + ("OH", "Ohio (OH)"), + ("OK", "Oklahoma (OK)"), + ("OR", "Oregon (OR)"), + ("PA", "Pennsylvania (PA)"), + ("PR", "Puerto Rico (PR)"), + ("RI", "Rhode Island (RI)"), + ("SC", "South Carolina (SC)"), + ("SD", "South Dakota (SD)"), + ("TN", "Tennessee (TN)"), + ("TX", "Texas (TX)"), + ("UM", "United States Minor Outlying Islands (UM)"), + ("UT", "Utah (UT)"), + ("VT", "Vermont (VT)"), + ("VI", "Virgin Islands (VI)"), + ("VA", "Virginia (VA)"), + ("WA", "Washington (WA)"), + ("WV", "West Virginia (WV)"), + ("WI", "Wisconsin (WI)"), + ("WY", "Wyoming (WY)"), + ("AA", "Armed Forces Americas (AA)"), + ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"), + ("AP", "Armed Forces Pacific (AP)"), + ], + max_length=2, + null=True, + verbose_name="state / territory", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="submitter", + field=models.ForeignKey( + blank=True, + help_text='Person listed under "your contact information" in the request form', + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="submitted_domain_requests_information", + to="registrar.contact", + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="tribe_name", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domaininformation", + name="urbanization", + field=models.CharField( + blank=True, help_text="Required for Puerto Rico only", null=True, verbose_name="urbanization" + ), + ), + migrations.AlterField( + model_name="domaininformation", + name="zipcode", + field=models.CharField(blank=True, db_index=True, max_length=10, null=True, verbose_name="zip code"), + ), + migrations.AlterField( + model_name="domainrequest", + name="about_your_organization", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="address_line1", + field=models.CharField(blank=True, null=True, verbose_name="Address line 1"), + ), + migrations.AlterField( + model_name="domainrequest", + name="address_line2", + field=models.CharField(blank=True, null=True, verbose_name="Address line 2"), + ), + migrations.AlterField( + model_name="domainrequest", + name="alternative_domains", + field=models.ManyToManyField( + blank=True, + help_text="Other domain names the creator provided for consideration", + related_name="alternatives+", + to="registrar.website", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="anything_else", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="approved_domain", + field=models.OneToOneField( + blank=True, + help_text="Domain associated with this request; will be blank until request is approved", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="domain_request", + to="registrar.domain", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="city", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="creator", + field=models.ForeignKey( + help_text="Person who submitted the domain request; will not receive email updates", + on_delete=django.db.models.deletion.PROTECT, + related_name="domain_requests_created", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="federal_agency", + field=models.CharField( + blank=True, + choices=[ + ( + "Administrative Conference of the United States", + "Administrative Conference of the United States", + ), + ("Advisory Council on Historic Preservation", "Advisory Council on Historic Preservation"), + ("American Battle Monuments Commission", "American Battle Monuments Commission"), + ("AMTRAK", "AMTRAK"), + ("Appalachian Regional Commission", "Appalachian Regional Commission"), + ( + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + "Appraisal Subcommittee of the Federal Financial Institutions Examination Council", + ), + ("Appraisal Subcommittee", "Appraisal Subcommittee"), + ("Architect of the Capitol", "Architect of the Capitol"), + ("Armed Forces Retirement Home", "Armed Forces Retirement Home"), + ( + "Barry Goldwater Scholarship and Excellence in Education Foundation", + "Barry Goldwater Scholarship and Excellence in Education Foundation", + ), + ( + "Barry Goldwater Scholarship and Excellence in Education Program", + "Barry Goldwater Scholarship and Excellence in Education Program", + ), + ("Central Intelligence Agency", "Central Intelligence Agency"), + ("Chemical Safety Board", "Chemical Safety Board"), + ("Christopher Columbus Fellowship Foundation", "Christopher Columbus Fellowship Foundation"), + ("Civil Rights Cold Case Records Review Board", "Civil Rights Cold Case Records Review Board"), + ( + "Commission for the Preservation of America's Heritage Abroad", + "Commission for the Preservation of America's Heritage Abroad", + ), + ("Commission of Fine Arts", "Commission of Fine Arts"), + ( + "Committee for Purchase From People Who Are Blind or Severely Disabled", + "Committee for Purchase From People Who Are Blind or Severely Disabled", + ), + ("Commodity Futures Trading Commission", "Commodity Futures Trading Commission"), + ("Congressional Budget Office", "Congressional Budget Office"), + ("Consumer Financial Protection Bureau", "Consumer Financial Protection Bureau"), + ("Consumer Product Safety Commission", "Consumer Product Safety Commission"), + ("Corporation for National & Community Service", "Corporation for National & Community Service"), + ( + "Corporation for National and Community Service", + "Corporation for National and Community Service", + ), + ( + "Council of Inspectors General on Integrity and Efficiency", + "Council of Inspectors General on Integrity and Efficiency", + ), + ("Court Services and Offender Supervision", "Court Services and Offender Supervision"), + ("Cyberspace Solarium Commission", "Cyberspace Solarium Commission"), + ( + "DC Court Services and Offender Supervision Agency", + "DC Court Services and Offender Supervision Agency", + ), + ("DC Pre-trial Services", "DC Pre-trial Services"), + ("Defense Nuclear Facilities Safety Board", "Defense Nuclear Facilities Safety Board"), + ("Delta Regional Authority", "Delta Regional Authority"), + ("Denali Commission", "Denali Commission"), + ("Department of Agriculture", "Department of Agriculture"), + ("Department of Commerce", "Department of Commerce"), + ("Department of Defense", "Department of Defense"), + ("Department of Education", "Department of Education"), + ("Department of Energy", "Department of Energy"), + ("Department of Health and Human Services", "Department of Health and Human Services"), + ("Department of Homeland Security", "Department of Homeland Security"), + ("Department of Housing and Urban Development", "Department of Housing and Urban Development"), + ("Department of Justice", "Department of Justice"), + ("Department of Labor", "Department of Labor"), + ("Department of State", "Department of State"), + ("Department of the Interior", "Department of the Interior"), + ("Department of the Treasury", "Department of the Treasury"), + ("Department of Transportation", "Department of Transportation"), + ("Department of Veterans Affairs", "Department of Veterans Affairs"), + ("Director of National Intelligence", "Director of National Intelligence"), + ("Dwight D. Eisenhower Memorial Commission", "Dwight D. Eisenhower Memorial Commission"), + ("Election Assistance Commission", "Election Assistance Commission"), + ("Environmental Protection Agency", "Environmental Protection Agency"), + ("Equal Employment Opportunity Commission", "Equal Employment Opportunity Commission"), + ("Executive Office of the President", "Executive Office of the President"), + ("Export-Import Bank of the United States", "Export-Import Bank of the United States"), + ("Export/Import Bank of the U.S.", "Export/Import Bank of the U.S."), + ("Farm Credit Administration", "Farm Credit Administration"), + ("Farm Credit System Insurance Corporation", "Farm Credit System Insurance Corporation"), + ("Federal Communications Commission", "Federal Communications Commission"), + ("Federal Deposit Insurance Corporation", "Federal Deposit Insurance Corporation"), + ("Federal Election Commission", "Federal Election Commission"), + ("Federal Energy Regulatory Commission", "Federal Energy Regulatory Commission"), + ( + "Federal Financial Institutions Examination Council", + "Federal Financial Institutions Examination Council", + ), + ("Federal Housing Finance Agency", "Federal Housing Finance Agency"), + ("Federal Judiciary", "Federal Judiciary"), + ("Federal Labor Relations Authority", "Federal Labor Relations Authority"), + ("Federal Maritime Commission", "Federal Maritime Commission"), + ("Federal Mediation and Conciliation Service", "Federal Mediation and Conciliation Service"), + ( + "Federal Mine Safety and Health Review Commission", + "Federal Mine Safety and Health Review Commission", + ), + ( + "Federal Permitting Improvement Steering Council", + "Federal Permitting Improvement Steering Council", + ), + ("Federal Reserve Board of Governors", "Federal Reserve Board of Governors"), + ("Federal Reserve System", "Federal Reserve System"), + ("Federal Trade Commission", "Federal Trade Commission"), + ("General Services Administration", "General Services Administration"), + ("gov Administration", "gov Administration"), + ("Government Accountability Office", "Government Accountability Office"), + ("Government Publishing Office", "Government Publishing Office"), + ("Gulf Coast Ecosystem Restoration Council", "Gulf Coast Ecosystem Restoration Council"), + ("Harry S Truman Scholarship Foundation", "Harry S Truman Scholarship Foundation"), + ("Harry S. Truman Scholarship Foundation", "Harry S. Truman Scholarship Foundation"), + ("Institute of Museum and Library Services", "Institute of Museum and Library Services"), + ("Institute of Peace", "Institute of Peace"), + ("Inter-American Foundation", "Inter-American Foundation"), + ( + "International Boundary and Water Commission: United States and Mexico", + "International Boundary and Water Commission: United States and Mexico", + ), + ( + "International Boundary Commission: United States and Canada", + "International Boundary Commission: United States and Canada", + ), + ( + "International Joint Commission: United States and Canada", + "International Joint Commission: United States and Canada", + ), + ("James Madison Memorial Fellowship Foundation", "James Madison Memorial Fellowship Foundation"), + ("Japan-United States Friendship Commission", "Japan-United States Friendship Commission"), + ("Japan-US Friendship Commission", "Japan-US Friendship Commission"), + ("John F. Kennedy Center for Performing Arts", "John F. Kennedy Center for Performing Arts"), + ( + "John F. Kennedy Center for the Performing Arts", + "John F. Kennedy Center for the Performing Arts", + ), + ("Legal Services Corporation", "Legal Services Corporation"), + ("Legislative Branch", "Legislative Branch"), + ("Library of Congress", "Library of Congress"), + ("Marine Mammal Commission", "Marine Mammal Commission"), + ( + "Medicaid and CHIP Payment and Access Commission", + "Medicaid and CHIP Payment and Access Commission", + ), + ("Medical Payment Advisory Commission", "Medical Payment Advisory Commission"), + ("Medicare Payment Advisory Commission", "Medicare Payment Advisory Commission"), + ("Merit Systems Protection Board", "Merit Systems Protection Board"), + ("Millennium Challenge Corporation", "Millennium Challenge Corporation"), + ( + "Morris K. Udall and Stewart L. Udall Foundation", + "Morris K. Udall and Stewart L. Udall Foundation", + ), + ("National Aeronautics and Space Administration", "National Aeronautics and Space Administration"), + ("National Archives and Records Administration", "National Archives and Records Administration"), + ("National Capital Planning Commission", "National Capital Planning Commission"), + ("National Council on Disability", "National Council on Disability"), + ("National Credit Union Administration", "National Credit Union Administration"), + ("National Endowment for the Arts", "National Endowment for the Arts"), + ("National Endowment for the Humanities", "National Endowment for the Humanities"), + ( + "National Foundation on the Arts and the Humanities", + "National Foundation on the Arts and the Humanities", + ), + ("National Gallery of Art", "National Gallery of Art"), + ("National Indian Gaming Commission", "National Indian Gaming Commission"), + ("National Labor Relations Board", "National Labor Relations Board"), + ("National Mediation Board", "National Mediation Board"), + ("National Science Foundation", "National Science Foundation"), + ( + "National Security Commission on Artificial Intelligence", + "National Security Commission on Artificial Intelligence", + ), + ("National Transportation Safety Board", "National Transportation Safety Board"), + ( + "Networking Information Technology Research and Development", + "Networking Information Technology Research and Development", + ), + ("Non-Federal Agency", "Non-Federal Agency"), + ("Northern Border Regional Commission", "Northern Border Regional Commission"), + ("Nuclear Regulatory Commission", "Nuclear Regulatory Commission"), + ("Nuclear Safety Oversight Committee", "Nuclear Safety Oversight Committee"), + ("Nuclear Waste Technical Review Board", "Nuclear Waste Technical Review Board"), + ( + "Occupational Safety & Health Review Commission", + "Occupational Safety & Health Review Commission", + ), + ( + "Occupational Safety and Health Review Commission", + "Occupational Safety and Health Review Commission", + ), + ("Office of Compliance", "Office of Compliance"), + ("Office of Congressional Workplace Rights", "Office of Congressional Workplace Rights"), + ("Office of Government Ethics", "Office of Government Ethics"), + ("Office of Navajo and Hopi Indian Relocation", "Office of Navajo and Hopi Indian Relocation"), + ("Office of Personnel Management", "Office of Personnel Management"), + ("Open World Leadership Center", "Open World Leadership Center"), + ("Overseas Private Investment Corporation", "Overseas Private Investment Corporation"), + ("Peace Corps", "Peace Corps"), + ("Pension Benefit Guaranty Corporation", "Pension Benefit Guaranty Corporation"), + ("Postal Regulatory Commission", "Postal Regulatory Commission"), + ("Presidio Trust", "Presidio Trust"), + ("Privacy and Civil Liberties Oversight Board", "Privacy and Civil Liberties Oversight Board"), + ("Public Buildings Reform Board", "Public Buildings Reform Board"), + ( + "Public Defender Service for the District of Columbia", + "Public Defender Service for the District of Columbia", + ), + ("Railroad Retirement Board", "Railroad Retirement Board"), + ("Securities and Exchange Commission", "Securities and Exchange Commission"), + ("Selective Service System", "Selective Service System"), + ("Small Business Administration", "Small Business Administration"), + ("Smithsonian Institution", "Smithsonian Institution"), + ("Social Security Administration", "Social Security Administration"), + ("Social Security Advisory Board", "Social Security Advisory Board"), + ("Southeast Crescent Regional Commission", "Southeast Crescent Regional Commission"), + ("Southwest Border Regional Commission", "Southwest Border Regional Commission"), + ("State Justice Institute", "State Justice Institute"), + ("State, Local, and Tribal Government", "State, Local, and Tribal Government"), + ("Stennis Center for Public Service", "Stennis Center for Public Service"), + ("Surface Transportation Board", "Surface Transportation Board"), + ("Tennessee Valley Authority", "Tennessee Valley Authority"), + ("The Executive Office of the President", "The Executive Office of the President"), + ("The Intelligence Community", "The Intelligence Community"), + ("The Legislative Branch", "The Legislative Branch"), + ("The Supreme Court", "The Supreme Court"), + ( + "The United States World War One Centennial Commission", + "The United States World War One Centennial Commission", + ), + ("U.S. Access Board", "U.S. Access Board"), + ("U.S. Agency for Global Media", "U.S. Agency for Global Media"), + ("U.S. Agency for International Development", "U.S. Agency for International Development"), + ("U.S. Capitol Police", "U.S. Capitol Police"), + ("U.S. Chemical Safety Board", "U.S. Chemical Safety Board"), + ( + "U.S. China Economic and Security Review Commission", + "U.S. China Economic and Security Review Commission", + ), + ( + "U.S. Commission for the Preservation of Americas Heritage Abroad", + "U.S. Commission for the Preservation of Americas Heritage Abroad", + ), + ("U.S. Commission of Fine Arts", "U.S. Commission of Fine Arts"), + ("U.S. Commission on Civil Rights", "U.S. Commission on Civil Rights"), + ( + "U.S. Commission on International Religious Freedom", + "U.S. Commission on International Religious Freedom", + ), + ("U.S. Courts", "U.S. Courts"), + ("U.S. Department of Agriculture", "U.S. Department of Agriculture"), + ("U.S. Interagency Council on Homelessness", "U.S. Interagency Council on Homelessness"), + ("U.S. International Trade Commission", "U.S. International Trade Commission"), + ("U.S. Nuclear Waste Technical Review Board", "U.S. Nuclear Waste Technical Review Board"), + ("U.S. Office of Special Counsel", "U.S. Office of Special Counsel"), + ("U.S. Peace Corps", "U.S. Peace Corps"), + ("U.S. Postal Service", "U.S. Postal Service"), + ("U.S. Semiquincentennial Commission", "U.S. Semiquincentennial Commission"), + ("U.S. Trade and Development Agency", "U.S. Trade and Development Agency"), + ( + "U.S.-China Economic and Security Review Commission", + "U.S.-China Economic and Security Review Commission", + ), + ("Udall Foundation", "Udall Foundation"), + ("United States AbilityOne", "United States AbilityOne"), + ("United States Access Board", "United States Access Board"), + ("United States African Development Foundation", "United States African Development Foundation"), + ("United States Agency for Global Media", "United States Agency for Global Media"), + ("United States Arctic Research Commission", "United States Arctic Research Commission"), + ("United States Global Change Research Program", "United States Global Change Research Program"), + ("United States Holocaust Memorial Museum", "United States Holocaust Memorial Museum"), + ("United States Institute of Peace", "United States Institute of Peace"), + ( + "United States Interagency Council on Homelessness", + "United States Interagency Council on Homelessness", + ), + ( + "United States International Development Finance Corporation", + "United States International Development Finance Corporation", + ), + ("United States International Trade Commission", "United States International Trade Commission"), + ("United States Postal Service", "United States Postal Service"), + ("United States Senate", "United States Senate"), + ("United States Trade and Development Agency", "United States Trade and Development Agency"), + ( + "Utah Reclamation Mitigation and Conservation Commission", + "Utah Reclamation Mitigation and Conservation Commission", + ), + ("Vietnam Education Foundation", "Vietnam Education Foundation"), + ("Western Hemisphere Drug Policy Commission", "Western Hemisphere Drug Policy Commission"), + ( + "Woodrow Wilson International Center for Scholars", + "Woodrow Wilson International Center for Scholars", + ), + ("World War I Centennial Commission", "World War I Centennial Commission"), + ], + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="federal_type", + field=models.CharField( + blank=True, + choices=[("executive", "Executive"), ("judicial", "Judicial"), ("legislative", "Legislative")], + max_length=50, + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="federally_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="generic_org_type", + field=models.CharField( + blank=True, + choices=[ + ("federal", "Federal"), + ("interstate", "Interstate"), + ("state_or_territory", "State or territory"), + ("tribal", "Tribal"), + ("county", "County"), + ("city", "City"), + ("special_district", "Special district"), + ("school_district", "School district"), + ], + max_length=255, + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="is_election_board", + field=models.BooleanField(blank=True, null=True, verbose_name="election office"), + ), + migrations.AlterField( + model_name="domainrequest", + name="no_other_contacts_rationale", + field=models.TextField( + blank=True, help_text="Required if creator does not list other employees", null=True + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="notes", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="organization_name", + field=models.CharField(blank=True, db_index=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="organization_type", + field=models.CharField( + blank=True, + choices=[ + ("federal", "Federal"), + ("interstate", "Interstate"), + ("state_or_territory", "State or territory"), + ("tribal", "Tribal"), + ("county", "County"), + ("city", "City"), + ("special_district", "Special district"), + ("school_district", "School district"), + ("state_or_territory_election", "State or territory - Election"), + ("tribal_election", "Tribal - Election"), + ("county_election", "County - Election"), + ("city_election", "City - Election"), + ("special_district_election", "Special district - Election"), + ], + help_text='"Election" appears after the org type if it\'s an election office.', + max_length=255, + null=True, + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="purpose", + field=models.TextField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="requested_domain", + field=models.OneToOneField( + blank=True, + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="domain_request", + to="registrar.draftdomain", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="state_recognized_tribe", + field=models.BooleanField(null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="state_territory", + field=models.CharField( + blank=True, + choices=[ + ("AL", "Alabama (AL)"), + ("AK", "Alaska (AK)"), + ("AS", "American Samoa (AS)"), + ("AZ", "Arizona (AZ)"), + ("AR", "Arkansas (AR)"), + ("CA", "California (CA)"), + ("CO", "Colorado (CO)"), + ("CT", "Connecticut (CT)"), + ("DE", "Delaware (DE)"), + ("DC", "District of Columbia (DC)"), + ("FL", "Florida (FL)"), + ("GA", "Georgia (GA)"), + ("GU", "Guam (GU)"), + ("HI", "Hawaii (HI)"), + ("ID", "Idaho (ID)"), + ("IL", "Illinois (IL)"), + ("IN", "Indiana (IN)"), + ("IA", "Iowa (IA)"), + ("KS", "Kansas (KS)"), + ("KY", "Kentucky (KY)"), + ("LA", "Louisiana (LA)"), + ("ME", "Maine (ME)"), + ("MD", "Maryland (MD)"), + ("MA", "Massachusetts (MA)"), + ("MI", "Michigan (MI)"), + ("MN", "Minnesota (MN)"), + ("MS", "Mississippi (MS)"), + ("MO", "Missouri (MO)"), + ("MT", "Montana (MT)"), + ("NE", "Nebraska (NE)"), + ("NV", "Nevada (NV)"), + ("NH", "New Hampshire (NH)"), + ("NJ", "New Jersey (NJ)"), + ("NM", "New Mexico (NM)"), + ("NY", "New York (NY)"), + ("NC", "North Carolina (NC)"), + ("ND", "North Dakota (ND)"), + ("MP", "Northern Mariana Islands (MP)"), + ("OH", "Ohio (OH)"), + ("OK", "Oklahoma (OK)"), + ("OR", "Oregon (OR)"), + ("PA", "Pennsylvania (PA)"), + ("PR", "Puerto Rico (PR)"), + ("RI", "Rhode Island (RI)"), + ("SC", "South Carolina (SC)"), + ("SD", "South Dakota (SD)"), + ("TN", "Tennessee (TN)"), + ("TX", "Texas (TX)"), + ("UM", "United States Minor Outlying Islands (UM)"), + ("UT", "Utah (UT)"), + ("VT", "Vermont (VT)"), + ("VI", "Virgin Islands (VI)"), + ("VA", "Virginia (VA)"), + ("WA", "Washington (WA)"), + ("WV", "West Virginia (WV)"), + ("WI", "Wisconsin (WI)"), + ("WY", "Wyoming (WY)"), + ("AA", "Armed Forces Americas (AA)"), + ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"), + ("AP", "Armed Forces Pacific (AP)"), + ], + max_length=2, + null=True, + verbose_name="state / territory", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="submitter", + field=models.ForeignKey( + blank=True, + help_text='Person listed under "your contact information" in the request form; will receive email updates', + null=True, + on_delete=django.db.models.deletion.PROTECT, + related_name="submitted_domain_requests", + to="registrar.contact", + ), + ), + migrations.AlterField( + model_name="domainrequest", + name="tribe_name", + field=models.CharField(blank=True, null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="urbanization", + field=models.CharField(blank=True, help_text="Required for Puerto Rico only", null=True), + ), + migrations.AlterField( + model_name="domainrequest", + name="zipcode", + field=models.CharField(blank=True, db_index=True, max_length=10, null=True, verbose_name="zip code"), + ), + migrations.AlterField( + model_name="host", + name="domain", + field=models.ForeignKey( + help_text="Domain associated with this host", + on_delete=django.db.models.deletion.PROTECT, + related_name="host", + to="registrar.domain", + ), + ), + migrations.AlterField( + model_name="host", + name="name", + field=models.CharField(default=None, max_length=253, verbose_name="host name"), + ), + migrations.AlterField( + model_name="hostip", + name="address", + field=models.CharField( + default=None, + max_length=46, + validators=[django.core.validators.validate_ipv46_address], + verbose_name="IP address", + ), + ), + migrations.AlterField( + model_name="hostip", + name="host", + field=models.ForeignKey( + help_text="IP associated with this host", + on_delete=django.db.models.deletion.PROTECT, + related_name="ip", + to="registrar.host", + ), + ), + migrations.AlterField( + model_name="user", + name="status", + field=models.CharField( + blank=True, + choices=[("restricted", "restricted")], + default=None, + help_text='Users in "restricted" status cannot make updates in the registrar or start a new request.', + max_length=10, + null=True, + verbose_name="user status", + ), + ), + migrations.AlterField( + model_name="verifiedbystaff", + name="email", + field=models.EmailField(db_index=True, max_length=254), + ), + migrations.AlterField( + model_name="verifiedbystaff", + name="notes", + field=models.TextField(), + ), + migrations.AlterField( + model_name="verifiedbystaff", + name="requestor", + field=models.ForeignKey( + blank=True, + help_text="Person who verified this user", + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="verifiedby_user", + to=settings.AUTH_USER_MODEL, + ), + ), + migrations.AlterField( + model_name="website", + name="website", + field=models.CharField( + help_text="An alternative domain or current website listed on a domain request", max_length=255 + ), + ), + ] diff --git a/src/registrar/models/domain.py b/src/registrar/models/domain.py index 0d0d8020d..7f53bb234 100644 --- a/src/registrar/models/domain.py +++ b/src/registrar/models/domain.py @@ -159,6 +159,31 @@ class Domain(TimeStampedModel, DomainHelper): return help_texts.get(state, "") + @classmethod + def get_admin_help_text(cls, state): + """Returns a help message for a desired state for /admin. If none is found, an empty string is returned""" + admin_help_texts = { + cls.UNKNOWN: ( + "The creator of the associated domain request has not logged in to " + "manage the domain since it was approved. " + 'The state will switch to "DNS needed" after they access the domain in the registrar.' + ), + cls.DNS_NEEDED: ( + "Before this domain can be used, name server addresses need to be added within the registrar." + ), + cls.READY: "This domain has name servers and is ready for use.", + cls.ON_HOLD: ( + "While on hold, this domain won't resolve in DNS and " + "any infrastructure (like websites) will be offline." + ), + cls.DELETED: ( + "This domain was permanently removed from the registry. " + "The domain no longer resolves in DNS and any infrastructure (like websites) is offline." + ), + } + + return admin_help_texts.get(state, "") + class Cache(property): """ Python descriptor to turn class methods into properties. @@ -992,22 +1017,25 @@ class Domain(TimeStampedModel, DomainHelper): blank=False, default=None, # prevent saving without a value unique=True, - verbose_name="domain", help_text="Fully qualified domain name", + verbose_name="domain", ) state = FSMField( max_length=21, choices=State.choices, default=State.UNKNOWN, - protected=True, # cannot change state directly, particularly in Django admin + # cannot change state directly, particularly in Django admin + protected=True, + # This must be defined for custom state help messages, + # as otherwise the view will purge the help field as it does not exist. + help_text=" ", verbose_name="domain state", - help_text="Very basic info about the lifecycle of this domain object", ) expiration_date = DateField( null=True, - help_text=("Duplication of registry's expiration date saved for ease of reporting"), + help_text=("Date the domain expires in the registry"), ) security_contact_registry_id = TextField( @@ -1019,15 +1047,15 @@ class Domain(TimeStampedModel, DomainHelper): deleted = DateField( null=True, editable=False, + help_text='Will appear blank unless the domain is in "deleted" state', verbose_name="deleted on", - help_text="Deleted at date", ) first_ready = DateField( null=True, editable=False, + help_text='Date when this domain first moved into "ready" state; date will never change', verbose_name="first ready on", - help_text="The last time this domain moved into the READY state", ) def isActive(self): diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py index 3c206457e..238b658f8 100644 --- a/src/registrar/models/domain_information.py +++ b/src/registrar/models/domain_information.py @@ -47,6 +47,7 @@ class DomainInformation(TimeStampedModel): "registrar.User", on_delete=models.PROTECT, related_name="information_created", + help_text="Person who submitted the domain request", ) domain_request = models.OneToOneField( @@ -55,7 +56,7 @@ class DomainInformation(TimeStampedModel): blank=True, null=True, related_name="DomainRequest_info", - help_text="Associated domain request", + help_text="Request associated with this domain", unique=True, ) @@ -73,7 +74,6 @@ class DomainInformation(TimeStampedModel): null=True, blank=True, verbose_name="election office", - help_text="Is your organization an election office?", ) # TODO - Ticket #1911: stub this data from DomainRequest @@ -82,30 +82,26 @@ class DomainInformation(TimeStampedModel): choices=DomainRequest.OrgChoicesElectionOffice.choices, null=True, blank=True, - help_text="Type of organization - Election office", + help_text='"Election" appears after the org type if it\'s an election office.', ) federally_recognized_tribe = models.BooleanField( null=True, - help_text="Is the tribe federally recognized", ) state_recognized_tribe = models.BooleanField( null=True, - help_text="Is the tribe recognized by a state", ) tribe_name = models.CharField( null=True, blank=True, - help_text="Name of tribe", ) federal_agency = models.CharField( choices=AGENCY_CHOICES, null=True, blank=True, - help_text="Federal agency", ) federal_type = models.CharField( @@ -113,38 +109,32 @@ class DomainInformation(TimeStampedModel): choices=BranchChoices.choices, null=True, blank=True, - help_text="Federal government branch", ) is_election_board = models.BooleanField( null=True, blank=True, verbose_name="election office", - help_text="Is your organization an election office?", ) organization_name = models.CharField( null=True, blank=True, - help_text="Organization name", db_index=True, ) address_line1 = models.CharField( null=True, blank=True, - help_text="Street address", verbose_name="address line 1", ) address_line2 = models.CharField( null=True, blank=True, - help_text="Street address line 2 (optional)", verbose_name="address line 2", ) city = models.CharField( null=True, blank=True, - help_text="City", ) state_territory = models.CharField( max_length=2, @@ -152,27 +142,24 @@ class DomainInformation(TimeStampedModel): null=True, blank=True, verbose_name="state / territory", - help_text="State, territory, or military post", ) zipcode = models.CharField( max_length=10, null=True, blank=True, - help_text="Zip code", - verbose_name="zip code", db_index=True, + verbose_name="zip code", ) urbanization = models.CharField( null=True, blank=True, - help_text="Urbanization (required for Puerto Rico only)", + help_text="Required for Puerto Rico only", verbose_name="urbanization", ) about_your_organization = models.TextField( null=True, blank=True, - help_text="Information about your organization", ) authorizing_official = models.ForeignKey( @@ -190,7 +177,6 @@ class DomainInformation(TimeStampedModel): null=True, # Access this information via Domain as "domain.domain_info" related_name="domain_info", - help_text="Domain to which this information belongs", ) # This is the contact information provided by the domain requestor. The @@ -201,6 +187,7 @@ class DomainInformation(TimeStampedModel): blank=True, related_name="submitted_domain_requests_information", on_delete=models.PROTECT, + help_text='Person listed under "your contact information" in the request form', ) purpose = models.TextField( @@ -219,13 +206,12 @@ class DomainInformation(TimeStampedModel): no_other_contacts_rationale = models.TextField( null=True, blank=True, - help_text="Reason for listing no additional contacts", + help_text="Required if creator does not list other employees", ) anything_else = models.TextField( null=True, blank=True, - help_text="Anything else?", ) is_policy_acknowledged = models.BooleanField( @@ -237,7 +223,6 @@ class DomainInformation(TimeStampedModel): notes = models.TextField( null=True, blank=True, - help_text="Notes about the request", ) def __str__(self): diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py index 5b8fc59e3..9ed35f489 100644 --- a/src/registrar/models/domain_request.py +++ b/src/registrar/models/domain_request.py @@ -464,6 +464,7 @@ class DomainRequest(TimeStampedModel): "registrar.User", on_delete=models.PROTECT, related_name="domain_requests_created", + help_text="Person who submitted the domain request; will not receive email updates", ) investigator = models.ForeignKey( @@ -481,14 +482,12 @@ class DomainRequest(TimeStampedModel): choices=OrganizationChoices.choices, null=True, blank=True, - help_text="Type of organization", ) is_election_board = models.BooleanField( null=True, blank=True, verbose_name="election office", - help_text="Is your organization an election office?", ) # TODO - Ticket #1911: stub this data from DomainRequest @@ -497,30 +496,26 @@ class DomainRequest(TimeStampedModel): choices=OrgChoicesElectionOffice.choices, null=True, blank=True, - help_text="Type of organization - Election office", + help_text='"Election" appears after the org type if it\'s an election office.', ) federally_recognized_tribe = models.BooleanField( null=True, - help_text="Is the tribe federally recognized", ) state_recognized_tribe = models.BooleanField( null=True, - help_text="Is the tribe recognized by a state", ) tribe_name = models.CharField( null=True, blank=True, - help_text="Name of tribe", ) federal_agency = models.CharField( choices=AGENCY_CHOICES, null=True, blank=True, - help_text="Federal agency", ) federal_type = models.CharField( @@ -528,32 +523,27 @@ class DomainRequest(TimeStampedModel): choices=BranchChoices.choices, null=True, blank=True, - help_text="Federal government branch", ) organization_name = models.CharField( null=True, blank=True, - help_text="Organization name", db_index=True, ) address_line1 = models.CharField( null=True, blank=True, - help_text="Street address", verbose_name="Address line 1", ) address_line2 = models.CharField( null=True, blank=True, - help_text="Street address line 2 (optional)", verbose_name="Address line 2", ) city = models.CharField( null=True, blank=True, - help_text="City", ) state_territory = models.CharField( max_length=2, @@ -561,26 +551,23 @@ class DomainRequest(TimeStampedModel): null=True, blank=True, verbose_name="state / territory", - help_text="State, territory, or military post", ) zipcode = models.CharField( max_length=10, null=True, blank=True, verbose_name="zip code", - help_text="Zip code", db_index=True, ) urbanization = models.CharField( null=True, blank=True, - help_text="Urbanization (required for Puerto Rico only)", + help_text="Required for Puerto Rico only", ) about_your_organization = models.TextField( null=True, blank=True, - help_text="Information about your organization", ) authorizing_official = models.ForeignKey( @@ -603,7 +590,7 @@ class DomainRequest(TimeStampedModel): "Domain", null=True, blank=True, - help_text="The approved domain", + help_text="Domain associated with this request; will be blank until request is approved", related_name="domain_request", on_delete=models.SET_NULL, ) @@ -612,7 +599,6 @@ class DomainRequest(TimeStampedModel): "DraftDomain", null=True, blank=True, - help_text="The requested domain", related_name="domain_request", on_delete=models.PROTECT, ) @@ -621,6 +607,7 @@ class DomainRequest(TimeStampedModel): "registrar.Website", blank=True, related_name="alternatives+", + help_text="Other domain names the creator provided for consideration", ) # This is the contact information provided by the domain requestor. The @@ -631,12 +618,12 @@ class DomainRequest(TimeStampedModel): blank=True, related_name="submitted_domain_requests", on_delete=models.PROTECT, + help_text='Person listed under "your contact information" in the request form; will receive email updates', ) purpose = models.TextField( null=True, blank=True, - help_text="Purpose of your domain", ) other_contacts = models.ManyToManyField( @@ -649,13 +636,12 @@ class DomainRequest(TimeStampedModel): no_other_contacts_rationale = models.TextField( null=True, blank=True, - help_text="Reason for listing no additional contacts", + help_text="Required if creator does not list other employees", ) anything_else = models.TextField( null=True, blank=True, - help_text="Anything else?", ) is_policy_acknowledged = models.BooleanField( @@ -676,7 +662,6 @@ class DomainRequest(TimeStampedModel): notes = models.TextField( null=True, blank=True, - help_text="Notes about this request", ) def sync_organization_type(self): diff --git a/src/registrar/models/host.py b/src/registrar/models/host.py index 2fb4b980b..97ee1f8aa 100644 --- a/src/registrar/models/host.py +++ b/src/registrar/models/host.py @@ -22,14 +22,13 @@ class Host(TimeStampedModel): default=None, # prevent saving without a value unique=False, verbose_name="host name", - help_text="Fully qualified domain name", ) domain = models.ForeignKey( "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", + help_text="Domain associated with this host", ) def __str__(self): diff --git a/src/registrar/models/host_ip.py b/src/registrar/models/host_ip.py index 216ad9eca..e0c5bb1c9 100644 --- a/src/registrar/models/host_ip.py +++ b/src/registrar/models/host_ip.py @@ -21,12 +21,11 @@ class HostIP(TimeStampedModel): default=None, # prevent saving without a value validators=[validate_ipv46_address], verbose_name="IP address", - help_text="IP address", ) host = models.ForeignKey( "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", + help_text="IP associated with this host", ) diff --git a/src/registrar/models/user.py b/src/registrar/models/user.py index 3a37f5b61..0b16d45cb 100644 --- a/src/registrar/models/user.py +++ b/src/registrar/models/user.py @@ -58,6 +58,7 @@ class User(AbstractUser): null=True, # Allow the field to be null blank=True, # Allow the field to be blank verbose_name="user status", + help_text='Users in "restricted" status cannot make updates in the registrar or start a new request.', ) domains = models.ManyToManyField( diff --git a/src/registrar/models/utility/generic_helper.py b/src/registrar/models/utility/generic_helper.py index 892298967..209c0303f 100644 --- a/src/registrar/models/utility/generic_helper.py +++ b/src/registrar/models/utility/generic_helper.py @@ -164,7 +164,7 @@ class CreateOrUpdateOrganizationTypeHelper: # There is no avenue for this to occur in the UI, # as such - this can only occur if the object is initialized in this way. # Or if there are pre-existing data. - logger.warning( + logger.debug( "create_or_update_organization_type() -> is_election_board " f"cannot exist for {generic_org_type}. Setting to None." ) diff --git a/src/registrar/models/verified_by_staff.py b/src/registrar/models/verified_by_staff.py index a6d861504..c09dce822 100644 --- a/src/registrar/models/verified_by_staff.py +++ b/src/registrar/models/verified_by_staff.py @@ -9,7 +9,6 @@ class VerifiedByStaff(TimeStampedModel): email = models.EmailField( null=False, blank=False, - help_text="Email", db_index=True, ) @@ -19,12 +18,12 @@ class VerifiedByStaff(TimeStampedModel): blank=True, on_delete=models.SET_NULL, related_name="verifiedby_user", + help_text="Person who verified this user", ) notes = models.TextField( null=False, blank=False, - help_text="Notes", ) class Meta: diff --git a/src/registrar/models/website.py b/src/registrar/models/website.py index 29739b8ee..a062fe248 100644 --- a/src/registrar/models/website.py +++ b/src/registrar/models/website.py @@ -12,7 +12,7 @@ class Website(TimeStampedModel): website = models.CharField( max_length=255, null=False, - help_text="", + help_text="An alternative domain or current website listed on a domain request", ) def __str__(self) -> str: diff --git a/src/registrar/templates/django/admin/domain_change_form.html b/src/registrar/templates/django/admin/domain_change_form.html index 44fe6851b..80888423d 100644 --- a/src/registrar/templates/django/admin/domain_change_form.html +++ b/src/registrar/templates/django/admin/domain_change_form.html @@ -33,7 +33,10 @@ {% endif %} - {{ block.super }} + + {% for fieldset in adminform %} + {% include "django/admin/includes/domain_fieldset.html" with state_help_message=state_help_message %} + {% endfor %} {% endblock %} {% block submit_buttons_bottom %} diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html index 7b468faec..6374406c1 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -67,9 +67,16 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endwith %} {% endblock field_readonly %} +{% block help_text %} +
+
{{ field.field.help_text|safe }}
+
+{% endblock help_text %} + + {% block after_help_text %} {% if field.field.name == "creator" %} -
+
{% include "django/admin/includes/contact_detail_list.html" with user=original_object.creator no_title_top_padding=field.is_readonly %}
diff --git a/src/registrar/templates/django/admin/includes/domain_fieldset.html b/src/registrar/templates/django/admin/includes/domain_fieldset.html new file mode 100644 index 000000000..d5f5bc1af --- /dev/null +++ b/src/registrar/templates/django/admin/includes/domain_fieldset.html @@ -0,0 +1,21 @@ +{% extends "admin/fieldset.html" %} +{% load static url_helpers %} + + +{% block field_readonly %} + {% if field.field.name == "state" %} +
{{ domain_state }}
+ {% else %} +
{{ field.contents }}
+ {% endif %} +{% endblock %} + +{% block help_text %} +
+ {% if field.field.name == "state" %} +
{{ state_help_message }}
+ {% else %} +
{{ field.field.help_text|safe }}
+ {% endif %} +
+{% endblock help_text %} diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 20ada4d14..cff4455fb 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -18,6 +18,7 @@ from registrar.admin import ( AuditedAdmin, ContactAdmin, DomainInformationAdmin, + MyHostAdmin, UserDomainRoleAdmin, VerifiedByStaffAdmin, ) @@ -76,6 +77,13 @@ class TestDomainAdmin(MockEppLib, WebTest): self.app.set_user(self.superuser.username) self.client.force_login(self.superuser) + # Add domain data + self.ready_domain, _ = Domain.objects.get_or_create(name="fakeready.gov", state=Domain.State.READY) + self.unknown_domain, _ = Domain.objects.get_or_create(name="fakeunknown.gov", state=Domain.State.UNKNOWN) + self.dns_domain, _ = Domain.objects.get_or_create(name="fakedns.gov", state=Domain.State.DNS_NEEDED) + self.hold_domain, _ = Domain.objects.get_or_create(name="fakehold.gov", state=Domain.State.ON_HOLD) + self.deleted_domain, _ = Domain.objects.get_or_create(name="fakedeleted.gov", state=Domain.State.DELETED) + # Contains some test tools self.test_helper = GenericTestHelper( factory=self.factory, @@ -159,6 +167,68 @@ class TestDomainAdmin(MockEppLib, WebTest): # Test for the copy link self.assertContains(response, "usa-button__clipboard") + @less_console_noise_decorator + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + + # Create a ready domain with a preset expiration date + domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY) + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domain/{}/change/".format(domain.pk), + follow=True, + ) + + # Make sure the page loaded, and that we're on the right page + self.assertEqual(response.status_code, 200) + self.assertContains(response, domain.name) + + # These should exist in the response + expected_values = [ + ("expiration_date", "Date the domain expires in the registry"), + ("first_ready_at", 'Date when this domain first moved into "ready" state; date will never change'), + ("deleted_at", 'Will appear blank unless the domain is in "deleted" state'), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) + + @less_console_noise_decorator + def test_helper_text_state(self): + """ + Tests for the correct state helper text on this page + """ + + # We don't need to check for all text content, just a portion of it + expected_unknown_domain_message = "The creator of the associated domain request has not logged in to" + expected_dns_message = "Before this domain can be used, name server addresses need" + expected_hold_message = "While on hold, this domain" + expected_deleted_message = "This domain was permanently removed from the registry." + expected_messages = [ + (self.ready_domain, "This domain has name servers and is ready for use."), + (self.unknown_domain, expected_unknown_domain_message), + (self.dns_domain, expected_dns_message), + (self.hold_domain, expected_hold_message), + (self.deleted_domain, expected_deleted_message), + ] + + p = "adminpass" + self.client.login(username="superuser", password=p) + for domain, message in expected_messages: + with self.subTest(domain_state=domain.state): + response = self.client.get( + "/admin/registrar/domain/{}/change/".format(domain.id), + ) + + # Make sure the page loaded, and that we're on the right page + self.assertEqual(response.status_code, 200) + self.assertContains(response, domain.name) + + # Check that the right help text exists + self.assertContains(response, message) + @patch("registrar.admin.DomainAdmin._get_current_date", return_value=date(2024, 1, 1)) def test_extend_expiration_date_button(self, mock_date_today): """ @@ -782,6 +852,41 @@ class TestDomainRequestAdmin(MockEppLib): ) self.mock_client = MockSESClient() + @less_console_noise_decorator + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + + # Create a fake domain request and domain + domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domainrequest/{}/change/".format(domain_request.pk), + follow=True, + ) + + # Make sure the page loaded, and that we're on the right page + self.assertEqual(response.status_code, 200) + self.assertContains(response, domain_request.requested_domain.name) + + # These should exist in the response + expected_values = [ + ("creator", "Person who submitted the domain request; will not receive email updates"), + ( + "submitter", + 'Person listed under "your contact information" in the request form; will receive email updates', + ), + ("approved_domain", "Domain associated with this request; will be blank until request is approved"), + ("no_other_contacts_rationale", "Required if creator does not list other employees"), + ("alternative_domains", "Other domain names the creator provided for consideration"), + ("no_other_contacts_rationale", "Required if creator does not list other employees"), + ("Urbanization", "Required for Puerto Rico only"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) + @less_console_noise_decorator def test_analyst_can_see_and_edit_alternative_domain(self): """Tests if an analyst can still see and edit the alternative domain field""" @@ -2315,6 +2420,54 @@ class DomainInvitationAdminTest(TestCase): self.assertContains(response, retrieved_html, count=1) +class TestHostAdmin(TestCase): + def setUp(self): + """Setup environment for a mock admin user""" + super().setUp() + self.site = AdminSite() + self.factory = RequestFactory() + self.admin = MyHostAdmin(model=Host, admin_site=self.site) + self.client = Client(HTTP_HOST="localhost:8080") + self.superuser = create_superuser() + self.test_helper = GenericTestHelper( + factory=self.factory, + user=self.superuser, + admin=self.admin, + url="/admin/registrar/Host/", + model=Host, + ) + + def tearDown(self): + super().tearDown() + Host.objects.all().delete() + Domain.objects.all().delete() + + @less_console_noise_decorator + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + domain, _ = Domain.objects.get_or_create(name="fake.gov", state=Domain.State.READY) + # Create a fake host + host, _ = Host.objects.get_or_create(name="ns1.test.gov", domain=domain) + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/host/{}/change/".format(host.pk), + follow=True, + ) + + # Make sure the page loaded + self.assertEqual(response.status_code, 200) + + # These should exist in the response + expected_values = [ + ("domain", "Domain associated with this host"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) + + class TestDomainInformationAdmin(TestCase): def setUp(self): """Setup environment for a mock admin user""" @@ -2367,6 +2520,38 @@ class TestDomainInformationAdmin(TestCase): Contact.objects.all().delete() User.objects.all().delete() + @less_console_noise_decorator + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + + # Create a fake domain request and domain + domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) + domain_request.approve() + domain_info = DomainInformation.objects.filter(domain=domain_request.approved_domain).get() + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/domaininformation/{}/change/".format(domain_info.pk), + follow=True, + ) + + # Make sure the page loaded, and that we're on the right page + self.assertEqual(response.status_code, 200) + self.assertContains(response, domain_info.domain.name) + + # These should exist in the response + expected_values = [ + ("creator", "Person who submitted the domain request"), + ("submitter", 'Person listed under "your contact information" in the request form'), + ("domain_request", "Request associated with this domain"), + ("no_other_contacts_rationale", "Required if creator does not list other employees"), + ("urbanization", "Required for Puerto Rico only"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) + @less_console_noise_decorator def test_other_contacts_has_readonly_link(self): """Tests if the readonly other_contacts field has links""" @@ -2711,7 +2896,7 @@ class UserDomainRoleAdminTest(TestCase): self.assertContains(response, "Joe Jones AntarcticPolarBears@example.com", count=1) -class ListHeaderAdminTest(TestCase): +class TestListHeaderAdmin(TestCase): def setUp(self): self.site = AdminSite() self.factory = RequestFactory() @@ -2784,10 +2969,43 @@ class ListHeaderAdminTest(TestCase): User.objects.all().delete() -class MyUserAdminTest(TestCase): +class TestMyUserAdmin(TestCase): def setUp(self): admin_site = AdminSite() self.admin = MyUserAdmin(model=get_user_model(), admin_site=admin_site) + self.client = Client(HTTP_HOST="localhost:8080") + self.superuser = create_superuser() + self.test_helper = GenericTestHelper(admin=self.admin) + + def tearDown(self): + super().tearDown() + User.objects.all().delete() + + @less_console_noise_decorator + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + user = create_user() + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/user/{}/change/".format(user.pk), + follow=True, + ) + + # Make sure the page loaded + self.assertEqual(response.status_code, 200) + + # These should exist in the response + expected_values = [ + ("password", "Raw passwords are not stored, so they will not display here."), + ("status", 'Users in "restricted" status cannot make updates in the registrar or start a new request.'), + ("is_staff", "Designates whether the user can log in to this admin site"), + ("is_superuser", "For development purposes only; provides superuser access on the database level"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) def test_list_display_without_username(self): with less_console_noise(): @@ -2809,8 +3027,9 @@ class MyUserAdminTest(TestCase): def test_get_fieldsets_superuser(self): with less_console_noise(): request = self.client.request().wsgi_request - request.user = create_superuser() + request.user = self.superuser fieldsets = self.admin.get_fieldsets(request) + expected_fieldsets = super(MyUserAdmin, self.admin).get_fieldsets(request) self.assertEqual(fieldsets, expected_fieldsets) @@ -2820,16 +3039,13 @@ class MyUserAdminTest(TestCase): request.user = create_user() fieldsets = self.admin.get_fieldsets(request) expected_fieldsets = ( - (None, {"fields": ("password", "status", "verification_type")}), + (None, {"fields": ("status", "verification_type",)}), ("Personal Info", {"fields": ("first_name", "last_name", "email")}), ("Permissions", {"fields": ("is_active", "groups")}), ("Important dates", {"fields": ("last_login", "date_joined")}), ) self.assertEqual(fieldsets, expected_fieldsets) - def tearDown(self): - User.objects.all().delete() - class AuditedAdminTest(TestCase): def setUp(self): @@ -3303,10 +3519,43 @@ class ContactAdminTest(TestCase): User.objects.all().delete() -class VerifiedByStaffAdminTestCase(TestCase): +class TestVerifiedByStaffAdmin(TestCase): def setUp(self): + super().setUp() + self.site = AdminSite() self.superuser = create_superuser() + self.admin = VerifiedByStaffAdmin(model=VerifiedByStaff, admin_site=self.site) self.factory = RequestFactory() + self.client = Client(HTTP_HOST="localhost:8080") + self.test_helper = GenericTestHelper(admin=self.admin) + + def tearDown(self): + super().tearDown() + VerifiedByStaff.objects.all().delete() + User.objects.all().delete() + + @less_console_noise_decorator + def test_helper_text(self): + """ + Tests for the correct helper text on this page + """ + vip_instance, _ = VerifiedByStaff.objects.get_or_create(email="test@example.com", notes="Test Notes") + + p = "adminpass" + self.client.login(username="superuser", password=p) + response = self.client.get( + "/admin/registrar/verifiedbystaff/{}/change/".format(vip_instance.pk), + follow=True, + ) + + # Make sure the page loaded + self.assertEqual(response.status_code, 200) + + # These should exist in the response + expected_values = [ + ("requestor", "Person who verified this user"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_values) def test_save_model_sets_user_field(self): with less_console_noise():