Fix timezone handling of `TemplateResponse`s for users with a custom timezone

The PR #9628 missed the cases, where a TemplateResponse is used, which
defers the rendering to a point outside the override_tz() context
manager. This change re-uses the existing handling for the user's
preferred language.

Fixes #10243
pull/10249/head
Stefan Hammer 2023-03-20 08:33:36 +01:00 zatwierdzone przez Sage Abdullah
rodzic 8705124eaf
commit 0016ee7dfd
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: EB1A33CC51CC0217
6 zmienionych plików z 123 dodań i 22 usunięć

Wyświetl plik

@ -45,6 +45,7 @@ Changelog
* Fix: Ensure that document search results count shows the correct all matches, not the paginate total (Andy Chosak)
* Fix: Fix radio and checkbox elements shrinking when using a long label (Sage Abdullah)
* Fix: Fix select elements expanding beyond their container when using a long option label (Sage Abdullah)
* Fix: Fix timezone handling of `TemplateResponse`s for users with a custom timezone (Stefan Hammer, Sage Abdullah)
* Docs: Add code block to make it easier to understand contribution docs (Suyash Singh)
* Docs: Add new "Icons" page for icons customisation and reuse across the admin interface (Coen van der Kamp)
* Docs: Fix broken formatting for MultiFieldPanel / FieldRowPanel permission kwarg docs (Matt Westcott)
@ -85,6 +86,7 @@ Changelog
* Fix: Fix radio and checkbox elements shrinking when using a long label (Sage Abdullah)
* Fix: Fix select elements expanding beyond their container when using a long option label (Sage Abdullah)
* Fix: Fix timezone handling of `TemplateResponse`s for users with a custom timezone (Stefan Hammer, Sage Abdullah)
4.2.1 (13.03.2023)
@ -260,6 +262,7 @@ Changelog
* Fix: Fix radio and checkbox elements shrinking when using a long label (Sage Abdullah)
* Fix: Fix select elements expanding beyond their container when using a long option label (Sage Abdullah)
* Fix: Fix timezone handling of `TemplateResponse`s for users with a custom timezone (Stefan Hammer, Sage Abdullah)
4.1.3 (13.03.2023)

Wyświetl plik

@ -15,3 +15,4 @@ depth: 1
* Fix radio and checkbox elements shrinking when using a long label (Sage Abdullah)
* Fix select elements expanding beyond their container when using a long option label (Sage Abdullah)
* Fix timezone handling of `TemplateResponse`s for users with a custom timezone (Stefan Hammer, Sage Abdullah)

Wyświetl plik

@ -15,3 +15,4 @@ depth: 1
* Fix radio and checkbox elements shrinking when using a long label (Sage Abdullah)
* Fix select elements expanding beyond their container when using a long option label (Sage Abdullah)
* Fix timezone handling of `TemplateResponse`s for users with a custom timezone (Stefan Hammer, Sage Abdullah)

Wyświetl plik

@ -59,6 +59,7 @@ Support for adding custom validation logic to StreamField blocks has been formal
* Ensure that document search results count shows the correct all matches, not the paginate total (Andy Chosak)
* Fix radio and checkbox elements shrinking when using a long label (Sage Abdullah)
* Fix select elements expanding beyond their container when using a long option label (Sage Abdullah)
* Fix timezone handling of `TemplateResponse`s for users with a custom timezone (Stefan Hammer, Sage Abdullah)
### Documentation

Wyświetl plik

@ -182,29 +182,30 @@ def require_admin_access(view_func):
if preferred_language:
with override(preferred_language):
response = view_func(request, *args, **kwargs)
if hasattr(response, "render"):
# If the response has a render() method, Django treats it
# like a TemplateResponse, so we should do the same
# In this case, we need to guarantee that when the TemplateResponse
# is rendered, it is done within the override context manager
# or the user preferred_language will not be used
# (this could be replaced with simply rendering the TemplateResponse
# for simplicity but this does remove some of its middleware modification
# potential)
render = response.render
def overridden_render(response):
with override(preferred_language):
return render()
response.render = types.MethodType(
overridden_render, response
)
# decorate the response render method with the override context manager
return response
else:
return view_func(request, *args, **kwargs)
response = view_func(request, *args, **kwargs)
if hasattr(response, "render"):
# If the response has a render() method, Django treats it
# like a TemplateResponse, so we should do the same
# In this case, we need to guarantee that when the TemplateResponse
# is rendered, it is done within the override context manager
# or the user preferred_language/timezone will not be used
# (this could be replaced with simply rendering the TemplateResponse
# for simplicity but this does remove some of its middleware modification
# potential)
render = response.render
def overridden_render(response):
with override_tz(time_zone):
if preferred_language:
with override(preferred_language):
return render()
return render()
response.render = types.MethodType(overridden_render, response)
# decorate the response render method with the override context manager
return response
except PermissionDenied:
if request.headers.get("x-requested-with") == "XMLHttpRequest":

Wyświetl plik

@ -444,6 +444,100 @@ class TestPageEdit(WagtailTestUtils, TestCase):
"This publishing schedule will only take effect after you have published",
)
def test_edit_post_scheduled_custom_timezone(self):
# Set user's timezone to something different from the server timezone
UserProfile.objects.update_or_create(
user=self.user,
defaults={"current_time_zone": "Asia/Jakarta"},
)
post_data = {
"title": "I've been edited!",
"content": "Some content",
"slug": "hello-world",
"go_live_at": "2022-03-20 06:00",
}
edit_url = reverse("wagtailadmin_pages:edit", args=(self.child_page.id,))
response = self.client.post(edit_url, post_data, follow=True)
html = response.content.decode()
# Should be redirected to the edit page again
self.assertRedirects(response, edit_url, 302, 200)
child_page_new = SimplePage.objects.get(id=self.child_page.id)
# The page will still be live
self.assertTrue(child_page_new.live)
# A revision with approved_go_live_at should not exist
self.assertFalse(
Revision.page_revisions.filter(object_id=child_page_new.id)
.exclude(approved_go_live_at__isnull=True)
.exists()
)
# But a revision with go_live_at in their content json *should* exist
if settings.USE_TZ:
# The saved timestamp should be in UTC
self.assertTrue(
Revision.page_revisions.filter(
object_id=child_page_new.id,
content__go_live_at="2022-03-19T23:00:00Z",
).exists()
)
else:
# Without TZ support, just use the submitted timestamp as-is
self.assertTrue(
Revision.page_revisions.filter(
object_id=child_page_new.id,
content__go_live_at="2022-03-20T06:00:00",
).exists()
)
# Should show the draft go_live_at under the "Once published" label
# and should be in the user's timezone
self.assertContains(
response,
'<div class="w-label-3">Once published:</div>',
html=True,
count=1,
)
self.assertContains(
response,
'<span class="w-text-primary">Go-live:</span> March 20, 2022, 6 a.m.',
html=True,
count=1,
)
# Should show the "Edit schedule" button
self.assertTagInHTML(
'<button type="button" data-a11y-dialog-show="schedule-publishing-dialog">Edit schedule</button>',
html,
count=1,
allow_extra_attrs=True,
)
# Should show the dialog template pointing to the [data-edit-form] selector as the root
self.assertTagInHTML(
'<div id="schedule-publishing-dialog" class="w-dialog publishing" data-dialog-root-selector="[data-edit-form]">',
html,
count=1,
allow_extra_attrs=True,
)
# Should show the input with the correct value in the user's timezone
self.assertTagInHTML(
'<input type="text" name="go_live_at" value="2022-03-20 06:00">',
html,
count=1,
allow_extra_attrs=True,
)
self.assertContains(
response,
"This publishing schedule will only take effect after you have published",
)
def test_schedule_panel_without_publish_permission(self):
editor = self.create_user("editor", password="password")
editor.groups.add(Group.objects.get(name="Editors"))