Modify the web code to allow the Query Tool and Debugger to be opened in new tabs, per settings in Preferences. Fixes #1344

Note that this does *not* enable the runtime to use multiple windows at this stage. It's really only useful in Server mode.
pull/3/head
Akshay Joshi 2017-03-24 14:43:56 +00:00 committed by Dave Page
parent 0eda6033df
commit 569ceb3906
8 changed files with 295 additions and 165 deletions

View File

@ -24,6 +24,7 @@ from pgadmin.utils.ajax import make_json_response, bad_request, \
internal_server_error internal_server_error
from config import PG_DEFAULT_DRIVER from config import PG_DEFAULT_DRIVER
from pgadmin.utils.preferences import Preferences
class DataGridModule(PgAdminModule): class DataGridModule(PgAdminModule):
@ -135,7 +136,11 @@ def initialize_datagrid(cmd_type, obj_type, sid, did, obj_id):
# Store the grid dictionary into the session variable # Store the grid dictionary into the session variable
session['gridData'] = sql_grid_data session['gridData'] = sql_grid_data
return make_json_response(data={'gridTransId': trans_id}) pref = Preferences.module('sqleditor')
new_browser_tab = pref.preference('new_browser_tab').get()
return make_json_response(data={'gridTransId': trans_id,
'newBrowserTab': new_browser_tab})
@blueprint.route('/panel/<int:trans_id>/<is_query_tool>/<path:editor_title>', methods=["GET"]) @blueprint.route('/panel/<int:trans_id>/<is_query_tool>/<path:editor_title>', methods=["GET"])
@ -171,10 +176,18 @@ def panel(trans_id, is_query_tool, editor_title):
if "linux" in _platform: if "linux" in _platform:
is_linux_platform = True is_linux_platform = True
pref = Preferences.module('sqleditor')
if pref.preference('new_browser_tab').get():
new_browser_tab = 'true'
else:
new_browser_tab = 'false'
return render_template("datagrid/index.html", _=gettext, uniqueId=trans_id, return render_template("datagrid/index.html", _=gettext, uniqueId=trans_id,
is_query_tool=is_query_tool, editor_title=editor_title, is_query_tool=is_query_tool,
script_type_url=sURL, is_desktop_mode=app.PGADMIN_RUNTIME, editor_title=editor_title, script_type_url=sURL,
is_linux=is_linux_platform) is_desktop_mode=app.PGADMIN_RUNTIME,
is_linux=is_linux_platform,
is_new_browser_tab=new_browser_tab)
@blueprint.route( @blueprint.route(
@ -234,7 +247,11 @@ def initialize_query_tool(sid, did=None):
# Store the grid dictionary into the session variable # Store the grid dictionary into the session variable
session['gridData'] = sql_grid_data session['gridData'] = sql_grid_data
return make_json_response(data={'gridTransId': trans_id}) pref = Preferences.module('sqleditor')
new_browser_tab = pref.preference('new_browser_tab').get()
return make_json_response(data={'gridTransId': trans_id,
'newBrowserTab': new_browser_tab})
@blueprint.route('/close/<int:trans_id>', methods=["GET"]) @blueprint.route('/close/<int:trans_id>', methods=["GET"])

View File

@ -310,7 +310,8 @@ function($, pgAdmin, R, S) {
{% endif %} {% endif %}
// Start the query tool. // Start the query tool.
sqlEditorController.start({{ is_query_tool }}, "{{ editor_title }}", script_sql); sqlEditorController.start({{ is_query_tool }}, "{{ editor_title }}",
script_sql, {{ is_new_browser_tab }});
}); });
}); });
{% endblock %} {% endblock %}

View File

@ -327,39 +327,57 @@ define(
var panel_title = ' Query-' + self.title_index; var panel_title = ' Query-' + self.title_index;
self.title_index += 1; self.title_index += 1;
var dashboardPanel = pgBrowser.docker.findPanels('dashboard');
dataGridPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, dashboardPanel[0]);
dataGridPanel.title(panel_title);
dataGridPanel.icon('fa fa-bolt');
dataGridPanel.focus();
// Listen on the panel closed event.
dataGridPanel.on(wcDocker.EVENT.CLOSED, function() {
$.ajax({
url: "{{ url_for('datagrid.index') }}" + "close/" + res.data.gridTransId,
method: 'GET'
});
});
// Open the panel if frame is initialized
baseUrl = "{{ url_for('datagrid.index') }}" + "panel/" + res.data.gridTransId + "/false/" baseUrl = "{{ url_for('datagrid.index') }}" + "panel/" + res.data.gridTransId + "/false/"
+ encodeURIComponent(grid_title); + encodeURIComponent(grid_title);
var openDataGridURL = function(j) {
j.data('embeddedFrame').$container.append(self.spinner_el); if (res.data.newBrowserTab) {
setTimeout(function() { var newWin = window.open(baseUrl, '_blank');
var frameInitialized = j.data('frameInitialized');
if (frameInitialized) { // Listen on the window closed event.
var frame = j.data('embeddedFrame'); newWin.addEventListener("unload", function(e){
if (frame) { $.ajax({
frame.openURL(baseUrl); url: "{{ url_for('datagrid.index') }}" + "close/" + res.data.gridTransId,
frame.$container.find('.wcLoadingContainer').hide(1); method: 'GET'
} });
} else { }, false);
// add a load listener to the window so that the title gets changed on page load
newWin.addEventListener("load", function() {
newWin.document.title = panel_title;
});
} else {
var dashboardPanel = pgBrowser.docker.findPanels('dashboard');
dataGridPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, dashboardPanel[0]);
dataGridPanel.title(panel_title);
dataGridPanel.icon('fa fa-bolt');
dataGridPanel.focus();
// Listen on the panel closed event.
dataGridPanel.on(wcDocker.EVENT.CLOSED, function() {
$.ajax({
url: "{{ url_for('datagrid.index') }}" + "close/" + res.data.gridTransId,
method: 'GET'
});
});
var openDataGridURL = function(j) {
j.data('embeddedFrame').$container.append(self.spinner_el);
setTimeout(function() {
var frameInitialized = j.data('frameInitialized');
if (frameInitialized) {
var frame = j.data('embeddedFrame');
if (frame) {
frame.openURL(baseUrl);
frame.$container.find('.wcLoadingContainer').hide(1);
}
} else {
openDataGridURL(j); openDataGridURL(j);
} }
}, 100); }, 100);
}; };
openDataGridURL($(dataGridPanel));
openDataGridURL($(dataGridPanel));
}
}, },
error: function(e) { error: function(e) {
alertify.alert( alertify.alert(
@ -422,42 +440,61 @@ define(
contentType: "application/json", contentType: "application/json",
success: function(res) { success: function(res) {
/* On successfully initialization find the dashboard panel,
* create new panel and add it to the dashboard panel.
*/
var dashboardPanel = pgBrowser.docker.findPanels('dashboard');
queryToolPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, dashboardPanel[0]);
queryToolPanel.title(panel_title);
queryToolPanel.icon('fa fa-bolt');
queryToolPanel.focus();
// Listen on the panel closed event.
queryToolPanel.on(wcDocker.EVENT.CLOSED, function() {
$.ajax({
url: "{{ url_for('datagrid.index') }}" + "close/" + res.data.gridTransId,
method: 'GET'
});
});
// Open the panel if frame is initialized // Open the panel if frame is initialized
baseUrl = "{{ url_for('datagrid.index') }}" + "panel/" + res.data.gridTransId + "/true/" baseUrl = "{{ url_for('datagrid.index') }}" + "panel/" + res.data.gridTransId + "/true/"
+ encodeURIComponent(grid_title) + '?' + "query_url=" + encodeURI(sURL); + encodeURIComponent(grid_title) + '?' + "query_url=" + encodeURI(sURL);
var openQueryToolURL = function(j) {
j.data('embeddedFrame').$container.append(pgAdmin.DataGrid.spinner_el); if (res.data.newBrowserTab) {
setTimeout(function() { var newWin = window.open(baseUrl, '_blank');
var frameInitialized = j.data('frameInitialized');
if (frameInitialized) { // Listen on the window closed event.
var frame = j.data('embeddedFrame'); newWin.addEventListener("unload", function(e){
if (frame) { $.ajax({
frame.openURL(baseUrl); url: "{{ url_for('datagrid.index') }}" + "close/" + res.data.gridTransId,
frame.$container.find('.wcLoadingContainer').delay(1000).hide(1); method: 'GET'
});
}, false);
// add a load listener to the window so that the title gets changed on page load
newWin.addEventListener("load", function() {
newWin.document.title = panel_title;
});
} else {
/* On successfully initialization find the dashboard panel,
* create new panel and add it to the dashboard panel.
*/
var dashboardPanel = pgBrowser.docker.findPanels('dashboard');
queryToolPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, dashboardPanel[0]);
queryToolPanel.title(panel_title);
queryToolPanel.icon('fa fa-bolt');
queryToolPanel.focus();
// Listen on the panel closed event.
queryToolPanel.on(wcDocker.EVENT.CLOSED, function() {
$.ajax({
url: "{{ url_for('datagrid.index') }}" + "close/" + res.data.gridTransId,
method: 'GET'
});
});
var openQueryToolURL = function(j) {
j.data('embeddedFrame').$container.append(pgAdmin.DataGrid.spinner_el);
setTimeout(function() {
var frameInitialized = j.data('frameInitialized');
if (frameInitialized) {
var frame = j.data('embeddedFrame');
if (frame) {
frame.openURL(baseUrl);
frame.$container.find('.wcLoadingContainer').delay(1000).hide(1);
}
} else {
openQueryToolURL(j);
} }
} else { }, 100);
openQueryToolURL(j); };
}
}, 100); openQueryToolURL($(queryToolPanel));
}; }
openQueryToolURL($(queryToolPanel));
}, },
error: function(e) { error: function(e) {
alertify.alert( alertify.alert(

View File

@ -25,6 +25,7 @@ from pgadmin.utils.driver import get_driver
from config import PG_DEFAULT_DRIVER from config import PG_DEFAULT_DRIVER
from pgadmin.model import db, DebuggerFunctionArguments from pgadmin.model import db, DebuggerFunctionArguments
from pgadmin.utils.preferences import Preferences
# Constants # Constants
ASYNC_OK = 1 ASYNC_OK = 1
@ -42,6 +43,7 @@ class DebuggerModule(PgAdminModule):
- Method is used to load the required javascript files for debugger module - Method is used to load the required javascript files for debugger module
""" """
LABEL = gettext("Debugger")
def get_own_javascripts(self): def get_own_javascripts(self):
scripts = list() scripts = list()
@ -58,6 +60,14 @@ class DebuggerModule(PgAdminModule):
return scripts return scripts
def register_preferences(self):
self.open_in_new_tab = self.preference.register(
'display', 'debugger_new_browser_tab',
gettext("Open in New Browser Tab"), 'boolean', False,
category_label=gettext('Display'),
help_str=gettext('If set to True, the Debugger '
'will be opened in a new browser tab.')
)
blueprint = DebuggerModule(MODULE_NAME, __name__) blueprint = DebuggerModule(MODULE_NAME, __name__)
@ -317,7 +327,7 @@ def direct_new(trans_id):
return render_template( return render_template(
"debugger/direct.html", "debugger/direct.html",
_=gettext, _=gettext,
function_name='test', function_name=obj['function_name'],
uniqueId=trans_id, uniqueId=trans_id,
debug_type=debug_type, debug_type=debug_type,
is_desktop_mode=current_app.PGADMIN_RUNTIME, is_desktop_mode=current_app.PGADMIN_RUNTIME,
@ -435,6 +445,7 @@ def initialize_target(debug_type, sid, did, scid, func_id, tri_id=None):
'database_id': did, 'database_id': did,
'schema_id': scid, 'schema_id': scid,
'function_id': func_id, 'function_id': func_id,
'function_name': session['funcData']['name'],
'debug_type': debug_type, 'debug_type': debug_type,
'debugger_version': debugger_version, 'debugger_version': debugger_version,
'frame_id': 0, 'frame_id': 0,
@ -478,7 +489,13 @@ def initialize_target(debug_type, sid, did, scid, func_id, tri_id=None):
# Delete the 'funcData' session variables as it is not used now as target is initialized # Delete the 'funcData' session variables as it is not used now as target is initialized
del session['funcData'] del session['funcData']
return make_json_response(data={'status': status, 'debuggerTransId': trans_id}) pref = Preferences.module('debugger')
new_browser_tab = pref.preference('debugger_new_browser_tab').get()
return make_json_response(data={'status': status,
'debuggerTransId': trans_id,
'newBrowserTab': new_browser_tab})
@blueprint.route('/close/<int:trans_id>', methods=["GET"]) @blueprint.route('/close/<int:trans_id>', methods=["GET"])

View File

@ -241,14 +241,26 @@ define(
success: function(res) { success: function(res) {
var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId; var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId;
pgBrowser.Events.once( if (res.data.newBrowserTab) {
'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) { var newWin = window.open(url, '_blank');
frame.openURL(url);
});
// Create the debugger panel as per the data received from user input dialog. // Listen on the window closed event.
var dashboardPanel = pgBrowser.docker.findPanels( newWin.addEventListener("unload", function(e){
'dashboard' var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
$.ajax({
url: closeUrl,
method: 'GET'
});
}, false);
} else {
pgBrowser.Events.once(
'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
frame.openURL(url);
});
// Create the debugger panel as per the data received from user input dialog.
var dashboardPanel = pgBrowser.docker.findPanels(
'dashboard'
), ),
panel = pgBrowser.docker.addPanel( panel = pgBrowser.docker.addPanel(
'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0] 'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
@ -264,12 +276,13 @@ define(
method: 'GET' method: 'GET'
}); });
}); });
},
error: function(e) {
Alertify.alert(
'Debugger target Initialize Error'
);
} }
},
error: function(e) {
Alertify.alert(
'Debugger target initialization error'
);
}
}); });
}, },
@ -331,29 +344,42 @@ define(
var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId; var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId;
pgBrowser.Events.once( if (res.data.newBrowserTab) {
'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) { var newWin = window.open(url, '_blank');
frame.openURL(url);
});
// Create the debugger panel as per the data received from user input dialog. // Listen on the window closed event.
var dashboardPanel = pgBrowser.docker.findPanels( newWin.addEventListener("unload", function(e){
'dashboard' var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
), $.ajax({
panel = pgBrowser.docker.addPanel( url: closeUrl,
'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0] method: 'GET'
); });
}, false);
panel.focus(); } else {
pgBrowser.Events.once(
// Register Panel Closed event 'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
panel.on(wcDocker.EVENT.CLOSED, function() { frame.openURL(url);
var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
$.ajax({
url: closeUrl,
method: 'GET'
}); });
});
// Create the debugger panel as per the data received from user input dialog.
var dashboardPanel = pgBrowser.docker.findPanels(
'dashboard'
),
panel = pgBrowser.docker.addPanel(
'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
);
panel.focus();
// Register Panel Closed event
panel.on(wcDocker.EVENT.CLOSED, function() {
var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
$.ajax({
url: closeUrl,
method: 'GET'
});
});
}
}, },
error: function(e) { error: function(e) {
Alertify.alert( Alertify.alert(

View File

@ -553,29 +553,40 @@ define(
var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId; var url = "{{ url_for('debugger.index') }}" + "direct/" + res.data.debuggerTransId;
pgBrowser.Events.once( if (res.data.newBrowserTab) {
'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) { var newWin = window.open(url, '_blank');
frame.openURL(url);
});
// Create the debugger panel as per the data received from user input dialog. // Listen on the window closed event.
var dashboardPanel = pgBrowser.docker.findPanels( newWin.addEventListener("unload", function(e){
'dashboard' var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
), $.ajax({
panel = pgBrowser.docker.addPanel( url: closeUrl,
'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0] method: 'GET'
); });
}, false);
panel.focus(); } else {
pgBrowser.Events.once(
// Panel Closed event 'pgadmin-browser:frame:urlloaded:frm_debugger', function(frame) {
panel.on(wcDocker.EVENT.CLOSED, function() { frame.openURL(url);
var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
$.ajax({
url: closeUrl,
method: 'GET'
}); });
});
// Create the debugger panel as per the data received from user input dialog.
var dashboardPanel = pgBrowser.docker.findPanels('dashboard'),
panel = pgBrowser.docker.addPanel(
'frm_debugger', wcDocker.DOCK.STACKED, dashboardPanel[0]
);
panel.focus();
// Panel Closed event
panel.on(wcDocker.EVENT.CLOSED, function() {
var closeUrl = "{{ url_for('debugger.index') }}" + "close/" + res.data.debuggerTransId;
$.ajax({
url: closeUrl,
method: 'GET'
});
});
}
if (d._type == "function") { if (d._type == "function") {
var _Url = "{{ url_for('debugger.index') }}" + "set_arguments/" + treeInfo.server._id + var _Url = "{{ url_for('debugger.index') }}" + "set_arguments/" + treeInfo.server._id +

View File

@ -84,6 +84,14 @@ class SqlEditorModule(PgAdminModule):
'Values greater than 1 display the notifier for the number of seconds specified.') 'Values greater than 1 display the notifier for the number of seconds specified.')
) )
self.open_in_new_tab = self.preference.register(
'display', 'new_browser_tab',
gettext("Open in New Browser Tab"), 'boolean', False,
category_label=gettext('Display'),
help_str=gettext('If set to True, the Query Tool '
'will be opened in a new browser tab.')
)
self.explain_verbose = self.preference.register( self.explain_verbose = self.preference.register(
'Explain', 'explain_verbose', 'Explain', 'explain_verbose',
gettext("Verbose output?"), 'boolean', False, gettext("Verbose output?"), 'boolean', False,

View File

@ -228,36 +228,38 @@ define(
self.render_history_grid(); self.render_history_grid();
// Listen on the panel closed event and notify user to save modifications. if (!self.handler.is_new_browser_tab) {
_.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) { // Listen on the panel closed event and notify user to save modifications.
if(p.isVisible()) { _.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
p.on(wcDocker.EVENT.CLOSING, function() { if(p.isVisible()) {
// Only if we can edit data then perform this check p.on(wcDocker.EVENT.CLOSING, function() {
var notify = false, msg; // Only if we can edit data then perform this check
if(self.handler.can_edit) { var notify = false, msg;
var data_store = self.handler.data_store; if(self.handler.can_edit) {
if(data_store && (_.size(data_store.added) || var data_store = self.handler.data_store;
_.size(data_store.updated))) { if(data_store && (_.size(data_store.added) ||
msg = "{{ _('The data has been modified, but not saved. Are you sure you wish to discard the changes?') }}"; _.size(data_store.updated))) {
msg = "{{ _('The data has been modified, but not saved. Are you sure you wish to discard the changes?') }}";
notify = true;
}
} else if(self.handler.is_query_tool && self.handler.is_query_changed) {
msg = "{{ _('The query has been modified, but not saved. Are you sure you wish to discard the changes?') }}";
notify = true; notify = true;
} }
} else if(self.handler.is_query_tool && self.handler.is_query_changed) { if(notify) {return self.user_confirmation(p, msg);}
msg = "{{ _('The query has been modified, but not saved. Are you sure you wish to discard the changes?') }}"; return true;
notify = true; });
} // Set focus on query tool of active panel
if(notify) {return self.user_confirmation(p, msg);} p.on(wcDocker.EVENT.GAIN_FOCUS, function() {
return true; if (!$(p.$container).hasClass('wcPanelTabContentHidden')) {
}); setTimeout(function() {
// Set focus on query tool of active panel self.handler.gridView.query_tool_obj.focus();
p.on(wcDocker.EVENT.GAIN_FOCUS, function() { }, 200);
if (!$(p.$container).hasClass('wcPanelTabContentHidden')) { }
setTimeout(function() { });
self.handler.gridView.query_tool_obj.focus(); }
}, 200); });
} }
});
}
});
// set focus on query tool once loaded // set focus on query tool once loaded
setTimeout(function() { setTimeout(function() {
@ -1517,7 +1519,7 @@ define(
* call the render method of the grid view to render the backgrid * call the render method of the grid view to render the backgrid
* header and loading icon and start execution of the sql query. * header and loading icon and start execution of the sql query.
*/ */
start: function(is_query_tool, editor_title, script_sql) { start: function(is_query_tool, editor_title, script_sql, is_new_browser_tab) {
var self = this; var self = this;
self.is_query_tool = is_query_tool; self.is_query_tool = is_query_tool;
@ -1527,6 +1529,7 @@ define(
self.explain_costs = false; self.explain_costs = false;
self.explain_buffers = false; self.explain_buffers = false;
self.explain_timing = false; self.explain_timing = false;
self.is_new_browser_tab = is_new_browser_tab;
// We do not allow to call the start multiple times. // We do not allow to call the start multiple times.
if (self.gridView) if (self.gridView)
@ -2423,11 +2426,17 @@ define(
// Set panel title. // Set panel title.
setTitle: function(title) { setTitle: function(title) {
_.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) { var self = this;
if(p.isVisible()) {
p.title(decodeURIComponent(title)); if (self.is_new_browser_tab) {
} window.document.title = title;
}); } else {
_.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
if(p.isVisible()) {
p.title(decodeURIComponent(title));
}
});
}
}, },
// load select file dialog // load select file dialog
@ -2571,14 +2580,18 @@ define(
} else { } else {
var title = ''; var title = '';
// Find the title of the visible panel if (self.is_new_browser_tab) {
_.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) { title = window.document.title + ' *';
if(p.isVisible()) { } else {
self.gridView.panel_title = p._title; // Find the title of the visible panel
} _.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function(p) {
}); if(p.isVisible()) {
self.gridView.panel_title = p._title;
}
});
title = self.gridView.panel_title + ' *'; title = self.gridView.panel_title + ' *';
}
self.setTitle(title); self.setTitle(title);
} }