diff --git a/docs/installation/config.rst b/docs/installation/config.rst index fe03fe29..c62ba443 100644 --- a/docs/installation/config.rst +++ b/docs/installation/config.rst @@ -128,6 +128,7 @@ Optional * ``SITE_DOMAIN``: Defines the primary domain where the application is hosted. Defaults to: ``(empty string)``. * ``SENTRY_DSN``: URL of the sentry project to send error reports to. Default empty, i.e. -> no monitoring set up. Highly recommended to configure this. * ``DISABLE_2FA``: Whether or not two factor authentication should be disabled. Defaults to: ``False``. +* ``OBJECTS_ADMIN_SEARCH_DISABLED``: Indicates whether or not searching in the Objects admin should be disabled. Defaults to: ``False``. diff --git a/src/objects/conf/base.py b/src/objects/conf/base.py index 766df05d..6e29d1cc 100644 --- a/src/objects/conf/base.py +++ b/src/objects/conf/base.py @@ -77,6 +77,15 @@ SITE_TITLE = "Starting point" SHOW_ALERT = True +# TODO remove this once https://github.com/maykinmedia/objects-api/issues/621 is fixed +OBJECTS_ADMIN_SEARCH_DISABLED = config( + "OBJECTS_ADMIN_SEARCH_DISABLED", + help_text=( + "Indicates whether or not searching in the Objects admin should be disabled" + ), + default=False, +) + # Default (connection timeout, read timeout) for the requests library (in seconds) REQUESTS_DEFAULT_TIMEOUT = (10, 30) diff --git a/src/objects/core/admin.py b/src/objects/core/admin.py index f0f025cc..1ba7977d 100644 --- a/src/objects/core/admin.py +++ b/src/objects/core/admin.py @@ -1,8 +1,11 @@ +from typing import Sequence + from django import forms +from django.conf import settings from django.contrib import admin from django.contrib.admin import SimpleListFilter from django.contrib.gis.db.models import GeometryField -from django.http import JsonResponse +from django.http import HttpRequest, JsonResponse from django.urls import path import requests @@ -142,6 +145,15 @@ class ObjectAdmin(admin.ModelAdmin): inlines = (ObjectRecordInline,) list_filter = (ObjectTypeFilter, "created_on", "modified_on") + def get_search_fields(self, request: HttpRequest) -> Sequence[str]: + if settings.OBJECTS_ADMIN_SEARCH_DISABLED: + return () + + return ( + "uuid", + "records__data", + ) + @admin.display(description="Object type UUID") def get_object_type_uuid(self, obj): return obj.object_type.uuid diff --git a/src/objects/core/tests/test_admin.py b/src/objects/core/tests/test_admin.py index c6b505d2..a36886cc 100644 --- a/src/objects/core/tests/test_admin.py +++ b/src/objects/core/tests/test_admin.py @@ -1,4 +1,4 @@ -from django.test import tag +from django.test import override_settings, tag from django.urls import reverse from django_webtest import WebTest @@ -7,7 +7,11 @@ from zgw_consumers.test.factories import ServiceFactory from objects.accounts.tests.factories import UserFactory -from objects.core.tests.factories import ObjectFactory, ObjectTypeFactory +from objects.core.tests.factories import ( + ObjectFactory, + ObjectRecordFactory, + ObjectTypeFactory, +) @disable_admin_mfa() @@ -49,3 +53,35 @@ def test_object_changelist_filter_by_objecttype(self): self.assertEqual(len(rows), 1) self.assertIn(str(object1.uuid), response.text) self.assertNotIn(str(object2.uuid), response.text) + + @tag("gh-621") + def test_object_admin_search_disabled(self): + list_url = reverse("admin:core_object_changelist") + + ObjectRecordFactory.create(data={"foo": "bar"}) + ObjectRecordFactory.create(data={"foo": "baz"}) + + def get_num_results(response) -> int: + result_list = response.html.find("table", {"id": "result_list"}) + return len(result_list.find("tbody").find_all("tr")) + + with self.subTest("search is enabled by default"): + response = self.app.get(list_url, user=self.user) + + self.assertIsNotNone(response.html.find("input", {"id": "searchbar"})) + + response = self.app.get(list_url, params={"q": "bar"}, user=self.user) + + self.assertEqual(get_num_results(response), 1) + + with self.subTest("search is disabled if OBJECTS_ADMIN_SEARCH_DISABLED=True"): + with override_settings(OBJECTS_ADMIN_SEARCH_DISABLED=True): + response = self.app.get( + reverse("admin:core_object_changelist"), user=self.user + ) + + self.assertIsNone(response.html.find("input", {"id": "searchbar"})) + + response = self.app.get(list_url, params={"q": "bar"}, user=self.user) + + self.assertEqual(get_num_results(response), 2)