diff --git a/debug_toolbar/panels/__init__.py b/debug_toolbar/panels/__init__.py index 579e9221a..6133f65c8 100644 --- a/debug_toolbar/panels/__init__.py +++ b/debug_toolbar/panels/__init__.py @@ -94,6 +94,13 @@ def content(self): if self.has_content: return render_to_string(self.template, self.get_stats()) + @property + def scripts(self): + """ + Scripts used by the HTML content of the panel when it's displayed. + """ + return [] + # URLs for panel-specific views @classmethod diff --git a/debug_toolbar/panels/sql/views.py b/debug_toolbar/panels/sql/views.py index de525a87c..4d86128e2 100644 --- a/debug_toolbar/panels/sql/views.py +++ b/debug_toolbar/panels/sql/views.py @@ -1,5 +1,5 @@ -from django.http import HttpResponseBadRequest -from django.template.response import SimpleTemplateResponse +from django.http import HttpResponseBadRequest, JsonResponse +from django.template.loader import render_to_string from django.views.decorators.csrf import csrf_exempt from debug_toolbar.decorators import require_show_toolbar @@ -27,8 +27,8 @@ def sql_select(request): "headers": headers, "alias": form.cleaned_data["alias"], } - # Using SimpleTemplateResponse avoids running global context processors. - return SimpleTemplateResponse("debug_toolbar/panels/sql_select.html", context) + content = render_to_string("debug_toolbar/panels/sql_select.html", context) + return JsonResponse({"content": content}) return HttpResponseBadRequest("Form errors") @@ -64,8 +64,8 @@ def sql_explain(request): "headers": headers, "alias": form.cleaned_data["alias"], } - # Using SimpleTemplateResponse avoids running global context processors. - return SimpleTemplateResponse("debug_toolbar/panels/sql_explain.html", context) + content = render_to_string("debug_toolbar/panels/sql_explain.html", context) + return JsonResponse({"content": content}) return HttpResponseBadRequest("Form errors") @@ -115,6 +115,6 @@ def sql_profile(request): "headers": headers, "alias": form.cleaned_data["alias"], } - # Using SimpleTemplateResponse avoids running global context processors. - return SimpleTemplateResponse("debug_toolbar/panels/sql_profile.html", context) + content = render_to_string("debug_toolbar/panels/sql_profile.html", context) + return JsonResponse({"content": content}) return HttpResponseBadRequest("Form errors") diff --git a/debug_toolbar/panels/templates/views.py b/debug_toolbar/panels/templates/views.py index 2b3089798..524f36e9c 100644 --- a/debug_toolbar/panels/templates/views.py +++ b/debug_toolbar/panels/templates/views.py @@ -1,8 +1,8 @@ from django.core import signing -from django.http import HttpResponseBadRequest +from django.http import HttpResponseBadRequest, JsonResponse from django.template import Origin, TemplateDoesNotExist from django.template.engine import Engine -from django.template.response import SimpleTemplateResponse +from django.template.loader import render_to_string from django.utils.safestring import mark_safe from debug_toolbar.decorators import require_show_toolbar @@ -57,8 +57,8 @@ def template_source(request): except ImportError: pass - # Using SimpleTemplateResponse avoids running global context processors. - return SimpleTemplateResponse( + content = render_to_string( "debug_toolbar/panels/template_source.html", {"source": source, "template_name": template_name}, ) + return JsonResponse({"content": content}) diff --git a/debug_toolbar/panels/timer.py b/debug_toolbar/panels/timer.py index 130462ba0..b9daf9956 100644 --- a/debug_toolbar/panels/timer.py +++ b/debug_toolbar/panels/timer.py @@ -1,6 +1,7 @@ import time from django.template.loader import render_to_string +from django.templatetags.static import static from django.utils.translation import gettext_lazy as _ from debug_toolbar.panels import Panel @@ -51,6 +52,12 @@ def content(self): ) return render_to_string(self.template, {"rows": rows}) + @property + def scripts(self): + scripts = super().scripts + scripts.append(static("debug_toolbar/js/toolbar.timer.js")) + return scripts + def process_request(self, request): self._start_time = time.time() if self.has_content: diff --git a/debug_toolbar/static/debug_toolbar/js/toolbar.js b/debug_toolbar/static/debug_toolbar/js/toolbar.js index 49e31aac4..44a9977d2 100644 --- a/debug_toolbar/static/debug_toolbar/js/toolbar.js +++ b/debug_toolbar/static/debug_toolbar/js/toolbar.js @@ -25,12 +25,12 @@ const style = getComputedStyle(element); return style.display !== 'none'; }, - executeScripts: function(root) { - root.querySelectorAll('script').forEach(function(e) { - const clone = document.createElement('script'); - clone.src = e.src; - clone.async = true; - root.appendChild(clone); + executeScripts: function(scripts) { + scripts.forEach(function(script) { + const el = document.createElement('script'); + el.src = script; + el.async = true; + document.head.appendChild(el); }); }, }; @@ -45,7 +45,7 @@ init = Object.assign({credentials: 'same-origin'}, init); return fetch(url, init).then(function(response) { if (response.ok) { - return response.text(); + return response.json(); } else { const win = document.querySelector('#djDebugWindow'); win.innerHTML = '
ยป

