Merge remote-tracking branch 'origin' into rh/1031-staff-enable-editing

This commit is contained in:
Rebecca Hsieh 2023-10-20 14:49:04 -07:00
commit f8ec478f78
No known key found for this signature in database
GPG key ID: 644527A2F375A379
15 changed files with 232 additions and 38 deletions

View file

@ -6,13 +6,13 @@ body:
id: title-help
attributes:
value: |
> Titles should be short, descriptive, and compelling.
> Titles should be short, descriptive, and compelling. Use sentence case.
- type: textarea
id: issue-description
attributes:
label: Issue description and context
label: Issue description
description: |
Describe the issue so that someone who wasn't present for its discovery can understand the problem and why it matters. Use full sentences, plain language, and good [formatting](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax). Share desired outcomes or potential next steps. Images or links to other content/context (like documents or Slack discussions) are welcome.
Describe the issue so that someone who wasn't present for its discovery can understand why it matters. Use full sentences, plain language, and good [formatting](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax).
validations:
required: true
- type: textarea
@ -20,16 +20,22 @@ body:
attributes:
label: Acceptance criteria
description: "If known, share 1-3 statements that would need to be true for this issue to be considered resolved. Use a [task list](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/about-task-lists#creating-task-lists) if appropriate."
placeholder: "- [ ] The button does the thing."
placeholder: "- [ ]"
- type: textarea
id: additional-context
attributes:
label: Additional context
description: "Share any other thoughts, like how this might be implemented or fixed. Screenshots and links to documents/discussions are welcome."
- type: textarea
id: links-to-other-issues
attributes:
label: Links to other issues
description: |
Add the issue #number of other issues this relates to and how (e.g., 🚧 Blocks, ⛔️ Is blocked by, 🔄 Relates to).
"Add issue #numbers this relates to and how (e.g., 🚧 :construction: Blocks, ⛔️ :no_entry: Is blocked by, 🔄 :repeat: Relates to)."
placeholder: 🔄 Relates to...
- type: markdown
id: note
attributes:
value: |
> We may edit this issue's text to document our understanding and clarify the product work.
> We may edit the text in this issue to document our understanding and clarify the product work.

View file

@ -219,9 +219,9 @@ class MyUserAdmin(BaseUserAdmin):
# (which should in theory be the ONLY group)
def group(self, obj):
if obj.groups.filter(name="full_access_group").exists():
return "Full access"
return "full_access_group"
elif obj.groups.filter(name="cisa_analysts_group").exists():
return "Analyst"
return "cisa_analysts_group"
return ""
def get_list_display(self, request):
@ -735,7 +735,7 @@ class DomainAdmin(ListHeaderAdmin):
]
def organization_type(self, obj):
return obj.domain_info.organization_type
return obj.domain_info.get_organization_type_display()
organization_type.admin_order_field = ( # type: ignore
"domain_info__organization_type"

View file

@ -153,7 +153,8 @@ class RegistrarFormSet(forms.BaseFormSet):
class OrganizationTypeForm(RegistrarForm):
organization_type = forms.ChoiceField(
choices=DomainApplication.OrganizationChoices.choices,
# use the long names in the application form
choices=DomainApplication.OrganizationChoicesVerbose.choices,
widget=forms.RadioSelect,
error_messages={"required": "Select the type of organization you represent."},
)

View file

@ -0,0 +1,52 @@
# Generated by Django 4.2.1 on 2023-10-20 21:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("registrar", "0040_alter_userdomainrole_role"),
]
operations = [
migrations.AlterField(
model_name="domainapplication",
name="organization_type",
field=models.CharField(
blank=True,
choices=[
("federal", "Federal"),
("interstate", "Interstate"),
("state_or_territory", "State or territory"),
("tribal", "Tribal"),
("county", "County"),
("city", "City"),
("special_district", "Special district"),
("school_district", "School district"),
],
help_text="Type of organization",
max_length=255,
null=True,
),
),
migrations.AlterField(
model_name="domaininformation",
name="organization_type",
field=models.CharField(
blank=True,
choices=[
("federal", "Federal"),
("interstate", "Interstate"),
("state_or_territory", "State or territory"),
("tribal", "Tribal"),
("county", "County"),
("city", "City"),
("special_district", "Special district"),
("school_district", "School district"),
],
help_text="Type of Organization",
max_length=255,
null=True,
),
),
]

