mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-18 18:39:21 +02:00
Initial architecture for rejection reason
This commit is contained in:
parent
48b9206ffc
commit
9b23262d61
15 changed files with 306 additions and 82 deletions
|
@ -1994,7 +1994,7 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
|||
# 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)
|
||||
default_email = get_rejection_reason_default_email(obj, obj.rejection_reason)
|
||||
if obj.rejection_reason_email:
|
||||
emails = get_all_rejection_reason_emails(obj)
|
||||
is_custom_email = obj.rejection_reason_email not in emails.values()
|
||||
|
|
|
@ -348,7 +348,7 @@ function initializeWidgetOnList(list, parentId) {
|
|||
* status select and to show/hide the rejection reason
|
||||
*/
|
||||
(function (){
|
||||
let rejectionReasonFormGroup = document.querySelector('.field-rejection_reason')
|
||||
let rejectionReasonFormGroup = document.querySelector('.field-rejection_reason');
|
||||
// This is the "action needed reason" field
|
||||
let actionNeededReasonFormGroup = document.querySelector('.field-action_needed_reason');
|
||||
// This is the "Email" field
|
||||
|
@ -501,7 +501,7 @@ function initializeWidgetOnList(list, parentId) {
|
|||
})();
|
||||
|
||||
class CustomizableEmailBase {
|
||||
constructor(dropdown, textarea, textareaPlaceholder, directEditButton, modalTrigger, modalConfirm, formLabel, lastSentEmailContent, apiUrl) {
|
||||
constructor(dropdown, textarea, textareaPlaceholder, directEditButton, modalTrigger, modalConfirm, formLabel, lastSentEmailContent, apiUrl, textAreaFormGroup, dropdownFormGroup) {
|
||||
this.dropdown = dropdown;
|
||||
this.textarea = textarea;
|
||||
this.textareaPlaceholder = textareaPlaceholder;
|
||||
|
@ -512,6 +512,11 @@ class CustomizableEmailBase {
|
|||
this.lastSentEmailContent = lastSentEmailContent;
|
||||
this.apiUrl = apiUrl;
|
||||
|
||||
// These fields are hidden on pageload
|
||||
this.textAreaFormGroup = textAreaFormGroup;
|
||||
this.dropdownFormGroup = dropdownFormGroup;
|
||||
this.statusSelect = document.getElementById("id_status");
|
||||
|
||||
this.domainRequestId = this.dropdown ? document.getElementById("domain_request_id").value : null
|
||||
this.initialDropdownValue = this.dropdown ? this.dropdown.value : null;
|
||||
this.initialEmailValue = this.textarea ? this.textarea.value : null;
|
||||
|
@ -520,11 +525,47 @@ class CustomizableEmailBase {
|
|||
if (lastSentEmailContent && textarea) {
|
||||
this.isEmailAlreadySentConst = lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, '');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Handle showing/hiding the related fields on page load.
|
||||
initializeFormGroups(statusToCheck, sessionVariableName) {
|
||||
let isStatus = statusSelect.value == statusToCheck;
|
||||
|
||||
// Initial handling of these groups.
|
||||
updateFormGroupVisibility(isStatus, isStatus);
|
||||
|
||||
// Listen to change events and handle rejectionReasonFormGroup display, then save status to session storage
|
||||
this.statusSelect.addEventListener('change', () => {
|
||||
// Show the action needed field if the status is what we expect.
|
||||
// Then track if its shown or hidden in our session cache.
|
||||
isStatus = statusSelect.value == statusToCheck;
|
||||
updateFormGroupVisibility(isStatus, isStatus);
|
||||
addOrRemoveSessionBoolean(sessionVariableName, add=isStatus);
|
||||
});
|
||||
|
||||
// Listen to Back/Forward button navigation and handle rejectionReasonFormGroup display based on session storage
|
||||
// When you navigate using forward/back after changing status but not saving, when you land back on the DA page the
|
||||
// status select will say (for example) Rejected but the selected option can be something else. To manage the show/hide
|
||||
// accurately for this edge case, we use cache and test for the back/forward navigation.
|
||||
const observer = new PerformanceObserver((list) => {
|
||||
list.getEntries().forEach((entry) => {
|
||||
if (entry.type === "back_forward") {
|
||||
let showTextAreaFormGroup = sessionStorage.getItem(sessionVariableName) !== null;
|
||||
updateFormGroupVisibility(showTextAreaFormGroup, isStatus);
|
||||
}
|
||||
});
|
||||
});
|
||||
observer.observe({ type: "navigation" });
|
||||
}
|
||||
|
||||
updateFormGroupVisibility(showTextAreaFormGroup, showdropDownFormGroup) {
|
||||
showTextAreaFormGroup ? showElement(this.textAreaFormGroup) : hideElement(this.textAreaFormGroup);
|
||||
showdropDownFormGroup ? showElement(this.dropdownFormGroup) : hideElement(this.dropdownFormGroup);
|
||||
}
|
||||
|
||||
initializeDropdown(errorMessage) {
|
||||
this.dropdown.addEventListener("change", () => {
|
||||
console.log(this.dropdown)
|
||||
let reason = this.dropdown.value;
|
||||
if (this.initialDropdownValue !== this.dropdown.value || this.initialEmailValue !== this.textarea.value) {
|
||||
let searchParams = new URLSearchParams(
|
||||
|
@ -542,7 +583,7 @@ class CustomizableEmailBase {
|
|||
if (data.error) {
|
||||
console.error("Error in AJAX call: " + data.error);
|
||||
}else {
|
||||
this.textarea.value = data.action_needed_email;
|
||||
this.textarea.value = data.email;
|
||||
}
|
||||
this.updateUserInterface(reason);
|
||||
})
|
||||
|
@ -578,11 +619,17 @@ class CustomizableEmailBase {
|
|||
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");
|
||||
this.showPlaceholderNoReason();
|
||||
} 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");
|
||||
this.showPlaceholderOtherReason();
|
||||
} else {
|
||||
this.showReadonlyTextarea();
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function that makes overriding the readonly textarea easy
|
||||
showReadonlyTextarea() {
|
||||
// A triggering selection is selected, all hands on board:
|
||||
this.textarea.setAttribute('readonly', true);
|
||||
showElement(this.textarea);
|
||||
|
@ -602,6 +649,15 @@ class CustomizableEmailBase {
|
|||
this.formLabel.innerHTML = "Email:";
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function that makes overriding the placeholder reason easy
|
||||
showPlaceholderNoReason() {
|
||||
this.showPlaceholder("Email:", "Select a reason to see email");
|
||||
}
|
||||
|
||||
// Helper function that makes overriding the placeholder reason easy
|
||||
showPlaceholderOtherReason() {
|
||||
this.showPlaceholder("Email:", "No email will be sent");
|
||||
}
|
||||
|
||||
showPlaceholder(formLabelText, placeholderText) {
|
||||
|
@ -629,6 +685,11 @@ class customActionNeededEmail extends CustomizableEmailBase {
|
|||
|
||||
let apiContainer = document.getElementById("get-action-needed-email-for-user-json")
|
||||
const apiUrl = apiContainer ? apiContainer.value : null;
|
||||
|
||||
// These fields are hidden on pageload
|
||||
const textAreaFormGroup = document.querySelector('.field-action_needed_reason');
|
||||
const dropdownFormGroup = document.querySelector('.field-action_needed_reason_email');
|
||||
|
||||
super(
|
||||
dropdown,
|
||||
textarea,
|
||||
|
@ -638,16 +699,32 @@ class customActionNeededEmail extends CustomizableEmailBase {
|
|||
modalConfirm,
|
||||
formLabel,
|
||||
lastSentEmailContent,
|
||||
apiUrl
|
||||
apiUrl,
|
||||
textAreaFormGroup,
|
||||
dropdownFormGroup,
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
loadActionNeededEmail() {
|
||||
if (this.textAreaFormGroup && this.dropdownFormGroup) {
|
||||
this.initializeFormGroups("action needed", "showActionNeededReason");
|
||||
}
|
||||
this.updateUserInterface(this.dropdown.value);
|
||||
this.initializeDropdown("Error when attempting to grab action needed email: ")
|
||||
this.initializeModalConfirm()
|
||||
this.initializeDirectEditButton()
|
||||
}
|
||||
|
||||
// Overrides the placeholder text when no reason is selected
|
||||
showPlaceholderNoReason() {
|
||||
this.showPlaceholder("Email:", "Select an action needed reason to see email");
|
||||
}
|
||||
|
||||
// Overrides the placeholder text when the reason other is selected
|
||||
showPlaceholderOtherReason() {
|
||||
this.showPlaceholder("Email:", "No email will be sent");
|
||||
}
|
||||
}
|
||||
|
||||
/** An IIFE that hooks to the show/hide button underneath action needed reason.
|
||||
|
@ -675,8 +752,13 @@ class customRejectedEmail extends CustomizableEmailBase {
|
|||
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")
|
||||
let apiContainer = document.getElementById("get-rejection-email-for-user-json");
|
||||
const apiUrl = apiContainer ? apiContainer.value : null;
|
||||
|
||||
// These fields are hidden on pageload
|
||||
const textAreaFormGroup = document.querySelector('.field-rejection_reason');
|
||||
const dropdownFormGroup = document.querySelector('.field-rejection_reason_email');
|
||||
|
||||
super(
|
||||
dropdown,
|
||||
textarea,
|
||||
|
@ -686,16 +768,31 @@ class customRejectedEmail extends CustomizableEmailBase {
|
|||
modalConfirm,
|
||||
formLabel,
|
||||
lastSentEmailContent,
|
||||
apiUrl
|
||||
apiUrl,
|
||||
textAreaFormGroup,
|
||||
dropdownFormGroup,
|
||||
);
|
||||
}
|
||||
|
||||
loadRejectedEmail() {
|
||||
if (this.textAreaFormGroup && this.dropdownFormGroup) {
|
||||
this.initializeFormGroups("rejected", "showRejectionReason");
|
||||
}
|
||||
this.updateUserInterface(this.dropdown.value);
|
||||
this.initializeDropdown("Error when attempting to grab rejected email: ")
|
||||
this.initializeModalConfirm()
|
||||
this.initializeDirectEditButton()
|
||||
}
|
||||
|
||||
// Overrides the placeholder text when no reason is selected
|
||||
showPlaceholderNoReason() {
|
||||
this.showPlaceholder("Email:", "Select a rejection reason to see email");
|
||||
}
|
||||
|
||||
// Overrides the placeholder text when the reason other is selected
|
||||
showPlaceholderOtherReason() {
|
||||
this.showPlaceholder("Email:", "No email will be sent");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ from registrar.views.utility.api_views import (
|
|||
get_senior_official_from_federal_agency_json,
|
||||
get_federal_and_portfolio_types_from_federal_agency_json,
|
||||
get_action_needed_email_for_user_json,
|
||||
get_rejection_email_for_user_json,
|
||||
)
|
||||
from registrar.views.domains_json import get_domains_json
|
||||
from registrar.views.utility import always_404
|
||||
|
@ -159,6 +160,11 @@ urlpatterns = [
|
|||
get_action_needed_email_for_user_json,
|
||||
name="get-action-needed-email-for-user-json",
|
||||
),
|
||||
path(
|
||||
"admin/api/get-rejection-email-for-user-json/",
|
||||
get_rejection_email_for_user_json,
|
||||
name="get-rejection-email-for-user-json",
|
||||
),
|
||||
path("admin/", admin.site.urls),
|
||||
path(
|
||||
"reports/export_data_type_user/",
|
||||
|
|
|
@ -640,15 +640,16 @@ class DomainRequest(TimeStampedModel):
|
|||
# Actually updates the organization_type field
|
||||
org_type_helper.create_or_update_organization_type()
|
||||
|
||||
def _cache_status_and_action_needed_reason(self):
|
||||
def _cache_status_and_status_reasons(self):
|
||||
"""Maintains a cache of properties so we can avoid a DB call"""
|
||||
self._cached_action_needed_reason = self.action_needed_reason
|
||||
self._cached_rejection_reason = self.rejection_reason
|
||||
self._cached_status = self.status
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
# Store original values for caching purposes. Used to compare them on save.
|
||||
self._cache_status_and_action_needed_reason()
|
||||
self._cache_status_and_status_reasons()
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
"""Save override for custom properties"""
|
||||
|
@ -662,21 +663,42 @@ class DomainRequest(TimeStampedModel):
|
|||
|
||||
# Handle the action needed email.
|
||||
# An email is sent out when action_needed_reason is changed or added.
|
||||
if self.action_needed_reason and self.status == self.DomainRequestStatus.ACTION_NEEDED:
|
||||
self.sync_action_needed_reason()
|
||||
if self.status == self.DomainRequestStatus.ACTION_NEEDED:
|
||||
self.send_another_status_reason_email(
|
||||
checked_status=self.DomainRequestStatus.ACTION_NEEDED,
|
||||
old_reason=self._cached_action_needed_reason,
|
||||
new_reason=self.action_needed_reason,
|
||||
excluded_reasons=[DomainRequest.ActionNeededReasons.OTHER],
|
||||
email_to_send=self.action_needed_reason_email
|
||||
)
|
||||
elif self.status == self.DomainRequestStatus.REJECTED:
|
||||
self.send_another_status_reason_email(
|
||||
checked_status=self.DomainRequestStatus.REJECTED,
|
||||
old_reason=self._cached_rejection_reason,
|
||||
new_reason=self.rejection_reason,
|
||||
excluded_reasons=[DomainRequest.RejectionReasons.OTHER],
|
||||
email_to_send=self.rejection_reason_email,
|
||||
)
|
||||
|
||||
# Update the cached values after saving
|
||||
self._cache_status_and_action_needed_reason()
|
||||
self._cache_status_and_status_reasons()
|
||||
|
||||
def sync_action_needed_reason(self):
|
||||
"""Checks if we need to send another action needed email"""
|
||||
was_already_action_needed = self._cached_status == self.DomainRequestStatus.ACTION_NEEDED
|
||||
reason_exists = self._cached_action_needed_reason is not None and self.action_needed_reason is not None
|
||||
reason_changed = self._cached_action_needed_reason != self.action_needed_reason
|
||||
if was_already_action_needed and reason_exists and reason_changed:
|
||||
# We don't send emails out in state "other"
|
||||
if self.action_needed_reason != self.ActionNeededReasons.OTHER:
|
||||
self._send_action_needed_reason_email(email_content=self.action_needed_reason_email)
|
||||
def send_another_status_reason_email(self, checked_status, old_reason, new_reason, excluded_reasons, email_to_send):
|
||||
"""Helper function to send out a second status email when the status remains the same,
|
||||
but the reason has changed."""
|
||||
|
||||
# If the status itself changed, then we already sent out an email
|
||||
if self._cached_status != checked_status or old_reason is None:
|
||||
return
|
||||
|
||||
# We should never send an email if no reason was specified
|
||||
# Additionally, Don't send out emails for reasons that shouldn't send them
|
||||
if new_reason is None or self.action_needed_reason in excluded_reasons:
|
||||
return
|
||||
|
||||
# Only send out an email if the underlying email itself changed
|
||||
if old_reason != new_reason:
|
||||
self._send_custom_status_update_email(email_content=email_to_send)
|
||||
|
||||
def sync_yes_no_form_fields(self):
|
||||
"""Some yes/no forms use a db field to track whether it was checked or not.
|
||||
|
@ -806,9 +828,7 @@ class DomainRequest(TimeStampedModel):
|
|||
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
|
||||
|
||||
bcc_address = settings.DEFAULT_FROM_EMAIL if settings.IS_PRODUCTION else ""
|
||||
self._send_status_update_email(
|
||||
new_status="action needed",
|
||||
email_template=f"emails/includes/custom_email.txt",
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
<input id="has_audit_logs" class="display-none" value="{%if filtered_audit_log_entries %}true{% else %}false{% endif %}"/>
|
||||
{% url 'get-action-needed-email-for-user-json' as url %}
|
||||
<input id="get-action-needed-email-for-user-json" class="display-none" value="{{ url }}" />
|
||||
{% url 'get-rejection-email-for-user-json' as url_2 %}
|
||||
<input id="get-rejection-email-for-user-json" class="display-none" value="{{ url_2 }}" />
|
||||
{% for fieldset in adminform %}
|
||||
{% comment %}
|
||||
TODO: this will eventually need to be changed to something like this
|
||||
|
|
|
@ -226,6 +226,94 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
|||
<input id="last-sent-email-content" class="display-none" value="None">
|
||||
{% endif %}
|
||||
|
||||
{% elif field.field.name == "rejection_reason_email" %}
|
||||
<div class="margin-top-05 text-faded field-rejection_reason_email__placeholder">
|
||||
–
|
||||
</div>
|
||||
|
||||
{{ field.field }}
|
||||
|
||||
<button
|
||||
aria-label="Edit email in textarea"
|
||||
type="button"
|
||||
class="usa-button usa-button--unstyled usa-button--dja-link-color usa-button__small-text margin-left-1 text-no-underline field-rejection_reason_email__edit flex-align-self-start"
|
||||
><img src="/public/admin/img/icon-changelink.svg" alt="Change"> Edit email</button
|
||||
>
|
||||
<a
|
||||
href="#email-already-sent-modal"
|
||||
class="usa-button usa-button--unstyled usa-button--dja-link-color usa-button__small-text text-no-underline margin-left-1 field-rejection_reason_email__modal-trigger flex-align-self-start"
|
||||
aria-controls="email-already-sent-modal"
|
||||
data-open-modal
|
||||
><img src="/public/admin/img/icon-changelink.svg" alt="Change"> Edit email</a
|
||||
>
|
||||
<div
|
||||
class="usa-modal"
|
||||
id="email-already-sent-modal"
|
||||
aria-labelledby="Are you sure you want to edit this email?"
|
||||
aria-describedby="The creator of this request already received an email"
|
||||
>
|
||||
<div class="usa-modal__content">
|
||||
<div class="usa-modal__main">
|
||||
<h2 class="usa-modal__heading">
|
||||
Are you sure you want to edit this email?
|
||||
</h2>
|
||||
<div class="usa-prose">
|
||||
<p>
|
||||
The creator of this request already received an email for this status/reason:
|
||||
</p>
|
||||
<ul>
|
||||
<li class="font-body-sm">Status: <b>Rejected</b></li>
|
||||
<li class="font-body-sm">Reason: <b>{{ original_object.get_rejection_reason_display }}</b></li>
|
||||
</ul>
|
||||
<p>
|
||||
If you edit this email's text, <b>the system will send another email</b> to
|
||||
the creator after you “save” your changes. If you do not want to send another email, click “cancel” below.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="usa-modal__footer">
|
||||
<ul class="usa-button-group">
|
||||
<li class="usa-button-group__item">
|
||||
<button
|
||||
type="submit"
|
||||
class="usa-button"
|
||||
id="confirm-edit-email"
|
||||
data-close-modal
|
||||
>
|
||||
Yes, continue editing
|
||||
</button>
|
||||
</li>
|
||||
<li class="usa-button-group__item">
|
||||
<button
|
||||
type="button"
|
||||
class="usa-button usa-button--unstyled padding-105 text-center"
|
||||
name="_cancel_edit_email"
|
||||
data-close-modal
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
class="usa-button usa-modal__close"
|
||||
aria-label="Close this window"
|
||||
data-close-modal
|
||||
>
|
||||
<svg class="usa-icon" aria-hidden="true" focusable="false" role="img">
|
||||
<use xlink:href="{%static 'img/sprite.svg'%}#close"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if original_object.rejection_reason_reason_email %}
|
||||
<input id="last-sent-email-content" class="display-none" value="{{original_object.action_needed_reason_email}}">
|
||||
{% else %}
|
||||
<input id="last-sent-email-content" class="display-none" value="None">
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{{ field.field }}
|
||||
{% endif %}
|
||||
|
|
|
@ -1,8 +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" %}
|
||||
{% include "emails/includes/status_change_rejected_header.txt" %}
|
||||
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" %}
|
||||
{% include "emails/includes/email_footer.txt" %}
|
||||
{% endautoescape %}
|
|
@ -1,5 +1,5 @@
|
|||
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||
{% include "emails/includes/status_change_rejected_header.txt" %}
|
||||
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
|
||||
|
@ -11,5 +11,5 @@ 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" %}
|
||||
{% include "emails/includes/email_footer.txt" %}
|
||||
{% endautoescape %}
|
|
@ -1,5 +1,5 @@
|
|||
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||
{% include "emails/includes/status_change_rejected_header.txt" %}
|
||||
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
|
||||
|
@ -11,5 +11,5 @@ Read more about our practice of approving one domain per online service
|
|||
|
||||
If you have questions or comments, reply to this email.
|
||||
|
||||
{% include "emails/includes/email_footer.html" %}
|
||||
{% include "emails/includes/email_footer.txt" %}
|
||||
{% endautoescape %}
|
|
@ -1,5 +1,5 @@
|
|||
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||
{% include "emails/includes/status_change_rejected_header.txt" %}
|
||||
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
|
||||
|
@ -10,5 +10,5 @@ Learn more about eligibility for .gov domains
|
|||
|
||||
If you have questions or comments, reply to this email.
|
||||
|
||||
{% include "emails/includes/email_footer.html" %}
|
||||
{% include "emails/includes/email_footer.txt" %}
|
||||
{% endautoescape %}
|
|
@ -1,15 +0,0 @@
|
|||
{% 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 %}
|
|
@ -1,5 +1,5 @@
|
|||
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||
{% include "emails/includes/status_change_rejected_header.txt" %}
|
||||
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
|
||||
|
@ -11,5 +11,5 @@ Learn more about:
|
|||
|
||||
If you have questions or comments, reply to this email.
|
||||
|
||||
{% include "emails/includes/email_footer.html" %}
|
||||
{% include "emails/includes/email_footer.txt" %}
|
||||
{% endautoescape %}
|
|
@ -1,5 +1,5 @@
|
|||
{% autoescape off %}{# In a text file, we don't want to have HTML entities escaped #}
|
||||
{% include "emails/includes/status_change_rejected_header.html" %}
|
||||
{% include "emails/includes/status_change_rejected_header.txt" %}
|
||||
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
|
||||
|
@ -10,5 +10,5 @@ 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" %}
|
||||
{% include "emails/includes/email_footer.txt" %}
|
||||
{% endautoescape %}
|
|
@ -20,7 +20,7 @@ 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",
|
||||
path_root="emails/action_needed_reasons",
|
||||
reason=action_needed_reason,
|
||||
excluded_reasons=[DomainRequest.ActionNeededReasons.OTHER]
|
||||
)
|
||||
|
@ -40,12 +40,12 @@ def get_all_rejection_reason_emails(domain_request):
|
|||
)
|
||||
|
||||
|
||||
def get_rejection_reason_default_email(domain_request, action_needed_reason):
|
||||
def get_rejection_reason_default_email(domain_request, rejection_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,
|
||||
reason=rejection_reason,
|
||||
excluded_reasons=[DomainRequest.RejectionReasons.OTHER]
|
||||
)
|
||||
|
||||
|
@ -56,6 +56,7 @@ def _get_all_default_emails(reasons, path_root, excluded_reasons, domain_request
|
|||
emails[reason.value] = _get_default_email(
|
||||
domain_request, path_root, reason, excluded_reasons
|
||||
)
|
||||
return emails
|
||||
|
||||
def _get_default_email(domain_request, path_root, reason, excluded_reasons=None):
|
||||
if not reason:
|
||||
|
|
|
@ -4,7 +4,7 @@ from django.forms.models import model_to_dict
|
|||
from registrar.models import FederalAgency, SeniorOfficial, DomainRequest
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from registrar.utility.admin_helpers import get_all_action_needed_reason_emails
|
||||
from registrar.utility.admin_helpers import get_action_needed_reason_default_email, get_rejection_reason_default_email
|
||||
from registrar.models.portfolio import Portfolio
|
||||
from registrar.utility.constants import BranchChoices
|
||||
|
||||
|
@ -90,5 +90,30 @@ def get_action_needed_email_for_user_json(request):
|
|||
return JsonResponse({"error": "No domain_request_id specified"}, status=404)
|
||||
|
||||
domain_request = DomainRequest.objects.filter(id=domain_request_id).first()
|
||||
emails = get_all_action_needed_reason_emails(domain_request)
|
||||
return JsonResponse({"action_needed_email": emails.get(reason)}, status=200)
|
||||
|
||||
email = get_action_needed_reason_default_email(domain_request, reason)
|
||||
return JsonResponse({"email": email}, status=200)
|
||||
|
||||
|
||||
@login_required
|
||||
@staff_member_required
|
||||
def get_rejection_email_for_user_json(request):
|
||||
"""Returns a default rejection email for a given user"""
|
||||
|
||||
# This API is only accessible to admins and analysts
|
||||
superuser_perm = request.user.has_perm("registrar.full_access_permission")
|
||||
analyst_perm = request.user.has_perm("registrar.analyst_access_permission")
|
||||
if not request.user.is_authenticated or not any([analyst_perm, superuser_perm]):
|
||||
return JsonResponse({"error": "You do not have access to this resource"}, status=403)
|
||||
|
||||
reason = request.GET.get("reason")
|
||||
domain_request_id = request.GET.get("domain_request_id")
|
||||
if not reason:
|
||||
return JsonResponse({"error": "No reason specified"}, status=404)
|
||||
|
||||
if not domain_request_id:
|
||||
return JsonResponse({"error": "No domain_request_id specified"}, status=404)
|
||||
|
||||
domain_request = DomainRequest.objects.filter(id=domain_request_id).first()
|
||||
email = get_rejection_reason_default_email(domain_request, reason)
|
||||
return JsonResponse({"email": email}, status=200)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue