mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-28 16:29:54 +02:00
Merge remote-tracking branch 'origin' into rh/1031-staff-enable-editing
This commit is contained in:
commit
f8ec478f78
15 changed files with 232 additions and 38 deletions
18
.github/ISSUE_TEMPLATE/issue-default.yml
vendored
18
.github/ISSUE_TEMPLATE/issue-default.yml
vendored
|
@ -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.
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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."},
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
]
|
|
@ -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,
|
||||
|
|
|
@ -21,6 +21,7 @@ class DomainInformation(TimeStampedModel):
|
|||
|
||||
StateTerritoryChoices = DomainApplication.StateTerritoryChoices
|
||||
|
||||
# use the short names in Django admin
|
||||
OrganizationChoices = DomainApplication.OrganizationChoices
|
||||
|
||||
BranchChoices = DomainApplication.BranchChoices
|
||||
|
|
|
@ -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" }}
|
||||
|
|
|
@ -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 %}
|
||||
|
|
|
@ -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 #}
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
<a href="{{ url }}"
|
||||
{% if request.path|startswith:url %}class="usa-current"{% endif %}
|
||||
>
|
||||
User management
|
||||
Domain managers
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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})
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue