mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-06-08 21:44:43 +02:00
Merge branch 'main' into za/1212-extend-expiration-dates
This commit is contained in:
commit
0ee7c598f8
58 changed files with 2190 additions and 137 deletions
|
@ -1,7 +1,7 @@
|
|||
"""Internal API views"""
|
||||
from django.apps import apps
|
||||
from django.views.decorators.http import require_http_methods
|
||||
from django.http import JsonResponse
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from registrar.templatetags.url_helpers import public_site_url
|
||||
|
@ -13,6 +13,8 @@ from login_required import login_not_required
|
|||
|
||||
from cachetools.func import ttl_cache
|
||||
|
||||
from registrar.utility.s3_bucket import S3ClientError, S3ClientHelper
|
||||
|
||||
|
||||
DOMAIN_FILE_URL = "https://raw.githubusercontent.com/cisagov/dotgov-data/main/current-full.csv"
|
||||
|
||||
|
@ -95,3 +97,36 @@ def available(request, domain=""):
|
|||
return JsonResponse({"available": False, "message": DOMAIN_API_MESSAGES["unavailable"]})
|
||||
except Exception:
|
||||
return JsonResponse({"available": False, "message": DOMAIN_API_MESSAGES["error"]})
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
@login_not_required
|
||||
def get_current_full(request, file_name="current-full.csv"):
|
||||
"""This will return the file content of current-full.csv which is the command
|
||||
output of generate_current_full_report.py. This command iterates through each Domain
|
||||
and returns a CSV representation."""
|
||||
return serve_file(file_name)
|
||||
|
||||
|
||||
@require_http_methods(["GET"])
|
||||
@login_not_required
|
||||
def get_current_federal(request, file_name="current-federal.csv"):
|
||||
"""This will return the file content of current-federal.csv which is the command
|
||||
output of generate_current_federal_report.py. This command iterates through each Domain
|
||||
and returns a CSV representation."""
|
||||
return serve_file(file_name)
|
||||
|
||||
|
||||
def serve_file(file_name):
|
||||
"""Downloads a file based on a given filepath. Returns a 500 if not found."""
|
||||
s3_client = S3ClientHelper()
|
||||
# Serve the CSV file. If not found, an exception will be thrown.
|
||||
# This will then be caught by flat, causing it to not read it - which is what we want.
|
||||
try:
|
||||
file = s3_client.get_file(file_name, decode_to_utf=True)
|
||||
except S3ClientError as err:
|
||||
# TODO - #1317: Notify operations when auto report generation fails
|
||||
raise err
|
||||
|
||||
response = HttpResponse(file)
|
||||
return response
|
||||
|
|
|
@ -51,6 +51,11 @@ services:
|
|||
# AWS credentials
|
||||
- AWS_ACCESS_KEY_ID
|
||||
- AWS_SECRET_ACCESS_KEY
|
||||
# AWS S3 bucket credentials
|
||||
- AWS_S3_ACCESS_KEY_ID
|
||||
- AWS_S3_SECRET_ACCESS_KEY
|
||||
- AWS_S3_REGION
|
||||
- AWS_S3_BUCKET_NAME
|
||||
stdin_open: true
|
||||
tty: true
|
||||
ports:
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import logging
|
||||
from django import forms
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import redirect
|
||||
from django_fsm import get_available_FIELD_transitions
|
||||
from django.contrib import admin, messages
|
||||
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
|
||||
|
@ -117,6 +118,15 @@ class ListHeaderAdmin(AuditedAdmin):
|
|||
)
|
||||
return filters
|
||||
|
||||
# customize the help_text for all formfields for manytomany
|
||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||
formfield = super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||
formfield.help_text = (
|
||||
formfield.help_text
|
||||
+ " If more than one value is selected, the change/delete/view actions will be disabled."
|
||||
)
|
||||
return formfield
|
||||
|
||||
|
||||
class UserContactInline(admin.StackedInline):
|
||||
"""Edit a user's profile on the user page."""
|
||||
|
@ -351,6 +361,17 @@ class UserDomainRoleAdmin(ListHeaderAdmin):
|
|||
|
||||
autocomplete_fields = ["user", "domain"]
|
||||
|
||||
# Fixes a bug where non-superusers are redirected to the main page
|
||||
def delete_view(self, request, object_id, extra_context=None):
|
||||
"""Custom delete_view implementation that specifies redirect behaviour"""
|
||||
response = super().delete_view(request, object_id, extra_context)
|
||||
|
||||
if isinstance(response, HttpResponseRedirect) and not request.user.has_perm("registrar.full_access_permission"):
|
||||
url = reverse("admin:registrar_userdomainrole_changelist")
|
||||
return redirect(url)
|
||||
else:
|
||||
return response
|
||||
|
||||
|
||||
class DomainInvitationAdmin(ListHeaderAdmin):
|
||||
"""Custom domain invitation admin class."""
|
||||
|
@ -445,7 +466,7 @@ class DomainInformationAdmin(ListHeaderAdmin):
|
|||
"No other employees from your organization?",
|
||||
{"fields": ["no_other_contacts_rationale"]},
|
||||
),
|
||||
("Anything else we should know?", {"fields": ["anything_else"]}),
|
||||
("Anything else?", {"fields": ["anything_else"]}),
|
||||
(
|
||||
"Requirements for operating .gov domains",
|
||||
{"fields": ["is_policy_acknowledged"]},
|
||||
|
@ -464,6 +485,17 @@ class DomainInformationAdmin(ListHeaderAdmin):
|
|||
"is_policy_acknowledged",
|
||||
]
|
||||
|
||||
# For each filter_horizontal, init in admin js extendFilterHorizontalWidgets
|
||||
# to activate the edit/delete/view buttons
|
||||
filter_horizontal = ("other_contacts",)
|
||||
|
||||
# lists in filter_horizontal are not sorted properly, sort them
|
||||
# by first_name
|
||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||
if db_field.name in ("other_contacts",):
|
||||
kwargs["queryset"] = models.Contact.objects.all().order_by("first_name") # Sort contacts
|
||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||
|
||||
def get_readonly_fields(self, request, obj=None):
|
||||
"""Set the read-only state on form elements.
|
||||
We have 1 conditions that determine which fields are read-only:
|
||||
|
@ -580,7 +612,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
|||
"No other employees from your organization?",
|
||||
{"fields": ["no_other_contacts_rationale"]},
|
||||
),
|
||||
("Anything else we should know?", {"fields": ["anything_else"]}),
|
||||
("Anything else?", {"fields": ["anything_else"]}),
|
||||
(
|
||||
"Requirements for operating .gov domains",
|
||||
{"fields": ["is_policy_acknowledged"]},
|
||||
|
@ -600,6 +632,15 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
|||
"is_policy_acknowledged",
|
||||
]
|
||||
|
||||
filter_horizontal = ("current_websites", "alternative_domains", "other_contacts")
|
||||
|
||||
# lists in filter_horizontal are not sorted properly, sort them
|
||||
# by website
|
||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||
if db_field.name in ("current_websites", "alternative_domains"):
|
||||
kwargs["queryset"] = models.Website.objects.all().order_by("website") # Sort websites
|
||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||
|
||||
# Trigger action when a fieldset is changed
|
||||
def save_model(self, request, obj, form, change):
|
||||
if obj and obj.creator.status != models.User.RESTRICTED:
|
||||
|
@ -728,6 +769,16 @@ class DomainInformationInline(admin.StackedInline):
|
|||
|
||||
fieldsets = DomainInformationAdmin.fieldsets
|
||||
analyst_readonly_fields = DomainInformationAdmin.analyst_readonly_fields
|
||||
# For each filter_horizontal, init in admin js extendFilterHorizontalWidgets
|
||||
# to activate the edit/delete/view buttons
|
||||
filter_horizontal = ("other_contacts",)
|
||||
|
||||
# lists in filter_horizontal are not sorted properly, sort them
|
||||
# by first_name
|
||||
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||
if db_field.name in ("other_contacts",):
|
||||
kwargs["queryset"] = models.Contact.objects.all().order_by("first_name") # Sort contacts
|
||||
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||
|
||||
def get_readonly_fields(self, request, obj=None):
|
||||
return DomainInformationAdmin.get_readonly_fields(self, request, obj=None)
|
||||
|
|
|
@ -47,4 +47,231 @@ function openInNewTab(el, removeAttribute = false){
|
|||
}
|
||||
|
||||
prepareDjangoAdmin();
|
||||
})();
|
||||
})();
|
||||
|
||||
/**
|
||||
* An IIFE to listen to changes on filter_horizontal and enable or disable the change/delete/view buttons as applicable
|
||||
*
|
||||
*/
|
||||
(function extendFilterHorizontalWidgets() {
|
||||
// Initialize custom filter_horizontal widgets; each widget has a "from" select list
|
||||
// and a "to" select list; initialization is based off of the presence of the
|
||||
// "to" select list
|
||||
checkToListThenInitWidget('id_other_contacts_to', 0);
|
||||
checkToListThenInitWidget('id_domain_info-0-other_contacts_to', 0);
|
||||
checkToListThenInitWidget('id_current_websites_to', 0);
|
||||
checkToListThenInitWidget('id_alternative_domains_to', 0);
|
||||
})();
|
||||
|
||||
// Function to check for the existence of the "to" select list element in the DOM, and if and when found,
|
||||
// initialize the associated widget
|
||||
function checkToListThenInitWidget(toListId, attempts) {
|
||||
let toList = document.getElementById(toListId);
|
||||
attempts++;
|
||||
|
||||
if (attempts < 6) {
|
||||
if ((toList !== null)) {
|
||||
// toList found, handle it
|
||||
// Add an event listener on the element
|
||||
// Add disabled buttons on the element's great-grandparent
|
||||
initializeWidgetOnToList(toList, toListId);
|
||||
} else {
|
||||
// Element not found, check again after a delay
|
||||
setTimeout(() => checkToListThenInitWidget(toListId, attempts), 1000); // Check every 1000 milliseconds (1 second)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the widget:
|
||||
// add related buttons to the widget for edit, delete and view
|
||||
// add event listeners on the from list, the to list, and selector buttons which either enable or disable the related buttons
|
||||
function initializeWidgetOnToList(toList, toListId) {
|
||||
// create the change button
|
||||
let changeLink = createAndCustomizeLink(
|
||||
toList,
|
||||
toListId,
|
||||
'related-widget-wrapper-link change-related',
|
||||
'Change',
|
||||
'/public/admin/img/icon-changelink.svg',
|
||||
{
|
||||
'contacts': '/admin/registrar/contact/__fk__/change/?_to_field=id&_popup=1',
|
||||
'websites': '/admin/registrar/website/__fk__/change/?_to_field=id&_popup=1',
|
||||
'alternative_domains': '/admin/registrar/website/__fk__/change/?_to_field=id&_popup=1',
|
||||
},
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
let hasDeletePermission = hasDeletePermissionOnPage();
|
||||
|
||||
let deleteLink = null;
|
||||
if (hasDeletePermission) {
|
||||
// create the delete button if user has permission to delete
|
||||
deleteLink = createAndCustomizeLink(
|
||||
toList,
|
||||
toListId,
|
||||
'related-widget-wrapper-link delete-related',
|
||||
'Delete',
|
||||
'/public/admin/img/icon-deletelink.svg',
|
||||
{
|
||||
'contacts': '/admin/registrar/contact/__fk__/delete/?_to_field=id&_popup=1',
|
||||
'websites': '/admin/registrar/website/__fk__/delete/?_to_field=id&_popup=1',
|
||||
'alternative_domains': '/admin/registrar/website/__fk__/delete/?_to_field=id&_popup=1',
|
||||
},
|
||||
true,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
// create the view button
|
||||
let viewLink = createAndCustomizeLink(
|
||||
toList,
|
||||
toListId,
|
||||
'related-widget-wrapper-link view-related',
|
||||
'View',
|
||||
'/public/admin/img/icon-viewlink.svg',
|
||||
{
|
||||
'contacts': '/admin/registrar/contact/__fk__/change/?_to_field=id',
|
||||
'websites': '/admin/registrar/website/__fk__/change/?_to_field=id',
|
||||
'alternative_domains': '/admin/registrar/website/__fk__/change/?_to_field=id',
|
||||
},
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
// identify the fromList element in the DOM
|
||||
let fromList = toList.closest('.selector').querySelector(".selector-available select");
|
||||
|
||||
fromList.addEventListener('click', function(event) {
|
||||
handleSelectClick(fromList, changeLink, deleteLink, viewLink);
|
||||
});
|
||||
|
||||
toList.addEventListener('click', function(event) {
|
||||
handleSelectClick(toList, changeLink, deleteLink, viewLink);
|
||||
});
|
||||
|
||||
// Disable buttons when the selectors are interacted with (items are moved from one column to the other)
|
||||
let selectorButtons = [];
|
||||
selectorButtons.push(toList.closest(".selector").querySelector(".selector-chooseall"));
|
||||
selectorButtons.push(toList.closest(".selector").querySelector(".selector-add"));
|
||||
selectorButtons.push(toList.closest(".selector").querySelector(".selector-remove"));
|
||||
|
||||
selectorButtons.forEach((selector) => {
|
||||
selector.addEventListener("click", ()=>{disableRelatedWidgetButtons(changeLink, deleteLink, viewLink)});
|
||||
});
|
||||
}
|
||||
|
||||
// create and customize the button, then add to the DOM, relative to the toList
|
||||
// toList - the element in the DOM for the toList
|
||||
// toListId - the ID of the element in the DOM
|
||||
// className - className to add to the created link
|
||||
// action - the action to perform on the item {change, delete, view}
|
||||
// imgSrc - the img.src for the created link
|
||||
// dataMappings - dictionary which relates toListId to href for the created link
|
||||
// dataPopup - boolean for whether the link should produce a popup window
|
||||
// firstPosition - boolean indicating if link should be first position in list of links, otherwise, should be last link
|
||||
function createAndCustomizeLink(toList, toListId, className, action, imgSrc, dataMappings, dataPopup, firstPosition) {
|
||||
// Create a link element
|
||||
var link = document.createElement('a');
|
||||
|
||||
// Set class attribute for the link
|
||||
link.className = className;
|
||||
|
||||
// Set id
|
||||
// Determine function {change, link, view} from the className
|
||||
// Add {function}_ to the beginning of the string
|
||||
let modifiedLinkString = className.split('-')[0] + '_' + toListId;
|
||||
// Remove '_to' from the end of the string
|
||||
modifiedLinkString = modifiedLinkString.replace('_to', '');
|
||||
link.id = modifiedLinkString;
|
||||
|
||||
// Set data-href-template
|
||||
for (const [idPattern, template] of Object.entries(dataMappings)) {
|
||||
if (toListId.includes(idPattern)) {
|
||||
link.setAttribute('data-href-template', template);
|
||||
break; // Stop checking once a match is found
|
||||
}
|
||||
}
|
||||
|
||||
if (dataPopup)
|
||||
link.setAttribute('data-popup', 'yes');
|
||||
|
||||
link.setAttribute('title-template', action + " selected item")
|
||||
link.title = link.getAttribute('title-template');
|
||||
|
||||
// Create an 'img' element
|
||||
var img = document.createElement('img');
|
||||
|
||||
// Set attributes for the new image
|
||||
img.src = imgSrc;
|
||||
img.alt = action;
|
||||
|
||||
// Append the image to the link
|
||||
link.appendChild(img);
|
||||
|
||||
let relatedWidgetWrapper = toList.closest('.related-widget-wrapper');
|
||||
// If firstPosition is true, insert link as the first child element
|
||||
if (firstPosition) {
|
||||
relatedWidgetWrapper.insertBefore(link, relatedWidgetWrapper.children[0]);
|
||||
} else {
|
||||
// otherwise, insert the link prior to the last child (which is a div)
|
||||
// and also prior to any text elements immediately preceding the last
|
||||
// child node
|
||||
var lastChild = relatedWidgetWrapper.lastChild;
|
||||
|
||||
// Check if lastChild is an element node (not a text node, comment, etc.)
|
||||
if (lastChild.nodeType === 1) {
|
||||
var previousSibling = lastChild.previousSibling;
|
||||
// need to work around some white space which has been inserted into the dom
|
||||
while (previousSibling.nodeType !== 1) {
|
||||
previousSibling = previousSibling.previousSibling;
|
||||
}
|
||||
relatedWidgetWrapper.insertBefore(link, previousSibling.nextSibling);
|
||||
}
|
||||
}
|
||||
|
||||
// Return the link, which we'll use in the disable and enable functions
|
||||
return link;
|
||||
}
|
||||
|
||||
// Either enable or disable widget buttons when select is clicked. Action (enable or disable) taken depends on the count
|
||||
// of selected items in selectElement. If exactly one item is selected, buttons are enabled, and urls for the buttons are
|
||||
// associated with the selected item
|
||||
function handleSelectClick(selectElement, changeLink, deleteLink, viewLink) {
|
||||
|
||||
// If one item is selected (across selectElement and relatedSelectElement), enable buttons; otherwise, disable them
|
||||
if (selectElement.selectedOptions.length === 1) {
|
||||
// enable buttons for selected item in selectElement
|
||||
enableRelatedWidgetButtons(changeLink, deleteLink, viewLink, selectElement.selectedOptions[0].value, selectElement.selectedOptions[0].text);
|
||||
} else {
|
||||
disableRelatedWidgetButtons(changeLink, deleteLink, viewLink);
|
||||
}
|
||||
}
|
||||
|
||||
// return true if there exist elements on the page with classname of delete-related.
|
||||
// presence of one or more of these elements indicates user has permission to delete
|
||||
function hasDeletePermissionOnPage() {
|
||||
return document.querySelector('.delete-related') != null
|
||||
}
|
||||
|
||||
function disableRelatedWidgetButtons(changeLink, deleteLink, viewLink) {
|
||||
changeLink.removeAttribute('href');
|
||||
changeLink.setAttribute('title', changeLink.getAttribute('title-template'));
|
||||
if (deleteLink) {
|
||||
deleteLink.removeAttribute('href');
|
||||
deleteLink.setAttribute('title', deleteLink.getAttribute('title-template'));
|
||||
}
|
||||
viewLink.removeAttribute('href');
|
||||
viewLink.setAttribute('title', viewLink.getAttribute('title-template'));
|
||||
}
|
||||
|
||||
function enableRelatedWidgetButtons(changeLink, deleteLink, viewLink, elementPk, elementText) {
|
||||
changeLink.setAttribute('href', changeLink.getAttribute('data-href-template').replace('__fk__', elementPk));
|
||||
changeLink.setAttribute('title', changeLink.getAttribute('title-template').replace('selected item', elementText));
|
||||
if (deleteLink) {
|
||||
deleteLink.setAttribute('href', deleteLink.getAttribute('data-href-template').replace('__fk__', elementPk));
|
||||
deleteLink.setAttribute('title', deleteLink.getAttribute('title-template').replace('selected item', elementText));
|
||||
}
|
||||
viewLink.setAttribute('href', viewLink.getAttribute('data-href-template').replace('__fk__', elementPk));
|
||||
viewLink.setAttribute('title', viewLink.getAttribute('title-template').replace('selected item', elementText));
|
||||
}
|
||||
|
|
|
@ -37,9 +37,9 @@ body {
|
|||
@include typeset('sans', 'xl', 2);
|
||||
color: color('primary-darker');
|
||||
}
|
||||
|
||||
|
||||
.usa-nav__primary {
|
||||
margin-top: units(1);
|
||||
margin-top:units(1);
|
||||
}
|
||||
|
||||
.section--outlined {
|
||||
|
|
|
@ -33,11 +33,20 @@ env = environs.Env()
|
|||
# Get secrets from Cloud.gov user provided service, if exists
|
||||
# If not, get secrets from environment variables
|
||||
key_service = AppEnv().get_service(name="getgov-credentials")
|
||||
|
||||
|
||||
# Get secrets from Cloud.gov user provided s3 service, if it exists
|
||||
s3_key_service = AppEnv().get_service(name="getgov-s3")
|
||||
|
||||
if key_service and key_service.credentials:
|
||||
if s3_key_service and s3_key_service.credentials:
|
||||
# Concatenate the credentials from our S3 service into our secret service
|
||||
key_service.credentials.update(s3_key_service.credentials)
|
||||
secret = key_service.credentials.get
|
||||
else:
|
||||
secret = env
|
||||
|
||||
|
||||
# # # ###
|
||||
# Values obtained externally #
|
||||
# # # ###
|
||||
|
@ -58,6 +67,12 @@ secret_key = secret("DJANGO_SECRET_KEY")
|
|||
secret_aws_ses_key_id = secret("AWS_ACCESS_KEY_ID", None)
|
||||
secret_aws_ses_key = secret("AWS_SECRET_ACCESS_KEY", None)
|
||||
|
||||
# These keys are present in a getgov-s3 instance, or they can be defined locally
|
||||
aws_s3_region_name = secret("region", None) or secret("AWS_S3_REGION", None)
|
||||
secret_aws_s3_key_id = secret("access_key_id", None) or secret("AWS_S3_ACCESS_KEY_ID", None)
|
||||
secret_aws_s3_key = secret("secret_access_key", None) or secret("AWS_S3_SECRET_ACCESS_KEY", None)
|
||||
secret_aws_s3_bucket_name = secret("bucket", None) or secret("AWS_S3_BUCKET_NAME", None)
|
||||
|
||||
secret_registry_cl_id = secret("REGISTRY_CL_ID")
|
||||
secret_registry_password = secret("REGISTRY_PASSWORD")
|
||||
secret_registry_cert = b64decode(secret("REGISTRY_CERT", ""))
|
||||
|
@ -257,7 +272,14 @@ AUTH_USER_MODEL = "registrar.User"
|
|||
AWS_ACCESS_KEY_ID = secret_aws_ses_key_id
|
||||
AWS_SECRET_ACCESS_KEY = secret_aws_ses_key
|
||||
AWS_REGION = "us-gov-west-1"
|
||||
# https://boto3.amazonaws.com/v1/documentation/api/latest/guide/retries.html#standard-retry-mode
|
||||
|
||||
# Configuration for accessing AWS S3
|
||||
AWS_S3_ACCESS_KEY_ID = secret_aws_s3_key_id
|
||||
AWS_S3_SECRET_ACCESS_KEY = secret_aws_s3_key
|
||||
AWS_S3_REGION = aws_s3_region_name
|
||||
AWS_S3_BUCKET_NAME = secret_aws_s3_bucket_name
|
||||
|
||||
# https://boto3.amazonaws.com/v1/documentation/latest/guide/retries.html#standard-retry-mode
|
||||
AWS_RETRY_MODE: Final = "standard"
|
||||
# base 2 exponential backoff with max of 20 seconds:
|
||||
AWS_MAX_ATTEMPTS = 3
|
||||
|
@ -518,7 +540,7 @@ OIDC_PROVIDERS = {
|
|||
"response_type": "code",
|
||||
"scope": ["email", "profile:name", "phone"],
|
||||
"user_info_request": ["email", "first_name", "last_name", "phone"],
|
||||
"acr_value": "http://idmanagement.gov/ns/assurance/ial/2",
|
||||
"acr_value": "http://idmanagement.gov/ns/assurance/ial/1",
|
||||
},
|
||||
"client_registration": {
|
||||
"client_id": "cisa_dotgov_registrar",
|
||||
|
@ -535,7 +557,7 @@ OIDC_PROVIDERS = {
|
|||
"response_type": "code",
|
||||
"scope": ["email", "profile:name", "phone"],
|
||||
"user_info_request": ["email", "first_name", "last_name", "phone"],
|
||||
"acr_value": "http://idmanagement.gov/ns/assurance/ial/2",
|
||||
"acr_value": "http://idmanagement.gov/ns/assurance/ial/1",
|
||||
},
|
||||
"client_registration": {
|
||||
"client_id": ("urn:gov:cisa:openidconnect.profiles:sp:sso:cisa:dotgov_registrar"),
|
||||
|
|
|
@ -11,7 +11,8 @@ from django.views.generic import RedirectView
|
|||
from registrar import views
|
||||
from registrar.views.application import Step
|
||||
from registrar.views.utility import always_404
|
||||
from api.views import available
|
||||
from api.views import available, get_current_federal, get_current_full
|
||||
|
||||
|
||||
APPLICATION_NAMESPACE = views.ApplicationWizard.URL_NAMESPACE
|
||||
application_urls = [
|
||||
|
@ -73,6 +74,8 @@ urlpatterns = [
|
|||
path("openid/", include("djangooidc.urls")),
|
||||
path("register/", include((application_urls, APPLICATION_NAMESPACE))),
|
||||
path("api/v1/available/<domain>", available, name="available"),
|
||||
path("api/v1/get-report/current-federal", get_current_federal, name="get-current-federal"),
|
||||
path("api/v1/get-report/current-full", get_current_full, name="get-current-full"),
|
||||
path(
|
||||
"todo",
|
||||
lambda r: always_404(r, "We forgot to include this link, sorry."),
|
||||
|
|
|
@ -244,7 +244,7 @@ class OrganizationContactForm(RegistrarForm):
|
|||
)
|
||||
address_line2 = forms.CharField(
|
||||
required=False,
|
||||
label="Street address line 2",
|
||||
label="Street address line 2 (optional)",
|
||||
)
|
||||
city = forms.CharField(
|
||||
label="City",
|
||||
|
@ -268,7 +268,7 @@ class OrganizationContactForm(RegistrarForm):
|
|||
)
|
||||
urbanization = forms.CharField(
|
||||
required=False,
|
||||
label="Urbanization (Puerto Rico only)",
|
||||
label="Urbanization (required for Puerto Rico only)",
|
||||
)
|
||||
|
||||
def clean_federal_agency(self):
|
||||
|
@ -331,7 +331,7 @@ class AuthorizingOfficialForm(RegistrarForm):
|
|||
)
|
||||
middle_name = forms.CharField(
|
||||
required=False,
|
||||
label="Middle name",
|
||||
label="Middle name (optional)",
|
||||
)
|
||||
last_name = forms.CharField(
|
||||
label="Last name / family name",
|
||||
|
@ -407,7 +407,7 @@ class AlternativeDomainForm(RegistrarForm):
|
|||
|
||||
alternative_domain = forms.CharField(
|
||||
required=False,
|
||||
label="Alternative domain",
|
||||
label="",
|
||||
)
|
||||
|
||||
|
||||
|
@ -533,7 +533,7 @@ class YourContactForm(RegistrarForm):
|
|||
)
|
||||
middle_name = forms.CharField(
|
||||
required=False,
|
||||
label="Middle name",
|
||||
label="Middle name (optional)",
|
||||
)
|
||||
last_name = forms.CharField(
|
||||
label="Last name / family name",
|
||||
|
@ -562,7 +562,7 @@ class OtherContactsForm(RegistrarForm):
|
|||
)
|
||||
middle_name = forms.CharField(
|
||||
required=False,
|
||||
label="Middle name",
|
||||
label="Middle name (optional)",
|
||||
)
|
||||
last_name = forms.CharField(
|
||||
label="Last name / family name",
|
||||
|
@ -614,8 +614,8 @@ class NoOtherContactsForm(RegistrarForm):
|
|||
required=True,
|
||||
# label has to end in a space to get the label_suffix to show
|
||||
label=(
|
||||
"Please explain why there are no other employees from your organization"
|
||||
" we can contact to help us assess your eligibility for a .gov domain."
|
||||
"Please explain why there are no other employees from your organization "
|
||||
"we can contact to help us assess your eligibility for a .gov domain."
|
||||
),
|
||||
widget=forms.Textarea(),
|
||||
)
|
||||
|
@ -624,7 +624,7 @@ class NoOtherContactsForm(RegistrarForm):
|
|||
class AnythingElseForm(RegistrarForm):
|
||||
anything_else = forms.CharField(
|
||||
required=False,
|
||||
label="Anything else we should know?",
|
||||
label="Anything else?",
|
||||
widget=forms.Textarea(),
|
||||
validators=[
|
||||
MaxLengthValidator(
|
||||
|
|
|
@ -181,6 +181,9 @@ class ContactForm(forms.ModelForm):
|
|||
for field_name in self.required:
|
||||
self.fields[field_name].required = True
|
||||
|
||||
# Set custom form label
|
||||
self.fields["middle_name"].label = "Middle name (optional)"
|
||||
|
||||
# Set custom error messages
|
||||
self.fields["first_name"].error_messages = {"required": "Enter your first name / given name."}
|
||||
self.fields["last_name"].error_messages = {"required": "Enter your last name / family name."}
|
||||
|
@ -190,7 +193,7 @@ class ContactForm(forms.ModelForm):
|
|||
self.fields["email"].error_messages = {
|
||||
"required": "Enter your email address in the required format, like name@example.com."
|
||||
}
|
||||
self.fields["phone"].error_messages = {"required": "Enter your phone number."}
|
||||
self.fields["phone"].error_messages["required"] = "Enter your phone number."
|
||||
|
||||
|
||||
class AuthorizingOfficialContactForm(ContactForm):
|
||||
|
@ -213,14 +216,14 @@ class AuthorizingOfficialContactForm(ContactForm):
|
|||
self.fields["email"].error_messages = {
|
||||
"required": "Enter an email address in the required format, like name@example.com."
|
||||
}
|
||||
self.fields["phone"].error_messages = {"required": "Enter a phone number for your authorizing official."}
|
||||
self.fields["phone"].error_messages["required"] = "Enter a phone number for your authorizing official."
|
||||
|
||||
|
||||
class DomainSecurityEmailForm(forms.Form):
|
||||
"""Form for adding or editing a security email to a domain."""
|
||||
|
||||
security_email = forms.EmailField(
|
||||
label="Security email",
|
||||
label="Security email (optional)",
|
||||
required=False,
|
||||
error_messages={
|
||||
"invalid": str(SecurityEmailError(code=SecurityEmailErrorCodes.BAD_DATA)),
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
"""Generates current-full.csv and current-federal.csv then uploads them to the desired URL."""
|
||||
import logging
|
||||
import os
|
||||
|
||||
from django.core.management import BaseCommand
|
||||
from registrar.utility import csv_export
|
||||
from registrar.utility.s3_bucket import S3ClientHelper
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = (
|
||||
"Generates and uploads a current-federal.csv file to our S3 bucket "
|
||||
"which is based off of all existing federal Domains."
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add our two filename arguments."""
|
||||
parser.add_argument("--directory", default="migrationdata", help="Desired directory")
|
||||
parser.add_argument(
|
||||
"--checkpath",
|
||||
default=True,
|
||||
help="Flag that determines if we do a check for os.path.exists. Used for test cases",
|
||||
)
|
||||
|
||||
def handle(self, **options):
|
||||
"""Grabs the directory then creates current-federal.csv in that directory"""
|
||||
file_name = "current-federal.csv"
|
||||
# Ensures a slash is added
|
||||
directory = os.path.join(options.get("directory"), "")
|
||||
check_path = options.get("checkpath")
|
||||
|
||||
logger.info("Generating report...")
|
||||
try:
|
||||
self.generate_current_federal_report(directory, file_name, check_path)
|
||||
except Exception as err:
|
||||
# TODO - #1317: Notify operations when auto report generation fails
|
||||
raise err
|
||||
else:
|
||||
logger.info(f"Success! Created {file_name}")
|
||||
|
||||
def generate_current_federal_report(self, directory, file_name, check_path):
|
||||
"""Creates a current-full.csv file under the specified directory,
|
||||
then uploads it to a AWS S3 bucket"""
|
||||
s3_client = S3ClientHelper()
|
||||
file_path = os.path.join(directory, file_name)
|
||||
|
||||
# Generate a file locally for upload
|
||||
with open(file_path, "w") as file:
|
||||
csv_export.export_data_federal_to_csv(file)
|
||||
|
||||
if check_path and not os.path.exists(file_path):
|
||||
raise FileNotFoundError(f"Could not find newly created file at '{file_path}'")
|
||||
|
||||
# Upload this generated file for our S3 instance
|
||||
s3_client.upload_file(file_path, file_name)
|
|
@ -0,0 +1,57 @@
|
|||
"""Generates current-full.csv and current-federal.csv then uploads them to the desired URL."""
|
||||
import logging
|
||||
import os
|
||||
|
||||
from django.core.management import BaseCommand
|
||||
from registrar.utility import csv_export
|
||||
from registrar.utility.s3_bucket import S3ClientHelper
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = (
|
||||
"Generates and uploads a current-full.csv file to our S3 bucket " "which is based off of all existing Domains."
|
||||
)
|
||||
|
||||
def add_arguments(self, parser):
|
||||
"""Add our two filename arguments."""
|
||||
parser.add_argument("--directory", default="migrationdata", help="Desired directory")
|
||||
parser.add_argument(
|
||||
"--checkpath",
|
||||
default=True,
|
||||
help="Flag that determines if we do a check for os.path.exists. Used for test cases",
|
||||
)
|
||||
|
||||
def handle(self, **options):
|
||||
"""Grabs the directory then creates current-full.csv in that directory"""
|
||||
file_name = "current-full.csv"
|
||||
# Ensures a slash is added
|
||||
directory = os.path.join(options.get("directory"), "")
|
||||
check_path = options.get("checkpath")
|
||||
|
||||
logger.info("Generating report...")
|
||||
try:
|
||||
self.generate_current_full_report(directory, file_name, check_path)
|
||||
except Exception as err:
|
||||
# TODO - #1317: Notify operations when auto report generation fails
|
||||
raise err
|
||||
else:
|
||||
logger.info(f"Success! Created {file_name}")
|
||||
|
||||
def generate_current_full_report(self, directory, file_name, check_path):
|
||||
"""Creates a current-full.csv file under the specified directory,
|
||||
then uploads it to a AWS S3 bucket"""
|
||||
s3_client = S3ClientHelper()
|
||||
file_path = os.path.join(directory, file_name)
|
||||
|
||||
# Generate a file locally for upload
|
||||
with open(file_path, "w") as file:
|
||||
csv_export.export_data_full_to_csv(file)
|
||||
|
||||
if check_path and not os.path.exists(file_path):
|
||||
raise FileNotFoundError(f"Could not find newly created file at '{file_path}'")
|
||||
|
||||
# Upload this generated file for our S3 instance
|
||||
s3_client.upload_file(file_path, file_name)
|
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 4.2.7 on 2023-12-05 10:00
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0048_alter_transitiondomain_status"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="domainapplication",
|
||||
name="current_websites",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, related_name="current+", to="registrar.website", verbose_name="websites"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domainapplication",
|
||||
name="other_contacts",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, related_name="contact_applications", to="registrar.contact", verbose_name="contacts"
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domaininformation",
|
||||
name="other_contacts",
|
||||
field=models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="contact_applications_information",
|
||||
to="registrar.contact",
|
||||
verbose_name="contacts",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,37 @@
|
|||
# Generated by Django 4.2.7 on 2023-11-20 20:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0049_alter_domainapplication_current_websites_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="contact",
|
||||
name="middle_name",
|
||||
field=models.TextField(blank=True, help_text="Middle name (optional)", null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domainapplication",
|
||||
name="address_line2",
|
||||
field=models.TextField(blank=True, help_text="Street address line 2 (optional)", null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domaininformation",
|
||||
name="address_line2",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
help_text="Street address line 2 (optional)",
|
||||
null=True,
|
||||
verbose_name="Street address line 2 (optional)",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="transitiondomain",
|
||||
name="middle_name",
|
||||
field=models.TextField(blank=True, help_text="Middle name (optional)", null=True),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,27 @@
|
|||
# Generated by Django 4.2.7 on 2023-11-22 20:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0050_alter_contact_middle_name_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="domainapplication",
|
||||
name="urbanization",
|
||||
field=models.TextField(blank=True, help_text="Urbanization (required for Puerto Rico only)", null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domaininformation",
|
||||
name="urbanization",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
help_text="Urbanization (required for Puerto Rico only)",
|
||||
null=True,
|
||||
verbose_name="Urbanization (required for Puerto Rico only)",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 4.2.7 on 2023-11-29 19:05
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0051_alter_domainapplication_urbanization_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="domainapplication",
|
||||
name="anything_else",
|
||||
field=models.TextField(blank=True, help_text="Anything else?", null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domaininformation",
|
||||
name="anything_else",
|
||||
field=models.TextField(blank=True, help_text="Anything else?", null=True),
|
||||
),
|
||||
]
|
37
src/registrar/migrations/0053_create_groups_v05.py
Normal file
37
src/registrar/migrations/0053_create_groups_v05.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", "0052_alter_domainapplication_anything_else_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
create_groups,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
atomic=True,
|
||||
),
|
||||
]
|
|
@ -0,0 +1,771 @@
|
|||
# Generated by Django 4.2.7 on 2023-11-29 22:32
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("registrar", "0053_create_groups_v05"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="domainapplication",
|
||||
name="federal_agency",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
choices=[
|
||||
(
|
||||
"Administrative Conference of the United States",
|
||||
"Administrative Conference of the United States",
|
||||
),
|
||||
("Advisory Council on Historic Preservation", "Advisory Council on Historic Preservation"),
|
||||
("American Battle Monuments Commission", "American Battle Monuments Commission"),
|
||||
("AMTRAK", "AMTRAK"),
|
||||
("Appalachian Regional Commission", "Appalachian Regional Commission"),
|
||||
(
|
||||
"Appraisal Subcommittee of the Federal Financial Institutions Examination Council",
|
||||
"Appraisal Subcommittee of the Federal Financial Institutions Examination Council",
|
||||
),
|
||||
("Appraisal Subcommittee", "Appraisal Subcommittee"),
|
||||
("Architect of the Capitol", "Architect of the Capitol"),
|
||||
("Armed Forces Retirement Home", "Armed Forces Retirement Home"),
|
||||
(
|
||||
"Barry Goldwater Scholarship and Excellence in Education Foundation",
|
||||
"Barry Goldwater Scholarship and Excellence in Education Foundation",
|
||||
),
|
||||
(
|
||||
"Barry Goldwater Scholarship and Excellence in Education Program",
|
||||
"Barry Goldwater Scholarship and Excellence in Education Program",
|
||||
),
|
||||
("Central Intelligence Agency", "Central Intelligence Agency"),
|
||||
("Chemical Safety Board", "Chemical Safety Board"),
|
||||
("Christopher Columbus Fellowship Foundation", "Christopher Columbus Fellowship Foundation"),
|
||||
("Civil Rights Cold Case Records Review Board", "Civil Rights Cold Case Records Review Board"),
|
||||
(
|
||||
"Commission for the Preservation of America's Heritage Abroad",
|
||||
"Commission for the Preservation of America's Heritage Abroad",
|
||||
),
|
||||
("Commission of Fine Arts", "Commission of Fine Arts"),
|
||||
(
|
||||
"Committee for Purchase From People Who Are Blind or Severely Disabled",
|
||||
"Committee for Purchase From People Who Are Blind or Severely Disabled",
|
||||
),
|
||||
("Commodity Futures Trading Commission", "Commodity Futures Trading Commission"),
|
||||
("Congressional Budget Office", "Congressional Budget Office"),
|
||||
("Consumer Financial Protection Bureau", "Consumer Financial Protection Bureau"),
|
||||
("Consumer Product Safety Commission", "Consumer Product Safety Commission"),
|
||||
("Corporation for National & Community Service", "Corporation for National & Community Service"),
|
||||
(
|
||||
"Corporation for National and Community Service",
|
||||
"Corporation for National and Community Service",
|
||||
),
|
||||
(
|
||||
"Council of Inspectors General on Integrity and Efficiency",
|
||||
"Council of Inspectors General on Integrity and Efficiency",
|
||||
),
|
||||
("Court Services and Offender Supervision", "Court Services and Offender Supervision"),
|
||||
("Cyberspace Solarium Commission", "Cyberspace Solarium Commission"),
|
||||
(
|
||||
"DC Court Services and Offender Supervision Agency",
|
||||
"DC Court Services and Offender Supervision Agency",
|
||||
),
|
||||
("DC Pre-trial Services", "DC Pre-trial Services"),
|
||||
("Defense Nuclear Facilities Safety Board", "Defense Nuclear Facilities Safety Board"),
|
||||
("Delta Regional Authority", "Delta Regional Authority"),
|
||||
("Denali Commission", "Denali Commission"),
|
||||
("Department of Agriculture", "Department of Agriculture"),
|
||||
("Department of Commerce", "Department of Commerce"),
|
||||
("Department of Defense", "Department of Defense"),
|
||||
("Department of Education", "Department of Education"),
|
||||
("Department of Energy", "Department of Energy"),
|
||||
("Department of Health and Human Services", "Department of Health and Human Services"),
|
||||
("Department of Homeland Security", "Department of Homeland Security"),
|
||||
("Department of Housing and Urban Development", "Department of Housing and Urban Development"),
|
||||
("Department of Justice", "Department of Justice"),
|
||||
("Department of Labor", "Department of Labor"),
|
||||
("Department of State", "Department of State"),
|
||||
("Department of the Interior", "Department of the Interior"),
|
||||
("Department of the Treasury", "Department of the Treasury"),
|
||||
("Department of Transportation", "Department of Transportation"),
|
||||
("Department of Veterans Affairs", "Department of Veterans Affairs"),
|
||||
("Director of National Intelligence", "Director of National Intelligence"),
|
||||
("Dwight D. Eisenhower Memorial Commission", "Dwight D. Eisenhower Memorial Commission"),
|
||||
("Election Assistance Commission", "Election Assistance Commission"),
|
||||
("Environmental Protection Agency", "Environmental Protection Agency"),
|
||||
("Equal Employment Opportunity Commission", "Equal Employment Opportunity Commission"),
|
||||
("Executive Office of the President", "Executive Office of the President"),
|
||||
("Export-Import Bank of the United States", "Export-Import Bank of the United States"),
|
||||
("Export/Import Bank of the U.S.", "Export/Import Bank of the U.S."),
|
||||
("Farm Credit Administration", "Farm Credit Administration"),
|
||||
("Farm Credit System Insurance Corporation", "Farm Credit System Insurance Corporation"),
|
||||
("Federal Communications Commission", "Federal Communications Commission"),
|
||||
("Federal Deposit Insurance Corporation", "Federal Deposit Insurance Corporation"),
|
||||
("Federal Election Commission", "Federal Election Commission"),
|
||||
("Federal Energy Regulatory Commission", "Federal Energy Regulatory Commission"),
|
||||
(
|
||||
"Federal Financial Institutions Examination Council",
|
||||
"Federal Financial Institutions Examination Council",
|
||||
),
|
||||
("Federal Housing Finance Agency", "Federal Housing Finance Agency"),
|
||||
("Federal Judiciary", "Federal Judiciary"),
|
||||
("Federal Labor Relations Authority", "Federal Labor Relations Authority"),
|
||||
("Federal Maritime Commission", "Federal Maritime Commission"),
|
||||
("Federal Mediation and Conciliation Service", "Federal Mediation and Conciliation Service"),
|
||||
(
|
||||
"Federal Mine Safety and Health Review Commission",
|
||||
"Federal Mine Safety and Health Review Commission",
|
||||
),
|
||||
(
|
||||
"Federal Permitting Improvement Steering Council",
|
||||
"Federal Permitting Improvement Steering Council",
|
||||
),
|
||||
("Federal Reserve Board of Governors", "Federal Reserve Board of Governors"),
|
||||
("Federal Reserve System", "Federal Reserve System"),
|
||||
("Federal Trade Commission", "Federal Trade Commission"),
|
||||
("General Services Administration", "General Services Administration"),
|
||||
("gov Administration", "gov Administration"),
|
||||
("Government Accountability Office", "Government Accountability Office"),
|
||||
("Government Publishing Office", "Government Publishing Office"),
|
||||
("Gulf Coast Ecosystem Restoration Council", "Gulf Coast Ecosystem Restoration Council"),
|
||||
("Harry S Truman Scholarship Foundation", "Harry S Truman Scholarship Foundation"),
|
||||
("Harry S. Truman Scholarship Foundation", "Harry S. Truman Scholarship Foundation"),
|
||||
("Institute of Museum and Library Services", "Institute of Museum and Library Services"),
|
||||
("Institute of Peace", "Institute of Peace"),
|
||||
("Inter-American Foundation", "Inter-American Foundation"),
|
||||
(
|
||||
"International Boundary and Water Commission: United States and Mexico",
|
||||
"International Boundary and Water Commission: United States and Mexico",
|
||||
),
|
||||
(
|
||||
"International Boundary Commission: United States and Canada",
|
||||
"International Boundary Commission: United States and Canada",
|
||||
),
|
||||
(
|
||||
"International Joint Commission: United States and Canada",
|
||||
"International Joint Commission: United States and Canada",
|
||||
),
|
||||
("James Madison Memorial Fellowship Foundation", "James Madison Memorial Fellowship Foundation"),
|
||||
("Japan-United States Friendship Commission", "Japan-United States Friendship Commission"),
|
||||
("Japan-US Friendship Commission", "Japan-US Friendship Commission"),
|
||||
("John F. Kennedy Center for Performing Arts", "John F. Kennedy Center for Performing Arts"),
|
||||
(
|
||||
"John F. Kennedy Center for the Performing Arts",
|
||||
"John F. Kennedy Center for the Performing Arts",
|
||||
),
|
||||
("Legal Services Corporation", "Legal Services Corporation"),
|
||||
("Legislative Branch", "Legislative Branch"),
|
||||
("Library of Congress", "Library of Congress"),
|
||||
("Marine Mammal Commission", "Marine Mammal Commission"),
|
||||
(
|
||||
"Medicaid and CHIP Payment and Access Commission",
|
||||
"Medicaid and CHIP Payment and Access Commission",
|
||||
),
|
||||
("Medical Payment Advisory Commission", "Medical Payment Advisory Commission"),
|
||||
("Medicare Payment Advisory Commission", "Medicare Payment Advisory Commission"),
|
||||
("Merit Systems Protection Board", "Merit Systems Protection Board"),
|
||||
("Millennium Challenge Corporation", "Millennium Challenge Corporation"),
|
||||
(
|
||||
"Morris K. Udall and Stewart L. Udall Foundation",
|
||||
"Morris K. Udall and Stewart L. Udall Foundation",
|
||||
),
|
||||
("National Aeronautics and Space Administration", "National Aeronautics and Space Administration"),
|
||||
("National Archives and Records Administration", "National Archives and Records Administration"),
|
||||
("National Capital Planning Commission", "National Capital Planning Commission"),
|
||||
("National Council on Disability", "National Council on Disability"),
|
||||
("National Credit Union Administration", "National Credit Union Administration"),
|
||||
("National Endowment for the Arts", "National Endowment for the Arts"),
|
||||
("National Endowment for the Humanities", "National Endowment for the Humanities"),
|
||||
(
|
||||
"National Foundation on the Arts and the Humanities",
|
||||
"National Foundation on the Arts and the Humanities",
|
||||
),
|
||||
("National Gallery of Art", "National Gallery of Art"),
|
||||
("National Indian Gaming Commission", "National Indian Gaming Commission"),
|
||||
("National Labor Relations Board", "National Labor Relations Board"),
|
||||
("National Mediation Board", "National Mediation Board"),
|
||||
("National Science Foundation", "National Science Foundation"),
|
||||
(
|
||||
"National Security Commission on Artificial Intelligence",
|
||||
"National Security Commission on Artificial Intelligence",
|
||||
),
|
||||
("National Transportation Safety Board", "National Transportation Safety Board"),
|
||||
(
|
||||
"Networking Information Technology Research and Development",
|
||||
"Networking Information Technology Research and Development",
|
||||
),
|
||||
("Non-Federal Agency", "Non-Federal Agency"),
|
||||
("Northern Border Regional Commission", "Northern Border Regional Commission"),
|
||||
("Nuclear Regulatory Commission", "Nuclear Regulatory Commission"),
|
||||
("Nuclear Safety Oversight Committee", "Nuclear Safety Oversight Committee"),
|
||||
("Nuclear Waste Technical Review Board", "Nuclear Waste Technical Review Board"),
|
||||
(
|
||||
"Occupational Safety & Health Review Commission",
|
||||
"Occupational Safety & Health Review Commission",
|
||||
),
|
||||
(
|
||||
"Occupational Safety and Health Review Commission",
|
||||
"Occupational Safety and Health Review Commission",
|
||||
),
|
||||
("Office of Compliance", "Office of Compliance"),
|
||||
("Office of Congressional Workplace Rights", "Office of Congressional Workplace Rights"),
|
||||
("Office of Government Ethics", "Office of Government Ethics"),
|
||||
("Office of Navajo and Hopi Indian Relocation", "Office of Navajo and Hopi Indian Relocation"),
|
||||
("Office of Personnel Management", "Office of Personnel Management"),
|
||||
("Open World Leadership Center", "Open World Leadership Center"),
|
||||
("Overseas Private Investment Corporation", "Overseas Private Investment Corporation"),
|
||||
("Peace Corps", "Peace Corps"),
|
||||
("Pension Benefit Guaranty Corporation", "Pension Benefit Guaranty Corporation"),
|
||||
("Postal Regulatory Commission", "Postal Regulatory Commission"),
|
||||
("Presidio Trust", "Presidio Trust"),
|
||||
("Privacy and Civil Liberties Oversight Board", "Privacy and Civil Liberties Oversight Board"),
|
||||
("Public Buildings Reform Board", "Public Buildings Reform Board"),
|
||||
(
|
||||
"Public Defender Service for the District of Columbia",
|
||||
"Public Defender Service for the District of Columbia",
|
||||
),
|
||||
("Railroad Retirement Board", "Railroad Retirement Board"),
|
||||
("Securities and Exchange Commission", "Securities and Exchange Commission"),
|
||||
("Selective Service System", "Selective Service System"),
|
||||
("Small Business Administration", "Small Business Administration"),
|
||||
("Smithsonian Institution", "Smithsonian Institution"),
|
||||
("Social Security Administration", "Social Security Administration"),
|
||||
("Social Security Advisory Board", "Social Security Advisory Board"),
|
||||
("Southeast Crescent Regional Commission", "Southeast Crescent Regional Commission"),
|
||||
("Southwest Border Regional Commission", "Southwest Border Regional Commission"),
|
||||
("State Justice Institute", "State Justice Institute"),
|
||||
("State, Local, and Tribal Government", "State, Local, and Tribal Government"),
|
||||
("Stennis Center for Public Service", "Stennis Center for Public Service"),
|
||||
("Surface Transportation Board", "Surface Transportation Board"),
|
||||
("Tennessee Valley Authority", "Tennessee Valley Authority"),
|
||||
("The Executive Office of the President", "The Executive Office of the President"),
|
||||
("The Intelligence Community", "The Intelligence Community"),
|
||||
("The Legislative Branch", "The Legislative Branch"),
|
||||
("The Supreme Court", "The Supreme Court"),
|
||||
(
|
||||
"The United States World War One Centennial Commission",
|
||||
"The United States World War One Centennial Commission",
|
||||
),
|
||||
("U.S. Access Board", "U.S. Access Board"),
|
||||
("U.S. Agency for Global Media", "U.S. Agency for Global Media"),
|
||||
("U.S. Agency for International Development", "U.S. Agency for International Development"),
|
||||
("U.S. Capitol Police", "U.S. Capitol Police"),
|
||||
("U.S. Chemical Safety Board", "U.S. Chemical Safety Board"),
|
||||
(
|
||||
"U.S. China Economic and Security Review Commission",
|
||||
"U.S. China Economic and Security Review Commission",
|
||||
),
|
||||
(
|
||||
"U.S. Commission for the Preservation of Americas Heritage Abroad",
|
||||
"U.S. Commission for the Preservation of Americas Heritage Abroad",
|
||||
),
|
||||
("U.S. Commission of Fine Arts", "U.S. Commission of Fine Arts"),
|
||||
("U.S. Commission on Civil Rights", "U.S. Commission on Civil Rights"),
|
||||
(
|
||||
"U.S. Commission on International Religious Freedom",
|
||||
"U.S. Commission on International Religious Freedom",
|
||||
),
|
||||
("U.S. Courts", "U.S. Courts"),
|
||||
("U.S. Department of Agriculture", "U.S. Department of Agriculture"),
|
||||
("U.S. Interagency Council on Homelessness", "U.S. Interagency Council on Homelessness"),
|
||||
("U.S. International Trade Commission", "U.S. International Trade Commission"),
|
||||
("U.S. Nuclear Waste Technical Review Board", "U.S. Nuclear Waste Technical Review Board"),
|
||||
("U.S. Office of Special Counsel", "U.S. Office of Special Counsel"),
|
||||
("U.S. Peace Corps", "U.S. Peace Corps"),
|
||||
("U.S. Postal Service", "U.S. Postal Service"),
|
||||
("U.S. Semiquincentennial Commission", "U.S. Semiquincentennial Commission"),
|
||||
("U.S. Trade and Development Agency", "U.S. Trade and Development Agency"),
|
||||
(
|
||||
"U.S.-China Economic and Security Review Commission",
|
||||
"U.S.-China Economic and Security Review Commission",
|
||||
),
|
||||
("Udall Foundation", "Udall Foundation"),
|
||||
("United States AbilityOne", "United States AbilityOne"),
|
||||
("United States Access Board", "United States Access Board"),
|
||||
("United States African Development Foundation", "United States African Development Foundation"),
|
||||
("United States Agency for Global Media", "United States Agency for Global Media"),
|
||||
("United States Arctic Research Commission", "United States Arctic Research Commission"),
|
||||
("United States Global Change Research Program", "United States Global Change Research Program"),
|
||||
("United States Holocaust Memorial Museum", "United States Holocaust Memorial Museum"),
|
||||
("United States Institute of Peace", "United States Institute of Peace"),
|
||||
(
|
||||
"United States Interagency Council on Homelessness",
|
||||
"United States Interagency Council on Homelessness",
|
||||
),
|
||||
(
|
||||
"United States International Development Finance Corporation",
|
||||
"United States International Development Finance Corporation",
|
||||
),
|
||||
("United States International Trade Commission", "United States International Trade Commission"),
|
||||
("United States Postal Service", "United States Postal Service"),
|
||||
("United States Senate", "United States Senate"),
|
||||
("United States Trade and Development Agency", "United States Trade and Development Agency"),
|
||||
(
|
||||
"Utah Reclamation Mitigation and Conservation Commission",
|
||||
"Utah Reclamation Mitigation and Conservation Commission",
|
||||
),
|
||||
("Vietnam Education Foundation", "Vietnam Education Foundation"),
|
||||
("Western Hemisphere Drug Policy Commission", "Western Hemisphere Drug Policy Commission"),
|
||||
(
|
||||
"Woodrow Wilson International Center for Scholars",
|
||||
"Woodrow Wilson International Center for Scholars",
|
||||
),
|
||||
("World War I Centennial Commission", "World War I Centennial Commission"),
|
||||
],
|
||||
help_text="Federal agency",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domainapplication",
|
||||
name="state_territory",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("AL", "Alabama (AL)"),
|
||||
("AK", "Alaska (AK)"),
|
||||
("AS", "American Samoa (AS)"),
|
||||
("AZ", "Arizona (AZ)"),
|
||||
("AR", "Arkansas (AR)"),
|
||||
("CA", "California (CA)"),
|
||||
("CO", "Colorado (CO)"),
|
||||
("CT", "Connecticut (CT)"),
|
||||
("DE", "Delaware (DE)"),
|
||||
("DC", "District of Columbia (DC)"),
|
||||
("FL", "Florida (FL)"),
|
||||
("GA", "Georgia (GA)"),
|
||||
("GU", "Guam (GU)"),
|
||||
("HI", "Hawaii (HI)"),
|
||||
("ID", "Idaho (ID)"),
|
||||
("IL", "Illinois (IL)"),
|
||||
("IN", "Indiana (IN)"),
|
||||
("IA", "Iowa (IA)"),
|
||||
("KS", "Kansas (KS)"),
|
||||
("KY", "Kentucky (KY)"),
|
||||
("LA", "Louisiana (LA)"),
|
||||
("ME", "Maine (ME)"),
|
||||
("MD", "Maryland (MD)"),
|
||||
("MA", "Massachusetts (MA)"),
|
||||
("MI", "Michigan (MI)"),
|
||||
("MN", "Minnesota (MN)"),
|
||||
("MS", "Mississippi (MS)"),
|
||||
("MO", "Missouri (MO)"),
|
||||
("MT", "Montana (MT)"),
|
||||
("NE", "Nebraska (NE)"),
|
||||
("NV", "Nevada (NV)"),
|
||||
("NH", "New Hampshire (NH)"),
|
||||
("NJ", "New Jersey (NJ)"),
|
||||
("NM", "New Mexico (NM)"),
|
||||
("NY", "New York (NY)"),
|
||||
("NC", "North Carolina (NC)"),
|
||||
("ND", "North Dakota (ND)"),
|
||||
("MP", "Northern Mariana Islands (MP)"),
|
||||
("OH", "Ohio (OH)"),
|
||||
("OK", "Oklahoma (OK)"),
|
||||
("OR", "Oregon (OR)"),
|
||||
("PA", "Pennsylvania (PA)"),
|
||||
("PR", "Puerto Rico (PR)"),
|
||||
("RI", "Rhode Island (RI)"),
|
||||
("SC", "South Carolina (SC)"),
|
||||
("SD", "South Dakota (SD)"),
|
||||
("TN", "Tennessee (TN)"),
|
||||
("TX", "Texas (TX)"),
|
||||
("UM", "United States Minor Outlying Islands (UM)"),
|
||||
("UT", "Utah (UT)"),
|
||||
("VT", "Vermont (VT)"),
|
||||
("VI", "Virgin Islands (VI)"),
|
||||
("VA", "Virginia (VA)"),
|
||||
("WA", "Washington (WA)"),
|
||||
("WV", "West Virginia (WV)"),
|
||||
("WI", "Wisconsin (WI)"),
|
||||
("WY", "Wyoming (WY)"),
|
||||
("AA", "Armed Forces Americas (AA)"),
|
||||
("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"),
|
||||
("AP", "Armed Forces Pacific (AP)"),
|
||||
],
|
||||
help_text="State, territory, or military post",
|
||||
max_length=2,
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domaininformation",
|
||||
name="federal_agency",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
choices=[
|
||||
(
|
||||
"Administrative Conference of the United States",
|
||||
"Administrative Conference of the United States",
|
||||
),
|
||||
("Advisory Council on Historic Preservation", "Advisory Council on Historic Preservation"),
|
||||
("American Battle Monuments Commission", "American Battle Monuments Commission"),
|
||||
("AMTRAK", "AMTRAK"),
|
||||
("Appalachian Regional Commission", "Appalachian Regional Commission"),
|
||||
(
|
||||
"Appraisal Subcommittee of the Federal Financial Institutions Examination Council",
|
||||
"Appraisal Subcommittee of the Federal Financial Institutions Examination Council",
|
||||
),
|
||||
("Appraisal Subcommittee", "Appraisal Subcommittee"),
|
||||
("Architect of the Capitol", "Architect of the Capitol"),
|
||||
("Armed Forces Retirement Home", "Armed Forces Retirement Home"),
|
||||
(
|
||||
"Barry Goldwater Scholarship and Excellence in Education Foundation",
|
||||
"Barry Goldwater Scholarship and Excellence in Education Foundation",
|
||||
),
|
||||
(
|
||||
"Barry Goldwater Scholarship and Excellence in Education Program",
|
||||
"Barry Goldwater Scholarship and Excellence in Education Program",
|
||||
),
|
||||
("Central Intelligence Agency", "Central Intelligence Agency"),
|
||||
("Chemical Safety Board", "Chemical Safety Board"),
|
||||
("Christopher Columbus Fellowship Foundation", "Christopher Columbus Fellowship Foundation"),
|
||||
("Civil Rights Cold Case Records Review Board", "Civil Rights Cold Case Records Review Board"),
|
||||
(
|
||||
"Commission for the Preservation of America's Heritage Abroad",
|
||||
"Commission for the Preservation of America's Heritage Abroad",
|
||||
),
|
||||
("Commission of Fine Arts", "Commission of Fine Arts"),
|
||||
(
|
||||
"Committee for Purchase From People Who Are Blind or Severely Disabled",
|
||||
"Committee for Purchase From People Who Are Blind or Severely Disabled",
|
||||
),
|
||||
("Commodity Futures Trading Commission", "Commodity Futures Trading Commission"),
|
||||
("Congressional Budget Office", "Congressional Budget Office"),
|
||||
("Consumer Financial Protection Bureau", "Consumer Financial Protection Bureau"),
|
||||
("Consumer Product Safety Commission", "Consumer Product Safety Commission"),
|
||||
("Corporation for National & Community Service", "Corporation for National & Community Service"),
|
||||
(
|
||||
"Corporation for National and Community Service",
|
||||
"Corporation for National and Community Service",
|
||||
),
|
||||
(
|
||||
"Council of Inspectors General on Integrity and Efficiency",
|
||||
"Council of Inspectors General on Integrity and Efficiency",
|
||||
),
|
||||
("Court Services and Offender Supervision", "Court Services and Offender Supervision"),
|
||||
("Cyberspace Solarium Commission", "Cyberspace Solarium Commission"),
|
||||
(
|
||||
"DC Court Services and Offender Supervision Agency",
|
||||
"DC Court Services and Offender Supervision Agency",
|
||||
),
|
||||
("DC Pre-trial Services", "DC Pre-trial Services"),
|
||||
("Defense Nuclear Facilities Safety Board", "Defense Nuclear Facilities Safety Board"),
|
||||
("Delta Regional Authority", "Delta Regional Authority"),
|
||||
("Denali Commission", "Denali Commission"),
|
||||
("Department of Agriculture", "Department of Agriculture"),
|
||||
("Department of Commerce", "Department of Commerce"),
|
||||
("Department of Defense", "Department of Defense"),
|
||||
("Department of Education", "Department of Education"),
|
||||
("Department of Energy", "Department of Energy"),
|
||||
("Department of Health and Human Services", "Department of Health and Human Services"),
|
||||
("Department of Homeland Security", "Department of Homeland Security"),
|
||||
("Department of Housing and Urban Development", "Department of Housing and Urban Development"),
|
||||
("Department of Justice", "Department of Justice"),
|
||||
("Department of Labor", "Department of Labor"),
|
||||
("Department of State", "Department of State"),
|
||||
("Department of the Interior", "Department of the Interior"),
|
||||
("Department of the Treasury", "Department of the Treasury"),
|
||||
("Department of Transportation", "Department of Transportation"),
|
||||
("Department of Veterans Affairs", "Department of Veterans Affairs"),
|
||||
("Director of National Intelligence", "Director of National Intelligence"),
|
||||
("Dwight D. Eisenhower Memorial Commission", "Dwight D. Eisenhower Memorial Commission"),
|
||||
("Election Assistance Commission", "Election Assistance Commission"),
|
||||
("Environmental Protection Agency", "Environmental Protection Agency"),
|
||||
("Equal Employment Opportunity Commission", "Equal Employment Opportunity Commission"),
|
||||
("Executive Office of the President", "Executive Office of the President"),
|
||||
("Export-Import Bank of the United States", "Export-Import Bank of the United States"),
|
||||
("Export/Import Bank of the U.S.", "Export/Import Bank of the U.S."),
|
||||
("Farm Credit Administration", "Farm Credit Administration"),
|
||||
("Farm Credit System Insurance Corporation", "Farm Credit System Insurance Corporation"),
|
||||
("Federal Communications Commission", "Federal Communications Commission"),
|
||||
("Federal Deposit Insurance Corporation", "Federal Deposit Insurance Corporation"),
|
||||
("Federal Election Commission", "Federal Election Commission"),
|
||||
("Federal Energy Regulatory Commission", "Federal Energy Regulatory Commission"),
|
||||
(
|
||||
"Federal Financial Institutions Examination Council",
|
||||
"Federal Financial Institutions Examination Council",
|
||||
),
|
||||
("Federal Housing Finance Agency", "Federal Housing Finance Agency"),
|
||||
("Federal Judiciary", "Federal Judiciary"),
|
||||
("Federal Labor Relations Authority", "Federal Labor Relations Authority"),
|
||||
("Federal Maritime Commission", "Federal Maritime Commission"),
|
||||
("Federal Mediation and Conciliation Service", "Federal Mediation and Conciliation Service"),
|
||||
(
|
||||
"Federal Mine Safety and Health Review Commission",
|
||||
"Federal Mine Safety and Health Review Commission",
|
||||
),
|
||||
(
|
||||
"Federal Permitting Improvement Steering Council",
|
||||
"Federal Permitting Improvement Steering Council",
|
||||
),
|
||||
("Federal Reserve Board of Governors", "Federal Reserve Board of Governors"),
|
||||
("Federal Reserve System", "Federal Reserve System"),
|
||||
("Federal Trade Commission", "Federal Trade Commission"),
|
||||
("General Services Administration", "General Services Administration"),
|
||||
("gov Administration", "gov Administration"),
|
||||
("Government Accountability Office", "Government Accountability Office"),
|
||||
("Government Publishing Office", "Government Publishing Office"),
|
||||
("Gulf Coast Ecosystem Restoration Council", "Gulf Coast Ecosystem Restoration Council"),
|
||||
("Harry S Truman Scholarship Foundation", "Harry S Truman Scholarship Foundation"),
|
||||
("Harry S. Truman Scholarship Foundation", "Harry S. Truman Scholarship Foundation"),
|
||||
("Institute of Museum and Library Services", "Institute of Museum and Library Services"),
|
||||
("Institute of Peace", "Institute of Peace"),
|
||||
("Inter-American Foundation", "Inter-American Foundation"),
|
||||
(
|
||||
"International Boundary and Water Commission: United States and Mexico",
|
||||
"International Boundary and Water Commission: United States and Mexico",
|
||||
),
|
||||
(
|
||||
"International Boundary Commission: United States and Canada",
|
||||
"International Boundary Commission: United States and Canada",
|
||||
),
|
||||
(
|
||||
"International Joint Commission: United States and Canada",
|
||||
"International Joint Commission: United States and Canada",
|
||||
),
|
||||
("James Madison Memorial Fellowship Foundation", "James Madison Memorial Fellowship Foundation"),
|
||||
("Japan-United States Friendship Commission", "Japan-United States Friendship Commission"),
|
||||
("Japan-US Friendship Commission", "Japan-US Friendship Commission"),
|
||||
("John F. Kennedy Center for Performing Arts", "John F. Kennedy Center for Performing Arts"),
|
||||
(
|
||||
"John F. Kennedy Center for the Performing Arts",
|
||||
"John F. Kennedy Center for the Performing Arts",
|
||||
),
|
||||
("Legal Services Corporation", "Legal Services Corporation"),
|
||||
("Legislative Branch", "Legislative Branch"),
|
||||
("Library of Congress", "Library of Congress"),
|
||||
("Marine Mammal Commission", "Marine Mammal Commission"),
|
||||
(
|
||||
"Medicaid and CHIP Payment and Access Commission",
|
||||
"Medicaid and CHIP Payment and Access Commission",
|
||||
),
|
||||
("Medical Payment Advisory Commission", "Medical Payment Advisory Commission"),
|
||||
("Medicare Payment Advisory Commission", "Medicare Payment Advisory Commission"),
|
||||
("Merit Systems Protection Board", "Merit Systems Protection Board"),
|
||||
("Millennium Challenge Corporation", "Millennium Challenge Corporation"),
|
||||
(
|
||||
"Morris K. Udall and Stewart L. Udall Foundation",
|
||||
"Morris K. Udall and Stewart L. Udall Foundation",
|
||||
),
|
||||
("National Aeronautics and Space Administration", "National Aeronautics and Space Administration"),
|
||||
("National Archives and Records Administration", "National Archives and Records Administration"),
|
||||
("National Capital Planning Commission", "National Capital Planning Commission"),
|
||||
("National Council on Disability", "National Council on Disability"),
|
||||
("National Credit Union Administration", "National Credit Union Administration"),
|
||||
("National Endowment for the Arts", "National Endowment for the Arts"),
|
||||
("National Endowment for the Humanities", "National Endowment for the Humanities"),
|
||||
(
|
||||
"National Foundation on the Arts and the Humanities",
|
||||
"National Foundation on the Arts and the Humanities",
|
||||
),
|
||||
("National Gallery of Art", "National Gallery of Art"),
|
||||
("National Indian Gaming Commission", "National Indian Gaming Commission"),
|
||||
("National Labor Relations Board", "National Labor Relations Board"),
|
||||
("National Mediation Board", "National Mediation Board"),
|
||||
("National Science Foundation", "National Science Foundation"),
|
||||
(
|
||||
"National Security Commission on Artificial Intelligence",
|
||||
"National Security Commission on Artificial Intelligence",
|
||||
),
|
||||
("National Transportation Safety Board", "National Transportation Safety Board"),
|
||||
(
|
||||
"Networking Information Technology Research and Development",
|
||||
"Networking Information Technology Research and Development",
|
||||
),
|
||||
("Non-Federal Agency", "Non-Federal Agency"),
|
||||
("Northern Border Regional Commission", "Northern Border Regional Commission"),
|
||||
("Nuclear Regulatory Commission", "Nuclear Regulatory Commission"),
|
||||
("Nuclear Safety Oversight Committee", "Nuclear Safety Oversight Committee"),
|
||||
("Nuclear Waste Technical Review Board", "Nuclear Waste Technical Review Board"),
|
||||
(
|
||||
"Occupational Safety & Health Review Commission",
|
||||
"Occupational Safety & Health Review Commission",
|
||||
),
|
||||
(
|
||||
"Occupational Safety and Health Review Commission",
|
||||
"Occupational Safety and Health Review Commission",
|
||||
),
|
||||
("Office of Compliance", "Office of Compliance"),
|
||||
("Office of Congressional Workplace Rights", "Office of Congressional Workplace Rights"),
|
||||
("Office of Government Ethics", "Office of Government Ethics"),
|
||||
("Office of Navajo and Hopi Indian Relocation", "Office of Navajo and Hopi Indian Relocation"),
|
||||
("Office of Personnel Management", "Office of Personnel Management"),
|
||||
("Open World Leadership Center", "Open World Leadership Center"),
|
||||
("Overseas Private Investment Corporation", "Overseas Private Investment Corporation"),
|
||||
("Peace Corps", "Peace Corps"),
|
||||
("Pension Benefit Guaranty Corporation", "Pension Benefit Guaranty Corporation"),
|
||||
("Postal Regulatory Commission", "Postal Regulatory Commission"),
|
||||
("Presidio Trust", "Presidio Trust"),
|
||||
("Privacy and Civil Liberties Oversight Board", "Privacy and Civil Liberties Oversight Board"),
|
||||
("Public Buildings Reform Board", "Public Buildings Reform Board"),
|
||||
(
|
||||
"Public Defender Service for the District of Columbia",
|
||||
"Public Defender Service for the District of Columbia",
|
||||
),
|
||||
("Railroad Retirement Board", "Railroad Retirement Board"),
|
||||
("Securities and Exchange Commission", "Securities and Exchange Commission"),
|
||||
("Selective Service System", "Selective Service System"),
|
||||
("Small Business Administration", "Small Business Administration"),
|
||||
("Smithsonian Institution", "Smithsonian Institution"),
|
||||
("Social Security Administration", "Social Security Administration"),
|
||||
("Social Security Advisory Board", "Social Security Advisory Board"),
|
||||
("Southeast Crescent Regional Commission", "Southeast Crescent Regional Commission"),
|
||||
("Southwest Border Regional Commission", "Southwest Border Regional Commission"),
|
||||
("State Justice Institute", "State Justice Institute"),
|
||||
("State, Local, and Tribal Government", "State, Local, and Tribal Government"),
|
||||
("Stennis Center for Public Service", "Stennis Center for Public Service"),
|
||||
("Surface Transportation Board", "Surface Transportation Board"),
|
||||
("Tennessee Valley Authority", "Tennessee Valley Authority"),
|
||||
("The Executive Office of the President", "The Executive Office of the President"),
|
||||
("The Intelligence Community", "The Intelligence Community"),
|
||||
("The Legislative Branch", "The Legislative Branch"),
|
||||
("The Supreme Court", "The Supreme Court"),
|
||||
(
|
||||
"The United States World War One Centennial Commission",
|
||||
"The United States World War One Centennial Commission",
|
||||
),
|
||||
("U.S. Access Board", "U.S. Access Board"),
|
||||
("U.S. Agency for Global Media", "U.S. Agency for Global Media"),
|
||||
("U.S. Agency for International Development", "U.S. Agency for International Development"),
|
||||
("U.S. Capitol Police", "U.S. Capitol Police"),
|
||||
("U.S. Chemical Safety Board", "U.S. Chemical Safety Board"),
|
||||
(
|
||||
"U.S. China Economic and Security Review Commission",
|
||||
"U.S. China Economic and Security Review Commission",
|
||||
),
|
||||
(
|
||||
"U.S. Commission for the Preservation of Americas Heritage Abroad",
|
||||
"U.S. Commission for the Preservation of Americas Heritage Abroad",
|
||||
),
|
||||
("U.S. Commission of Fine Arts", "U.S. Commission of Fine Arts"),
|
||||
("U.S. Commission on Civil Rights", "U.S. Commission on Civil Rights"),
|
||||
(
|
||||
"U.S. Commission on International Religious Freedom",
|
||||
"U.S. Commission on International Religious Freedom",
|
||||
),
|
||||
("U.S. Courts", "U.S. Courts"),
|
||||
("U.S. Department of Agriculture", "U.S. Department of Agriculture"),
|
||||
("U.S. Interagency Council on Homelessness", "U.S. Interagency Council on Homelessness"),
|
||||
("U.S. International Trade Commission", "U.S. International Trade Commission"),
|
||||
("U.S. Nuclear Waste Technical Review Board", "U.S. Nuclear Waste Technical Review Board"),
|
||||
("U.S. Office of Special Counsel", "U.S. Office of Special Counsel"),
|
||||
("U.S. Peace Corps", "U.S. Peace Corps"),
|
||||
("U.S. Postal Service", "U.S. Postal Service"),
|
||||
("U.S. Semiquincentennial Commission", "U.S. Semiquincentennial Commission"),
|
||||
("U.S. Trade and Development Agency", "U.S. Trade and Development Agency"),
|
||||
(
|
||||
"U.S.-China Economic and Security Review Commission",
|
||||
"U.S.-China Economic and Security Review Commission",
|
||||
),
|
||||
("Udall Foundation", "Udall Foundation"),
|
||||
("United States AbilityOne", "United States AbilityOne"),
|
||||
("United States Access Board", "United States Access Board"),
|
||||
("United States African Development Foundation", "United States African Development Foundation"),
|
||||
("United States Agency for Global Media", "United States Agency for Global Media"),
|
||||
("United States Arctic Research Commission", "United States Arctic Research Commission"),
|
||||
("United States Global Change Research Program", "United States Global Change Research Program"),
|
||||
("United States Holocaust Memorial Museum", "United States Holocaust Memorial Museum"),
|
||||
("United States Institute of Peace", "United States Institute of Peace"),
|
||||
(
|
||||
"United States Interagency Council on Homelessness",
|
||||
"United States Interagency Council on Homelessness",
|
||||
),
|
||||
(
|
||||
"United States International Development Finance Corporation",
|
||||
"United States International Development Finance Corporation",
|
||||
),
|
||||
("United States International Trade Commission", "United States International Trade Commission"),
|
||||
("United States Postal Service", "United States Postal Service"),
|
||||
("United States Senate", "United States Senate"),
|
||||
("United States Trade and Development Agency", "United States Trade and Development Agency"),
|
||||
(
|
||||
"Utah Reclamation Mitigation and Conservation Commission",
|
||||
"Utah Reclamation Mitigation and Conservation Commission",
|
||||
),
|
||||
("Vietnam Education Foundation", "Vietnam Education Foundation"),
|
||||
("Western Hemisphere Drug Policy Commission", "Western Hemisphere Drug Policy Commission"),
|
||||
(
|
||||
"Woodrow Wilson International Center for Scholars",
|
||||
"Woodrow Wilson International Center for Scholars",
|
||||
),
|
||||
("World War I Centennial Commission", "World War I Centennial Commission"),
|
||||
],
|
||||
help_text="Federal agency",
|
||||
null=True,
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="domaininformation",
|
||||
name="state_territory",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
choices=[
|
||||
("AL", "Alabama (AL)"),
|
||||
("AK", "Alaska (AK)"),
|
||||
("AS", "American Samoa (AS)"),
|
||||
("AZ", "Arizona (AZ)"),
|
||||
("AR", "Arkansas (AR)"),
|
||||
("CA", "California (CA)"),
|
||||
("CO", "Colorado (CO)"),
|
||||
("CT", "Connecticut (CT)"),
|
||||
("DE", "Delaware (DE)"),
|
||||
("DC", "District of Columbia (DC)"),
|
||||
("FL", "Florida (FL)"),
|
||||
("GA", "Georgia (GA)"),
|
||||
("GU", "Guam (GU)"),
|
||||
("HI", "Hawaii (HI)"),
|
||||
("ID", "Idaho (ID)"),
|
||||
("IL", "Illinois (IL)"),
|
||||
("IN", "Indiana (IN)"),
|
||||
("IA", "Iowa (IA)"),
|
||||
("KS", "Kansas (KS)"),
|
||||
("KY", "Kentucky (KY)"),
|
||||
("LA", "Louisiana (LA)"),
|
||||
("ME", "Maine (ME)"),
|
||||
("MD", "Maryland (MD)"),
|
||||
("MA", "Massachusetts (MA)"),
|
||||
("MI", "Michigan (MI)"),
|
||||
("MN", "Minnesota (MN)"),
|
||||
("MS", "Mississippi (MS)"),
|
||||
("MO", "Missouri (MO)"),
|
||||
("MT", "Montana (MT)"),
|
||||
("NE", "Nebraska (NE)"),
|
||||
("NV", "Nevada (NV)"),
|
||||
("NH", "New Hampshire (NH)"),
|
||||
("NJ", "New Jersey (NJ)"),
|
||||
("NM", "New Mexico (NM)"),
|
||||
("NY", "New York (NY)"),
|
||||
("NC", "North Carolina (NC)"),
|
||||
("ND", "North Dakota (ND)"),
|
||||
("MP", "Northern Mariana Islands (MP)"),
|
||||
("OH", "Ohio (OH)"),
|
||||
("OK", "Oklahoma (OK)"),
|
||||
("OR", "Oregon (OR)"),
|
||||
("PA", "Pennsylvania (PA)"),
|
||||
("PR", "Puerto Rico (PR)"),
|
||||
("RI", "Rhode Island (RI)"),
|
||||
("SC", "South Carolina (SC)"),
|
||||
("SD", "South Dakota (SD)"),
|
||||
("TN", "Tennessee (TN)"),
|
||||
("TX", "Texas (TX)"),
|
||||
("UM", "United States Minor Outlying Islands (UM)"),
|
||||
("UT", "Utah (UT)"),
|
||||
("VT", "Vermont (VT)"),
|
||||
("VI", "Virgin Islands (VI)"),
|
||||
("VA", "Virginia (VA)"),
|
||||
("WA", "Washington (WA)"),
|
||||
("WV", "West Virginia (WV)"),
|
||||
("WI", "Wisconsin (WI)"),
|
||||
("WY", "Wyoming (WY)"),
|
||||
("AA", "Armed Forces Americas (AA)"),
|
||||
("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"),
|
||||
("AP", "Armed Forces Pacific (AP)"),
|
||||
],
|
||||
help_text="State, territory, or military post",
|
||||
max_length=2,
|
||||
null=True,
|
||||
verbose_name="State, territory, or military post",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -38,6 +38,7 @@ auditlog.register(DomainApplication)
|
|||
auditlog.register(Domain)
|
||||
auditlog.register(DraftDomain)
|
||||
auditlog.register(DomainInvitation)
|
||||
auditlog.register(DomainInformation)
|
||||
auditlog.register(HostIP)
|
||||
auditlog.register(Host)
|
||||
auditlog.register(Nameserver)
|
||||
|
|
|
@ -26,7 +26,7 @@ class Contact(TimeStampedModel):
|
|||
middle_name = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Middle name",
|
||||
help_text="Middle name (optional)",
|
||||
)
|
||||
last_name = models.TextField(
|
||||
null=True,
|
||||
|
|
|
@ -409,6 +409,7 @@ class DomainApplication(TimeStampedModel):
|
|||
)
|
||||
|
||||
federal_agency = models.TextField(
|
||||
choices=AGENCY_CHOICES,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Federal agency",
|
||||
|
@ -442,7 +443,7 @@ class DomainApplication(TimeStampedModel):
|
|||
address_line2 = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Street address line 2",
|
||||
help_text="Street address line 2 (optional)",
|
||||
)
|
||||
city = models.TextField(
|
||||
null=True,
|
||||
|
@ -451,6 +452,7 @@ class DomainApplication(TimeStampedModel):
|
|||
)
|
||||
state_territory = models.CharField(
|
||||
max_length=2,
|
||||
choices=StateTerritoryChoices.choices,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="State, territory, or military post",
|
||||
|
@ -465,7 +467,7 @@ class DomainApplication(TimeStampedModel):
|
|||
urbanization = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Urbanization (Puerto Rico only)",
|
||||
help_text="Urbanization (required for Puerto Rico only)",
|
||||
)
|
||||
|
||||
about_your_organization = models.TextField(
|
||||
|
@ -487,6 +489,7 @@ class DomainApplication(TimeStampedModel):
|
|||
"registrar.Website",
|
||||
blank=True,
|
||||
related_name="current+",
|
||||
verbose_name="websites",
|
||||
)
|
||||
|
||||
approved_domain = models.OneToOneField(
|
||||
|
@ -532,6 +535,7 @@ class DomainApplication(TimeStampedModel):
|
|||
"registrar.Contact",
|
||||
blank=True,
|
||||
related_name="contact_applications",
|
||||
verbose_name="contacts",
|
||||
)
|
||||
|
||||
no_other_contacts_rationale = models.TextField(
|
||||
|
@ -543,7 +547,7 @@ class DomainApplication(TimeStampedModel):
|
|||
anything_else = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Anything else we should know?",
|
||||
help_text="Anything else?",
|
||||
)
|
||||
|
||||
is_policy_acknowledged = models.BooleanField(
|
||||
|
|
|
@ -72,6 +72,7 @@ class DomainInformation(TimeStampedModel):
|
|||
)
|
||||
|
||||
federal_agency = models.TextField(
|
||||
choices=AGENCY_CHOICES,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Federal agency",
|
||||
|
@ -106,8 +107,8 @@ class DomainInformation(TimeStampedModel):
|
|||
address_line2 = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Street address line 2",
|
||||
verbose_name="Street address line 2",
|
||||
help_text="Street address line 2 (optional)",
|
||||
verbose_name="Street address line 2 (optional)",
|
||||
)
|
||||
city = models.TextField(
|
||||
null=True,
|
||||
|
@ -116,6 +117,7 @@ class DomainInformation(TimeStampedModel):
|
|||
)
|
||||
state_territory = models.CharField(
|
||||
max_length=2,
|
||||
choices=StateTerritoryChoices.choices,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="State, territory, or military post",
|
||||
|
@ -131,8 +133,8 @@ class DomainInformation(TimeStampedModel):
|
|||
urbanization = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Urbanization (Puerto Rico only)",
|
||||
verbose_name="Urbanization (Puerto Rico only)",
|
||||
help_text="Urbanization (required for Puerto Rico only)",
|
||||
verbose_name="Urbanization (required for Puerto Rico only)",
|
||||
)
|
||||
|
||||
about_your_organization = models.TextField(
|
||||
|
@ -179,6 +181,7 @@ class DomainInformation(TimeStampedModel):
|
|||
"registrar.Contact",
|
||||
blank=True,
|
||||
related_name="contact_applications_information",
|
||||
verbose_name="contacts",
|
||||
)
|
||||
|
||||
no_other_contacts_rationale = models.TextField(
|
||||
|
@ -190,7 +193,7 @@ class DomainInformation(TimeStampedModel):
|
|||
anything_else = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Anything else we should know?",
|
||||
help_text="Anything else?",
|
||||
)
|
||||
|
||||
is_policy_acknowledged = models.BooleanField(
|
||||
|
|
|
@ -84,7 +84,7 @@ class TransitionDomain(TimeStampedModel):
|
|||
middle_name = models.TextField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Middle name",
|
||||
help_text="Middle name (optional)",
|
||||
)
|
||||
last_name = models.TextField(
|
||||
null=True,
|
||||
|
|
|
@ -61,6 +61,11 @@ class UserGroup(Group):
|
|||
"model": "website",
|
||||
"permissions": ["change_website"],
|
||||
},
|
||||
{
|
||||
"app_label": "registrar",
|
||||
"model": "userdomainrole",
|
||||
"permissions": ["view_userdomainrole", "delete_userdomainrole"],
|
||||
},
|
||||
]
|
||||
|
||||
# Avoid error: You can't execute queries until the end
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<link rel="apple-touch-icon" size="180x180"
|
||||
href="{% static 'img/registrar/favicons/favicon-180.png' %}"
|
||||
>
|
||||
<script type="application/javascript" src="{% static 'js/get-gov-admin.js' %}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
<p class="text-semibold"><abbr class="usa-hint usa-hint--required" title="required">*</abbr>This question is required.</p>
|
||||
{# commented out so it does not appear on this page #}
|
||||
{% endblock %}
|
||||
|
||||
{% block form_fields %}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{% load field_helpers %}
|
||||
|
||||
{% block form_instructions %}
|
||||
<p>Is there anything else we should know about your domain request?</p>
|
||||
<p>Is there anything else you'd like us to know about your domain request? This question is optional.</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
{% block form_instructions %}
|
||||
<p>Enter your organization’s current public website, if you have one. For example,
|
||||
www.city.com. We can better evaluate your domain request if we know about domains
|
||||
you’re already using. If you already have any .gov domains please include them.</p>
|
||||
you’re already using. If you already have any .gov domains please include them. This question is optional.</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
|
|
|
@ -38,8 +38,7 @@
|
|||
|
||||
<fieldset class="usa-fieldset margin-top-2">
|
||||
<legend>
|
||||
<h2>What .gov domain do you want? <abbr class="usa-hint usa-hint--required
|
||||
text-super" title="required">*</abbr></h2>
|
||||
<h2>What .gov domain do you want?</h2>
|
||||
</legend>
|
||||
|
||||
<p id="domain_instructions" class="margin-top-05">After you enter your domain, we’ll make sure it’s
|
||||
|
@ -47,8 +46,6 @@
|
|||
these initial checks, we’ll verify that it meets all of our requirements once you
|
||||
complete and submit the rest of this form.</p>
|
||||
|
||||
<p class="text-semibold"><abbr class="usa-hint usa-hint--required" title="required">*</abbr> This question is required.</p>
|
||||
|
||||
{% with attr_aria_describedby="domain_instructions domain_instructions2" %}
|
||||
{# attr_validate / validate="domain" invokes code in get-gov.js #}
|
||||
{% with append_gov=True attr_validate="domain" add_label_class="usa-sr-only" %}
|
||||
|
@ -66,11 +63,11 @@
|
|||
|
||||
<fieldset class="usa-fieldset margin-top-1">
|
||||
<legend>
|
||||
<h2>Alternative domains</h2>
|
||||
<h2>Alternative domains (optional)</h2>
|
||||
</legend>
|
||||
|
||||
<p id="alt_domain_instructions" class="margin-top-05">Are there other domains you’d like if we can’t give
|
||||
you your first choice? Entering alternative domains is optional.</p>
|
||||
you your first choice?</p>
|
||||
|
||||
{% with attr_aria_describedby="alt_domain_instructions" %}
|
||||
{# attr_validate / validate="domain" invokes code in get-gov.js #}
|
||||
|
|
|
@ -56,9 +56,12 @@
|
|||
{% block form_instructions %}
|
||||
{% endblock %}
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
{% include "includes/required_fields.html" %}
|
||||
{% endblock %}
|
||||
<!-- The "No other employees from your organization?" page is a one-field form and should not have the required fields sentence -->
|
||||
{% if steps.current != "no_other_contacts" %}
|
||||
{% block form_required_fields_help_text %}
|
||||
{% include "includes/required_fields.html" %}
|
||||
{% endblock %}
|
||||
{% endif %}
|
||||
|
||||
<form id="step__{{steps.current}}" class="usa-form usa-form--large" method="post" novalidate>
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
{% load field_helpers %}
|
||||
|
||||
{% block form_instructions %}
|
||||
<h2 class="margin-bottom-05">
|
||||
Is your organization an election office? <abbr class="usa-hint usa-hint--required" title="required">*</abbr>
|
||||
</h2>
|
||||
<h2 class="margin-bottom-05">Is your organization an election office?</h2>
|
||||
|
||||
<p>An election office is a government entity whose <em>primary</em> responsibility is overseeing elections and/or conducting voter registration.</p>
|
||||
|
||||
|
@ -13,7 +11,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
<p class="text-semibold"><abbr class="usa-hint usa-hint--required" title="required">*</abbr> This question is required.</p>
|
||||
{# commented out so it does not appear on this page #}
|
||||
{% endblock %}
|
||||
|
||||
{% block form_fields %}
|
||||
|
|
|
@ -3,15 +3,14 @@
|
|||
|
||||
{% block form_instructions %}
|
||||
<h2 class="margin-bottom-05">
|
||||
Which federal branch is your organization in? <abbr class="usa-hint usa-hint--required text-super" title="required">*</abbr>
|
||||
Which federal branch is your organization in?
|
||||
</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
<p class="text-semibold"><abbr class="usa-hint usa-hint--required" title="required">*</abbr> This question is required.</p>
|
||||
{# commented out so it does not appear on this page #}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block form_fields %}
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.0.federal_type %}
|
||||
|
|
|
@ -3,16 +3,15 @@
|
|||
|
||||
{% block form_instructions %}
|
||||
<h2 class="margin-bottom-05">
|
||||
What kind of U.S.-based government organization do you represent? <abbr class="usa-hint usa-hint--required text-super" title="required">*</abbr>
|
||||
What kind of U.S.-based government organization do you represent?
|
||||
</h2>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
<p class="text-semibold"><abbr class="usa-hint usa-hint--required" title="required">*</abbr> This question is required.</p>
|
||||
{# commented out so it does not appear on this page #}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block form_fields %}
|
||||
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.0.organization_type %}
|
||||
|
|
|
@ -13,18 +13,16 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
{# there are no required fields on this page so don't show this #}
|
||||
{% include "includes/required_fields.html" %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block form_fields %}
|
||||
{{ forms.0.management_form }}
|
||||
{# forms.0 is a formset and this iterates over its forms #}
|
||||
{% for form in forms.0.forms %}
|
||||
<fieldset class="usa-fieldset">
|
||||
<legend>
|
||||
<h2>Organization contact {{ forloop.counter }}</h2>
|
||||
<h2>Organization contact {{ forloop.counter }} (optional)</h2>
|
||||
</legend>
|
||||
|
||||
{% input_with_errors form.first_name %}
|
||||
|
|
|
@ -13,11 +13,9 @@ Read about <a class="usa-link" rel="noopener noreferrer" target="_blank" href="{
|
|||
{% endblock %}
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
<p class="text-semibold"><abbr class="usa-hint usa-hint--required" title="required">*</abbr> This question is required.</p>
|
||||
{# commented out so it does not appear on this page #}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block form_fields %}
|
||||
{% with attr_maxlength=1000 add_label_class="usa-sr-only" %}
|
||||
{% input_with_errors forms.0.purpose %}
|
||||
|
|
|
@ -50,6 +50,10 @@
|
|||
<p>We understand the critical importance of the availability of .gov domains. Suspending or terminating a .gov domain is reserved for prolonged, unresolved, serious violations where the registrant is non-responsive. We'll make extensive efforts to contact registrants and to identify potential solutions. We'll make reasonable accommodations for remediation timelines based on the severity of the issue.</p>
|
||||
{% endblock %}
|
||||
|
||||
{% block form_required_fields_help_text %}
|
||||
{# commented out so it does not appear on this page #}
|
||||
{% endblock %}
|
||||
|
||||
{% block form_fields %}
|
||||
<fieldset class="usa-fieldset">
|
||||
<legend>
|
||||
|
|
|
@ -111,7 +111,7 @@
|
|||
|
||||
{% 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='Anything else we should know' 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 %}
|
||||
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<!doctype html>{# keep this on the first line #}
|
||||
{% load i18n static %}
|
||||
{% load static url_helpers %}
|
||||
|
||||
<html class="no-js" lang="{{ LANGUAGE_CODE }}">
|
||||
|
||||
<head>
|
||||
|
@ -144,13 +146,13 @@
|
|||
<header class="usa-header usa-header-basic">
|
||||
<div class="usa-nav-container">
|
||||
<div class="usa-navbar">
|
||||
{% block logo %}
|
||||
<div class="usa-logo" id="extended-logo">
|
||||
<strong class="usa-logo__text" >
|
||||
<a href="{% url 'home' %}"> .gov </a>
|
||||
</strong>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block logo %}
|
||||
<div class="usa-logo display-inline-block" id="extended-logo">
|
||||
<strong class="usa-logo__text" >
|
||||
<a href="{% url 'home' %}">.gov registrar </a>
|
||||
</strong>
|
||||
</div>
|
||||
{% endblock %}
|
||||
<button type="button" class="usa-menu-btn">Menu</button>
|
||||
</div>
|
||||
{% block usa_nav %}
|
||||
|
|
|
@ -1,11 +1,6 @@
|
|||
{% extends 'admin/change_form.html' %}
|
||||
{% load i18n static %}
|
||||
|
||||
{% block extrahead %}
|
||||
{{ block.super }}
|
||||
<script type="application/javascript" src="{% static 'js/get-gov-admin.js' %}" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block field_sets %}
|
||||
<div class="submit-row">
|
||||
<input id="manageDomainSubmitButton" type="submit" value="Manage domain" name="_edit_domain">
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
{% else %}
|
||||
{{ field.label }}
|
||||
{% endif %}
|
||||
|
||||
{% if widget.attrs.required %}
|
||||
<abbr class="usa-hint usa-hint--required" title="required">*</abbr>
|
||||
<!--Don't add asterisk to one-field forms -->
|
||||
{% if field.label == "Is your organization an election office?" or field.label == "What .gov domain do you want?" or field.label == "I read and agree to the requirements for operating .gov domains." or field.label == "Please explain why there are no other employees from your organization we can contact to help us assess your eligibility for a .gov domain." %}
|
||||
{% else %}
|
||||
<abbr class="usa-hint usa-hint--required" title="required">*</abbr>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
</{{ label_tag }}>
|
||||
|
|
|
@ -5,17 +5,15 @@
|
|||
|
||||
{% block content %}
|
||||
<div class="grid-container">
|
||||
<div class="grid-row">
|
||||
{% if not is_analyst_or_superuser or not analyst_action or analyst_action_location != domain.pk %}
|
||||
<p class="font-body-md margin-top-0 margin-bottom-2
|
||||
text-primary-darker text-semibold"
|
||||
>
|
||||
<span class="usa-sr-only"> Domain name:</span> {{ domain.name }}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="grid-row grid-gap">
|
||||
<div class="tablet:grid-col-3">
|
||||
<p class="font-body-md margin-top-0 margin-bottom-2
|
||||
text-primary-darker text-semibold"
|
||||
>
|
||||
<span class="usa-sr-only"> Domain name:</span> {{ domain.name }}
|
||||
</p>
|
||||
|
||||
{% if domain.domain_info %}
|
||||
{% include 'domain_sidebar.html' %}
|
||||
{% endif %}
|
||||
|
@ -42,16 +40,6 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<a href="{% url 'home' %}" class="breadcrumb__back">
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
|
||||
<use xlink:href="{% static 'img/sprite.svg' %}#arrow_back"></use>
|
||||
</svg>
|
||||
|
||||
<p class="margin-left-05 margin-top-0 margin-bottom-0 line-height-sans-1">
|
||||
Back to manage your domains
|
||||
</p>
|
||||
</a>
|
||||
{% endif %}
|
||||
{# messages block is under the back breadcrumb link #}
|
||||
{% if messages %}
|
||||
|
|
|
@ -34,6 +34,6 @@ Other employees from your organization:
|
|||
{% for other in application.other_contacts.all %}
|
||||
{% spaceless %}{% include "emails/includes/contact.txt" with contact=other %}{% endspaceless %}
|
||||
{% endfor %}{% endif %}{% if application.anything_else %}
|
||||
Anything else we should know?
|
||||
Anything else?
|
||||
{{ application.anything_else }}
|
||||
{% endif %}
|
|
@ -26,6 +26,10 @@
|
|||
>
|
||||
<address class="usa-footer__address">
|
||||
<div class="usa-footer__contact-info grid-row grid-gap-md">
|
||||
<div class="grid-col-auto">
|
||||
<a class="usa-link" rel="noopener noreferrer" href="{% url 'home' %}">Manage your domains</a>
|
||||
</div>
|
||||
<span class=""> | </span>
|
||||
<div class="grid-col-auto">
|
||||
<a class="usa-link" rel="noopener noreferrer" target="_blank" href="{% public_site_url 'help/' %}">Help </a>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
<p class="margin-top-3">
|
||||
Required fields are marked with an asterisk (<abbr class="usa-hint usa-hint--required" title="required">*</abbr>).
|
||||
<em>Required fields are marked with an asterisk (<abbr class="usa-hint usa-hint--required" title="required">*</abbr>).</em>
|
||||
</p>
|
||||
|
|
3
src/registrar/tests/data/fake_current_federal.csv
Normal file
3
src/registrar/tests/data/fake_current_federal.csv
Normal file
|
@ -0,0 +1,3 @@
|
|||
Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email
|
||||
cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,
|
||||
ddomain3.gov,Federal,Armed Forces Retirement Home,,,,
|
|
4
src/registrar/tests/data/fake_current_full.csv
Normal file
4
src/registrar/tests/data/fake_current_full.csv
Normal file
|
@ -0,0 +1,4 @@
|
|||
Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email
|
||||
cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,
|
||||
ddomain3.gov,Federal,Armed Forces Retirement Home,,,,
|
||||
adomain2.gov,Interstate,,,,,
|
|
|
@ -158,7 +158,7 @@ class TestEmails(TestCase):
|
|||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
# spacing should be right between adjacent elements
|
||||
self.assertRegex(body, r"5557\n\nAnything else we should know?")
|
||||
self.assertRegex(body, r"5557\n\nAnything else?")
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_submission_confirmation_no_anything_else_spacing(self):
|
||||
|
@ -168,6 +168,6 @@ class TestEmails(TestCase):
|
|||
application.submit()
|
||||
_, kwargs = self.mock_client.send_email.call_args
|
||||
body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
|
||||
self.assertNotIn("Anything else we should know", body)
|
||||
self.assertNotIn("Anything else", body)
|
||||
# spacing should be right between adjacent elements
|
||||
self.assertRegex(body, r"5557\n\n----")
|
||||
|
|
|
@ -41,6 +41,8 @@ class TestGroups(TestCase):
|
|||
"change_draftdomain",
|
||||
"analyst_access_permission",
|
||||
"change_user",
|
||||
"delete_userdomainrole",
|
||||
"view_userdomainrole",
|
||||
"change_website",
|
||||
]
|
||||
|
||||
|
|
|
@ -1,11 +1,219 @@
|
|||
from django.test import TestCase
|
||||
from io import StringIO
|
||||
import csv
|
||||
import io
|
||||
from django.test import Client, RequestFactory, TestCase
|
||||
from io import StringIO
|
||||
from registrar.models.domain_information import DomainInformation
|
||||
from registrar.models.domain import Domain
|
||||
from registrar.models.user import User
|
||||
from django.contrib.auth import get_user_model
|
||||
from registrar.utility.csv_export import export_domains_to_writer
|
||||
from django.core.management import call_command
|
||||
from unittest.mock import MagicMock, call, mock_open, patch
|
||||
from api.views import get_current_federal, get_current_full
|
||||
from django.conf import settings
|
||||
from botocore.exceptions import ClientError
|
||||
import boto3_mocking
|
||||
from registrar.utility.s3_bucket import S3ClientError, S3ClientErrorCodes # type: ignore
|
||||
|
||||
|
||||
class CsvReportsTest(TestCase):
|
||||
"""Tests to determine if we are uploading our reports correctly"""
|
||||
|
||||
def setUp(self):
|
||||
"""Create fake domain data"""
|
||||
self.client = Client(HTTP_HOST="localhost:8080")
|
||||
self.factory = RequestFactory()
|
||||
username = "test_user"
|
||||
first_name = "First"
|
||||
last_name = "Last"
|
||||
email = "info@example.com"
|
||||
self.user = get_user_model().objects.create(
|
||||
username=username, first_name=first_name, last_name=last_name, email=email
|
||||
)
|
||||
|
||||
self.domain_1, _ = Domain.objects.get_or_create(name="cdomain1.gov", state=Domain.State.READY)
|
||||
self.domain_2, _ = Domain.objects.get_or_create(name="adomain2.gov", state=Domain.State.DNS_NEEDED)
|
||||
self.domain_3, _ = Domain.objects.get_or_create(name="ddomain3.gov", state=Domain.State.ON_HOLD)
|
||||
self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN)
|
||||
self.domain_4, _ = Domain.objects.get_or_create(name="bdomain4.gov", state=Domain.State.UNKNOWN)
|
||||
|
||||
self.domain_information_1, _ = DomainInformation.objects.get_or_create(
|
||||
creator=self.user,
|
||||
domain=self.domain_1,
|
||||
organization_type="federal",
|
||||
federal_agency="World War I Centennial Commission",
|
||||
federal_type="executive",
|
||||
)
|
||||
self.domain_information_2, _ = DomainInformation.objects.get_or_create(
|
||||
creator=self.user,
|
||||
domain=self.domain_2,
|
||||
organization_type="interstate",
|
||||
)
|
||||
self.domain_information_3, _ = DomainInformation.objects.get_or_create(
|
||||
creator=self.user,
|
||||
domain=self.domain_3,
|
||||
organization_type="federal",
|
||||
federal_agency="Armed Forces Retirement Home",
|
||||
)
|
||||
self.domain_information_4, _ = DomainInformation.objects.get_or_create(
|
||||
creator=self.user,
|
||||
domain=self.domain_4,
|
||||
organization_type="federal",
|
||||
federal_agency="Armed Forces Retirement Home",
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
"""Delete all faked data"""
|
||||
Domain.objects.all().delete()
|
||||
DomainInformation.objects.all().delete()
|
||||
User.objects.all().delete()
|
||||
super().tearDown()
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_generate_federal_report(self):
|
||||
"""Ensures that we correctly generate current-federal.csv"""
|
||||
mock_client = MagicMock()
|
||||
fake_open = mock_open()
|
||||
expected_file_content = [
|
||||
call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n"),
|
||||
call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"),
|
||||
call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"),
|
||||
]
|
||||
# We don't actually want to write anything for a test case,
|
||||
# we just want to verify what is being written.
|
||||
with boto3_mocking.clients.handler_for("s3", mock_client):
|
||||
with patch("builtins.open", fake_open):
|
||||
call_command("generate_current_federal_report", checkpath=False)
|
||||
content = fake_open()
|
||||
|
||||
content.write.assert_has_calls(expected_file_content)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_generate_full_report(self):
|
||||
"""Ensures that we correctly generate current-full.csv"""
|
||||
mock_client = MagicMock()
|
||||
fake_open = mock_open()
|
||||
expected_file_content = [
|
||||
call("Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\r\n"),
|
||||
call("cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,, \r\n"),
|
||||
call("ddomain3.gov,Federal,Armed Forces Retirement Home,,,, \r\n"),
|
||||
call("adomain2.gov,Interstate,,,,, \r\n"),
|
||||
]
|
||||
# We don't actually want to write anything for a test case,
|
||||
# we just want to verify what is being written.
|
||||
with boto3_mocking.clients.handler_for("s3", mock_client):
|
||||
with patch("builtins.open", fake_open):
|
||||
call_command("generate_current_full_report", checkpath=False)
|
||||
content = fake_open()
|
||||
|
||||
content.write.assert_has_calls(expected_file_content)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_not_found_full_report(self):
|
||||
"""Ensures that we get a not found when the report doesn't exist"""
|
||||
|
||||
def side_effect(Bucket, Key):
|
||||
raise ClientError({"Error": {"Code": "NoSuchKey", "Message": "No such key"}}, "get_object")
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client.get_object.side_effect = side_effect
|
||||
|
||||
response = None
|
||||
with boto3_mocking.clients.handler_for("s3", mock_client):
|
||||
with patch("boto3.client", return_value=mock_client):
|
||||
with self.assertRaises(S3ClientError) as context:
|
||||
response = self.client.get("/api/v1/get-report/current-full")
|
||||
# Check that the response has status code 500
|
||||
self.assertEqual(response.status_code, 500)
|
||||
|
||||
# Check that we get the right error back from the page
|
||||
self.assertEqual(context.exception.code, S3ClientErrorCodes.FILE_NOT_FOUND_ERROR)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_not_found_federal_report(self):
|
||||
"""Ensures that we get a not found when the report doesn't exist"""
|
||||
|
||||
def side_effect(Bucket, Key):
|
||||
raise ClientError({"Error": {"Code": "NoSuchKey", "Message": "No such key"}}, "get_object")
|
||||
|
||||
mock_client = MagicMock()
|
||||
mock_client.get_object.side_effect = side_effect
|
||||
|
||||
with boto3_mocking.clients.handler_for("s3", mock_client):
|
||||
with patch("boto3.client", return_value=mock_client):
|
||||
with self.assertRaises(S3ClientError) as context:
|
||||
response = self.client.get("/api/v1/get-report/current-federal")
|
||||
# Check that the response has status code 500
|
||||
self.assertEqual(response.status_code, 500)
|
||||
|
||||
# Check that we get the right error back from the page
|
||||
self.assertEqual(context.exception.code, S3ClientErrorCodes.FILE_NOT_FOUND_ERROR)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_load_federal_report(self):
|
||||
"""Tests the get_current_federal api endpoint"""
|
||||
self.maxDiff = None
|
||||
mock_client = MagicMock()
|
||||
mock_client_instance = mock_client.return_value
|
||||
|
||||
with open("registrar/tests/data/fake_current_federal.csv", "r") as file:
|
||||
file_content = file.read()
|
||||
|
||||
# Mock a recieved file
|
||||
mock_client_instance.get_object.return_value = {"Body": io.BytesIO(file_content.encode())}
|
||||
with boto3_mocking.clients.handler_for("s3", mock_client):
|
||||
request = self.factory.get("/fake-path")
|
||||
response = get_current_federal(request)
|
||||
|
||||
# Check that we are sending the correct calls.
|
||||
# Ensures that we are decoding the file content recieved from AWS.
|
||||
expected_call = [call.get_object(Bucket=settings.AWS_S3_BUCKET_NAME, Key="current-federal.csv")]
|
||||
mock_client_instance.assert_has_calls(expected_call)
|
||||
|
||||
# Check that the response has status code 200
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Check that the response contains what we expect
|
||||
expected_file_content = (
|
||||
"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n"
|
||||
"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\n"
|
||||
"ddomain3.gov,Federal,Armed Forces Retirement Home,,,,"
|
||||
).encode()
|
||||
|
||||
self.assertEqual(expected_file_content, response.content)
|
||||
|
||||
@boto3_mocking.patching
|
||||
def test_load_full_report(self):
|
||||
"""Tests the current-federal api link"""
|
||||
mock_client = MagicMock()
|
||||
mock_client_instance = mock_client.return_value
|
||||
|
||||
with open("registrar/tests/data/fake_current_full.csv", "r") as file:
|
||||
file_content = file.read()
|
||||
|
||||
# Mock a recieved file
|
||||
mock_client_instance.get_object.return_value = {"Body": io.BytesIO(file_content.encode())}
|
||||
with boto3_mocking.clients.handler_for("s3", mock_client):
|
||||
request = self.factory.get("/fake-path")
|
||||
response = get_current_full(request)
|
||||
|
||||
# Check that we are sending the correct calls.
|
||||
# Ensures that we are decoding the file content recieved from AWS.
|
||||
expected_call = [call.get_object(Bucket=settings.AWS_S3_BUCKET_NAME, Key="current-full.csv")]
|
||||
mock_client_instance.assert_has_calls(expected_call)
|
||||
|
||||
# Check that the response has status code 200
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Check that the response contains what we expect
|
||||
expected_file_content = (
|
||||
"Domain name,Domain type,Agency,Organization name,City,State,Security Contact Email\n"
|
||||
"cdomain1.gov,Federal - Executive,World War I Centennial Commission,,,,\n"
|
||||
"ddomain3.gov,Federal,Armed Forces Retirement Home,,,,\n"
|
||||
"adomain2.gov,Interstate,,,,,"
|
||||
).encode()
|
||||
|
||||
self.assertEqual(expected_file_content, response.content)
|
||||
|
||||
|
||||
class ExportDataTest(TestCase):
|
||||
|
|
|
@ -111,6 +111,8 @@ class TestURLAuth(TestCase):
|
|||
"/openid/callback/login/",
|
||||
"/openid/callback/logout/",
|
||||
"/api/v1/available/whitehouse.gov",
|
||||
"/api/v1/get-report/current-federal",
|
||||
"/api/v1/get-report/current-full",
|
||||
]
|
||||
|
||||
def assertURLIsProtectedByAuth(self, url):
|
||||
|
|
|
@ -1083,7 +1083,7 @@ class DomainApplicationTests(TestWithUser, WebTest):
|
|||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Edit" link
|
||||
detail_page = home_page.click("Manage")
|
||||
detail_page = home_page.click("Manage", index=0)
|
||||
self.assertContains(detail_page, "Federal: an agency of the U.S. government")
|
||||
|
||||
|
||||
|
@ -2123,7 +2123,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Manage" link
|
||||
detail_page = home_page.click("Manage")
|
||||
detail_page = home_page.click("Manage", index=0)
|
||||
self.assertContains(detail_page, "city.gov")
|
||||
self.assertContains(detail_page, "city1.gov")
|
||||
self.assertContains(detail_page, "Chief Tester")
|
||||
|
@ -2143,7 +2143,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Manage" link
|
||||
detail_page = home_page.click("Manage")
|
||||
detail_page = home_page.click("Manage", index=0)
|
||||
self.assertContains(detail_page, "city.gov")
|
||||
self.assertContains(detail_page, "Chief Tester")
|
||||
self.assertContains(detail_page, "testy@town.com")
|
||||
|
@ -2158,7 +2158,7 @@ class TestApplicationStatus(TestWithUser, WebTest):
|
|||
home_page = self.app.get("/")
|
||||
self.assertContains(home_page, "city.gov")
|
||||
# click the "Manage" link
|
||||
detail_page = home_page.click("Manage")
|
||||
detail_page = home_page.click("Manage", index=0)
|
||||
self.assertContains(detail_page, "city.gov")
|
||||
self.assertContains(detail_page, "city1.gov")
|
||||
self.assertContains(detail_page, "Chief Tester")
|
||||
|
|
149
src/registrar/utility/s3_bucket.py
Normal file
149
src/registrar/utility/s3_bucket.py
Normal file
|
@ -0,0 +1,149 @@
|
|||
"""Utilities for accessing an AWS S3 bucket"""
|
||||
|
||||
from enum import IntEnum
|
||||
import boto3
|
||||
from botocore.exceptions import ClientError
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class S3ClientErrorCodes(IntEnum):
|
||||
"""Used for S3ClientError
|
||||
Error code overview:
|
||||
- 1 ACCESS_S3_CLIENT_ERROR
|
||||
- 2 UPLOAD_FILE_ERROR
|
||||
- 3 FILE_NOT_FOUND_ERROR
|
||||
- 4 GET_FILE_ERROR
|
||||
"""
|
||||
|
||||
ACCESS_S3_CLIENT_ERROR = 1
|
||||
UPLOAD_FILE_ERROR = 2
|
||||
FILE_NOT_FOUND_ERROR = 3
|
||||
GET_FILE_ERROR = 4
|
||||
|
||||
|
||||
class S3ClientError(RuntimeError):
|
||||
"""
|
||||
Custom exception class for handling errors related to interactions with the S3 storage service via boto3.client.
|
||||
|
||||
This class maps error codes to human-readable error messages. When an instance of S3ClientError is created,
|
||||
an error code can be passed in to set the error message for that instance.
|
||||
|
||||
Attributes:
|
||||
_error_mapping: A dictionary mapping error codes to error messages.
|
||||
code: The error code for a specific instance of S3ClientError.
|
||||
message: The error message for a specific instance of S3ClientError, determined by the error code.
|
||||
"""
|
||||
|
||||
_error_mapping = {
|
||||
S3ClientErrorCodes.ACCESS_S3_CLIENT_ERROR: "Failed to establish a connection with the storage service.",
|
||||
S3ClientErrorCodes.UPLOAD_FILE_ERROR: "File upload to the storage service failed.",
|
||||
S3ClientErrorCodes.FILE_NOT_FOUND_ERROR: "Requested file not found in the storage service.",
|
||||
S3ClientErrorCodes.GET_FILE_ERROR: (
|
||||
"Retrieval of the requested file from " "the storage service failed due to an unspecified error."
|
||||
),
|
||||
}
|
||||
|
||||
def __init__(self, *args, code=None, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.code = code
|
||||
if self.code in self._error_mapping:
|
||||
self.message = self._error_mapping.get(self.code)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.message}"
|
||||
|
||||
|
||||
class S3ClientHelper:
|
||||
"""
|
||||
A helper class for interacting with Amazon S3 via the boto3 client.
|
||||
|
||||
This class simplifies the process of initializing the boto3 client,
|
||||
uploading files to S3, and retrieving files from S3.
|
||||
|
||||
Attributes:
|
||||
boto_client: The boto3 client used to interact with S3.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
try:
|
||||
self.boto_client = boto3.client(
|
||||
"s3",
|
||||
region_name=settings.AWS_S3_REGION,
|
||||
aws_access_key_id=settings.AWS_S3_ACCESS_KEY_ID,
|
||||
aws_secret_access_key=settings.AWS_S3_SECRET_ACCESS_KEY,
|
||||
config=settings.BOTO_CONFIG,
|
||||
)
|
||||
except Exception as exc:
|
||||
raise S3ClientError(code=S3ClientErrorCodes.ACCESS_S3_CLIENT_ERROR) from exc
|
||||
|
||||
def get_bucket_name(self):
|
||||
"""
|
||||
Retrieves the name of the S3 bucket.
|
||||
|
||||
This method returns the name of the S3 bucket as defined in the application's settings.
|
||||
|
||||
Returns:
|
||||
str: The name of the S3 bucket.
|
||||
"""
|
||||
|
||||
return settings.AWS_S3_BUCKET_NAME
|
||||
|
||||
def upload_file(self, file_path, file_name):
|
||||
"""
|
||||
Uploads a file to the S3 bucket.
|
||||
|
||||
This method attempts to upload a file to the S3 bucket using the boto3 client.
|
||||
If an exception occurs during the upload process, it raises an S3ClientError with an UPLOAD_FILE_ERROR code.
|
||||
|
||||
Args:
|
||||
file_path (str): The path of the file to upload.
|
||||
file_name (str): The name to give to the file in the S3 bucket.
|
||||
|
||||
Returns:
|
||||
dict: The response from the boto3 client's upload_file method.
|
||||
|
||||
Raises:
|
||||
S3ClientError: If the file cannot be uploaded to the S3 bucket.
|
||||
"""
|
||||
|
||||
try:
|
||||
response = self.boto_client.upload_file(file_path, self.get_bucket_name(), file_name)
|
||||
except Exception as exc:
|
||||
raise S3ClientError(code=S3ClientErrorCodes.UPLOAD_FILE_ERROR) from exc
|
||||
return response
|
||||
|
||||
def get_file(self, file_name, decode_to_utf=False):
|
||||
"""
|
||||
Retrieves a file from the S3 bucket and returns its content.
|
||||
|
||||
This method attempts to retrieve a file from the S3 bucket using the boto3 client.
|
||||
If the file is not found, it raises an S3ClientError with a FILE_NOT_FOUND_ERROR code.
|
||||
For any other exceptions during the retrieval process, it raises an S3ClientError with a GET_FILE_ERROR code.
|
||||
|
||||
Args:
|
||||
file_name (str): The name of the file to retrieve from the S3 bucket.
|
||||
decode_to_utf (bool, optional): If True, the file content is decoded from bytes to a UTF-8 string.
|
||||
Defaults to False.
|
||||
|
||||
Returns:
|
||||
bytes or str: The content of the file. If decode_to_utf is True, this is a string. Otherwise, its bytes.
|
||||
|
||||
Raises:
|
||||
S3ClientError: If the file cannot be retrieved from the S3 bucket.
|
||||
"""
|
||||
|
||||
try:
|
||||
response = self.boto_client.get_object(Bucket=self.get_bucket_name(), Key=file_name)
|
||||
except ClientError as exc:
|
||||
if exc.response["Error"]["Code"] == "NoSuchKey":
|
||||
raise S3ClientError(code=S3ClientErrorCodes.FILE_NOT_FOUND_ERROR) from exc
|
||||
else:
|
||||
raise S3ClientError(code=S3ClientErrorCodes.GET_FILE_ERROR) from exc
|
||||
except Exception as exc:
|
||||
raise S3ClientError(code=S3ClientErrorCodes.GET_FILE_ERROR) from exc
|
||||
|
||||
file_content = response["Body"].read()
|
||||
if decode_to_utf:
|
||||
return file_content.decode("utf-8")
|
||||
else:
|
||||
return file_content
|
|
@ -86,7 +86,7 @@ class ApplicationWizard(ApplicationWizardPermissionView, TemplateView):
|
|||
Step.YOUR_CONTACT: _("Your contact information"),
|
||||
Step.OTHER_CONTACTS: _("Other employees from your organization"),
|
||||
Step.NO_OTHER_CONTACTS: _("No other employees from your organization?"),
|
||||
Step.ANYTHING_ELSE: _("Anything else we should know?"),
|
||||
Step.ANYTHING_ELSE: _("Anything else?"),
|
||||
Step.REQUIREMENTS: _("Requirements for operating .gov domains"),
|
||||
Step.REVIEW: _("Review and submit your domain request"),
|
||||
}
|
||||
|
|
|
@ -280,6 +280,7 @@ class DomainNameserversView(DomainFormBaseView):
|
|||
form.fields["server"].required = True
|
||||
else:
|
||||
form.fields["server"].required = False
|
||||
form.fields["server"].label += " (optional)"
|
||||
form.fields["domain"].initial = self.object.name
|
||||
return formset
|
||||
|
||||
|
@ -643,7 +644,46 @@ class DomainAddUserView(DomainFormBaseView):
|
|||
"""Get an absolute URL for this domain."""
|
||||
return self.request.build_absolute_uri(reverse("domain", kwargs={"pk": self.object.id}))
|
||||
|
||||
def _make_invitation(self, email_address):
|
||||
def _send_domain_invitation_email(self, email: str, add_success=True):
|
||||
"""Performs the sending of the domain invitation email,
|
||||
does not make a domain information object
|
||||
email: string- email to send to
|
||||
add_success: bool- default True indicates:
|
||||
adding a success message to the view if the email sending succeeds"""
|
||||
# created a new invitation in the database, so send an email
|
||||
domainInfoResults = DomainInformation.objects.filter(domain=self.object)
|
||||
domainInfo = domainInfoResults.first()
|
||||
first = ""
|
||||
last = ""
|
||||
if domainInfo is not None:
|
||||
first = domainInfo.creator.first_name
|
||||
last = domainInfo.creator.last_name
|
||||
full_name = f"{first} {last}"
|
||||
|
||||
try:
|
||||
send_templated_email(
|
||||
"emails/domain_invitation.txt",
|
||||
"emails/domain_invitation_subject.txt",
|
||||
to_address=email,
|
||||
context={
|
||||
"domain_url": self._domain_abs_url(),
|
||||
"domain": self.object,
|
||||
"full_name": full_name,
|
||||
},
|
||||
)
|
||||
except EmailSendingError:
|
||||
messages.warning(self.request, "Could not send email invitation.")
|
||||
logger.warn(
|
||||
"Could not sent email invitation to %s for domain %s",
|
||||
email,
|
||||
self.object,
|
||||
exc_info=True,
|
||||
)
|
||||
else:
|
||||
if add_success:
|
||||
messages.success(self.request, f"Invited {email} to this domain.")
|
||||
|
||||
def _make_invitation(self, email_address: str):
|
||||
"""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)
|
||||
if not created:
|
||||
|
@ -653,34 +693,7 @@ class DomainAddUserView(DomainFormBaseView):
|
|||
f"{email_address} has already been invited to this domain.",
|
||||
)
|
||||
else:
|
||||
# created a new invitation in the database, so send an email
|
||||
domaininfo = DomainInformation.objects.filter(domain=self.object)
|
||||
first = domaininfo.first().creator.first_name
|
||||
last = domaininfo.first().creator.last_name
|
||||
full_name = f"{first} {last}"
|
||||
|
||||
try:
|
||||
send_templated_email(
|
||||
"emails/domain_invitation.txt",
|
||||
"emails/domain_invitation_subject.txt",
|
||||
to_address=email_address,
|
||||
context={
|
||||
"domain_url": self._domain_abs_url(),
|
||||
"domain": self.object,
|
||||
"full_name": full_name,
|
||||
},
|
||||
)
|
||||
except EmailSendingError:
|
||||
messages.warning(self.request, "Could not send email invitation.")
|
||||
logger.warn(
|
||||
"Could not sent email invitation to %s for domain %s",
|
||||
email_address,
|
||||
self.object,
|
||||
exc_info=True,
|
||||
)
|
||||
else:
|
||||
messages.success(self.request, f"Invited {email_address} to this domain.")
|
||||
|
||||
self._send_domain_invitation_email(email=email_address)
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
def form_valid(self, form):
|
||||
|
@ -692,6 +705,9 @@ class DomainAddUserView(DomainFormBaseView):
|
|||
except User.DoesNotExist:
|
||||
# no matching user, go make an invitation
|
||||
return self._make_invitation(requested_email)
|
||||
else:
|
||||
# if user already exists then just send an email
|
||||
self._send_domain_invitation_email(requested_email, add_success=False)
|
||||
|
||||
try:
|
||||
UserDomainRole.objects.create(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue