diff --git a/src/woo_publications/logging/admin_tools.py b/src/woo_publications/logging/admin_tools.py index d9da7f56..80bd012c 100644 --- a/src/woo_publications/logging/admin_tools.py +++ b/src/woo_publications/logging/admin_tools.py @@ -1,3 +1,5 @@ +from typing import Any + from django.contrib.contenttypes.models import ContentType from django.db import models from django.forms import BaseInlineFormSet @@ -58,10 +60,28 @@ def log_deletion(self, request, object, object_repr): return super().log_deletion(request, object, object_repr) # type: ignore reportAttributeAccessIssue - def change_view(self, request, object_id, form_url="", extra_context=None): + def change_view( + self, + request, + object_id, + form_url="", + extra_context: dict[str, Any] | None = None, + ): if object_id and request.method == "GET": object = self.model.objects.get(pk=object_id) + if extra_context is None: + extra_context = {} + + # extra context to render show logs button next to history button in change form + audit_url, audit_label = get_logs_link(object) + extra_context.update( + { + "audit_log_url": audit_url, + "audit_log_label": audit_label, + } + ) + assert isinstance(request.user, User) audit_admin_read(content_object=object, django_user=request.user) diff --git a/src/woo_publications/logging/tests/test_admin.py b/src/woo_publications/logging/tests/test_admin.py index 53d6795d..cbd75ff3 100644 --- a/src/woo_publications/logging/tests/test_admin.py +++ b/src/woo_publications/logging/tests/test_admin.py @@ -1,4 +1,5 @@ from django.contrib.contenttypes.models import ContentType +from django.db import models from django.test import override_settings from django.urls import NoReverseMatch, reverse, reverse_lazy from django.utils.translation import gettext as _ @@ -9,7 +10,14 @@ from timeline_logger.models import TimelineLog from woo_publications.accounts.tests.factories import UserFactory -from woo_publications.publications.tests.factories import PublicationFactory +from woo_publications.metadata.tests.factories import ( + InformationCategoryFactory, + ThemeFactory, +) +from woo_publications.publications.tests.factories import ( + DocumentFactory, + PublicationFactory, +) from ..constants import Events from ..models import TimelineLogProxy @@ -321,3 +329,39 @@ def test_event_search(self): self.assertEqual(filtered_response.status_code, 200) self.assertNumLogsDisplayed(filtered_response, 1) self.assertContains(filtered_response, "User Two") + + def test_admin_change_view_audit_log_button(self): + self.app.set_user(self.superuser) + # Create some incorrectly shaped log records to ensure that filtering doesn't + # cause the list view to crash on them. + TimelineLog.objects.create(extra_data=None) + TimelineLog.objects.create(extra_data={}) + TimelineLog.objects.create(extra_data=[]) + + objects_with_logging: list[models.Model] = [ + PublicationFactory.create(), + DocumentFactory.create(), + ThemeFactory.create(), + InformationCategoryFactory.create(), + self.superuser, + ] + + for obj in objects_with_logging: + opts = obj._meta + with self.subTest( + f"{opts.verbose_name} change view has 'show logs' button and " + "filters correctly" + ): + reverse_url = reverse( + f"admin:{opts.app_label}_{opts.model_name}_change", + kwargs={"object_id": obj.pk}, + ) + + response = self.app.get(reverse_url) + + self.assertEqual(response.status_code, 200) + + response = response.click("Show logs") + + self.assertEqual(response.status_code, 200) + self.assertNumLogsDisplayed(response, 1) diff --git a/src/woo_publications/templates/admin/change_form_object_tools.html b/src/woo_publications/templates/admin/change_form_object_tools.html new file mode 100644 index 00000000..f579a62d --- /dev/null +++ b/src/woo_publications/templates/admin/change_form_object_tools.html @@ -0,0 +1,10 @@ +{% extends "admin/change_form_object_tools.html" %} + +{% block object-tools-items %} +{% if audit_log_url and audit_log_label %} +
  • + {{ audit_log_label }} +
  • +{% endif %} +{{ block.super }} +{% endblock %}