mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-15 05:54:11 +02:00
Merge branch 'main' of github.com:cisagov/manage.get.gov into rh/1500-domain-req-alt-req-same
This commit is contained in:
commit
7900031a1b
28 changed files with 493 additions and 196 deletions
8
.github/workflows/deploy-development.yaml
vendored
8
.github/workflows/deploy-development.yaml
vendored
|
@ -38,3 +38,11 @@ jobs:
|
||||||
cf_org: cisa-dotgov
|
cf_org: cisa-dotgov
|
||||||
cf_space: development
|
cf_space: development
|
||||||
push_arguments: "-f ops/manifests/manifest-development.yaml"
|
push_arguments: "-f ops/manifests/manifest-development.yaml"
|
||||||
|
- name: Run Django migrations
|
||||||
|
uses: cloud-gov/cg-cli-tools@main
|
||||||
|
with:
|
||||||
|
cf_username: ${{ secrets.CF_DEVELOPMENT_USERNAME }}
|
||||||
|
cf_password: ${{ secrets.CF_DEVELOPMENT_PASSWORD }}
|
||||||
|
cf_org: cisa-dotgov
|
||||||
|
cf_space: development
|
||||||
|
cf_command: "run-task getgov-development --command 'python manage.py migrate' --name migrate"
|
||||||
|
|
1
.github/workflows/migrate.yaml
vendored
1
.github/workflows/migrate.yaml
vendored
|
@ -26,7 +26,6 @@ on:
|
||||||
- rb
|
- rb
|
||||||
- ko
|
- ko
|
||||||
- ab
|
- ab
|
||||||
- bl
|
|
||||||
- rjm
|
- rjm
|
||||||
- dk
|
- dk
|
||||||
|
|
||||||
|
|
1
.github/workflows/reset-db.yaml
vendored
1
.github/workflows/reset-db.yaml
vendored
|
@ -26,7 +26,6 @@ on:
|
||||||
- rb
|
- rb
|
||||||
- ko
|
- ko
|
||||||
- ab
|
- ab
|
||||||
- bl
|
|
||||||
- rjm
|
- rjm
|
||||||
- dk
|
- dk
|
||||||
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
---
|
|
||||||
applications:
|
|
||||||
- name: getgov-bl
|
|
||||||
buildpacks:
|
|
||||||
- python_buildpack
|
|
||||||
path: ../../src
|
|
||||||
instances: 1
|
|
||||||
memory: 512M
|
|
||||||
stack: cflinuxfs4
|
|
||||||
timeout: 180
|
|
||||||
command: ./run.sh
|
|
||||||
health-check-type: http
|
|
||||||
health-check-http-endpoint: /health
|
|
||||||
health-check-invocation-timeout: 40
|
|
||||||
env:
|
|
||||||
# Send stdout and stderr straight to the terminal without buffering
|
|
||||||
PYTHONUNBUFFERED: yup
|
|
||||||
# Tell Django where to find its configuration
|
|
||||||
DJANGO_SETTINGS_MODULE: registrar.config.settings
|
|
||||||
# Tell Django where it is being hosted
|
|
||||||
DJANGO_BASE_URL: https://getgov-bl.app.cloud.gov
|
|
||||||
# Tell Django how much stuff to log
|
|
||||||
DJANGO_LOG_LEVEL: INFO
|
|
||||||
# default public site location
|
|
||||||
GETGOV_PUBLIC_SITE_URL: https://get.gov
|
|
||||||
# Flag to disable/enable features in prod environments
|
|
||||||
IS_PRODUCTION: False
|
|
||||||
routes:
|
|
||||||
- route: getgov-bl.app.cloud.gov
|
|
||||||
services:
|
|
||||||
- getgov-credentials
|
|
||||||
- getgov-bl-database
|
|
|
@ -20,6 +20,8 @@ from . import models
|
||||||
from auditlog.models import LogEntry # type: ignore
|
from auditlog.models import LogEntry # type: ignore
|
||||||
from auditlog.admin import LogEntryAdmin # type: ignore
|
from auditlog.admin import LogEntryAdmin # type: ignore
|
||||||
from django_fsm import TransitionNotAllowed # type: ignore
|
from django_fsm import TransitionNotAllowed # type: ignore
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.utils.html import escape
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -452,6 +454,60 @@ class ContactAdmin(ListHeaderAdmin):
|
||||||
readonly_fields.extend([field for field in self.analyst_readonly_fields])
|
readonly_fields.extend([field for field in self.analyst_readonly_fields])
|
||||||
return readonly_fields # Read-only fields for analysts
|
return readonly_fields # Read-only fields for analysts
|
||||||
|
|
||||||
|
def change_view(self, request, object_id, form_url="", extra_context=None):
|
||||||
|
"""Extend the change_view for Contact objects in django admin.
|
||||||
|
Customize to display related objects to the Contact. These will be passed
|
||||||
|
through the messages construct to the template for display to the user."""
|
||||||
|
|
||||||
|
# Fetch the Contact instance
|
||||||
|
contact = models.Contact.objects.get(pk=object_id)
|
||||||
|
|
||||||
|
# initialize related_objects array
|
||||||
|
related_objects = []
|
||||||
|
# for all defined fields in the model
|
||||||
|
for related_field in contact._meta.get_fields():
|
||||||
|
# if the field is a relation to another object
|
||||||
|
if related_field.is_relation:
|
||||||
|
# Check if the related field is not None
|
||||||
|
related_manager = getattr(contact, related_field.name)
|
||||||
|
if related_manager is not None:
|
||||||
|
# Check if it's a ManyToManyField/reverse ForeignKey or a OneToOneField
|
||||||
|
# Do this by checking for get_queryset method on the related_manager
|
||||||
|
if hasattr(related_manager, "get_queryset"):
|
||||||
|
# Handles ManyToManyRel and ManyToOneRel
|
||||||
|
queryset = related_manager.get_queryset()
|
||||||
|
else:
|
||||||
|
# Handles OneToOne rels, ie. User
|
||||||
|
queryset = [related_manager]
|
||||||
|
|
||||||
|
for obj in queryset:
|
||||||
|
# for each object, build the edit url in this view and add as tuple
|
||||||
|
# to the related_objects array
|
||||||
|
app_label = obj._meta.app_label
|
||||||
|
model_name = obj._meta.model_name
|
||||||
|
obj_id = obj.id
|
||||||
|
change_url = reverse("admin:%s_%s_change" % (app_label, model_name), args=[obj_id])
|
||||||
|
related_objects.append((change_url, obj))
|
||||||
|
|
||||||
|
if related_objects:
|
||||||
|
message = "<ul class='messagelist_content-list--unstyled'>"
|
||||||
|
for i, (url, obj) in enumerate(related_objects):
|
||||||
|
if i < 5:
|
||||||
|
escaped_obj = escape(obj)
|
||||||
|
message += f"<li>Joined to {obj.__class__.__name__}: <a href='{url}'>{escaped_obj}</a></li>"
|
||||||
|
message += "</ul>"
|
||||||
|
if len(related_objects) > 5:
|
||||||
|
related_objects_over_five = len(related_objects) - 5
|
||||||
|
message += f"<p class='font-sans-3xs'>And {related_objects_over_five} more...</p>"
|
||||||
|
|
||||||
|
message_html = mark_safe(message) # nosec
|
||||||
|
messages.warning(
|
||||||
|
request,
|
||||||
|
message_html,
|
||||||
|
)
|
||||||
|
|
||||||
|
return super().change_view(request, object_id, form_url, extra_context=extra_context)
|
||||||
|
|
||||||
|
|
||||||
class WebsiteAdmin(ListHeaderAdmin):
|
class WebsiteAdmin(ListHeaderAdmin):
|
||||||
"""Custom website admin class."""
|
"""Custom website admin class."""
|
||||||
|
|
|
@ -258,3 +258,15 @@ h1, h2, h3,
|
||||||
#select2-id_user-results {
|
#select2-id_user-results {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Content list inside of a DjA alert, unstyled
|
||||||
|
.messagelist_content-list--unstyled {
|
||||||
|
padding-left: 0;
|
||||||
|
li {
|
||||||
|
font-family: "Source Sans Pro Web", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif;
|
||||||
|
font-size: 13.92px!important;
|
||||||
|
background: none!important;
|
||||||
|
padding: 0!important;
|
||||||
|
margin: 0!important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
14
src/registrar/assets/sass/_theme/_lists.scss
Normal file
14
src/registrar/assets/sass/_theme/_lists.scss
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
@use "uswds-core" as *;
|
||||||
|
|
||||||
|
dt {
|
||||||
|
color: color('primary-dark');
|
||||||
|
margin-top: units(2);
|
||||||
|
font-weight: font-weight('semibold');
|
||||||
|
// The units mixin can only get us close, so it's between
|
||||||
|
// hardcoding the value and using in markup
|
||||||
|
font-size: 16.96px;
|
||||||
|
}
|
||||||
|
dd {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
margin-top: units(-1);
|
margin-top: units(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Tighter spacing when H2 is immediatly after H1
|
// Tighter spacing when h2 is immediatly after h1
|
||||||
.register-form-step .usa-fieldset:first-of-type h2:first-of-type,
|
.register-form-step .usa-fieldset:first-of-type h2:first-of-type,
|
||||||
.register-form-step h1 + h2 {
|
.register-form-step h1 + h2 {
|
||||||
margin-top: units(1);
|
margin-top: units(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// register-form-review-header is used on the summary page and
|
||||||
|
// should not be styled like the register form headers
|
||||||
.register-form-step h3 {
|
.register-form-step h3 {
|
||||||
color: color('primary-dark');
|
color: color('primary-dark');
|
||||||
letter-spacing: $letter-space--xs;
|
letter-spacing: $letter-space--xs;
|
||||||
|
@ -23,6 +25,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
h3.register-form-review-header {
|
||||||
|
color: color('primary-dark');
|
||||||
|
margin-top: units(2);
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-weight: font-weight('semibold');
|
||||||
|
// The units mixin can only get us close, so it's between
|
||||||
|
// hardcoding the value and using in markup
|
||||||
|
font-size: 16.96px;
|
||||||
|
}
|
||||||
|
|
||||||
.register-form-step h4 {
|
.register-form-step h4 {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
--- Custom Styles ---------------------------------*/
|
--- Custom Styles ---------------------------------*/
|
||||||
@forward "base";
|
@forward "base";
|
||||||
@forward "typography";
|
@forward "typography";
|
||||||
|
@forward "lists";
|
||||||
@forward "buttons";
|
@forward "buttons";
|
||||||
@forward "forms";
|
@forward "forms";
|
||||||
@forward "fieldsets";
|
@forward "fieldsets";
|
||||||
|
|
|
@ -660,7 +660,6 @@ ALLOWED_HOSTS = [
|
||||||
"getgov-rb.app.cloud.gov",
|
"getgov-rb.app.cloud.gov",
|
||||||
"getgov-ko.app.cloud.gov",
|
"getgov-ko.app.cloud.gov",
|
||||||
"getgov-ab.app.cloud.gov",
|
"getgov-ab.app.cloud.gov",
|
||||||
"getgov-bl.app.cloud.gov",
|
|
||||||
"getgov-rjm.app.cloud.gov",
|
"getgov-rjm.app.cloud.gov",
|
||||||
"getgov-dk.app.cloud.gov",
|
"getgov-dk.app.cloud.gov",
|
||||||
"manage.get.gov",
|
"manage.get.gov",
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 4.2.7 on 2024-01-23 22:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("registrar", "0063_veryimportantperson"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domainapplication",
|
||||||
|
name="address_line1",
|
||||||
|
field=models.TextField(blank=True, help_text="Street address", null=True, verbose_name="Address line 1"),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="domainapplication",
|
||||||
|
name="address_line2",
|
||||||
|
field=models.TextField(
|
||||||
|
blank=True, help_text="Street address line 2 (optional)", null=True, verbose_name="Address line 2"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
37
src/registrar/migrations/0065_create_groups_v06.py
Normal file
37
src/registrar/migrations/0065_create_groups_v06.py
Normal file
|
@ -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", "0064_alter_domainapplication_address_line1_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(
|
||||||
|
create_groups,
|
||||||
|
reverse_code=migrations.RunPython.noop,
|
||||||
|
atomic=True,
|
||||||
|
),
|
||||||
|
]
|
|
@ -431,11 +431,13 @@ class DomainApplication(TimeStampedModel):
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Street address",
|
help_text="Street address",
|
||||||
|
verbose_name="Address line 1",
|
||||||
)
|
)
|
||||||
address_line2 = models.TextField(
|
address_line2 = models.TextField(
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text="Street address line 2 (optional)",
|
help_text="Street address line 2 (optional)",
|
||||||
|
verbose_name="Address line 2",
|
||||||
)
|
)
|
||||||
city = models.TextField(
|
city = models.TextField(
|
||||||
null=True,
|
null=True,
|
||||||
|
|
|
@ -66,6 +66,11 @@ class UserGroup(Group):
|
||||||
"model": "userdomainrole",
|
"model": "userdomainrole",
|
||||||
"permissions": ["view_userdomainrole", "delete_userdomainrole"],
|
"permissions": ["view_userdomainrole", "delete_userdomainrole"],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"app_label": "registrar",
|
||||||
|
"model": "veryimportantperson",
|
||||||
|
"permissions": ["add_veryimportantperson", "change_veryimportantperson", "delete_veryimportantperson"],
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
# Avoid error: You can't execute queries until the end
|
# Avoid error: You can't execute queries until the end
|
||||||
|
|
|
@ -57,6 +57,9 @@ class DomainHelper:
|
||||||
# If blank ok is true, just return the domain
|
# If blank ok is true, just return the domain
|
||||||
return domain
|
return domain
|
||||||
|
|
||||||
|
if domain.startswith("www."):
|
||||||
|
domain = domain[4:]
|
||||||
|
|
||||||
if domain.endswith(".gov"):
|
if domain.endswith(".gov"):
|
||||||
domain = domain[:-4]
|
domain = domain[:-4]
|
||||||
|
|
||||||
|
|
|
@ -20,108 +20,158 @@
|
||||||
|
|
||||||
{% block form_fields %}
|
{% block form_fields %}
|
||||||
{% for step in steps.all|slice:":-1" %}
|
{% for step in steps.all|slice:":-1" %}
|
||||||
<section class="review__step">
|
<section class="summary-item margin-top-3">
|
||||||
<hr />
|
|
||||||
<div class="review__step__title display-flex flex-justify">
|
|
||||||
<div class="review__step__value">
|
|
||||||
<div class="review__step__name">{{ form_titles|get_item:step }}</div>
|
|
||||||
<div>
|
|
||||||
{% if step == Step.ORGANIZATION_TYPE %}
|
{% if step == Step.ORGANIZATION_TYPE %}
|
||||||
|
{% namespaced_url 'application' step as application_url %}
|
||||||
{% if application.organization_type is not None %}
|
{% if application.organization_type is not None %}
|
||||||
{% with long_org_type=application.organization_type|get_organization_long_name %}
|
{% with title=form_titles|get_item:step value=application.get_organization_type_display|default:"Incomplete" %}
|
||||||
{{ long_org_type }}
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% else %}
|
{% else %}
|
||||||
Incomplete
|
{% with title=form_titles|get_item:step value="Incomplete" %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if step == Step.TRIBAL_GOVERNMENT %}
|
{% if step == Step.TRIBAL_GOVERNMENT %}
|
||||||
{{ application.tribe_name|default:"Incomplete" }}
|
{% namespaced_url 'application' step as application_url %}
|
||||||
|
{% with title=form_titles|get_item:step value=application.tribe_name|default:"Incomplete" %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% if application.federally_recognized_tribe %}<p>Federally-recognized tribe</p>{% endif %}
|
{% if application.federally_recognized_tribe %}<p>Federally-recognized tribe</p>{% endif %}
|
||||||
{% if application.state_recognized_tribe %}<p>State-recognized tribe</p>{% endif %}
|
{% if application.state_recognized_tribe %}<p>State-recognized tribe</p>{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if step == Step.ORGANIZATION_FEDERAL %}
|
{% if step == Step.ORGANIZATION_FEDERAL %}
|
||||||
{{ application.get_federal_type_display|default:"Incomplete" }}
|
{% namespaced_url 'application' step as application_url %}
|
||||||
|
{% with title=form_titles|get_item:step value=application.get_federal_type_display|default:"Incomplete" %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if step == Step.ORGANIZATION_ELECTION %}
|
{% if step == Step.ORGANIZATION_ELECTION %}
|
||||||
{{ application.is_election_board|yesno:"Yes,No,Incomplete" }}
|
{% namespaced_url 'application' step as application_url %}
|
||||||
|
{% with title=form_titles|get_item:step value=application.is_election_board|yesno:"Yes,No,Incomplete" %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if step == Step.ORGANIZATION_CONTACT %}
|
{% if step == Step.ORGANIZATION_CONTACT %}
|
||||||
|
{% namespaced_url 'application' step as application_url %}
|
||||||
{% if application.organization_name %}
|
{% if application.organization_name %}
|
||||||
{% include "includes/organization_address.html" with organization=application %}
|
{% with title=form_titles|get_item:step value=application %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url address='true' %}
|
||||||
|
{% endwith %}
|
||||||
{% else %}
|
{% else %}
|
||||||
Incomplete
|
{% with title=form_titles|get_item:step value='Incomplete' %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if step == Step.ABOUT_YOUR_ORGANIZATION %}
|
{% if step == Step.ABOUT_YOUR_ORGANIZATION %}
|
||||||
<p>{{ application.about_your_organization|default:"Incomplete" }}</p>
|
{% namespaced_url 'application' step as application_url %}
|
||||||
|
{% with title=form_titles|get_item:step value=application.about_your_organization|default:"Incomplete" %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if step == Step.AUTHORIZING_OFFICIAL %}
|
{% if step == Step.AUTHORIZING_OFFICIAL %}
|
||||||
{% if application.authorizing_official %}
|
{% namespaced_url 'application' step as application_url %}
|
||||||
<div class="margin-bottom-105">
|
{% if application.authorizing_official is not None %}
|
||||||
{% include "includes/contact.html" with contact=application.authorizing_official %}
|
{% with title=form_titles|get_item:step value=application.authorizing_official %}
|
||||||
</div>
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url contact='true' %}
|
||||||
|
{% endwith %}
|
||||||
{% else %}
|
{% else %}
|
||||||
Incomplete
|
{% with title=form_titles|get_item:step value="Incomplete" %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if step == Step.CURRENT_SITES %}
|
{% if step == Step.CURRENT_SITES %}
|
||||||
<ul class="add-list-reset">
|
{% namespaced_url 'application' step as application_url %}
|
||||||
{% for site in application.current_websites.all %}
|
{% if application.current_websites.all %}
|
||||||
<li>{{ site.website }}</li>
|
{% with title=form_titles|get_item:step value=application.current_websites.all %}
|
||||||
{% empty %}
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url list='true' %}
|
||||||
<li>None</li>
|
{% endwith %}
|
||||||
{% endfor %}
|
{% else %}
|
||||||
</ul>
|
{% with title=form_titles|get_item:step value='None' %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if step == Step.DOTGOV_DOMAIN %}
|
{% if step == Step.DOTGOV_DOMAIN %}
|
||||||
<ul class="add-list-reset margin-bottom-105">
|
{% namespaced_url 'application' step as application_url %}
|
||||||
<li>{{ application.requested_domain.name|default:"Incomplete" }}</li>
|
{% with title=form_titles|get_item:step value=application.requested_domain.name|default:"Incomplete" %}
|
||||||
</ul>
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
<ul class="add-list-reset">
|
{% endwith %}
|
||||||
|
|
||||||
|
{% if application.alternative_domains.all %}
|
||||||
|
<h3 class="register-form-review-header">Alternative domains</h3>
|
||||||
|
<ul class="usa-list usa-list--unstyled margin-top-0">
|
||||||
{% for site in application.alternative_domains.all %}
|
{% for site in application.alternative_domains.all %}
|
||||||
<li>{{ site.website }}</li>
|
<li>{{ site.website }}</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if step == Step.PURPOSE %}
|
{% if step == Step.PURPOSE %}
|
||||||
{{ application.purpose|default:"Incomplete" }}
|
{% namespaced_url 'application' step as application_url %}
|
||||||
|
{% with title=form_titles|get_item:step value=application.purpose|default:"Incomplete" %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if step == Step.YOUR_CONTACT %}
|
{% if step == Step.YOUR_CONTACT %}
|
||||||
{% if application.submitter %}
|
{% namespaced_url 'application' step as application_url %}
|
||||||
<div class="margin-bottom-105">
|
{% if application.submitter is not None %}
|
||||||
{% include "includes/contact.html" with contact=application.submitter %}
|
{% with title=form_titles|get_item:step value=application.submitter %}
|
||||||
</div>
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url contact='true' %}
|
||||||
|
{% endwith %}
|
||||||
{% else %}
|
{% else %}
|
||||||
Incomplete
|
{% with title=form_titles|get_item:step value="Incomplete" %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if step == Step.OTHER_CONTACTS %}
|
{% if step == Step.OTHER_CONTACTS %}
|
||||||
{% for other in application.other_contacts.all %}
|
{% namespaced_url 'application' step as application_url %}
|
||||||
<div class="margin-bottom-105">
|
{% if application.other_contacts.all %}
|
||||||
<p class="text-semibold margin-top-1 margin-bottom-0">Contact {{ forloop.counter }}</p>
|
{% with title=form_titles|get_item:step value=application.other_contacts.all %}
|
||||||
{% include "includes/contact.html" with contact=other %}
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url contact='true' list='true' %}
|
||||||
</div>
|
{% endwith %}
|
||||||
{% empty %}
|
{% else %}
|
||||||
<div class="margin-bottom-105">
|
{% with title=form_titles|get_item:step value=application.no_other_contacts_rationale|default:"Incomplete" %}
|
||||||
<p class="text-semibold margin-top-1 margin-bottom-0">No other employees from your organization?</p>
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
{{ application.no_other_contacts_rationale|default:"Incomplete" }}
|
{% endwith %}
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if step == Step.ANYTHING_ELSE %}
|
{% if step == Step.ANYTHING_ELSE %}
|
||||||
{{ application.anything_else|default:"No" }}
|
{% namespaced_url 'application' step as application_url %}
|
||||||
|
{% with title=form_titles|get_item:step value=application.anything_else|default:"No" %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
{% if step == Step.REQUIREMENTS %}
|
{% if step == Step.REQUIREMENTS %}
|
||||||
{{ application.is_policy_acknowledged|yesno:"I agree.,I do not agree.,I do not agree." }}
|
{% namespaced_url 'application' step as application_url %}
|
||||||
|
{% with title=form_titles|get_item:step value=application.is_policy_acknowledged|yesno:"I agree.,I do not agree.,I do not agree." %}
|
||||||
|
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=True edit_link=application_url %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a
|
|
||||||
aria-describedby="review_step_title__{{step}}"
|
|
||||||
href="{% namespaced_url 'application' step %}"
|
|
||||||
>Edit<span class="sr-only"> {{ form_titles|get_item:step }}</span></a>
|
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -52,8 +52,8 @@
|
||||||
<div class="grid-col desktop:grid-offset-2 maxw-tablet">
|
<div class="grid-col desktop:grid-offset-2 maxw-tablet">
|
||||||
<h2 class="text-primary-darker"> Summary of your domain request </h2>
|
<h2 class="text-primary-darker"> Summary of your domain request </h2>
|
||||||
{% with heading_level='h3' %}
|
{% with heading_level='h3' %}
|
||||||
{% with long_org_type=domainapplication.organization_type|get_organization_long_name %}
|
{% with org_type=domainapplication.get_organization_type_display %}
|
||||||
{% include "includes/summary_item.html" with title='Type of organization' value=long_org_type heading_level=heading_level %}
|
{% include "includes/summary_item.html" with title='Type of organization' value=org_type heading_level=heading_level %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
{% if domainapplication.tribe_name %}
|
{% if domainapplication.tribe_name %}
|
||||||
|
@ -74,7 +74,9 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if domainapplication.is_election_board %}
|
{% if domainapplication.is_election_board %}
|
||||||
{% include "includes/summary_item.html" with title='Election office' value=domainapplication.is_election_board heading_level=heading_level %}
|
{% with value=domainapplication.is_election_board|yesno:"Yes,No,Incomplete" %}
|
||||||
|
{% include "includes/summary_item.html" with title='Election office' value=value heading_level=heading_level %}
|
||||||
|
{% endwith %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if domainapplication.organization_name %}
|
{% if domainapplication.organization_name %}
|
||||||
|
@ -109,7 +111,11 @@
|
||||||
{% include "includes/summary_item.html" with title='Your contact information' value=domainapplication.submitter contact='true' heading_level=heading_level %}
|
{% include "includes/summary_item.html" with title='Your contact information' value=domainapplication.submitter contact='true' heading_level=heading_level %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if domainapplication.other_contacts.all %}
|
||||||
{% include "includes/summary_item.html" with title='Other employees from your organization' value=domainapplication.other_contacts.all contact='true' list='true' heading_level=heading_level %}
|
{% include "includes/summary_item.html" with title='Other employees from your organization' value=domainapplication.other_contacts.all contact='true' list='true' heading_level=heading_level %}
|
||||||
|
{% else %}
|
||||||
|
{% include "includes/summary_item.html" with title='Other employees from your organization' value=domainapplication.no_other_contacts_rationale heading_level=heading_level %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% include "includes/summary_item.html" with title='Anything else?' value=domainapplication.anything_else|default:"No" heading_level=heading_level %}
|
{% include "includes/summary_item.html" with title='Anything else?' value=domainapplication.anything_else|default:"No" heading_level=heading_level %}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||||
Hi.
|
Hi.
|
||||||
|
|
||||||
{{ requester_email }} has added you as a manager on {{ domain.name }}.
|
{{ requestor_email }} has added you as a manager on {{ domain.name }}.
|
||||||
|
|
||||||
You can manage this domain on the .gov registrar <https://manage.get.gov>.
|
You can manage this domain on the .gov registrar <https://manage.get.gov>.
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,22 @@ SUMMARY OF YOUR DOMAIN REQUEST
|
||||||
|
|
||||||
Type of organization:
|
Type of organization:
|
||||||
{{ application.get_organization_type_display }}
|
{{ application.get_organization_type_display }}
|
||||||
|
{% if application.show_organization_federal %}
|
||||||
|
Federal government branch:
|
||||||
|
{{ application.get_federal_type_display }}
|
||||||
|
{% elif application.show_tribal_government %}
|
||||||
|
Tribal government:
|
||||||
|
{{ application.tribe_name|default:"Incomplete" }}{% if application.federally_recognized_tribe %}
|
||||||
|
Federally-recognized tribe
|
||||||
|
{% endif %}{% if application.state_recognized_tribe %}
|
||||||
|
State-recognized tribe
|
||||||
|
{% endif %}{% endif %}{% if application.show_organization_election %}
|
||||||
|
Election office:
|
||||||
|
{{ application.is_election_board|yesno:"Yes,No,Incomplete" }}
|
||||||
|
{% endif %}
|
||||||
Organization name and mailing address:
|
Organization name and mailing address:
|
||||||
{% spaceless %}{{ application.organization_name }}
|
{% spaceless %}{{ application.federal_agency }}
|
||||||
|
{{ application.organization_name }}
|
||||||
{{ application.address_line1 }}{% if application.address_line2 %}
|
{{ application.address_line1 }}{% if application.address_line2 %}
|
||||||
{{ application.address_line2 }}{% endif %}
|
{{ application.address_line2 }}{% endif %}
|
||||||
{{ application.city }}, {{ application.state_territory }}
|
{{ application.city }}, {{ application.state_territory }}
|
||||||
|
@ -22,18 +35,21 @@ Current websites: {% for site in application.current_websites.all %}
|
||||||
{% endfor %}{% endif %}
|
{% endfor %}{% endif %}
|
||||||
.gov domain:
|
.gov domain:
|
||||||
{{ application.requested_domain.name }}
|
{{ application.requested_domain.name }}
|
||||||
|
{% if application.alternative_domains.all %}
|
||||||
|
Alternative domains:
|
||||||
{% for site in application.alternative_domains.all %}{% spaceless %}{{ site.website }}{% endspaceless %}
|
{% for site in application.alternative_domains.all %}{% spaceless %}{{ site.website }}{% endspaceless %}
|
||||||
{% endfor %}
|
{% endfor %}{% endif %}
|
||||||
Purpose of your domain:
|
Purpose of your domain:
|
||||||
{{ application.purpose }}
|
{{ application.purpose }}
|
||||||
|
|
||||||
Your contact information:
|
Your contact information:
|
||||||
{% spaceless %}{% include "emails/includes/contact.txt" with contact=application.submitter %}{% endspaceless %}
|
{% spaceless %}{% include "emails/includes/contact.txt" with contact=application.submitter %}{% endspaceless %}
|
||||||
{% if application.other_contacts.all %}
|
|
||||||
Other employees from your organization:
|
Other employees from your organization:{% for other in application.other_contacts.all %}
|
||||||
{% for other in application.other_contacts.all %}
|
|
||||||
{% spaceless %}{% include "emails/includes/contact.txt" with contact=other %}{% endspaceless %}
|
{% spaceless %}{% include "emails/includes/contact.txt" with contact=other %}{% endspaceless %}
|
||||||
{% endfor %}{% endif %}{% if application.anything_else %}
|
{% empty %}
|
||||||
|
{{ application.no_other_contacts_rationale }}
|
||||||
|
{% endfor %}{% if application.anything_else %}
|
||||||
Anything else?
|
Anything else?
|
||||||
{{ application.anything_else }}
|
{{ application.anything_else }}
|
||||||
{% endif %}
|
{% endif %}
|
|
@ -1,4 +1,7 @@
|
||||||
<address>
|
<address>
|
||||||
|
{% if organization.federal_agency %}
|
||||||
|
{{ organization.federal_agency }}<br />
|
||||||
|
{% endif %}
|
||||||
{% if organization.organization_name %}
|
{% if organization.organization_name %}
|
||||||
{{ organization.organization_name }}
|
{{ organization.organization_name }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
|
@ -28,17 +28,22 @@
|
||||||
{% if value|length == 1 %}
|
{% if value|length == 1 %}
|
||||||
{% include "includes/contact.html" with contact=value|first %}
|
{% include "includes/contact.html" with contact=value|first %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<ul class="usa-list usa-list--unstyled margin-top-0">
|
{% if value %}
|
||||||
|
<dl class="usa-list usa-list--unstyled margin-top-0">
|
||||||
{% for item in value %}
|
{% for item in value %}
|
||||||
<li>
|
<dt>
|
||||||
<p class="text-semibold margin-top-1 margin-bottom-0">
|
|
||||||
Contact {{forloop.counter}}
|
Contact {{forloop.counter}}
|
||||||
</p>
|
</dt>
|
||||||
{% include "includes/contact.html" with contact=item %}</li>
|
<dd>
|
||||||
{% empty %}
|
{% include "includes/contact.html" with contact=item %}
|
||||||
<li>None</li>
|
</dd>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</dl>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
None
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% include "includes/contact.html" with contact=value %}
|
{% include "includes/contact.html" with contact=value %}
|
||||||
|
@ -57,10 +62,10 @@
|
||||||
{% endspaceless %})
|
{% endspaceless %})
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<p class="margin-top-0">{{ value | first }} </p>
|
<p class="margin-top-0 margin-bottom-0">{{ value | first }} </p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<ul class="usa-list margin-top-0">
|
<ul class="usa-list usa-list--unstyled margin-top-0">
|
||||||
{% for item in value %}
|
{% for item in value %}
|
||||||
{% if users %}
|
{% if users %}
|
||||||
<li>{{ item.user.email }}</li>
|
<li>{{ item.user.email }}</li>
|
||||||
|
|
|
@ -526,6 +526,7 @@ def completed_application(
|
||||||
has_anything_else=True,
|
has_anything_else=True,
|
||||||
status=DomainApplication.ApplicationStatus.STARTED,
|
status=DomainApplication.ApplicationStatus.STARTED,
|
||||||
user=False,
|
user=False,
|
||||||
|
submitter=False,
|
||||||
name="city.gov",
|
name="city.gov",
|
||||||
):
|
):
|
||||||
"""A completed domain application."""
|
"""A completed domain application."""
|
||||||
|
@ -541,7 +542,8 @@ def completed_application(
|
||||||
domain, _ = DraftDomain.objects.get_or_create(name=name)
|
domain, _ = DraftDomain.objects.get_or_create(name=name)
|
||||||
alt, _ = Website.objects.get_or_create(website="city1.gov")
|
alt, _ = Website.objects.get_or_create(website="city1.gov")
|
||||||
current, _ = Website.objects.get_or_create(website="city.com")
|
current, _ = Website.objects.get_or_create(website="city.com")
|
||||||
you, _ = Contact.objects.get_or_create(
|
if not submitter:
|
||||||
|
submitter, _ = Contact.objects.get_or_create(
|
||||||
first_name="Testy2",
|
first_name="Testy2",
|
||||||
last_name="Tester2",
|
last_name="Tester2",
|
||||||
title="Admin Tester",
|
title="Admin Tester",
|
||||||
|
@ -567,7 +569,7 @@ def completed_application(
|
||||||
zipcode="10002",
|
zipcode="10002",
|
||||||
authorizing_official=ao,
|
authorizing_official=ao,
|
||||||
requested_domain=domain,
|
requested_domain=domain,
|
||||||
submitter=you,
|
submitter=submitter,
|
||||||
creator=user,
|
creator=user,
|
||||||
status=status,
|
status=status,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1737,7 +1737,86 @@ class ContactAdminTest(TestCase):
|
||||||
|
|
||||||
self.assertEqual(readonly_fields, expected_fields)
|
self.assertEqual(readonly_fields, expected_fields)
|
||||||
|
|
||||||
|
def test_change_view_for_joined_contact_five_or_less(self):
|
||||||
|
"""Create a contact, join it to 4 domain requests. The 5th join will be a user.
|
||||||
|
Assert that the warning on the contact form lists 5 joins."""
|
||||||
|
|
||||||
|
self.client.force_login(self.superuser)
|
||||||
|
|
||||||
|
# Create an instance of the model
|
||||||
|
contact, _ = Contact.objects.get_or_create(user=self.staffuser)
|
||||||
|
|
||||||
|
# join it to 4 domain requests. The 5th join will be a user.
|
||||||
|
application1 = completed_application(submitter=contact, name="city1.gov")
|
||||||
|
application2 = completed_application(submitter=contact, name="city2.gov")
|
||||||
|
application3 = completed_application(submitter=contact, name="city3.gov")
|
||||||
|
application4 = completed_application(submitter=contact, name="city4.gov")
|
||||||
|
|
||||||
|
with patch("django.contrib.messages.warning") as mock_warning:
|
||||||
|
# Use the test client to simulate the request
|
||||||
|
response = self.client.get(reverse("admin:registrar_contact_change", args=[contact.pk]))
|
||||||
|
|
||||||
|
# Assert that the error message was called with the correct argument
|
||||||
|
# Note: The 5th join will be a user.
|
||||||
|
mock_warning.assert_called_once_with(
|
||||||
|
response.wsgi_request,
|
||||||
|
"<ul class='messagelist_content-list--unstyled'>"
|
||||||
|
"<li>Joined to DomainApplication: <a href='/admin/registrar/"
|
||||||
|
f"domainapplication/{application1.pk}/change/'>city1.gov</a></li>"
|
||||||
|
"<li>Joined to DomainApplication: <a href='/admin/registrar/"
|
||||||
|
f"domainapplication/{application2.pk}/change/'>city2.gov</a></li>"
|
||||||
|
"<li>Joined to DomainApplication: <a href='/admin/registrar/"
|
||||||
|
f"domainapplication/{application3.pk}/change/'>city3.gov</a></li>"
|
||||||
|
"<li>Joined to DomainApplication: <a href='/admin/registrar/"
|
||||||
|
f"domainapplication/{application4.pk}/change/'>city4.gov</a></li>"
|
||||||
|
"<li>Joined to User: <a href='/admin/registrar/"
|
||||||
|
f"user/{self.staffuser.pk}/change/'>staff@example.com</a></li>"
|
||||||
|
"</ul>",
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_change_view_for_joined_contact_five_or_more(self):
|
||||||
|
"""Create a contact, join it to 5 domain requests. The 6th join will be a user.
|
||||||
|
Assert that the warning on the contact form lists 5 joins and a '1 more' ellispsis."""
|
||||||
|
|
||||||
|
self.client.force_login(self.superuser)
|
||||||
|
|
||||||
|
# Create an instance of the model
|
||||||
|
# join it to 5 domain requests. The 6th join will be a user.
|
||||||
|
contact, _ = Contact.objects.get_or_create(user=self.staffuser)
|
||||||
|
application1 = completed_application(submitter=contact, name="city1.gov")
|
||||||
|
application2 = completed_application(submitter=contact, name="city2.gov")
|
||||||
|
application3 = completed_application(submitter=contact, name="city3.gov")
|
||||||
|
application4 = completed_application(submitter=contact, name="city4.gov")
|
||||||
|
application5 = completed_application(submitter=contact, name="city5.gov")
|
||||||
|
|
||||||
|
with patch("django.contrib.messages.warning") as mock_warning:
|
||||||
|
# Use the test client to simulate the request
|
||||||
|
response = self.client.get(reverse("admin:registrar_contact_change", args=[contact.pk]))
|
||||||
|
|
||||||
|
logger.info(mock_warning)
|
||||||
|
|
||||||
|
# Assert that the error message was called with the correct argument
|
||||||
|
# Note: The 6th join will be a user.
|
||||||
|
mock_warning.assert_called_once_with(
|
||||||
|
response.wsgi_request,
|
||||||
|
"<ul class='messagelist_content-list--unstyled'>"
|
||||||
|
"<li>Joined to DomainApplication: <a href='/admin/registrar/"
|
||||||
|
f"domainapplication/{application1.pk}/change/'>city1.gov</a></li>"
|
||||||
|
"<li>Joined to DomainApplication: <a href='/admin/registrar/"
|
||||||
|
f"domainapplication/{application2.pk}/change/'>city2.gov</a></li>"
|
||||||
|
"<li>Joined to DomainApplication: <a href='/admin/registrar/"
|
||||||
|
f"domainapplication/{application3.pk}/change/'>city3.gov</a></li>"
|
||||||
|
"<li>Joined to DomainApplication: <a href='/admin/registrar/"
|
||||||
|
f"domainapplication/{application4.pk}/change/'>city4.gov</a></li>"
|
||||||
|
"<li>Joined to DomainApplication: <a href='/admin/registrar/"
|
||||||
|
f"domainapplication/{application5.pk}/change/'>city5.gov</a></li>"
|
||||||
|
"</ul>"
|
||||||
|
"<p class='font-sans-3xs'>And 1 more...</p>",
|
||||||
|
)
|
||||||
|
|
||||||
def tearDown(self):
|
def tearDown(self):
|
||||||
|
DomainApplication.objects.all().delete()
|
||||||
|
Contact.objects.all().delete()
|
||||||
User.objects.all().delete()
|
User.objects.all().delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -102,9 +102,9 @@ class TestEmails(TestCase):
|
||||||
application.submit()
|
application.submit()
|
||||||
_, kwargs = self.mock_client.send_email.call_args
|
_, kwargs = self.mock_client.send_email.call_args
|
||||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||||
self.assertNotIn("Other employees from your organization:", body)
|
|
||||||
# spacing should be right between adjacent elements
|
# spacing should be right between adjacent elements
|
||||||
self.assertRegex(body, r"5556\n\nAnything else")
|
self.assertRegex(body, r"5556\n\nOther employees")
|
||||||
|
self.assertRegex(body, r"None\n\nAnything else")
|
||||||
|
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_submission_confirmation_alternative_govdomain_spacing(self):
|
def test_submission_confirmation_alternative_govdomain_spacing(self):
|
||||||
|
@ -117,7 +117,7 @@ class TestEmails(TestCase):
|
||||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||||
self.assertIn("city1.gov", body)
|
self.assertIn("city1.gov", body)
|
||||||
# spacing should be right between adjacent elements
|
# spacing should be right between adjacent elements
|
||||||
self.assertRegex(body, r"city.gov\ncity1.gov\n\nPurpose of your domain:")
|
self.assertRegex(body, r"city.gov\n\nAlternative domains:\ncity1.gov\n\nPurpose of your domain:")
|
||||||
|
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_submission_confirmation_no_alternative_govdomain_spacing(self):
|
def test_submission_confirmation_no_alternative_govdomain_spacing(self):
|
||||||
|
|
|
@ -64,6 +64,12 @@ class TestFormValidation(MockEppLib):
|
||||||
form = DotGovDomainForm(data={"requested_domain": "top-level-agency"})
|
form = DotGovDomainForm(data={"requested_domain": "top-level-agency"})
|
||||||
self.assertEqual(len(form.errors), 0)
|
self.assertEqual(len(form.errors), 0)
|
||||||
|
|
||||||
|
def test_requested_domain_starting_www(self):
|
||||||
|
"""Test a valid domain name with .www at the beginning."""
|
||||||
|
form = DotGovDomainForm(data={"requested_domain": "www.top-level-agency"})
|
||||||
|
self.assertEqual(len(form.errors), 0)
|
||||||
|
self.assertEqual(form.cleaned_data["requested_domain"], "top-level-agency")
|
||||||
|
|
||||||
def test_requested_domain_ending_dotgov(self):
|
def test_requested_domain_ending_dotgov(self):
|
||||||
"""Just a valid domain name with .gov at the end."""
|
"""Just a valid domain name with .gov at the end."""
|
||||||
form = DotGovDomainForm(data={"requested_domain": "top-level-agency.gov"})
|
form = DotGovDomainForm(data={"requested_domain": "top-level-agency.gov"})
|
||||||
|
|
|
@ -43,6 +43,9 @@ class TestGroups(TestCase):
|
||||||
"change_user",
|
"change_user",
|
||||||
"delete_userdomainrole",
|
"delete_userdomainrole",
|
||||||
"view_userdomainrole",
|
"view_userdomainrole",
|
||||||
|
"add_veryimportantperson",
|
||||||
|
"change_veryimportantperson",
|
||||||
|
"delete_veryimportantperson",
|
||||||
"change_website",
|
"change_website",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -712,7 +712,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
||||||
|
|
||||||
# Review page contains all the previously entered data
|
# Review page contains all the previously entered data
|
||||||
# Let's make sure the long org name is displayed
|
# 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, "Federal")
|
||||||
self.assertContains(review_page, "Executive")
|
self.assertContains(review_page, "Executive")
|
||||||
self.assertContains(review_page, "Testorg")
|
self.assertContains(review_page, "Testorg")
|
||||||
self.assertContains(review_page, "address 1")
|
self.assertContains(review_page, "address 1")
|
||||||
|
@ -2360,18 +2360,6 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
||||||
|
|
||||||
self.assertContains(type_page, "Federal: an agency of the U.S. government")
|
self.assertContains(type_page, "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.ApplicationStatus.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", index=0)
|
|
||||||
self.assertContains(detail_page, "Federal: an agency of the U.S. government")
|
|
||||||
|
|
||||||
def test_submit_modal_no_domain_text_fallback(self):
|
def test_submit_modal_no_domain_text_fallback(self):
|
||||||
"""When user clicks on submit your domain request and the requested domain
|
"""When user clicks on submit your domain request and the requested domain
|
||||||
is null (possible through url direct access to the review page), present
|
is null (possible through url direct access to the review page), present
|
||||||
|
@ -2803,7 +2791,7 @@ class TestDomainManagers(TestDomainOverview):
|
||||||
)
|
)
|
||||||
|
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_domain_invitation_email_has_email_as_requester_non_existent(self):
|
def test_domain_invitation_email_has_email_as_requestor_non_existent(self):
|
||||||
"""Inviting a non existent user sends them an email, with email as the name."""
|
"""Inviting a non existent user sends them an email, with email as the name."""
|
||||||
# make sure there is no user with this email
|
# make sure there is no user with this email
|
||||||
email_address = "mayor@igorville.gov"
|
email_address = "mayor@igorville.gov"
|
||||||
|
@ -2836,13 +2824,13 @@ class TestDomainManagers(TestDomainOverview):
|
||||||
email_content = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
email_content = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||||
self.assertIn("info@example.com", email_content)
|
self.assertIn("info@example.com", email_content)
|
||||||
|
|
||||||
# Check that the requesters first/last name do not exist
|
# Check that the requestors first/last name do not exist
|
||||||
self.assertNotIn("First", email_content)
|
self.assertNotIn("First", email_content)
|
||||||
self.assertNotIn("Last", email_content)
|
self.assertNotIn("Last", email_content)
|
||||||
self.assertNotIn("First Last", email_content)
|
self.assertNotIn("First Last", email_content)
|
||||||
|
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_domain_invitation_email_has_email_as_requester(self):
|
def test_domain_invitation_email_has_email_as_requestor(self):
|
||||||
"""Inviting a user sends them an email, with email as the name."""
|
"""Inviting a user sends them an email, with email as the name."""
|
||||||
# Create a fake user object
|
# Create a fake user object
|
||||||
email_address = "mayor@igorville.gov"
|
email_address = "mayor@igorville.gov"
|
||||||
|
@ -2875,13 +2863,13 @@ class TestDomainManagers(TestDomainOverview):
|
||||||
email_content = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
email_content = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||||
self.assertIn("info@example.com", email_content)
|
self.assertIn("info@example.com", email_content)
|
||||||
|
|
||||||
# Check that the requesters first/last name do not exist
|
# Check that the requestors first/last name do not exist
|
||||||
self.assertNotIn("First", email_content)
|
self.assertNotIn("First", email_content)
|
||||||
self.assertNotIn("Last", email_content)
|
self.assertNotIn("Last", email_content)
|
||||||
self.assertNotIn("First Last", email_content)
|
self.assertNotIn("First Last", email_content)
|
||||||
|
|
||||||
@boto3_mocking.patching
|
@boto3_mocking.patching
|
||||||
def test_domain_invitation_email_has_email_as_requester_staff(self):
|
def test_domain_invitation_email_has_email_as_requestor_staff(self):
|
||||||
"""Inviting a user sends them an email, with email as the name."""
|
"""Inviting a user sends them an email, with email as the name."""
|
||||||
# Create a fake user object
|
# Create a fake user object
|
||||||
email_address = "mayor@igorville.gov"
|
email_address = "mayor@igorville.gov"
|
||||||
|
@ -2918,7 +2906,7 @@ class TestDomainManagers(TestDomainOverview):
|
||||||
email_content = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
email_content = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||||
self.assertIn("help@get.gov", email_content)
|
self.assertIn("help@get.gov", email_content)
|
||||||
|
|
||||||
# Check that the requesters first/last name do not exist
|
# Check that the requestors first/last name do not exist
|
||||||
self.assertNotIn("First", email_content)
|
self.assertNotIn("First", email_content)
|
||||||
self.assertNotIn("Last", email_content)
|
self.assertNotIn("Last", email_content)
|
||||||
self.assertNotIn("First Last", email_content)
|
self.assertNotIn("First Last", email_content)
|
||||||
|
|
|
@ -648,7 +648,7 @@ class DomainAddUserView(DomainFormBaseView):
|
||||||
"""Get an absolute URL for this domain."""
|
"""Get an absolute URL for this domain."""
|
||||||
return self.request.build_absolute_uri(reverse("domain", kwargs={"pk": self.object.id}))
|
return self.request.build_absolute_uri(reverse("domain", kwargs={"pk": self.object.id}))
|
||||||
|
|
||||||
def _send_domain_invitation_email(self, email: str, requester: User, add_success=True):
|
def _send_domain_invitation_email(self, email: str, requestor: User, add_success=True):
|
||||||
"""Performs the sending of the domain invitation email,
|
"""Performs the sending of the domain invitation email,
|
||||||
does not make a domain information object
|
does not make a domain information object
|
||||||
email: string- email to send to
|
email: string- email to send to
|
||||||
|
@ -656,16 +656,16 @@ class DomainAddUserView(DomainFormBaseView):
|
||||||
adding a success message to the view if the email sending succeeds"""
|
adding a success message to the view if the email sending succeeds"""
|
||||||
|
|
||||||
# Set a default email address to send to for staff
|
# Set a default email address to send to for staff
|
||||||
requester_email = "help@get.gov"
|
requestor_email = "help@get.gov"
|
||||||
|
|
||||||
# Check if the email requester has a valid email address
|
# Check if the email requestor has a valid email address
|
||||||
if not requester.is_staff and requester.email is not None and requester.email.strip() != "":
|
if not requestor.is_staff and requestor.email is not None and requestor.email.strip() != "":
|
||||||
requester_email = requester.email
|
requestor_email = requestor.email
|
||||||
elif not requester.is_staff:
|
elif not requestor.is_staff:
|
||||||
messages.error(self.request, "Can't send invitation email. No email is associated with your account.")
|
messages.error(self.request, "Can't send invitation email. No email is associated with your account.")
|
||||||
logger.error(
|
logger.error(
|
||||||
f"Can't send email to '{email}' on domain '{self.object}'."
|
f"Can't send email to '{email}' on domain '{self.object}'."
|
||||||
f"No email exists for the requester '{requester.username}'.",
|
f"No email exists for the requestor '{requestor.username}'.",
|
||||||
exc_info=True,
|
exc_info=True,
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
@ -678,7 +678,7 @@ class DomainAddUserView(DomainFormBaseView):
|
||||||
context={
|
context={
|
||||||
"domain_url": self._domain_abs_url(),
|
"domain_url": self._domain_abs_url(),
|
||||||
"domain": self.object,
|
"domain": self.object,
|
||||||
"requester_email": requester_email,
|
"requestor_email": requestor_email,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
except EmailSendingError:
|
except EmailSendingError:
|
||||||
|
@ -693,7 +693,7 @@ class DomainAddUserView(DomainFormBaseView):
|
||||||
if add_success:
|
if add_success:
|
||||||
messages.success(self.request, f"{email} has been invited to this domain.")
|
messages.success(self.request, f"{email} has been invited to this domain.")
|
||||||
|
|
||||||
def _make_invitation(self, email_address: str, requester: User):
|
def _make_invitation(self, email_address: str, requestor: User):
|
||||||
"""Make a Domain invitation for this email and redirect with a message."""
|
"""Make a Domain invitation for this email and redirect with a message."""
|
||||||
invitation, created = DomainInvitation.objects.get_or_create(email=email_address, domain=self.object)
|
invitation, created = DomainInvitation.objects.get_or_create(email=email_address, domain=self.object)
|
||||||
if not created:
|
if not created:
|
||||||
|
@ -703,22 +703,22 @@ class DomainAddUserView(DomainFormBaseView):
|
||||||
f"{email_address} has already been invited to this domain.",
|
f"{email_address} has already been invited to this domain.",
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self._send_domain_invitation_email(email=email_address, requester=requester)
|
self._send_domain_invitation_email(email=email_address, requestor=requestor)
|
||||||
return redirect(self.get_success_url())
|
return redirect(self.get_success_url())
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
"""Add the specified user on this domain."""
|
"""Add the specified user on this domain."""
|
||||||
requested_email = form.cleaned_data["email"]
|
requested_email = form.cleaned_data["email"]
|
||||||
requester = self.request.user
|
requestor = self.request.user
|
||||||
# look up a user with that email
|
# look up a user with that email
|
||||||
try:
|
try:
|
||||||
requested_user = User.objects.get(email=requested_email)
|
requested_user = User.objects.get(email=requested_email)
|
||||||
except User.DoesNotExist:
|
except User.DoesNotExist:
|
||||||
# no matching user, go make an invitation
|
# no matching user, go make an invitation
|
||||||
return self._make_invitation(requested_email, requester)
|
return self._make_invitation(requested_email, requestor)
|
||||||
else:
|
else:
|
||||||
# if user already exists then just send an email
|
# if user already exists then just send an email
|
||||||
self._send_domain_invitation_email(requested_email, requester, add_success=False)
|
self._send_domain_invitation_email(requested_email, requestor, add_success=False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
UserDomainRole.objects.create(
|
UserDomainRole.objects.create(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue