Add constraint on admin saving a domain application when its creator is ineligible, make all fields readonly when creator is ineligible

This commit is contained in:
rachidatecs 2023-08-24 18:53:20 -04:00
parent 96ea396da4
commit 4b82f5e131
No known key found for this signature in database
GPG key ID: 3CEBBFA7325E5525
2 changed files with 163 additions and 71 deletions

View file

@ -4,11 +4,43 @@ from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.contenttypes.models import ContentType
from django.http.response import HttpResponseRedirect
from django.urls import reverse
# from django.forms.widgets import CheckboxSelectMultiple
# from django import forms
from . import models
logger = logging.getLogger(__name__)
# class TextDisplayWidget(forms.Widget):
# def render(self, name, value, attrs=None, renderer=None):
# if value:
# choices_dict = dict(self.choices)
# selected_values = set(value)
# display_values = [choices_dict[val] for val in selected_values]
# return ', '.join(display_values)
# return ''
# class DomainApplicationForm(forms.ModelForm):
# class Meta:
# model = models.DomainApplication
# fields = '__all__'
# def __init__(self, *args, **kwargs):
# super().__init__(*args, **kwargs)
# # Replace the current_websites field with text if a condition is met
# if self.instance and self.instance.creator.status == 'ineligible':
# self.fields['current_websites'].widget = TextDisplayWidget()
# self.fields['current_websites'].widget.choices = self.fields['current_websites'].choices
# self.fields['other_contacts'].widget = TextDisplayWidget()
# self.fields['other_contacts'].widget.choices = self.fields['other_contacts'].choices
# self.fields['alternative_domains'].widget = TextDisplayWidget()
# self.fields['alternative_domains'].widget.choices = self.fields['alternative_domains'].choices
class AuditedAdmin(admin.ModelAdmin):
"""Custom admin to make auditing easier."""
@ -181,6 +213,10 @@ class ContactAdmin(ListHeaderAdmin):
class DomainApplicationAdmin(ListHeaderAdmin):
"""Customize the applications listing view."""
# Set multi-selects 'read-only' (hide selects and show data)
# based on user perms and application creator's status
#form = DomainApplicationForm
# Columns
list_display = [
@ -255,7 +291,7 @@ class DomainApplicationAdmin(ListHeaderAdmin):
]
# Read only that we'll leverage for CISA Analysts
readonly_fields = [
analyst_readonly_fields = [
"creator",
"type_of_work",
"more_organization_information",
@ -273,52 +309,72 @@ class DomainApplicationAdmin(ListHeaderAdmin):
# Trigger action when a fieldset is changed
def save_model(self, request, obj, form, change):
if change: # Check if the application is being edited
# Get the original application from the database
original_obj = models.DomainApplication.objects.get(pk=obj.pk)
if obj and obj.creator.status != "ineligible":
if change: # Check if the application is being edited
# Get the original application from the database
original_obj = models.DomainApplication.objects.get(pk=obj.pk)
if obj.status != original_obj.status:
if obj.status == models.DomainApplication.STARTED:
# No conditions
pass
elif obj.status == models.DomainApplication.SUBMITTED:
# This is an fsm in model which will throw an error if the
# transition condition is violated, so we roll back the
# status to what it was before the admin user changed it and
# let the fsm method set it. Same comment applies to
# transition method calls below.
obj.status = original_obj.status
obj.submit()
elif obj.status == models.DomainApplication.IN_REVIEW:
obj.status = original_obj.status
obj.in_review()
elif obj.status == models.DomainApplication.ACTION_NEEDED:
obj.status = original_obj.status
obj.action_needed()
elif obj.status == models.DomainApplication.APPROVED:
obj.status = original_obj.status
obj.approve()
elif obj.status == models.DomainApplication.WITHDRAWN:
obj.status = original_obj.status
obj.withdraw()
elif obj.status == models.DomainApplication.REJECTED:
obj.status = original_obj.status
obj.reject()
elif obj.status == models.DomainApplication.INELIGIBLE:
obj.status = original_obj.status
obj.reject_with_prejudice()
else:
logger.warning("Unknown status selected in django admin")
if obj.status != original_obj.status:
if obj.status == models.DomainApplication.STARTED:
# No conditions
pass
elif obj.status == models.DomainApplication.SUBMITTED:
# This is an fsm in model which will throw an error if the
# transition condition is violated, so we roll back the
# status to what it was before the admin user changed it and
# let the fsm method set it. Same comment applies to
# transition method calls below.
obj.status = original_obj.status
obj.submit()
elif obj.status == models.DomainApplication.IN_REVIEW:
obj.status = original_obj.status
obj.in_review()
elif obj.status == models.DomainApplication.ACTION_NEEDED:
obj.status = original_obj.status
obj.action_needed()
elif obj.status == models.DomainApplication.APPROVED:
obj.status = original_obj.status
obj.approve()
elif obj.status == models.DomainApplication.WITHDRAWN:
obj.status = original_obj.status
obj.withdraw()
elif obj.status == models.DomainApplication.REJECTED:
obj.status = original_obj.status
obj.reject()
elif obj.status == models.DomainApplication.INELIGIBLE:
obj.status = original_obj.status
obj.reject_with_prejudice()
else:
logger.warning("Unknown status selected in django admin")
super().save_model(request, obj, form, change)
super().save_model(request, obj, form, change)
else:
messages.error(request, "This action is not permitted for applications with an ineligible creator.")
def get_readonly_fields(self, request, obj=None):
"""Set the read-only state on form elements.
We have 2 conditions that determine which fields are read-only:
admin user permissions and the application creator's status, so
we'll use the baseline readonly_fields and extend it as needed.
"""
readonly_fields = list(self.readonly_fields)
# Check if the creator is ineligible
if obj and obj.creator.status == "ineligible":
# For fields like CharField, IntegerField, etc., the widget used is straightforward,
# and the readonly_fields list can control their behavior
readonly_fields.extend([field.name for field in self.model._meta.fields])
# Add the multi-select fields to readonly_fields:
# Complex fields like ManyToManyField require special handling
readonly_fields.extend(["current_websites", "other_contacts", "alternative_domains"])
if request.user.is_superuser:
# Superusers have full access, no fields are read-only
return []
return readonly_fields
else:
# Regular users can only view the specified fields
return self.readonly_fields
readonly_fields.extend([field for field in self.analyst_readonly_fields])
return readonly_fields
admin.site.register(models.User, MyUserAdmin)