diff options
| author | Percy Perez-Pinedo | 2009-07-20 15:46:57 -0700 |
|---|---|---|
| committer | Percy Perez-Pinedo | 2009-07-20 15:46:57 -0700 |
| commit | b7629d5768295131c35d613dbb16d9a070474c77 (patch) | |
| tree | f7557bdc972287765a0bb687c2fecf04ddd0718f | |
| parent | 073ab2c8255ebc310a54e9f937b4b17f94f7eac2 (diff) | |
| parent | 4dc0710b6a5eedec0448909f6b90e55a4f3555d4 (diff) | |
| download | django-debug-toolbar-b7629d5768295131c35d613dbb16d9a070474c77.tar.bz2 | |
removing trans so we can merge for now
32 files changed, 518 insertions, 119 deletions
@@ -15,7 +15,7 @@ Currently, the following panels have been written and are working: - GET/POST/cookie/session variable display - Templates and context used, and their template paths - SQL queries including time to execute and links to EXPLAIN each query -- Cache stats +- List of signals, their args and receivers - Logging output via Python's built-in logging module If you have ideas for other panels please let us know. @@ -36,17 +36,30 @@ Installation must come after any other middleware that encodes the response's content (such as GZipMiddleware). + Note: The debug toolbar will only display itself if the mimetype of the + response is either `text/html` or `application/xhtml+xml` and contains a + closing `</body>` tag. + #. Make sure your IP is listed in the `INTERNAL_IPS` setting. If you are working locally this will be: INTERNAL_IPS = ('127.0.0.1',) + Note: This is required because of the built-in requirements of the + `show_toolbar` method. See below for how to define a method to determine + your own logic for displaying the toolbar. + #. Add `debug_toolbar` to your `INSTALLED_APPS` setting so Django can find the - the template files associated with the Debug Toolbar. - + template files associated with the Debug Toolbar. + Alternatively, add the path to the debug toolbar templates (``'path/to/debug_toolbar/templates'`` to your ``TEMPLATE_DIRS`` setting.) +Configuration +============= + +The debug toolbar has two settings that can be set in `settings.py`: + #. Optional: Add a tuple called `DEBUG_TOOLBAR_PANELS` to your ``settings.py`` file that specifies the full Python path to the panel that you want included in the Toolbar. This setting looks very much like the `MIDDLEWARE_CLASSES` @@ -60,14 +73,45 @@ Installation 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', 'debug_toolbar.panels.template.TemplateDebugPanel', 'debug_toolbar.panels.sql.SQLDebugPanel', - 'debug_toolbar.panels.cache.CacheDebugPanel', + 'debug_toolbar.panels.signals.SignalDebugPanel', 'debug_toolbar.panels.logger.LoggingPanel', ) You can change the ordering of this tuple to customize the order of the - panels you want to display. And you can include panels that you have created - or that are specific to your project. + panels you want to display, or add/remove panels. If you have custom panels + you can include them in this way -- just provide the full Python path to + your panel. + +#. Optional: There are a few configuration options to the debug toolbar that + can be placed in a dictionary: + + * `INTERCEPT_REDIRECTS`: If set to True (default), the debug toolbar will + show an intermediate page upon redirect so you can view any debug + information prior to redirecting. This page will provide a link to the + redirect destination you can follow when ready. If set to False, redirects + will proceed as normal. + + * `SHOW_TOOLBAR_CALLBACK`: If not set or set to None, the debug_toolbar + middleware will use its built-in show_toolbar method for determining whether + the toolbar should show or not. The default checks are that DEBUG must be + set to True and the IP of the request must be in INTERNAL_IPS. You can + provide your own method for displaying the toolbar which contains your + custom logic. This method should return True or False. + + * `EXTRA_SIGNALS`: An array of custom signals that might be in your project, + defined as the python path to the signal. + + Example configuration:: + + def custom_show_toolbar(request): + return True # Always show toolbar, for example purposes only. + + DEBUG_TOOLBAR_CONFIG = { + 'INTERCEPT_REDIRECTS': False, + 'SHOW_TOOLBAR_CALLBACK': custom_show_toolbar, + 'EXTRA_SIGNALS': ['myproject.signals.MySignal'], + } TODOs and BUGS ============== -See: http://code.google.com/p/django-debug-toolbar/issues/list +See: http://github.com/robhudson/django-debug-toolbar/issues diff --git a/debug_toolbar/__init__.py b/debug_toolbar/__init__.py index 9603d9e..e2fa70b 100644 --- a/debug_toolbar/__init__.py +++ b/debug_toolbar/__init__.py @@ -1,2 +1,2 @@ -VERSION = (0, 0, 1) +VERSION = (0, 7, 0) __version__ = '.'.join(map(str, VERSION)) diff --git a/debug_toolbar/media/Makefile b/debug_toolbar/media/debug_toolbar/Makefile index 7f8f2b2..a2d6abb 100644 --- a/debug_toolbar/media/Makefile +++ b/debug_toolbar/media/debug_toolbar/Makefile @@ -3,6 +3,7 @@ all: compress_js compress_css compress_js: java -jar ~/bin/yuicompressor.jar toolbar.js > toolbar.min.js + java -jar ~/bin/yuicompressor.jar jquery.cookie.js >> toolbar.min.js compress_css: java -jar ~/bin/yuicompressor.jar --type css toolbar.css > toolbar.min.css diff --git a/debug_toolbar/media/debug_toolbar/jquery.cookie.js b/debug_toolbar/media/debug_toolbar/jquery.cookie.js new file mode 100644 index 0000000..6df1fac --- /dev/null +++ b/debug_toolbar/media/debug_toolbar/jquery.cookie.js @@ -0,0 +1,96 @@ +/** + * Cookie plugin + * + * Copyright (c) 2006 Klaus Hartl (stilbuero.de) + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ + +/** + * Create a cookie with the given name and value and other optional parameters. + * + * @example $.cookie('the_cookie', 'the_value'); + * @desc Set the value of a cookie. + * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true }); + * @desc Create a cookie with all available options. + * @example $.cookie('the_cookie', 'the_value'); + * @desc Create a session cookie. + * @example $.cookie('the_cookie', null); + * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain + * used when the cookie was set. + * + * @param String name The name of the cookie. + * @param String value The value of the cookie. + * @param Object options An object literal containing key/value pairs to provide optional cookie attributes. + * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object. + * If a negative value is specified (e.g. a date in the past), the cookie will be deleted. + * If set to null or omitted, the cookie will be a session cookie and will not be retained + * when the the browser exits. + * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie). + * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie). + * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will + * require a secure protocol (like HTTPS). + * @type undefined + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ + +/** + * Get the value of a cookie with the given name. + * + * @example $.cookie('the_cookie'); + * @desc Get the value of a cookie. + * + * @param String name The name of the cookie. + * @return The value of the cookie. + * @type String + * + * @name $.cookie + * @cat Plugins/Cookie + * @author Klaus Hartl/klaus.hartl@stilbuero.de + */ +jQuery.cookie = function(name, value, options) { + if (typeof value != 'undefined') { // name and value given, set cookie + options = options || {}; + if (value === null) { + value = ''; + options.expires = -1; + } + var expires = ''; + if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) { + var date; + if (typeof options.expires == 'number') { + date = new Date(); + date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000)); + } else { + date = options.expires; + } + expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE + } + // CAUTION: Needed to parenthesize options.path and options.domain + // in the following expressions, otherwise they evaluate to undefined + // in the packed version for some reason... + var path = options.path ? '; path=' + (options.path) : ''; + var domain = options.domain ? '; domain=' + (options.domain) : ''; + var secure = options.secure ? '; secure' : ''; + document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join(''); + } else { // only name given, get cookie + var cookieValue = null; + if (document.cookie && document.cookie != '') { + var cookies = document.cookie.split(';'); + for (var i = 0; i < cookies.length; i++) { + var cookie = jQuery.trim(cookies[i]); + // Does this cookie string begin with the name we want? + if (cookie.substring(0, name.length + 1) == (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; + } +};
\ No newline at end of file diff --git a/debug_toolbar/media/jquery.js b/debug_toolbar/media/debug_toolbar/jquery.js index 82b98e1..82b98e1 100644 --- a/debug_toolbar/media/jquery.js +++ b/debug_toolbar/media/debug_toolbar/jquery.js diff --git a/debug_toolbar/media/toolbar.css b/debug_toolbar/media/debug_toolbar/toolbar.css index 2ce4226..9ef6dc3 100644 --- a/debug_toolbar/media/toolbar.css +++ b/debug_toolbar/media/debug_toolbar/toolbar.css @@ -4,6 +4,7 @@ margin: 0; padding: 0; position: static; + text-align: left; } #djDebug a { color: #f7c757; @@ -23,6 +24,27 @@ right:0; } +#djDebugToolbarHandle { + background: #326342; + height: 30px; + z-index: 100000000; + border-bottom: 2px solid #234f32; + position:absolute; + top:0; + left:0; + right:0; + width: 16px; +} + +#djDebugToolbarHandle ul li { + padding: 3px 0px 0px 3px; +} + +#djDebugToolbarHandle ul li a { + font-size: 16px; + font-weight: bold; +} + #djDebugToolbar ul { margin: 0; padding: 0; @@ -65,6 +87,9 @@ background-color: #ffffff; } +#djDebug tr.djDebugOdd pre { + background-color: #eeeeee; +} #djDebug .panelContent { background: #2a5738; @@ -132,7 +157,7 @@ color: #000; vertical-align: top; } -#djDebug .panelContent table tr.odd td { +#djDebug .panelContent table tr.djDebugOdd td { background: #eee; } diff --git a/debug_toolbar/media/debug_toolbar/toolbar.js b/debug_toolbar/media/debug_toolbar/toolbar.js new file mode 100644 index 0000000..40e1a58 --- /dev/null +++ b/debug_toolbar/media/debug_toolbar/toolbar.js @@ -0,0 +1,105 @@ +jQuery.noConflict(); +jQuery(function($j) { + var COOKIE_NAME = 'dj_debug_panel'; + $j.djDebug = function(data, klass) { + $j.djDebug.init(); + } + $j.extend($j.djDebug, { + init: function() { + var current = null; + $j('#djDebugPanelList li a').click(function() { + if (!this.className) { + return false; + } + current = $j('#djDebug #' + this.className); + if (current.is(':visible')) { + $j(document).trigger('close.djDebug'); + } else { + $j('.panelContent').hide(); + current.show(); + $j.djDebug.open(); + } + return false; + }); + $j('#djDebug a.close').click(function() { + $j(document).trigger('close.djDebug'); + return false; + }); + $j('#djDebug a.remoteCall').click(function() { + $j('#djDebugWindow').load(this.href, {}, function() { + $j('#djDebugWindow a.back').click(function() { + $j(this).parent().hide(); + return false; + }); + }); + $j('#djDebugWindow').show(); + return false; + }); + $j('#djDebugTemplatePanel a.djTemplateShowContext').click(function() { + $j.djDebug.toggle_content($j(this).parent().next()); + return false; + }); + $j('#djDebugSQLPanel a.djSQLShowStacktrace').click(function() { + $j.djDebug.toggle_content($j(this).parent().next()); + return false; + }); + $j('#djHideToolBarButton').click(function() { + $j.djDebug.hide_toolbar(true); + return false; + }); + $j('#djShowToolBarButton').click(function() { + $j.djDebug.show_toolbar(); + return false; + }); + if ($j.cookie(COOKIE_NAME)) { + $j.djDebug.hide_toolbar(false); + } else { + $j('#djDebugToolbar').show(); + } + }, + open: function() { + $j(document).bind('keydown.djDebug', function(e) { + if (e.keyCode == 27) { + $j.djDebug.close(); + } + }); + }, + toggle_content: function(elem) { + if (elem.is(':visible')) { + elem.hide(); + } else { + elem.show(); + } + }, + close: function() { + $j(document).trigger('close.djDebug'); + return false; + }, + hide_toolbar: function(setCookie) { + $j('#djDebugToolbar').hide("fast"); + $j(document).trigger('close.djDebug'); + $j('#djDebugToolbarHandle').show(); + if (setCookie) { + $j.cookie(COOKIE_NAME, 'hide', { + path: '/', + expires: 10 + }); + } + }, + show_toolbar: function() { + $j('#djDebugToolbarHandle').hide(); + $j('#djDebugToolbar').show("fast"); + $j.cookie(COOKIE_NAME, null, { + path: '/', + expires: -1 + }); + } + }); + $j(document).bind('close.djDebug', function() { + $j(document).unbind('keydown.djDebug'); + $j('.panelContent').hide(); + }); +}); +jQuery(function() { + jQuery.djDebug(); +}); diff --git a/debug_toolbar/media/debug_toolbar/toolbar.min.css b/debug_toolbar/media/debug_toolbar/toolbar.min.css new file mode 100644 index 0000000..c172fbd --- /dev/null +++ b/debug_toolbar/media/debug_toolbar/toolbar.min.css @@ -0,0 +1 @@ +#djDebug *{color:#000;float:none;margin:0;padding:0;position:static;text-align:left;}#djDebug a{color:#f7c757;}#djDebug a:hover{color:#aaa;}#djDebugToolbar{background:#326342;height:30px;z-index:100000000;border-bottom:2px solid #234f32;position:absolute;top:0;left:0;right:0;}#djDebugToolbarHandle{background:#326342;height:30px;z-index:100000000;border-bottom:2px solid #234f32;position:absolute;top:0;left:0;right:0;width:16px;}#djDebugToolbarHandle ul li{padding:3px 0 0 3px;}#djDebugToolbarHandle ul li a{font-size:16px;font-weight:bold;}#djDebugToolbar ul{margin:0;padding:0;list-style:none;}#djDebugToolbar li{border-left:1px solid #487858;color:#fff;display:inline;font-size:11px;font-weight:bold;float:none;height:20px;margin:0;padding:0;line-height:30px;padding:8px 9px 9px;position:relative;width:auto;}#djDebugToolbar li:hover{background:#487858;}#djDebugToolbar li:hover a{color:#fff;}#djDebugToolbar li:last-child{border-right:1px solid #487858;}#djDebugToolbar #djDebugButton{color:#92ef3f;}#djDebug pre{background-color:#fff;}#djDebug tr.djDebugOdd pre{background-color:#eee;}#djDebug .panelContent{background:#2a5738;border-bottom:2px solid #234f32;border-top:2px solid #487858;display:none;position:absolute;margin:0;padding:10px;top:32px;width:auto;left:0;right:0;bottom:5px;color:black;z-index:1000000;overflow:auto;}#djDebug .panelContent p a,#djDebug .panelContent dl a{color:#40684c;}#djDebug .panelContent p a:hover,#djDebug .panelContent dl a:hover{color:#92EF3F;}#djDebug .panelContent h3{border-bottom:1px solid #40684c;color:#92ef3f;padding:0 0 5px;}#djDebug .panelContent p{padding:0 5px;}#djDebug .panelContent p,#djDebug .panelContent table,#djDebug .panelContent ol,#djDebug .panelContent ul,#djDebug .panelContent dl{margin:5px 0 15px;background-color:#fff;}#djDebug .panelContent table{width:100%;clear:both;}#djDebug .panelContent table a{color:#40684C;}#djDebug .panelContent table th{background-color:#9dcc49;font-weight:bold;color:#000;font-size:11px;padding:3px 7px 3px;text-align:left;cursor:pointer;border-right:1px solid #b9d977;}#djDebug .panelContent table td{padding:5px 10px;font-size:11px;background:#fff;color:#000;vertical-align:top;}#djDebug .panelContent table tr.djDebugOdd td{background:#eee;}#djDebug .panelContent .close{float:right;font-weight:bold;}#djDebug .panelContent dt,#djDebug .panelContent dd{display:block;}#djDebug .panelContent dd{margin-left:10px;}#djDebug .highlight{color:#000;}#djDebug .highlight .err{color:#000;}#djDebug .highlight .g{color:#000;}#djDebug .highlight .k{color:#40684C;font-weight:bold;}#djDebug .highlight .o{color:#000;}#djDebug .highlight .n{color:#000;}#djDebug .highlight .mi{color:#40684C;font-weight:bold;}#djDebug .highlight .l{color:#000;}#djDebug .highlight .x{color:#000;}#djDebug .highlight .p{color:#000;}#djDebug .highlight .m{color:#40684C;font-weight:bold;}#djDebug .highlight .s{color:#0086d2;}#djDebug .highlight .w{color:#888;}#djDebug .highlight .il{color:#40684C;font-weight:bold;}#djDebug .highlight .na{color:#7D9029;}#djDebug .highlight .nt{color:#008000;font-weight:bold;}#djDebug .highlight .nv{color:#19177C;}#djDebug .highlight .s2{color:#BA2121;}#djDebug .highlight .cp{color:#BC7A00;}
\ No newline at end of file diff --git a/debug_toolbar/media/debug_toolbar/toolbar.min.js b/debug_toolbar/media/debug_toolbar/toolbar.min.js new file mode 100644 index 0000000..7f8658f --- /dev/null +++ b/debug_toolbar/media/debug_toolbar/toolbar.min.js @@ -0,0 +1 @@ +jQuery.noConflict();jQuery(function(b){var a="dj_debug_panel";b.djDebug=function(d,c){b.djDebug.init()};b.extend(b.djDebug,{init:function(){var c=null;b("#djDebugPanelList li a").click(function(){if(!this.className){return false}c=b("#djDebug #"+this.className);if(c.is(":visible")){b(document).trigger("close.djDebug")}else{b(".panelContent").hide();c.show();b.djDebug.open()}return false});b("#djDebug a.close").click(function(){b(document).trigger("close.djDebug");return false});b("#djDebug a.remoteCall").click(function(){b("#djDebugWindow").load(this.href,{},function(){b("#djDebugWindow a.back").click(function(){b(this).parent().hide();return false})});b("#djDebugWindow").show();return false});b("#djDebugTemplatePanel a.djTemplateShowContext").click(function(){b.djDebug.toggle_content(b(this).parent().next());return false});b("#djDebugSQLPanel a.djSQLShowStacktrace").click(function(){b.djDebug.toggle_content(b(this).parent().next());return false});b("#djHideToolBarButton").click(function(){b.djDebug.hide_toolbar(true);return false});b("#djShowToolBarButton").click(function(){b.djDebug.show_toolbar();return false});if(b.cookie(a)){b.djDebug.hide_toolbar(false)}else{b("#djDebugToolbar").show()}},open:function(){b(document).bind("keydown.djDebug",function(c){if(c.keyCode==27){b.djDebug.close()}})},toggle_content:function(c){if(c.is(":visible")){c.hide()}else{c.show()}},close:function(){b(document).trigger("close.djDebug");return false},hide_toolbar:function(c){b("#djDebugToolbar").hide("fast");b(document).trigger("close.djDebug");b("#djDebugToolbarHandle").show();if(c){b.cookie(a,"hide",{path:"/",expires:10})}},show_toolbar:function(){b("#djDebugToolbarHandle").hide();b("#djDebugToolbar").show("fast");b.cookie(a,null,{path:"/",expires:-1})}});b(document).bind("close.djDebug",function(){b(document).unbind("keydown.djDebug");b(".panelContent").hide()})});jQuery(function(){jQuery.djDebug()});jQuery.cookie=function(b,j,m){if(typeof j!="undefined"){m=m||{};if(j===null){j="";m.expires=-1}var e="";if(m.expires&&(typeof m.expires=="number"||m.expires.toUTCString)){var f;if(typeof m.expires=="number"){f=new Date();f.setTime(f.getTime()+(m.expires*24*60*60*1000))}else{f=m.expires}e="; expires="+f.toUTCString()}var l=m.path?"; path="+(m.path):"";var g=m.domain?"; domain="+(m.domain):"";var a=m.secure?"; secure":"";document.cookie=[b,"=",encodeURIComponent(j),e,l,g,a].join("")}else{var d=null;if(document.cookie&&document.cookie!=""){var k=document.cookie.split(";");for(var h=0;h<k.length;h++){var c=jQuery.trim(k[h]);if(c.substring(0,b.length+1)==(b+"=")){d=decodeURIComponent(c.substring(b.length+1));break}}}return d}};
\ No newline at end of file diff --git a/debug_toolbar/media/toolbar.js b/debug_toolbar/media/toolbar.js deleted file mode 100644 index 455a2fc..0000000 --- a/debug_toolbar/media/toolbar.js +++ /dev/null @@ -1,66 +0,0 @@ -var _$ = window.$; -jQuery.noConflict(); -jQuery(function($) { - $.djDebug = function(data, klass) { - $.djDebug.init(); - } - $.extend($.djDebug, { - init: function() { - var current = null; - $('#djDebugPanelList li a').click(function() { - current = $('#djDebug #' + this.className); - if (current.is(':visible')) { - $(document).trigger('close.djDebug'); - } else { - $('.panelContent').hide(); - current.show(); - $.djDebug.open(); - } - return false; - }); - $('#djDebug a.close').click(function() { - $(document).trigger('close.djDebug'); - return false; - }); - $('#djDebug a.remoteCall').click(function() { - $('#djDebugWindow').load(this.href, {}, function() { - $('#djDebugWindow a.back').click(function() { - $(this).parent().hide(); - return false; - }); - }); - $('#djDebugWindow').show(); - return false; - }); - $('#djDebugTemplatePanel a.djTemplateShowContext').click(function() { - $.djDebug.toggle_content($(this).parent().next()); - }); - }, - open: function() { - $(document).bind('keydown.djDebug', function(e) { - if (e.keyCode == 27) { - $.djDebug.close(); - } - }); - }, - toggle_content: function(elem) { - if (elem.is(':visible')) { - elem.hide(); - } else { - elem.show(); - } - }, - close: function() { - $(document).trigger('close.djDebug'); - return false; - } - }); - $(document).bind('close.djDebug', function() { - $(document).unbind('keydown.djDebug'); - $('.panelContent').hide(); - }); -}); -jQuery(function() { - jQuery.djDebug(); -}); -$ = _$; diff --git a/debug_toolbar/media/toolbar.min.css b/debug_toolbar/media/toolbar.min.css deleted file mode 100644 index bb7c034..0000000 --- a/debug_toolbar/media/toolbar.min.css +++ /dev/null @@ -1 +0,0 @@ -#djDebug *{color:#000;float:none;margin:0;padding:0;position:static;}#djDebug a{color:#f7c757;}#djDebug a:hover{color:#aaa;}#djDebugToolbar{background:#326342;height:30px;z-index:100000000;border-bottom:2px solid #234f32;position:absolute;top:0;left:0;right:0;}#djDebugToolbar ul{margin:0;padding:0;list-style:none;}#djDebugToolbar li{border-left:1px solid #487858;color:#fff;display:inline;font-size:11px;font-weight:bold;float:none;height:20px;margin:0;padding:0;line-height:30px;padding:8px 9px 9px;position:relative;width:auto;}#djDebugToolbar li:hover{background:#487858;}#djDebugToolbar li:hover a{color:#fff;}#djDebugToolbar li:last-child{border-right:1px solid #487858;}#djDebugToolbar #djDebugButton{color:#92ef3f;}#djDebug pre{background-color:#fff;}#djDebug .panelContent{background:#2a5738;border-bottom:2px solid #234f32;border-top:2px solid #487858;display:none;position:absolute;margin:0;padding:10px;top:32px;width:auto;left:0;right:0;bottom:5px;color:black;z-index:1000000;overflow:auto;}#djDebug .panelContent p a,#djDebug .panelContent dl a{color:#40684c;}#djDebug .panelContent p a:hover,#djDebug .panelContent dl a:hover{color:#92EF3F;}#djDebug .panelContent h3{border-bottom:1px solid #40684c;color:#92ef3f;padding:0 0 5px;}#djDebug .panelContent p{padding:0 5px;}#djDebug .panelContent p,#djDebug .panelContent table,#djDebug .panelContent ol,#djDebug .panelContent ul,#djDebug .panelContent dl{margin:5px 0 15px;background-color:#fff;}#djDebug .panelContent table{width:100%;clear:both;}#djDebug .panelContent table a{color:#40684C;}#djDebug .panelContent table th{background-color:#9dcc49;font-weight:bold;color:#000;font-size:11px;padding:3px 7px 3px;text-align:left;cursor:pointer;border-right:1px solid #b9d977;}#djDebug .panelContent table td{padding:5px 10px;font-size:11px;background:#fff;color:#000;vertical-align:top;}#djDebug .panelContent table tr.odd td{background:#eee;}#djDebug .panelContent .close{float:right;font-weight:bold;}#djDebug .panelContent dt,#djDebug .panelContent dd{display:block;}#djDebug .panelContent dd{margin-left:10px;}#djDebug .highlight{color:#000;}#djDebug .highlight .err{color:#000;}#djDebug .highlight .g{color:#000;}#djDebug .highlight .k{color:#40684C;font-weight:bold;}#djDebug .highlight .o{color:#000;}#djDebug .highlight .n{color:#000;}#djDebug .highlight .mi{color:#40684C;font-weight:bold;}#djDebug .highlight .l{color:#000;}#djDebug .highlight .x{color:#000;}#djDebug .highlight .p{color:#000;}#djDebug .highlight .m{color:#40684C;font-weight:bold;}#djDebug .highlight .s{color:#0086d2;}#djDebug .highlight .w{color:#888;}#djDebug .highlight .il{color:#40684C;font-weight:bold;}#djDebug .highlight .na{color:#7D9029;}#djDebug .highlight .nt{color:#008000;font-weight:bold;}#djDebug .highlight .nv{color:#19177C;}#djDebug .highlight .s2{color:#BA2121;}#djDebug .highlight .cp{color:#BC7A00;}
\ No newline at end of file diff --git a/debug_toolbar/media/toolbar.min.js b/debug_toolbar/media/toolbar.min.js deleted file mode 100644 index 1f8b8b5..0000000 --- a/debug_toolbar/media/toolbar.min.js +++ /dev/null @@ -1 +0,0 @@ -var _$=window.$;jQuery.noConflict();jQuery(function(A){A.djDebug=function(C,B){A.djDebug.init()};A.extend(A.djDebug,{init:function(){var B=null;A("#djDebugPanelList li a").click(function(){B=A("#djDebug #"+this.className);if(B.is(":visible")){A(document).trigger("close.djDebug")}else{A(".panelContent").hide();B.show();A.djDebug.open()}return false});A("#djDebug a.close").click(function(){A(document).trigger("close.djDebug");return false});A("#djDebug a.remoteCall").click(function(){A("#djDebugWindow").load(this.href,{},function(){A("#djDebugWindow a.back").click(function(){A(this).parent().hide();return false})});A("#djDebugWindow").show();return false});A("#djDebugTemplatePanel a.djTemplateShowContext").click(function(){A.djDebug.toggle_content(A(this).parent().next())})},open:function(){A(document).bind("keydown.djDebug",function(B){if(B.keyCode==27){A.djDebug.close()}})},toggle_content:function(B){if(B.is(":visible")){B.hide()}else{B.show()}},close:function(){A(document).trigger("close.djDebug");return false}});A(document).bind("close.djDebug",function(){A(document).unbind("keydown.djDebug");A(".panelContent").hide()})});jQuery(function(){jQuery.djDebug()});$=_$;
\ No newline at end of file diff --git a/debug_toolbar/middleware.py b/debug_toolbar/middleware.py index 8dcf454..e4d7494 100644 --- a/debug_toolbar/middleware.py +++ b/debug_toolbar/middleware.py @@ -1,11 +1,14 @@ """ Debug Toolbar middleware """ +import os + from django.conf import settings from django.http import HttpResponseRedirect from django.shortcuts import render_to_response from django.utils.encoding import smart_unicode from django.conf.urls.defaults import include, patterns + import debug_toolbar.urls from debug_toolbar.toolbar.loader import DebugToolbar @@ -17,7 +20,7 @@ def replace_insensitive(string, target, replacement): Code borrowed from: http://forums.devshed.com/python-programming-11/case-insensitive-string-replace-490921.html """ no_case = string.lower() - index = no_case.find(target.lower()) + index = no_case.rfind(target.lower()) if index >= 0: return string[:index] + replacement + string[index + len(target):] else: # no results so return the original string @@ -34,11 +37,21 @@ class DebugToolbarMiddleware(object): self.original_pattern = patterns('', ('', include(self.original_urlconf)),) self.override_url = True - def show_toolbar(self, request): + # Set method to use to decide to show toolbar + self.show_toolbar = self._show_toolbar # default + if hasattr(settings, 'DEBUG_TOOLBAR_CONFIG'): + show_toolbar_callback = settings.DEBUG_TOOLBAR_CONFIG.get( + 'SHOW_TOOLBAR_CALLBACK', None) + if show_toolbar_callback: + self.show_toolbar = show_toolbar_callback + + def _show_toolbar(self, request): if not settings.DEBUG: return False - if request.is_ajax(): - return False + if request.is_ajax() and not \ + request.path.startswith(os.path.join('/', debug_toolbar.urls._PREFIX)): + # Allow ajax requests from the debug toolbar + return False if not request.META.get('REMOTE_ADDR') in settings.INTERNAL_IPS: return False return True diff --git a/debug_toolbar/panels/signals.py b/debug_toolbar/panels/signals.py new file mode 100644 index 0000000..7fe382e --- /dev/null +++ b/debug_toolbar/panels/signals.py @@ -0,0 +1,79 @@ +import sys + +from django.conf import settings +from django.core.signals import request_started, request_finished, \ + got_request_exception +from django.db.models.signals import class_prepared, pre_init, post_init, \ + pre_save, post_save, pre_delete, post_delete, post_syncdb +from django.dispatch.dispatcher import WEAKREF_TYPES +from django.template.loader import render_to_string + +try: + from django.db.backends.signals import connection_created +except ImportError: + connection_created = None + +from debug_toolbar.panels import DebugPanel + +class SignalDebugPanel(DebugPanel): + name = "Signals" + has_content = True + + SIGNALS = { + 'request_started': request_started, + 'request_finished': request_finished, + 'got_request_exception': got_request_exception, + 'connection_created': connection_created, + 'class_prepared': class_prepared, + 'pre_init': pre_init, + 'post_init': post_init, + 'pre_save': pre_save, + 'post_save': post_save, + 'pre_delete': pre_delete, + 'post_delete': post_delete, + 'post_syncdb': post_syncdb, + } + + def title(self): + return "Signals" + + def url(self): + return '' + + def signals(self): + signals = self.SIGNALS.copy() + if hasattr(settings, 'DEBUG_TOOLBAR_CONFIG'): + extra_signals = settings.DEBUG_TOOLBAR_CONFIG.get('EXTRA_SIGNALS', []) + else: + extra_signals = [] + for signal in extra_signals: + parts = signal.split('.') + path = '.'.join(parts[:-1]) + __import__(path) + signals[parts[-1]] = getattr(sys.modules[path], parts[-1]) + return signals + signals = property(signals) + + def content(self): + signals = [] + keys = self.signals.keys() + keys.sort() + for name in keys: + signal = self.signals[name] + if signal is None: + continue + receivers = [] + for (receiverkey, r_senderkey), receiver in signal.receivers: + if isinstance(receiver, WEAKREF_TYPES): + receiver = receiver() + if receiver is None: + continue + if getattr(receiver, 'im_self', None) is not None: + text = "method %s on %s object" % (receiver.__name__, receiver.im_self.__class__.__name__) + elif getattr(receiver, 'im_class', None) is not None: + text = "method %s on %s" % (receiver.__name__, receiver.im_class.__name__) + else: + text = "function %s" % receiver.__name__ + receivers.append(text) + signals.append((name, signal, receivers)) + return render_to_string('debug_toolbar/panels/signals.html', {'signals': signals}) diff --git a/debug_toolbar/panels/sql.py b/debug_toolbar/panels/sql.py index 7396c3a..d3ac7f3 100644 --- a/debug_toolbar/panels/sql.py +++ b/debug_toolbar/panels/sql.py @@ -1,5 +1,8 @@ +import os +import SocketServer import time -from debug_toolbar.panels import DebugPanel +import traceback +import django from django.conf import settings from django.db import connection from django.db.backends import util @@ -7,6 +10,28 @@ from django.template.loader import render_to_string from django.utils import simplejson from django.utils.encoding import force_unicode from django.utils.hashcompat import sha_constructor +from debug_toolbar.panels import DebugPanel + +# Figure out some paths +django_path = os.path.realpath(os.path.dirname(django.__file__)) +socketserver_path = os.path.realpath(os.path.dirname(SocketServer.__file__)) + +def tidy_stacktrace(strace): + """ + Clean up stacktrace and remove all entries that: + 1. Are part of Django (except contrib apps) + 2. Are part of SocketServer (used by Django's dev server) + 3. Are the last entry (which is part of our stacktracing code) + """ + trace = [] + for s in strace[:-1]: + s_path = os.path.realpath(s[0]) + if django_path in s_path and not 'django/contrib' in s_path: + continue + if socketserver_path in s_path: + continue + trace.append((s[0], s[1], s[2], s[3])) + return trace class DatabaseStatTracker(util.CursorDebugWrapper): """ @@ -19,6 +44,7 @@ class DatabaseStatTracker(util.CursorDebugWrapper): return self.cursor.execute(sql, params) finally: stop = time.time() + stacktrace = tidy_stacktrace(traceback.extract_stack()) _params = '' try: _params = simplejson.dumps([force_unicode(x) for x in params]) @@ -31,6 +57,7 @@ class DatabaseStatTracker(util.CursorDebugWrapper): 'raw_sql': sql, 'params': _params, 'hash': sha_constructor(settings.SECRET_KEY + sql + _params).hexdigest(), + 'stacktrace': stacktrace, }) util.CursorDebugWrapper = DatabaseStatTracker @@ -71,13 +98,15 @@ class SQLDebugPanel(DebugPanel): return render_to_string('debug_toolbar/panels/sql.html', context) def reformat_sql(sql): - sql = sql.replace('`,`', '`, `') + sql = sql.replace(',', ', ') sql = sql.replace('SELECT ', 'SELECT\n\t') - sql = sql.replace('` FROM ', '`\nFROM\n\t') + sql = sql.replace(' FROM ', '\nFROM\n\t') sql = sql.replace(' WHERE ', '\nWHERE\n\t') - sql = sql.replace(' INNER JOIN ', '\nINNER JOIN\n\t') - sql = sql.replace(' LEFT OUTER JOIN ', '\nLEFT OUTER JOIN\n\t') + sql = sql.replace(' INNER JOIN', '\n\tINNER JOIN') + sql = sql.replace(' LEFT OUTER JOIN' , '\n\tLEFT OUTER JOIN') sql = sql.replace(' ORDER BY ', '\nORDER BY\n\t') + sql = sql.replace(' HAVING ', '\nHAVING\n\t') + sql = sql.replace(' GROUP BY ', '\nGROUP BY\n\t') # Use Pygments to highlight SQL if it's available try: from pygments import highlight diff --git a/debug_toolbar/panels/template.py b/debug_toolbar/panels/template.py index fa85cb8..7dc7b06 100644 --- a/debug_toolbar/panels/template.py +++ b/debug_toolbar/panels/template.py @@ -48,11 +48,15 @@ class TemplateDebugPanel(DebugPanel): return '' def process_request(self, request): - self.context_processors = dict( - [("%s.%s" % (k.__module__, k.__name__), pformat(k(request))) for k in get_standard_processors()] - ) + self.request = request def content(self): + context_processors = dict( + [ + ("%s.%s" % (k.__module__, k.__name__), + pformat(k(self.request))) for k in get_standard_processors() + ] + ) template_context = [] for i, d in enumerate(self.templates): info = {} @@ -73,6 +77,6 @@ class TemplateDebugPanel(DebugPanel): context = { 'templates': template_context, 'template_dirs': [normpath(x) for x in settings.TEMPLATE_DIRS], - 'context_processors': self.context_processors, + 'context_processors': context_processors, } return render_to_string('debug_toolbar/panels/templates.html', context) diff --git a/debug_toolbar/templates/debug_toolbar/base.html b/debug_toolbar/templates/debug_toolbar/base.html index fca6256..66d126e 100644 --- a/debug_toolbar/templates/debug_toolbar/base.html +++ b/debug_toolbar/templates/debug_toolbar/base.html @@ -1,18 +1,29 @@ {% load i18n %} <script type="text/javascript" charset="utf-8"> + // When jQuery is sourced, it's going to overwrite whatever might be in the + // '$' variable, so store a reference of it in a temporary variable... + var _$ = window.$; if (typeof jQuery == 'undefined') { var jquery_url = '{{ BASE_URL }}/__debug__/m/jquery.js'; document.write(unescape('%3Cscript src="' + jquery_url + '" type="text/javascript"%3E%3C/script%3E')); } </script> <script type="text/javascript" src="{{ BASE_URL }}/__debug__/m/toolbar.min.js"></script> +<script type="text/javascript" charset="utf-8"> + // Now that jQuery is done loading, put the '$' variable back to what it was... + var $ = _$; +</script> <style type="text/css"> @import url({{ BASE_URL }}/__debug__/m/toolbar.min.css); </style> <div id="djDebug"> - <div id="djDebugToolbar"> + <div style="display: none;" id="djDebugToolbar"> <ul id="djDebugPanelList"> + {% if panels %} + <li><a id="djHideToolBarButton" href="#" title="Hide Toolbar">« Hide</a></li> + {% else %} <li id="djDebugButton">DEBUG</li> + {% endif %} {% for panel in panels %} <li> {% if panel.has_content %} @@ -24,6 +35,11 @@ {% endfor %} </ul> </div> + <div style="display: none;" id="djDebugToolbarHandle"> + <ul id="djDebugPanelList"> + <li><a title="Show Toolbar" id="djShowToolBarButton" href="#">»</a></li> + </ul> + </div> {% for panel in panels %} {% if panel.has_content %} <div id="{{ panel.dom_id }}" class="panelContent"> diff --git a/debug_toolbar/templates/debug_toolbar/panels/headers.html b/debug_toolbar/templates/debug_toolbar/panels/headers.html index 0a1578d..dec83bf 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/headers.html +++ b/debug_toolbar/templates/debug_toolbar/panels/headers.html @@ -9,7 +9,7 @@ </thead> <tbody> {% for key, value in headers.iteritems %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <td>{{ key|escape }}</td> <td>{{ value|escape }}</td> </tr> diff --git a/debug_toolbar/templates/debug_toolbar/panels/logger.html b/debug_toolbar/templates/debug_toolbar/panels/logger.html index 112cc7d..441337c 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/logger.html +++ b/debug_toolbar/templates/debug_toolbar/panels/logger.html @@ -12,7 +12,7 @@ </thead> <tbody> {% for record in records %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <td>{{ record.level }}</td> <td>{{ record.time|date:"h:i:s m/d/Y" }}</td> <td>{{ record.message }}</td> diff --git a/debug_toolbar/templates/debug_toolbar/panels/request_vars.html b/debug_toolbar/templates/debug_toolbar/panels/request_vars.html index 751e1da..677714d 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/request_vars.html +++ b/debug_toolbar/templates/debug_toolbar/panels/request_vars.html @@ -14,7 +14,7 @@ </thead> <tbody> {% for key, value in cookies %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <td>{{ key|escape }}</td> <td>{{ value|escape }}</td> </tr> @@ -39,7 +39,7 @@ </thead> <tbody> {% for key, value in session %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <td>{{ key|escape }}</td> <td>{{ value|escape }}</td> </tr> @@ -60,7 +60,7 @@ </thead> <tbody> {% for key, value in get %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <td>{{ key|escape }}</td> <td>{{ value|join:", "|escape }}</td> </tr> diff --git a/debug_toolbar/templates/debug_toolbar/panels/settings_vars.html b/debug_toolbar/templates/debug_toolbar/panels/settings_vars.html index 7b285ff..92b65cd 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/settings_vars.html +++ b/debug_toolbar/templates/debug_toolbar/panels/settings_vars.html @@ -9,7 +9,7 @@ </thead> <tbody> {% for var in settings.items|dictsort:"0" %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <td>{{ var.0 }}</td> <td><code>{{ var.1|pprint }}</code></td> </tr> diff --git a/debug_toolbar/templates/debug_toolbar/panels/signals.html b/debug_toolbar/templates/debug_toolbar/panels/signals.html new file mode 100644 index 0000000..e9a189e --- /dev/null +++ b/debug_toolbar/templates/debug_toolbar/panels/signals.html @@ -0,0 +1,19 @@ +<h3>Signals</h3> +<table> + <thead> + <tr> + <th>Signal</th> + <th>Providing Args</th> + <th>Receivers</th> + </tr> + </thead> + <tbody> + {% for name, signal, receivers in signals %} + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> + <td>{{ name|escape }}</td> + <td>{{ signal.providing_args|join:", " }}</td> + <td>{{ receivers|join:", " }}</td> + </tr> + {% endfor %} + </tbody> +</table> diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql.html b/debug_toolbar/templates/debug_toolbar/panels/sql.html index 50480bf..99ce611 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql.html @@ -3,14 +3,15 @@ <table> <thead> <tr> - <th>{% trans "Time" %} (ms)</th> - <th>{% trans "Action" %}</th> - <th>{% trans "Query" %}</th> + <th>Time (ms)</th> + <th>Action</th> + <th>Stacktrace</th> + <th>Query</th> </tr> </thead> <tbody> {% for query in queries %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <td>{{ query.time|floatformat:"2" }}</td> <td> {% if query.params %} @@ -21,6 +22,27 @@ {% endif %} {% endif %} </td> + <td> + {% if query.stacktrace %} + <div class="djSQLShowStacktraceDiv"><a class="djSQLShowStacktrace" href="#">Toggle Stacktrace</a></div> + <div class="djSQLHideStacktraceDiv" style="display:none;"> + <table> + <tr> + <th>Line</th> + <th>Method</th> + <th>File</th> + </tr> + {% for file, line, method in query.stacktrace %} + <tr> + <td>{{ line }}</td> + <td><pre>{{ method|escape }}<pre></td> + <td><pre>{{ file|escape }}</pre></td> + </tr> + {% endfor %} + </table> + </div> + {% endif %} + </td> <td class="syntax">{{ query.sql|safe }}</td> </tr> {% endfor %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html b/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html index cb3ca5e..64076e7 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql_explain.html @@ -17,7 +17,7 @@ </thead> <tbody> {% for row in result %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> {% for column in row %} <td>{{ column|escape }}</td> {% endfor %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html b/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html index c058b2b..f53dcc0 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql_profile.html @@ -17,7 +17,7 @@ </thead> <tbody> {% for row in result %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> {% for column in row %} <td>{{ column|escape }}</td> {% endfor %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/sql_select.html b/debug_toolbar/templates/debug_toolbar/panels/sql_select.html index c2ab7b6..1438050 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/sql_select.html +++ b/debug_toolbar/templates/debug_toolbar/panels/sql_select.html @@ -18,7 +18,7 @@ </thead> <tbody> {% for row in result %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> {% for column in row %} <td>{{ column|escape }}</td> {% endfor %} diff --git a/debug_toolbar/templates/debug_toolbar/panels/timer.html b/debug_toolbar/templates/debug_toolbar/panels/timer.html index 2d409b0..f593b44 100644 --- a/debug_toolbar/templates/debug_toolbar/panels/timer.html +++ b/debug_toolbar/templates/debug_toolbar/panels/timer.html @@ -13,7 +13,7 @@ </thead> <tbody> {% for key, value in rows %} - <tr class="{% cycle 'odd' 'even' %}"> + <tr class="{% cycle 'djDebugOdd' 'djDebugEven' %}"> <td>{{ key|escape }}</td> <td>{{ value|escape }}</td> </tr> diff --git a/debug_toolbar/templates/debug_toolbar/redirect.html b/debug_toolbar/templates/debug_toolbar/redirect.html index cdc08b9..5611977 100644 --- a/debug_toolbar/templates/debug_toolbar/redirect.html +++ b/debug_toolbar/templates/debug_toolbar/redirect.html @@ -4,7 +4,7 @@ </head> <body> <h1>HttpResponseRedirect</h1> -<p>Location: <a href="{{ redirect_to }}">{{ redirect_to }}</a></p> +<p>Location: <a href="{{ redirect_to|urlencode }}">{{ redirect_to }}</a></p> <p class="notice"> {% trans "The Django Debug Toolbar has intercepted a redirect to the above URL for debug viewing purposes. You can click the above link to continue with the diff --git a/debug_toolbar/toolbar/loader.py b/debug_toolbar/toolbar/loader.py index b217665..c7f999e 100644 --- a/debug_toolbar/toolbar/loader.py +++ b/debug_toolbar/toolbar/loader.py @@ -18,9 +18,10 @@ class DebugToolbar(object): 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', 'debug_toolbar.panels.headers.HeaderDebugPanel', 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', - 'debug_toolbar.panels.template.TemplateDebugPanel', 'debug_toolbar.panels.sql.SQLDebugPanel', - 'debug_toolbar.panels.cache.CacheDebugPanel', + 'debug_toolbar.panels.template.TemplateDebugPanel', + #'debug_toolbar.panels.cache.CacheDebugPanel', + 'debug_toolbar.panels.signals.SignalDebugPanel', 'debug_toolbar.panels.logger.LoggingPanel', ) self.load_panels() diff --git a/debug_toolbar/urls.py b/debug_toolbar/urls.py index 77d1a80..77491e3 100644 --- a/debug_toolbar/urls.py +++ b/debug_toolbar/urls.py @@ -7,10 +7,12 @@ this into the urlconf for the request. from django.conf.urls.defaults import * from django.conf import settings +_PREFIX = '__debug__' + urlpatterns = patterns('', - url(r'^__debug__/m/(.*)$', 'debug_toolbar.views.debug_media'), - url(r'^__debug__/sql_select/$', 'debug_toolbar.views.sql_select', name='sql_select'), - url(r'^__debug__/sql_explain/$', 'debug_toolbar.views.sql_explain', name='sql_explain'), - url(r'^__debug__/sql_profile/$', 'debug_toolbar.views.sql_profile', name='sql_profile'), - url(r'^__debug__/template_source/$', 'debug_toolbar.views.template_source', name='template_source'), + url(r'^%s/m/(.*)$' % _PREFIX, 'debug_toolbar.views.debug_media'), + url(r'^%s/sql_select/$' % _PREFIX, 'debug_toolbar.views.sql_select', name='sql_select'), + url(r'^%s/sql_explain/$' % _PREFIX, 'debug_toolbar.views.sql_explain', name='sql_explain'), + url(r'^%s/sql_profile/$' % _PREFIX, 'debug_toolbar.views.sql_profile', name='sql_profile'), + url(r'^%s/template_source/$' % _PREFIX, 'debug_toolbar.views.template_source', name='template_source'), ) diff --git a/debug_toolbar/views.py b/debug_toolbar/views.py index e3bb5b1..9123a00 100644 --- a/debug_toolbar/views.py +++ b/debug_toolbar/views.py @@ -8,16 +8,22 @@ import os import django.views.static from django.conf import settings from django.db import connection -from django.http import HttpResponse, HttpResponseBadRequest +from django.http import HttpResponseBadRequest from django.shortcuts import render_to_response from django.utils import simplejson from django.utils.hashcompat import sha_constructor +class InvalidSQLError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + def debug_media(request, path): root = getattr(settings, 'DEBUG_TOOLBAR_MEDIA_ROOT', None) if root is None: parent = os.path.abspath(os.path.dirname(__file__)) - root = os.path.join(parent, 'media') + root = os.path.join(parent, 'media', 'debug_toolbar') return django.views.static.serve(request, path, root) def sql_select(request): @@ -36,7 +42,7 @@ def sql_select(request): hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest() if hash != request.GET.get('hash', ''): return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert - if sql.lower().startswith('select'): + if sql.lower().strip().startswith('select'): params = simplejson.loads(params) cursor = connection.cursor() cursor.execute(sql, params) @@ -50,6 +56,7 @@ def sql_select(request): 'headers': headers, } return render_to_response('debug_toolbar/panels/sql_select.html', context) + raise InvalidSQLError("Only 'select' queries are allowed.") def sql_explain(request): """ @@ -67,7 +74,7 @@ def sql_explain(request): hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest() if hash != request.GET.get('hash', ''): return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert - if sql.lower().startswith('select'): + if sql.lower().strip().startswith('select'): params = simplejson.loads(params) cursor = connection.cursor() cursor.execute("EXPLAIN %s" % (sql,), params) @@ -81,6 +88,7 @@ def sql_explain(request): 'headers': headers, } return render_to_response('debug_toolbar/panels/sql_explain.html', context) + raise InvalidSQLError("Only 'select' queries are allowed.") def sql_profile(request): """ @@ -98,7 +106,7 @@ def sql_profile(request): hash = sha_constructor(settings.SECRET_KEY + sql + params).hexdigest() if hash != request.GET.get('hash', ''): return HttpResponseBadRequest('Tamper alert') # SQL Tampering alert - if sql.lower().startswith('select'): + if sql.lower().strip().startswith('select'): params = simplejson.loads(params) cursor = connection.cursor() cursor.execute("SET PROFILING=1") # Enable profiling @@ -116,6 +124,7 @@ def sql_profile(request): 'headers': headers, } return render_to_response('debug_toolbar/panels/sql_explain.html', context) + raise InvalidSQLError("Only 'select' queries are allowed.") def template_source(request): """ @@ -9,7 +9,7 @@ setup( author='Rob Hudson', author_email='rob@cogit8.org', url='http://rob.cogit8.org/blog/2008/Sep/19/introducing-django-debug-toolbar/', - #download_url='http://github.com/robhudson/django-debug-toolbar/tree/master', + download_url='http://github.com/robhudson/django-debug-toolbar/downloads', license='BSD', packages=find_packages(exclude=['ez_setup']), include_package_data=True, |
