From d920c890df802ad94fc994888516b1bc033b3b7d Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Fri, 15 Mar 2024 12:29:07 -0700 Subject: [PATCH 01/61] Starting point for adding federal agency table --- src/registrar/models/federal_agency.py | 231 +++++++++++++++++++++++++ 1 file changed, 231 insertions(+) create mode 100644 src/registrar/models/federal_agency.py diff --git a/src/registrar/models/federal_agency.py b/src/registrar/models/federal_agency.py new file mode 100644 index 000000000..b6e72f58d --- /dev/null +++ b/src/registrar/models/federal_agency.py @@ -0,0 +1,231 @@ +from django.contrib.auth.models import Group +import logging + +logger = logging.getLogger(__name__) + + +# TODO: Update param model +class FederalAgency(Group): + class Meta: + verbose_name = "Federal agency" + verbose_name_plural = "Federal agencies" + + # TODO: Update parameters to put in + def create_federal_agencies(apps, schema_editor): + """This method gets run from a data migration.""" + + # Hard to pass self to these methods as the calls from migrations + # are only expecting apps and schema_editor, so we'll just define + # apps, schema_editor in the local scope instead + AGENCIES = [ + "Administrative Conference of the United States", + "Advisory Council on Historic Preservation", + "American Battle Monuments Commission", + "AMTRAK", + "Appalachian Regional Commission", + ("Appraisal Subcommittee of the Federal Financial " "Institutions Examination Council"), + "Architect of the Capitol", + "Armed Forces Retirement Home", + "Barry Goldwater Scholarship and Excellence in Education Foundation", + "Central Intelligence Agency", + "Christopher Columbus Fellowship Foundation", + "Civil Rights Cold Case Records Review Board", + "Commission for the Preservation of America's Heritage Abroad", + "Commission of Fine Arts", + "Committee for Purchase From People Who Are Blind or Severely Disabled", + "Commodity Futures Trading Commission", + "Congressional Budget Office", + "Consumer Financial Protection Bureau", + "Consumer Product Safety Commission", + "Corporation for National and Community Service", + "Council of Inspectors General on Integrity and Efficiency", + "Court Services and Offender Supervision", + "Cyberspace Solarium Commission", + "DC Court Services and Offender Supervision Agency", + "DC Pre-trial Services", + "Defense Nuclear Facilities Safety Board", + "Delta Regional Authority", + "Denali Commission", + "Department of Agriculture", + "Department of Commerce", + "Department of Defense", + "Department of Education", + "Department of Energy", + "Department of Health and Human Services", + "Department of Homeland Security", + "Department of Housing and Urban Development", + "Department of Justice", + "Department of Labor", + "Department of State", + "Department of the Interior", + "Department of the Treasury", + "Department of Transportation", + "Department of Veterans Affairs", + "Director of National Intelligence", + "Dwight D. Eisenhower Memorial Commission", + "Election Assistance Commission", + "Environmental Protection Agency", + "Equal Employment Opportunity Commission", + "Executive Office of the President", + "Export-Import Bank of the United States", + "Farm Credit Administration", + "Farm Credit System Insurance Corporation", + "Federal Communications Commission", + "Federal Deposit Insurance Corporation", + "Federal Election Commission", + "Federal Energy Regulatory Commission", + "Federal Financial Institutions Examination Council", + "Federal Housing Finance Agency", + "Federal Judiciary", + "Federal Labor Relations Authority", + "Federal Maritime Commission", + "Federal Mediation and Conciliation Service", + "Federal Mine Safety and Health Review Commission", + "Federal Permitting Improvement Steering Council", + "Federal Reserve Board of Governors", + "Federal Trade Commission", + "General Services Administration", + "gov Administration", + "Government Accountability Office", + "Government Publishing Office", + "Gulf Coast Ecosystem Restoration Council", + "Harry S. Truman Scholarship Foundation", + "Institute of Museum and Library Services", + "Institute of Peace", + "Inter-American Foundation", + "International Boundary and Water Commission: United States and Mexico", + "International Boundary Commission: United States and Canada", + "International Joint Commission: United States and Canada", + "James Madison Memorial Fellowship Foundation", + "Japan-U.S. Friendship Commission", + "John F. Kennedy Center for the Performing Arts", + "Legal Services Corporation", + "Legislative Branch", + "Library of Congress", + "Marine Mammal Commission", + "Medicaid and CHIP Payment and Access Commission", + "Medicare Payment Advisory Commission", + "Merit Systems Protection Board", + "Millennium Challenge Corporation", + "Morris K. Udall and Stewart L. Udall Foundation", + "National Aeronautics and Space Administration", + "National Archives and Records Administration", + "National Capital Planning Commission", + "National Council on Disability", + "National Credit Union Administration", + "National Endowment for the Arts", + "National Endowment for the Humanities", + "National Foundation on the Arts and the Humanities", + "National Gallery of Art", + "National Indian Gaming Commission", + "National Labor Relations Board", + "National Mediation Board", + "National Science Foundation", + "National Security Commission on Artificial Intelligence", + "National Transportation Safety Board", + "Networking Information Technology Research and Development", + "Non-Federal Agency", + "Northern Border Regional Commission", + "Nuclear Regulatory Commission", + "Nuclear Safety Oversight Committee", + "Occupational Safety and Health Review Commission", + "Office of Compliance", + "Office of Congressional Workplace Rights", + "Office of Government Ethics", + "Office of Navajo and Hopi Indian Relocation", + "Office of Personnel Management", + "Open World Leadership Center", + "Overseas Private Investment Corporation", + "Peace Corps", + "Pension Benefit Guaranty Corporation", + "Postal Regulatory Commission", + "Presidio Trust", + "Privacy and Civil Liberties Oversight Board", + "Public Buildings Reform Board", + "Public Defender Service for the District of Columbia", + "Railroad Retirement Board", + "Securities and Exchange Commission", + "Selective Service System", + "Small Business Administration", + "Smithsonian Institution", + "Social Security Administration", + "Social Security Advisory Board", + "Southeast Crescent Regional Commission", + "Southwest Border Regional Commission", + "State Justice Institute", + "Stennis Center for Public Service", + "Surface Transportation Board", + "Tennessee Valley Authority", + "The Executive Office of the President", + "The Intelligence Community", + "The Legislative Branch", + "The Supreme Court", + "The United States World War One Centennial Commission", + "U.S. Access Board", + "U.S. Agency for Global Media", + "U.S. Agency for International Development", + "U.S. Capitol Police", + "U.S. Chemical Safety Board", + "U.S. China Economic and Security Review Commission", + "U.S. Commission for the Preservation of Americas Heritage Abroad", + "U.S. Commission of Fine Arts", + "U.S. Commission on Civil Rights", + "U.S. Commission on International Religious Freedom", + "U.S. Courts", + "U.S. Department of Agriculture", + "U.S. Interagency Council on Homelessness", + "U.S. International Trade Commission", + "U.S. Nuclear Waste Technical Review Board", + "U.S. Office of Special Counsel", + "U.S. Postal Service", + "U.S. Semiquincentennial Commission", + "U.S. Trade and Development Agency", + "U.S.-China Economic and Security Review Commission", + "Udall Foundation", + "United States AbilityOne", + "United States Access Board", + "United States African Development Foundation", + "United States Agency for Global Media", + "United States Arctic Research Commission", + "United States Global Change Research Program", + "United States Holocaust Memorial Museum", + "United States Institute of Peace", + "United States Interagency Council on Homelessness", + "United States International Development Finance Corporation", + "United States International Trade Commission", + "United States Postal Service", + "United States Senate", + "United States Trade and Development Agency", + "Utah Reclamation Mitigation and Conservation Commission", + "Vietnam Education Foundation", + "Western Hemisphere Drug Policy Commission", + "Woodrow Wilson International Center for Scholars", + "World War I Centennial Commission", + ] + + # TODO: Get apps back here + FederalAgency = apps.get_model("registrar", "FederalAgency") + + try: + federal_agencies_list, _ = FederalAgency.objects.get_or_create( + name="cisa_analysts_group", + ) + + federal_agencies_list.federal_agency.clear() + + # TODO: Why is AGENCIES not loading here? + for agency in AGENCIES: + + # Assign the permissions to the group + federal_agencies_list.agency.add(*agency) + + # TODO: Maybe remove? + # Convert the permissions QuerySet to a list of codenames + agency_list = list(agency.values_list("codename", flat=True)) + + logger.debug(agency + " added to group " + federal_agencies_list.name) + + federal_agencies_list.save() + logger.debug("Federal agency added to table " + federal_agencies_list.name) + except Exception as e: + logger.error(f"Error creating fedearl agency: {e}") From caed970e14b02b2994c849421d1d4007921d5e5f Mon Sep 17 00:00:00 2001 From: CocoByte Date: Sun, 17 Mar 2024 22:01:28 -0600 Subject: [PATCH 02/61] Made other contacts and current websites read-only for analysts --- src/registrar/admin.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 4968da527..10ea1af29 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1098,6 +1098,8 @@ class DomainRequestAdmin(ListHeaderAdmin): "requested_domain", "approved_domain", "alternative_domains", + "other_contacts", + "current_websites", "purpose", "submitter", "no_other_contacts_rationale", From 2ad571354e16046a53ddba402be2ea8cfb1017ce Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 19 Mar 2024 11:15:05 -0600 Subject: [PATCH 03/61] Basics --- src/registrar/admin.py | 4 ++ src/registrar/templates/admin/fieldset.html | 49 ++++++++++++++ .../admin/domain_request_change_form.html | 8 +++ .../includes/domain_request_fieldset.html | 65 +++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 src/registrar/templates/admin/fieldset.html create mode 100644 src/registrar/templates/django/admin/domain_request_change_form.html create mode 100644 src/registrar/templates/django/admin/includes/domain_request_fieldset.html diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 5f571ac73..a5ad62492 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1099,6 +1099,8 @@ class DomainRequestAdmin(ListHeaderAdmin): "about_your_organization", "requested_domain", "approved_domain", + "other_contacts", + "current_websites", "alternative_domains", "purpose", "submitter", @@ -1119,6 +1121,8 @@ class DomainRequestAdmin(ListHeaderAdmin): # Table ordering ordering = ["requested_domain__name"] + change_form_template = "django/admin/domain_request_change_form.html" + # Trigger action when a fieldset is changed def save_model(self, request, obj, form, change): """Custom save_model definition that handles edge cases""" diff --git a/src/registrar/templates/admin/fieldset.html b/src/registrar/templates/admin/fieldset.html new file mode 100644 index 000000000..dadae5811 --- /dev/null +++ b/src/registrar/templates/admin/fieldset.html @@ -0,0 +1,49 @@ +{% load i18n static %} + +{% comment %} +This is copied from Djangos implementation of this template, with added "blocks" +It is not inherently customizable on its own, so we can modify this instead. +https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/includes/fieldset.html +{% endcomment %} +
+ {% block fieldset_title %} + {% if fieldset.name %}

{{ fieldset.name }}

{% endif %} + {% endblock fieldset_title %} + + {% block fieldset_description %} + {% if fieldset.description %} +
{{ fieldset.description|safe }}
+ {% endif %} + {% endblock fieldset_description %} + + {% block fieldset_lines %} + {% for line in fieldset %} +
+ {% if line.fields|length == 1 %}{{ line.errors }}{% else %}
{% endif %} + {% for field in line %} +
+ {% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %} +
+ {% if field.is_checkbox %} + {{ field.field }}{{ field.label_tag }} + {% else %} + {{ field.label_tag }} + {% if field.is_readonly %} +
{{ field.contents }}
+ {% else %} + {{ field.field }} + {% endif %} + {% endif %} +
+ {% if field.field.help_text %} +
+
{{ field.field.help_text|safe }}
+
+ {% endif %} +
+ {% endfor %} + {% if not line.fields|length == 1 %}
{% endif %} +
+ {% endfor %} + {% endblock fieldset_lines %} +
\ No newline at end of file diff --git a/src/registrar/templates/django/admin/domain_request_change_form.html b/src/registrar/templates/django/admin/domain_request_change_form.html new file mode 100644 index 000000000..e48ab37a0 --- /dev/null +++ b/src/registrar/templates/django/admin/domain_request_change_form.html @@ -0,0 +1,8 @@ +{% extends 'admin/change_form.html' %} +{% load i18n static %} + +{% block field_sets %} + {% for fieldset in adminform %} + {% include "django/admin/includes/domain_request_fieldset.html" %} + {% endfor %} +{% endblock %} diff --git a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html new file mode 100644 index 000000000..fb7232a45 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html @@ -0,0 +1,65 @@ +{% extends "admin/fieldset.html" %} + +{% comment %} +This is using a custom implementation fieldset.html (see admin/fieldset.html) +{% endcomment %} +{% block fieldset_lines %} +{% for line in fieldset %} +
+ {% if line.fields|length == 1 %}{{ line.errors }}{% else %}
{% endif %} + {% for field in line %} +
+ {% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %} +
+ {% if field.is_checkbox %} + {{ field.field }}{{ field.label_tag }} + {% else %} + {{ field.label_tag }} + {% if field.is_readonly %} +
{{ field.contents }}
+ {% else %} + {{ field.field }} + {% endif %} + {% endif %} +
+ {% if field.field.name == "creator" %} +
    + {% if original.creator.title %} +
  • {{ original.creator.title }}
  • + {% endif %} + + {% if original.creator.email %} +
  • {{ original.creator.email }}
  • + {% endif %} + + {% if original.creator.phone %} +
  • {{ original.creator.phone }}
  • + {% endif %} +
+ {% elif field.field.name == "submitter" %} +
    + {% if original.submitter.title %} +
  • {{ original.submitter.title }}
  • + {% endif %} + + {% if original.submitter.email %} +
  • {{ original.submitter.email }}
  • + {% endif %} + + {% if original.submitter.phone %} +
  • {{ original.submitter.phone }}
  • + {% endif %} +
+ {% endif %} + {% if field.field.help_text %} +
+
{{ field.field.help_text|safe }}
+
+ {% endif %} +
+ {% endfor %} + {% if not line.fields|length == 1 %}
{% endif %} +
+{% endfor %} + +{% endblock fieldset_lines %} From 7b5beba813b40a6994cd671d0aaa7d69d10b70d2 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 19 Mar 2024 10:36:14 -0700 Subject: [PATCH 04/61] Add initial federal agency model migration --- src/registrar/admin.py | 3 +- .../migrations/0077_federalagency.py | 26 +++++++++ src/registrar/models/__init__.py | 3 + src/registrar/models/federal_agency.py | 55 +++++++++---------- 4 files changed, 56 insertions(+), 31 deletions(-) create mode 100644 src/registrar/migrations/0077_federalagency.py diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 4968da527..82f010692 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -15,7 +15,7 @@ from django.contrib.contenttypes.models import ContentType from django.urls import reverse from dateutil.relativedelta import relativedelta # type: ignore from epplibwrapper.errors import ErrorCode, RegistryError -from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website +from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website, FederalAgency from registrar.utility import csv_export from registrar.utility.errors import FSMApplicationError, FSMErrorCodes from registrar.views.utility.mixins import OrderableFieldsMixin @@ -1839,6 +1839,7 @@ admin.site.register(models.DomainInvitation, DomainInvitationAdmin) admin.site.register(models.DomainInformation, DomainInformationAdmin) admin.site.register(models.Domain, DomainAdmin) admin.site.register(models.DraftDomain, DraftDomainAdmin) +admin.site.register(FederalAgency) # Host and HostIP removed from django admin because changes in admin # do not propagate to registry and logic not applied admin.site.register(models.Host, MyHostAdmin) diff --git a/src/registrar/migrations/0077_federalagency.py b/src/registrar/migrations/0077_federalagency.py new file mode 100644 index 000000000..3913fbde3 --- /dev/null +++ b/src/registrar/migrations/0077_federalagency.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.10 on 2024-03-19 05:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0076_alter_domainrequest_current_websites_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="FederalAgency", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("agency", models.CharField(blank=True, help_text="Federal agency", null=True)), + ], + options={ + "verbose_name": "Federal agency", + "verbose_name_plural": "Federal agencies", + }, + ), + ] diff --git a/src/registrar/models/__init__.py b/src/registrar/models/__init__.py index d203421ac..d3bbb3ae5 100644 --- a/src/registrar/models/__init__.py +++ b/src/registrar/models/__init__.py @@ -4,6 +4,7 @@ from .domain_request import DomainRequest from .domain_information import DomainInformation from .domain import Domain from .draft_domain import DraftDomain +from .federal_agency import FederalAgency from .host_ip import HostIP from .host import Host from .domain_invitation import DomainInvitation @@ -22,6 +23,7 @@ __all__ = [ "Domain", "DraftDomain", "DomainInvitation", + "FederalAgency", "HostIP", "Host", "UserDomainRole", @@ -39,6 +41,7 @@ auditlog.register(Domain) auditlog.register(DraftDomain) auditlog.register(DomainInvitation) auditlog.register(DomainInformation) +auditlog.register(FederalAgency) auditlog.register(HostIP) auditlog.register(Host) auditlog.register(UserDomainRole) diff --git a/src/registrar/models/federal_agency.py b/src/registrar/models/federal_agency.py index b6e72f58d..544f00a9b 100644 --- a/src/registrar/models/federal_agency.py +++ b/src/registrar/models/federal_agency.py @@ -1,22 +1,30 @@ -from django.contrib.auth.models import Group +from .utility.time_stamped_model import TimeStampedModel +from django.db import models import logging logger = logging.getLogger(__name__) # TODO: Update param model -class FederalAgency(Group): +class FederalAgency(TimeStampedModel): class Meta: verbose_name = "Federal agency" verbose_name_plural = "Federal agencies" + + agency = models.CharField( + null=True, + blank=True, + help_text="Federal agency", + ) # TODO: Update parameters to put in def create_federal_agencies(apps, schema_editor): """This method gets run from a data migration.""" - + # Hard to pass self to these methods as the calls from migrations # are only expecting apps and schema_editor, so we'll just define # apps, schema_editor in the local scope instead + AGENCIES = [ "Administrative Conference of the United States", "Advisory Council on Historic Preservation", @@ -202,30 +210,17 @@ class FederalAgency(Group): "Woodrow Wilson International Center for Scholars", "World War I Centennial Commission", ] - - # TODO: Get apps back here - FederalAgency = apps.get_model("registrar", "FederalAgency") - - try: - federal_agencies_list, _ = FederalAgency.objects.get_or_create( - name="cisa_analysts_group", - ) - - federal_agencies_list.federal_agency.clear() - - # TODO: Why is AGENCIES not loading here? - for agency in AGENCIES: - - # Assign the permissions to the group - federal_agencies_list.agency.add(*agency) - - # TODO: Maybe remove? - # Convert the permissions QuerySet to a list of codenames - agency_list = list(agency.values_list("codename", flat=True)) - - logger.debug(agency + " added to group " + federal_agencies_list.name) - - federal_agencies_list.save() - logger.debug("Federal agency added to table " + federal_agencies_list.name) - except Exception as e: - logger.error(f"Error creating fedearl agency: {e}") + + FederalAgency = apps.get_model("registrar", "FederalAgency") + logger.info("Creating federal agency table.") + + try: + for agency in AGENCIES: + federal_agencies_list, _ = FederalAgency.objects.get_or_create( + agency=agency, + ) + logger.debug(agency + " added to record " + federal_agencies_list.agency) + federal_agencies_list.save() + logger.debug("Federal agency added to table " + federal_agencies_list.agency) + except Exception as e: + logger.error(f"Error creating federal agency: {e}") From 1841b2f728ae1675f6b5729774eee88bcb85147c Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 19 Mar 2024 12:46:22 -0600 Subject: [PATCH 05/61] Styling --- src/registrar/assets/sass/_theme/_admin.scss | 14 +++++ .../includes/domain_request_fieldset.html | 52 ++++++++++++++----- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index 4f715de3c..596de17ac 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -316,3 +316,17 @@ input.admin-confirm-button { .usa-summary-box__dhs-color { color: $dhs-blue-70; } + +details.dja-detail-table { + background-color: var(--darkened-bg); + display: inline-table; + .dja-details-summary { + color: var(--header-link-color); + background-color: var(--primary); + } + .dja-details-contents { + tr { + background-color: var(--darkened-bg); + } + } +} diff --git a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html index fb7232a45..a05a80f46 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html @@ -37,19 +37,45 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endif %} {% elif field.field.name == "submitter" %} - +
+ Details +
+ + + + + + + + + + + + + + + +
Title{{ original.submitter.title }}
Email{{ original.submitter.email }}
Phone{{ original.submitter.phone }}
+
+
+ {% elif field.field.name == "other_contacts" %} +
+ Details +
+ + + {% for contact in original.other_contacts.all %} + + + + + + + {% endfor %} + +
{{contact.first_name}} {{contact.last_name}}{{ contact.title }}{{ contact.email }}{{ contact.phone }}
+
+
{% endif %} {% if field.field.help_text %}
From 7ea27e3aa287be3c6a1c1039e3127f5e08bc62d1 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 19 Mar 2024 13:50:25 -0600 Subject: [PATCH 06/61] Slight abstraction --- .../includes/domain_request_detail_table.html | 27 ++++++++++++ .../includes/domain_request_fieldset.html | 41 +++---------------- 2 files changed, 33 insertions(+), 35 deletions(-) create mode 100644 src/registrar/templates/django/admin/includes/domain_request_detail_table.html diff --git a/src/registrar/templates/django/admin/includes/domain_request_detail_table.html b/src/registrar/templates/django/admin/includes/domain_request_detail_table.html new file mode 100644 index 000000000..5fbffa425 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/domain_request_detail_table.html @@ -0,0 +1,27 @@ +{% load i18n static %} + +{% if start_opened %} +
+{% else %} +
+{% endif%} + Details +
+ + + + + + + + + + + + + + + +
Title{{ user.title }}
Email{{ user.email }}
Phone{{ user.phone }}
+
+
\ No newline at end of file diff --git a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html index a05a80f46..d034d2441 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html @@ -23,43 +23,13 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endif %}
{% if field.field.name == "creator" %} - + {% include "django/admin/includes/domain_request_detail_table.html" with user=original.creator start_opened=True %} {% elif field.field.name == "submitter" %} -
- Details -
- - - - - - - - - - - - - - - -
Title{{ original.submitter.title }}
Email{{ original.submitter.email }}
Phone{{ original.submitter.phone }}
-
-
+ {% include "django/admin/includes/domain_request_detail_table.html" with user=original.submitter start_opened=True %} + {% elif field.field.name == "authorizing_official" %} + {% include "django/admin/includes/domain_request_detail_table.html" with user=original.authorizing_official start_opened=True %} {% elif field.field.name == "other_contacts" %} -
+
Details
@@ -77,6 +47,7 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endif %} + {% if field.field.help_text %}
{{ field.field.help_text|safe }}
From 983f1bc00ee1dbca0cb278450b3f495f713f7b92 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:06:00 -0600 Subject: [PATCH 07/61] Update domain_request_detail_table.html --- .../admin/includes/domain_request_detail_table.html | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/registrar/templates/django/admin/includes/domain_request_detail_table.html b/src/registrar/templates/django/admin/includes/domain_request_detail_table.html index 5fbffa425..f34ffa491 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_detail_table.html +++ b/src/registrar/templates/django/admin/includes/domain_request_detail_table.html @@ -1,12 +1,10 @@ {% load i18n static %} -{% if start_opened %} -
-{% else %} -
-{% endif%} + +
Details
+ {% if user.title or user.email or user.phone %}
@@ -23,5 +21,8 @@
+ {% else %} +
No details found
+ {% endif %}
\ No newline at end of file From 9952d248f6dd3fba0808debb354b3d9ad563c6ea Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:07:02 -0600 Subject: [PATCH 08/61] Update admin.py --- src/registrar/admin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 2059d1d69..38f3ecb2c 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1099,8 +1099,6 @@ class DomainRequestAdmin(ListHeaderAdmin): "about_your_organization", "requested_domain", "approved_domain", - "other_contacts", - "current_websites", "alternative_domains", "other_contacts", "current_websites", From d62e95fd10f8769a4a5546079ee4700c66a95df0 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:40:36 -0700 Subject: [PATCH 09/61] Populate fed agency seed data --- src/registrar/migrations/0077_federalagency.py | 12 ++++++++++++ src/registrar/models/federal_agency.py | 3 +++ 2 files changed, 15 insertions(+) diff --git a/src/registrar/migrations/0077_federalagency.py b/src/registrar/migrations/0077_federalagency.py index 3913fbde3..06c4e932d 100644 --- a/src/registrar/migrations/0077_federalagency.py +++ b/src/registrar/migrations/0077_federalagency.py @@ -1,7 +1,14 @@ # Generated by Django 4.2.10 on 2024-03-19 05:42 +# This migration seeds the federal agency table with initial data and manually calls +# FederalAgency.create_federal_agencies to populate initial table values. from django.db import migrations, models +from registrar.models import FederalAgency +from typing import Any +# For linting: RunPython expects a function reference. +def create_federal_agencies(apps, schema_editor) -> Any: + FederalAgency.create_federal_agencies(apps, schema_editor) class Migration(migrations.Migration): @@ -23,4 +30,9 @@ class Migration(migrations.Migration): "verbose_name_plural": "Federal agencies", }, ), + migrations.RunPython( + create_federal_agencies, + reverse_code=migrations.RunPython.noop, + atomic=True, + ), ] diff --git a/src/registrar/models/federal_agency.py b/src/registrar/models/federal_agency.py index 544f00a9b..fc20a0c29 100644 --- a/src/registrar/models/federal_agency.py +++ b/src/registrar/models/federal_agency.py @@ -17,6 +17,9 @@ class FederalAgency(TimeStampedModel): help_text="Federal agency", ) + def __str__(self) -> str: + return self.agency + # TODO: Update parameters to put in def create_federal_agencies(apps, schema_editor): """This method gets run from a data migration.""" From 712bb26d03df5693277de892a1177bafe023959b Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:48:25 -0700 Subject: [PATCH 10/61] Add search --- src/registrar/admin.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 82f010692..bfd432c85 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1825,6 +1825,11 @@ class VerifiedByStaffAdmin(ListHeaderAdmin): obj.requestor = request.user if request.user.is_authenticated else None super().save_model(request, obj, form, change) +class FederalAgencyAdmin(ListHeaderAdmin): + search_fields = ["agency"] + search_help_text = "Search by agency name." + ordering = ["agency"] + admin.site.unregister(LogEntry) # Unregister the default registration admin.site.register(LogEntry, CustomLogEntryAdmin) @@ -1839,7 +1844,7 @@ admin.site.register(models.DomainInvitation, DomainInvitationAdmin) admin.site.register(models.DomainInformation, DomainInformationAdmin) admin.site.register(models.Domain, DomainAdmin) admin.site.register(models.DraftDomain, DraftDomainAdmin) -admin.site.register(FederalAgency) +admin.site.register(models.FederalAgency, FederalAgencyAdmin) # Host and HostIP removed from django admin because changes in admin # do not propagate to registry and logic not applied admin.site.register(models.Host, MyHostAdmin) From a8c10816b8e6913b57ba9369d7adae4fcbb0eb10 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:49:32 -0600 Subject: [PATCH 11/61] Add links to urls --- src/registrar/assets/js/get-gov-admin.js | 44 +++++++++++++++++++ src/registrar/assets/sass/_theme/_admin.scss | 32 ++++++++++++++ .../includes/domain_request_detail_table.html | 42 +++++++----------- .../includes/domain_request_fieldset.html | 24 +++++++--- src/registrar/templatetags/url_helpers.py | 7 +++ 5 files changed, 117 insertions(+), 32 deletions(-) diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index 4ed00c33f..28149999c 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -170,6 +170,50 @@ function checkToListThenInitWidget(toListId, attempts) { } } +(function() { + function addUrlsToOtherContacts() { + document.addEventListener('DOMContentLoaded', function() { + // Select all table rows that have a data-contact-id attribute + let contactRows = document.querySelectorAll("tr[data-contact-url]"); + + if (contactRows){ + // Add a click event listener to each row + let index = 1; + contactRows.forEach(function(row) { + let otherContactUrl = row.getAttribute("data-contact-url"); + if (otherContactUrl){ + let otherContact = document.querySelector(`.other-contact__${index}`); + if (otherContact) { + otherContact.href = otherContactUrl; + } + } + index++; + }); + } + }); + } + + addUrlsToOtherContacts() +})(); + +document.addEventListener('DOMContentLoaded', function() { + // Select all table rows that have a data-contact-id attribute + let contactRows = document.querySelectorAll("tr[data-contact-url]"); + + // Add a click event listener to each row + index = 1 + contactRows.forEach(function(row) { + let otherContactUrl = row.getAttribute("data-contact-url"); + if (otherContactUrl){ + let otherContact = document.querySelector(`.other-contact__${index}`) + if (otherContact) { + otherContact.href = otherContactUrl + } + } + index++ + }); +}); + // Initialize the widget: // add related buttons to the widget for edit, delete and view // add event listeners on the from list, the to list, and selector buttons which either enable or disable the related buttons diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index 596de17ac..98e5009db 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -330,3 +330,35 @@ details.dja-detail-table { } } } + +table.dja-user-detail-table { + margin-left: 160px; + tr { + background-color: var(--body-bg); + } +} + +.admin-icon-group { + position: relative; + display: flex; + align-items: center; + + .usa-button__icon { + position: absolute; + right: 0; + height: 100%; + } + + input { + // Allow for padding around the copy button + padding-right: 35px; + // Match the height of other inputs + min-height: 2.25rem; + } + + .no-outline-on-click:focus { + outline: none !important; + } +} + + diff --git a/src/registrar/templates/django/admin/includes/domain_request_detail_table.html b/src/registrar/templates/django/admin/includes/domain_request_detail_table.html index f34ffa491..fba588b61 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_detail_table.html +++ b/src/registrar/templates/django/admin/includes/domain_request_detail_table.html @@ -1,28 +1,18 @@ {% load i18n static %} - -
- Details -
- {% if user.title or user.email or user.phone %} - - - - - - - - - - - - - - - -
Title{{ user.title }}
Email{{ user.email }}
Phone{{ user.phone }}
- {% else %} -
No details found
- {% endif %} -
-
\ No newline at end of file + + + + + + + + + + + + + + + +
Title{{ user.title }}
Email{{ user.email }}
Phone{{ user.phone }}
diff --git a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html index d034d2441..c90423474 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html @@ -1,5 +1,5 @@ {% extends "admin/fieldset.html" %} - +{% load static url_helpers %} {% comment %} This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endcomment %} @@ -16,18 +16,26 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% else %} {{ field.label_tag }} {% if field.is_readonly %} -
{{ field.contents }}
+ {% if field.field.name == "other_contacts" %} +
+ {% for contact in field.contents|split:", " %} + {{ contact }}{% if not forloop.last %}, {% endif %} + {% endfor %} +
+ {% else %} +
{{ field.contents }}
+ {% endif %} {% else %} {{ field.field }} {% endif %} {% endif %} {% if field.field.name == "creator" %} - {% include "django/admin/includes/domain_request_detail_table.html" with user=original.creator start_opened=True %} + {% include "django/admin/includes/domain_request_detail_table.html" with user=original.creator %} {% elif field.field.name == "submitter" %} - {% include "django/admin/includes/domain_request_detail_table.html" with user=original.submitter start_opened=True %} + {% include "django/admin/includes/domain_request_detail_table.html" with user=original.submitter %} {% elif field.field.name == "authorizing_official" %} - {% include "django/admin/includes/domain_request_detail_table.html" with user=original.authorizing_official start_opened=True %} + {% include "django/admin/includes/domain_request_detail_table.html" with user=original.authorizing_official %} {% elif field.field.name == "other_contacts" %}
Details @@ -35,7 +43,11 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% for contact in original.other_contacts.all %} - + {% comment %} + Since we can't get the id from field, we can embed this information here. + Then we can link these two fields using javascript. + {% endcomment %} + diff --git a/src/registrar/templatetags/url_helpers.py b/src/registrar/templatetags/url_helpers.py index 931eedc92..9413892aa 100644 --- a/src/registrar/templatetags/url_helpers.py +++ b/src/registrar/templatetags/url_helpers.py @@ -26,6 +26,13 @@ def endswith(text, ends): return False +@register.filter("split") +def split_string(value, key): + """ + Splits a given string + """ + return value.split(key) + @register.simple_tag def public_site_url(url_path): """Make a full URL for this path at our public site. From 664b3eb895e1a4124f649ad35236ba5af6c9e584 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:51:48 -0700 Subject: [PATCH 12/61] Add sorting to federal agency table --- src/registrar/admin.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index bfd432c85..43a416d0f 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1826,6 +1826,7 @@ class VerifiedByStaffAdmin(ListHeaderAdmin): super().save_model(request, obj, form, change) class FederalAgencyAdmin(ListHeaderAdmin): + list_display = ["agency"] search_fields = ["agency"] search_help_text = "Search by agency name." ordering = ["agency"] From 5a5faecb2a6c905e140b7b43f5ebb0f8997d8314 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:02:49 -0700 Subject: [PATCH 13/61] Update test_migrations with federal agency permissions --- src/registrar/models/user_group.py | 5 +++++ src/registrar/tests/test_migrations.py | 3 +++ 2 files changed, 8 insertions(+) diff --git a/src/registrar/models/user_group.py b/src/registrar/models/user_group.py index a84da798a..2aa2f642e 100644 --- a/src/registrar/models/user_group.py +++ b/src/registrar/models/user_group.py @@ -71,6 +71,11 @@ class UserGroup(Group): "model": "verifiedbystaff", "permissions": ["add_verifiedbystaff", "change_verifiedbystaff", "delete_verifiedbystaff"], }, + { + "app_label": "registrar", + "model": "federalagency", + "permissions": ["add_federalagency", "change_federalagency", "delete_federalagency"], + }, ] # Avoid error: You can't execute queries until the end diff --git a/src/registrar/tests/test_migrations.py b/src/registrar/tests/test_migrations.py index 50861d97f..bf3b09d0d 100644 --- a/src/registrar/tests/test_migrations.py +++ b/src/registrar/tests/test_migrations.py @@ -39,6 +39,9 @@ class TestGroups(TestCase): "view_domaininvitation", "change_domainrequest", "change_draftdomain", + "add_federalagency", + "change_federalagency", + "delete_federalagency", "analyst_access_permission", "change_user", "delete_userdomainrole", From 601ded68ac4f4497025a35ec456f68dc4ba31146 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:19:44 -0700 Subject: [PATCH 14/61] Fix lint errors --- src/registrar/admin.py | 4 ++-- src/registrar/migrations/0077_federalagency.py | 2 ++ src/registrar/models/federal_agency.py | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index ac1591584..9916027e6 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -15,8 +15,7 @@ from django.contrib.contenttypes.models import ContentType from django.urls import reverse from dateutil.relativedelta import relativedelta # type: ignore from epplibwrapper.errors import ErrorCode, RegistryError -from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website, FederalAgency -from registrar.utility import csv_export +from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website from registrar.utility.errors import FSMApplicationError, FSMErrorCodes from registrar.views.utility.mixins import OrderableFieldsMixin from django.contrib.admin.views.main import ORDER_VAR @@ -1789,6 +1788,7 @@ class VerifiedByStaffAdmin(ListHeaderAdmin): obj.requestor = request.user if request.user.is_authenticated else None super().save_model(request, obj, form, change) + class FederalAgencyAdmin(ListHeaderAdmin): list_display = ["agency"] search_fields = ["agency"] diff --git a/src/registrar/migrations/0077_federalagency.py b/src/registrar/migrations/0077_federalagency.py index 06c4e932d..0b3be0f70 100644 --- a/src/registrar/migrations/0077_federalagency.py +++ b/src/registrar/migrations/0077_federalagency.py @@ -6,10 +6,12 @@ from django.db import migrations, models from registrar.models import FederalAgency from typing import Any + # For linting: RunPython expects a function reference. def create_federal_agencies(apps, schema_editor) -> Any: FederalAgency.create_federal_agencies(apps, schema_editor) + class Migration(migrations.Migration): dependencies = [ diff --git a/src/registrar/models/federal_agency.py b/src/registrar/models/federal_agency.py index fc20a0c29..5ccd69b50 100644 --- a/src/registrar/models/federal_agency.py +++ b/src/registrar/models/federal_agency.py @@ -10,7 +10,7 @@ class FederalAgency(TimeStampedModel): class Meta: verbose_name = "Federal agency" verbose_name_plural = "Federal agencies" - + agency = models.CharField( null=True, blank=True, @@ -18,12 +18,12 @@ class FederalAgency(TimeStampedModel): ) def __str__(self) -> str: - return self.agency + return f"{self.agency}" # TODO: Update parameters to put in def create_federal_agencies(apps, schema_editor): """This method gets run from a data migration.""" - + # Hard to pass self to these methods as the calls from migrations # are only expecting apps and schema_editor, so we'll just define # apps, schema_editor in the local scope instead @@ -213,10 +213,10 @@ class FederalAgency(TimeStampedModel): "Woodrow Wilson International Center for Scholars", "World War I Centennial Commission", ] - + FederalAgency = apps.get_model("registrar", "FederalAgency") logger.info("Creating federal agency table.") - + try: for agency in AGENCIES: federal_agencies_list, _ = FederalAgency.objects.get_or_create( From d0826fe8473601a6d01fa33c1405cc4e23b299d9 Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 19 Mar 2024 15:21:11 -0700 Subject: [PATCH 15/61] Update tests to reflect federal agency text occurrences --- src/registrar/tests/test_admin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 9666633e9..af9d9aca3 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -284,7 +284,7 @@ class TestDomainAdmin(MockEppLib, WebTest): # There are 4 template references to Federal (4) plus four references in the table # for our actual domain_request - self.assertContains(response, "Federal", count=8) + self.assertContains(response, "Federal", count=36) # This may be a bit more robust self.assertContains(response, '', count=1) # Now let's make sure the long description does not exist @@ -690,7 +690,7 @@ class TestDomainRequestAdmin(MockEppLib): response = self.client.get("/admin/registrar/domainrequest/") # There are 4 template references to Federal (4) plus two references in the table # for our actual domain request - self.assertContains(response, "Federal", count=6) + self.assertContains(response, "Federal", count=34) # This may be a bit more robust self.assertContains(response, '', count=1) # Now let's make sure the long description does not exist From c4c1fd602c5e6909228fee30634c79b0c9949652 Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Tue, 19 Mar 2024 15:34:30 -0700 Subject: [PATCH 16/61] Remove todos --- src/registrar/models/federal_agency.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/registrar/models/federal_agency.py b/src/registrar/models/federal_agency.py index 5ccd69b50..c99843b31 100644 --- a/src/registrar/models/federal_agency.py +++ b/src/registrar/models/federal_agency.py @@ -5,7 +5,6 @@ import logging logger = logging.getLogger(__name__) -# TODO: Update param model class FederalAgency(TimeStampedModel): class Meta: verbose_name = "Federal agency" @@ -20,7 +19,6 @@ class FederalAgency(TimeStampedModel): def __str__(self) -> str: return f"{self.agency}" - # TODO: Update parameters to put in def create_federal_agencies(apps, schema_editor): """This method gets run from a data migration.""" From e0aa729133043d7ca74c348b2b3cf8fe03ab78b3 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 20 Mar 2024 09:11:51 -0600 Subject: [PATCH 17/61] Fix merge conflicts --- src/registrar/assets/js/get-gov-admin.js | 18 --- src/registrar/assets/js/get-gov-reports.js | 122 ++++++++++-------- .../admin/domain_application_change_form.html | 96 -------------- .../admin/domain_request_change_form.html | 90 +++++++++++++ src/registrar/tests/test_admin.py | 2 + 5 files changed, 160 insertions(+), 168 deletions(-) delete mode 100644 src/registrar/templates/django/admin/domain_application_change_form.html diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index fe03d6864..3d72332fd 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -196,24 +196,6 @@ function checkToListThenInitWidget(toListId, attempts) { addUrlsToOtherContacts() })(); -document.addEventListener('DOMContentLoaded', function() { - // Select all table rows that have a data-contact-id attribute - let contactRows = document.querySelectorAll("tr[data-contact-url]"); - - // Add a click event listener to each row - index = 1 - contactRows.forEach(function(row) { - let otherContactUrl = row.getAttribute("data-contact-url"); - if (otherContactUrl){ - let otherContact = document.querySelector(`.other-contact__${index}`) - if (otherContact) { - otherContact.href = otherContactUrl - } - } - index++ - }); -}); - // Initialize the widget: // add related buttons to the widget for edit, delete and view // add event listeners on the from list, the to list, and selector buttons which either enable or disable the related buttons diff --git a/src/registrar/assets/js/get-gov-reports.js b/src/registrar/assets/js/get-gov-reports.js index d10cf2dc6..2fc9b6219 100644 --- a/src/registrar/assets/js/get-gov-reports.js +++ b/src/registrar/assets/js/get-gov-reports.js @@ -54,64 +54,78 @@ })(); -document.addEventListener("DOMContentLoaded", function () { - createComparativeColumnChart("myChart1", "Managed domains", "Start Date", "End Date"); - createComparativeColumnChart("myChart2", "Unmanaged domains", "Start Date", "End Date"); - createComparativeColumnChart("myChart3", "Deleted domains", "Start Date", "End Date"); - createComparativeColumnChart("myChart4", "Ready domains", "Start Date", "End Date"); - createComparativeColumnChart("myChart5", "Submitted requests", "Start Date", "End Date"); - createComparativeColumnChart("myChart6", "All requests", "Start Date", "End Date"); -}); -function createComparativeColumnChart(canvasId, title, labelOne, labelTwo) { - var canvas = document.getElementById(canvasId); - var ctx = canvas.getContext("2d"); +/** An IIFE to initialize the analytics page +*/ +(function () { + function createComparativeColumnChart(canvasId, title, labelOne, labelTwo) { + var canvas = document.getElementById(canvasId); + if (!canvas) { + console.log("Could not find canvas") + return + } - var listOne = JSON.parse(canvas.getAttribute('data-list-one')); - var listTwo = JSON.parse(canvas.getAttribute('data-list-two')); + var ctx = canvas.getContext("2d"); - var data = { - labels: ["Total", "Federal", "Interstate", "State/Territory", "Tribal", "County", "City", "Special District", "School District", "Election Board"], - datasets: [ - { - label: labelOne, - backgroundColor: "rgba(255, 99, 132, 0.2)", - borderColor: "rgba(255, 99, 132, 1)", - borderWidth: 1, - data: listOne, + var listOne = JSON.parse(canvas.getAttribute('data-list-one')); + var listTwo = JSON.parse(canvas.getAttribute('data-list-two')); + + var data = { + labels: ["Total", "Federal", "Interstate", "State/Territory", "Tribal", "County", "City", "Special District", "School District", "Election Board"], + datasets: [ + { + label: labelOne, + backgroundColor: "rgba(255, 99, 132, 0.2)", + borderColor: "rgba(255, 99, 132, 1)", + borderWidth: 1, + data: listOne, + }, + { + label: labelTwo, + backgroundColor: "rgba(75, 192, 192, 0.2)", + borderColor: "rgba(75, 192, 192, 1)", + borderWidth: 1, + data: listTwo, + }, + ], + }; + + var options = { + responsive: true, + maintainAspectRatio: false, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: title + } }, - { - label: labelTwo, - backgroundColor: "rgba(75, 192, 192, 0.2)", - borderColor: "rgba(75, 192, 192, 1)", - borderWidth: 1, - data: listTwo, + scales: { + y: { + beginAtZero: true, + }, }, - ], + }; + + new Chart(ctx, { + type: "bar", + data: data, + options: options, + }); + } + + function initComparativeColumnCharts() { + document.addEventListener("DOMContentLoaded", function () { + createComparativeColumnChart("myChart1", "Managed domains", "Start Date", "End Date"); + createComparativeColumnChart("myChart2", "Unmanaged domains", "Start Date", "End Date"); + createComparativeColumnChart("myChart3", "Deleted domains", "Start Date", "End Date"); + createComparativeColumnChart("myChart4", "Ready domains", "Start Date", "End Date"); + createComparativeColumnChart("myChart5", "Submitted requests", "Start Date", "End Date"); + createComparativeColumnChart("myChart6", "All requests", "Start Date", "End Date"); + }); }; - var options = { - responsive: true, - maintainAspectRatio: false, - plugins: { - legend: { - position: 'top', - }, - title: { - display: true, - text: title - } - }, - scales: { - y: { - beginAtZero: true, - }, - }, - }; - - new Chart(ctx, { - type: "bar", - data: data, - options: options, - }); -} \ No newline at end of file + initComparativeColumnCharts(); +})(); \ No newline at end of file diff --git a/src/registrar/templates/django/admin/domain_application_change_form.html b/src/registrar/templates/django/admin/domain_application_change_form.html deleted file mode 100644 index 95392da1e..000000000 --- a/src/registrar/templates/django/admin/domain_application_change_form.html +++ /dev/null @@ -1,96 +0,0 @@ -{% extends 'admin/change_form.html' %} -{% load i18n static %} - -{% block field_sets %} - {# Create an invisible tag so that we can use a click event to toggle the modal. #} - - {{ block.super }} -{% endblock %} - -{% block submit_buttons_bottom %} - {% comment %} - Modals behave very weirdly in django admin. - They tend to "strip out" any injected form elements, leaving only the main form. - In addition, USWDS handles modals by first destroying the element, then repopulating it toward the end of the page. - In effect, this means that the modal is not, and cannot, be surrounded by any form element at compile time. - - The current workaround for this is to use javascript to inject a hidden input, and bind submit of that - element to the click of the confirmation button within this modal. - - This is controlled by the class `dja-form-placeholder` on the button. - - In addition, the modal element MUST be placed low in the DOM. The script loads slower on DJA than on other portions - of the application, so this means that it will briefly "populate", causing unintended visual effects. - {% endcomment %} - {# Create a modal for when a domain is marked as ineligible #} -
-
-
- -
-

- When a domain request is in ineligible status, the registrant's permissions within the registrar are restricted as follows: -

-
    -
  • They cannot edit the ineligible request or any other pending requests.
  • -
  • They cannot manage any of their approved domains.
  • -
  • They cannot initiate a new domain request.
  • -
-

- The restrictions will not take effect until you “save” the changes for this domain request. - This action can be reversed, if needed. -

-

