-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Rearrage admin files. * Don't require login for some admin views. * Rationalise request_decide view. * Split decision_form into seperate file. * Fix js for new form. * Finissh work on request_decide page. * Allow writing a comment against pending requests. * Split admin request into it's own file. * Use the new frontend view to handle deciding requests from the backend. * Fix typo and broken link. * Send the right set of requests and grants.
- Loading branch information
Showing
21 changed files
with
898 additions
and
1,226 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
from datetime import date | ||
|
||
from django.contrib import admin | ||
|
||
from ..models import RequestState, Service | ||
|
||
|
||
class ServiceFilter(admin.SimpleListFilter): | ||
title = "Service" | ||
parameter_name = "service_id" | ||
|
||
def lookups(self, request, model_admin): | ||
# Fetch the services and the categories at once | ||
services = Service.objects.all().select_related("category") | ||
return tuple((s.pk, str(s)) for s in services) | ||
|
||
def queryset(self, request, queryset): | ||
if self.value(): | ||
return queryset.filter(access__role__service__pk=self.value()) | ||
|
||
|
||
class ActiveListFilter(admin.SimpleListFilter): | ||
title = "Active" | ||
parameter_name = "active" | ||
|
||
def lookups(self, request, model_admin): | ||
return (("1", "Active only"),) | ||
|
||
def queryset(self, request, queryset): | ||
if self.value(): | ||
return queryset.filter_active() | ||
|
||
|
||
class ExpiredListFilter(admin.SimpleListFilter): | ||
title = "Expired" | ||
parameter_name = "expired" | ||
|
||
def lookups(self, request, model_admin): | ||
return (("1", "Yes"), ("0", "No")) | ||
|
||
def queryset(self, request, queryset): | ||
if self.value() == "1": | ||
return queryset.filter(expires__lt=date.today()) | ||
elif self.value() == "0": | ||
return queryset.filter(expires__gte=date.today()) | ||
|
||
|
||
class StateListFilter(admin.SimpleListFilter): | ||
title = "State" | ||
parameter_name = "state" | ||
|
||
def lookups(self, request, model_admin): | ||
return RequestState.choices() | ||
|
||
def queryset(self, request, queryset): | ||
value = self.value() | ||
if value in RequestState.all(): | ||
return queryset.filter(state=value) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,217 @@ | ||
from urllib.parse import urlparse | ||
|
||
from django.contrib import admin | ||
from django.contrib.contenttypes.models import ContentType | ||
from django.shortcuts import redirect, render | ||
from django.urls import Resolver404, re_path, resolve, reverse | ||
|
||
from jasmin_metadata.admin import HasMetadataModelAdmin | ||
from jasmin_metadata.models import Metadatum | ||
|
||
from ..actions import send_expiry_notifications, synchronise_service_access | ||
from ..forms import AdminGrantForm, AdminRevokeForm | ||
from ..models import Grant, Request, Role | ||
from . import filters | ||
|
||
|
||
class GrantAdmin(HasMetadataModelAdmin): | ||
list_display = ("access", "active", "revoked", "expired", "expires", "granted_at") | ||
list_filter = ( | ||
filters.ServiceFilter, | ||
"access__role__name", | ||
("access__user", admin.RelatedOnlyFieldListFilter), | ||
filters.ActiveListFilter, | ||
"revoked", | ||
filters.ExpiredListFilter, | ||
) | ||
# This is expensive and unnecessary | ||
show_full_result_count = False | ||
search_fields = ( | ||
"access__role__service__name", | ||
"access__role__name", | ||
"access__user__username", | ||
"access__user__email", | ||
"access__user__last_name", | ||
) | ||
actions = ( | ||
"synchronise_service_access", | ||
"send_expiry_notifications", | ||
"revoke_grants", | ||
) | ||
list_select_related = ( | ||
"access__role", | ||
"access__role__service", | ||
"access__role__service__category", | ||
"access__user", | ||
) | ||
# Allow "Save as new" for quick duplication of grants | ||
save_as = True | ||
|
||
change_form_template = "admin/jasmin_services/grant/change_form.html" | ||
|
||
raw_id_fields = ( | ||
"access", | ||
"previous_grant", | ||
) | ||
|
||
def get_form(self, request, obj=None, change=None, **kwargs): | ||
kwargs["form"] = AdminGrantForm | ||
return super().get_form(request, obj=obj, change=change, **kwargs) | ||
|
||
def get_queryset(self, request): | ||
# Annotate with information about active status | ||
return super().get_queryset(request).annotate_active() | ||
|
||
def synchronise_service_access(self, request, queryset): | ||
""" | ||
Admin action that synchronises actual service access with the selected grants. | ||
""" | ||
synchronise_service_access(queryset) | ||
|
||
synchronise_service_access.short_description = "Synchronise service access" | ||
|
||
def send_expiry_notifications(self, request, queryset): | ||
""" | ||
Admin action that sends expiry notifications, where required, for the selected grants. | ||
""" | ||
send_expiry_notifications(queryset) | ||
|
||
send_expiry_notifications.short_description = "Send expiry notifications" | ||
|
||
def revoke_grants(self, request, queryset): | ||
""" | ||
Admin action that revokes the selected grants. | ||
""" | ||
selected = queryset.values_list("pk", flat=True) | ||
selected_ids = "_".join(str(pk) for pk in selected) | ||
|
||
return redirect( | ||
reverse( | ||
"admin:jasmin_services_bulk_revoke", | ||
kwargs={"ids": selected_ids}, | ||
current_app=self.admin_site.name, | ||
) | ||
) | ||
|
||
revoke_grants.short_description = "Revoke selected grants" | ||
|
||
def active(self, obj): | ||
""" | ||
Returns ``True`` if the given grant is the active grant for the | ||
service/role/user combination. | ||
""" | ||
return obj.active | ||
|
||
active.boolean = True | ||
|
||
def expired(self, obj): | ||
""" | ||
Returns ``True`` if the given grant has expired, ``False`` otherwise. | ||
""" | ||
return obj.expired | ||
|
||
expired.boolean = True | ||
|
||
def get_referring_request(self, request): | ||
""" | ||
Tries to get the request which referred the user to the grant page. | ||
""" | ||
# If the request is not a GET request, don't bother | ||
if request.method != "GET": | ||
return None | ||
referrer = request.META.get("HTTP_REFERER") | ||
if not referrer: | ||
return None | ||
req_change_url_name = "{}_{}_change".format( | ||
Request._meta.app_label, Request._meta.model_name | ||
) | ||
try: | ||
match = resolve(urlparse(referrer).path) | ||
if match.url_name == req_change_url_name: | ||
return Request.objects.get(pk=match.args[0]) | ||
except (ValueError, Resolver404, Request.DoesNotExist): | ||
# These are expected errors | ||
return None | ||
|
||
def get_changeform_initial_data(self, request): | ||
initial = super().get_changeform_initial_data(request) | ||
initial["granted_by"] = request.user.username | ||
# If there is data from a referring request to populate, do that | ||
referring = self.get_referring_request(request) | ||
if referring: | ||
initial.update(role=referring.access.role, user=referring.access.user) | ||
return initial | ||
|
||
def add_view(self, request, form_url="", extra_context=None): | ||
# When adding a grant, add the ID of the referring request to the context if present | ||
if request.method == "GET": | ||
referring = self.get_referring_request(request) | ||
if referring: | ||
extra_context = extra_context or {} | ||
extra_context.update(from_request=referring.pk) | ||
elif "_from_request" in request.POST: | ||
extra_context = extra_context or {} | ||
extra_context.update(from_request=request.POST["_from_request"]) | ||
return super().add_view(request, form_url, extra_context) | ||
|
||
def get_metadata_form_class(self, request, obj=None): | ||
if not obj: | ||
return None | ||
try: | ||
return obj.access.role.metadata_form_class | ||
except Role.DoesNotExist: | ||
return None | ||
|
||
def get_metadata_form_initial_data(self, request, obj): | ||
""" | ||
Gets the initial data for the metadata form. By default, this just | ||
returns the metadata currently attached to the object. | ||
""" | ||
if obj.pk: | ||
return super().get_metadata_form_initial_data(request, obj) | ||
# If the object has not been saved, try to get initial metadata from a | ||
# referring request | ||
if "_from_request" in request.POST: | ||
referring = Request.objects.filter(pk=request.POST["_from_request"]).first() | ||
else: | ||
referring = self.get_referring_request(request) | ||
if referring: | ||
ctype = ContentType.objects.get_for_model(referring) | ||
metadata = Metadatum.objects.filter(content_type=ctype, object_id=referring.pk) | ||
return {d.key: d.value for d in metadata.all()} | ||
return super().get_metadata_form_initial_data(request, obj) | ||
|
||
def get_urls(self): | ||
return [ | ||
re_path( | ||
r"^bulk_revoke/(?P<ids>[0-9_]+)/$", | ||
self.admin_site.admin_view(self.bulk_revoke), | ||
name="jasmin_services_bulk_revoke", | ||
), | ||
] + super().get_urls() | ||
|
||
def bulk_revoke(self, request, ids): | ||
ids = ids.split("_") | ||
if request.method == "POST": | ||
form = AdminRevokeForm(data=request.POST) | ||
if form.is_valid(): | ||
user_reason = form.cleaned_data["user_reason"] | ||
internal_reason = form.cleaned_data["internal_reason"] | ||
|
||
Grant.objects.filter(pk__in=ids).update( | ||
revoked=True, | ||
user_reason=user_reason, | ||
internal_reason=internal_reason, | ||
) | ||
return redirect(f"{self.admin_site.name}:jasmin_services_grant_changelist") | ||
else: | ||
form = AdminRevokeForm() | ||
context = { | ||
"title": "Bulk Revoke Grants", | ||
"form": form, | ||
"opts": self.model._meta, | ||
"media": self.media + form.media, | ||
} | ||
context.update(self.admin_site.each_context(request)) | ||
request.current_app = self.admin_site.name | ||
return render(request, "admin/jasmin_services/grant/bulk_revoke.html", context) |
Oops, something went wrong.