mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-05-19 10:59:21 +02:00
Use API instead of json
This commit is contained in:
parent
ec9f085371
commit
487c2e5d62
7 changed files with 107 additions and 73 deletions
|
@ -21,6 +21,7 @@ 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.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
|
||||||
|
@ -1956,9 +1957,9 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
# Set the action_needed_reason_email to the default if nothing exists.
|
# 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.
|
# Since this check occurs after save, if the user enters a value then we won't update.
|
||||||
|
|
||||||
default_email = self._get_action_needed_reason_default_email(obj, obj.action_needed_reason)
|
default_email = get_action_needed_reason_default_email(request, obj, obj.action_needed_reason)
|
||||||
if obj.action_needed_reason_email:
|
if obj.action_needed_reason_email:
|
||||||
emails = self.get_all_action_needed_reason_emails(obj)
|
emails = get_all_action_needed_reason_emails(request, obj)
|
||||||
is_custom_email = obj.action_needed_reason_email not in emails.values()
|
is_custom_email = obj.action_needed_reason_email not in emails.values()
|
||||||
if not is_custom_email:
|
if not is_custom_email:
|
||||||
obj.action_needed_reason_email = default_email
|
obj.action_needed_reason_email = default_email
|
||||||
|
@ -2170,8 +2171,6 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
# Initialize extra_context and add filtered entries
|
# Initialize extra_context and add filtered entries
|
||||||
extra_context = extra_context or {}
|
extra_context = extra_context or {}
|
||||||
extra_context["filtered_audit_log_entries"] = filtered_audit_log_entries
|
extra_context["filtered_audit_log_entries"] = filtered_audit_log_entries
|
||||||
emails = self.get_all_action_needed_reason_emails(obj)
|
|
||||||
extra_context["action_needed_reason_emails"] = json.dumps(emails)
|
|
||||||
extra_context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature")
|
extra_context["has_profile_feature_flag"] = flag_is_active(request, "profile_feature")
|
||||||
|
|
||||||
# Denote if an action needed email was sent or not
|
# Denote if an action needed email was sent or not
|
||||||
|
@ -2183,42 +2182,6 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
|
||||||
# Call the superclass method with updated extra_context
|
# Call the superclass method with updated extra_context
|
||||||
return super().change_view(request, object_id, form_url, extra_context)
|
return super().change_view(request, object_id, form_url, extra_context)
|
||||||
|
|
||||||
def get_all_action_needed_reason_emails(self, domain_request):
|
|
||||||
"""Returns a json dictionary of every action needed reason and its associated email
|
|
||||||
for this particular domain request."""
|
|
||||||
|
|
||||||
emails = {}
|
|
||||||
for action_needed_reason in domain_request.ActionNeededReasons:
|
|
||||||
# Map the action_needed_reason to its default email
|
|
||||||
emails[action_needed_reason.value] = self._get_action_needed_reason_default_email(
|
|
||||||
domain_request, action_needed_reason.value
|
|
||||||
)
|
|
||||||
|
|
||||||
return emails
|
|
||||||
|
|
||||||
def _get_action_needed_reason_default_email(self, 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
|
|
||||||
|
|
||||||
if flag_is_active(None, "profile_feature"): # type: ignore
|
|
||||||
recipient = domain_request.creator
|
|
||||||
else:
|
|
||||||
recipient = domain_request.submitter
|
|
||||||
|
|
||||||
# Return the context of the rendered views
|
|
||||||
context = {"domain_request": domain_request, "recipient": recipient}
|
|
||||||
|
|
||||||
# Get the email body
|
|
||||||
template_path = f"emails/action_needed_reasons/{action_needed_reason}.txt"
|
|
||||||
|
|
||||||
email_body_text = get_template(template_path).render(context=context)
|
|
||||||
email_body_text_cleaned = None
|
|
||||||
if email_body_text:
|
|
||||||
email_body_text_cleaned = email_body_text.strip().lstrip("\n")
|
|
||||||
|
|
||||||
return email_body_text_cleaned
|
|
||||||
|
|
||||||
def process_log_entry(self, log_entry):
|
def process_log_entry(self, log_entry):
|
||||||
"""Process a log entry and return filtered entry dictionary if applicable."""
|
"""Process a log entry and return filtered entry dictionary if applicable."""
|
||||||
changes = log_entry.changes
|
changes = log_entry.changes
|
||||||
|
|
|
@ -510,7 +510,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
const dropdown = document.getElementById("id_action_needed_reason");
|
const dropdown = document.getElementById("id_action_needed_reason");
|
||||||
const textarea = document.getElementById("id_action_needed_reason_email")
|
const textarea = document.getElementById("id_action_needed_reason_email")
|
||||||
const domainRequestId = dropdown ? document.getElementById("domain_request_id").value : null
|
const domainRequestId = dropdown ? document.getElementById("domain_request_id").value : null
|
||||||
const texareaPlaceholder = document.querySelector(".field-action_needed_reason_email__placeholder");
|
const textareaPlaceholder = document.querySelector(".field-action_needed_reason_email__placeholder");
|
||||||
const directEditButton = document.querySelector('.field-action_needed_reason_email__edit');
|
const directEditButton = document.querySelector('.field-action_needed_reason_email__edit');
|
||||||
const modalTrigger = document.querySelector('.field-action_needed_reason_email__modal-trigger');
|
const modalTrigger = document.querySelector('.field-action_needed_reason_email__modal-trigger');
|
||||||
const modalConfirm = document.getElementById('confirm-edit-email');
|
const modalConfirm = document.getElementById('confirm-edit-email');
|
||||||
|
@ -520,11 +520,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
</svg>`;
|
</svg>`;
|
||||||
let lastSentEmailContent = document.getElementById("last-sent-email-content");
|
let lastSentEmailContent = document.getElementById("last-sent-email-content");
|
||||||
const helpText = document.querySelector('.field-action_needed_reason_email .help');
|
const helpText = document.querySelector('.field-action_needed_reason_email .help');
|
||||||
const emailData = document.getElementById('action-needed-emails-data');
|
|
||||||
const actionNeededEmailData = emailData.textContent;
|
|
||||||
const actionNeededEmailsJson = JSON.parse(actionNeededEmailData);
|
|
||||||
const initialDropdownValue = dropdown ? dropdown.value : null;
|
const initialDropdownValue = dropdown ? dropdown.value : null;
|
||||||
const initialEmailValue = actionNeededEmailData ? actionNeededEmailData.value : null;
|
const initialEmailValue = textarea.value;
|
||||||
|
|
||||||
// We will use the const to control the modal
|
// We will use the const to control the modal
|
||||||
let isEmailAlreadySentConst = lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, '');
|
let isEmailAlreadySentConst = lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, '');
|
||||||
// We will use the function to control the label and help
|
// We will use the function to control the label and help
|
||||||
|
@ -532,34 +530,36 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
return lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, '');
|
return lastSentEmailContent.value.replace(/\s+/g, '') === textarea.value.replace(/\s+/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dropdown || !textarea || !domainRequestId || !formLabel || !modalConfirm || !emailData) return;
|
if (!dropdown || !textarea || !domainRequestId || !formLabel || !modalConfirm) return;
|
||||||
|
const apiUrl = document.getElementById("get-action-needed-email-for-user-json").value;
|
||||||
|
|
||||||
function updateUserInterface(reason) {
|
function updateUserInterface(reason) {
|
||||||
if (!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
|
// 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:";
|
formLabel.innerHTML = "Email:";
|
||||||
showElement(texareaPlaceholder);
|
textareaPlaceholder.innerHTML = "Select an action needed reason to see email";
|
||||||
texareaPlaceholder.innerHTML = "Select an action needed reason to see email";
|
showElement(textareaPlaceholder);
|
||||||
hideElement(directEditButton);
|
hideElement(directEditButton);
|
||||||
hideElement(modalTrigger);
|
hideElement(modalTrigger);
|
||||||
hideElement(textarea);
|
hideElement(textarea);
|
||||||
hideElement(helpText);
|
hideElement(helpText);
|
||||||
} else if (reason == 'other') {
|
} 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
|
// '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:";
|
formLabel.innerHTML = "Email:";
|
||||||
|
textareaPlaceholder.innerHTML = "No email will be sent";
|
||||||
|
showElement(textareaPlaceholder);
|
||||||
showElement(helpText);
|
showElement(helpText);
|
||||||
showElement(texareaPlaceholder);
|
|
||||||
texareaPlaceholder.innerHTML = "No email will be sent";
|
|
||||||
hideElement(directEditButton);
|
hideElement(directEditButton);
|
||||||
hideElement(modalTrigger);
|
hideElement(modalTrigger);
|
||||||
hideElement(textarea);
|
hideElement(textarea);
|
||||||
hideElement(helpText);
|
hideElement(helpText);
|
||||||
} else {
|
} else {
|
||||||
// A triggering selection is selected, all hands on board:
|
// A triggering selection is selected, all hands on board:
|
||||||
hideElement(texareaPlaceholder);
|
|
||||||
showElement(textarea);
|
|
||||||
textarea.setAttribute('readonly', true);
|
textarea.setAttribute('readonly', true);
|
||||||
|
showElement(textarea);
|
||||||
showElement(helpText);
|
showElement(helpText);
|
||||||
|
hideElement(textareaPlaceholder);
|
||||||
|
|
||||||
if (isEmailAlreadySentConst) {
|
if (isEmailAlreadySentConst) {
|
||||||
hideElement(directEditButton);
|
hideElement(directEditButton);
|
||||||
showElement(modalTrigger);
|
showElement(modalTrigger);
|
||||||
|
@ -582,13 +582,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
|
||||||
dropdown.addEventListener("change", function() {
|
dropdown.addEventListener("change", function() {
|
||||||
const reason = dropdown.value;
|
const reason = dropdown.value;
|
||||||
const emailBody = reason in actionNeededEmailsJson ? actionNeededEmailsJson[reason] : null;
|
if (reason && reason !== "other") {
|
||||||
|
|
||||||
if (reason && emailBody) {
|
|
||||||
// If it's not the initial value
|
// If it's not the initial value
|
||||||
if (initialDropdownValue !== dropdown.value || initialEmailValue !== textarea.value) {
|
if (initialDropdownValue !== dropdown.value || initialEmailValue !== textarea.value) {
|
||||||
// Replace the email content
|
// Replace the email content
|
||||||
textarea.value = emailBody;
|
fetch(`${apiUrl}?reason=${reason}&domain_request_id=${domainRequestId}`)
|
||||||
|
.then(response => {
|
||||||
|
return response.json().then(data => data);
|
||||||
|
})
|
||||||
|
.then(data => {
|
||||||
|
if (data.error) {
|
||||||
|
console.error("Error in AJAX call: " + data.error);
|
||||||
|
}else {
|
||||||
|
textarea.value = data.action_needed_email;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error("Error action needed email: ", error)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ from registrar.views.domain_requests_json import get_domain_requests_json
|
||||||
from registrar.views.utility.api_views import (
|
from registrar.views.utility.api_views import (
|
||||||
get_senior_official_from_federal_agency_json,
|
get_senior_official_from_federal_agency_json,
|
||||||
get_federal_and_portfolio_types_from_federal_agency_json,
|
get_federal_and_portfolio_types_from_federal_agency_json,
|
||||||
|
get_action_needed_email_for_user_json,
|
||||||
)
|
)
|
||||||
from registrar.views.domains_json import get_domains_json
|
from registrar.views.domains_json import get_domains_json
|
||||||
from registrar.views.utility import always_404
|
from registrar.views.utility import always_404
|
||||||
|
@ -147,6 +148,11 @@ urlpatterns = [
|
||||||
get_federal_and_portfolio_types_from_federal_agency_json,
|
get_federal_and_portfolio_types_from_federal_agency_json,
|
||||||
name="get-federal-and-portfolio-types-from-federal-agency-json",
|
name="get-federal-and-portfolio-types-from-federal-agency-json",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"admin/api/get-action-needed-email-for-user-json/",
|
||||||
|
get_action_needed_email_for_user_json,
|
||||||
|
name="get-action-needed-email-for-user-json",
|
||||||
|
),
|
||||||
path("admin/", admin.site.urls),
|
path("admin/", admin.site.urls),
|
||||||
path(
|
path(
|
||||||
"reports/export_data_type_user/",
|
"reports/export_data_type_user/",
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
{# Store the current object id so we can access it easier #}
|
{# Store the current object id so we can access it easier #}
|
||||||
<input id="domain_request_id" class="display-none" value="{{original.id}}" />
|
<input id="domain_request_id" class="display-none" value="{{original.id}}" />
|
||||||
<input id="has_audit_logs" class="display-none" value="{%if filtered_audit_log_entries %}true{% else %}false{% endif %}"/>
|
<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 }}" />
|
||||||
{% for fieldset in adminform %}
|
{% for fieldset in adminform %}
|
||||||
{% comment %}
|
{% comment %}
|
||||||
TODO: this will eventually need to be changed to something like this
|
TODO: this will eventually need to be changed to something like this
|
||||||
|
|
|
@ -218,22 +218,8 @@ This is using a custom implementation fieldset.html (see admin/fieldset.html)
|
||||||
|
|
||||||
{% block after_help_text %}
|
{% block after_help_text %}
|
||||||
{% if field.field.name == "action_needed_reason_email" %}
|
{% if field.field.name == "action_needed_reason_email" %}
|
||||||
|
|
||||||
<div class="help">
|
<div class="help">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% comment %}
|
|
||||||
Store the action needed reason emails in a json-based dictionary.
|
|
||||||
This allows us to change the action_needed_reason_email field dynamically, depending on value.
|
|
||||||
The alternative to this is an API endpoint.
|
|
||||||
|
|
||||||
Given that we have a limited number of emails, doing it this way makes sense.
|
|
||||||
{% endcomment %}
|
|
||||||
{% if action_needed_reason_emails %}
|
|
||||||
<script id="action-needed-emails-data" type="application/json">
|
|
||||||
{{ action_needed_reason_emails|safe }}
|
|
||||||
</script>
|
|
||||||
{% endif %}
|
|
||||||
{% elif field.field.name == "creator" %}
|
{% elif field.field.name == "creator" %}
|
||||||
<div class="flex-container tablet:margin-top-2">
|
<div class="flex-container tablet:margin-top-2">
|
||||||
<label aria-label="Creator contact details"></label>
|
<label aria-label="Creator contact details"></label>
|
||||||
|
|
42
src/registrar/utility/admin_helpers.py
Normal file
42
src/registrar/utility/admin_helpers.py
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
from django.http import HttpRequest
|
||||||
|
from registrar.models.domain_request import DomainRequest
|
||||||
|
from waffle.decorators import flag_is_active
|
||||||
|
from django.template.loader import get_template
|
||||||
|
|
||||||
|
|
||||||
|
def get_all_action_needed_reason_emails(request, domain_request):
|
||||||
|
"""Returns a dictionary of every action needed reason and its associated email
|
||||||
|
for this particular domain request."""
|
||||||
|
|
||||||
|
emails = {}
|
||||||
|
for action_needed_reason in domain_request.ActionNeededReasons:
|
||||||
|
# Map the action_needed_reason to its default email
|
||||||
|
emails[action_needed_reason.value] = get_action_needed_reason_default_email(
|
||||||
|
request, domain_request, action_needed_reason.value
|
||||||
|
)
|
||||||
|
|
||||||
|
return emails
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
if flag_is_active(request, "profile_feature"): # type: ignore
|
||||||
|
recipient = domain_request.creator
|
||||||
|
else:
|
||||||
|
recipient = domain_request.submitter
|
||||||
|
|
||||||
|
# Return the context of the rendered views
|
||||||
|
context = {"domain_request": domain_request, "recipient": recipient}
|
||||||
|
|
||||||
|
# Get the email body
|
||||||
|
template_path = f"emails/action_needed_reasons/{action_needed_reason}.txt"
|
||||||
|
|
||||||
|
email_body_text = get_template(template_path).render(context=context)
|
||||||
|
email_body_text_cleaned = None
|
||||||
|
if email_body_text:
|
||||||
|
email_body_text_cleaned = email_body_text.strip().lstrip("\n")
|
||||||
|
|
||||||
|
return email_body_text_cleaned
|
|
@ -1,10 +1,10 @@
|
||||||
import logging
|
import logging
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
from django.forms.models import model_to_dict
|
from django.forms.models import model_to_dict
|
||||||
from registrar.models import FederalAgency, SeniorOfficial
|
from registrar.models import FederalAgency, SeniorOfficial, DomainRequest
|
||||||
from django.contrib.admin.views.decorators import staff_member_required
|
from django.contrib.admin.views.decorators import staff_member_required
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from registrar.utility.admin_helpers import get_all_action_needed_reason_emails
|
||||||
from registrar.models.portfolio import Portfolio
|
from registrar.models.portfolio import Portfolio
|
||||||
from registrar.utility.constants import BranchChoices
|
from registrar.utility.constants import BranchChoices
|
||||||
|
|
||||||
|
@ -68,3 +68,27 @@ def get_federal_and_portfolio_types_from_federal_agency_json(request):
|
||||||
}
|
}
|
||||||
|
|
||||||
return JsonResponse(response_data)
|
return JsonResponse(response_data)
|
||||||
|
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@staff_member_required
|
||||||
|
def get_action_needed_email_for_user_json(request):
|
||||||
|
"""Returns a default action needed 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()
|
||||||
|
emails = get_all_action_needed_reason_emails(request, domain_request)
|
||||||
|
return JsonResponse({"action_needed_email": emails.get(reason)}, status=200)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue