mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-17 18:09:25 +02:00
Refactor
This commit is contained in:
parent
f9e60ac237
commit
61b1ceeb9c
16 changed files with 435 additions and 138 deletions
|
@ -21,7 +21,12 @@ from epplibwrapper.errors import ErrorCode, RegistryError
|
||||||
from registrar.models.user_domain_role import UserDomainRole
|
from registrar.models.user_domain_role import UserDomainRole
|
||||||
from waffle.admin import FlagAdmin
|
from waffle.admin import FlagAdmin
|
||||||
from waffle.models import Sample, Switch
|
from waffle.models import Sample, Switch
|
||||||
from registrar.utility.admin_helpers import get_all_action_needed_reason_emails, get_action_needed_reason_default_email
|
from registrar.utility.admin_helpers import (
|
||||||
|
get_all_action_needed_reason_emails,
|
||||||
|
get_action_needed_reason_default_email,
|
||||||
|
get_all_rejection_reason_emails,
|
||||||
|
get_rejection_reason_default_email,
|
||||||
|
)
|
||||||
from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website, SeniorOfficial
|
from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website, SeniorOfficial
|
||||||
from registrar.utility.constants import BranchChoices
|
from registrar.utility.constants import BranchChoices
|
||||||
from registrar.utility.errors import FSMDomainRequestError, FSMErrorCodes
|
from registrar.utility.errors import FSMDomainRequestError, FSMErrorCodes
|
||||||
|
@ -234,6 +239,7 @@ class DomainRequestAdminForm(forms.ModelForm):
|
||||||
}
|
}
|
||||||
labels = {
|
labels = {
|
||||||
"action_needed_reason_email": "Email",
|
"action_needed_reason_email": "Email",
|
||||||
|
"rejection_reason_email": "Email",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -1755,6 +1761,7 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
"status_history",
|
"status_history",
|
||||||
"status",
|
"status",
|
||||||
"rejection_reason",
|
"rejection_reason",
|
||||||
|
"rejection_reason_email",
|
||||||
"action_needed_reason",
|
"action_needed_reason",
|
||||||
"action_needed_reason_email",
|
"action_needed_reason_email",
|
||||||
"investigator",
|
"investigator",
|
||||||
|
@ -1938,24 +1945,16 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
original_obj = models.DomainRequest.objects.get(pk=obj.pk)
|
original_obj = models.DomainRequest.objects.get(pk=obj.pk)
|
||||||
|
|
||||||
# == Handle action_needed_reason == #
|
# == Handle action_needed_reason == #
|
||||||
|
action_needed_reason_changed = obj.action_needed_reason != original_obj.action_needed_reason
|
||||||
|
if action_needed_reason_changed:
|
||||||
|
obj = self._handle_action_needed_reason(request, obj, original_obj)
|
||||||
|
|
||||||
reason_changed = obj.action_needed_reason != original_obj.action_needed_reason
|
# == Handle rejection_reason == #
|
||||||
if reason_changed:
|
rejection_reason_changed = obj.rejection_reason != original_obj.rejection_reason
|
||||||
# Track the fact that we sent out an email
|
if rejection_reason_changed:
|
||||||
request.session["action_needed_email_sent"] = True
|
obj = self._handle_rejection_reason(request, obj, original_obj)
|
||||||
|
|
||||||
# Set the action_needed_reason_email to the default if nothing exists.
|
|
||||||
# Since this check occurs after save, if the user enters a value then we won't update.
|
|
||||||
|
|
||||||
default_email = get_action_needed_reason_default_email(request, obj, obj.action_needed_reason)
|
|
||||||
if obj.action_needed_reason_email:
|
|
||||||
emails = get_all_action_needed_reason_emails(request, obj)
|
|
||||||
is_custom_email = obj.action_needed_reason_email not in emails.values()
|
|
||||||
if not is_custom_email:
|
|
||||||
obj.action_needed_reason_email = default_email
|
|
||||||
else:
|
|
||||||
obj.action_needed_reason_email = default_email
|
|
||||||
|
|
||||||
|
# == Handle allowed emails == #
|
||||||
if obj.status in DomainRequest.get_statuses_that_send_emails() and not settings.IS_PRODUCTION:
|
if obj.status in DomainRequest.get_statuses_that_send_emails() and not settings.IS_PRODUCTION:
|
||||||
self._check_for_valid_email(request, obj)
|
self._check_for_valid_email(request, obj)
|
||||||
|
|
||||||
|
@ -1971,6 +1970,40 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
if should_save:
|
if should_save:
|
||||||
return super().save_model(request, obj, form, change)
|
return super().save_model(request, obj, form, change)
|
||||||
|
|
||||||
|
def _handle_action_needed_reason(self, request, obj, original_obj):
|
||||||
|
# Track the fact that we sent out an email
|
||||||
|
request.session["action_needed_email_sent"] = True
|
||||||
|
|
||||||
|
# Set the action_needed_reason_email to the default if nothing exists.
|
||||||
|
# Since this check occurs after save, if the user enters a value then we won't update.
|
||||||
|
|
||||||
|
default_email = get_action_needed_reason_default_email(obj, obj.action_needed_reason)
|
||||||
|
if obj.action_needed_reason_email:
|
||||||
|
emails = get_all_action_needed_reason_emails(obj)
|
||||||
|
is_custom_email = obj.action_needed_reason_email not in emails.values()
|
||||||
|
if not is_custom_email:
|
||||||
|
obj.action_needed_reason_email = default_email
|
||||||
|
else:
|
||||||
|
obj.action_needed_reason_email = default_email
|
||||||
|
return obj
|
||||||
|
|
||||||
|
def _handle_rejection_reason(self, request, obj, original_obj):
|
||||||
|
# Track the fact that we sent out an email
|
||||||
|
request.session["rejection_reason_email_sent"] = True
|
||||||
|
|
||||||
|
# Set the rejection_reason_email to the default if nothing exists.
|
||||||
|
# Since this check occurs after save, if the user enters a value then we won't update.
|
||||||
|
|
||||||
|
default_email = get_rejection_reason_default_email(obj, obj.action_needed_reason)
|
||||||
|
if obj.rejection_reason_email:
|
||||||
|
emails = get_all_rejection_reason_emails(obj)
|
||||||
|
is_custom_email = obj.rejection_reason_email not in emails.values()
|
||||||
|
if not is_custom_email:
|
||||||
|
obj.rejection_reason_email = default_email
|
||||||
|
else:
|
||||||
|
obj.rejection_reason_email = default_email
|
||||||
|
return obj
|
||||||
|
|
||||||
def _check_for_valid_email(self, request, obj):
|
def _check_for_valid_email(self, request, obj):
|
||||||
"""Certain emails are whitelisted in non-production environments,
|
"""Certain emails are whitelisted in non-production environments,
|
||||||
so we should display that information using this function.
|
so we should display that information using this function.
|
||||||
|
|
|
@ -500,83 +500,41 @@ function initializeWidgetOnList(list, parentId) {
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
class CustomizableEmailBase {
|
||||||
|
constructor(dropdown, textarea, textareaPlaceholder, directEditButton, modalTrigger, modalConfirm, formLabel, lastSentEmailContent, apiUrl) {
|
||||||
|
this.dropdown = dropdown;
|
||||||
|
this.textarea = textarea;
|
||||||
|
this.textareaPlaceholder = textareaPlaceholder;
|
||||||
|
this.directEditButton = directEditButton;
|
||||||
|
this.modalTrigger = modalTrigger;
|
||||||
|
this.modalConfirm = modalConfirm;
|
||||||
|
this.formLabel = formLabel;
|
||||||
|
this.lastSentEmailContent = lastSentEmailContent;
|
||||||
|
this.apiUrl = apiUrl;
|
||||||
|
|
||||||
/** An IIFE that hooks to the show/hide button underneath action needed reason.
|
this.domainRequestId = this.dropdown ? document.getElementById("domain_request_id").value : null
|
||||||
* This shows the auto generated email on action needed reason.
|
this.initialDropdownValue = this.dropdown ? this.dropdown.value : null;
|
||||||
*/
|
this.initialEmailValue = this.textarea ? this.textarea.value : null;
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const dropdown = document.getElementById("id_action_needed_reason");
|
|
||||||
const textarea = document.getElementById("id_action_needed_reason_email")
|
|
||||||
const domainRequestId = dropdown ? document.getElementById("domain_request_id").value : null
|
|
||||||
const textareaPlaceholder = document.querySelector(".field-action_needed_reason_email__placeholder");
|
|
||||||
const directEditButton = document.querySelector('.field-action_needed_reason_email__edit');
|
|
||||||
const modalTrigger = document.querySelector('.field-action_needed_reason_email__modal-trigger');
|
|
||||||
const modalConfirm = document.getElementById('confirm-edit-email');
|
|
||||||
const formLabel = document.querySelector('label[for="id_action_needed_reason_email"]');
|
|
||||||
let lastSentEmailContent = document.getElementById("last-sent-email-content");
|
|
||||||
const initialDropdownValue = dropdown ? dropdown.value : null;
|
|
||||||
const initialEmailValue = textarea.value;
|
|
||||||
|
|
||||||
// We will use the const to control the modal
|
this.isEmailAlreadySentConst;
|
||||||
let isEmailAlreadySentConst = lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, '');
|
if (lastSentEmailContent && textarea) {
|
||||||
// We will use the function to control the label and help
|
this.isEmailAlreadySentConst = lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, '');
|
||||||
function isEmailAlreadySent() {
|
|
||||||
return lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dropdown || !textarea || !domainRequestId || !formLabel || !modalConfirm) return;
|
|
||||||
const apiUrl = document.getElementById("get-action-needed-email-for-user-json").value;
|
|
||||||
|
|
||||||
function updateUserInterface(reason) {
|
|
||||||
if (!reason) {
|
|
||||||
// No reason selected, we will set the label to "Email", show the "Make a selection" placeholder, hide the trigger, textarea, hide the help text
|
|
||||||
formLabel.innerHTML = "Email:";
|
|
||||||
textareaPlaceholder.innerHTML = "Select an action needed reason to see email";
|
|
||||||
showElement(textareaPlaceholder);
|
|
||||||
hideElement(directEditButton);
|
|
||||||
hideElement(modalTrigger);
|
|
||||||
hideElement(textarea);
|
|
||||||
} else if (reason === 'other') {
|
|
||||||
// 'Other' selected, we will set the label to "Email", show the "No email will be sent" placeholder, hide the trigger, textarea, hide the help text
|
|
||||||
formLabel.innerHTML = "Email:";
|
|
||||||
textareaPlaceholder.innerHTML = "No email will be sent";
|
|
||||||
showElement(textareaPlaceholder);
|
|
||||||
hideElement(directEditButton);
|
|
||||||
hideElement(modalTrigger);
|
|
||||||
hideElement(textarea);
|
|
||||||
} else {
|
|
||||||
// A triggering selection is selected, all hands on board:
|
|
||||||
textarea.setAttribute('readonly', true);
|
|
||||||
showElement(textarea);
|
|
||||||
hideElement(textareaPlaceholder);
|
|
||||||
|
|
||||||
if (isEmailAlreadySentConst) {
|
|
||||||
hideElement(directEditButton);
|
|
||||||
showElement(modalTrigger);
|
|
||||||
} else {
|
|
||||||
showElement(directEditButton);
|
|
||||||
hideElement(modalTrigger);
|
|
||||||
}
|
|
||||||
if (isEmailAlreadySent()) {
|
|
||||||
formLabel.innerHTML = "Email sent to creator:";
|
|
||||||
} else {
|
|
||||||
formLabel.innerHTML = "Email:";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize UI
|
initializeDropdown(errorMessage) {
|
||||||
updateUserInterface(dropdown.value);
|
this.dropdown.addEventListener("change", () => {
|
||||||
|
console.log(this.dropdown)
|
||||||
dropdown.addEventListener("change", function() {
|
let reason = this.dropdown.value;
|
||||||
const reason = dropdown.value;
|
if (this.initialDropdownValue !== this.dropdown.value || this.initialEmailValue !== this.textarea.value) {
|
||||||
// Update the UI
|
let searchParams = new URLSearchParams(
|
||||||
updateUserInterface(reason);
|
{
|
||||||
if (reason && reason !== "other") {
|
"reason": reason,
|
||||||
// If it's not the initial value
|
"domain_request_id": this.domainRequestId,
|
||||||
if (initialDropdownValue !== dropdown.value || initialEmailValue !== textarea.value) {
|
}
|
||||||
|
);
|
||||||
// Replace the email content
|
// Replace the email content
|
||||||
fetch(`${apiUrl}?reason=${reason}&domain_request_id=${domainRequestId}`)
|
fetch(`${this.apiUrl}?${searchParams.toString()}`)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
return response.json().then(data => data);
|
return response.json().then(data => data);
|
||||||
})
|
})
|
||||||
|
@ -584,30 +542,174 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
console.error("Error in AJAX call: " + data.error);
|
console.error("Error in AJAX call: " + data.error);
|
||||||
}else {
|
}else {
|
||||||
textarea.value = data.action_needed_email;
|
this.textarea.value = data.action_needed_email;
|
||||||
}
|
}
|
||||||
updateUserInterface(reason);
|
this.updateUserInterface(reason);
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
console.error("Error action needed email: ", error)
|
console.error(errorMessage, error)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeModalConfirm() {
|
||||||
|
this.modalConfirm.addEventListener("click", () => {
|
||||||
|
this.textarea.removeAttribute('readonly');
|
||||||
|
this.textarea.focus();
|
||||||
|
hideElement(this.directEditButton);
|
||||||
|
hideElement(this.modalTrigger);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeDirectEditButton() {
|
||||||
|
this.directEditButton.addEventListener("click", () => {
|
||||||
|
this.textarea.removeAttribute('readonly');
|
||||||
|
this.textarea.focus();
|
||||||
|
hideElement(this.directEditButton);
|
||||||
|
hideElement(this.modalTrigger);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
isEmailAlreadySent() {
|
||||||
|
return this.lastSentEmailContent.value.replace(/\s+/g, '') === this.textarea.value.replace(/\s+/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUserInterface(reason) {
|
||||||
|
if (!reason) {
|
||||||
|
// No reason selected, we will set the label to "Email", show the "Make a selection" placeholder, hide the trigger, textarea, hide the help text
|
||||||
|
this.showPlaceholder("Email:", "Select an action needed reason to see email");
|
||||||
|
} else if (reason === 'other') {
|
||||||
|
// 'Other' selected, we will set the label to "Email", show the "No email will be sent" placeholder, hide the trigger, textarea, hide the help text
|
||||||
|
this.showPlaceholder("Email:", "No email will be sent");
|
||||||
|
} else {
|
||||||
|
// A triggering selection is selected, all hands on board:
|
||||||
|
this.textarea.setAttribute('readonly', true);
|
||||||
|
showElement(this.textarea);
|
||||||
|
hideElement(this.textareaPlaceholder);
|
||||||
|
|
||||||
|
if (this.isEmailAlreadySentConst) {
|
||||||
|
hideElement(this.directEditButton);
|
||||||
|
showElement(this.modalTrigger);
|
||||||
|
} else {
|
||||||
|
showElement(this.directEditButton);
|
||||||
|
hideElement(this.modalTrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isEmailAlreadySent()) {
|
||||||
|
this.formLabel.innerHTML = "Email sent to creator:";
|
||||||
|
} else {
|
||||||
|
this.formLabel.innerHTML = "Email:";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
});
|
showPlaceholder(formLabelText, placeholderText) {
|
||||||
|
this.formLabel.innerHTML = formLabelText;
|
||||||
|
this.textareaPlaceholder.innerHTML = placeholderText;
|
||||||
|
showElement(this.textareaPlaceholder);
|
||||||
|
hideElement(this.directEditButton);
|
||||||
|
hideElement(this.modalTrigger);
|
||||||
|
hideElement(this.textarea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
modalConfirm.addEventListener("click", () => {
|
|
||||||
textarea.removeAttribute('readonly');
|
|
||||||
textarea.focus();
|
class customActionNeededEmail extends CustomizableEmailBase {
|
||||||
hideElement(directEditButton);
|
constructor() {
|
||||||
hideElement(modalTrigger);
|
const dropdown = document.getElementById("id_action_needed_reason");
|
||||||
});
|
const textarea = document.getElementById("id_action_needed_reason_email")
|
||||||
directEditButton.addEventListener("click", () => {
|
const textareaPlaceholder = document.querySelector(".field-action_needed_reason_email__placeholder");
|
||||||
textarea.removeAttribute('readonly');
|
const directEditButton = document.querySelector('.field-action_needed_reason_email__edit');
|
||||||
textarea.focus();
|
const modalTrigger = document.querySelector('.field-action_needed_reason_email__modal-trigger');
|
||||||
hideElement(directEditButton);
|
const modalConfirm = document.getElementById('confirm-edit-email');
|
||||||
hideElement(modalTrigger);
|
const formLabel = document.querySelector('label[for="id_action_needed_reason_email"]');
|
||||||
});
|
const lastSentEmailContent = document.getElementById("last-sent-email-content");
|
||||||
|
|
||||||
|
let apiContainer = document.getElementById("get-action-needed-email-for-user-json")
|
||||||
|
const apiUrl = apiContainer ? apiContainer.value : null;
|
||||||
|
super(
|
||||||
|
dropdown,
|
||||||
|
textarea,
|
||||||
|
textareaPlaceholder,
|
||||||
|
directEditButton,
|
||||||
|
modalTrigger,
|
||||||
|
modalConfirm,
|
||||||
|
formLabel,
|
||||||
|
lastSentEmailContent,
|
||||||
|
apiUrl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadActionNeededEmail() {
|
||||||
|
this.updateUserInterface(this.dropdown.value);
|
||||||
|
this.initializeDropdown("Error when attempting to grab action needed email: ")
|
||||||
|
this.initializeModalConfirm()
|
||||||
|
this.initializeDirectEditButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** An IIFE that hooks to the show/hide button underneath action needed reason.
|
||||||
|
* This shows the auto generated email on action needed reason.
|
||||||
|
*/
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const customEmail = new customActionNeededEmail();
|
||||||
|
if (!customEmail.dropdown || !customEmail.textarea || !customEmail.domainRequestId || !customEmail.formLabel || !customEmail.modalConfirm){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize UI
|
||||||
|
customEmail.loadActionNeededEmail()
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
class customRejectedEmail extends CustomizableEmailBase {
|
||||||
|
constructor() {
|
||||||
|
const dropdown = document.getElementById("id_rejection_reason");
|
||||||
|
const textarea = document.getElementById("id_rejection_reason_email")
|
||||||
|
const textareaPlaceholder = document.querySelector(".field-rejection_reason_email__placeholder");
|
||||||
|
const directEditButton = document.querySelector('.field-rejection_reason_email__edit');
|
||||||
|
const modalTrigger = document.querySelector('.field-rejection_reason_email__modal-trigger');
|
||||||
|
const modalConfirm = document.getElementById('confirm-edit-email');
|
||||||
|
const formLabel = document.querySelector('label[for="id_rejection_reason_email"]');
|
||||||
|
const lastSentEmailContent = document.getElementById("last-sent-email-content");
|
||||||
|
|
||||||
|
let apiContainer = document.getElementById("get-rejection-reason-email-for-user-json")
|
||||||
|
const apiUrl = apiContainer ? apiContainer.value : null;
|
||||||
|
super(
|
||||||
|
dropdown,
|
||||||
|
textarea,
|
||||||
|
textareaPlaceholder,
|
||||||
|
directEditButton,
|
||||||
|
modalTrigger,
|
||||||
|
modalConfirm,
|
||||||
|
formLabel,
|
||||||
|
lastSentEmailContent,
|
||||||
|
apiUrl
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadRejectedEmail() {
|
||||||
|
this.updateUserInterface(this.dropdown.value);
|
||||||
|
this.initializeDropdown("Error when attempting to grab rejected email: ")
|
||||||
|
this.initializeModalConfirm()
|
||||||
|
this.initializeDirectEditButton()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** An IIFE that hooks to the show/hide button underneath rejected reason.
|
||||||
|
* This shows the auto generated email on action needed reason.
|
||||||
|
*/
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const customEmail = new customRejectedEmail();
|
||||||
|
if (!customEmail.dropdown || !customEmail.textarea || !customEmail.domainRequestId || !customEmail.formLabel || !customEmail.modalConfirm){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize UI
|
||||||
|
customEmail.loadRejectedEmail()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -300,6 +300,11 @@ class DomainRequest(TimeStampedModel):
|
||||||
blank=True,
|
blank=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
rejection_reason_email = models.TextField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
)
|
||||||
|
|
||||||
action_needed_reason = models.TextField(
|
action_needed_reason = models.TextField(
|
||||||
choices=ActionNeededReasons.choices,
|
choices=ActionNeededReasons.choices,
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -798,6 +803,21 @@ class DomainRequest(TimeStampedModel):
|
||||||
except EmailSendingError:
|
except EmailSendingError:
|
||||||
logger.warning("Failed to send confirmation email", exc_info=True)
|
logger.warning("Failed to send confirmation email", exc_info=True)
|
||||||
|
|
||||||
|
def _send_custom_status_update_email(self, email_content):
|
||||||
|
"""Wrapper for `_send_status_update_email` that bcc's help@get.gov
|
||||||
|
and sends an email equivalent to the 'email_content' variable."""
|
||||||
|
if settings.IS_PRODUCTION:
|
||||||
|
bcc_address = settings.DEFAULT_FROM_EMAIL
|
||||||
|
|
||||||
|
self._send_status_update_email(
|
||||||
|
new_status="action needed",
|
||||||
|
email_template=f"emails/includes/custom_email.txt",
|
||||||
|
email_template_subject=f"emails/status_change_subject.txt",
|
||||||
|
bcc_address=bcc_address,
|
||||||
|
custom_email_content=email_content,
|
||||||
|
wrap_email=True,
|
||||||
|
)
|
||||||
|
|
||||||
def investigator_exists_and_is_staff(self):
|
def investigator_exists_and_is_staff(self):
|
||||||
"""Checks if the current investigator is in a valid state for a state transition"""
|
"""Checks if the current investigator is in a valid state for a state transition"""
|
||||||
is_valid = True
|
is_valid = True
|
||||||
|
@ -901,7 +921,7 @@ class DomainRequest(TimeStampedModel):
|
||||||
target=DomainRequestStatus.ACTION_NEEDED,
|
target=DomainRequestStatus.ACTION_NEEDED,
|
||||||
conditions=[domain_is_not_active, investigator_exists_and_is_staff],
|
conditions=[domain_is_not_active, investigator_exists_and_is_staff],
|
||||||
)
|
)
|
||||||
def action_needed(self, send_email=True):
|
def action_needed(self):
|
||||||
"""Send back an domain request that is under investigation or rejected.
|
"""Send back an domain request that is under investigation or rejected.
|
||||||
|
|
||||||
This action is logged.
|
This action is logged.
|
||||||
|
@ -924,27 +944,7 @@ class DomainRequest(TimeStampedModel):
|
||||||
# Send out an email if an action needed reason exists
|
# Send out an email if an action needed reason exists
|
||||||
if self.action_needed_reason and self.action_needed_reason != self.ActionNeededReasons.OTHER:
|
if self.action_needed_reason and self.action_needed_reason != self.ActionNeededReasons.OTHER:
|
||||||
email_content = self.action_needed_reason_email
|
email_content = self.action_needed_reason_email
|
||||||
self._send_action_needed_reason_email(send_email, email_content)
|
self._send_custom_status_update_email(email_content)
|
||||||
|
|
||||||
def _send_action_needed_reason_email(self, send_email=True, email_content=None):
|
|
||||||
"""Sends out an automatic email for each valid action needed reason provided"""
|
|
||||||
|
|
||||||
email_template_name = "custom_email.txt"
|
|
||||||
email_template_subject_name = f"{self.action_needed_reason}_subject.txt"
|
|
||||||
|
|
||||||
bcc_address = ""
|
|
||||||
if settings.IS_PRODUCTION:
|
|
||||||
bcc_address = settings.DEFAULT_FROM_EMAIL
|
|
||||||
|
|
||||||
self._send_status_update_email(
|
|
||||||
new_status="action needed",
|
|
||||||
email_template=f"emails/action_needed_reasons/{email_template_name}",
|
|
||||||
email_template_subject=f"emails/action_needed_reasons/{email_template_subject_name}",
|
|
||||||
send_email=send_email,
|
|
||||||
bcc_address=bcc_address,
|
|
||||||
custom_email_content=email_content,
|
|
||||||
wrap_email=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
field="status",
|
field="status",
|
||||||
|
@ -1051,6 +1051,11 @@ class DomainRequest(TimeStampedModel):
|
||||||
"emails/status_change_rejected_subject.txt",
|
"emails/status_change_rejected_subject.txt",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Send out an email if a rejection reason exists
|
||||||
|
if self.rejection_reason:
|
||||||
|
email_content = self.rejection_reason_email
|
||||||
|
self._send_custom_status_update_email(email_content)
|
||||||
|
|
||||||
@transition(
|
@transition(
|
||||||
field="status",
|
field="status",
|
||||||
source=[
|
source=[
|
||||||
|
|
10
src/registrar/templates/emails/includes/email_footer.txt
Normal file
10
src/registrar/templates/emails/includes/email_footer.txt
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
THANK YOU
|
||||||
|
.Gov helps the public identify official, trusted information. Thank you for requesting a .gov domain.
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
The .gov team
|
||||||
|
Contact us: <https://get.gov/contact/>
|
||||||
|
Learn about .gov <https://get.gov>
|
||||||
|
|
||||||
|
The .gov registry is a part of the Cybersecurity and Infrastructure Security Agency (CISA) <https://cisa.gov/>
|
|
@ -0,0 +1,8 @@
|
||||||
|
Hi, {{ recipient.first_name }}.
|
||||||
|
|
||||||
|
Your .gov domain request has been rejected.
|
||||||
|
|
||||||
|
DOMAIN REQUESTED: {{ domain_request.requested_domain.name }}
|
||||||
|
REQUEST RECEIVED ON: {{ domain_request.last_submitted_date|date }}
|
||||||
|
STATUS: Rejected
|
||||||
|
----------------------------------------------------------------
|
|
@ -0,0 +1,8 @@
|
||||||
|
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||||
|
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||||
|
REJECTION REASON
|
||||||
|
Your domain request was rejected because we could not verify the organizational
|
||||||
|
contacts you provided. If you have questions or comments, reply to this email.
|
||||||
|
|
||||||
|
{% include "emails/includes/email_footer.html" %}
|
||||||
|
{% endautoescape %}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||||
|
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||||
|
REJECTION REASON
|
||||||
|
Your domain request was rejected because it does not meet our naming requirements.
|
||||||
|
Domains should uniquely identify a government organization and be clear to the
|
||||||
|
general public. Learn more about naming requirements for your type of organization
|
||||||
|
<https://get.gov/domains/choosing/>.
|
||||||
|
|
||||||
|
|
||||||
|
YOU CAN SUBMIT A NEW REQUEST
|
||||||
|
We encourage you to request a domain that meets our requirements. If you have
|
||||||
|
questions or want to discuss potential domain names, reply to this email.
|
||||||
|
|
||||||
|
{% include "emails/includes/email_footer.html" %}
|
||||||
|
{% endautoescape %}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||||
|
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||||
|
REJECTION REASON
|
||||||
|
Your domain request was rejected because {{ domain_request.organization_name }} has a .gov domain. Our
|
||||||
|
practice is to approve one domain per online service per government organization. We
|
||||||
|
evaluate additional requests on a case-by-case basis. You did not provide sufficient
|
||||||
|
justification for an additional domain.
|
||||||
|
|
||||||
|
Read more about our practice of approving one domain per online service
|
||||||
|
<https://get.gov/domains/before/#one-domain-per-service>.
|
||||||
|
|
||||||
|
If you have questions or comments, reply to this email.
|
||||||
|
|
||||||
|
{% include "emails/includes/email_footer.html" %}
|
||||||
|
{% endautoescape %}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||||
|
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||||
|
REJECTION REASON
|
||||||
|
Your domain request was rejected because we determined that {{ domain_request.organization_name }} is not
|
||||||
|
eligible for a .gov domain. .Gov domains are only available to official U.S.-based
|
||||||
|
government organizations.
|
||||||
|
|
||||||
|
Learn more about eligibility for .gov domains
|
||||||
|
<https://get.gov/domains/eligibility/>.
|
||||||
|
|
||||||
|
If you have questions or comments, reply to this email.
|
||||||
|
|
||||||
|
{% include "emails/includes/email_footer.html" %}
|
||||||
|
{% endautoescape %}
|
15
src/registrar/templates/emails/rejection_reasons/other.txt
Normal file
15
src/registrar/templates/emails/rejection_reasons/other.txt
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||||
|
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||||
|
YOU CAN SUBMIT A NEW REQUEST
|
||||||
|
If your organization is eligible for a .gov domain and you meet our other requirements, you can submit a new request.
|
||||||
|
|
||||||
|
Learn more about:
|
||||||
|
- Eligibility for a .gov domain <https://get.gov/domains/eligibility>
|
||||||
|
- Choosing a .gov domain name <https://get.gov/domains/choosing>
|
||||||
|
|
||||||
|
|
||||||
|
NEED ASSISTANCE?
|
||||||
|
If you have questions about this domain request or need help choosing a new domain name, reply to this email.
|
||||||
|
|
||||||
|
{% include "emails/includes/email_footer.html" %}
|
||||||
|
{% endautoescape %}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||||
|
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||||
|
REJECTION REASON
|
||||||
|
Your domain request was rejected because the purpose you provided did not meet our
|
||||||
|
requirements. You didn’t provide enough information about how you intend to use the
|
||||||
|
domain.
|
||||||
|
|
||||||
|
Learn more about:
|
||||||
|
- Eligibility for a .gov domain <https://get.gov/domains/eligibility>
|
||||||
|
- What you can and can’t do with .gov domains <https://get.gov/domains/requirements/>
|
||||||
|
|
||||||
|
If you have questions or comments, reply to this email.
|
||||||
|
|
||||||
|
{% include "emails/includes/email_footer.html" %}
|
||||||
|
{% endautoescape %}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||||
|
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||||
|
REJECTION REASON
|
||||||
|
Your domain request was rejected because we don’t believe you’re eligible to request a
|
||||||
|
.gov domain on behalf of {{ domain_request.organization_name }}. You must be a government employee, or be
|
||||||
|
working on behalf of a government organization, to request a .gov domain.
|
||||||
|
|
||||||
|
|
||||||
|
DEMONSTRATE ELIGIBILITY
|
||||||
|
If you can provide more information that demonstrates your eligibility, or you want to
|
||||||
|
discuss further, reply to this email.
|
||||||
|
|
||||||
|
{% include "emails/includes/email_footer.html" %}
|
||||||
|
{% endautoescape %}
|
|
@ -2,23 +2,66 @@ from registrar.models.domain_request import DomainRequest
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
|
|
||||||
|
|
||||||
def get_all_action_needed_reason_emails(request, domain_request):
|
def get_all_action_needed_reason_emails(domain_request):
|
||||||
"""Returns a dictionary of every action needed reason and its associated email
|
"""Returns a dictionary of every action needed reason and its associated email
|
||||||
for this particular domain request."""
|
for this particular domain request."""
|
||||||
|
return _get_all_default_emails(
|
||||||
|
reasons=DomainRequest.ActionNeededReasons,
|
||||||
|
# Where the emails are stored. This assumes that it contains a list of .txt files with the reason.
|
||||||
|
path_root="emails/action_needed_reasons",
|
||||||
|
# What reasons don't send out emails (none is handled automagically)
|
||||||
|
excluded_reasons=[DomainRequest.ActionNeededReasons.OTHER],
|
||||||
|
# Who to send it to, and from what domain request to reference
|
||||||
|
domain_request=domain_request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_action_needed_reason_default_email(domain_request, action_needed_reason):
|
||||||
|
"""Returns the default email associated with the given action needed reason"""
|
||||||
|
return _get_default_email(
|
||||||
|
domain_request,
|
||||||
|
path_root="emails/rejection_reasons",
|
||||||
|
reason=action_needed_reason,
|
||||||
|
excluded_reasons=[DomainRequest.ActionNeededReasons.OTHER]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_rejection_reason_emails(domain_request):
|
||||||
|
"""Returns a dictionary of every rejection reason and its associated email
|
||||||
|
for this particular domain request."""
|
||||||
|
return _get_all_default_emails(
|
||||||
|
reasons=DomainRequest.RejectionReasons,
|
||||||
|
# Where the emails are stored. This assumes that it contains a list of .txt files with the reason.
|
||||||
|
path_root="emails/rejection_reasons",
|
||||||
|
# What reasons don't send out emails (none is handled automagically)
|
||||||
|
excluded_reasons=[DomainRequest.RejectionReasons.OTHER],
|
||||||
|
# Who to send it to, and from what domain request to reference
|
||||||
|
domain_request=domain_request
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_rejection_reason_default_email(domain_request, action_needed_reason):
|
||||||
|
"""Returns the default email associated with the given rejection reason"""
|
||||||
|
return _get_default_email(
|
||||||
|
domain_request,
|
||||||
|
path_root="emails/rejection_reasons",
|
||||||
|
reason=action_needed_reason,
|
||||||
|
excluded_reasons=[DomainRequest.RejectionReasons.OTHER]
|
||||||
|
)
|
||||||
|
|
||||||
|
def _get_all_default_emails(reasons, path_root, excluded_reasons, domain_request):
|
||||||
emails = {}
|
emails = {}
|
||||||
for action_needed_reason in domain_request.ActionNeededReasons:
|
for reason in reasons:
|
||||||
# Map the action_needed_reason to its default email
|
# Map the reason to its default email
|
||||||
emails[action_needed_reason.value] = get_action_needed_reason_default_email(
|
emails[reason.value] = _get_default_email(
|
||||||
request, domain_request, action_needed_reason.value
|
domain_request, path_root, reason, excluded_reasons
|
||||||
)
|
)
|
||||||
|
|
||||||
return emails
|
def _get_default_email(domain_request, path_root, reason, excluded_reasons=None):
|
||||||
|
if not reason:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if excluded_reasons and reason in excluded_reasons:
|
||||||
def get_action_needed_reason_default_email(request, domain_request, action_needed_reason):
|
|
||||||
"""Returns the default email associated with the given action needed reason"""
|
|
||||||
if not action_needed_reason or action_needed_reason == DomainRequest.ActionNeededReasons.OTHER:
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
recipient = domain_request.creator
|
recipient = domain_request.creator
|
||||||
|
@ -26,7 +69,7 @@ def get_action_needed_reason_default_email(request, domain_request, action_neede
|
||||||
context = {"domain_request": domain_request, "recipient": recipient}
|
context = {"domain_request": domain_request, "recipient": recipient}
|
||||||
|
|
||||||
# Get the email body
|
# Get the email body
|
||||||
template_path = f"emails/action_needed_reasons/{action_needed_reason}.txt"
|
template_path = f"{path_root}/{reason}.txt"
|
||||||
|
|
||||||
email_body_text = get_template(template_path).render(context=context)
|
email_body_text = get_template(template_path).render(context=context)
|
||||||
email_body_text_cleaned = None
|
email_body_text_cleaned = None
|
||||||
|
|
|
@ -90,5 +90,5 @@ def get_action_needed_email_for_user_json(request):
|
||||||
return JsonResponse({"error": "No domain_request_id specified"}, status=404)
|
return JsonResponse({"error": "No domain_request_id specified"}, status=404)
|
||||||
|
|
||||||
domain_request = DomainRequest.objects.filter(id=domain_request_id).first()
|
domain_request = DomainRequest.objects.filter(id=domain_request_id).first()
|
||||||
emails = get_all_action_needed_reason_emails(request, domain_request)
|
emails = get_all_action_needed_reason_emails(domain_request)
|
||||||
return JsonResponse({"action_needed_email": emails.get(reason)}, status=200)
|
return JsonResponse({"action_needed_email": emails.get(reason)}, status=200)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue