Index: t3lib/config_default.php =================================================================== --- t3lib/config_default.php (Revision 7582) +++ t3lib/config_default.php (Arbeitskopie) @@ -509,7 +509,8 @@ $GLOBALS['error']->debug($variable, $name, $line, $file, $recursiveDepth, $debugLevel); } else { $br = ($name == '*variable*') ? 0 : $name; - t3lib_div::debug($variable, $br); + $group = $line ? $line : NULL; + t3lib_div::debug($variable, $br, $group); } } function debugBegin() { Index: t3lib/class.t3lib_db.php =================================================================== --- t3lib/class.t3lib_db.php (Revision 7582) +++ t3lib/class.t3lib_db.php (Arbeitskopie) @@ -1374,7 +1374,8 @@ 'lastBuiltQuery' => ($query ? $query : $this->debug_lastBuiltQuery), 'debug_backtrace' => t3lib_div::debug_trail(), ), - 'SQL debug' + $func, + is_object($GLOBALS['error']) && @is_callable(array($GLOBALS['error'],'debug')) ? '' : 'DB Error' ); } } Index: t3lib/js/extjs/ux/ext.ux.tabclosemenu.js =================================================================== --- t3lib/js/extjs/ux/ext.ux.tabclosemenu.js (Revision 0) +++ t3lib/js/extjs/ux/ext.ux.tabclosemenu.js (Revision 0) @@ -0,0 +1,151 @@ +/*! + * Ext JS Library 3.2.1 + * Copyright(c) 2006-2010 Ext JS, Inc. + * licensing@extjs.com + * http://www.extjs.com/license + */ +/** + * @class Ext.ux.TabCloseMenu + * @extends Object + * Plugin (ptype = 'tabclosemenu') for adding a close context menu to tabs. Note that the menu respects + * the closable configuration on the tab. As such, commands like remove others and remove all will not + * remove items that are not closable. + * + * @constructor + * @param {Object} config The configuration options + * @ptype tabclosemenu + */ +Ext.ux.TabCloseMenu = Ext.extend(Object, { + /** + * @cfg {String} closeTabText + * The text for closing the current tab. Defaults to 'Close Tab'. + */ + closeTabText: 'Close Tab', + + /** + * @cfg {String} closeOtherTabsText + * The text for closing all tabs except the current one. Defaults to 'Close Other Tabs'. + */ + closeOtherTabsText: 'Close Other Tabs', + + /** + * @cfg {Boolean} showCloseAll + * Indicates whether to show the 'Close All' option. Defaults to true. + */ + showCloseAll: true, + + /** + * @cfg {String} closeAllTabsText + *

The text for closing all tabs. Defaults to 'Close All Tabs'. + */ + closeAllTabsText: 'Close All Tabs', + + constructor : function(config){ + Ext.apply(this, config || {}); + }, + + //public + init : function(tabs){ + this.tabs = tabs; + tabs.on({ + scope: this, + contextmenu: this.onContextMenu, + destroy: this.destroy + }); + }, + + destroy : function(){ + Ext.destroy(this.menu); + delete this.menu; + delete this.tabs; + delete this.active; + }, + + // private + onContextMenu : function(tabs, item, e){ + this.active = item; + var m = this.createMenu(), + disableAll = true, + disableOthers = true, + closeAll = m.getComponent('closeall'); + + m.getComponent('close').setDisabled(!item.closable); + tabs.items.each(function(){ + if(this.closable){ + disableAll = false; + if(this != item){ + disableOthers = false; + return false; + } + } + return true; + }); + m.getComponent('closeothers').setDisabled(disableOthers); + if(closeAll){ + closeAll.setDisabled(disableAll); + } + + e.stopEvent(); + m.showAt(e.getPoint()); + }, + + createMenu : function(){ + if(!this.menu){ + var items = [{ + itemId: 'close', + text: this.closeTabText, + scope: this, + handler: this.onClose + }]; + if(this.showCloseAll){ + items.push('-'); + } + items.push({ + itemId: 'closeothers', + text: this.closeOtherTabsText, + scope: this, + handler: this.onCloseOthers + }); + if(this.showCloseAll){ + items.push({ + itemId: 'closeall', + text: this.closeAllTabsText, + scope: this, + handler: this.onCloseAll + }); + } + this.menu = new Ext.menu.Menu({ + items: items + }); + } + return this.menu; + }, + + onClose : function(){ + this.tabs.remove(this.active); + }, + + onCloseOthers : function(){ + this.doClose(true); + }, + + onCloseAll : function(){ + this.doClose(false); + }, + + doClose : function(excludeActive){ + var items = []; + this.tabs.items.each(function(item){ + if(item.closable){ + if(!excludeActive || item != this.active){ + items.push(item); + } + } + }, this); + Ext.each(items, function(item){ + this.tabs.remove(item); + }, this); + } +}); + +Ext.preg('tabclosemenu', Ext.ux.TabCloseMenu); \ No newline at end of file Index: t3lib/class.t3lib_div.php =================================================================== --- t3lib/class.t3lib_div.php (Revision 7582) +++ t3lib/class.t3lib_div.php (Arbeitskopie) @@ -3577,9 +3577,10 @@ * * @param mixed Variable to print * @param string The header. + * @param string Group for the debug console * @return void */ - public static function debug($var = '', $header = '') { + public static function debug($var = '', $header = '', $group = 'Debug') { // buffer the output of debug if no buffering started before if (ob_get_level()==0) { ob_start(); @@ -3618,59 +3619,44 @@ } if (TYPO3_MODE === 'BE') { - $tabHeader = 'Debug'; - if ($header) { - $tabHeader .= ': ' . htmlspecialchars($header); + $group = htmlspecialchars($group); + + if ($header !== '') { + $tabHeader = htmlspecialchars($header); + } else { + $tabHeader = 'Debug'; } + if (is_object($var)) { $debug = str_replace( - array('"', "\n", "\r"), - array('\"', '
', ''), + array('"', '/', '<', "\n", "\r"), + array('\"', '\/', '\<', '
', ''), $debug ); } else { $debug = str_replace( - array('"', "\n", "\r"), - array('\"', '', ''), + array('"', '/', '<', "\n", "\r"), + array('\"', '\/', '\<', '', ''), $debug ); } $script = ' - var TYPO3ViewportInstance = null, tab; - var debugString = "' . $debug . '"; + var TYPO3ViewportInstance = null; + var debugMessage = "' . $debug . '"; + var header = "' . $tabHeader . '"; + var group = "' . $group . '"; if (top && top.TYPO3 && typeof top.TYPO3.Backend === "object") { TYPO3ViewportInstance = top.TYPO3.Backend; - } else if (top.TYPO3 && typeof TYPO3.Backend === "object") { + } else if (typeof TYPO3 === "object" && typeof TYPO3.Backend === "object") { TYPO3ViewportInstance = TYPO3.Backend; } if (TYPO3ViewportInstance !== null) { - if (TYPO3ViewportInstance.DebugConsole.hidden) { - TYPO3ViewportInstance.DebugConsole.show(); - } else if (TYPO3ViewportInstance.DebugConsole.collapsed) { - TYPO3ViewportInstance.DebugConsole.expand(); - } - - tab = TYPO3ViewportInstance.DebugConsole.add({ - title: "' . $tabHeader . '", - html: debugString, - border: false, - closable: true, - autoScroll: true, - listeners: { - close: function(tab) { - if (tab.ownerCt.items.getCount() === 1) { - tab.ownerCt.hide(); - } - } - } - }); - TYPO3ViewportInstance.DebugConsole.setActiveTab(tab); - TYPO3ViewportInstance.doLayout(); + TYPO3ViewportInstance.DebugConsole.addTab(header, debugMessage, group); } else { - document.write(debugString); + document.write(debugMessage); } '; echo self::wrapJS($script); Index: typo3/js/extjs/debugPanel.js =================================================================== --- typo3/js/extjs/debugPanel.js (Revision 0) +++ typo3/js/extjs/debugPanel.js (Revision 0) @@ -0,0 +1,386 @@ +/*************************************************************** + * Copyright notice + * + * (c) 2010 Stefan Galinski + * All rights reserved + * + * This script is part of the TYPO3 project. The TYPO3 project is + * free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The GNU General Public License can be found at + * http://www.gnu.org/copyleft/gpl.html. + * A copy is found in the textfile GPL.txt and important notices to the license + * from the author is found in LICENSE.txt distributed with these scripts. + * + * + * This script is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * This copyright notice MUST APPEAR in all copies of the script! + ***************************************************************/ + +Ext.ns('TYPO3'); + +/** + * Debug panel based upon the widget tab panel + * + * If you want to add a new tab, you can use the addTab or addTabWidget methods. The first one + * creates the widget itself. If you need the latter one, you must create the widget yourself. + * + * The drag&drop functionality introduced a new attribute for the widget that should be added + * as a tab. It's called "draggableTab" and needs to be set to true, if you want activated + * drag&drop for the new tab. + * + * Additional Features: + * - Drag&Drop + * - Close tabs with a simple wheel/middle click + * - utilization of the tabCloseMenu context menu (several closing options) + * - Grouping of tabs (Only one nested level allowed!) + * + * @author Stefan Galinski + */ +TYPO3.DebugPanel = Ext.extend(Ext.TabPanel, { + /** + * Tab Groups + * + * @var Ext.util.MixedCollection + */ + tabGroups: new Ext.util.MixedCollection(), + + /** + * Initializes the widget and merges our defaults with the user-defined ones. The + * user-defined settings are preferred. + * + * @return void + */ + initComponent: function(config) { + config = config || {}; + Ext.apply(this, config, { + // activate general tab navigation with mouse wheel support + enableTabScroll: true, + defaults: { + autoScroll: true + }, + + // add the context menu actions + plugins: new Ext.ux.TabCloseMenu({ + closeTabText: TYPO3.LLL.core.tabs_close, + closeOtherTabsText: TYPO3.LLL.core.tabs_closeOther, + closeAllTabsText: TYPO3.LLL.core.tabs_closeAll + }) + }); + + // create a drop arrow indicator + this.on('render', function() { + this.arrow = Ext.DomHelper.append( + Ext.getBody(), + '

 
', + true + ); + this.arrow.hide(); + }, this); + + TYPO3.DebugPanel.superclass.initComponent.call(this); + }, + + /** + * Cleanup + * + * @return void + */ + onDestroy: function() { + Ext.destroy(this.arrow); + TYPO3.DebugPanel.superclass.onDestroy.call(this); + }, + + /** + * Adds a new tab + * + * If you need more possibilites, you should use the addTabWidget method. + * + * @see addTabWidget() + * @param header String tab header + * @param tabContent String content of the new tab + * @param group String tab group + * @param position Integer position of the new tab + * @return void + */ + addTab: function(header, tabContent, group, position) { + var tabWidget = new Ext.Panel({ + title: header, + html: tabContent, + border: false, + autoScroll: true, + closable: true, + draggableTab: true + }); + + this.addTabWidget(tabWidget, group, position); + }, + + /** + * Adds a new tab to the widget + * + * You can inject any Ext component, but you need to create it yourself. If you just + * want to add some text into a new tab, you should use the addTab function. + * + * @see addTab() + * @param tabWidget Component the component that should be added as a new tab + * @param group String tab group + * @param position Integer position of the new tab + * @return void + */ + addTabWidget: function(tabWidget, group, position) { + if (this.hidden) { + this.show(); + } else if (this.collapsed) { + this.expand(); + } + + // Move the widget into a tab group? + var tabGroup = this; + if (typeof group !== 'undefined' && group != '' && !this.isTabChildren) { + if (this.tabGroups.indexOfKey(group) === -1) { + tabGroup = new TYPO3.DebugPanel({ + border: false, + title: group, + autoScroll: true, + closable: true, + isTabChildren: true, + tabParent: this, + draggableTab: true + }); + this.addTabWidget(tabGroup); + + this.tabGroups.add(group, tabGroup); + } else { + tabGroup = this.tabGroups.key(group); + } + } + + // recalculate position if necessary + if (typeof position === 'undefined') { + position = tabGroup.items.getCount(); + } + + // hide the debug panel if the last element is closed + tabWidget.on('destroy', function(element) { + if (this.isTabChildren) { + if (!this.items.getCount()) { + this.tabParent.remove(this.tabParent.tabGroups.key(this.title)); + } + } else { + if (!this.items.getCount()) { + this.hide(); + this.fireEvent('resize'); + } + this.tabGroups.removeKey(element.title); + } + }, tabGroup); + + // add drag&drop and the wheel click functionality + tabWidget.on('afterlayout', function(element) { + Ext.get(this.id + '__' + element.id).on('mousedown', function(event) { + if (!Ext.isIE6 && !Ext.isIE7 && event.button === 1) { + event.stopEvent(); + this.remove(tabWidget); + return false; + } + return true; + }, this); + + if (tabWidget.draggableTab) { + this.initDragAndDropForTab(tabWidget); + } + }, tabGroup); + + // add the widget as a new tab + tabGroup.insert(position, tabWidget).show(); + tabGroup.ownerCt.doLayout(); + }, + + /** + * Extends the tab item with drag&drop functionality. + * + * @param item Component the tab widget + * @return void + */ + initDragAndDropForTab: function(item) { + item.tabDragZone = new Ext.dd.DragZone(this.id + '__' + item.id, { + ddGroup: this.id, + + /** + * Reintroduces the simple click event on a tab element. + * + * @return void + */ + b4MouseDown : function() { + item.show(); + Ext.dd.DragZone.superclass.b4MouseDown.apply(this, arguments); + }, + + /** + * On receipt of a mousedown event, see if it is within a draggable element. + * Return a drag data object if so. The data object can contain arbitrary application + * data, but it should also contain a DOM element in the ddel property to provide + * a proxy to drag. + * + * @param event Ext.EventObject + * @return drag data + */ + getDragData: function(event) { + var sourceElement = event.getTarget(item.itemSelector, 10); + if (sourceElement) { + var dragComponent = sourceElement.cloneNode(true); + dragComponent.id = Ext.id(); + item.dragData = { + ddel: dragComponent, + sourceEl: sourceElement, + repairXY: Ext.fly(sourceElement).getXY() + }; + return item.dragData; + } + + return false; + }, + + /** + * Provide coordinates for the proxy to slide back to on failed drag. + * This is the original XY coordinates of the draggable element. + * + * @return x,y coordinations of the original component position + */ + getRepairXY: function() { + return this.dragData.repairXY; + } + }); + + item.tabDropZone = new Ext.dd.DropZone(this.id + '__' + item.id, { + debugPanel: this, + ddGroup: this.id, + + /** + * If the mouse is over a tab element, return that node. This is + * provided as the "target" parameter in all "onNodeXXXX" node event + * handling functions + * + * @param event Ext.EventObject + * @return the tab element or boolean false + */ + getTargetFromEvent: function(event) { + var tabElement = Ext.get(event.getTarget()).findParentNode('li'); + if (tabElement !== null) { + return tabElement; + } + + return false; + }, + + /** + * On entry into a target node, highlight that node. + * + * @param target string id of the target element + * @return void + */ + onNodeEnter : function(target) { + Ext.get(target).addClass('typo3-debugPanel-dragDropOver'); + }, + + /** + * On exit from a target node, unhighlight that node. + * + * @param target string id of the target element + * @return void + */ + onNodeOut : function(target) { + Ext.get(target).removeClass('typo3-debugPanel-dragDropOver'); + this.debugPanel.arrow.hide(); + }, + + /** + * While over a target node, return the default drop allowed class which + * places a "tick" icon into the drag proxy. Also the arrow position is + * recalculated. + * + * @param target string id of the target element + * @param proxy Ext.dd.DDProxy proxy element + * @param event Ext.EventObject + * @return default dropAllowed class or a boolean false + */ + onNodeOver : function(target, proxy, event) { + // set arrow position + var element = Ext.get(target); + var left = 0; + var tabLeft = element.getX(); + var tabMiddle = tabLeft + element.dom.clientWidth / 2; + var tabRight = tabLeft + element.dom.clientWidth; + if (event.getPageX() <= tabMiddle) { + left = tabLeft; + } else { + left = tabRight; + } + this.debugPanel.arrow.setTop(this.el.getY() - 8).setLeft(left - 9).show(); + + // drop allowed? + if (proxy.handleElId !== target.id) { + return Ext.dd.DropZone.prototype.dropAllowed; + } + + return false; + }, + + /** + * On node drop we move the dragged tab element at the position of + * the dropped element. + * + * @param target string id of the target element + * @param proxy Ext.dd.DDProxy proxy element + * @param event Ext.EventObject + * @return true or false + */ + onNodeDrop : function(target, proxy, event) { + if (proxy.handleElId === target.id) { + return false; + } + + var dropPanelId = target.id.substring(this.debugPanel.id.length + 2); + var dragPanelId = proxy.handleElId.substring(this.debugPanel.id.length + 2); + + var dropPanelPosition = this.debugPanel.items.indexOfKey(dropPanelId); + var dragPanelPosition = this.debugPanel.items.indexOfKey(dragPanelId); + + if (dropPanelPosition !== undefined && + dropPanelPosition !== -1 && + dropPanelPosition <= this.debugPanel.items.getCount() + ) { + // calculate arrow position to decide if the elements needs + // to be inserted on the right or left + var element = Ext.get(target); + var tabMiddle = element.getX() + element.dom.clientWidth / 2; + if (dragPanelPosition > dropPanelPosition) { + if (event.getPageX() > tabMiddle) { + dropPanelPosition += 1; + } + } else { + if (event.getPageX() <= tabMiddle) { + dropPanelPosition -= 1; + } + } + + var dropEl = this.debugPanel.remove(dragPanelId, false); + this.debugPanel.addTabWidget(dropEl, '', dropPanelPosition); + } + + this.debugPanel.arrow.hide(); + return true; + } + }); + } +}); + +Ext.reg('typo3DebugPanel', TYPO3.DebugPanel); Index: typo3/js/extjs/viewportConfiguration.js =================================================================== --- typo3/js/extjs/viewportConfiguration.js (Revision 7582) +++ typo3/js/extjs/viewportConfiguration.js (Arbeitskopie) @@ -24,6 +24,8 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +Ext.ns('TYPO3'); + /** * The backend viewport configuration * @@ -75,7 +77,7 @@ }] }, { region: 'south', - xtype: 'tabpanel', + xtype: 'typo3DebugPanel', collapsible: true, collapseMode: 'mini', hideCollapseTool: true, @@ -84,30 +86,7 @@ autoScroll: true, hidden: true, height: 200, - title: 'Debug-Console', id: 'typo3-debug-console', - border: false, - enableTabScroll: true, - items: [{ - title: "", - id: 'typo3-debug-console-closerTab', - border: false, - closable: false, - listeners: { - activate: function(tab) { - var i, id = [], tabCount = tab.ownerCt.items.getCount(); - // get tab id's - for (var i = 1; i < tabCount; i++) { - id.push(tab.ownerCt.items.keys[i]); - } - // remove all tabs except first - for (var i = 0; i < id.length; i++) { - tab.ownerCt.remove(id[i]); - } - tab.ownerCt.hide(); - tab.ownerCt.fireEvent("resize"); - } - } - }] + border: false }] }; \ No newline at end of file Index: typo3/js/extjs/backendsizemanager.js =================================================================== --- typo3/js/extjs/backendsizemanager.js (Revision 7582) +++ typo3/js/extjs/backendsizemanager.js (Arbeitskopie) @@ -35,8 +35,14 @@ var resizeBackend = function() { var viewportHeight = document.viewport.getHeight(); var topHeight = Ext.get('typo3-topbar').getHeight(); + + var consoleHeight = 0; var debugConsole = Ext.get('typo3-debug-console'); - var consoleHeight = debugConsole.isVisible() ? 0 : debugConsole.getHeight() + debugConsole.getHeight(); + if (debugConsole.isVisible()) { + consoleHeight = debugConsole.getHeight() + + Ext.get('typo3-debug-console-xsplit').getHeight() + } + var styles = { height: (viewportHeight - topHeight - consoleHeight) + 'px' } Index: typo3/js/extjs/viewport.js =================================================================== --- typo3/js/extjs/viewport.js (Revision 7582) +++ typo3/js/extjs/viewport.js (Arbeitskopie) @@ -24,6 +24,8 @@ * This copyright notice MUST APPEAR in all copies of the script! ***************************************************************/ +Ext.ns('TYPO3'); + /** * Extends the viewport with some functionality for TYPO3. * @@ -115,7 +117,6 @@ } } }); - Ext.ComponentMgr.get('typo3-debug-console-closerTab').setTitle(TYPO3.LLL.core.tabs_closeAll); }, /** Index: typo3/backend.php =================================================================== --- typo3/backend.php (Revision 7582) +++ typo3/backend.php (Arbeitskopie) @@ -128,8 +128,10 @@ 'js/flashupload.js', '../t3lib/jsfunc.evalfield.js', '../t3lib/js/extjs/ux/flashmessages.js', + '../t3lib/js/extjs/ux/ext.ux.tabclosemenu.js', 'js/backend.js', 'js/loginrefresh.js', + 'js/extjs/debugPanel.js', 'js/extjs/viewport.js', 'js/extjs/viewportConfiguration.js', ); @@ -389,6 +391,8 @@ 'refresh_login_refresh_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:mess.refresh_login_refresh_button'), 'refresh_direct_logout_button' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:mess.refresh_direct_logout_button'), 'tabs_closeAll' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:tabs.closeAll'), + 'tabs_closeOther' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:tabs.closeOther'), + 'tabs_close' => $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xml:tabs.close'), ); $t3LLLfileUpload = array( 'windowTitle' => $GLOBALS['LANG']->getLL('fileUpload_windowTitle'), Index: typo3/sysext/t3skin/stylesheets/visual/debug_panel.css =================================================================== --- typo3/sysext/t3skin/stylesheets/visual/debug_panel.css (Revision 0) +++ typo3/sysext/t3skin/stylesheets/visual/debug_panel.css (Revision 0) @@ -0,0 +1,20 @@ +/* - - - - - - - - - - - - - - - - - - - - - +TYPO3 debug panel + +$Id$ +- - - - - - - - - - - - - - - - - - - - - */ + +.typo3-debugPanel-dragDropOver { +} + +.typo3-debugPanel-dragDropArrowDown { + background-image: url('../../icons/gfx/arrowdown.png'); + display: block; + visibility: visible; + z-index: 20000; + position: absolute; + width: 16px; + height: 16px; + top: 0; + left: 0; +} Index: typo3/sysext/lang/locallang_core.xml =================================================================== --- typo3/sysext/lang/locallang_core.xml (Revision 7582) +++ typo3/sysext/lang/locallang_core.xml (Arbeitskopie) @@ -263,7 +263,9 @@ - + + +