Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use correct language in panels #1717

Merged
merged 6 commits into from Jan 16, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions 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):
Expand All @@ -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
4 changes: 3 additions & 1 deletion 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)
Expand Down Expand Up @@ -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)
Expand Down
5 changes: 4 additions & 1 deletion debug_toolbar/panels/sql/views.py
Expand Up @@ -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

Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion debug_toolbar/panels/templates/views.py
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion debug_toolbar/views.py
Expand Up @@ -2,11 +2,12 @@
from django.utils.html import escape
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"])
Expand Down
5 changes: 5 additions & 0 deletions docs/changes.rst
Expand Up @@ -4,6 +4,10 @@ Change log
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)
------------------

Expand All @@ -25,6 +29,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)
------------------
Expand Down
6 changes: 3 additions & 3 deletions docs/configuration.rst
Expand Up @@ -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``
Expand All @@ -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
~~~~~~~~~~~~~
Expand Down
15 changes: 12 additions & 3 deletions docs/panels.rst
Expand Up @@ -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 <TOOLBAR_LANGUAGE>` rather than
:setting:`LANGUAGE_CODE`.

There is no public CSS API at this time.

.. autoclass:: debug_toolbar.panels.Panel

Expand Down
26 changes: 26 additions & 0 deletions 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")
43 changes: 43 additions & 0 deletions tests/test_integration.py
Expand Up @@ -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)