summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStanislav Volyk <max.begemot@gmail.com>2023-01-25 21:53:38 +0200
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2023-01-30 09:37:53 +0100
commit20a0850099340fb4cb8df0e4441e5019b2cbd1ea (patch)
tree93e1bdf76887d4271f7adaa2a3957b7cdfec93f7
parent0265b1b49ba10f957abfd1311d0bae0ecefc3111 (diff)
downloaddjango-20a0850099340fb4cb8df0e4441e5019b2cbd1ea.tar.gz
Fixed #34283 -- Escaped title in admin's changelist filters.
Regression in 27aa7035f57f0db30b6632e4274e18b430906799.
-rw-r--r--django/contrib/admin/static/admin/js/filters.js2
-rw-r--r--tests/admin_changelist/admin.py23
-rw-r--r--tests/admin_changelist/models.py6
-rw-r--r--tests/admin_changelist/tests.py20
4 files changed, 49 insertions, 2 deletions
diff --git a/django/contrib/admin/static/admin/js/filters.js b/django/contrib/admin/static/admin/js/filters.js
index ba691ac82f..f5536ebc2d 100644
--- a/django/contrib/admin/static/admin/js/filters.js
+++ b/django/contrib/admin/static/admin/js/filters.js
@@ -11,7 +11,7 @@
}
Object.entries(filters).forEach(([key, value]) => {
- const detailElement = document.querySelector(`[data-filter-title='${key}']`);
+ const detailElement = document.querySelector(`[data-filter-title='${CSS.escape(key)}']`);
// Check if the filter is present, it could be from other view.
if (detailElement) {
diff --git a/tests/admin_changelist/admin.py b/tests/admin_changelist/admin.py
index 67187f5b79..8ffc45e391 100644
--- a/tests/admin_changelist/admin.py
+++ b/tests/admin_changelist/admin.py
@@ -3,7 +3,7 @@ from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from django.core.paginator import Paginator
-from .models import Band, Child, Event, Parent, Swallow
+from .models import Band, Child, Event, Parent, ProxyUser, Swallow
site = admin.AdminSite(name="admin")
@@ -185,3 +185,24 @@ class EmptyValueChildAdmin(admin.ModelAdmin):
@admin.display(empty_value="&dagger;")
def age_display(self, obj):
return obj.age
+
+
+class UnescapedTitleFilter(admin.SimpleListFilter):
+ title = "It's OK"
+ parameter_name = "is_active"
+
+ def lookups(self, request, model_admin):
+ return [("yes", "yes"), ("no", "no")]
+
+ def queryset(self, request, queryset):
+ if self.value() == "yes":
+ return queryset.filter(is_active=True)
+ else:
+ return queryset.filter(is_active=False)
+
+
+class CustomUserAdmin(UserAdmin):
+ list_filter = [UnescapedTitleFilter]
+
+
+site.register(ProxyUser, CustomUserAdmin)
diff --git a/tests/admin_changelist/models.py b/tests/admin_changelist/models.py
index 180c38428a..aa4656e93e 100644
--- a/tests/admin_changelist/models.py
+++ b/tests/admin_changelist/models.py
@@ -1,5 +1,6 @@
import uuid
+from django.contrib.auth.models import User
from django.db import models
@@ -121,3 +122,8 @@ class CustomIdUser(models.Model):
class CharPK(models.Model):
char_pk = models.CharField(max_length=100, primary_key=True)
+
+
+class ProxyUser(User):
+ class Meta:
+ proxy = True
diff --git a/tests/admin_changelist/tests.py b/tests/admin_changelist/tests.py
index 128fbb6aac..bab988af23 100644
--- a/tests/admin_changelist/tests.py
+++ b/tests/admin_changelist/tests.py
@@ -1910,3 +1910,23 @@ class SeleniumTests(AdminSeleniumTestCase):
"[data-filter-title='number of members']",
).get_attribute("open")
)
+
+ def test_collapse_filter_with_unescaped_title(self):
+ from selenium.webdriver.common.by import By
+
+ self.admin_login(username="super", password="secret")
+ changelist_url = reverse("admin:admin_changelist_proxyuser_changelist")
+ self.selenium.get(self.live_server_url + changelist_url)
+ # Title is escaped.
+ filter_title = self.selenium.find_element(
+ By.CSS_SELECTOR, "[data-filter-title='It\\'s OK']"
+ )
+ filter_title.find_element(By.CSS_SELECTOR, "summary").click()
+ self.assertFalse(filter_title.get_attribute("open"))
+ # Filter is in the same state after refresh.
+ self.selenium.refresh()
+ self.assertFalse(
+ self.selenium.find_element(
+ By.CSS_SELECTOR, "[data-filter-title='It\\'s OK']"
+ ).get_attribute("open")
+ )