View file

@ -105,28 +105,57 @@ class DomainApplication(TimeStampedModel):
ARMED_FORCES_AP = "AP", "Armed Forces Pacific (AP)"
class OrganizationChoices(models.TextChoices):
"""
Primary organization choices:
For use in django admin
Keys need to match OrganizationChoicesVerbose
"""
FEDERAL = "federal", "Federal"
INTERSTATE = "interstate", "Interstate"
STATE_OR_TERRITORY = "state_or_territory", "State or territory"
TRIBAL = "tribal", "Tribal"
COUNTY = "county", "County"
CITY = "city", "City"
SPECIAL_DISTRICT = "special_district", "Special district"
SCHOOL_DISTRICT = "school_district", "School district"
class OrganizationChoicesVerbose(models.TextChoices):
"""
Secondary organization choices
For use in the application form and on the templates
Keys need to match OrganizationChoices
"""
FEDERAL = (
"federal",
"Federal: an agency of the U.S. government's executive, legislative, "
"or judicial branches",
"Federal: an agency of the U.S. government's executive, "
"legislative, or judicial branches",
)
INTERSTATE = "interstate", "Interstate: an organization of two or more states"
STATE_OR_TERRITORY = "state_or_territory", (
"State or territory: one of the 50 U.S. states, the District of "
"Columbia, American Samoa, Guam, Northern Mariana Islands, "
"Puerto Rico, or the U.S. Virgin Islands"
STATE_OR_TERRITORY = (
"state_or_territory",
"State or territory: one of the 50 U.S. states, the District of Columbia, "
"American Samoa, Guam, Northern Mariana Islands, Puerto Rico, or the U.S. "
"Virgin Islands",
)
TRIBAL = "tribal", (
"Tribal: a tribal government recognized by the federal or "
"a state government"
TRIBAL = (
"tribal",
"Tribal: a tribal government recognized by the federal or a state "
"government",
)
COUNTY = "county", "County: a county, parish, or borough"
CITY = "city", "City: a city, town, township, village, etc."
SPECIAL_DISTRICT = "special_district", (
"Special district: an independent organization within a single state"
SPECIAL_DISTRICT = (
"special_district",
"Special district: an independent organization within a single state",
)
SCHOOL_DISTRICT = "school_district", (
"School district: a school district that is not part of a local government"
SCHOOL_DISTRICT = (
"school_district",
"School district: a school district that is not part of a local "
"government",
)
class BranchChoices(models.TextChoices):
@ -297,6 +326,7 @@ class DomainApplication(TimeStampedModel):
# ##### data fields from the initial form #####
organization_type = models.CharField(
max_length=255,
# use the short names in Django admin
choices=OrganizationChoices.choices,
null=True,
blank=True,

View file

@ -21,6 +21,7 @@ class DomainInformation(TimeStampedModel):
StateTerritoryChoices = DomainApplication.StateTerritoryChoices
# use the short names in Django admin
OrganizationChoices = DomainApplication.OrganizationChoices
BranchChoices = DomainApplication.BranchChoices

View file

@ -1,5 +1,6 @@
{% extends 'application_form.html' %}
{% load static url_helpers %}
{% load custom_filters %}
{% block form_required_fields_help_text %}
{# there are no required fields on this page so don't show this #}
@ -26,7 +27,13 @@
<div class="review__step__name">{{ form_titles|get_item:step }}</div>
<div>
{% if step == Step.ORGANIZATION_TYPE %}
{{ application.get_organization_type_display|default:"Incomplete" }}
{% if application.organization_type is not None %}
{% with long_org_type=application.organization_type|get_organization_long_name %}
{{ long_org_type }}
{% endwith %}
{% else %}
Incomplete
{% endif %}
{% endif %}
{% if step == Step.TRIBAL_GOVERNMENT %}
{{ application.tribe_name|default:"Incomplete" }}

View file

@ -1,5 +1,7 @@
{% extends 'base.html' %}
{% load custom_filters %}
{% block title %}Domain request status | {{ domainapplication.requested_domain.name }} | {% endblock %}
{% load static url_helpers %}
@ -50,7 +52,9 @@
<div class="grid-col desktop:grid-offset-2 maxw-tablet">
<h2 class="text-primary-darker"> Summary of your domain request </h2>
{% with heading_level='h3' %}
{% include "includes/summary_item.html" with title='Type of organization' value=domainapplication.get_organization_type_display heading_level=heading_level %}
{% with long_org_type=domainapplication.organization_type|get_organization_long_name %}
{% include "includes/summary_item.html" with title='Type of organization' value=long_org_type heading_level=heading_level %}
{% endwith %}
{% if domainapplication.tribe_name %}
{% include "includes/summary_item.html" with title='Tribal government' value=domainapplication.tribe_name heading_level=heading_level %}

View file

@ -52,7 +52,7 @@
{% include "includes/summary_item.html" with title='Security email' value='None provided' edit_link=url %}
{% endif %}
{% url 'domain-users' pk=domain.id as url %}
{% include "includes/summary_item.html" with title='User management' users='true' list=True value=domain.permissions.all edit_link=url %}
{% include "includes/summary_item.html" with title='Domain managers' users='true' list=True value=domain.permissions.all edit_link=url %}
</div>
{% endblock %} {# domain_content #}

View file

@ -100,7 +100,7 @@
<a href="{{ url }}"
{% if request.path|startswith:url %}class="usa-current"{% endif %}
>
User management
Domain managers
</a>
</li>
</ul>

View file

@ -1,10 +1,23 @@
{% extends "domain_base.html" %}
{% load static %}
{% load static url_helpers %}
{% block title %}User management | {{ domain.name }} | {% endblock %}
{% block title %}Domain managers | {{ domain.name }} | {% endblock %}
{% block domain_content %}
<h1>User management</h1>
<h1>Domain managers</h1>
<p>
Domain managers can update all information related to a domain within the
.gov registrar, including contact details, authorizing official, security
email, and DNS name servers.
</p>
<ul>
<li>There is no limit to the number of domain managers you can add.</li>
<li>After adding a domain manager, an email invitation will be sent to that user with
instructions on how to set up an account.</li>
<li>To remove a domain manager, <a href="{% public_site_url 'contact/' %}" class="usa-link">contact us</a> for assistance.
</ul>
{% if domain.permissions %}
<section class="section--outlined">

View file

@ -1,7 +1,10 @@
import logging
from django import template
import re
from registrar.models.domain_application import DomainApplication
register = template.Library()
logger = logging.getLogger(__name__)
@register.filter(name="extract_value")
@ -48,3 +51,16 @@ def contains_checkbox(html_list):
if re.search(r'<input[^>]*type="checkbox"', html_string):
return True
return False
@register.filter
def get_organization_long_name(organization_type):
organization_choices_dict = dict(
DomainApplication.OrganizationChoicesVerbose.choices
)
long_form_type = organization_choices_dict[organization_type]
if long_form_type is None:
logger.error("Organization type error, triggered by a template's custom filter")
return "Error"
return long_form_type

View file

@ -53,6 +53,26 @@ class TestDomainAdmin(MockEppLib):
self.factory = RequestFactory()
super().setUp()
def test_short_org_name_in_domains_list(self):
"""
Make sure the short name is displaying in admin on the list page
"""
self.client.force_login(self.superuser)
application = completed_application(status=DomainApplication.IN_REVIEW)
application.approve()
response = self.client.get("/admin/registrar/domain/")
# There are 3 template references to Federal (3) plus one reference in the table
# for our actual application
self.assertContains(response, "Federal", count=4)
# This may be a bit more robust
self.assertContains(
response, '<td class="field-organization_type">Federal</td>', count=1
)
# Now let's make sure the long description does not exist
self.assertNotContains(response, "Federal: an agency of the U.S. government")
@skip("Why did this test stop working, and is is a good test")
def test_place_and_remove_hold(self):
domain = create_ready_domain()
@ -244,8 +264,11 @@ class TestDomainAdmin(MockEppLib):
raise
def tearDown(self):
User.objects.all().delete()
super().tearDown()
Domain.objects.all().delete()
DomainInformation.objects.all().delete()
DomainApplication.objects.all().delete()
User.objects.all().delete()
class TestDomainApplicationAdminForm(TestCase):
@ -301,6 +324,23 @@ class TestDomainApplicationAdmin(MockEppLib):
self.superuser = create_superuser()
self.staffuser = create_user()
def test_short_org_name_in_applications_list(self):
"""
Make sure the short name is displaying in admin on the list page
"""
self.client.force_login(self.superuser)
completed_application()
response = self.client.get("/admin/registrar/domainapplication/")
# There are 3 template references to Federal (3) plus one reference in the table
# for our actual application
self.assertContains(response, "Federal", count=4)
# This may be a bit more robust
self.assertContains(
response, '<td class="field-organization_type">Federal</td>', count=1
)
# Now let's make sure the long description does not exist
self.assertNotContains(response, "Federal: an agency of the U.S. government")
@boto3_mocking.patching
def test_save_model_sends_submitted_email(self):
# make sure there is no user with this email

View file

@ -142,9 +142,12 @@ class DomainApplicationTests(TestWithUser, WebTest):
@boto3_mocking.patching
def test_application_form_submission(self):
"""Can fill out the entire form and submit.
"""
Can fill out the entire form and submit.
As we add additional form pages, we need to include them here to make
this test work.
This test also looks for the long organization name on the summary page.
"""
num_pages_tested = 0
# elections, type_of_work, tribal_government, no_other_contacts
@ -428,7 +431,8 @@ class DomainApplicationTests(TestWithUser, WebTest):
review_form = review_page.form
# Review page contains all the previously entered data
self.assertContains(review_page, "Federal")
# Let's make sure the long org name is displayed
self.assertContains(review_page, "Federal: an agency of the U.S. government")
self.assertContains(review_page, "Executive")
self.assertContains(review_page, "Testorg")
self.assertContains(review_page, "address 1")
@ -1066,6 +1070,26 @@ class DomainApplicationTests(TestWithUser, WebTest):
# page = self.app.get(url)
# self.assertNotContains(page, "VALUE")
def test_long_org_name_in_application(self):
"""
Make sure the long name is displaying in the application form,
org step
"""
request = self.app.get(reverse("application:")).follow()
self.assertContains(request, "Federal: an agency of the U.S. government")
def test_long_org_name_in_application_manage(self):
"""
Make sure the long name is displaying in the application summary
page (manage your application)
"""
completed_application(status=DomainApplication.SUBMITTED, user=self.user)
home_page = self.app.get("/")
self.assertContains(home_page, "city.gov")
# click the "Edit" link
detail_page = home_page.click("Manage")
self.assertContains(detail_page, "Federal: an agency of the U.S. government")
class TestWithDomainPermissions(TestWithUser):
def setUp(self):
@ -1201,14 +1225,14 @@ class TestDomainOverview(TestWithDomainPermissions, WebTest):
self.assertEqual(response.status_code, 403)
class TestDomainUserManagement(TestDomainOverview):
def test_domain_user_management(self):
class TestDomainManagers(TestDomainOverview):
def test_domain_managers(self):
response = self.client.get(
reverse("domain-users", kwargs={"pk": self.domain.id})
)
self.assertContains(response, "User management")
self.assertContains(response, "Domain managers")
def test_domain_user_management_add_link(self):
def test_domain_managers_add_link(self):
"""Button to get to user add page works."""
management_page = self.app.get(
reverse("domain-users", kwargs={"pk": self.domain.id})

View file

@ -656,7 +656,7 @@ class DomainSecurityEmailView(DomainFormBaseView):
class DomainUsersView(DomainBaseView):
"""User management page in the domain details."""
"""Domain managers page in the domain details."""
template_name = "domain_users.html"