mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-07-24 03:30:50 +02:00
Merge branch 'main' into za/1676-require-investigator-da
This commit is contained in:
commit
07058c3abe
3 changed files with 118 additions and 9 deletions
|
@ -19,17 +19,94 @@ from registrar.utility import csv_export
|
|||
from registrar.utility.errors import ApplicationStatusError, FSMErrorCodes
|
||||
from registrar.views.utility.mixins import OrderableFieldsMixin
|
||||
from django.contrib.admin.views.main import ORDER_VAR
|
||||
from registrar.widgets import NoAutocompleteFilteredSelectMultiple
|
||||
from . import models
|
||||
from auditlog.models import LogEntry # type: ignore
|
||||
from auditlog.admin import LogEntryAdmin # type: ignore
|
||||
from django_fsm import TransitionNotAllowed # type: ignore
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.html import escape
|
||||
from django.contrib.auth.forms import UserChangeForm, UsernameField
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MyUserAdminForm(UserChangeForm):
|
||||
"""This form utilizes the custom widget for its class's ManyToMany UIs.
|
||||
|
||||
It inherits from UserChangeForm which has special handling for the password and username fields."""
|
||||
|
||||
class Meta:
|
||||
model = models.User
|
||||
fields = "__all__"
|
||||
field_classes = {"username": UsernameField}
|
||||
widgets = {
|
||||
"groups": NoAutocompleteFilteredSelectMultiple("groups", False),
|
||||
"user_permissions": NoAutocompleteFilteredSelectMultiple("user_permissions", False),
|
||||
}
|
||||
|
||||
|
||||
class DomainInformationAdminForm(forms.ModelForm):
|
||||
"""This form utilizes the custom widget for its class's ManyToMany UIs."""
|
||||
|
||||
class Meta:
|
||||
model = models.DomainInformation
|
||||
fields = "__all__"
|
||||
widgets = {
|
||||
"other_contacts": NoAutocompleteFilteredSelectMultiple("other_contacts", False),
|
||||
}
|
||||
|
||||
|
||||
class DomainInformationInlineForm(forms.ModelForm):
|
||||
"""This form utilizes the custom widget for its class's ManyToMany UIs."""
|
||||
|
||||
class Meta:
|
||||
model = models.DomainInformation
|
||||
fields = "__all__"
|
||||
widgets = {
|
||||
"other_contacts": NoAutocompleteFilteredSelectMultiple("other_contacts", False),
|
||||
}
|
||||
|
||||
|
||||
class DomainApplicationAdminForm(forms.ModelForm):
|
||||
"""Custom form to limit transitions to available transitions.
|
||||
This form utilizes the custom widget for its class's ManyToMany UIs."""
|
||||
|
||||
class Meta:
|
||||
model = models.DomainApplication
|
||||
fields = "__all__"
|
||||
widgets = {
|
||||
"current_websites": NoAutocompleteFilteredSelectMultiple("current_websites", False),
|
||||
"alternative_domains": NoAutocompleteFilteredSelectMultiple("alternative_domains", False),
|
||||
"other_contacts": NoAutocompleteFilteredSelectMultiple("other_contacts", False),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
application = kwargs.get("instance")
|
||||
if application and application.pk:
|
||||
current_state = application.status
|
||||
|
||||
# first option in status transitions is current state
|
||||
available_transitions = [(current_state, application.get_status_display())]
|
||||
|
||||
transitions = get_available_FIELD_transitions(
|
||||
application, models.DomainApplication._meta.get_field("status")
|
||||
)
|
||||
|
||||
for transition in transitions:
|
||||
available_transitions.append((transition.target, transition.target.label))
|
||||
|
||||
# only set the available transitions if the user is not restricted
|
||||
# from editing the domain application; otherwise, the form will be
|
||||
# readonly and the status field will not have a widget
|
||||
if not application.creator.is_restricted():
|
||||
self.fields["status"].widget.choices = available_transitions
|
||||
|
||||
|
||||
# Based off of this excellent example: https://djangosnippets.org/snippets/10471/
|
||||
class MultiFieldSortableChangeList(admin.views.main.ChangeList):
|
||||
"""
|
||||
|
@ -289,6 +366,8 @@ class UserContactInline(admin.StackedInline):
|
|||
class MyUserAdmin(BaseUserAdmin):
|
||||
"""Custom user admin class to use our inlines."""
|
||||
|
||||
form = MyUserAdminForm
|
||||
|
||||
class Meta:
|
||||
"""Contains meta information about this class"""
|
||||
|
||||
|
@ -674,6 +753,8 @@ class DomainInvitationAdmin(ListHeaderAdmin):
|
|||
class DomainInformationAdmin(ListHeaderAdmin):
|
||||
"""Customize domain information admin class."""
|
||||
|
||||
form = DomainInformationAdminForm
|
||||
|
||||
# Columns
|
||||
list_display = [
|
||||
"domain",
|
||||
|
@ -891,6 +972,8 @@ class DomainApplicationAdminForm(forms.ModelForm):
|
|||
class DomainApplicationAdmin(ListHeaderAdmin):
|
||||
"""Custom domain applications admin class."""
|
||||
|
||||
form = DomainApplicationAdminForm
|
||||
|
||||
class InvestigatorFilter(admin.SimpleListFilter):
|
||||
"""Custom investigator filter that only displays users with the manager role"""
|
||||
|
||||
|
@ -910,13 +993,19 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
|||
)
|
||||
|
||||
# Annotate the full name and return a values list that lookups can use
|
||||
privileged_users_annotated = privileged_users.annotate(
|
||||
privileged_users_annotated = (
|
||||
privileged_users.annotate(
|
||||
full_name=Coalesce(
|
||||
Concat("investigator__first_name", Value(" "), "investigator__last_name", output_field=CharField()),
|
||||
Concat(
|
||||
"investigator__first_name", Value(" "), "investigator__last_name", output_field=CharField()
|
||||
),
|
||||
"investigator__email",
|
||||
output_field=CharField(),
|
||||
)
|
||||
).values_list("investigator__id", "full_name")
|
||||
)
|
||||
.values_list("investigator__id", "full_name")
|
||||
.distinct()
|
||||
)
|
||||
|
||||
return privileged_users_annotated
|
||||
|
||||
|
@ -992,8 +1081,6 @@ class DomainApplicationAdmin(ListHeaderAdmin):
|
|||
]
|
||||
search_help_text = "Search by domain or submitter."
|
||||
|
||||
# Detail view
|
||||
form = DomainApplicationAdminForm
|
||||
fieldsets = [
|
||||
(None, {"fields": ["status", "rejection_reason", "investigator", "creator", "approved_domain", "notes"]}),
|
||||
(
|
||||
|
@ -1270,6 +1357,8 @@ class DomainInformationInline(admin.StackedInline):
|
|||
classes conflict, so we'll just pull what we need
|
||||
from DomainInformationAdmin"""
|
||||
|
||||
form = DomainInformationInlineForm
|
||||
|
||||
model = models.DomainInformation
|
||||
|
||||
fieldsets = DomainInformationAdmin.fieldsets
|
||||
|
|
|
@ -162,7 +162,11 @@ function initializeWidgetOnToList(toList, toListId) {
|
|||
'websites': '/admin/registrar/website/__fk__/change/?_to_field=id',
|
||||
'alternative_domains': '/admin/registrar/website/__fk__/change/?_to_field=id',
|
||||
},
|
||||
false,
|
||||
// NOTE: If we open view in the same window then use the back button
|
||||
// to go back, the 'chosen' list will fail to initialize correctly in
|
||||
// sandbozes (but will work fine on local). This is related to how the
|
||||
// Django JS runs (SelectBox.js) and is probably due to a race condition.
|
||||
true,
|
||||
false
|
||||
);
|
||||
|
||||
|
|
16
src/registrar/widgets.py
Normal file
16
src/registrar/widgets.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
# widgets.py
|
||||
|
||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
|
||||
class NoAutocompleteFilteredSelectMultiple(FilteredSelectMultiple):
|
||||
"""Firefox and Edge are unable to correctly initialize the source select in filter_horizontal
|
||||
widgets. We add the attribute autocomplete=off to fix that."""
|
||||
|
||||
def render(self, name, value, attrs=None, renderer=None):
|
||||
if attrs is None:
|
||||
attrs = {}
|
||||
attrs["autocomplete"] = "off"
|
||||
output = super().render(name, value, attrs=attrs, renderer=renderer)
|
||||
return mark_safe(output) # nosec
|
Loading…
Add table
Add a link
Reference in a new issue