mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-26 04:28:39 +02:00
Merge branch 'main' into za/813-sort-dropdowns-alphabetically
This commit is contained in:
commit
251b8291f9
19 changed files with 375 additions and 155 deletions
|
@ -277,22 +277,27 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
|||
pass
|
||||
elif obj.status == models.DomainApplication.SUBMITTED:
|
||||
# This is an fsm in model which will throw an error if the
|
||||
# transition condition is violated, so we call it on the
|
||||
# original object which has the right status value, and pass
|
||||
# the updated object which contains the up-to-date data
|
||||
# for the side effects (like an email send). Same
|
||||
# comment applies to original_obj method calls below.
|
||||
original_obj.submit(updated_domain_application=obj)
|
||||
# transition condition is violated, so we roll back the
|
||||
# status to what it was before the admin user changed it and
|
||||
# let the fsm method set it. Same comment applies to
|
||||
# transition method calls below.
|
||||
obj.status = original_obj.status
|
||||
obj.submit()
|
||||
elif obj.status == models.DomainApplication.IN_REVIEW:
|
||||
original_obj.in_review(updated_domain_application=obj)
|
||||
obj.status = original_obj.status
|
||||
obj.in_review()
|
||||
elif obj.status == models.DomainApplication.ACTION_NEEDED:
|
||||
original_obj.action_needed(updated_domain_application=obj)
|
||||
obj.status = original_obj.status
|
||||
obj.action_needed()
|
||||
elif obj.status == models.DomainApplication.APPROVED:
|
||||
original_obj.approve(updated_domain_application=obj)
|
||||
obj.status = original_obj.status
|
||||
obj.approve()
|
||||
elif obj.status == models.DomainApplication.WITHDRAWN:
|
||||
original_obj.withdraw()
|
||||
obj.status = original_obj.status
|
||||
obj.withdraw()
|
||||
elif obj.status == models.DomainApplication.REJECTED:
|
||||
original_obj.reject(updated_domain_application=obj)
|
||||
obj.status = original_obj.status
|
||||
obj.reject()
|
||||
else:
|
||||
logger.warning("Unknown status selected in django admin")
|
||||
|
||||
|
|
|
@ -105,19 +105,29 @@ html[data-theme="light"] {
|
|||
}
|
||||
|
||||
// Dark mode django (bug due to scss cascade) and USWDS tables
|
||||
body,
|
||||
.change-list .usa-table,
|
||||
.change-list .usa-table--striped tbody tr:nth-child(odd) td {
|
||||
color: var(--body-fg)!important;
|
||||
.change-list .usa-table--striped tbody tr:nth-child(odd) td,
|
||||
.change-list .usa-table--borderless thead th,
|
||||
.change-list .usa-table thead td,
|
||||
.change-list .usa-table thead th,
|
||||
body.dashboard,
|
||||
body.change-list,
|
||||
body.change-form {
|
||||
color: var(--body-fg);
|
||||
}
|
||||
}
|
||||
|
||||
// Firefox needs this to be specifically set
|
||||
html[data-theme="dark"] {
|
||||
body,
|
||||
.change-list .usa-table,
|
||||
.change-list .usa-table--striped tbody tr:nth-child(odd) td {
|
||||
color: var(--body-fg)!important;
|
||||
.change-list .usa-table--striped tbody tr:nth-child(odd) td,
|
||||
.change-list .usa-table--borderless thead th,
|
||||
.change-list .usa-table thead td,
|
||||
.change-list .usa-table thead th,
|
||||
body.dashboard,
|
||||
body.change-list,
|
||||
body.change-form {
|
||||
color: var(--body-fg);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ For more information see:
|
|||
https://docs.djangoproject.com/en/4.0/topics/http/urls/
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path
|
||||
from django.views.generic import RedirectView
|
||||
|
@ -45,6 +44,10 @@ for step, view in [
|
|||
|
||||
urlpatterns = [
|
||||
path("", views.index, name="home"),
|
||||
path(
|
||||
"admin/logout/",
|
||||
RedirectView.as_view(pattern_name="logout", permanent=False),
|
||||
),
|
||||
path("admin/", admin.site.urls),
|
||||
path(
|
||||
"application/<id>/edit/",
|
||||
|
@ -114,20 +117,6 @@ urlpatterns = [
|
|||
),
|
||||
]
|
||||
|
||||
|
||||
if not settings.DEBUG:
|
||||
urlpatterns += [
|
||||
# redirect to login.gov
|
||||
path(
|
||||
"admin/login/", RedirectView.as_view(pattern_name="login", permanent=False)
|
||||
),
|
||||
# redirect to login.gov
|
||||
path(
|
||||
"admin/logout/",
|
||||
RedirectView.as_view(pattern_name="logout", permanent=False),
|
||||
),
|
||||
]
|
||||
|
||||
# we normally would guard these with `if settings.DEBUG` but tests run with
|
||||
# DEBUG = False even when these apps have been loaded because settings.DEBUG
|
||||
# was actually True. Instead, let's add these URLs any time we are able to
|
||||
|
|
|
@ -67,6 +67,11 @@ class UserFixture:
|
|||
"first_name": "Paul",
|
||||
"last_name": "Kuykendall",
|
||||
},
|
||||
{
|
||||
"username": "2a88a97b-be96-4aad-b99e-0b605b492c78",
|
||||
"first_name": "Rebecca",
|
||||
"last_name": "Hsieh",
|
||||
},
|
||||
]
|
||||
|
||||
STAFF = [
|
||||
|
@ -74,6 +79,7 @@ class UserFixture:
|
|||
"username": "319c490d-453b-43d9-bc4d-7d6cd8ff6844",
|
||||
"first_name": "Rachid-Analyst",
|
||||
"last_name": "Mrad-Analyst",
|
||||
"email": "rachid.mrad@gmail.com",
|
||||
},
|
||||
{
|
||||
"username": "b6a15987-5c88-4e26-8de2-ca71a0bdb2cd",
|
||||
|
@ -90,6 +96,11 @@ class UserFixture:
|
|||
"first_name": "Paul-Analyst",
|
||||
"last_name": "Kuykendall-Analyst",
|
||||
},
|
||||
{
|
||||
"username": "e474e7a9-71ca-449d-833c-8a6e094dd117",
|
||||
"first_name": "Rebecca-Analyst",
|
||||
"last_name": "Hsieh-Analyst",
|
||||
},
|
||||
]
|
||||
|
||||
STAFF_PERMISSIONS = [
|
||||
|
@ -119,6 +130,8 @@ class UserFixture:
|
|||
user.is_superuser = True
|
||||
user.first_name = admin["first_name"]
|
||||
user.last_name = admin["last_name"]
|
||||
if "email" in admin.keys():
|
||||
user.email = admin["email"]
|
||||
user.is_staff = True
|
||||
user.is_active = True
|
||||
user.save()
|
||||
|
@ -136,6 +149,8 @@ class UserFixture:
|
|||
user.is_superuser = False
|
||||
user.first_name = staff["first_name"]
|
||||
user.last_name = staff["last_name"]
|
||||
if "email" in admin.keys():
|
||||
user.email = admin["email"]
|
||||
user.is_staff = True
|
||||
user.is_active = True
|
||||
|
||||
|
|
|
@ -504,15 +504,10 @@ class DomainApplication(TimeStampedModel):
|
|||
@transition(
|
||||
field="status", source=[STARTED, ACTION_NEEDED, WITHDRAWN], target=SUBMITTED
|
||||
)
|
||||
def submit(self, updated_domain_application=None):
|
||||
def submit(self):
|
||||
"""Submit an application that is started.
|
||||
|
||||
As a side effect, an email notification is sent.
|
||||
|
||||
This method is called in admin.py on the original application
|
||||
which has the correct status value, but is passed the changed
|
||||
application which has the up-to-date data that we'll use
|
||||
in the email."""
|
||||
As a side effect, an email notification is sent."""
|
||||
|
||||
# check our conditions here inside the `submit` method so that we
|
||||
# can raise more informative exceptions
|
||||
|
@ -528,46 +523,31 @@ class DomainApplication(TimeStampedModel):
|
|||
if not DraftDomain.string_could_be_domain(self.requested_domain.name):
|
||||
raise ValueError("Requested domain is not a valid domain name.")
|
||||
|
||||
if updated_domain_application is not None:
|
||||
# A DomainApplication is being passed to this method (ie from admin)
|
||||
updated_domain_application._send_status_update_email(
|
||||
"submission confirmation",
|
||||
"emails/submission_confirmation.txt",
|
||||
"emails/submission_confirmation_subject.txt",
|
||||
)
|
||||
else:
|
||||
# Or this method is called with the right application
|
||||
# for context, ie from views/application.py
|
||||
self._send_status_update_email(
|
||||
"submission confirmation",
|
||||
"emails/submission_confirmation.txt",
|
||||
"emails/submission_confirmation_subject.txt",
|
||||
)
|
||||
self._send_status_update_email(
|
||||
"submission confirmation",
|
||||
"emails/submission_confirmation.txt",
|
||||
"emails/submission_confirmation_subject.txt",
|
||||
)
|
||||
|
||||
@transition(field="status", source=SUBMITTED, target=IN_REVIEW)
|
||||
def in_review(self, updated_domain_application):
|
||||
def in_review(self):
|
||||
"""Investigate an application that has been submitted.
|
||||
|
||||
As a side effect, an email notification is sent.
|
||||
As a side effect, an email notification is sent."""
|
||||
|
||||
This method is called in admin.py on the original application
|
||||
which has the correct status value, but is passed the changed
|
||||
application which has the up-to-date data that we'll use
|
||||
in the email."""
|
||||
|
||||
updated_domain_application._send_status_update_email(
|
||||
self._send_status_update_email(
|
||||
"application in review",
|
||||
"emails/status_change_in_review.txt",
|
||||
"emails/status_change_in_review_subject.txt",
|
||||
)
|
||||
|
||||
@transition(field="status", source=[IN_REVIEW, REJECTED], target=ACTION_NEEDED)
|
||||
def action_needed(self, updated_domain_application):
|
||||
def action_needed(self):
|
||||
"""Send back an application that is under investigation or rejected.
|
||||
|
||||
As a side effect, an email notification is sent, similar to in_review"""
|
||||
As a side effect, an email notification is sent."""
|
||||
|
||||
updated_domain_application._send_status_update_email(
|
||||
self._send_status_update_email(
|
||||
"action needed",
|
||||
"emails/status_change_action_needed.txt",
|
||||
"emails/status_change_action_needed_subject.txt",
|
||||
|
@ -576,19 +556,13 @@ class DomainApplication(TimeStampedModel):
|
|||
@transition(
|
||||
field="status", source=[SUBMITTED, IN_REVIEW, REJECTED], target=APPROVED
|
||||
)
|
||||
def approve(self, updated_domain_application=None):
|
||||
def approve(self):
|
||||
"""Approve an application that has been submitted.
|
||||
|
||||
This has substantial side-effects because it creates another database
|
||||
object for the approved Domain and makes the user who created the
|
||||
application into an admin on that domain. It also triggers an email
|
||||
notification.
|
||||
|
||||
This method is called in admin.py on the original application
|
||||
which has the correct status value, but is passed the changed
|
||||
application which has the up-to-date data that we'll use
|
||||
in the email.
|
||||
"""
|
||||
notification."""
|
||||
|
||||
# create the domain
|
||||
Domain = apps.get_model("registrar.Domain")
|
||||
|
@ -607,31 +581,23 @@ class DomainApplication(TimeStampedModel):
|
|||
user=self.creator, domain=created_domain, role=UserDomainRole.Roles.ADMIN
|
||||
)
|
||||
|
||||
if updated_domain_application is not None:
|
||||
# A DomainApplication is being passed to this method (ie from admin)
|
||||
updated_domain_application._send_status_update_email(
|
||||
"application approved",
|
||||
"emails/status_change_approved.txt",
|
||||
"emails/status_change_approved_subject.txt",
|
||||
)
|
||||
else:
|
||||
self._send_status_update_email(
|
||||
"application approved",
|
||||
"emails/status_change_approved.txt",
|
||||
"emails/status_change_approved_subject.txt",
|
||||
)
|
||||
self._send_status_update_email(
|
||||
"application approved",
|
||||
"emails/status_change_approved.txt",
|
||||
"emails/status_change_approved_subject.txt",
|
||||
)
|
||||
|
||||
@transition(field="status", source=[SUBMITTED, IN_REVIEW], target=WITHDRAWN)
|
||||
def withdraw(self):
|
||||
"""Withdraw an application that has been submitted."""
|
||||
|
||||
@transition(field="status", source=[IN_REVIEW, APPROVED], target=REJECTED)
|
||||
def reject(self, updated_domain_application):
|
||||
def reject(self):
|
||||
"""Reject an application that has been submitted.
|
||||
|
||||
As a side effect, an email notification is sent, similar to in_review"""
|
||||
|
||||
updated_domain_application._send_status_update_email(
|
||||
self._send_status_update_email(
|
||||
"action needed",
|
||||
"emails/status_change_rejected.txt",
|
||||
"emails/status_change_rejected_subject.txt",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{% extends "admin/base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
|
||||
|
||||
|
@ -13,5 +14,25 @@
|
|||
{% include "admin/color_theme_toggle.html" %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% comment %}
|
||||
This was copied from the 'userlinks' template, with a few minor changes.
|
||||
You can find that here:
|
||||
https://github.com/django/django/blob/d25f3892114466d689fd6936f79f3bd9a9acc30e/django/contrib/admin/templates/admin/base.html#L59
|
||||
{% endcomment %}
|
||||
{% block userlinks %}
|
||||
{% if site_url %}
|
||||
<a href="{{ site_url }}">{% translate 'View site' %}</a> /
|
||||
{% endif %}
|
||||
{% if user.is_active and user.is_staff %}
|
||||
{% url 'django-admindocs-docroot' as docsroot %}
|
||||
{% if docsroot %}
|
||||
<a href="{{ docsroot }}">{% translate 'Documentation' %}</a> /
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if user.has_usable_password %}
|
||||
<a href="{% url 'admin:password_change' %}">{% translate 'Change password' %}</a> /
|
||||
{% endif %}
|
||||
<a href="{% url 'admin:logout' %}" id="admin-logout-button">{% translate 'Log out' %}</a>
|
||||
{% include "admin/color_theme_toggle.html" %}
|
||||
{% endblock %}
|
||||
{% block nav-global %}{% endblock %}
|
|
@ -17,53 +17,45 @@ Load our custom filters to extract info from the django generated markup.
|
|||
<thead>
|
||||
<tr>
|
||||
|
||||
{# .gov - hardcode the select all checkbox #}
|
||||
<th scope="col" class="action-checkbox-column" title="Toggle all">
|
||||
<div class="text">
|
||||
<span>
|
||||
<input type="checkbox" name="_selected_action" id="action-toggle">
|
||||
<label for="action-toggle" class="usa-sr-only">Toggle all</label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</th>
|
||||
{# .gov - don't let django generate the select all checkbox #}
|
||||
{% for header in result_headers|slice:"1:" %}
|
||||
{% if results.0.form %}
|
||||
{# .gov - hardcode the select all checkbox #}
|
||||
<th scope="col" class="action-checkbox-column" title="Toggle all">
|
||||
<div class="text">
|
||||
<span>
|
||||
<input type="checkbox" name="_selected_action" id="action-toggle">
|
||||
<label for="action-toggle" class="usa-sr-only">Toggle all</label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</th>
|
||||
{# .gov - don't let django generate the select all checkbox #}
|
||||
{% for header in result_headers|slice:"1:" %}
|
||||
<th scope="col"{{ header.class_attrib }}>
|
||||
{% if header.sortable %}
|
||||
{% if header.sort_priority > 0 %}
|
||||
<div class="sortoptions">
|
||||
<a class="sortremove" href="{{ header.url_remove }}" title="{% translate "Remove from sorting" %}"></a>
|
||||
{% if num_sorted_fields > 1 %}<span class="sortpriority" title="{% blocktranslate with priority_number=header.sort_priority %}Sorting priority: {{ priority_number }}{% endblocktranslate %}">{{ header.sort_priority }}</span>{% endif %}
|
||||
<a href="{{ header.url_toggle }}" class="toggle {% if header.ascending %}ascending{% else %}descending{% endif %}" title="{% translate "Toggle sorting" %}"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class="text">{% if header.sortable %}<a href="{{ header.url_primary }}">{{ header.text|capfirst }}</a>{% else %}<span>{{ header.text|capfirst }}</span>{% endif %}</div>
|
||||
<div class="clear"></div>
|
||||
</th>{% endfor %}
|
||||
|
||||
<th scope="col"{{ header.class_attrib }}>
|
||||
{% if header.sortable %}
|
||||
{% if header.sort_priority > 0 %}
|
||||
<div class="sortoptions">
|
||||
<a class="sortremove" href="{{ header.url_remove }}" title="{% translate "Remove from sorting" %}"></a>
|
||||
{% if num_sorted_fields > 1 %}<span class="sortpriority" title="{% blocktranslate with priority_number=header.sort_priority %}Sorting priority: {{ priority_number }}{% endblocktranslate %}">{{ header.sort_priority }}</span>{% endif %}
|
||||
<a href="{{ header.url_toggle }}" class="toggle {% if header.ascending %}ascending{% else %}descending{% endif %}" title="{% translate "Toggle sorting" %}"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class="text">{% if header.sortable %}<a href="{{ header.url_primary }}">{{ header.text|capfirst }}</a>{% else %}<span>{{ header.text|capfirst }}</span>{% endif %}</div>
|
||||
<div class="clear"></div>
|
||||
</th>{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% comment %}
|
||||
.gov - hardcode the row checkboxes using the custom filters to extract
|
||||
the value attribute's value, and a label based on the anchor elements's
|
||||
text. Then edit the for loop to keep django from generating the row select
|
||||
checkboxes.
|
||||
{% endcomment %}
|
||||
|
||||
{% comment %}
|
||||
{% for result in results %}
|
||||
{% if result.form.non_field_errors %}
|
||||
<tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
|
||||
{% endif %}
|
||||
<tr>{% for item in result %}{{ item }}{% endfor %}</tr>
|
||||
{% endfor %}
|
||||
{% endcomment %}
|
||||
|
||||
{% comment %}
|
||||
.gov - hardcode the row checkboxes using the custom filters to extract
|
||||
the value attribute's value, and a label based on the anchor elements's
|
||||
text. Then edit the for loop to keep django from generating the row select
|
||||
checkboxes.
|
||||
{% endcomment %}
|
||||
{% for result in results %}
|
||||
{% for result in results %}
|
||||
{% if result.form.non_field_errors %}
|
||||
<tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
|
||||
{% endif %}
|
||||
|
@ -82,7 +74,37 @@ checkboxes.
|
|||
{{ item }}
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
{% else %} {# results doesn't have a form as its first element #}
|
||||
|
||||
{% for header in result_headers %}
|
||||
<th scope="col"{{ header.class_attrib }}>
|
||||
{% if header.sortable %}
|
||||
{% if header.sort_priority > 0 %}
|
||||
<div class="sortoptions">
|
||||
<a class="sortremove" href="{{ header.url_remove }}" title="{% translate "Remove from sorting" %}"></a>
|
||||
{% if num_sorted_fields > 1 %}<span class="sortpriority" title="{% blocktranslate with priority_number=header.sort_priority %}Sorting priority: {{ priority_number }}{% endblocktranslate %}">{{ header.sort_priority }}</span>{% endif %}
|
||||
<a href="{{ header.url_toggle }}" class="toggle {% if header.ascending %}ascending{% else %}descending{% endif %}" title="{% translate "Toggle sorting" %}"></a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class="text">{% if header.sortable %}<a href="{{ header.url_primary }}">{{ header.text|capfirst }}</a>{% else %}<span>{{ header.text|capfirst }}</span>{% endif %}</div>
|
||||
<div class="clear"></div>
|
||||
</th>{% endfor %}
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
||||
{% for result in results %}
|
||||
{% if result.form.non_field_errors %}
|
||||
<tr><td colspan="{{ result|length }}">{{ result.form.non_field_errors }}</td></tr>
|
||||
{% endif %}
|
||||
<tr>{% for item in result %}{{ item }}{% endfor %}</tr>
|
||||
{% endfor %}
|
||||
|
||||
{% endif %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
@ -6,13 +6,13 @@
|
|||
Who is the authorizing official for your organization?
|
||||
</h2>
|
||||
|
||||
<p>Your authorizing official is the person within your organization who can authorize your domain request. This is generally the highest-ranking or highest-elected official in your organization.</p>
|
||||
<p>Your authorizing official is the person within your organization who can authorize your domain request. This person must be in a role of significant, executive responsibility within the organization.</p>
|
||||
|
||||
<div class="ao_example">
|
||||
{% include "includes/ao_example.html" %}
|
||||
</div>
|
||||
|
||||
<p>We might contact your authorizing official, or their office, to double check that they approve this request. Read more about <a href="{% public_site_url 'domains/eligibility/#you-must-have-approval-from-an-authorizing-official-within-your-organization' %}">who can serve as an authorizing official</a>.</p>
|
||||
<p>We typically don’t reach out to the authorizing official, but if contact is necessary, our practice is to coordinate first with you, the requestor. Read more about <a href="{% public_site_url 'domains/eligibility/#you-must-have-approval-from-an-authorizing-official-within-your-organization' %}">who can serve as an authorizing official</a>.</p>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -2,7 +2,13 @@
|
|||
{% load static field_helpers %}
|
||||
|
||||
{% block form_instructions %}
|
||||
<p>We’d like to contact other employees in your organization about your domain request. For example, they could be involved in managing your organization or its technical infrastructure. <strong>This information will help us assess your eligibility for a .gov domain.</strong> These contacts should be in addition to you and your authorizing official. They should be employees of your organization.</p>
|
||||
<p>To help us assess your eligibility for a .gov domain, please provide contact information for other employees from your organization.
|
||||
<ul class="usa-list">
|
||||
<li>They should be clearly and publicly affiliated with your organization and familiar with your domain request. </li>
|
||||
<li>They don't need to be involved with the technical management of your domain (although they can be). </li>
|
||||
<li>We typically don’t reach out to these employees, but if contact is necessary, our practice is to coordinate first with you, the requestor. </li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
{% if domainapplication.status == 'approved' %} Approved
|
||||
{% elif domainapplication.status == 'in review' %} In Review
|
||||
{% elif domainapplication.status == 'rejected' %} Rejected
|
||||
{% elif domainapplication.status == 'submitted' %} Received
|
||||
{% elif domainapplication.status == 'submitted' %} Submitted
|
||||
{% else %}ERROR Please contact technical support/dev
|
||||
{% endif %}
|
||||
</p>
|
||||
|
|
|
@ -125,13 +125,14 @@
|
|||
<p>You don't have any archived domains</p>
|
||||
</section>
|
||||
|
||||
<section class="tablet:grid-col-11 desktop:grid-col-10">
|
||||
<!-- Note: Uncomment below when this is being implemented post-MVP -->
|
||||
<!-- <section class="tablet:grid-col-11 desktop:grid-col-10">
|
||||
<h2 class="padding-top-1 mobile-lg:padding-top-3"> Export domains</h2>
|
||||
<p>Download a list of your domains and their statuses as a csv file.</p>
|
||||
<a href="{% url 'todo' %}" class="usa-button usa-button--outline">
|
||||
Export domains as csv
|
||||
</a>
|
||||
</section>
|
||||
</section> -->
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -174,6 +174,33 @@ class TestDomainApplicationAdmin(TestCase):
|
|||
# Perform assertions on the mock call itself
|
||||
mock_client_instance.send_email.assert_called_once()
|
||||
|
||||
def test_save_model_sets_approved_domain(self):
|
||||
# make sure there is no user with this email
|
||||
EMAIL = "mayor@igorville.gov"
|
||||
User.objects.filter(email=EMAIL).delete()
|
||||
|
||||
# Create a sample application
|
||||
application = completed_application(status=DomainApplication.IN_REVIEW)
|
||||
|
||||
# Create a mock request
|
||||
request = self.factory.post(
|
||||
"/admin/registrar/domainapplication/{}/change/".format(application.pk)
|
||||
)
|
||||
|
||||
# Create an instance of the model admin
|
||||
model_admin = DomainApplicationAdmin(DomainApplication, self.site)
|
||||
|
||||
# Modify the application's property
|
||||
application.status = DomainApplication.APPROVED
|
||||
|
||||
# Use the model admin's save_model method
|
||||
model_admin.save_model(request, application, form=None, change=True)
|
||||
|
||||
# Test that approved domain exists and equals requested domain
|
||||
self.assertEqual(
|
||||
application.requested_domain.name, application.approved_domain.name
|
||||
)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_save_model_sends_action_needed_email(self):
|
||||
# make sure there is no user with this email
|
||||
|
|
|
@ -7,7 +7,7 @@ certifi==2023.7.22 ; python_version >= '3.6'
|
|||
cfenv==0.5.3
|
||||
cffi==1.15.1
|
||||
charset-normalizer==3.1.0 ; python_full_version >= '3.7.0'
|
||||
cryptography==41.0.2 ; python_version >= '3.7'
|
||||
cryptography==41.0.3 ; python_version >= '3.7'
|
||||
defusedxml==0.7.1 ; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'
|
||||
dj-database-url==2.0.0
|
||||
dj-email-url==1.0.6
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
10027 OUTOFSCOPE http://app:8080/public/js/uswds-init.min.js
|
||||
# get-gov.js contains suspicious word "from" as in `Array.from()`
|
||||
10027 OUTOFSCOPE http://app:8080/public/js/get-gov.js
|
||||
# Ignore wording of "TODO"
|
||||
10027 OUTOFSCOPE http://app:8080.*$
|
||||
10028 FAIL (Open Redirect - Passive/beta)
|
||||
10029 FAIL (Cookie Poisoning - Passive/beta)
|
||||
10030 FAIL (User Controllable Charset - Passive/beta)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue