From 595364595ef57359444ca13a325d51961c7fde45 Mon Sep 17 00:00:00 2001 From: Rust Saiargaliev Date: Wed, 7 Dec 2022 16:02:15 +0100 Subject: [PATCH 1/5] Use correct language in panels PR #1703 introduced a bug where SQLPanel (and probably others) is rendered in `LANGUAGE_CODE` language, while it should use `TOOLBAR_LANGUAGE` if provided. This PR fixes panel's content language. --- debug_toolbar/views.py | 10 +++++---- tests/test_integration.py | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index 1d319027d..e05a2a3d1 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -1,6 +1,6 @@ from django.http import JsonResponse from django.utils.html import escape -from django.utils.translation import gettext as _ +from django.utils.translation import get_language, gettext as _, override as lang_override from debug_toolbar.decorators import require_show_toolbar from debug_toolbar.toolbar import DebugToolbar @@ -18,7 +18,9 @@ def render_panel(request): content = "

%s

" % escape(content) scripts = [] else: - panel = toolbar.get_panel_by_id(request.GET["panel_id"]) - content = panel.content - scripts = panel.scripts + lang = toolbar.config["TOOLBAR_LANGUAGE"] or get_language() + with lang_override(lang): + panel = toolbar.get_panel_by_id(request.GET["panel_id"]) + content = panel.content + scripts = panel.scripts return JsonResponse({"content": content, "scripts": scripts}) diff --git a/tests/test_integration.py b/tests/test_integration.py index f41113938..b292dcbf0 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -671,8 +671,51 @@ def test_toolbar_language_will_render_to_default_language_when_not_set(self): hide_button = self.selenium.find_element(By.ID, "djHideToolBarButton") assert hide_button.text == "Hide »" + self.get("/execute_sql/") + sql_panel = self.selenium.find_element(By.ID, "SQLPanel") + + # Click to show the SQL panel + self.selenium.find_element(By.CLASS_NAME, "SQLPanel").click() + + table = self.wait.until( + lambda selenium: sql_panel.find_element(By.TAG_NAME, "table") + ) + self.assertIn("Query", table.text) + self.assertIn("Action", table.text) + @override_settings(DEBUG_TOOLBAR_CONFIG={"TOOLBAR_LANGUAGE": "pt-br"}) def test_toolbar_language_will_render_to_locale_when_set(self): self.get("/regular/basic/") hide_button = self.selenium.find_element(By.ID, "djHideToolBarButton") assert hide_button.text == "Esconder »" + + self.get("/execute_sql/") + sql_panel = self.selenium.find_element(By.ID, "SQLPanel") + + # Click to show the SQL panel + self.selenium.find_element(By.CLASS_NAME, "SQLPanel").click() + + table = self.wait.until( + lambda selenium: sql_panel.find_element(By.TAG_NAME, "table") + ) + self.assertIn("Query", table.text) + self.assertIn("Linha", table.text) + + @override_settings(DEBUG_TOOLBAR_CONFIG={"TOOLBAR_LANGUAGE": "en-us"}) + @override_settings(LANGUAGE_CODE="de") + def test_toolbar_language_will_render_to_locale_when_set_both(self): + self.get("/regular/basic/") + hide_button = self.selenium.find_element(By.ID, "djHideToolBarButton") + assert hide_button.text == "Hide »" + + self.get("/execute_sql/") + sql_panel = self.selenium.find_element(By.ID, "SQLPanel") + + # Click to show the SQL panel + self.selenium.find_element(By.CLASS_NAME, "SQLPanel").click() + + table = self.wait.until( + lambda selenium: sql_panel.find_element(By.TAG_NAME, "table") + ) + self.assertIn("Query", table.text) + self.assertIn("Action", table.text) From 80c13b22fcd54032dcad0e7a2ea87be54698eb37 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 7 Dec 2022 15:07:39 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- debug_toolbar/views.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index e05a2a3d1..e6fe90cfa 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -1,6 +1,10 @@ from django.http import JsonResponse from django.utils.html import escape -from django.utils.translation import get_language, gettext as _, override as lang_override +from django.utils.translation import ( + get_language, + gettext as _, + override as lang_override, +) from debug_toolbar.decorators import require_show_toolbar from debug_toolbar.toolbar import DebugToolbar From 82b9b1f53327af6f15886242c0367e3fa45ee801 Mon Sep 17 00:00:00 2001 From: Rust Saiargaliev Date: Wed, 7 Dec 2022 16:49:43 +0100 Subject: [PATCH 3/5] Add missing changelog entries --- docs/changes.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changes.rst b/docs/changes.rst index 65ad94c7f..a8e877bed 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -4,6 +4,8 @@ Change log Pending ------- +* [i18n] Use correct language in panels + 3.8.1 (2022-12-03) ------------------ @@ -25,6 +27,7 @@ Pending * Fix highlighting on history panel so odd rows are highlighted when selected. * Formalize support for Python 3.11. +* Added TOOLBAR_LANGUAGE setting. 3.7.0 (2022-09-25) ------------------ From b41da3cc895cccdd3cdfc6e52af91e3deb039f7e Mon Sep 17 00:00:00 2001 From: Tim Schilling Date: Wed, 7 Dec 2022 10:18:36 -0600 Subject: [PATCH 4/5] Use code formatting and include more details on the recent change. --- docs/changes.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changes.rst b/docs/changes.rst index a8e877bed..6bd3490f1 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -4,7 +4,8 @@ Change log Pending ------- -* [i18n] Use correct language in panels +* Use ``TOOLBAR_LANGUAGE`` setting when rendering individual panels + that are loaded via AJAX. 3.8.1 (2022-12-03) ------------------ @@ -27,7 +28,7 @@ Pending * Fix highlighting on history panel so odd rows are highlighted when selected. * Formalize support for Python 3.11. -* Added TOOLBAR_LANGUAGE setting. +* Added ``TOOLBAR_LANGUAGE`` setting. 3.7.0 (2022-09-25) ------------------ From d38ff8faa81261c393a3f5d244515a819d53b97d Mon Sep 17 00:00:00 2001 From: Tim Schilling Date: Wed, 7 Dec 2022 16:22:20 -0600 Subject: [PATCH 5/5] Create decorator to use TOOLBAR_LANGUAGE in toolbar specific views. This reviews the third-party panel documentation to reference these two decorators and explain them. They aren't necessary but should be used. --- debug_toolbar/decorators.py | 16 +++++++++++++++ debug_toolbar/panels/history/views.py | 4 +++- debug_toolbar/panels/sql/views.py | 5 ++++- debug_toolbar/panels/templates/views.py | 3 ++- debug_toolbar/views.py | 17 ++++++---------- docs/changes.rst | 1 + docs/configuration.rst | 6 +++--- docs/panels.rst | 15 +++++++++++--- tests/test_decorators.py | 26 +++++++++++++++++++++++++ 9 files changed, 73 insertions(+), 20 deletions(-) create mode 100644 tests/test_decorators.py diff --git a/debug_toolbar/decorators.py b/debug_toolbar/decorators.py index 8114b05d7..e7dd58ea8 100644 --- a/debug_toolbar/decorators.py +++ b/debug_toolbar/decorators.py @@ -1,6 +1,9 @@ import functools from django.http import Http404 +from django.utils.translation import get_language, override as language_override + +from debug_toolbar import settings as dt_settings def require_show_toolbar(view): @@ -15,3 +18,16 @@ def inner(request, *args, **kwargs): return view(request, *args, **kwargs) return inner + + +def render_with_toolbar_language(view): + """Force any rendering within the view to use the toolbar's language.""" + + @functools.wraps(view) + def inner(request, *args, **kwargs): + + lang = dt_settings.get_config()["TOOLBAR_LANGUAGE"] or get_language() + with language_override(lang): + return view(request, *args, **kwargs) + + return inner diff --git a/debug_toolbar/panels/history/views.py b/debug_toolbar/panels/history/views.py index 7f8d0cb7c..3fcbd9b32 100644 --- a/debug_toolbar/panels/history/views.py +++ b/debug_toolbar/panels/history/views.py @@ -1,12 +1,13 @@ from django.http import HttpResponseBadRequest, JsonResponse from django.template.loader import render_to_string -from debug_toolbar.decorators import require_show_toolbar +from debug_toolbar.decorators import render_with_toolbar_language, require_show_toolbar from debug_toolbar.panels.history.forms import HistoryStoreForm from debug_toolbar.toolbar import DebugToolbar @require_show_toolbar +@render_with_toolbar_language def history_sidebar(request): """Returns the selected debug toolbar history snapshot.""" form = HistoryStoreForm(request.GET) @@ -37,6 +38,7 @@ def history_sidebar(request): @require_show_toolbar +@render_with_toolbar_language def history_refresh(request): """Returns the refreshed list of table rows for the History Panel.""" form = HistoryStoreForm(request.GET) diff --git a/debug_toolbar/panels/sql/views.py b/debug_toolbar/panels/sql/views.py index fabca7a57..4b6ced9da 100644 --- a/debug_toolbar/panels/sql/views.py +++ b/debug_toolbar/panels/sql/views.py @@ -2,7 +2,7 @@ from django.template.loader import render_to_string from django.views.decorators.csrf import csrf_exempt -from debug_toolbar.decorators import require_show_toolbar +from debug_toolbar.decorators import render_with_toolbar_language, require_show_toolbar from debug_toolbar.forms import SignedDataForm from debug_toolbar.panels.sql.forms import SQLSelectForm @@ -18,6 +18,7 @@ def get_signed_data(request): @csrf_exempt @require_show_toolbar +@render_with_toolbar_language def sql_select(request): """Returns the output of the SQL SELECT statement""" verified_data = get_signed_data(request) @@ -47,6 +48,7 @@ def sql_select(request): @csrf_exempt @require_show_toolbar +@render_with_toolbar_language def sql_explain(request): """Returns the output of the SQL EXPLAIN on the given query""" verified_data = get_signed_data(request) @@ -85,6 +87,7 @@ def sql_explain(request): @csrf_exempt @require_show_toolbar +@render_with_toolbar_language def sql_profile(request): """Returns the output of running the SQL and getting the profiling statistics""" verified_data = get_signed_data(request) diff --git a/debug_toolbar/panels/templates/views.py b/debug_toolbar/panels/templates/views.py index 134b3d476..e65d1a9d5 100644 --- a/debug_toolbar/panels/templates/views.py +++ b/debug_toolbar/panels/templates/views.py @@ -5,10 +5,11 @@ from django.template.loader import render_to_string from django.utils.html import format_html, mark_safe -from debug_toolbar.decorators import require_show_toolbar +from debug_toolbar.decorators import render_with_toolbar_language, require_show_toolbar @require_show_toolbar +@render_with_toolbar_language def template_source(request): """ Return the source of a template, syntax-highlighted by Pygments if diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index e6fe90cfa..b93acbeed 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -1,16 +1,13 @@ from django.http import JsonResponse from django.utils.html import escape -from django.utils.translation import ( - get_language, - gettext as _, - override as lang_override, -) +from django.utils.translation import gettext as _ -from debug_toolbar.decorators import require_show_toolbar +from debug_toolbar.decorators import render_with_toolbar_language, require_show_toolbar from debug_toolbar.toolbar import DebugToolbar @require_show_toolbar +@render_with_toolbar_language def render_panel(request): """Render the contents of a panel""" toolbar = DebugToolbar.fetch(request.GET["store_id"]) @@ -22,9 +19,7 @@ def render_panel(request): content = "

%s

" % escape(content) scripts = [] else: - lang = toolbar.config["TOOLBAR_LANGUAGE"] or get_language() - with lang_override(lang): - panel = toolbar.get_panel_by_id(request.GET["panel_id"]) - content = panel.content - scripts = panel.scripts + panel = toolbar.get_panel_by_id(request.GET["panel_id"]) + content = panel.content + scripts = panel.scripts return JsonResponse({"content": content, "scripts": scripts}) diff --git a/docs/changes.rst b/docs/changes.rst index 6bd3490f1..6df606d73 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -6,6 +6,7 @@ Pending * Use ``TOOLBAR_LANGUAGE`` setting when rendering individual panels that are loaded via AJAX. +* Add decorator for rendering toolbar views with ``TOOLBAR_LANGUAGE``. 3.8.1 (2022-12-03) ------------------ diff --git a/docs/configuration.rst b/docs/configuration.rst index 7949ae501..0aa7891a0 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -151,6 +151,8 @@ Toolbar options the request doesn't originate from the toolbar itself, EG that ``is_toolbar_request`` is false for a given request. +.. _TOOLBAR_LANGUAGE: + * ``TOOLBAR_LANGUAGE`` Default: ``None`` @@ -160,9 +162,7 @@ Toolbar options render the toolbar in a different language than what the application is rendered in. For example, if you wish to use English for development, but want to render your application in French, you would set this to - ``"en-us"`` and `settings.LANGUAGE_CODE`_ to ``"fr"``. - -.. _settings.LANGUAGE_CODE: https://docs.djangoproject.com/en/stable/ref/settings/#std-setting-LANGUAGE_CODE + ``"en-us"`` and :setting:`LANGUAGE_CODE` to ``"fr"``. Panel options ~~~~~~~~~~~~~ diff --git a/docs/panels.rst b/docs/panels.rst index 50090962d..795cb96a9 100644 --- a/docs/panels.rst +++ b/docs/panels.rst @@ -350,9 +350,18 @@ Third-party panels must subclass :class:`~debug_toolbar.panels.Panel`, according to the public API described below. Unless noted otherwise, all methods are optional. -Panels can ship their own templates, static files and views. All views should -be decorated with ``debug_toolbar.decorators.require_show_toolbar`` to prevent -unauthorized access. There is no public CSS API at this time. +Panels can ship their own templates, static files and views. + +Any views defined for the third-party panel use the following decorators: + +- ``debug_toolbar.decorators.require_show_toolbar`` - Prevents unauthorized + access to the view. +- ``debug_toolbar.decorators.render_with_toolbar_language`` - Supports + internationalization for any content rendered by the view. This will render + the response with the :ref:`TOOLBAR_LANGUAGE ` rather than + :setting:`LANGUAGE_CODE`. + +There is no public CSS API at this time. .. autoclass:: debug_toolbar.panels.Panel diff --git a/tests/test_decorators.py b/tests/test_decorators.py new file mode 100644 index 000000000..5e7c8523b --- /dev/null +++ b/tests/test_decorators.py @@ -0,0 +1,26 @@ +from unittest.mock import patch + +from django.http import HttpResponse +from django.test import RequestFactory, TestCase +from django.test.utils import override_settings + +from debug_toolbar.decorators import render_with_toolbar_language + + +@render_with_toolbar_language +def stub_view(request): + return HttpResponse(200) + + +@override_settings(DEBUG=True, LANGUAGE_CODE="fr") +class RenderWithToolbarLanguageTestCase(TestCase): + @override_settings(DEBUG_TOOLBAR_CONFIG={"TOOLBAR_LANGUAGE": "de"}) + @patch("debug_toolbar.decorators.language_override") + def test_uses_toolbar_language(self, mock_language_override): + stub_view(RequestFactory().get("/")) + mock_language_override.assert_called_once_with("de") + + @patch("debug_toolbar.decorators.language_override") + def test_defaults_to_django_language_code(self, mock_language_override): + stub_view(RequestFactory().get("/")) + mock_language_override.assert_called_once_with("fr")