'+response.status+': '+response.statusText+'

'; @@ -82,10 +82,10 @@ url_params.append('store_id', store_id); url_params.append('panel_id', this.className); url += '?' + url_params.toString(); - ajax(url).then(function(body) { + ajax(url).then(function(data) { inner.previousElementSibling.remove(); // Remove AJAX loader - inner.innerHTML = body; - $$.executeScripts(inner); + inner.innerHTML = data.content; + $$.executeScripts(data.scripts); }); } } @@ -121,10 +121,9 @@ ajax_data.url = this.getAttribute('href'); } - ajax(ajax_data.url, ajax_data).then(function(body) { + ajax(ajax_data.url, ajax_data).then(function(data) { const win = djDebug.querySelector('#djDebugWindow'); - win.innerHTML = body; - $$.executeScripts(win); + win.innerHTML = data.content; $$.show(win); }); }); diff --git a/debug_toolbar/templates/debug_toolbar/panels/timer.html b/debug_toolbar/templates/debug_toolbar/panels/timer.html index a80aed242..676a0743b 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/timer.html +++ b/debug_toolbar/templates/debug_toolbar/panels/timer.html @@ -1,4 +1,4 @@ -{% load i18n %}{% load static %} +{% load i18n %}

{% trans "Resource usage" %}

@@ -41,4 +41,3 @@

{% trans "Browser timing" %}

- diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index 9d61b54ff..1d319027d 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -1,4 +1,4 @@ -from django.http import HttpResponse +from django.http import JsonResponse from django.utils.html import escape from django.utils.translation import gettext as _ @@ -16,7 +16,9 @@ def render_panel(request): "Please reload the page and retry." ) content = "

%s

" % escape(content) + scripts = [] else: panel = toolbar.get_panel_by_id(request.GET["panel_id"]) content = panel.content - return HttpResponse(content) + scripts = panel.scripts + return JsonResponse({"content": content, "scripts": scripts}) diff --git a/docs/changes.rst b/docs/changes.rst index a2c06c1d1..e17fd33e1 100644 --- a/docs/changes.rst +++ b/docs/changes.rst @@ -8,7 +8,18 @@ UNRELEASED * Updated the italian translation. * Added support for Django 3.1a1. * Pruned unused CSS and removed hacks for ancient browsers. +* Added the new :attr:`Panel.scripts ` + property. This property should return a list of JavaScript resources to be + loaded in the browser when displaying the panel. Right now, this is used by a + single panel, the Timer panel. Third party panels can use this property to + add scripts rather then embedding them in the content HTML. +**Backwards incompatible changes** +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +* Loading panel content no longer executes the scripts elements embedded in the + HTML. Third party panels that require JavaScript resources should now use the + :attr:`Panel.scripts ` property. 2.2 (2020-01-31) ---------------- diff --git a/docs/panels.rst b/docs/panels.rst index 143407935..cb6fa55eb 100644 --- a/docs/panels.rst +++ b/docs/panels.rst @@ -319,6 +319,8 @@ unauthorized access. There is no public CSS API at this time. .. autoattribute:: debug_toolbar.panels.Panel.content + .. autoattribute:: debug_toolbar.panels.Panel.scripts + .. automethod:: debug_toolbar.panels.Panel.get_urls .. automethod:: debug_toolbar.panels.Panel.enable_instrumentation