- Domain: {{ original.requested_domain.name }} - {# Acts as a
#} -

- New status: {{ original.DomainRequestStatus.INELIGIBLE|capfirst }} -

-
- - -
- -
-
-{{ block.super }} -{% endblock %} \ No newline at end of file diff --git a/src/registrar/templates/django/admin/domain_request_change_form.html b/src/registrar/templates/django/admin/domain_request_change_form.html index e48ab37a0..5d5e6ed60 100644 --- a/src/registrar/templates/django/admin/domain_request_change_form.html +++ b/src/registrar/templates/django/admin/domain_request_change_form.html @@ -2,7 +2,97 @@ {% load i18n static %} {% block field_sets %} + {# Create an invisible tag so that we can use a click event to toggle the modal. #} + {% for fieldset in adminform %} {% include "django/admin/includes/domain_request_fieldset.html" %} {% endfor %} {% endblock %} + +{% block submit_buttons_bottom %} + {% comment %} + Modals behave very weirdly in django admin. + They tend to "strip out" any injected form elements, leaving only the main form. + In addition, USWDS handles modals by first destroying the element, then repopulating it toward the end of the page. + In effect, this means that the modal is not, and cannot, be surrounded by any form element at compile time. + + The current workaround for this is to use javascript to inject a hidden input, and bind submit of that + element to the click of the confirmation button within this modal. + + This is controlled by the class `dja-form-placeholder` on the button. + + In addition, the modal element MUST be placed low in the DOM. The script loads slower on DJA than on other portions + of the application, so this means that it will briefly "populate", causing unintended visual effects. + {% endcomment %} + {# Create a modal for when a domain is marked as ineligible #} +
+
+
+ +
+

+ When a domain request is in ineligible status, the registrant's permissions within the registrar are restricted as follows: +

+
    +
  • They cannot edit the ineligible request or any other pending requests.
  • +
  • They cannot manage any of their approved domains.
  • +
  • They cannot initiate a new domain request.
  • +
+

+ The restrictions will not take effect until you “save” the changes for this domain request. + This action can be reversed, if needed. +

+

+ Domain: {{ original.requested_domain.name }} + {# Acts as a
#} +

+ New status: {{ original.DomainRequestStatus.INELIGIBLE|capfirst }} +

+
+ + +
+ +
+
+{{ block.super }} +{% endblock %} diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 9666633e9..d22973971 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1339,6 +1339,8 @@ class TestDomainRequestAdmin(MockEppLib): "about_your_organization", "requested_domain", "approved_domain", + "other_contacts", + "current_websites", "alternative_domains", "purpose", "submitter", From 62b405959aba0c4daafab4a5696ea6f844270bbe Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Wed, 20 Mar 2024 09:33:11 -0700 Subject: [PATCH 18/61] Add in permissions migration for federal agency --- .../migrations/0078_create_groups_v09.py | 37 +++++++++++++++++++ src/registrar/models/federal_agency.py | 2 + 2 files changed, 39 insertions(+) create mode 100644 src/registrar/migrations/0078_create_groups_v09.py diff --git a/src/registrar/migrations/0078_create_groups_v09.py b/src/registrar/migrations/0078_create_groups_v09.py new file mode 100644 index 000000000..6f89e0cb6 --- /dev/null +++ b/src/registrar/migrations/0078_create_groups_v09.py @@ -0,0 +1,37 @@ +# This migration creates the create_full_access_group and create_cisa_analyst_group groups +# It is dependent on 0035 (which populates ContentType and Permissions) +# If permissions on the groups need changing, edit CISA_ANALYST_GROUP_PERMISSIONS +# in the user_group model then: +# [NOT RECOMMENDED] +# step 1: docker-compose exec app ./manage.py migrate --fake registrar 0035_contenttypes_permissions +# step 2: docker-compose exec app ./manage.py migrate registrar 0036_create_groups +# step 3: fake run the latest migration in the migrations list +# [RECOMMENDED] +# Alternatively: +# step 1: duplicate the migration that loads data +# step 2: docker-compose exec app ./manage.py migrate + +from django.db import migrations +from registrar.models import UserGroup +from typing import Any + + +# For linting: RunPython expects a function reference, +# so let's give it one +def create_groups(apps, schema_editor) -> Any: + UserGroup.create_cisa_analyst_group(apps, schema_editor) + UserGroup.create_full_access_group(apps, schema_editor) + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0077_federalagency"), + ] + + operations = [ + migrations.RunPython( + create_groups, + reverse_code=migrations.RunPython.noop, + atomic=True, + ), + ] diff --git a/src/registrar/models/federal_agency.py b/src/registrar/models/federal_agency.py index c99843b31..0a68beb57 100644 --- a/src/registrar/models/federal_agency.py +++ b/src/registrar/models/federal_agency.py @@ -213,6 +213,8 @@ class FederalAgency(TimeStampedModel): ] FederalAgency = apps.get_model("registrar", "FederalAgency") + ContentType = apps.get_model("contenttypes", "ContentType") + logger.info("Creating federal agency table.") try: From e55c0c72e77cd442c899380552bd7968261b405a Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Wed, 20 Mar 2024 09:37:10 -0700 Subject: [PATCH 19/61] Remove contenttype --- src/registrar/models/federal_agency.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/registrar/models/federal_agency.py b/src/registrar/models/federal_agency.py index 0a68beb57..f1f19e845 100644 --- a/src/registrar/models/federal_agency.py +++ b/src/registrar/models/federal_agency.py @@ -213,7 +213,6 @@ class FederalAgency(TimeStampedModel): ] FederalAgency = apps.get_model("registrar", "FederalAgency") - ContentType = apps.get_model("contenttypes", "ContentType") logger.info("Creating federal agency table.") From 54603cbfad06d667781ff9b733149363813b0517 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:02:25 -0600 Subject: [PATCH 20/61] Unit tests, refactoring --- docs/developer/README.md | 10 ++++ .../includes/domain_request_detail_table.html | 32 ++++++++-- .../includes/domain_request_fieldset.html | 6 +- src/registrar/tests/common.py | 1 + src/registrar/tests/test_admin.py | 60 ++++++++++++++++++- 5 files changed, 101 insertions(+), 8 deletions(-) diff --git a/docs/developer/README.md b/docs/developer/README.md index 9f2e131b6..a06202d8d 100644 --- a/docs/developer/README.md +++ b/docs/developer/README.md @@ -204,6 +204,16 @@ from .common import less_console_noise # ``` +Or alternatively, if you prefer using a decorator, just use: + +```python +from .common import less_console_noise_decorator + +@less_console_noise_decorator +def some_function(): + # +``` + ### Accessibility Testing in the browser We use the [ANDI](https://www.ssa.gov/accessibility/andi/help/install.html) browser extension diff --git a/src/registrar/templates/django/admin/includes/domain_request_detail_table.html b/src/registrar/templates/django/admin/includes/domain_request_detail_table.html index fba588b61..c25812b4d 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_detail_table.html +++ b/src/registrar/templates/django/admin/includes/domain_request_detail_table.html @@ -1,18 +1,42 @@ {% load i18n static %} -
{{contact.first_name}} {{contact.last_name}} {{ contact.title }} {{ contact.email }}FederalFederal
+
- + {% if user.title or user.contact.title %} + {% if user.contact.title %} + + {% else %} + + {% endif %} + {% else %} + + {% endif %} - + {% if user.email or user.contact.email %} + {% if user.contact.email %} + + {% else %} + + {% endif %} + {% else %} + + {% endif %} - + {% if user.phone or user.contact.phone %} + {% if user.contact.phone %} + + {% else %} + + {% endif %} + {% else %} + + {% endif %}
Title{{ user.title }}{{ user.contact.title }}{{ user.title }}Nothing found
Email{{ user.email }}{{ user.contact.email }}{{ user.email }}Nothing found
Phone{{ user.phone }}{{ user.contact.phone }}{{ user.phone }}Nothing found
diff --git a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html index c90423474..efd90b909 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html @@ -31,11 +31,11 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endif %} {% if field.field.name == "creator" %} - {% include "django/admin/includes/domain_request_detail_table.html" with user=original.creator %} + {% include "django/admin/includes/domain_request_detail_table.html" with user=original.creator field_name="creator" %} {% elif field.field.name == "submitter" %} - {% include "django/admin/includes/domain_request_detail_table.html" with user=original.submitter %} + {% include "django/admin/includes/domain_request_detail_table.html" with user=original.submitter field_name="submitter" %} {% elif field.field.name == "authorizing_official" %} - {% include "django/admin/includes/domain_request_detail_table.html" with user=original.authorizing_official %} + {% include "django/admin/includes/domain_request_detail_table.html" with user=original.authorizing_official field_name="authorizing_official" %} {% elif field.field.name == "other_contacts" %}
Details diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 04eb4c82d..c7846d49d 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -734,6 +734,7 @@ def completed_domain_request( """A completed domain request.""" if not user: user = get_user_model().objects.create(username="username" + str(uuid.uuid4())[:8]) + ao, _ = Contact.objects.get_or_create( first_name="Testy", last_name="Tester", diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index d22973971..fce279416 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -2,6 +2,7 @@ from datetime import date from django.test import TestCase, RequestFactory, Client, override_settings from django.contrib.admin.sites import AdminSite from contextlib import ExitStack +from api.tests.common import less_console_noise_decorator from django_webtest import WebTest # type: ignore from django.contrib import messages from django.urls import reverse @@ -1193,6 +1194,63 @@ class TestDomainRequestAdmin(MockEppLib): # Test that approved domain exists and equals requested domain self.assertEqual(domain_request.requested_domain.name, domain_request.approved_domain.name) + @less_console_noise_decorator + def test_creator_has_detail_table(self): + """Tests if the "creator" field has the detail table which displays title, email, and phone""" + + # Create fake creator + _creator = User.objects.create( + username="MrMeoward", + first_name = "Meoward", + last_name = "Jones", + email="meoward.jones@igorville.gov", + phone="(555) 123 12345", + title="Treat inspector" + ) + # Create a fake domain request + domain_request = completed_domain_request( + status=DomainRequest.DomainRequestStatus.IN_REVIEW, + creator=_creator + ) + + p = "userpass" + self.client.login(username="staffuser", 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) + + # Check that the modal has the right content + # Check for the header + + # Check for the creator + self.assertContains(response, "Meoward Jones") + # Check that it is readonly + self.assertContains(response, "") + + # Check for the right title, email, and phone number in the response + + # Title + self.assertContains(response, "Treat inspector") + + # Email + self.assertContains(response, "meoward.jones@igorville.gov") + + # Phone number + self.assertContains(response, "(555) 123 12345") + + # Check for table titles + + # Title. We only need to check for the end tag + # (Otherwise this test will fail if we change classes, etc) + self.assertContains(response, "Title") + self.assertContains(response, "Email") + self.assertContains(response, "Phone") + def test_save_model_sets_restricted_status_on_user(self): with less_console_noise(): # make sure there is no user with this email @@ -1339,9 +1397,9 @@ class TestDomainRequestAdmin(MockEppLib): "about_your_organization", "requested_domain", "approved_domain", + "alternative_domains", "other_contacts", "current_websites", - "alternative_domains", "purpose", "submitter", "no_other_contacts_rationale", From b8b97245a460e8bbbcad749885d0f0f018f8d140 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:47:06 -0600 Subject: [PATCH 21/61] More tests --- src/registrar/tests/test_admin.py | 127 ++++++++++++++++++++++++------ 1 file changed, 101 insertions(+), 26 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index fce279416..ef4d05e0b 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1194,23 +1194,74 @@ class TestDomainRequestAdmin(MockEppLib): # Test that approved domain exists and equals requested domain self.assertEqual(domain_request.requested_domain.name, domain_request.approved_domain.name) + def assert_response_contains_distinct_values(self, response, expected_values): + """ + Asserts that each specified value appears exactly once in the response. + + This method iterates over a list of tuples, where each tuple contains a field name + and its expected value. It then performs an assertContains check for each value, + ensuring that each value appears exactly once in the response. + + Parameters: + - response: The HttpResponse object to inspect. + - expected_values: A list of tuples, where each tuple contains: + - field: The name of the field (used for subTest identification). + - value: The expected value to check for in the response. + + Example usage: + expected_values = [ + ("title", "Treat inspector"), + ("email", "meoward.jones@igorville.gov"), + ] + self.assert_response_contains_distinct_values(response, expected_values) + """ + for field, value in expected_values: + with self.subTest(field=field, expected_value=value): + self.assertContains(response, value, count=1) + + @less_console_noise_decorator + def test_other_contacts_readonly_has_link(self): + """Tests if the readonly other_contacts field has links""" + + # Create a fake domain request + domain_request = completed_domain_request( + status=DomainRequest.DomainRequestStatus.IN_REVIEW, + user=_creator + ) + + p = "userpass" + self.client.login(username="staffuser", 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) + @less_console_noise_decorator - def test_creator_has_detail_table(self): - """Tests if the "creator" field has the detail table which displays title, email, and phone""" + def test_contact_fields_have_detail_table(self): + """Tests if the contact fields have the detail table which displays title, email, and phone""" # Create fake creator _creator = User.objects.create( username="MrMeoward", first_name = "Meoward", last_name = "Jones", - email="meoward.jones@igorville.gov", - phone="(555) 123 12345", - title="Treat inspector" ) + + # Due to the relation between User <==> Contact, + # the underlying contact has to be modified this way. + _creator.contact.email = "meoward.jones@igorville.gov" + _creator.contact.phone = "(555) 123 12345" + _creator.contact.title = "Treat inspector" + _creator.contact.save() + # Create a fake domain request domain_request = completed_domain_request( status=DomainRequest.DomainRequestStatus.IN_REVIEW, - creator=_creator + user=_creator ) p = "userpass" @@ -1227,29 +1278,53 @@ class TestDomainRequestAdmin(MockEppLib): # Check that the modal has the right content # Check for the header - # Check for the creator + # == Check for the creator == # self.assertContains(response, "Meoward Jones") - # Check that it is readonly - self.assertContains(response, "") - # Check for the right title, email, and phone number in the response - - # Title - self.assertContains(response, "Treat inspector") - - # Email - self.assertContains(response, "meoward.jones@igorville.gov") - - # Phone number - self.assertContains(response, "(555) 123 12345") - - # Check for table titles - - # Title. We only need to check for the end tag + # Check for the right title, email, and phone number in the response. + # We only need to check for the end tag # (Otherwise this test will fail if we change classes, etc) - self.assertContains(response, "Title") - self.assertContains(response, "Email") - self.assertContains(response, "Phone") + expected_creator_fields = [ + # Field, expected value + ("title", "Treat inspector"), + ("email", "meoward.jones@igorville.gov"), + ("phone", "(555) 123 12345") + ] + self.assert_response_contains_distinct_values(response, expected_creator_fields) + + # == Check for the submitter == # + self.assertContains(response, "Testy2 Tester2") + + expected_submitter_fields = [ + # Field, expected value + ("title", "Admin Tester"), + ("email", "mayor@igorville.gov"), + ("phone", "(555) 555 5556") + ] + self.assert_response_contains_distinct_values(response, expected_submitter_fields) + + # == Check for the authorizing_official == # + self.assertContains(response, "Testy Tester") + + expected_ao_fields = [ + # Field, expected value + ("title", "Chief Tester"), + ("email", "testy@town.com"), + ("phone", "(555) 555 5555") + ] + self.assert_response_contains_distinct_values(response, expected_ao_fields) + + # Check for table titles. We only need to check for the end tag + # (Otherwise this test will fail if we change classes, etc) + + # Title. Count=3 because this table appears on three records. + self.assertContains(response, "Title", count=3) + + # Email. Count=3 because this table appears on three records. + self.assertContains(response, "Email", count=3) + + # Phone. Count=3 because this table appears on three records. + self.assertContains(response, "Phone", count=3) def test_save_model_sets_restricted_status_on_user(self): with less_console_noise(): From 4830f5630f057b42b99d46127856c8bda393bbe2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:11:11 -0600 Subject: [PATCH 22/61] Add unit tests + linting --- src/registrar/templatetags/url_helpers.py | 1 + src/registrar/tests/test_admin.py | 53 +++++++++++++---------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/src/registrar/templatetags/url_helpers.py b/src/registrar/templatetags/url_helpers.py index 9413892aa..e75b99ff3 100644 --- a/src/registrar/templatetags/url_helpers.py +++ b/src/registrar/templatetags/url_helpers.py @@ -33,6 +33,7 @@ def split_string(value, key): """ return value.split(key) + @register.simple_tag def public_site_url(url_path): """Make a full URL for this path at our public site. diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index ef4d05e0b..970742ae3 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1219,15 +1219,15 @@ class TestDomainRequestAdmin(MockEppLib): with self.subTest(field=field, expected_value=value): self.assertContains(response, value, count=1) - @less_console_noise_decorator - def test_other_contacts_readonly_has_link(self): + @less_console_noise_decorator + def test_other_contacts_has_readonly_link(self): """Tests if the readonly other_contacts field has links""" # Create a fake domain request - domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.IN_REVIEW, - user=_creator - ) + domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) + + # Get the other contact + other_contact = domain_request.other_contacts.all().first() p = "userpass" self.client.login(username="staffuser", password=p) @@ -1240,6 +1240,16 @@ class TestDomainRequestAdmin(MockEppLib): self.assertEqual(response.status_code, 200) self.assertContains(response, domain_request.requested_domain.name) + # Check that the page contains the url we expect + expected_href = reverse("admin:registrar_contact_change", args=[other_contact.id]) + self.assertContains(response, expected_href) + + # Check that the page contains the link we expect. + # Since the url is dynamic (populated by JS), we can test for its existence + # by checking for the end tag. + expected_url = f"Testy Tester" + self.assertContains(response, expected_url) + @less_console_noise_decorator def test_contact_fields_have_detail_table(self): """Tests if the contact fields have the detail table which displays title, email, and phone""" @@ -1247,8 +1257,8 @@ class TestDomainRequestAdmin(MockEppLib): # Create fake creator _creator = User.objects.create( username="MrMeoward", - first_name = "Meoward", - last_name = "Jones", + first_name="Meoward", + last_name="Jones", ) # Due to the relation between User <==> Contact, @@ -1259,10 +1269,7 @@ class TestDomainRequestAdmin(MockEppLib): _creator.contact.save() # Create a fake domain request - domain_request = completed_domain_request( - status=DomainRequest.DomainRequestStatus.IN_REVIEW, - user=_creator - ) + domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=_creator) p = "userpass" self.client.login(username="staffuser", password=p) @@ -1277,9 +1284,8 @@ class TestDomainRequestAdmin(MockEppLib): # Check that the modal has the right content # Check for the header - - # == Check for the creator == # - self.assertContains(response, "Meoward Jones") + + # == Check for the creator == # # Check for the right title, email, and phone number in the response. # We only need to check for the end tag @@ -1288,31 +1294,32 @@ class TestDomainRequestAdmin(MockEppLib): # Field, expected value ("title", "Treat inspector"), ("email", "meoward.jones@igorville.gov"), - ("phone", "(555) 123 12345") + ("phone", "(555) 123 12345"), ] self.assert_response_contains_distinct_values(response, expected_creator_fields) - # == Check for the submitter == # - self.assertContains(response, "Testy2 Tester2") + # Check for the field itself + self.assertContains(response, "Meoward Jones") + # == Check for the submitter == # expected_submitter_fields = [ # Field, expected value ("title", "Admin Tester"), ("email", "mayor@igorville.gov"), - ("phone", "(555) 555 5556") + ("phone", "(555) 555 5556"), ] self.assert_response_contains_distinct_values(response, expected_submitter_fields) + self.assertContains(response, "Testy2 Tester2") - # == Check for the authorizing_official == # - self.assertContains(response, "Testy Tester") - + # == Check for the authorizing_official == # expected_ao_fields = [ # Field, expected value ("title", "Chief Tester"), ("email", "testy@town.com"), - ("phone", "(555) 555 5555") + ("phone", "(555) 555 5555"), ] self.assert_response_contains_distinct_values(response, expected_ao_fields) + self.assertContains(response, "Testy Tester") # Check for table titles. We only need to check for the end tag # (Otherwise this test will fail if we change classes, etc) From bdc03c66a91b6e441dcc7683d84117b1d3c46b11 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:33:28 -0600 Subject: [PATCH 23/61] Final unit test --- .../includes/domain_request_fieldset.html | 12 ++++++++++ src/registrar/tests/test_admin.py | 24 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html index efd90b909..980fe0bfd 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html @@ -22,6 +22,18 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {{ contact }}{% if not forloop.last %}, {% endif %} {% endfor %} + {% elif field.field.name == "current_websites" %} + {% comment %} + The "website" model is essentially just a text field. + It is not useful to be redirected to the object definition, + rather it is more useful in this scenario to be redirected to the + actual website (as its just a plaintext string otherwise). + + This ONLY applies to analysts. For superusers, its business as usual. + {% endcomment %} + {% for website in field.contents|split:", " %} + {{ website }}{% if not forloop.last %}, {% endif %} + {% endfor %} {% else %}
{{ field.contents }}
{% endif %} diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 970742ae3..b4b12c56f 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1247,7 +1247,29 @@ class TestDomainRequestAdmin(MockEppLib): # Check that the page contains the link we expect. # Since the url is dynamic (populated by JS), we can test for its existence # by checking for the end tag. - expected_url = f"Testy Tester" + expected_url = "Testy Tester" + self.assertContains(response, expected_url) + + @less_console_noise_decorator + def test_other_websites_has_readonly_link(self): + """Tests if the readonly other_websites field has links""" + + # Create a fake domain request + domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) + + p = "userpass" + self.client.login(username="staffuser", 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) + + # Check that the page contains the link we expect. + expected_url = 'city.com' self.assertContains(response, expected_url) @less_console_noise_decorator From 516415d806e562516fd504cbfa880612e7827be2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 20 Mar 2024 12:44:21 -0600 Subject: [PATCH 24/61] Correct new lines --- src/registrar/assets/js/get-gov-reports.js | 2 +- src/registrar/templates/admin/fieldset.html | 2 +- src/registrar/tests/common.py | 1 - src/registrar/tests/test_admin.py | 50 ++++++++++----------- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/registrar/assets/js/get-gov-reports.js b/src/registrar/assets/js/get-gov-reports.js index 2fc9b6219..735681777 100644 --- a/src/registrar/assets/js/get-gov-reports.js +++ b/src/registrar/assets/js/get-gov-reports.js @@ -128,4 +128,4 @@ }; initComparativeColumnCharts(); -})(); \ No newline at end of file +})(); diff --git a/src/registrar/templates/admin/fieldset.html b/src/registrar/templates/admin/fieldset.html index dadae5811..96433e972 100644 --- a/src/registrar/templates/admin/fieldset.html +++ b/src/registrar/templates/admin/fieldset.html @@ -46,4 +46,4 @@ https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/ {% endfor %} {% endblock fieldset_lines %} - \ No newline at end of file + diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index c7846d49d..04eb4c82d 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -734,7 +734,6 @@ def completed_domain_request( """A completed domain request.""" if not user: user = get_user_model().objects.create(username="username" + str(uuid.uuid4())[:8]) - ao, _ = Contact.objects.get_or_create( first_name="Testy", last_name="Tester", diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index b4b12c56f..f0a40044b 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1194,31 +1194,6 @@ class TestDomainRequestAdmin(MockEppLib): # Test that approved domain exists and equals requested domain self.assertEqual(domain_request.requested_domain.name, domain_request.approved_domain.name) - def assert_response_contains_distinct_values(self, response, expected_values): - """ - Asserts that each specified value appears exactly once in the response. - - This method iterates over a list of tuples, where each tuple contains a field name - and its expected value. It then performs an assertContains check for each value, - ensuring that each value appears exactly once in the response. - - Parameters: - - response: The HttpResponse object to inspect. - - expected_values: A list of tuples, where each tuple contains: - - field: The name of the field (used for subTest identification). - - value: The expected value to check for in the response. - - Example usage: - expected_values = [ - ("title", "Treat inspector"), - ("email", "meoward.jones@igorville.gov"), - ] - self.assert_response_contains_distinct_values(response, expected_values) - """ - for field, value in expected_values: - with self.subTest(field=field, expected_value=value): - self.assertContains(response, value, count=1) - @less_console_noise_decorator def test_other_contacts_has_readonly_link(self): """Tests if the readonly other_contacts field has links""" @@ -1272,6 +1247,31 @@ class TestDomainRequestAdmin(MockEppLib): expected_url = 'city.com' self.assertContains(response, expected_url) + def assert_response_contains_distinct_values(self, response, expected_values): + """ + Asserts that each specified value appears exactly once in the response. + + This method iterates over a list of tuples, where each tuple contains a field name + and its expected value. It then performs an assertContains check for each value, + ensuring that each value appears exactly once in the response. + + Parameters: + - response: The HttpResponse object to inspect. + - expected_values: A list of tuples, where each tuple contains: + - field: The name of the field (used for subTest identification). + - value: The expected value to check for in the response. + + Example usage: + expected_values = [ + ("title", "Treat inspector"), + ("email", "meoward.jones@igorville.gov"), + ] + self.assert_response_contains_distinct_values(response, expected_values) + """ + for field, value in expected_values: + with self.subTest(field=field, expected_value=value): + self.assertContains(response, value, count=1) + @less_console_noise_decorator def test_contact_fields_have_detail_table(self): """Tests if the contact fields have the detail table which displays title, email, and phone""" From 407ff0cb76bdae7c509d5af0b33c867f62ee7e6b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:13:56 -0600 Subject: [PATCH 25/61] missing unit test --- src/registrar/tests/test_admin.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index f0a40044b..b29776f81 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1341,7 +1341,10 @@ class TestDomainRequestAdmin(MockEppLib): ("phone", "(555) 555 5555"), ] self.assert_response_contains_distinct_values(response, expected_ao_fields) - self.assertContains(response, "Testy Tester") + + # count=4 because the underlying domain has two users with this name. + # The dropdown has 3 of these. + self.assertContains(response, "Testy Tester", count=4) # Check for table titles. We only need to check for the end tag # (Otherwise this test will fail if we change classes, etc) @@ -1355,6 +1358,18 @@ class TestDomainRequestAdmin(MockEppLib): # Phone. Count=3 because this table appears on three records. self.assertContains(response, "Phone", count=3) + # == Test the other_employees field == # + expected_other_employees_fields = [ + # Field, expected value + ("title", "Another Tester"), + ("email", "testy2@town.com"), + ("phone", "(555) 555 5557"), + ] + self.assert_response_contains_distinct_values(response, expected_other_employees_fields) + + # count=1 as only one should exist in a table + self.assertContains(response, "Testy Tester", count=1) + def test_save_model_sets_restricted_status_on_user(self): with less_console_noise(): # make sure there is no user with this email From 7e8f07439e322cf36529ad58d6ec0fd887310547 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 20 Mar 2024 15:21:19 -0600 Subject: [PATCH 26/61] Move order --- .../admin/includes/domain_request_fieldset.html | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html index 980fe0bfd..f4e68bd0c 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html @@ -42,6 +42,13 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endif %} {% endif %} + + {% if field.field.help_text %} +
+
{{ field.field.help_text|safe }}
+
+ {% endif %} + {% if field.field.name == "creator" %} {% include "django/admin/includes/domain_request_detail_table.html" with user=original.creator field_name="creator" %} {% elif field.field.name == "submitter" %} @@ -71,12 +78,6 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
{% endif %} - - {% if field.field.help_text %} -
-
{{ field.field.help_text|safe }}
-
- {% endif %} {% endfor %} {% if not line.fields|length == 1 %}{% endif %} From 1179a6a6b7f0da5912ce4749b2a08a9adedbe37f Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 21 Mar 2024 13:57:24 -0400 Subject: [PATCH 27/61] Cancel removed from retrieved invitations, cancel attempts on retrieved invitations errors --- src/registrar/templates/domain_users.html | 2 ++ src/registrar/views/domain.py | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/registrar/templates/domain_users.html b/src/registrar/templates/domain_users.html index e295d2f7e..7de159871 100644 --- a/src/registrar/templates/domain_users.html +++ b/src/registrar/templates/domain_users.html @@ -133,9 +133,11 @@ {{ invitation.created_at|date }} {{ invitation.status|title }} + {% if invitation.status == invitation.DomainInvitationStatus.INVITED %}
{% csrf_token %}
+ {% endif %} {% endfor %} diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index b2ae76852..8ff55e52b 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -822,6 +822,18 @@ class DomainAddUserView(DomainFormBaseView): class DomainInvitationDeleteView(SuccessMessageMixin, DomainInvitationPermissionDeleteView): object: DomainInvitation # workaround for type mismatch in DeleteView + def post(self, request, *args, **kwargs): + """Override post method in order to error in the case when the + domain invitation status is RETRIEVED""" + self.object = self.get_object() + form = self.get_form() + if form.is_valid() and self.object.status == self.object.DomainInvitationStatus.INVITED: + return self.form_valid(form) + else: + # Produce an error message if the domain invatation status is RETRIEVED + messages.error(request, f"Invitation to {self.object.email} has already been retrieved") + return HttpResponseRedirect(self.get_success_url()) + def get_success_url(self): return reverse("domain-users", kwargs={"pk": self.object.domain.id}) From ed36fc6e3b36fcd6842ea17d10280b6f99c7067a Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:42:21 -0600 Subject: [PATCH 28/61] Code simplification --- src/registrar/admin.py | 2 + src/registrar/templates/admin/fieldset.html | 71 +++++++++------ .../admin/domain_information_change_form.html | 8 ++ ...l_table.html => contact_detail_table.html} | 0 .../admin/includes/detail_table_fieldset.html | 61 +++++++++++++ .../includes/domain_information_fieldset.html | 5 ++ .../includes/domain_request_fieldset.html | 88 +------------------ 7 files changed, 120 insertions(+), 115 deletions(-) create mode 100644 src/registrar/templates/django/admin/domain_information_change_form.html rename src/registrar/templates/django/admin/includes/{domain_request_detail_table.html => contact_detail_table.html} (100%) create mode 100644 src/registrar/templates/django/admin/includes/detail_table_fieldset.html create mode 100644 src/registrar/templates/django/admin/includes/domain_information_fieldset.html diff --git a/src/registrar/admin.py b/src/registrar/admin.py index a3cc0d2d1..ae925b5c3 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -913,6 +913,8 @@ class DomainInformationAdmin(ListHeaderAdmin): # Table ordering ordering = ["domain__name"] + change_form_template = "django/admin/domain_information_change_form.html" + def get_readonly_fields(self, request, obj=None): """Set the read-only state on form elements. We have 1 conditions that determine which fields are read-only: diff --git a/src/registrar/templates/admin/fieldset.html b/src/registrar/templates/admin/fieldset.html index 96433e972..37f79ab46 100644 --- a/src/registrar/templates/admin/fieldset.html +++ b/src/registrar/templates/admin/fieldset.html @@ -16,34 +16,49 @@ https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/ {% endif %} {% endblock fieldset_description %} - {% block fieldset_lines %} - {% for line in fieldset %} -
- {% if line.fields|length == 1 %}{{ line.errors }}{% else %}
{% endif %} - {% for field in line %} -
- {% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %} -
- {% if field.is_checkbox %} - {{ field.field }}{{ field.label_tag }} + {% for line in fieldset %} +
+ {% if line.fields|length == 1 %}{{ line.errors }}{% else %}
{% endif %} + {% for field in line %} +
+ {% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %} +
+ {% if field.is_checkbox %} + {% block field_checkbox %} + {{ field.field }}{{ field.label_tag }} + {% endblock field_checkbox%} + {% else %} + {{ field.label_tag }} + {% if field.is_readonly %} + {% block field_readonly %} +
{{ field.contents }}
+ {% endblock field_readonly%} {% else %} - {{ field.label_tag }} - {% if field.is_readonly %} -
{{ field.contents }}
- {% else %} - {{ field.field }} - {% endif %} + {% block field_other %} + {{ field.field }} + {% endblock field_other%} {% endif %} -
- {% if field.field.help_text %} -
-
{{ field.field.help_text|safe }}
-
- {% endif %} -
- {% endfor %} - {% if not line.fields|length == 1 %}
{% endif %} -
- {% endfor %} - {% endblock fieldset_lines %} + {% endif %} +
+ + {% block before_help_text %} + {# For templating purposes #} + {% endblock before_help_text %} + + {% if field.field.help_text %} + {% block help_text %} +
+
{{ field.field.help_text|safe }}
+
+ {% endblock help_text %} + {% endif %} + + {% block after_help_text %} + {# For templating purposes #} + {% endblock after_help_text %} +
+ {% endfor %} + {% if not line.fields|length == 1 %}
{% endif %} +
+ {% endfor %} diff --git a/src/registrar/templates/django/admin/domain_information_change_form.html b/src/registrar/templates/django/admin/domain_information_change_form.html new file mode 100644 index 000000000..86475890d --- /dev/null +++ b/src/registrar/templates/django/admin/domain_information_change_form.html @@ -0,0 +1,8 @@ +{% extends 'admin/change_form.html' %} +{% load i18n static %} + +{% block field_sets %} + {% for fieldset in adminform %} + {% include "django/admin/includes/domain_information_fieldset.html" %} + {% endfor %} +{% endblock %} diff --git a/src/registrar/templates/django/admin/includes/domain_request_detail_table.html b/src/registrar/templates/django/admin/includes/contact_detail_table.html similarity index 100% rename from src/registrar/templates/django/admin/includes/domain_request_detail_table.html rename to src/registrar/templates/django/admin/includes/contact_detail_table.html diff --git a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html new file mode 100644 index 000000000..f5a5b71ee --- /dev/null +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -0,0 +1,61 @@ +{% extends "admin/fieldset.html" %} +{% load static url_helpers %} + +{% comment %} +This is using a custom implementation fieldset.html (see admin/fieldset.html) +{% endcomment %} +{% block field_readonly %} + {% if field.field.name == "other_contacts" %} +
+ {% for contact in field.contents|split:", " %} + {{ contact }}{% if not forloop.last %}, {% endif %} + {% endfor %} +
+ {% elif field.field.name == "current_websites" %} + {% comment %} + The "website" model is essentially just a text field. + It is not useful to be redirected to the object definition, + rather it is more useful in this scenario to be redirected to the + actual website (as its just a plaintext string otherwise). + + This ONLY applies to analysts. For superusers, its business as usual. + {% endcomment %} + {% for website in field.contents|split:", " %} + {{ website }}{% if not forloop.last %}, {% endif %} + {% endfor %} + {% else %} +
{{ field.contents }}
+ {% endif %} +{% endblock field_readonly %} + +{% block after_help_text %} + {% if field.field.name == "creator" %} + {% include "django/admin/includes/contact_detail_table.html" with user=original.creator field_name="creator" %} + {% elif field.field.name == "submitter" %} + {% include "django/admin/includes/contact_detail_table.html" with user=original.submitter field_name="submitter" %} + {% elif field.field.name == "authorizing_official" %} + {% include "django/admin/includes/contact_detail_table.html" with user=original.authorizing_official field_name="authorizing_official" %} + {% elif field.field.name == "other_contacts" and original.other_contacts.all %} +
+ Details +
+ + + {% for contact in original.other_contacts.all %} + {% comment %} + Since we can't get the id from field, we can embed this information here. + Then we can link these two fields using javascript. + {% endcomment %} + + + + + + + {% endfor %} + +
{{contact.first_name}} {{contact.last_name}}{{ contact.title }}{{ contact.email }}{{ contact.phone }}
+
+
+ {% endif %} +{% endblock after_help_text %} diff --git a/src/registrar/templates/django/admin/includes/domain_information_fieldset.html b/src/registrar/templates/django/admin/includes/domain_information_fieldset.html new file mode 100644 index 000000000..564415f24 --- /dev/null +++ b/src/registrar/templates/django/admin/includes/domain_information_fieldset.html @@ -0,0 +1,5 @@ +{% extends "django/admin/includes/detail_table_fieldset.html" %} + +{% block after_help_text %} +
TESTING123
+{% endblock after_help_text %} \ No newline at end of file diff --git a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html index f4e68bd0c..c694eb353 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html @@ -1,87 +1 @@ -{% extends "admin/fieldset.html" %} -{% load static url_helpers %} -{% comment %} -This is using a custom implementation fieldset.html (see admin/fieldset.html) -{% endcomment %} -{% block fieldset_lines %} -{% for line in fieldset %} -
- {% if line.fields|length == 1 %}{{ line.errors }}{% else %}
{% endif %} - {% for field in line %} -
- {% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %} -
- {% if field.is_checkbox %} - {{ field.field }}{{ field.label_tag }} - {% else %} - {{ field.label_tag }} - {% if field.is_readonly %} - {% if field.field.name == "other_contacts" %} -
- {% for contact in field.contents|split:", " %} - {{ contact }}{% if not forloop.last %}, {% endif %} - {% endfor %} -
- {% elif field.field.name == "current_websites" %} - {% comment %} - The "website" model is essentially just a text field. - It is not useful to be redirected to the object definition, - rather it is more useful in this scenario to be redirected to the - actual website (as its just a plaintext string otherwise). - - This ONLY applies to analysts. For superusers, its business as usual. - {% endcomment %} - {% for website in field.contents|split:", " %} - {{ website }}{% if not forloop.last %}, {% endif %} - {% endfor %} - {% else %} -
{{ field.contents }}
- {% endif %} - {% else %} - {{ field.field }} - {% endif %} - {% endif %} -
- - {% if field.field.help_text %} -
-
{{ field.field.help_text|safe }}
-
- {% endif %} - - {% if field.field.name == "creator" %} - {% include "django/admin/includes/domain_request_detail_table.html" with user=original.creator field_name="creator" %} - {% elif field.field.name == "submitter" %} - {% include "django/admin/includes/domain_request_detail_table.html" with user=original.submitter field_name="submitter" %} - {% elif field.field.name == "authorizing_official" %} - {% include "django/admin/includes/domain_request_detail_table.html" with user=original.authorizing_official field_name="authorizing_official" %} - {% elif field.field.name == "other_contacts" %} -
- Details -
- - - {% for contact in original.other_contacts.all %} - {% comment %} - Since we can't get the id from field, we can embed this information here. - Then we can link these two fields using javascript. - {% endcomment %} - - - - - - - {% endfor %} - -
{{contact.first_name}} {{contact.last_name}}{{ contact.title }}{{ contact.email }}{{ contact.phone }}
-
-
- {% endif %} -
- {% endfor %} - {% if not line.fields|length == 1 %}
{% endif %} -
-{% endfor %} - -{% endblock fieldset_lines %} +{% extends "django/admin/includes/detail_table_fieldset.html" %} \ No newline at end of file From 100e3cf300b749436f38af15daced65b29d826f9 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 21 Mar 2024 15:09:02 -0400 Subject: [PATCH 29/61] test case written --- src/registrar/tests/test_views_domain.py | 14 ++++++++++++++ src/registrar/views/domain.py | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/registrar/tests/test_views_domain.py b/src/registrar/tests/test_views_domain.py index 78f4de83d..979cc6bc9 100644 --- a/src/registrar/tests/test_views_domain.py +++ b/src/registrar/tests/test_views_domain.py @@ -665,6 +665,20 @@ class TestDomainManagers(TestDomainOverview): with self.assertRaises(DomainInvitation.DoesNotExist): DomainInvitation.objects.get(id=invitation.id) + def test_domain_invitation_cancel_retrieved_invitation(self): + """Posting to the delete view when invitation retrieved returns an error message""" + email_address = "mayor@igorville.gov" + invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address, status=DomainInvitation.DomainInvitationStatus.RETRIEVED) + with less_console_noise(): + response = self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}), follow=True) + # Assert that an error message is displayed to the user + self.assertContains(response, f"Invitation to {email_address} has already been retrieved.") + # Assert that the Cancel link is not displayed + self.assertNotContains(response, "Cancel") + # Assert that the DomainInvitation is not deleted + self.assertTrue(DomainInvitation.objects.filter(id=invitation.id).exists()) + DomainInvitation.objects.filter(email=email_address).delete() + def test_domain_invitation_cancel_no_permissions(self): """Posting to the delete view as a different user should fail.""" email_address = "mayor@igorville.gov" diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py index 8ff55e52b..b367baf8f 100644 --- a/src/registrar/views/domain.py +++ b/src/registrar/views/domain.py @@ -831,7 +831,7 @@ class DomainInvitationDeleteView(SuccessMessageMixin, DomainInvitationPermission return self.form_valid(form) else: # Produce an error message if the domain invatation status is RETRIEVED - messages.error(request, f"Invitation to {self.object.email} has already been retrieved") + messages.error(request, f"Invitation to {self.object.email} has already been retrieved.") return HttpResponseRedirect(self.get_success_url()) def get_success_url(self): From 6f4febde436fd6b78b6089d5d3dd0eb3a09957a1 Mon Sep 17 00:00:00 2001 From: David Kennedy Date: Thu, 21 Mar 2024 15:20:07 -0400 Subject: [PATCH 30/61] reformatted for readability (and to satisfy the linter) --- src/registrar/tests/test_views_domain.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/registrar/tests/test_views_domain.py b/src/registrar/tests/test_views_domain.py index 979cc6bc9..1ddc28e0f 100644 --- a/src/registrar/tests/test_views_domain.py +++ b/src/registrar/tests/test_views_domain.py @@ -668,7 +668,9 @@ class TestDomainManagers(TestDomainOverview): def test_domain_invitation_cancel_retrieved_invitation(self): """Posting to the delete view when invitation retrieved returns an error message""" email_address = "mayor@igorville.gov" - invitation, _ = DomainInvitation.objects.get_or_create(domain=self.domain, email=email_address, status=DomainInvitation.DomainInvitationStatus.RETRIEVED) + invitation, _ = DomainInvitation.objects.get_or_create( + domain=self.domain, email=email_address, status=DomainInvitation.DomainInvitationStatus.RETRIEVED + ) with less_console_noise(): response = self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}), follow=True) # Assert that an error message is displayed to the user @@ -678,7 +680,7 @@ class TestDomainManagers(TestDomainOverview): # Assert that the DomainInvitation is not deleted self.assertTrue(DomainInvitation.objects.filter(id=invitation.id).exists()) DomainInvitation.objects.filter(email=email_address).delete() - + def test_domain_invitation_cancel_no_permissions(self): """Posting to the delete view as a different user should fail.""" email_address = "mayor@igorville.gov" From 98d940386bfc0c4dd67a28ad3edb5a0c96449f69 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 21 Mar 2024 13:45:06 -0600 Subject: [PATCH 31/61] New test cases --- src/registrar/admin.py | 1 + .../includes/domain_information_fieldset.html | 5 +- .../includes/domain_request_fieldset.html | 3 +- src/registrar/tests/common.py | 25 +++ src/registrar/tests/test_admin.py | 167 +++++++++++++++--- 5 files changed, 167 insertions(+), 34 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index ae925b5c3..e1b9bd5a9 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -896,6 +896,7 @@ class DomainInformationAdmin(ListHeaderAdmin): "no_other_contacts_rationale", "anything_else", "is_policy_acknowledged", + "other_contacts", ] # For each filter_horizontal, init in admin js extendFilterHorizontalWidgets diff --git a/src/registrar/templates/django/admin/includes/domain_information_fieldset.html b/src/registrar/templates/django/admin/includes/domain_information_fieldset.html index 564415f24..b42873c3c 100644 --- a/src/registrar/templates/django/admin/includes/domain_information_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_information_fieldset.html @@ -1,5 +1,2 @@ {% extends "django/admin/includes/detail_table_fieldset.html" %} - -{% block after_help_text %} -
TESTING123
-{% endblock after_help_text %} \ No newline at end of file +{# Stubbed file for future expansion #} diff --git a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html index c694eb353..b42873c3c 100644 --- a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html +++ b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html @@ -1 +1,2 @@ -{% extends "django/admin/includes/detail_table_fieldset.html" %} \ No newline at end of file +{% extends "django/admin/includes/detail_table_fieldset.html" %} +{# Stubbed file for future expansion #} diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 04eb4c82d..655fda02b 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -116,6 +116,31 @@ class GenericTestHelper(TestCase): self.url = url self.client = client + def assert_response_contains_distinct_values(self, response, expected_values): + """ + Asserts that each specified value appears exactly once in the response. + + This method iterates over a list of tuples, where each tuple contains a field name + and its expected value. It then performs an assertContains check for each value, + ensuring that each value appears exactly once in the response. + + Parameters: + - response: The HttpResponse object to inspect. + - expected_values: A list of tuples, where each tuple contains: + - field: The name of the field (used for subTest identification). + - value: The expected value to check for in the response. + + Example usage: + expected_values = [ + ("title", "Treat inspector"), + ("email", "meoward.jones@igorville.gov"), + ] + self.assert_response_contains_distinct_values(response, expected_values) + """ + for field, value in expected_values: + with self.subTest(field=field, expected_value=value): + self.assertContains(response, value, count=1) + def assert_table_sorted(self, o_index, sort_fields): """ This helper function validates the sorting functionality of a Django Admin table view. diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index b29776f81..042c7261b 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1247,31 +1247,6 @@ class TestDomainRequestAdmin(MockEppLib): expected_url = 'city.com' self.assertContains(response, expected_url) - def assert_response_contains_distinct_values(self, response, expected_values): - """ - Asserts that each specified value appears exactly once in the response. - - This method iterates over a list of tuples, where each tuple contains a field name - and its expected value. It then performs an assertContains check for each value, - ensuring that each value appears exactly once in the response. - - Parameters: - - response: The HttpResponse object to inspect. - - expected_values: A list of tuples, where each tuple contains: - - field: The name of the field (used for subTest identification). - - value: The expected value to check for in the response. - - Example usage: - expected_values = [ - ("title", "Treat inspector"), - ("email", "meoward.jones@igorville.gov"), - ] - self.assert_response_contains_distinct_values(response, expected_values) - """ - for field, value in expected_values: - with self.subTest(field=field, expected_value=value): - self.assertContains(response, value, count=1) - @less_console_noise_decorator def test_contact_fields_have_detail_table(self): """Tests if the contact fields have the detail table which displays title, email, and phone""" @@ -1318,7 +1293,7 @@ class TestDomainRequestAdmin(MockEppLib): ("email", "meoward.jones@igorville.gov"), ("phone", "(555) 123 12345"), ] - self.assert_response_contains_distinct_values(response, expected_creator_fields) + self.test_helper.assert_response_contains_distinct_values(response, expected_creator_fields) # Check for the field itself self.assertContains(response, "Meoward Jones") @@ -1330,7 +1305,7 @@ class TestDomainRequestAdmin(MockEppLib): ("email", "mayor@igorville.gov"), ("phone", "(555) 555 5556"), ] - self.assert_response_contains_distinct_values(response, expected_submitter_fields) + self.test_helper.assert_response_contains_distinct_values(response, expected_submitter_fields) self.assertContains(response, "Testy2 Tester2") # == Check for the authorizing_official == # @@ -1340,7 +1315,7 @@ class TestDomainRequestAdmin(MockEppLib): ("email", "testy@town.com"), ("phone", "(555) 555 5555"), ] - self.assert_response_contains_distinct_values(response, expected_ao_fields) + self.test_helper.assert_response_contains_distinct_values(response, expected_ao_fields) # count=4 because the underlying domain has two users with this name. # The dropdown has 3 of these. @@ -1365,7 +1340,7 @@ class TestDomainRequestAdmin(MockEppLib): ("email", "testy2@town.com"), ("phone", "(555) 555 5557"), ] - self.assert_response_contains_distinct_values(response, expected_other_employees_fields) + self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) # count=1 as only one should exist in a table self.assertContains(response, "Testy Tester", count=1) @@ -1965,6 +1940,139 @@ class TestDomainInformationAdmin(TestCase): Contact.objects.all().delete() User.objects.all().delete() + @less_console_noise_decorator + def test_other_contacts_has_readonly_link(self): + """Tests if the readonly other_contacts field has links""" + + # 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() + + # Get the other contact + other_contact = domain_info.other_contacts.all().first() + + p = "userpass" + self.client.login(username="staffuser", 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) + + # Check that the page contains the url we expect + expected_href = reverse("admin:registrar_contact_change", args=[other_contact.id]) + self.assertContains(response, expected_href) + + # Check that the page contains the link we expect. + # Since the url is dynamic (populated by JS), we can test for its existence + # by checking for the end tag. + expected_url = "Testy Tester" + self.assertContains(response, expected_url) + + @less_console_noise_decorator + def test_contact_fields_have_detail_table(self): + """Tests if the contact fields have the detail table which displays title, email, and phone""" + + # Create fake creator + _creator = User.objects.create( + username="MrMeoward", + first_name="Meoward", + last_name="Jones", + ) + + # Due to the relation between User <==> Contact, + # the underlying contact has to be modified this way. + _creator.contact.email = "meoward.jones@igorville.gov" + _creator.contact.phone = "(555) 123 12345" + _creator.contact.title = "Treat inspector" + _creator.contact.save() + + # Create a fake domain request + domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW, user=_creator) + domain_request.approve() + domain_info = DomainInformation.objects.filter(domain=domain_request.approved_domain).get() + + p = "userpass" + self.client.login(username="staffuser", 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) + + # Check that the modal has the right content + # Check for the header + + # == Check for the creator == # + + # Check for the right title, email, and phone number in the response. + # We only need to check for the end tag + # (Otherwise this test will fail if we change classes, etc) + expected_creator_fields = [ + # Field, expected value + ("title", "Treat inspector"), + ("email", "meoward.jones@igorville.gov"), + ("phone", "(555) 123 12345"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_creator_fields) + + # Check for the field itself + self.assertContains(response, "Meoward Jones") + + # == Check for the submitter == # + expected_submitter_fields = [ + # Field, expected value + ("title", "Admin Tester"), + ("email", "mayor@igorville.gov"), + ("phone", "(555) 555 5556"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_submitter_fields) + self.assertContains(response, "Testy2 Tester2") + + # == Check for the authorizing_official == # + expected_ao_fields = [ + # Field, expected value + ("title", "Chief Tester"), + ("email", "testy@town.com"), + ("phone", "(555) 555 5555"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_ao_fields) + + # count=4 because the underlying domain has two users with this name. + # The dropdown has 3 of these. + self.assertContains(response, "Testy Tester", count=4) + + # Check for table titles. We only need to check for the end tag + # (Otherwise this test will fail if we change classes, etc) + + # Title. Count=3 because this table appears on three records. + self.assertContains(response, "Title", count=3) + + # Email. Count=3 because this table appears on three records. + self.assertContains(response, "Email", count=3) + + # Phone. Count=3 because this table appears on three records. + self.assertContains(response, "Phone", count=3) + + # == Test the other_employees field == # + expected_other_employees_fields = [ + # Field, expected value + ("title", "Another Tester"), + ("email", "testy2@town.com"), + ("phone", "(555) 555 5557"), + ] + self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) + + # count=1 as only one should exist in a table + self.assertContains(response, "Testy Tester", count=1) + def test_readonly_fields_for_analyst(self): """Ensures that analysts have their permissions setup correctly""" with less_console_noise(): @@ -1983,6 +2091,7 @@ class TestDomainInformationAdmin(TestCase): "no_other_contacts_rationale", "anything_else", "is_policy_acknowledged", + "other_contacts", ] self.assertEqual(readonly_fields, expected_fields) From 14fb918719690d61e34680e52342a2aac97f6243 Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Thu, 21 Mar 2024 16:12:34 -0700 Subject: [PATCH 32/61] Update function description, and remove unecessary save and logger --- src/registrar/models/federal_agency.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/registrar/models/federal_agency.py b/src/registrar/models/federal_agency.py index f1f19e845..a65a5b148 100644 --- a/src/registrar/models/federal_agency.py +++ b/src/registrar/models/federal_agency.py @@ -20,7 +20,8 @@ class FederalAgency(TimeStampedModel): return f"{self.agency}" def create_federal_agencies(apps, schema_editor): - """This method gets run from a data migration.""" + """This method gets run from a data migration to prepopulate data + regarding federal agencies.""" # Hard to pass self to these methods as the calls from migrations # are only expecting apps and schema_editor, so we'll just define @@ -222,7 +223,5 @@ class FederalAgency(TimeStampedModel): agency=agency, ) logger.debug(agency + " added to record " + federal_agencies_list.agency) - federal_agencies_list.save() - logger.debug("Federal agency added to table " + federal_agencies_list.agency) except Exception as e: logger.error(f"Error creating federal agency: {e}") From be1f6558d0fb676187f267dd7e6c7318f8ad875a Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Thu, 21 Mar 2024 16:47:23 -0700 Subject: [PATCH 33/61] Remove our migrations to fix main --- .../migrations/0077_federalagency.py | 40 ------------------- .../migrations/0078_create_groups_v09.py | 37 ----------------- 2 files changed, 77 deletions(-) delete mode 100644 src/registrar/migrations/0077_federalagency.py delete mode 100644 src/registrar/migrations/0078_create_groups_v09.py diff --git a/src/registrar/migrations/0077_federalagency.py b/src/registrar/migrations/0077_federalagency.py deleted file mode 100644 index 0b3be0f70..000000000 --- a/src/registrar/migrations/0077_federalagency.py +++ /dev/null @@ -1,40 +0,0 @@ -# Generated by Django 4.2.10 on 2024-03-19 05:42 -# This migration seeds the federal agency table with initial data and manually calls -# FederalAgency.create_federal_agencies to populate initial table values. - -from django.db import migrations, models -from registrar.models import FederalAgency -from typing import Any - - -# For linting: RunPython expects a function reference. -def create_federal_agencies(apps, schema_editor) -> Any: - FederalAgency.create_federal_agencies(apps, schema_editor) - - -class Migration(migrations.Migration): - - dependencies = [ - ("registrar", "0076_alter_domainrequest_current_websites_and_more"), - ] - - operations = [ - migrations.CreateModel( - name="FederalAgency", - fields=[ - ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ("agency", models.CharField(blank=True, help_text="Federal agency", null=True)), - ], - options={ - "verbose_name": "Federal agency", - "verbose_name_plural": "Federal agencies", - }, - ), - migrations.RunPython( - create_federal_agencies, - reverse_code=migrations.RunPython.noop, - atomic=True, - ), - ] diff --git a/src/registrar/migrations/0078_create_groups_v09.py b/src/registrar/migrations/0078_create_groups_v09.py deleted file mode 100644 index 6f89e0cb6..000000000 --- a/src/registrar/migrations/0078_create_groups_v09.py +++ /dev/null @@ -1,37 +0,0 @@ -# This migration creates the create_full_access_group and create_cisa_analyst_group groups -# It is dependent on 0035 (which populates ContentType and Permissions) -# If permissions on the groups need changing, edit CISA_ANALYST_GROUP_PERMISSIONS -# in the user_group model then: -# [NOT RECOMMENDED] -# step 1: docker-compose exec app ./manage.py migrate --fake registrar 0035_contenttypes_permissions -# step 2: docker-compose exec app ./manage.py migrate registrar 0036_create_groups -# step 3: fake run the latest migration in the migrations list -# [RECOMMENDED] -# Alternatively: -# step 1: duplicate the migration that loads data -# step 2: docker-compose exec app ./manage.py migrate - -from django.db import migrations -from registrar.models import UserGroup -from typing import Any - - -# For linting: RunPython expects a function reference, -# so let's give it one -def create_groups(apps, schema_editor) -> Any: - UserGroup.create_cisa_analyst_group(apps, schema_editor) - UserGroup.create_full_access_group(apps, schema_editor) - - -class Migration(migrations.Migration): - dependencies = [ - ("registrar", "0077_federalagency"), - ] - - operations = [ - migrations.RunPython( - create_groups, - reverse_code=migrations.RunPython.noop, - atomic=True, - ), - ] From 690cfafc99063f2d616792aa9fc70518220ff080 Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Thu, 21 Mar 2024 17:09:14 -0700 Subject: [PATCH 34/61] Update migrations to main --- .../migrations/0078_federalagency.py | 37 +++++++++++++++++++ .../migrations/0079_create_groups_v08.py | 37 +++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 src/registrar/migrations/0078_federalagency.py create mode 100644 src/registrar/migrations/0079_create_groups_v08.py diff --git a/src/registrar/migrations/0078_federalagency.py b/src/registrar/migrations/0078_federalagency.py new file mode 100644 index 000000000..37d2298ec --- /dev/null +++ b/src/registrar/migrations/0078_federalagency.py @@ -0,0 +1,37 @@ +# Generated by Django 4.2.10 on 2024-03-22 00:05 + +from django.db import migrations, models +from registrar.models import FederalAgency +from typing import Any + + +# For linting: RunPython expects a function reference. +def create_federal_agencies(apps, schema_editor) -> Any: + FederalAgency.create_federal_agencies(apps, schema_editor) + +class Migration(migrations.Migration): + + dependencies = [ + ("registrar", "0077_alter_publiccontact_fax_alter_publiccontact_org_and_more"), + ] + + operations = [ + migrations.CreateModel( + name="FederalAgency", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("agency", models.CharField(blank=True, help_text="Federal agency", null=True)), + ], + options={ + "verbose_name": "Federal agency", + "verbose_name_plural": "Federal agencies", + }, + ), + migrations.RunPython( + create_federal_agencies, + reverse_code=migrations.RunPython.noop, + atomic=True, + ), + ] \ No newline at end of file diff --git a/src/registrar/migrations/0079_create_groups_v08.py b/src/registrar/migrations/0079_create_groups_v08.py new file mode 100644 index 000000000..307dda358 --- /dev/null +++ b/src/registrar/migrations/0079_create_groups_v08.py @@ -0,0 +1,37 @@ +# This migration creates the create_full_access_group and create_cisa_analyst_group groups +# It is dependent on 0035 (which populates ContentType and Permissions) +# If permissions on the groups need changing, edit CISA_ANALYST_GROUP_PERMISSIONS +# in the user_group model then: +# [NOT RECOMMENDED] +# step 1: docker-compose exec app ./manage.py migrate --fake registrar 0035_contenttypes_permissions +# step 2: docker-compose exec app ./manage.py migrate registrar 0036_create_groups +# step 3: fake run the latest migration in the migrations list +# [RECOMMENDED] +# Alternatively: +# step 1: duplicate the migration that loads data +# step 2: docker-compose exec app ./manage.py migrate + +from django.db import migrations +from registrar.models import UserGroup +from typing import Any + + +# For linting: RunPython expects a function reference, +# so let's give it one +def create_groups(apps, schema_editor) -> Any: + UserGroup.create_cisa_analyst_group(apps, schema_editor) + UserGroup.create_full_access_group(apps, schema_editor) + + +class Migration(migrations.Migration): + dependencies = [ + ("registrar", "0078_federalagency"), + ] + + operations = [ + migrations.RunPython( + create_groups, + reverse_code=migrations.RunPython.noop, + atomic=True, + ), + ] \ No newline at end of file From 969909895c6a03eefc1122eb4ff05e223f95f1c4 Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Thu, 21 Mar 2024 17:13:51 -0700 Subject: [PATCH 35/61] Linter --- src/registrar/migrations/0078_federalagency.py | 3 ++- .../{0079_create_groups_v08.py => 0079_create_groups_v09.py} | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) rename src/registrar/migrations/{0079_create_groups_v08.py => 0079_create_groups_v09.py} (99%) diff --git a/src/registrar/migrations/0078_federalagency.py b/src/registrar/migrations/0078_federalagency.py index 37d2298ec..14b1e9f1a 100644 --- a/src/registrar/migrations/0078_federalagency.py +++ b/src/registrar/migrations/0078_federalagency.py @@ -9,6 +9,7 @@ from typing import Any def create_federal_agencies(apps, schema_editor) -> Any: FederalAgency.create_federal_agencies(apps, schema_editor) + class Migration(migrations.Migration): dependencies = [ @@ -34,4 +35,4 @@ class Migration(migrations.Migration): reverse_code=migrations.RunPython.noop, atomic=True, ), - ] \ No newline at end of file + ] diff --git a/src/registrar/migrations/0079_create_groups_v08.py b/src/registrar/migrations/0079_create_groups_v09.py similarity index 99% rename from src/registrar/migrations/0079_create_groups_v08.py rename to src/registrar/migrations/0079_create_groups_v09.py index 307dda358..dd09a2a41 100644 --- a/src/registrar/migrations/0079_create_groups_v08.py +++ b/src/registrar/migrations/0079_create_groups_v09.py @@ -34,4 +34,4 @@ class Migration(migrations.Migration): reverse_code=migrations.RunPython.noop, atomic=True, ), - ] \ No newline at end of file + ] From b74dbcbcd4db1065fe987735824a3446f91850dd Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Fri, 22 Mar 2024 14:33:26 -0700 Subject: [PATCH 36/61] Add bulk_create to add federal agencies --- src/registrar/models/federal_agency.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/registrar/models/federal_agency.py b/src/registrar/models/federal_agency.py index a65a5b148..7bde05574 100644 --- a/src/registrar/models/federal_agency.py +++ b/src/registrar/models/federal_agency.py @@ -214,14 +214,10 @@ class FederalAgency(TimeStampedModel): ] FederalAgency = apps.get_model("registrar", "FederalAgency") - logger.info("Creating federal agency table.") try: - for agency in AGENCIES: - federal_agencies_list, _ = FederalAgency.objects.get_or_create( - agency=agency, - ) - logger.debug(agency + " added to record " + federal_agencies_list.agency) + agencies = [FederalAgency(agency=agency) for agency in AGENCIES] + FederalAgency.objects.bulk_create(agencies) except Exception as e: logger.error(f"Error creating federal agency: {e}") From 69bb18a6a620bd2ddd8114c501b21ddfac54140b Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Fri, 22 Mar 2024 15:23:14 -0700 Subject: [PATCH 37/61] Update migrations to most recent --- .../{0078_federalagency.py => 0079_federalagency.py} | 4 ++-- .../{0079_create_groups_v09.py => 0080_create_groups_v09.py} | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/registrar/migrations/{0078_federalagency.py => 0079_federalagency.py} (88%) rename src/registrar/migrations/{0079_create_groups_v09.py => 0080_create_groups_v09.py} (91%) diff --git a/src/registrar/migrations/0078_federalagency.py b/src/registrar/migrations/0079_federalagency.py similarity index 88% rename from src/registrar/migrations/0078_federalagency.py rename to src/registrar/migrations/0079_federalagency.py index 14b1e9f1a..2f42e3382 100644 --- a/src/registrar/migrations/0078_federalagency.py +++ b/src/registrar/migrations/0079_federalagency.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.10 on 2024-03-22 00:05 +# Generated by Django 4.2.10 on 2024-03-22 22:18 from django.db import migrations, models from registrar.models import FederalAgency @@ -13,7 +13,7 @@ def create_federal_agencies(apps, schema_editor) -> Any: class Migration(migrations.Migration): dependencies = [ - ("registrar", "0077_alter_publiccontact_fax_alter_publiccontact_org_and_more"), + ("registrar", "0078_rename_organization_type_domaininformation_generic_org_type_and_more"), ] operations = [ diff --git a/src/registrar/migrations/0079_create_groups_v09.py b/src/registrar/migrations/0080_create_groups_v09.py similarity index 91% rename from src/registrar/migrations/0079_create_groups_v09.py rename to src/registrar/migrations/0080_create_groups_v09.py index dd09a2a41..e2e44ecc8 100644 --- a/src/registrar/migrations/0079_create_groups_v09.py +++ b/src/registrar/migrations/0080_create_groups_v09.py @@ -1,5 +1,5 @@ # This migration creates the create_full_access_group and create_cisa_analyst_group groups -# It is dependent on 0035 (which populates ContentType and Permissions) +# It is dependent on 0079 (which populates ContentType and Permissions) # If permissions on the groups need changing, edit CISA_ANALYST_GROUP_PERMISSIONS # in the user_group model then: # [NOT RECOMMENDED] @@ -25,7 +25,7 @@ def create_groups(apps, schema_editor) -> Any: class Migration(migrations.Migration): dependencies = [ - ("registrar", "0078_federalagency"), + ("registrar", "0079_federalagency"), ] operations = [ From 4e5809b99c3d583de3009bfd4c1f6383bc0575bb Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 25 Mar 2024 09:50:52 -0600 Subject: [PATCH 38/61] Style simplification --- src/registrar/assets/sass/_theme/_admin.scss | 30 ------------------- .../admin/includes/detail_table_fieldset.html | 2 +- 2 files changed, 1 insertion(+), 31 deletions(-) diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index c711e296e..8dd2a59f3 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -347,27 +347,6 @@ input.admin-confirm-button { color: $dhs-blue-70; } -details.dja-detail-table { - background-color: var(--darkened-bg); - display: inline-table; - .dja-details-summary { - color: var(--header-link-color); - background-color: var(--primary); - } - .dja-details-contents { - tr { - background-color: var(--darkened-bg); - } - } -} - -table.dja-user-detail-table { - margin-left: 160px; - tr { - background-color: var(--body-bg); - } -} - .admin-icon-group { position: relative; display: flex; @@ -391,19 +370,10 @@ table.dja-user-detail-table { } } - - details.dja-detail-table { - background-color: var(--darkened-bg); display: inline-table; .dja-details-summary { color: var(--header-link-color); - background-color: var(--primary); - } - .dja-details-contents { - tr { - background-color: var(--darkened-bg); - } } } 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 f5a5b71ee..6ebb74245 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -38,7 +38,7 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% elif field.field.name == "other_contacts" and original.other_contacts.all %}
Details -
+
{% for contact in original.other_contacts.all %} From c54bffca4c962869dc82be2867f6f79c40f77036 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 25 Mar 2024 10:07:31 -0600 Subject: [PATCH 39/61] Further simplification --- src/registrar/assets/sass/_theme/_admin.scss | 9 +++++++++ .../django/admin/includes/detail_table_fieldset.html | 10 +++++----- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index 8dd2a59f3..9532f622d 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -372,9 +372,18 @@ input.admin-confirm-button { details.dja-detail-table { display: inline-table; + background-color: var(--body-bg); .dja-details-summary { color: var(--header-link-color); } + + tr { + background-color: transparent; + } + + td, th { + padding-left: 12px; + } } table.dja-user-detail-table { 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 6ebb74245..6784c1f3b 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -37,7 +37,7 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% include "django/admin/includes/contact_detail_table.html" with user=original.authorizing_official field_name="authorizing_official" %} {% elif field.field.name == "other_contacts" and original.other_contacts.all %}
- Details + Details
@@ -47,10 +47,10 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) Then we can link these two fields using javascript. {% endcomment %} - - - - + + + + {% endfor %} From cedd54c673636879d60dbbeb4fa6e7cb4a6d5366 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Mon, 25 Mar 2024 12:14:10 -0400 Subject: [PATCH 40/61] Refactor get_sliced_domains to get accurate numbers --- src/registrar/tests/common.py | 15 ++++++++++ src/registrar/tests/test_reports.py | 14 +++++++-- src/registrar/utility/csv_export.py | 46 +++++++++++++++++++++-------- 3 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 5349a3b83..259b07306 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -548,6 +548,9 @@ class MockDb(TestCase): state=Domain.State.READY, first_ready=timezone.make_aware(datetime.combine(date.today() + timedelta(days=1), datetime.min.time())), ) + self.domain_11, _ = Domain.objects.get_or_create( + name="cdomain11.gov", state=Domain.State.READY, first_ready=timezone.now() + ) self.domain_information_1, _ = DomainInformation.objects.get_or_create( creator=self.user, @@ -616,6 +619,14 @@ class MockDb(TestCase): federal_agency="Armed Forces Retirement Home", is_election_board=False, ) + self.domain_information_11, _ = DomainInformation.objects.get_or_create( + creator=self.user, + domain=self.domain_11, + generic_org_type="federal", + federal_agency="World War I Centennial Commission", + federal_type="executive", + is_election_board=True, + ) meoward_user = get_user_model().objects.create( username="meoward_username", first_name="first_meoward", last_name="last_meoward", email="meoward@rocks.com" @@ -641,6 +652,10 @@ class MockDb(TestCase): user=meoward_user, domain=self.domain_2, role=UserDomainRole.Roles.MANAGER ) + _, created = UserDomainRole.objects.get_or_create( + user=meoward_user, domain=self.domain_11, role=UserDomainRole.Roles.MANAGER + ) + with less_console_noise(): self.domain_request_1 = completed_domain_request( status=DomainRequest.DomainRequestStatus.STARTED, name="city1.gov" diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 029fe873a..613c3b9bc 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -44,6 +44,7 @@ class CsvReportsTest(MockDb): fake_open = mock_open() expected_file_content = [ call("Domain name,Domain type,Agency,Organization name,City,State,Security contact email\r\n"), + call("cdomain11.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), call("adomain10.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), @@ -65,6 +66,7 @@ class CsvReportsTest(MockDb): fake_open = mock_open() expected_file_content = [ call("Domain name,Domain type,Agency,Organization name,City,State,Security contact email\r\n"), + call("cdomain11.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"), call("adomain10.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"), @@ -255,6 +257,7 @@ class ExportDataTest(MockDb, MockEppLib): "AO email,Security contact email,Status,Expiration date\n" "adomain10.gov,Federal,Armed Forces Retirement Home,Ready\n" "adomain2.gov,Interstate,(blank),Dns needed\n" + "cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n" "ddomain3.gov,Federal,Armed Forces Retirement Home,123@mail.gov,On hold,2023-05-25\n" "defaultsecurity.gov,Federal - Executive,World War I Centennial Commission,(blank),Ready" ) @@ -315,6 +318,7 @@ class ExportDataTest(MockDb, MockEppLib): "Security contact email,Status\n" "adomain10.gov,Federal,Armed Forces Retirement Home,Ready\n" "adomain2.gov,Interstate,Dns needed\n" + "cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n" "cdomain1.gov,Federal - Executive,World War I Centennial Commission,Ready\n" "ddomain3.gov,Federal,Armed Forces Retirement Home,On hold\n" ) @@ -365,6 +369,7 @@ class ExportDataTest(MockDb, MockEppLib): "Domain name,Domain type,Agency,Organization name,City," "State,Security contact email\n" "adomain10.gov,Federal,Armed Forces Retirement Home\n" + "cdomain11.govFederal-ExecutiveWorldWarICentennialCommission\n" "cdomain1.gov,Federal - Executive,World War I Centennial Commission\n" "ddomain3.gov,Federal,Armed Forces Retirement Home\n" ) @@ -455,6 +460,7 @@ class ExportDataTest(MockDb, MockEppLib): "State,Status,Expiration date\n" "cdomain1.gov,Federal-Executive,World War I Centennial Commission,,,,Ready,\n" "adomain10.gov,Federal,Armed Forces Retirement Home,,,,Ready,\n" + "cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n" "zdomain9.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" "sdomain8.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" "xdomain7.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" @@ -515,6 +521,7 @@ class ExportDataTest(MockDb, MockEppLib): "Security contact email,Domain manager email 1,Domain manager email 2,Domain manager email 3\n" "adomain10.gov,Ready,,Federal,Armed Forces Retirement Home,,,, , ,\n" "adomain2.gov,Dns needed,,Interstate,,,,, , , ,meoward@rocks.com\n" + "cdomain11.govReadyFederal-ExecutiveWorldWarICentennialCommissionmeoward@rocks.com\n" "cdomain1.gov,Ready,,Federal - Executive,World War I Centennial Commission,,," ", , , ,meoward@rocks.com,info@example.com,big_lebowski@dude.co\n" "ddomain3.gov,On hold,,Federal,Armed Forces Retirement Home,,,, , , ,,\n" @@ -551,9 +558,10 @@ class ExportDataTest(MockDb, MockEppLib): "MANAGED DOMAINS COUNTS AT END DATE\n" "Total,Federal,Interstate,State or territory,Tribal,County,City," "Special district,School district,Election office\n" - "1,1,0,0,0,0,0,0,0,1\n" + "2,2,0,0,0,0,0,0,0,2\n" "\n" "Domain name,Domain type,Domain manager email 1,Domain manager email 2,Domain manager email 3\n" + "cdomain11.govFederal-Executivemeoward@rocks.com\n" "cdomain1.gov,Federal - Executive,meoward@rocks.com,info@example.com,big_lebowski@dude.co\n" ) @@ -674,12 +682,12 @@ class HelperFunctions(MockDb): } # Test with distinct managed_domains_sliced_at_end_date = get_sliced_domains(filter_condition, True) - expected_content = [1, 1, 0, 0, 0, 0, 0, 0, 0, 1] + expected_content = [2, 2, 0, 0, 0, 0, 0, 0, 0, 2] self.assertEqual(managed_domains_sliced_at_end_date, expected_content) # Test without distinct managed_domains_sliced_at_end_date = get_sliced_domains(filter_condition) - expected_content = [1, 3, 0, 0, 0, 0, 0, 0, 0, 1] + expected_content = [2, 4, 0, 0, 0, 0, 0, 0, 0, 2] self.assertEqual(managed_domains_sliced_at_end_date, expected_content) def test_get_sliced_requests(self): diff --git a/src/registrar/utility/csv_export.py b/src/registrar/utility/csv_export.py index 7b93f4efa..aee3fc71c 100644 --- a/src/registrar/utility/csv_export.py +++ b/src/registrar/utility/csv_export.py @@ -467,16 +467,43 @@ def get_sliced_domains(filter_condition, distinct=False): domains_count = DomainInformation.objects.filter(**filter_condition).distinct().count() # Round trip 2: Get counts for other slices + # This will require either 8 filterd and distinct DB round trips, + # or 2 DB round trips plus iteration on domain_permissions for each domain if distinct: - generic_org_types_query = ( - DomainInformation.objects.filter(**filter_condition).values_list("generic_org_type", flat=True).distinct() + generic_org_types_query = DomainInformation.objects.filter(**filter_condition).values_list( + "domain_id", "generic_org_type" ) + # Initialize Counter to store counts for each generic_org_type + generic_org_type_counts = Counter() + + # Keep track of domains already counted + domains_counted = set() + + # Iterate over distinct domains + for domain_id, generic_org_type in generic_org_types_query: + # Check if the domain has already been counted + if domain_id in domains_counted: + continue + + # Get all permissions for the current domain + domain_permissions = DomainInformation.objects.filter(domain_id=domain_id, **filter_condition).values_list( + "domain__permissions", flat=True + ) + + # Check if the domain has multiple permissions + if len(domain_permissions) > 0: + # Mark the domain as counted + domains_counted.add(domain_id) + + # Increment the count for the corresponding generic_org_type + generic_org_type_counts[generic_org_type] += 1 else: generic_org_types_query = DomainInformation.objects.filter(**filter_condition).values_list( "generic_org_type", flat=True ) - generic_org_type_counts = Counter(generic_org_types_query) + generic_org_type_counts = Counter(generic_org_types_query) + # Extract counts for each generic_org_type federal = generic_org_type_counts.get(DomainRequest.OrganizationChoices.FEDERAL, 0) interstate = generic_org_type_counts.get(DomainRequest.OrganizationChoices.INTERSTATE, 0) state_or_territory = generic_org_type_counts.get(DomainRequest.OrganizationChoices.STATE_OR_TERRITORY, 0) @@ -503,21 +530,16 @@ def get_sliced_domains(filter_condition, distinct=False): ] -def get_sliced_requests(filter_condition, distinct=False): +def get_sliced_requests(filter_condition): """Get filtered requests counts sliced by org type and election office.""" # Round trip 1: Get distinct requests based on filter condition requests_count = DomainRequest.objects.filter(**filter_condition).distinct().count() # Round trip 2: Get counts for other slices - if distinct: - generic_org_types_query = ( - DomainRequest.objects.filter(**filter_condition).values_list("generic_org_type", flat=True).distinct() - ) - else: - generic_org_types_query = DomainRequest.objects.filter(**filter_condition).values_list( - "generic_org_type", flat=True - ) + generic_org_types_query = DomainRequest.objects.filter(**filter_condition).values_list( + "generic_org_type", flat=True + ) generic_org_type_counts = Counter(generic_org_types_query) federal = generic_org_type_counts.get(DomainRequest.OrganizationChoices.FEDERAL, 0) From 6a87d38749ff205922b3fcb1e7fcae21f1655987 Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Mon, 25 Mar 2024 12:40:01 -0400 Subject: [PATCH 41/61] Fix silent bug ib domain_type, add data to tests --- src/registrar/tests/common.py | 13 +++++++++++++ src/registrar/tests/test_reports.py | 13 +++++++++---- src/registrar/utility/csv_export.py | 2 +- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 259b07306..87af54669 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -551,6 +551,9 @@ class MockDb(TestCase): self.domain_11, _ = Domain.objects.get_or_create( name="cdomain11.gov", state=Domain.State.READY, first_ready=timezone.now() ) + self.domain_12, _ = Domain.objects.get_or_create( + name="zdomain12.gov", state=Domain.State.READY, first_ready=timezone.now() + ) self.domain_information_1, _ = DomainInformation.objects.get_or_create( creator=self.user, @@ -627,6 +630,12 @@ class MockDb(TestCase): federal_type="executive", is_election_board=True, ) + self.domain_information_12, _ = DomainInformation.objects.get_or_create( + creator=self.user, + domain=self.domain_12, + generic_org_type="interstate", + is_election_board=False, + ) meoward_user = get_user_model().objects.create( username="meoward_username", first_name="first_meoward", last_name="last_meoward", email="meoward@rocks.com" @@ -656,6 +665,10 @@ class MockDb(TestCase): user=meoward_user, domain=self.domain_11, role=UserDomainRole.Roles.MANAGER ) + _, created = UserDomainRole.objects.get_or_create( + user=meoward_user, domain=self.domain_12, role=UserDomainRole.Roles.MANAGER + ) + with less_console_noise(): self.domain_request_1 = completed_domain_request( status=DomainRequest.DomainRequestStatus.STARTED, name="city1.gov" diff --git a/src/registrar/tests/test_reports.py b/src/registrar/tests/test_reports.py index 613c3b9bc..5bd594a15 100644 --- a/src/registrar/tests/test_reports.py +++ b/src/registrar/tests/test_reports.py @@ -259,7 +259,8 @@ class ExportDataTest(MockDb, MockEppLib): "adomain2.gov,Interstate,(blank),Dns needed\n" "cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n" "ddomain3.gov,Federal,Armed Forces Retirement Home,123@mail.gov,On hold,2023-05-25\n" - "defaultsecurity.gov,Federal - Executive,World War I Centennial Commission,(blank),Ready" + "defaultsecurity.gov,Federal - Executive,World War I Centennial Commission,(blank),Ready\n" + "zdomain12.govInterstateReady\n" ) # Normalize line endings and remove commas, # spaces and leading/trailing whitespace @@ -321,6 +322,7 @@ class ExportDataTest(MockDb, MockEppLib): "cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n" "cdomain1.gov,Federal - Executive,World War I Centennial Commission,Ready\n" "ddomain3.gov,Federal,Armed Forces Retirement Home,On hold\n" + "zdomain12.govInterstateReady\n" ) # Normalize line endings and remove commas, # spaces and leading/trailing whitespace @@ -461,6 +463,7 @@ class ExportDataTest(MockDb, MockEppLib): "cdomain1.gov,Federal-Executive,World War I Centennial Commission,,,,Ready,\n" "adomain10.gov,Federal,Armed Forces Retirement Home,,,,Ready,\n" "cdomain11.govFederal-ExecutiveWorldWarICentennialCommissionReady\n" + "zdomain12.govInterstateReady\n" "zdomain9.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" "sdomain8.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" "xdomain7.gov,Federal,Armed Forces Retirement Home,,,,Deleted,\n" @@ -525,6 +528,7 @@ class ExportDataTest(MockDb, MockEppLib): "cdomain1.gov,Ready,,Federal - Executive,World War I Centennial Commission,,," ", , , ,meoward@rocks.com,info@example.com,big_lebowski@dude.co\n" "ddomain3.gov,On hold,,Federal,Armed Forces Retirement Home,,,, , , ,,\n" + "zdomain12.govReadyInterstatemeoward@rocks.com\n" ) # Normalize line endings and remove commas, # spaces and leading/trailing whitespace @@ -558,11 +562,12 @@ class ExportDataTest(MockDb, MockEppLib): "MANAGED DOMAINS COUNTS AT END DATE\n" "Total,Federal,Interstate,State or territory,Tribal,County,City," "Special district,School district,Election office\n" - "2,2,0,0,0,0,0,0,0,2\n" + "3,2,1,0,0,0,0,0,0,2\n" "\n" "Domain name,Domain type,Domain manager email 1,Domain manager email 2,Domain manager email 3\n" "cdomain11.govFederal-Executivemeoward@rocks.com\n" "cdomain1.gov,Federal - Executive,meoward@rocks.com,info@example.com,big_lebowski@dude.co\n" + "zdomain12.govInterstatemeoward@rocks.com\n" ) # Normalize line endings and remove commas, @@ -682,12 +687,12 @@ class HelperFunctions(MockDb): } # Test with distinct managed_domains_sliced_at_end_date = get_sliced_domains(filter_condition, True) - expected_content = [2, 2, 0, 0, 0, 0, 0, 0, 0, 2] + expected_content = [3, 2, 1, 0, 0, 0, 0, 0, 0, 2] self.assertEqual(managed_domains_sliced_at_end_date, expected_content) # Test without distinct managed_domains_sliced_at_end_date = get_sliced_domains(filter_condition) - expected_content = [2, 4, 0, 0, 0, 0, 0, 0, 0, 2] + expected_content = [3, 4, 1, 0, 0, 0, 0, 0, 0, 2] self.assertEqual(managed_domains_sliced_at_end_date, expected_content) def test_get_sliced_requests(self): diff --git a/src/registrar/utility/csv_export.py b/src/registrar/utility/csv_export.py index aee3fc71c..92185a2c6 100644 --- a/src/registrar/utility/csv_export.py +++ b/src/registrar/utility/csv_export.py @@ -80,7 +80,7 @@ def parse_domain_row(columns, domain_info: DomainInformation, security_emails_di if security_email.lower() in invalid_emails: security_email = "(blank)" - if domain_info.federal_type: + if domain_info.federal_type and domain_info.generic_org_type == "federal": domain_type = f"{domain_info.get_generic_org_type_display()} - {domain_info.get_federal_type_display()}" else: domain_type = domain_info.get_generic_org_type_display() From 8b2beba87950e8ef5a13f57effcc3551732efe34 Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Mon, 25 Mar 2024 12:15:19 -0700 Subject: [PATCH 42/61] Update naming of migrations --- .../{0079_federalagency.py => 0079_federalagency_v01.py} | 0 src/registrar/migrations/0080_create_groups_v09.py | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename src/registrar/migrations/{0079_federalagency.py => 0079_federalagency_v01.py} (100%) diff --git a/src/registrar/migrations/0079_federalagency.py b/src/registrar/migrations/0079_federalagency_v01.py similarity index 100% rename from src/registrar/migrations/0079_federalagency.py rename to src/registrar/migrations/0079_federalagency_v01.py diff --git a/src/registrar/migrations/0080_create_groups_v09.py b/src/registrar/migrations/0080_create_groups_v09.py index e2e44ecc8..58ca4e813 100644 --- a/src/registrar/migrations/0080_create_groups_v09.py +++ b/src/registrar/migrations/0080_create_groups_v09.py @@ -1,5 +1,5 @@ # This migration creates the create_full_access_group and create_cisa_analyst_group groups -# It is dependent on 0079 (which populates ContentType and Permissions) +# It is dependent on 0079 (which populates federal agencies) # If permissions on the groups need changing, edit CISA_ANALYST_GROUP_PERMISSIONS # in the user_group model then: # [NOT RECOMMENDED] @@ -25,7 +25,7 @@ def create_groups(apps, schema_editor) -> Any: class Migration(migrations.Migration): dependencies = [ - ("registrar", "0079_federalagency"), + ("registrar", "0079_federalagency_v01"), ] operations = [ From 562ad3c20d55b32ff9589d6c1b1d96b0e18af94a Mon Sep 17 00:00:00 2001 From: Rebecca Hsieh Date: Mon, 25 Mar 2024 12:23:12 -0700 Subject: [PATCH 43/61] Update migration file name with create --- ...federalagency_v01.py => 0079_create_federal_agencies_v01.py} | 0 src/registrar/migrations/0080_create_groups_v09.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename src/registrar/migrations/{0079_federalagency_v01.py => 0079_create_federal_agencies_v01.py} (100%) diff --git a/src/registrar/migrations/0079_federalagency_v01.py b/src/registrar/migrations/0079_create_federal_agencies_v01.py similarity index 100% rename from src/registrar/migrations/0079_federalagency_v01.py rename to src/registrar/migrations/0079_create_federal_agencies_v01.py diff --git a/src/registrar/migrations/0080_create_groups_v09.py b/src/registrar/migrations/0080_create_groups_v09.py index 58ca4e813..342404aa1 100644 --- a/src/registrar/migrations/0080_create_groups_v09.py +++ b/src/registrar/migrations/0080_create_groups_v09.py @@ -25,7 +25,7 @@ def create_groups(apps, schema_editor) -> Any: class Migration(migrations.Migration): dependencies = [ - ("registrar", "0079_federalagency_v01"), + ("registrar", "0079_create_federal_agencies_v01"), ] operations = [ From 55fd53fb5630c78799aee8bae5c508325ed931ad Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Mon, 25 Mar 2024 14:13:52 -0600 Subject: [PATCH 44/61] PR suggestions --- src/registrar/assets/js/get-gov-reports.js | 1 - src/registrar/assets/sass/_theme/_admin.scss | 68 ++++++------------- src/registrar/templates/admin/fieldset.html | 10 ++- .../admin/domain_information_change_form.html | 7 ++ .../admin/domain_request_change_form.html | 10 ++- .../admin/includes/contact_detail_table.html | 25 ++++--- .../admin/includes/detail_table_fieldset.html | 16 ++++- .../includes/domain_information_fieldset.html | 2 - .../includes/domain_request_fieldset.html | 2 - 9 files changed, 75 insertions(+), 66 deletions(-) delete mode 100644 src/registrar/templates/django/admin/includes/domain_information_fieldset.html delete mode 100644 src/registrar/templates/django/admin/includes/domain_request_fieldset.html diff --git a/src/registrar/assets/js/get-gov-reports.js b/src/registrar/assets/js/get-gov-reports.js index 735681777..92bba4a1f 100644 --- a/src/registrar/assets/js/get-gov-reports.js +++ b/src/registrar/assets/js/get-gov-reports.js @@ -61,7 +61,6 @@ function createComparativeColumnChart(canvasId, title, labelOne, labelTwo) { var canvas = document.getElementById(canvasId); if (!canvas) { - console.log("Could not find canvas") return } diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index 9532f622d..912a67f10 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -347,29 +347,6 @@ input.admin-confirm-button { color: $dhs-blue-70; } -.admin-icon-group { - position: relative; - display: flex; - align-items: center; - - .usa-button__icon { - position: absolute; - right: 0; - height: 100%; - } - - input { - // Allow for padding around the copy button - padding-right: 35px; - // Match the height of other inputs - min-height: 2.25rem; - } - - .no-outline-on-click:focus { - outline: none !important; - } -} - details.dja-detail-table { display: inline-table; background-color: var(--body-bg); @@ -383,40 +360,35 @@ details.dja-detail-table { td, th { padding-left: 12px; + border: none } + + thead > tr > th { + border-radius: 4px; + border-top: none; + border-bottom: none; + } + } table.dja-user-detail-table { - margin-left: 160px; tr { background-color: var(--body-bg); } + + // Make the head of the user detail table "float" + thead > tr > th { + border-radius: 4px; + border-top: none; + border-bottom: none; + } + + th, td { + border: none + } + } -.admin-icon-group { - position: relative; - display: flex; - align-items: center; - - .usa-button__icon { - position: absolute; - right: 0; - height: 100%; - } - - input { - // Allow for padding around the copy button - padding-right: 35px; - // Match the height of other inputs - min-height: 2.25rem; - } - - .no-outline-on-click:focus { - outline: none !important; - } -} - - .errors span.select2-selection { border: 1px solid var(--error-fg) !important; } \ No newline at end of file diff --git a/src/registrar/templates/admin/fieldset.html b/src/registrar/templates/admin/fieldset.html index 37f79ab46..579ad7033 100644 --- a/src/registrar/templates/admin/fieldset.html +++ b/src/registrar/templates/admin/fieldset.html @@ -6,10 +6,12 @@ It is not inherently customizable on its own, so we can modify this instead. https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/includes/fieldset.html {% endcomment %}
+ {# .gov override #} {% block fieldset_title %} {% if fieldset.name %}

{{ fieldset.name }}

{% endif %} {% endblock fieldset_title %} + {# .gov override #} {% block fieldset_description %} {% if fieldset.description %}
{{ fieldset.description|safe }}
@@ -24,16 +26,19 @@ https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/ {% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %}
{% if field.is_checkbox %} + {# .gov override #} {% block field_checkbox %} {{ field.field }}{{ field.label_tag }} {% endblock field_checkbox%} {% else %} {{ field.label_tag }} {% if field.is_readonly %} + {# .gov override #} {% block field_readonly %}
{{ field.contents }}
{% endblock field_readonly%} {% else %} + {# .gov override #} {% block field_other %} {{ field.field }} {% endblock field_other%} @@ -41,18 +46,21 @@ https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/ {% endif %}
+ {# .gov addition #} {% block before_help_text %} {# For templating purposes #} {% endblock before_help_text %} {% if field.field.help_text %} + {# .gov override #} {% block help_text %}
{{ field.field.help_text|safe }}
{% endblock help_text %} {% endif %} - + + {# .gov addition #} {% block after_help_text %} {# For templating purposes #} {% endblock after_help_text %} diff --git a/src/registrar/templates/django/admin/domain_information_change_form.html b/src/registrar/templates/django/admin/domain_information_change_form.html index 86475890d..f58ee2239 100644 --- a/src/registrar/templates/django/admin/domain_information_change_form.html +++ b/src/registrar/templates/django/admin/domain_information_change_form.html @@ -3,6 +3,13 @@ {% block field_sets %} {% for fieldset in adminform %} + {% comment %} + TODO: this will eventually need to be changed to something like this + if we ever want to customize this file: {% include "django/admin/includes/domain_information_fieldset.html" %} + + Use detail_table_fieldset as an example, or just extend it. + {% endcomment %} + {% include "django/admin/includes/detail_table_fieldset.html" %} {% endfor %} {% endblock %} diff --git a/src/registrar/templates/django/admin/domain_request_change_form.html b/src/registrar/templates/django/admin/domain_request_change_form.html index 5d5e6ed60..b0ee6e044 100644 --- a/src/registrar/templates/django/admin/domain_request_change_form.html +++ b/src/registrar/templates/django/admin/domain_request_change_form.html @@ -4,8 +4,16 @@ {% block field_sets %} {# Create an invisible tag so that we can use a click event to toggle the modal. #} + {% for fieldset in adminform %} - {% include "django/admin/includes/domain_request_fieldset.html" %} + {% comment %} + TODO: this will eventually need to be changed to something like this + if we ever want to customize this file: + {% include "django/admin/includes/domain_information_fieldset.html" %} + + Use detail_table_fieldset as an example, or just extend it. + {% endcomment %} + {% include "django/admin/includes/detail_table_fieldset.html" %} {% endfor %} {% endblock %} diff --git a/src/registrar/templates/django/admin/includes/contact_detail_table.html b/src/registrar/templates/django/admin/includes/contact_detail_table.html index c25812b4d..68dbedb24 100644 --- a/src/registrar/templates/django/admin/includes/contact_detail_table.html +++ b/src/registrar/templates/django/admin/includes/contact_detail_table.html @@ -1,9 +1,14 @@ {% load i18n static %} -
{{contact.first_name}} {{contact.last_name}}{{ contact.title }}{{ contact.email }}{{ contact.phone }}{{contact.first_name}} {{contact.last_name}}{{ contact.title }}{{ contact.email }}{{ contact.phone }}
+
+ + + + + - + {% if user.title or user.contact.title %} {% if user.contact.title %} @@ -15,27 +20,27 @@ {% endif %} - + {% if user.email or user.contact.email %} {% if user.contact.email %} - + {% else %} - + {% endif %} {% else %} - + {% endif %} - + {% if user.phone or user.contact.phone %} {% if user.contact.phone %} - + {% else %} - + {% endif %} {% else %} - + {% endif %} 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 6784c1f3b..25b2826db 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -30,16 +30,30 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% block after_help_text %} {% if field.field.name == "creator" %} +
+ {% include "django/admin/includes/contact_detail_table.html" with user=original.creator field_name="creator" %} +
{% elif field.field.name == "submitter" %} +
+ {% include "django/admin/includes/contact_detail_table.html" with user=original.submitter field_name="submitter" %} +
{% elif field.field.name == "authorizing_official" %} +
+ {% include "django/admin/includes/contact_detail_table.html" with user=original.authorizing_official field_name="authorizing_official" %} +
{% elif field.field.name == "other_contacts" and original.other_contacts.all %} -
+
Details
Contact details
TitleTitle{{ user.contact.title }}
EmailEmail{{ user.contact.email }}{{ user.contact.email }}{{ user.email }}{{ user.email }}Nothing foundNothing found
PhonePhone{{ user.contact.phone }}{{ user.contact.phone }}{{ user.phone }}{{ user.phone }}Nothing foundNothing found
+ + + + + {% for contact in original.other_contacts.all %} {% comment %} diff --git a/src/registrar/templates/django/admin/includes/domain_information_fieldset.html b/src/registrar/templates/django/admin/includes/domain_information_fieldset.html deleted file mode 100644 index b42873c3c..000000000 --- a/src/registrar/templates/django/admin/includes/domain_information_fieldset.html +++ /dev/null @@ -1,2 +0,0 @@ -{% extends "django/admin/includes/detail_table_fieldset.html" %} -{# Stubbed file for future expansion #} diff --git a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html b/src/registrar/templates/django/admin/includes/domain_request_fieldset.html deleted file mode 100644 index b42873c3c..000000000 --- a/src/registrar/templates/django/admin/includes/domain_request_fieldset.html +++ /dev/null @@ -1,2 +0,0 @@ -{% extends "django/admin/includes/detail_table_fieldset.html" %} -{# Stubbed file for future expansion #} From a5b5edeef6b2ad348dbd7b4c17666cff8763732b Mon Sep 17 00:00:00 2001 From: Rachid Mrad Date: Mon, 25 Mar 2024 16:33:09 -0400 Subject: [PATCH 45/61] Use constant for FEDERAL --- src/registrar/utility/csv_export.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/utility/csv_export.py b/src/registrar/utility/csv_export.py index 92185a2c6..2723ab919 100644 --- a/src/registrar/utility/csv_export.py +++ b/src/registrar/utility/csv_export.py @@ -80,7 +80,7 @@ def parse_domain_row(columns, domain_info: DomainInformation, security_emails_di if security_email.lower() in invalid_emails: security_email = "(blank)" - if domain_info.federal_type and domain_info.generic_org_type == "federal": + if domain_info.federal_type and domain_info.generic_org_type == DomainRequest.OrganizationChoices.FEDERAL: domain_type = f"{domain_info.get_generic_org_type_display()} - {domain_info.get_federal_type_display()}" else: domain_type = domain_info.get_generic_org_type_display() From 8c4e43d17e2625ec706375006a70f7980fbeaf62 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Mon, 25 Mar 2024 16:44:58 -0700 Subject: [PATCH 46/61] updated to include redirect language --- docs/operations/runbooks/downtime_incident_management.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/operations/runbooks/downtime_incident_management.md b/docs/operations/runbooks/downtime_incident_management.md index 1e0870efa..5e7aabb87 100644 --- a/docs/operations/runbooks/downtime_incident_management.md +++ b/docs/operations/runbooks/downtime_incident_management.md @@ -16,12 +16,14 @@ The following set of rules should be followed while an incident is in progress. - If downtime occurs outside of working hours, team members who are off for the day may still be pinged and called but are not required to join if unavailable to do so. - Uncomment the [banner on get.gov](https://github.com/cisagov/get.gov/blob/0365d3d34b041cc9353497b2b5f81b6ab7fe75a9/_includes/header.html#L9), so it is transparent to users that we know about the issue on manage.get.gov. - Designers or Developers should be able to make this change; if designers are online and can help with this task, that will allow developers to focus on fixing the bug. +- If the issue persists for three hours or more, follow the [instructions for enabling/disabling a redirect to get.gov](https://docs.google.com/document/d/1PiWXpjBzbiKsSYqEo9Rkl72HMytMp7zTte9CI-vvwYw/edit) ## Post Incident The following checklist should be followed after the site is back up and running. - [ ] Message in #dotgov-announce with an @here saying the issue is resolved +- [ ] If the redirect was used, refer to the [instructions for enabling/disabling a redirect to get.gov](https://docs.google.com/document/d/1PiWXpjBzbiKsSYqEo9Rkl72HMytMp7zTte9CI-vvwYw/edit) to turn off this redirect. Double-check in the browser that this redirect is no longer occurring (the change may take a few minutes to take full effect) - [ ] Remove the [banner on get.gov](https://github.com/cisagov/get.gov/blob/0365d3d34b041cc9353497b2b5f81b6ab7fe75a9/_includes/header.html#L9) by commenting it out. - [ ] Write up what happened and when; if the cause is already known, write that as well. This is a draft for internal communications and not for any public facing site and can be as simple as using bullet points. - [ ] If the cause is not known yet, developers should investigate the issue as the highest priority task. From af81357d68dbd9bf8df369c7c32182640c3fd5ca Mon Sep 17 00:00:00 2001 From: Erin <121973038+erinysong@users.noreply.github.com> Date: Tue, 26 Mar 2024 10:59:51 -0700 Subject: [PATCH 47/61] Fix content of error message --- src/registrar/models/federal_agency.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/models/federal_agency.py b/src/registrar/models/federal_agency.py index 7bde05574..89b15ab56 100644 --- a/src/registrar/models/federal_agency.py +++ b/src/registrar/models/federal_agency.py @@ -220,4 +220,4 @@ class FederalAgency(TimeStampedModel): agencies = [FederalAgency(agency=agency) for agency in AGENCIES] FederalAgency.objects.bulk_create(agencies) except Exception as e: - logger.error(f"Error creating federal agency: {e}") + logger.error(f"Error creating federal agencies: {e}") From a70214e46763b3f821862d6c88934caa87b61f86 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:21:40 -0600 Subject: [PATCH 48/61] PR suggestions --- src/registrar/admin.py | 6 ++ .../admin/includes/contact_detail_table.html | 46 ++++++++--- .../admin/includes/detail_table_fieldset.html | 79 ++++++++++++------- 3 files changed, 88 insertions(+), 43 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index febd8123c..e4a94e20d 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -910,6 +910,9 @@ class DomainInformationAdmin(ListHeaderAdmin): ), ] + # Readonly fields for analysts and superusers + readonly_fields = ("other_contacts",) + # Read only that we'll leverage for CISA Analysts analyst_readonly_fields = [ "creator", @@ -1120,6 +1123,9 @@ class DomainRequestAdmin(ListHeaderAdmin): ), ] + # Readonly fields for analysts and superusers + readonly_fields = ("other_contacts", "current_websites") + # Read only that we'll leverage for CISA Analysts analyst_readonly_fields = [ "creator", diff --git a/src/registrar/templates/django/admin/includes/contact_detail_table.html b/src/registrar/templates/django/admin/includes/contact_detail_table.html index 68dbedb24..decce0f43 100644 --- a/src/registrar/templates/django/admin/includes/contact_detail_table.html +++ b/src/registrar/templates/django/admin/includes/contact_detail_table.html @@ -1,47 +1,67 @@ {% load i18n static %} -
Contact information
+
+ {% if show_table_details %} + {% endif %} + + {% if user.title or user.contact.title or user.email or user.contact.email or user.phone or user.contact.phone %} - + {% if show_table_details %} + + {% endif %} + {% if user.title or user.contact.title %} {% if user.contact.title %} - + {% else %} - + {% endif %} {% else %} - + {% endif %} - + {% if show_table_details %} + + {% endif %} + {% if user.email or user.contact.email %} {% if user.contact.email %} - + {% else %} - + {% endif %} {% else %} - + {% endif %} - + {% if show_table_details %} + + {% endif %} + {% if user.phone or user.contact.phone %} {% if user.contact.phone %} - + {% else %} - + {% endif %} {% else %} - + {% endif %} + {% else %} + + + + + + {% endif %}
Contact details
TitleTitle{{ user.contact.title }}{{ user.contact.title }}{{ user.title }}{{ user.title }}Nothing foundNone
EmailEmail{{ user.contact.email }}{{ user.contact.email }}{{ user.email }}{{ user.email }}Nothing foundNone
PhonePhone{{ user.contact.phone }}{{ user.contact.phone }}{{ user.phone }}{{ user.phone }}Nothing foundNone
No additional contact information found.
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 25b2826db..4ca577688 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -20,9 +20,17 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) This ONLY applies to analysts. For superusers, its business as usual. {% endcomment %} - {% for website in field.contents|split:", " %} +
+ {% with total_websites=field.contents|split:", " %} + {% for website in total_websites %} {{ website }}{% if not forloop.last %}, {% endif %} + {# Acts as a
#} + {% if total_websites|length < 5 %} +
+ {% endif %} {% endfor %} + {% endwith %} +
{% else %}
{{ field.contents }}
{% endif %} @@ -32,44 +40,55 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% if field.field.name == "creator" %}
- {% include "django/admin/includes/contact_detail_table.html" with user=original.creator field_name="creator" %} + {% include "django/admin/includes/contact_detail_table.html" with user=original.creator field_name="creator" no_title_top_padding=field.is_readonly %}
{% elif field.field.name == "submitter" %}
- {% include "django/admin/includes/contact_detail_table.html" with user=original.submitter field_name="submitter" %} + {% include "django/admin/includes/contact_detail_table.html" with user=original.submitter field_name="submitter" no_title_top_padding=field.is_readonly %}
{% elif field.field.name == "authorizing_official" %}
- {% include "django/admin/includes/contact_detail_table.html" with user=original.authorizing_official field_name="authorizing_official" %} + {% include "django/admin/includes/contact_detail_table.html" with user=original.authorizing_official field_name="authorizing_official" no_title_top_padding=field.is_readonly %}
{% elif field.field.name == "other_contacts" and original.other_contacts.all %} -
- Details -
- - - - - - - - {% for contact in original.other_contacts.all %} - {% comment %} - Since we can't get the id from field, we can embed this information here. - Then we can link these two fields using javascript. - {% endcomment %} - - - - - - - {% endfor %} - -
Contact information
{{contact.first_name}} {{contact.last_name}}{{ contact.title }}{{ contact.email }}{{ contact.phone }}
-
-
+ {% with all_contacts=original.other_contacts.all %} + {% if all_contacts.count == 1 %} + {% for contact in all_contacts %} +
+ + {% include "django/admin/includes/contact_detail_table.html" with user=contact field_name="other_contact" no_title_top_padding=field.is_readonly %} +
+ {% endfor %} + {% else %} +
+ Details +
+ + + + + + + + {% for contact in all_contacts %} + {% comment %} + Since we can't get the id from field, we can embed this information here. + Then we can link these two fields using javascript. + {% endcomment %} + + + + + + + {% endfor %} + +
Contact information
{{contact.first_name}} {{contact.last_name}}{{ contact.title }}{{ contact.email }}{{ contact.phone }}
+
+
+ {% endif %} + {% endwith %} {% endif %} {% endblock after_help_text %} From a512dcd1b17d8035ed3fad2b0bff53b149c64a18 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:45:26 -0600 Subject: [PATCH 49/61] Fix unit tests --- src/registrar/admin.py | 6 +-- src/registrar/assets/js/get-gov-admin.js | 6 +++ .../admin/includes/detail_table_fieldset.html | 1 + src/registrar/tests/common.py | 1 - src/registrar/tests/test_admin.py | 41 +++++++------------ 5 files changed, 22 insertions(+), 33 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index e4a94e20d..b3a603d84 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -924,7 +924,6 @@ class DomainInformationAdmin(ListHeaderAdmin): "no_other_contacts_rationale", "anything_else", "is_policy_acknowledged", - "other_contacts", ] # For each filter_horizontal, init in admin js extendFilterHorizontalWidgets @@ -1133,8 +1132,6 @@ class DomainRequestAdmin(ListHeaderAdmin): "requested_domain", "approved_domain", "alternative_domains", - "other_contacts", - "current_websites", "purpose", "submitter", "no_other_contacts_rationale", @@ -1310,7 +1307,7 @@ class DomainRequestAdmin(ListHeaderAdmin): readonly_fields.extend([field.name for field in self.model._meta.fields]) # Add the multi-select fields to readonly_fields: # Complex fields like ManyToManyField require special handling - readonly_fields.extend(["current_websites", "other_contacts", "alternative_domains"]) + readonly_fields.extend(["alternative_domains"]) if request.user.has_perm("registrar.full_access_permission"): return readonly_fields @@ -1421,7 +1418,6 @@ class DomainAdmin(ListHeaderAdmin): ) def queryset(self, request, queryset): - logger.debug(self.value()) if self.value() == "1": return queryset.filter(domain_info__is_election_board=True) if self.value() == "0": diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index 3d72332fd..f3208e346 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -175,6 +175,12 @@ function checkToListThenInitWidget(toListId, attempts) { document.addEventListener('DOMContentLoaded', function() { // Select all table rows that have a data-contact-id attribute let contactRows = document.querySelectorAll("tr[data-contact-url]"); + console.log(`contact rows at this point: ${contactRows}`) + console.log(`length of ont ${contactRows.length}`) + if (!contactRows || contactRows.length == 0) { + console.log("in if statement") + contactRows = document.querySelectorAll(".other-contact-id") + } if (contactRows){ // Add a click event listener to each row 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 4ca577688..577bd9f41 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -58,6 +58,7 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% for contact in all_contacts %}
+ {% include "django/admin/includes/contact_detail_table.html" with user=contact field_name="other_contact" no_title_top_padding=field.is_readonly %}
{% endfor %} diff --git a/src/registrar/tests/common.py b/src/registrar/tests/common.py index 8aa696aaf..50fdde35d 100644 --- a/src/registrar/tests/common.py +++ b/src/registrar/tests/common.py @@ -204,7 +204,6 @@ class GenericTestHelper(TestCase): {"action": "delete_selected", "select_across": selected_across, "index": index, "_selected_action": "23"}, follow=True, ) - print(f"what is the response? {response}") return response diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index a6b2d9777..bc2eb7ca1 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1335,9 +1335,9 @@ class TestDomainRequestAdmin(MockEppLib): ] self.test_helper.assert_response_contains_distinct_values(response, expected_ao_fields) - # count=4 because the underlying domain has two users with this name. - # The dropdown has 3 of these. - self.assertContains(response, "Testy Tester", count=4) + # count=5 because the underlying domain has two users with this name. + # The dropdown has 4 of these. + self.assertContains(response, "Testy Tester", count=5) # Check for table titles. We only need to check for the end tag # (Otherwise this test will fail if we change classes, etc) @@ -1446,6 +1446,7 @@ class TestDomainRequestAdmin(MockEppLib): self.assertContains(response, "Yes, select ineligible status") def test_readonly_when_restricted_creator(self): + self.maxDiff = None with less_console_noise(): domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW) with boto3_mocking.clients.handler_for("sesv2", self.mock_client): @@ -1458,6 +1459,8 @@ class TestDomainRequestAdmin(MockEppLib): readonly_fields = self.admin.get_readonly_fields(request, domain_request) expected_fields = [ + "other_contacts", + "current_websites", "id", "created_at", "updated_at", @@ -1490,8 +1493,6 @@ class TestDomainRequestAdmin(MockEppLib): "is_policy_acknowledged", "submission_date", "notes", - "current_websites", - "other_contacts", "alternative_domains", ] @@ -1505,13 +1506,13 @@ class TestDomainRequestAdmin(MockEppLib): readonly_fields = self.admin.get_readonly_fields(request) expected_fields = [ + "other_contacts", + "current_websites", "creator", "about_your_organization", "requested_domain", "approved_domain", "alternative_domains", - "other_contacts", - "current_websites", "purpose", "submitter", "no_other_contacts_rationale", @@ -1528,7 +1529,7 @@ class TestDomainRequestAdmin(MockEppLib): readonly_fields = self.admin.get_readonly_fields(request) - expected_fields = [] + expected_fields = ['other_contacts', 'current_websites'] self.assertEqual(readonly_fields, expected_fields) @@ -1972,6 +1973,7 @@ class TestDomainInformationAdmin(TestCase): p = "userpass" self.client.login(username="staffuser", password=p) + response = self.client.get( "/admin/registrar/domaininformation/{}/change/".format(domain_info.pk), follow=True, @@ -2063,21 +2065,9 @@ class TestDomainInformationAdmin(TestCase): ] self.test_helper.assert_response_contains_distinct_values(response, expected_ao_fields) - # count=4 because the underlying domain has two users with this name. - # The dropdown has 3 of these. - self.assertContains(response, "Testy Tester", count=4) - - # Check for table titles. We only need to check for the end tag - # (Otherwise this test will fail if we change classes, etc) - - # Title. Count=3 because this table appears on three records. - self.assertContains(response, "Title", count=3) - - # Email. Count=3 because this table appears on three records. - self.assertContains(response, "Email", count=3) - - # Phone. Count=3 because this table appears on three records. - self.assertContains(response, "Phone", count=3) + # count=5 because the underlying domain has two users with this name. + # The dropdown has 4 of these. + self.assertContains(response, "Testy Tester", count=5) # == Test the other_employees field == # expected_other_employees_fields = [ @@ -2088,9 +2078,6 @@ class TestDomainInformationAdmin(TestCase): ] self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) - # count=1 as only one should exist in a table - self.assertContains(response, "Testy Tester", count=1) - def test_readonly_fields_for_analyst(self): """Ensures that analysts have their permissions setup correctly""" with less_console_noise(): @@ -2100,6 +2087,7 @@ class TestDomainInformationAdmin(TestCase): readonly_fields = self.admin.get_readonly_fields(request) expected_fields = [ + "other_contacts", "creator", "type_of_work", "more_organization_information", @@ -2109,7 +2097,6 @@ class TestDomainInformationAdmin(TestCase): "no_other_contacts_rationale", "anything_else", "is_policy_acknowledged", - "other_contacts", ] self.assertEqual(readonly_fields, expected_fields) From bd9c6213e864aed93a65c82adf20e81188390c0b Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Tue, 26 Mar 2024 14:50:00 -0600 Subject: [PATCH 50/61] Linting --- src/registrar/tests/test_admin.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index bc2eb7ca1..fbdf8de7e 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1339,18 +1339,6 @@ class TestDomainRequestAdmin(MockEppLib): # The dropdown has 4 of these. self.assertContains(response, "Testy Tester", count=5) - # Check for table titles. We only need to check for the end tag - # (Otherwise this test will fail if we change classes, etc) - - # Title. Count=3 because this table appears on three records. - self.assertContains(response, "Title", count=3) - - # Email. Count=3 because this table appears on three records. - self.assertContains(response, "Email", count=3) - - # Phone. Count=3 because this table appears on three records. - self.assertContains(response, "Phone", count=3) - # == Test the other_employees field == # expected_other_employees_fields = [ # Field, expected value @@ -1360,9 +1348,6 @@ class TestDomainRequestAdmin(MockEppLib): ] self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) - # count=1 as only one should exist in a table - self.assertContains(response, "Testy Tester", count=1) - def test_save_model_sets_restricted_status_on_user(self): with less_console_noise(): # make sure there is no user with this email @@ -1529,7 +1514,7 @@ class TestDomainRequestAdmin(MockEppLib): readonly_fields = self.admin.get_readonly_fields(request) - expected_fields = ['other_contacts', 'current_websites'] + expected_fields = ["other_contacts", "current_websites"] self.assertEqual(readonly_fields, expected_fields) From 22412c1958ccba7ec0bf0718298c461d7e4985ec Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 27 Mar 2024 08:17:52 -0600 Subject: [PATCH 51/61] Change background color --- src/registrar/assets/sass/_theme/_admin.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index 912a67f10..979d17ed3 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -351,7 +351,7 @@ details.dja-detail-table { display: inline-table; background-color: var(--body-bg); .dja-details-summary { - color: var(--header-link-color); + color: var(--body-quiet-color); } tr { From d9b4d58e552353d64044e545e1573b8a1d44f9f2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 27 Mar 2024 13:52:44 -0600 Subject: [PATCH 52/61] Refactor --- src/registrar/assets/sass/_theme/_admin.scss | 40 +++++++++ .../admin/includes/contact_detail_list.html | 47 ++++++++++ .../admin/includes/contact_detail_table.html | 67 --------------- .../admin/includes/detail_table_fieldset.html | 86 +++++++++++-------- 4 files changed, 137 insertions(+), 103 deletions(-) create mode 100644 src/registrar/templates/django/admin/includes/contact_detail_list.html delete mode 100644 src/registrar/templates/django/admin/includes/contact_detail_table.html diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index 979d17ed3..e98c27174 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -389,6 +389,46 @@ table.dja-user-detail-table { } +ul.padding-left-0__detail-list { + padding-left: 0 !important; +} + +ul.margin-left-0__detail-list { + margin-left: 0 !important; +} + +ul.margin-top-neg-1__detail-list { + margin-top: -8px !important; +} + +.dja-detail-list { + dl { + padding-left: 0px !important; + } + // Mimic the normal label size + dt { + font-size: 0.8125rem; + color: var(--body-quiet-color); + } + + address { + font-size: 0.8125rem; + color: var(--body-quiet-color); + } +} + +// Mimic the normal label size +@media (max-width: 1024px){ + .dja-detail-list dt { + font-size: 0.875rem; + color: var(--body-quiet-color); + } + .dja-detail-list address { + font-size: 0.875rem; + color: var(--body-quiet-color); + } +} + .errors span.select2-selection { border: 1px solid var(--error-fg) !important; } \ No newline at end of file diff --git a/src/registrar/templates/django/admin/includes/contact_detail_list.html b/src/registrar/templates/django/admin/includes/contact_detail_list.html new file mode 100644 index 000000000..f01c5680f --- /dev/null +++ b/src/registrar/templates/django/admin/includes/contact_detail_list.html @@ -0,0 +1,47 @@ +{% load i18n static %} + + +
    + {% if user.title or user.contact.title or user.email or user.contact.email or user.phone or user.contact.phone %} + {# Title #} +
  • + {% if user.title or user.contact.title %} + {% if user.contact.title %} + {{ user.contact.title }} + {% else %} + {{ user.title }} + {% endif %} + {% else %} + None + {% endif %} +
  • + {# Email #} +
  • + {% if user.email or user.contact.email %} + {% if user.contact.email %} + {{ user.contact.email }} + {% else %} + {{ user.email }} + {% endif %} + {% else %} + None + {% endif %} +
  • + {# Phone #} +
  • + {% if user.phone or user.contact.phone %} + {% if user.contact.phone %} + {{ user.contact.phone.as_national }} + {% else %} + {{ user.phone.as_national }} + {% endif %} + {% else %} + None + {% endif %} +
  • + {% else %} +
  • + No additional contact information found. +
  • + {% endif %} +
diff --git a/src/registrar/templates/django/admin/includes/contact_detail_table.html b/src/registrar/templates/django/admin/includes/contact_detail_table.html deleted file mode 100644 index decce0f43..000000000 --- a/src/registrar/templates/django/admin/includes/contact_detail_table.html +++ /dev/null @@ -1,67 +0,0 @@ -{% load i18n static %} - - - {% if show_table_details %} - - - - - - {% endif %} - - {% if user.title or user.contact.title or user.email or user.contact.email or user.phone or user.contact.phone %} - - - {% if show_table_details %} - - {% endif %} - - {% if user.title or user.contact.title %} - {% if user.contact.title %} - - {% else %} - - {% endif %} - {% else %} - - {% endif %} - - - {% if show_table_details %} - - {% endif %} - - {% if user.email or user.contact.email %} - {% if user.contact.email %} - - {% else %} - - {% endif %} - {% else %} - - {% endif %} - - - {% if show_table_details %} - - {% endif %} - - {% if user.phone or user.contact.phone %} - {% if user.contact.phone %} - - {% else %} - - {% endif %} - {% else %} - - {% endif %} - - - {% else %} - - - - - - {% endif %} -
Contact details
Title{{ user.contact.title }}{{ user.title }}None
Email{{ user.contact.email }}{{ user.email }}None
Phone{{ user.contact.phone }}{{ user.phone }}None
No additional contact information found.
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 577bd9f41..01b9e6a8c 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -5,64 +5,78 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endcomment %} {% block field_readonly %} + {% with all_contacts=original.other_contacts.all %} {% if field.field.name == "other_contacts" %} -
- {% for contact in field.contents|split:", " %} - {{ contact }}{% if not forloop.last %}, {% endif %} - {% endfor %} -
- {% elif field.field.name == "current_websites" %} - {% comment %} - The "website" model is essentially just a text field. - It is not useful to be redirected to the object definition, - rather it is more useful in this scenario to be redirected to the - actual website (as its just a plaintext string otherwise). - - This ONLY applies to analysts. For superusers, its business as usual. - {% endcomment %} -
- {% with total_websites=field.contents|split:", " %} - {% for website in total_websites %} - {{ website }}{% if not forloop.last %}, {% endif %} - {# Acts as a
#} - {% if total_websites|length < 5 %} -
+ {% if all_contacts.count > 2 %} +
+ {% for contact in field.contents|split:", " %} + {{ contact }}{% if not forloop.last %}, {% endif %} + {% endfor %} +
+ {% else %} +
+
+ {% for contact in all_contacts %} +
+ Organization contact {{forloop.counter}} +
+
+
+ {% if contact.get_formatted_name %}{{ contact.get_formatted_name }}
{% else %}None
{% endif %} + {% if contact.title %}{{ contact.title }}
{% else %}None
{% endif %} + {% if contact.email %}{{ contact.email }}
{% else %}None
{% endif %} + {% if contact.phone %}{{ contact.phone.as_national }}{% else %}None{% endif %} +
+
+ {% endfor %} +
+
{% endif %} - {% endfor %} - {% endwith %} -
+ {% elif field.field.name == "current_websites" %} + {% comment %} + The "website" model is essentially just a text field. + It is not useful to be redirected to the object definition, + rather it is more useful in this scenario to be redirected to the + actual website (as its just a plaintext string otherwise). + + This ONLY applies to analysts. For superusers, its business as usual. + {% endcomment %} +
+ {% with total_websites=field.contents|split:", " %} + {% for website in total_websites %} + {{ website }}{% if not forloop.last %}, {% endif %} + {# Acts as a
#} + {% if total_websites|length < 5 %} +
+ {% endif %} + {% endfor %} + {% endwith %} +
{% else %}
{{ field.contents }}
{% endif %} + {% endwith %} {% endblock field_readonly %} {% block after_help_text %} {% if field.field.name == "creator" %}
- {% include "django/admin/includes/contact_detail_table.html" with user=original.creator field_name="creator" no_title_top_padding=field.is_readonly %} + {% include "django/admin/includes/contact_detail_list.html" with user=original.creator field_name="creator" no_title_top_padding=field.is_readonly %}
{% elif field.field.name == "submitter" %}
- {% include "django/admin/includes/contact_detail_table.html" with user=original.submitter field_name="submitter" no_title_top_padding=field.is_readonly %} + {% include "django/admin/includes/contact_detail_list.html" with user=original.submitter field_name="submitter" no_title_top_padding=field.is_readonly %}
{% elif field.field.name == "authorizing_official" %}
- {% include "django/admin/includes/contact_detail_table.html" with user=original.authorizing_official field_name="authorizing_official" no_title_top_padding=field.is_readonly %} + {% include "django/admin/includes/contact_detail_list.html" with user=original.authorizing_official field_name="authorizing_official" no_title_top_padding=field.is_readonly %}
{% elif field.field.name == "other_contacts" and original.other_contacts.all %} {% with all_contacts=original.other_contacts.all %} - {% if all_contacts.count == 1 %} - {% for contact in all_contacts %} -
- - - {% include "django/admin/includes/contact_detail_table.html" with user=contact field_name="other_contact" no_title_top_padding=field.is_readonly %} -
- {% endfor %} - {% else %} + {% if all_contacts.count > 2 %}
Details
From f53a4414eff163af76074241896b5c147c9df78d Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 27 Mar 2024 14:57:41 -0600 Subject: [PATCH 53/61] Fix tests, cleanup --- src/registrar/assets/js/get-gov-admin.js | 3 - src/registrar/assets/sass/_theme/_admin.scss | 37 +++++------- src/registrar/templates/admin/fieldset.html | 19 ++---- .../admin/includes/contact_detail_list.html | 49 +++++++-------- .../admin/includes/detail_table_fieldset.html | 8 +-- src/registrar/tests/test_admin.py | 60 +++++++++---------- 6 files changed, 74 insertions(+), 102 deletions(-) diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index f3208e346..f7ea0fa1d 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -175,10 +175,7 @@ function checkToListThenInitWidget(toListId, attempts) { document.addEventListener('DOMContentLoaded', function() { // Select all table rows that have a data-contact-id attribute let contactRows = document.querySelectorAll("tr[data-contact-url]"); - console.log(`contact rows at this point: ${contactRows}`) - console.log(`length of ont ${contactRows.length}`) if (!contactRows || contactRows.length == 0) { - console.log("in if statement") contactRows = document.querySelectorAll(".other-contact-id") } diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index e98c27174..fe3fcb89c 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -354,6 +354,13 @@ details.dja-detail-table { color: var(--body-quiet-color); } + @media (max-width: 1024px){ + .dja-detail-contents { + max-width: 400px !important; + overflow-x: scroll !important; + } + } + tr { background-color: transparent; } @@ -371,34 +378,18 @@ details.dja-detail-table { } -table.dja-user-detail-table { - tr { - background-color: var(--body-bg); +ul { + .padding-left-0__detail-list { + padding-left: 0 !important; } - // Make the head of the user detail table "float" - thead > tr > th { - border-radius: 4px; - border-top: none; - border-bottom: none; + .margin-left-0__detail-list { + margin-left: 0 !important; } - th, td { - border: none + .margin-top-neg-1__detail-list { + margin-top: -8px !important; } - -} - -ul.padding-left-0__detail-list { - padding-left: 0 !important; -} - -ul.margin-left-0__detail-list { - margin-left: 0 !important; -} - -ul.margin-top-neg-1__detail-list { - margin-top: -8px !important; } .dja-detail-list { diff --git a/src/registrar/templates/admin/fieldset.html b/src/registrar/templates/admin/fieldset.html index 579ad7033..8b8972e08 100644 --- a/src/registrar/templates/admin/fieldset.html +++ b/src/registrar/templates/admin/fieldset.html @@ -6,17 +6,11 @@ It is not inherently customizable on its own, so we can modify this instead. https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/includes/fieldset.html {% endcomment %}
- {# .gov override #} - {% block fieldset_title %} - {% if fieldset.name %}

{{ fieldset.name }}

{% endif %} - {% endblock fieldset_title %} + {% if fieldset.name %}

{{ fieldset.name }}

{% endif %} - {# .gov override #} - {% block fieldset_description %} - {% if fieldset.description %} -
{{ fieldset.description|safe }}
- {% endif %} - {% endblock fieldset_description %} + {% if fieldset.description %} +
{{ fieldset.description|safe }}
+ {% endif %} {% for line in fieldset %}
@@ -45,11 +39,6 @@ https://github.com/django/django/blob/main/django/contrib/admin/templates/admin/ {% endif %} {% endif %}
- - {# .gov addition #} - {% block before_help_text %} - {# For templating purposes #} - {% endblock before_help_text %} {% if field.field.help_text %} {# .gov override #} diff --git a/src/registrar/templates/django/admin/includes/contact_detail_list.html b/src/registrar/templates/django/admin/includes/contact_detail_list.html index f01c5680f..b0b21ea8e 100644 --- a/src/registrar/templates/django/admin/includes/contact_detail_list.html +++ b/src/registrar/templates/django/admin/includes/contact_detail_list.html @@ -4,41 +4,36 @@
    {% if user.title or user.contact.title or user.email or user.contact.email or user.phone or user.contact.phone %} {# Title #} -
  • - {% if user.title or user.contact.title %} - {% if user.contact.title %} - {{ user.contact.title }} - {% else %} - {{ user.title }} - {% endif %} + {% if user.title or user.contact.title %} + {% if user.contact.title %} +
  • {{ user.contact.title }}
  • {% else %} - None +
  • {{ user.title }}
  • {% endif %} - + {% else %} +
  • None
  • + {% endif %} {# Email #} -
  • - {% if user.email or user.contact.email %} - {% if user.contact.email %} - {{ user.contact.email }} - {% else %} - {{ user.email }} - {% endif %} + {% if user.email or user.contact.email %} + {% if user.contact.email %} +
  • {{ user.contact.email }}
  • {% else %} - None +
  • {{ user.email }}
  • {% endif %} - + {% else %} +
  • None
  • + {% endif %} + {# Phone #} -
  • - {% if user.phone or user.contact.phone %} - {% if user.contact.phone %} - {{ user.contact.phone.as_national }} - {% else %} - {{ user.phone.as_national }} - {% endif %} + {% if user.phone or user.contact.phone %} + {% if user.contact.phone %}
  • +
  • {{ user.contact.phone }}
  • {% else %} - None +
  • {{ user.phone }}
  • {% endif %} - + {% else %} +
  • None
  • + {% endif %} {% else %}
  • No additional contact information found. 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 01b9e6a8c..cf8e0f752 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -22,10 +22,10 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
    - {% if contact.get_formatted_name %}{{ contact.get_formatted_name }}
    {% else %}None
    {% endif %} - {% if contact.title %}{{ contact.title }}
    {% else %}None
    {% endif %} - {% if contact.email %}{{ contact.email }}
    {% else %}None
    {% endif %} - {% if contact.phone %}{{ contact.phone.as_national }}{% else %}None{% endif %} + {% if contact.get_formatted_name %}{{ contact.get_formatted_name }}
    {% else %}None
    {% endif %} + {% if contact.title %}{{ contact.title }}
    {% else %}None
    {% endif %} + {% if contact.email %}{{ contact.email }}
    {% else %}None
    {% endif %} + {% if contact.phone %}{{ contact.phone }}{% else %}None{% endif %}
    {% endfor %} diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index fbdf8de7e..df3d760a8 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1307,9 +1307,9 @@ class TestDomainRequestAdmin(MockEppLib): # (Otherwise this test will fail if we change classes, etc) expected_creator_fields = [ # Field, expected value - ("title", "Treat inspector"), - ("email", "meoward.jones@igorville.gov"), - ("phone", "(555) 123 12345"), + ("title", "Treat inspector
  • "), + ("email", "meoward.jones@igorville.gov"), + ("phone", "(555) 123 12345"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_creator_fields) @@ -1319,9 +1319,9 @@ class TestDomainRequestAdmin(MockEppLib): # == Check for the submitter == # expected_submitter_fields = [ # Field, expected value - ("title", "Admin Tester"), - ("email", "mayor@igorville.gov"), - ("phone", "(555) 555 5556"), + ("title", "Admin Tester"), + ("email", "mayor@igorville.gov"), + ("phone", "(555) 555 5556"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_submitter_fields) self.assertContains(response, "Testy2 Tester2") @@ -1329,22 +1329,22 @@ class TestDomainRequestAdmin(MockEppLib): # == Check for the authorizing_official == # expected_ao_fields = [ # Field, expected value - ("title", "Chief Tester"), - ("email", "testy@town.com"), - ("phone", "(555) 555 5555"), + ("title", "Chief Tester"), + ("email", "testy@town.com"), + ("phone", "(555) 555 5555"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_ao_fields) - # count=5 because the underlying domain has two users with this name. - # The dropdown has 4 of these. - self.assertContains(response, "Testy Tester", count=5) + # count=3 because the underlying domain has two users with this name. + # The dropdown has 2 of these. + self.assertContains(response, "Testy Tester", count=3) # == Test the other_employees field == # expected_other_employees_fields = [ # Field, expected value - ("title", "Another Tester"), - ("email", "testy2@town.com"), - ("phone", "(555) 555 5557"), + ("title", "Another Tester"), + ("email", "testy2@town.com"), + ("phone", "(555) 555 5557"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) @@ -2022,9 +2022,9 @@ class TestDomainInformationAdmin(TestCase): # (Otherwise this test will fail if we change classes, etc) expected_creator_fields = [ # Field, expected value - ("title", "Treat inspector"), - ("email", "meoward.jones@igorville.gov"), - ("phone", "(555) 123 12345"), + ("title", "Treat inspector"), + ("email", "meoward.jones@igorville.gov"), + ("phone", "(555) 123 12345"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_creator_fields) @@ -2034,9 +2034,9 @@ class TestDomainInformationAdmin(TestCase): # == Check for the submitter == # expected_submitter_fields = [ # Field, expected value - ("title", "Admin Tester"), - ("email", "mayor@igorville.gov"), - ("phone", "(555) 555 5556"), + ("title", "Admin Tester"), + ("email", "mayor@igorville.gov"), + ("phone", "(555) 555 5556"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_submitter_fields) self.assertContains(response, "Testy2 Tester2") @@ -2044,22 +2044,22 @@ class TestDomainInformationAdmin(TestCase): # == Check for the authorizing_official == # expected_ao_fields = [ # Field, expected value - ("title", "Chief Tester"), - ("email", "testy@town.com"), - ("phone", "(555) 555 5555"), + ("title", "Chief Tester"), + ("email", "testy@town.com"), + ("phone", "(555) 555 5555"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_ao_fields) - # count=5 because the underlying domain has two users with this name. - # The dropdown has 4 of these. - self.assertContains(response, "Testy Tester", count=5) + # count=3 because the underlying domain has two users with this name. + # The dropdown has 2 of these. + self.assertContains(response, "Testy Tester", count=3) # == Test the other_employees field == # expected_other_employees_fields = [ # Field, expected value - ("title", "Another Tester"), - ("email", "testy2@town.com"), - ("phone", "(555) 555 5557"), + ("title", "Another Tester"), + ("email", "testy2@town.com"), + ("phone", "(555) 555 5557"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields) From 25704e2814e5f98962f39362823a706822bb60ec Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:04:40 -0600 Subject: [PATCH 54/61] Update _admin.scss --- src/registrar/assets/sass/_theme/_admin.scss | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index fe3fcb89c..6310fa08e 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -378,18 +378,16 @@ details.dja-detail-table { } -ul { - .padding-left-0__detail-list { - padding-left: 0 !important; - } +ul.padding-left-0__detail-list { + padding-left: 0 !important; +} - .margin-left-0__detail-list { - margin-left: 0 !important; - } +ul.margin-left-0__detail-list { + margin-left: 0 !important; +} - .margin-top-neg-1__detail-list { - margin-top: -8px !important; - } +ul.margin-top-neg-1__detail-list { + margin-top: -8px !important; } .dja-detail-list { From 3e740f46684d672ca417da873091857dad5490a2 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Wed, 27 Mar 2024 15:46:21 -0600 Subject: [PATCH 55/61] Update detail_table_fieldset.html --- .../templates/django/admin/includes/detail_table_fieldset.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 cf8e0f752..28883207f 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -77,7 +77,7 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% elif field.field.name == "other_contacts" and original.other_contacts.all %} {% with all_contacts=original.other_contacts.all %} {% if all_contacts.count > 2 %} -
    +
    Details
    From ea59232cefc90d03bb7f473efa0a7f4c261311ab Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:15:48 -0600 Subject: [PATCH 56/61] PR suggestions (slight refactor) --- src/registrar/admin.py | 2 +- src/registrar/assets/js/get-gov-admin.js | 29 ----------- src/registrar/assets/sass/_theme/_admin.scss | 16 +++--- .../admin/includes/contact_detail_list.html | 39 +++++++++------ .../admin/includes/detail_table_fieldset.html | 35 ++++++------- src/registrar/tests/test_admin.py | 50 +++++++++---------- 6 files changed, 73 insertions(+), 98 deletions(-) diff --git a/src/registrar/admin.py b/src/registrar/admin.py index b3a603d84..c5dbb152b 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -1123,7 +1123,7 @@ class DomainRequestAdmin(ListHeaderAdmin): ] # Readonly fields for analysts and superusers - readonly_fields = ("other_contacts", "current_websites") + readonly_fields = ("other_contacts", "current_websites", "alternative_domains") # Read only that we'll leverage for CISA Analysts analyst_readonly_fields = [ diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index f7ea0fa1d..8c60c534f 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -170,35 +170,6 @@ function checkToListThenInitWidget(toListId, attempts) { } } -(function() { - function addUrlsToOtherContacts() { - document.addEventListener('DOMContentLoaded', function() { - // Select all table rows that have a data-contact-id attribute - let contactRows = document.querySelectorAll("tr[data-contact-url]"); - if (!contactRows || contactRows.length == 0) { - contactRows = document.querySelectorAll(".other-contact-id") - } - - if (contactRows){ - // Add a click event listener to each row - let index = 1; - contactRows.forEach(function(row) { - let otherContactUrl = row.getAttribute("data-contact-url"); - if (otherContactUrl){ - let otherContact = document.querySelector(`.other-contact__${index}`); - if (otherContact) { - otherContact.href = otherContactUrl; - } - } - index++; - }); - } - }); - } - - addUrlsToOtherContacts() -})(); - // Initialize the widget: // add related buttons to the widget for edit, delete and view // add event listeners on the from list, the to list, and selector buttons which either enable or disable the related buttons diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss index 6310fa08e..4bddfd1df 100644 --- a/src/registrar/assets/sass/_theme/_admin.scss +++ b/src/registrar/assets/sass/_theme/_admin.scss @@ -351,6 +351,7 @@ details.dja-detail-table { display: inline-table; background-color: var(--body-bg); .dja-details-summary { + cursor: pointer; color: var(--body-quiet-color); } @@ -378,21 +379,15 @@ details.dja-detail-table { } -ul.padding-left-0__detail-list { - padding-left: 0 !important; -} -ul.margin-left-0__detail-list { - margin-left: 0 !important; -} - -ul.margin-top-neg-1__detail-list { +address.margin-top-neg-1__detail-list { margin-top: -8px !important; } .dja-detail-list { dl { padding-left: 0px !important; + margin-top: 5px !important; } // Mimic the normal label size dt { @@ -406,6 +401,11 @@ ul.margin-top-neg-1__detail-list { } } +address.dja-address-contact-list { + font-size: 0.8125rem; + color: var(--body-quiet-color); +} + // Mimic the normal label size @media (max-width: 1024px){ .dja-detail-list dt { diff --git a/src/registrar/templates/django/admin/includes/contact_detail_list.html b/src/registrar/templates/django/admin/includes/contact_detail_list.html index b0b21ea8e..6afe4c8d6 100644 --- a/src/registrar/templates/django/admin/includes/contact_detail_list.html +++ b/src/registrar/templates/django/admin/includes/contact_detail_list.html @@ -1,42 +1,51 @@ {% load i18n static %} +
    + + {% if show_formatted_name %} + {% if contact.get_formatted_name %} + {{ user.get_formatted_name }}
    + {% else %} + None
    + {% endif %} + {% endif %} -
      {% if user.title or user.contact.title or user.email or user.contact.email or user.phone or user.contact.phone %} {# Title #} {% if user.title or user.contact.title %} {% if user.contact.title %} -
    • {{ user.contact.title }}
    • + {{ user.contact.title }} {% else %} -
    • {{ user.title }}
    • + {{ user.title }} {% endif %} +
      {% else %} -
    • None
    • + None
      {% endif %} {# Email #} {% if user.email or user.contact.email %} {% if user.contact.email %} -
    • {{ user.contact.email }}
    • + {{ user.contact.email }} {% else %} -
    • {{ user.email }}
    • + {{ user.email }} {% endif %} +
      {% else %} -
    • None
    • + None
      {% endif %} {# Phone #} {% if user.phone or user.contact.phone %} - {% if user.contact.phone %} -
    • {{ user.contact.phone }}
    • + {% if user.contact.phone %} + {{ user.contact.phone }} {% else %} -
    • {{ user.phone }}
    • + {{ user.phone }} {% endif %} +
      {% else %} -
    • None
    • + None
      {% endif %} {% else %} -
    • - No additional contact information found. -
    • + No additional contact information found. {% endif %} -
    +
    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 28883207f..47145faf2 100644 --- a/src/registrar/templates/django/admin/includes/detail_table_fieldset.html +++ b/src/registrar/templates/django/admin/includes/detail_table_fieldset.html @@ -9,24 +9,19 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% if field.field.name == "other_contacts" %} {% if all_contacts.count > 2 %}
    - {% for contact in field.contents|split:", " %} - {{ contact }}{% if not forloop.last %}, {% endif %} + {% for contact in all_contacts %} + {{ contact.get_formatted_name }}{% if not forloop.last %}, {% endif %} {% endfor %}
    {% else %}
    -
    +
    {% for contact in all_contacts %}
    Organization contact {{forloop.counter}}
    -
    - {% if contact.get_formatted_name %}{{ contact.get_formatted_name }}
    {% else %}None
    {% endif %} - {% if contact.title %}{{ contact.title }}
    {% else %}None
    {% endif %} - {% if contact.email %}{{ contact.email }}
    {% else %}None
    {% endif %} - {% if contact.phone %}{{ contact.phone }}{% else %}None{% endif %} -
    + {% include "django/admin/includes/contact_detail_list.html" with user=contact show_formatted_name=True %}
    {% endfor %}
    @@ -52,6 +47,12 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% endfor %} {% endwith %}
    + {% elif field.field.name == "alternative_domains" %} +
    + {% for alt_domain in original.alternative_domains.all %} + {{ alt_domain }}{% if not forloop.last %}, {% endif %} + {% endfor %} +
    {% else %}
    {{ field.contents }}
    {% endif %} @@ -62,17 +63,17 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html) {% if field.field.name == "creator" %}
    - {% include "django/admin/includes/contact_detail_list.html" with user=original.creator field_name="creator" no_title_top_padding=field.is_readonly %} + {% include "django/admin/includes/contact_detail_list.html" with user=original.creator no_title_top_padding=field.is_readonly %}
    {% elif field.field.name == "submitter" %}
    - {% include "django/admin/includes/contact_detail_list.html" with user=original.submitter field_name="submitter" no_title_top_padding=field.is_readonly %} + {% include "django/admin/includes/contact_detail_list.html" with user=original.submitter no_title_top_padding=field.is_readonly %}
    {% elif field.field.name == "authorizing_official" %}
    - {% include "django/admin/includes/contact_detail_list.html" with user=original.authorizing_official field_name="authorizing_official" no_title_top_padding=field.is_readonly %} + {% include "django/admin/includes/contact_detail_list.html" with user=original.authorizing_official no_title_top_padding=field.is_readonly %}
    {% elif field.field.name == "other_contacts" and original.other_contacts.all %} {% with all_contacts=original.other_contacts.all %} @@ -83,17 +84,13 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
    - + {% for contact in all_contacts %} - {% comment %} - Since we can't get the id from field, we can embed this information here. - Then we can link these two fields using javascript. - {% endcomment %} - - + + diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index df3d760a8..3d88bd8bd 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1303,13 +1303,11 @@ class TestDomainRequestAdmin(MockEppLib): # == Check for the creator == # # Check for the right title, email, and phone number in the response. - # We only need to check for the end tag - # (Otherwise this test will fail if we change classes, etc) expected_creator_fields = [ # Field, expected value - ("title", "Treat inspector"), - ("email", "meoward.jones@igorville.gov"), - ("phone", "(555) 123 12345"), + ("title", "Treat inspector"), + ("email", "meoward.jones@igorville.gov"), + ("phone", "(555) 123 12345"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_creator_fields) @@ -1319,9 +1317,9 @@ class TestDomainRequestAdmin(MockEppLib): # == Check for the submitter == # expected_submitter_fields = [ # Field, expected value - ("title", "Admin Tester"), - ("email", "mayor@igorville.gov"), - ("phone", "(555) 555 5556"), + ("title", "Admin Tester"), + ("email", "mayor@igorville.gov"), + ("phone", "(555) 555 5556"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_submitter_fields) self.assertContains(response, "Testy2 Tester2") @@ -1329,15 +1327,15 @@ class TestDomainRequestAdmin(MockEppLib): # == Check for the authorizing_official == # expected_ao_fields = [ # Field, expected value - ("title", "Chief Tester"), - ("email", "testy@town.com"), - ("phone", "(555) 555 5555"), + ("title", "Chief Tester"), + ("email", "testy@town.com"), + ("phone", "(555) 555 5555"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_ao_fields) - # count=3 because the underlying domain has two users with this name. - # The dropdown has 2 of these. - self.assertContains(response, "Testy Tester", count=3) + # count=5 because the underlying domain has two users with this name. + # The dropdown has 3 of these. + self.assertContains(response, "Testy Tester", count=5) # == Test the other_employees field == # expected_other_employees_fields = [ @@ -2022,9 +2020,9 @@ class TestDomainInformationAdmin(TestCase): # (Otherwise this test will fail if we change classes, etc) expected_creator_fields = [ # Field, expected value - ("title", "Treat inspector"), - ("email", "meoward.jones@igorville.gov"), - ("phone", "(555) 123 12345"), + ("title", "Treat inspector"), + ("email", "meoward.jones@igorville.gov"), + ("phone", "(555) 123 12345"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_creator_fields) @@ -2034,9 +2032,9 @@ class TestDomainInformationAdmin(TestCase): # == Check for the submitter == # expected_submitter_fields = [ # Field, expected value - ("title", "Admin Tester"), - ("email", "mayor@igorville.gov"), - ("phone", "(555) 555 5556"), + ("title", "Admin Tester"), + ("email", "mayor@igorville.gov"), + ("phone", "(555) 555 5556"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_submitter_fields) self.assertContains(response, "Testy2 Tester2") @@ -2044,15 +2042,15 @@ class TestDomainInformationAdmin(TestCase): # == Check for the authorizing_official == # expected_ao_fields = [ # Field, expected value - ("title", "Chief Tester"), - ("email", "testy@town.com"), - ("phone", "(555) 555 5555"), + ("title", "Chief Tester"), + ("email", "testy@town.com"), + ("phone", "(555) 555 5555"), ] self.test_helper.assert_response_contains_distinct_values(response, expected_ao_fields) - # count=3 because the underlying domain has two users with this name. - # The dropdown has 2 of these. - self.assertContains(response, "Testy Tester", count=3) + # count=5 because the underlying domain has two users with this name. + # The dropdown has 3 of these. + self.assertContains(response, "Testy Tester", count=5) # == Test the other_employees field == # expected_other_employees_fields = [ From cf6b8283b6fe7aebfa130c14269ee459c12b9be9 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:17:13 -0600 Subject: [PATCH 57/61] Tests --- src/registrar/tests/test_admin.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 3d88bd8bd..e8937e822 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1444,6 +1444,7 @@ class TestDomainRequestAdmin(MockEppLib): expected_fields = [ "other_contacts", "current_websites", + "alternative_domains", "id", "created_at", "updated_at", @@ -1491,6 +1492,7 @@ class TestDomainRequestAdmin(MockEppLib): expected_fields = [ "other_contacts", "current_websites", + "alternative_domains", "creator", "about_your_organization", "requested_domain", @@ -1512,7 +1514,7 @@ class TestDomainRequestAdmin(MockEppLib): readonly_fields = self.admin.get_readonly_fields(request) - expected_fields = ["other_contacts", "current_websites"] + expected_fields = ["other_contacts", "current_websites", "alternative_domains",] self.assertEqual(readonly_fields, expected_fields) From 3a519f75ab28c1c11543c8391b54f6589cea68f4 Mon Sep 17 00:00:00 2001 From: zandercymatics <141044360+zandercymatics@users.noreply.github.com> Date: Thu, 28 Mar 2024 09:25:15 -0600 Subject: [PATCH 58/61] Linting --- src/registrar/tests/test_admin.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index e8937e822..a53cfe9b8 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -1514,7 +1514,11 @@ class TestDomainRequestAdmin(MockEppLib): readonly_fields = self.admin.get_readonly_fields(request) - expected_fields = ["other_contacts", "current_websites", "alternative_domains",] + expected_fields = [ + "other_contacts", + "current_websites", + "alternative_domains", + ] self.assertEqual(readonly_fields, expected_fields) From ccadb88239c4234dd32f6a3b89c835cc57677659 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick <109625347+abroddrick@users.noreply.github.com> Date: Thu, 28 Mar 2024 14:02:58 -0700 Subject: [PATCH 59/61] Update docs/operations/runbooks/downtime_incident_management.md Co-authored-by: zandercymatics <141044360+zandercymatics@users.noreply.github.com> --- docs/operations/runbooks/downtime_incident_management.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/operations/runbooks/downtime_incident_management.md b/docs/operations/runbooks/downtime_incident_management.md index 5e7aabb87..ada9c9d5d 100644 --- a/docs/operations/runbooks/downtime_incident_management.md +++ b/docs/operations/runbooks/downtime_incident_management.md @@ -16,7 +16,7 @@ The following set of rules should be followed while an incident is in progress. - If downtime occurs outside of working hours, team members who are off for the day may still be pinged and called but are not required to join if unavailable to do so. - Uncomment the [banner on get.gov](https://github.com/cisagov/get.gov/blob/0365d3d34b041cc9353497b2b5f81b6ab7fe75a9/_includes/header.html#L9), so it is transparent to users that we know about the issue on manage.get.gov. - Designers or Developers should be able to make this change; if designers are online and can help with this task, that will allow developers to focus on fixing the bug. -- If the issue persists for three hours or more, follow the [instructions for enabling/disabling a redirect to get.gov](https://docs.google.com/document/d/1PiWXpjBzbiKsSYqEo9Rkl72HMytMp7zTte9CI-vvwYw/edit) +- If the issue persists for three hours or more, follow the [instructions for enabling/disabling a redirect to get.gov](https://docs.google.com/document/d/1PiWXpjBzbiKsSYqEo9Rkl72HMytMp7zTte9CI-vvwYw/edit). ## Post Incident From 7bb31fde9c873caf22f05131b45a1252820a71a0 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick <109625347+abroddrick@users.noreply.github.com> Date: Thu, 28 Mar 2024 14:03:29 -0700 Subject: [PATCH 60/61] Update docs/operations/runbooks/downtime_incident_management.md Co-authored-by: zandercymatics <141044360+zandercymatics@users.noreply.github.com> --- docs/operations/runbooks/downtime_incident_management.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/operations/runbooks/downtime_incident_management.md b/docs/operations/runbooks/downtime_incident_management.md index ada9c9d5d..4aa884e9d 100644 --- a/docs/operations/runbooks/downtime_incident_management.md +++ b/docs/operations/runbooks/downtime_incident_management.md @@ -22,8 +22,8 @@ The following set of rules should be followed while an incident is in progress. The following checklist should be followed after the site is back up and running. -- [ ] Message in #dotgov-announce with an @here saying the issue is resolved -- [ ] If the redirect was used, refer to the [instructions for enabling/disabling a redirect to get.gov](https://docs.google.com/document/d/1PiWXpjBzbiKsSYqEo9Rkl72HMytMp7zTte9CI-vvwYw/edit) to turn off this redirect. Double-check in the browser that this redirect is no longer occurring (the change may take a few minutes to take full effect) +- [ ] Message in #dotgov-announce with an @here saying the issue is resolved. +- [ ] If the redirect was used, refer to the [instructions for enabling/disabling a redirect to get.gov](https://docs.google.com/document/d/1PiWXpjBzbiKsSYqEo9Rkl72HMytMp7zTte9CI-vvwYw/edit) to turn off this redirect. Double-check in the browser that this redirect is no longer occurring (the change may take a few minutes to take full effect). - [ ] Remove the [banner on get.gov](https://github.com/cisagov/get.gov/blob/0365d3d34b041cc9353497b2b5f81b6ab7fe75a9/_includes/header.html#L9) by commenting it out. - [ ] Write up what happened and when; if the cause is already known, write that as well. This is a draft for internal communications and not for any public facing site and can be as simple as using bullet points. - [ ] If the cause is not known yet, developers should investigate the issue as the highest priority task. From e24760381f9d4bb9fae3753e29f438275fe7e1b1 Mon Sep 17 00:00:00 2001 From: Alysia Broddrick Date: Thu, 28 Mar 2024 16:00:46 -0700 Subject: [PATCH 61/61] added riley to fixtures --- src/registrar/fixtures_users.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py index bbe4d0b62..99fe4910e 100644 --- a/src/registrar/fixtures_users.py +++ b/src/registrar/fixtures_users.py @@ -93,6 +93,12 @@ class UserFixture: "last_name": "Chin", "email": "szu.chin@associates.cisa.dhs.gov", }, + { + "username": "012f844d-8a0f-4225-9d82-cbf87bff1d3e", + "first_name": "Riley", + "last_name": "Orr", + "email": "riley+320@truss.works", + }, ] STAFF = [ @@ -169,6 +175,12 @@ class UserFixture: "last_name": "Mcelya-Analyst", "email": "ALEXANDER.MCELYA@cisa.dhs.gov", }, + { + "username": "082a066f-e0a4-45f6-8672-4343a1208a36", + "first_name": "Riley-Analyst", + "last_name": "Orr-Analyst", + "email": "riley+321@truss.works", + }, ] def load_users(cls, users, group_name):
    Contact informationOther contact information
    {{contact.first_name}} {{contact.last_name}}
    {{ contact.get_formatted_name }} {{ contact.title }} {{ contact.email }} {{ contact.phone }}