Skip to content

Commit

Permalink
[4.2.x] Fixed #34283 -- Escaped title in admin's changelist filters.
Browse files Browse the repository at this point in the history
Regression in 27aa703.

Backport of 20a0850 from main
  • Loading branch information
ef-end-y authored and felixxm committed Jan 30, 2023
1 parent 5159e05 commit 7217c11
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 2 deletions.
2 changes: 1 addition & 1 deletion django/contrib/admin/static/admin/js/filters.js
Expand Up @@ -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) {
Expand Down
23 changes: 22 additions & 1 deletion tests/admin_changelist/admin.py
Expand Up @@ -3,7 +3,7 @@
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")

Expand Down Expand Up @@ -185,3 +185,24 @@ class EmptyValueChildAdmin(admin.ModelAdmin):
@admin.display(empty_value="†")
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)
6 changes: 6 additions & 0 deletions tests/admin_changelist/models.py
@@ -1,5 +1,6 @@
import uuid

from django.contrib.auth.models import User
from django.db import models


Expand Down Expand Up @@ -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
20 changes: 20 additions & 0 deletions tests/admin_changelist/tests.py
Expand Up @@ -1910,3 +1910,23 @@ def test_collapse_filters(self):
"[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")
)

0 comments on commit 7217c11

Please sign in to comment.