Hi, Plz find attached patch to fix session expired issue from sqleditor/datadrid.
-- *Harshal Dhumal* *Sr. Software Engineer* EnterpriseDB India: http://www.enterprisedb.com The Enterprise PostgreSQL Company
diff --git a/web/pgadmin/__init__.py b/web/pgadmin/__init__.py index db676c5..220ea6b 100644 --- a/web/pgadmin/__init__.py +++ b/web/pgadmin/__init__.py @@ -25,7 +25,7 @@ from flask_paranoid import Paranoid from pgadmin.utils import PgAdminModule, driver from pgadmin.utils.versioned_template_loader import VersionedTemplateLoader -from pgadmin.utils.session import create_session_interface +from pgadmin.utils.session import create_session_interface, pga_unauthorised from werkzeug.local import LocalProxy from werkzeug.utils import find_modules @@ -344,6 +344,9 @@ def create_app(app_name=None): security.init_app(app, user_datastore) + # register custom unauthorised handler. + app.login_manager.unauthorized_handler(pga_unauthorised) + app.session_interface = create_session_interface(app) # Make the Session more secure against XSS & CSRF when running in web mode diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py index 5330942..626526c 100644 --- a/web/pgadmin/browser/server_groups/servers/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/__init__.py @@ -202,6 +202,9 @@ class ServerModule(sg.ServerGroupPluginModule): """ ServerType.register_preferences() + def get_exposed_url_endpoints(self): + return ['NODE-server.connect_id'] + class ServerMenuItem(MenuItem): def __init__(self, **kwargs): diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py index 6b1d699..75f63d7 100644 --- a/web/pgadmin/tools/datagrid/__init__.py +++ b/web/pgadmin/tools/datagrid/__init__.py @@ -17,7 +17,6 @@ import random from flask import Response, url_for, session, request, make_response from werkzeug.useragents import UserAgent from flask import current_app as app -from flask_babel import gettext from flask_security import login_required from pgadmin.tools.sqleditor.command import * from pgadmin.utils import PgAdminModule @@ -27,6 +26,9 @@ from pgadmin.utils.ajax import make_json_response, bad_request, \ from config import PG_DEFAULT_DRIVER from pgadmin.utils.preferences import Preferences from pgadmin.model import Server +from pgadmin.utils.driver import get_driver +from pgadmin.utils.exception import ConnectionLost + class DataGridModule(PgAdminModule): """ @@ -90,11 +92,11 @@ def show_filter(): @blueprint.route( - '/initialize/datagrid/<int:cmd_type>/<obj_type>/<int:sid>/<int:did>/<int:obj_id>', + '/initialize/datagrid/<int:cmd_type>/<obj_type>/<int:sgid>/<int:sid>/<int:did>/<int:obj_id>', methods=["PUT", "POST"], endpoint="initialize_datagrid" ) @login_required -def initialize_datagrid(cmd_type, obj_type, sid, did, obj_id): +def initialize_datagrid(cmd_type, obj_type, sgid, sid, did, obj_id): """ This method is responsible for creating an asynchronous connection. After creating the connection it will instantiate and initialize @@ -104,6 +106,7 @@ def initialize_datagrid(cmd_type, obj_type, sid, did, obj_id): Args: cmd_type: Contains value for which menu item is clicked. obj_type: Contains type of selected object for which data grid to be render + sgid: Server group Id sid: Server Id did: Database Id obj_id: Id of currently selected object @@ -118,15 +121,26 @@ def initialize_datagrid(cmd_type, obj_type, sid, did, obj_id): conn_id = str(random.randint(1, 9999999)) try: manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) + # default_conn is same connection which is created when user connect to + # database from tree + default_conn = manager.connection(did=did) conn = manager.connection(did=did, conn_id=conn_id, use_binary_placeholder=True, array_to_string=True) + except ConnectionLost as e: + raise except Exception as e: + app.logger.error(e) return internal_server_error(errormsg=str(e)) - # Connect the Server + status, msg = default_conn.connect() + if not status: + app.logger.error(msg) + return internal_server_error(errormsg=str(msg)) + status, msg = conn.connect() if not status: + app.logger.error(msg) return internal_server_error(errormsg=str(msg)) try: @@ -135,10 +149,13 @@ def initialize_datagrid(cmd_type, obj_type, sid, did, obj_id): obj_type = 'table' # Get the object as per the object type - command_obj = ObjectRegistry.get_object(obj_type, conn_id=conn_id, sid=sid, - did=did, obj_id=obj_id, cmd_type=cmd_type, + command_obj = ObjectRegistry.get_object(obj_type, conn_id=conn_id, + sgid=sgid, sid=sid, did=did, + obj_id=obj_id, + cmd_type=cmd_type, sql_filter=filter_sql) except Exception as e: + app.logger.error(e) return internal_server_error(errormsg=str(e)) # Create a unique id for the transaction @@ -217,11 +234,6 @@ def panel(trans_id, is_query_tool, editor_title): else: new_browser_tab = 'false' - if is_query_tool == 'true': - prompt_save_changes = pref.preference('prompt_save_query_changes').get() - else: - prompt_save_changes = pref.preference('prompt_save_data_changes').get() - # Fetch the server details # bgcolor = None @@ -239,6 +251,21 @@ def panel(trans_id, is_query_tool, editor_title): bgcolor = s.bgcolor fgcolor = s.fgcolor or 'black' + url_params = dict() + if is_query_tool == 'true': + prompt_save_changes = pref.preference('prompt_save_query_changes').get() + url_params['sgid'] = trans_obj.sgid + url_params['sid'] = trans_obj.sid + url_params['did'] = trans_obj.did + else: + prompt_save_changes = pref.preference('prompt_save_data_changes').get() + url_params['cmd_type'] = trans_obj.cmd_type + url_params['obj_type'] = trans_obj.object_type + url_params['sgid'] = trans_obj.sgid + url_params['sid'] = trans_obj.sid + url_params['did'] = trans_obj.did + url_params['obj_id'] = trans_obj.obj_id + return render_template( "datagrid/index.html", _=gettext, uniqueId=trans_id, is_query_tool=is_query_tool, @@ -252,48 +279,41 @@ def panel(trans_id, is_query_tool, editor_title): fgcolor=fgcolor, # convert python boolean value to equivalent js boolean literal before # passing it to html template. - prompt_save_changes='true' if prompt_save_changes else 'false' + prompt_save_changes='true' if prompt_save_changes else 'false', + url_params=json.dumps(url_params) ) @blueprint.route( - '/initialize/query_tool/<int:sid>/<int:did>', + '/initialize/query_tool/<int:sgid>/<int:sid>/<int:did>', methods=["POST"], endpoint='initialize_query_tool_with_did' ) @blueprint.route( - '/initialize/query_tool/<int:sid>', + '/initialize/query_tool/<int:sgid>/<int:sid>', methods=["POST"], endpoint='initialize_query_tool' ) @login_required -def initialize_query_tool(sid, did=None): +def initialize_query_tool(sgid, sid, did=None): """ This method is responsible for instantiating and initializing the query tool object. It will also create a unique transaction id and store the information into session variable. Args: + sgid: Server group Id sid: Server Id did: Database Id """ if did is None: # Use Maintenance database OID - from pgadmin.utils.driver import get_driver - driver = get_driver(PG_DEFAULT_DRIVER) - manager = driver.connection_manager(sid) - conn = manager.connection() - if conn.connected(): - did = manager.did - else: - internal_server_error( - errormsg=gettext( - 'Server disconnected. Please connect and try again.' - ) - ) - + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) + did = manager.did try: - command_obj = ObjectRegistry.get_object('query_tool', sid=sid, did=did) + command_obj = ObjectRegistry.get_object('query_tool', sgid=sgid, + sid=sid, did=did) except Exception as e: + app.logger.error(e) return internal_server_error(errormsg=str(e)) # Create a unique id for the transaction @@ -330,6 +350,8 @@ def close(trans_id): Args: trans_id: unique transaction id """ + if 'gridData' not in session: + return make_json_response(data={'status': True}) grid_data = session['gridData'] # Return from the function if transaction id not found @@ -346,13 +368,15 @@ def close(trans_id): manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(cmd_obj.sid) conn = manager.connection(did=cmd_obj.did, conn_id=cmd_obj.conn_id) except Exception as e: + app.logger.error(e) return internal_server_error(errormsg=str(e)) # Release the connection if conn.connected(): manager.release(did=cmd_obj.did, conn_id=cmd_obj.conn_id) - # Remove the information of unique transaction id from the session variable. + # Remove the information of unique transaction id from the session + # variable. grid_data.pop(str(trans_id), None) session['gridData'] = grid_data @@ -384,6 +408,7 @@ def validate_filter(sid, did, obj_id): # Call validate_filter method to validate the SQL. status, res = sql_filter_obj.validate_filter(filter_sql) except Exception as e: + app.logger.error(e) return internal_server_error(errormsg=str(e)) return make_json_response(data={'status': status, 'result': res}) diff --git a/web/pgadmin/tools/datagrid/static/js/datagrid.js b/web/pgadmin/tools/datagrid/static/js/datagrid.js index c44fbb1..28082b8 100644 --- a/web/pgadmin/tools/datagrid/static/js/datagrid.js +++ b/web/pgadmin/tools/datagrid/static/js/datagrid.js @@ -1,7 +1,9 @@ define('pgadmin.datagrid', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'pgadmin.alertifyjs', - 'sources/pgadmin', 'bundled_codemirror', 'sources/sqleditor_utils', 'wcdocker' -], function(gettext, url_for, $, _, alertify, pgAdmin, codemirror, sqlEditorUtils) { + 'sources/pgadmin', 'bundled_codemirror', 'sources/sqleditor_utils', + 'backbone', 'wcdocker' +], function(gettext, url_for, $, _, alertify, pgAdmin, codemirror, +sqlEditorUtils, Backbone) { // Some scripts do export their object in the window only. // Generally the one, which do no have AMD support. var wcDocker = window.wcDocker, @@ -12,7 +14,9 @@ define('pgadmin.datagrid', [ if (pgAdmin.DataGrid) return pgAdmin.DataGrid; - pgAdmin.DataGrid = { + pgAdmin.DataGrid = + _.extend( + { init: function() { if (this.initialized) return; @@ -112,6 +116,9 @@ define('pgadmin.datagrid', [ // Load the newly created frame dataGridFrameType.load(pgBrowser.docker); + this.on("pgadmin-datagrid:transaction:created", function(trans_obj) { + this.launch_grid(trans_obj); + }); }, // This is a callback function to show data when user click on menu item. @@ -154,6 +161,7 @@ define('pgadmin.datagrid', [ var url_params = { 'cmd_type': data.mnuid, 'obj_type': d._type, + 'sgid': parentData.server_group._id, 'sid': parentData.server._id, 'did': parentData.database._id, 'obj_id': d._id @@ -163,8 +171,7 @@ define('pgadmin.datagrid', [ var grid_title = parentData.server.label + ' - ' + parentData.database.label + ' - ' + nsp_name + '.' + d.label; - // Initialize the data grid. - self.initialize_data_grid(baseUrl, grid_title, '', parentData.server.server_type); + self.create_transaction(baseUrl, null, 'false', parentData.server.server_type, '', grid_title, ''); }, // This is a callback function to show filtered data when user click on menu item. @@ -209,10 +216,10 @@ define('pgadmin.datagrid', [ var url_params = { 'cmd_type': data.mnuid, 'obj_type': d._type, + 'sgid': parentData.server_group._id, 'sid': parentData.server._id, 'did': parentData.database._id, 'obj_id': d._id - }; var baseUrl = url_for('datagrid.initialize_datagrid', url_params); @@ -295,7 +302,7 @@ define('pgadmin.datagrid', [ success: function(res) { if (res.data.status) { // Initialize the data grid. - self.initialize_data_grid(that.baseUrl, grid_title, sql, parentData.server.server_type); + self.create_transaction(that.baseUrl, null, 'false', parentData.server.server_type, '', grid_title, sql); } else { alertify.alert( @@ -348,88 +355,6 @@ define('pgadmin.datagrid', [ parentData.server.label; return grid_title; }, - - initialize_data_grid: function(baseUrl, grid_title, sql_filter, server_type) { - var self = this; - self.grid_title = grid_title; - - /* Ajax call to initialize the edit grid, which creates - * an asynchronous connection and create appropriate query - * for the selected node. - */ - $.ajax({ - url: baseUrl, - method: 'POST', - dataType: 'json', - contentType: "application/json", - data: JSON.stringify(sql_filter), - success: function(res) { - - /* On successfully initialization find the dashboard panel, - * create new panel and add it to the dashboard panel. - */ - var url_params = { - 'trans_id': res.data.gridTransId, - 'is_query_tool': 'false', - 'editor_title': encodeURIComponent(self.grid_title) - }; - - var baseUrl = url_for('datagrid.panel', url_params) + - "?query_url=&server_type=" + encodeURIComponent(server_type); - var grid_title = gettext('Edit Data - ') + self.grid_title; - if (res.data.newBrowserTab) { - var newWin = window.open(baseUrl, '_blank'); - - // add a load listener to the window so that the title gets changed on page load - newWin.addEventListener("load", function() { - newWin.document.title = grid_title; - }); - } else { - var propertiesPanel = pgBrowser.docker.findPanels('properties'); - var dataGridPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, propertiesPanel[0]); - - // Set panel title and icon - dataGridPanel.title('<span title="'+grid_title+'">'+grid_title+'</span>'); - dataGridPanel.icon('fa fa-bolt'); - dataGridPanel.focus(); - - // Listen on the panel closed event. - dataGridPanel.on(wcDocker.EVENT.CLOSED, function() { - $.ajax({ - url: url_for('datagrid.close', {'trans_id': res.data.gridTransId}), - method: 'GET' - }); - }); - - var openDataGridURL = function(j) { - // add spinner element - $(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); - } - }, 100); - }; - - openDataGridURL(dataGridPanel); - } - }, - error: function(e) { - alertify.alert( - gettext('Error'), - gettext('Query Tool Initialization Error') - ); - } - }); - }, - // This is a callback function to show query tool when user click on menu item. show_query_tool: function(url, i, panel_title) { var self = this, @@ -454,9 +379,10 @@ define('pgadmin.datagrid', [ } var url_params = { - 'sid': parentData.server._id - }; - var url_endpoint = 'datagrid.initialize_query_tool' + 'sgid': parentData.server_group._id, + 'sid': parentData.server._id + }, + url_endpoint = 'datagrid.initialize_query_tool'; // If database not present then use Maintenance database // We will handle this at server side if (parentData.database) { @@ -465,87 +391,123 @@ define('pgadmin.datagrid', [ } var baseUrl = url_for(url_endpoint, url_params); + this.create_transaction(baseUrl, null, 'true', parentData.server.server_type, sURL, panel_title, ''); + }, + create_transaction: function(baseUrl, target, is_query_tool, server_type, sURL, panel_title, sql_filter) { + var self = this; + target = target || self; + $.ajax({ url: baseUrl, method: 'POST', dataType: 'json', + data: JSON.stringify(sql_filter), contentType: "application/json", success: function(res) { - var grid_title = self.get_panel_title(); - // Open the panel if frame is initialized - var url_params = { - 'trans_id': res.data.gridTransId, - 'is_query_tool': 'true', - 'editor_title': encodeURIComponent(grid_title) + res.data.is_query_tool = is_query_tool; + res.data.server_type = server_type; + res.data.sURL = sURL; + res.data.panel_title = panel_title; + target.trigger("pgadmin-datagrid:transaction:created", res.data); + }, + error: function(xhr, status, error) { + if (target !== self) { + if(xhr.status == 503 && xhr.responseJSON.info != undefined && + xhr.responseJSON.info == "CONNECTION_LOST") { + setTimeout(function() { + target.handle_connection_lost(true); + }); + return; + } } - var baseUrl = url_for('datagrid.panel', url_params) + - '?' + "query_url=" + encodeURI(sURL) + "&server_type=" + encodeURIComponent(parentData.server.server_type); - - // Create title for CREATE/DELETE scripts - if (panel_title) { - panel_title = - sqlEditorUtils.capitalizeFirstLetter(panel_title) + ' script'; - } - else { - panel_title = gettext('Query - ') + grid_title; + try { + var err = $.parseJSON(xhr.responseText); + alertify.alert(gettext("Query Tool Initialize Error"), + err.errormsg + ); + } catch (e) { + alertify.alert( + e.statusText, gettext("Query Tool Initialize Error") + ); } + } + }); + }, + launch_grid: function(trans_obj) { + var self = this, + panel_title = trans_obj.panel_title, + grid_title = self.get_panel_title(), + // Open the panel if frame is initialized + url_params = { + 'trans_id': trans_obj.gridTransId, + 'is_query_tool': trans_obj.is_query_tool, + 'editor_title': encodeURIComponent(grid_title) + }, + baseUrl = url_for('datagrid.panel', url_params) + + '?' + "query_url=" + encodeURI(trans_obj.sURL) + "&server_type=" + encodeURIComponent(trans_obj.server_type); + + if(trans_obj.is_query_tool == 'false') { + panel_title = gettext('Edit Data - ') + grid_title; + } else { + // Create title for CREATE/DELETE scripts + if (panel_title) { + panel_title = + sqlEditorUtils.capitalizeFirstLetter(panel_title) + ' script'; + } else { + panel_title = gettext('Query - ') + grid_title; + } + } - if (res.data.newBrowserTab) { - var newWin = window.open(baseUrl, '_blank'); - - // 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 propertiesPanel = pgBrowser.docker.findPanels('properties'); - var queryToolPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, propertiesPanel[0]); - - // Set panel title and icon - queryToolPanel.title('<span title="'+panel_title+'">'+panel_title+'</span>'); - queryToolPanel.icon('fa fa-bolt'); - queryToolPanel.focus(); - - // Listen on the panel closed event. - queryToolPanel.on(wcDocker.EVENT.CLOSED, function() { - $.ajax({ - url: url_for('datagrid.close', {'trans_id': res.data.gridTransId}), - method: 'GET' - }); - }); + if (trans_obj.newBrowserTab) { + var newWin = window.open(baseUrl, '_blank'); - var openQueryToolURL = function(j) { - // add spinner element - $(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); - } - }, 100); - }; + // 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 propertiesPanel = pgBrowser.docker.findPanels('properties'); + var queryToolPanel = pgBrowser.docker.addPanel('frm_datagrid', wcDocker.DOCK.STACKED, propertiesPanel[0]); + + // Set panel title and icon + queryToolPanel.title('<span title="'+panel_title+'">'+panel_title+'</span>'); + queryToolPanel.icon('fa fa-bolt'); + queryToolPanel.focus(); + + // Listen on the panel closed event. + queryToolPanel.on(wcDocker.EVENT.CLOSED, function() { + $.ajax({ + url: url_for('datagrid.close', {'trans_id': trans_obj.gridTransId}), + method: 'GET' + }); + }); - openQueryToolURL(queryToolPanel); - } - }, - error: function(e) { - alertify.alert( - gettext("Query Tool Initialize Error") - ); - } - }); + var openQueryToolURL = function(j) { + // add spinner element + $(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); + } + }, 100); + }; + + openQueryToolURL(queryToolPanel); + } } - }; + }, + Backbone.Events); return pgAdmin.DataGrid; }); diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html index ff4368d..427d51f 100644 --- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html +++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html @@ -337,38 +337,14 @@ loadingDiv.addClass('hide'); } }); - - // Fetch the SQL for Scripts (eg: CREATE/UPDATE/DELETE/SELECT) - var script_sql = ''; -{% if script_type_url%} - // Call AJAX only if script type url is present - $.ajax({ - url: '{{ script_type_url }}', - type:'GET', - async: false, - success: function(res) { - script_sql = res; - }, - error: function(jqx) { - var msg = jqx.responseText; - /* Error from the server */ - if (jqx.status == 410 || jqx.status == 500) { - try { - var data = $.parseJSON(jqx.responseText); - msg = data.errormsg; - } catch (e) {} - } - pgBrowser.report_error( - S('{{ _('Error fetching SQL for script: "%s"') }}') - .sprintf(msg) - .value(), msg - ); - } - }); -{% endif %} - + {% if script_type_url %} + var script_type_url = '{{ script_type_url }}'; + {% else %} + var script_type_url = ''; + {% endif %} // Start the query tool. sqlEditorController.start({{ is_query_tool }}, "{{ editor_title }}", - script_sql, {{ is_new_browser_tab }}, "{{ server_type }}", {{ prompt_save_changes }}); + script_type_url, {{ is_new_browser_tab }}, "{{ server_type }}", {{ prompt_save_changes }}, {{ url_params|safe}}); + }); {% endblock %} diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py index d2b5f2e..4215ea9 100644 --- a/web/pgadmin/tools/sqleditor/__init__.py +++ b/web/pgadmin/tools/sqleditor/__init__.py @@ -14,7 +14,8 @@ import pickle import random import codecs -from flask import Response, url_for, render_template, session, request +from flask import Response, url_for, render_template, session, request,\ + current_app from flask_babel import gettext from flask_security import login_required from pgadmin.tools.sqleditor.command import QueryToolCommand @@ -26,6 +27,7 @@ from pgadmin.utils.driver import get_driver from pgadmin.utils.sqlautocomplete.autocomplete import SQLAutoComplete from pgadmin.misc.file_manager import Filemanager from pgadmin.utils.menu import MenuItem +from pgadmin.utils.exception import ConnectionLost from config import PG_DEFAULT_DRIVER, ON_DEMAND_RECORD_COUNT @@ -77,7 +79,6 @@ class SqlEditorModule(PgAdminModule): 'when': None }] - def get_panels(self): return [] @@ -363,6 +364,12 @@ def check_transaction_status(trans_id): Returns: status and connection object """ + + if 'gridData' not in session: + return False, gettext( + 'Transaction ID not found in the session.' + ), None, None, None + grid_data = session['gridData'] # Return from the function if transaction id not found @@ -381,7 +388,10 @@ def check_transaction_status(trans_id): conn = manager.connection(did=trans_obj.did, conn_id=trans_obj.conn_id, use_binary_placeholder=True, array_to_string=True) + except ConnectionLost as e: + raise except Exception as e: + current_app.logger.error(e) return False, internal_server_error(errormsg=str(e)), None, None, None if conn.connected(): @@ -408,12 +418,23 @@ def start_view_data(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + # get the default connection as current connection which is attached to # trans id holds the cursor which has query result so we cannot use that # connection to execute another query otherwise we'll lose query result. - manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(trans_obj.sid) - default_conn = manager.connection(did=trans_obj.did) + try: + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(trans_obj.sid) + default_conn = manager.connection(did=trans_obj.did) + except ConnectionLost as e: + raise + except Exception as e: + current_app.logger.error(e) + return internal_server_error(errormsg=str(e)) # Connect to the Server if not connected. if not default_conn.connected(): @@ -435,7 +456,10 @@ def start_view_data(trans_id): pk_names, primary_keys = trans_obj.get_primary_keys(default_conn) # Fetch OIDs status - has_oids = trans_obj.has_oids(default_conn) + if trans_obj.object_type == 'table': + has_oids = trans_obj.has_oids(default_conn) + else: + has_oids = False # Fetch the applied filter. filter_applied = trans_obj.is_filter_applied() @@ -495,16 +519,20 @@ def start_query_tool(trans_id): else: sql = request.args or request.form + if 'gridData' not in session: + return make_json_response( + success=0, + errormsg=gettext('Transaction ID not found in the session.'), + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + grid_data = session['gridData'] # Return from the function if transaction id not found if str(trans_id) not in grid_data: return make_json_response( - data={ - 'status': False, 'result': gettext('Transaction ID not found in the session.'), - 'can_edit': False, 'can_filter': False - } - ) + success=0, + errormsg=gettext('Transaction ID not found in the session.'), + info='DATAGRID_TRANSACTION_REQUIRED', status=404) # Fetch the object for the specified transaction id. # Use pickle.loads function to get the command object @@ -529,13 +557,17 @@ def start_query_tool(trans_id): conn = manager.connection(did=trans_obj.did, conn_id=conn_id, use_binary_placeholder=True, array_to_string=True) + except ConnectionLost as e: + raise except Exception as e: + current_app.logger.error(e) return internal_server_error(errormsg=str(e)) # Connect to the Server if not connected. if not conn.connected(): status, msg = conn.connect() if not status: + current_app.logger.error(msg) return internal_server_error(errormsg=str(msg)) if conn.connected(): @@ -602,9 +634,16 @@ def preferences(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None \ and trans_obj is not None and session_obj is not None: - # Call the set_auto_commit and set_auto_rollback method of transaction object + # Call the set_auto_commit and set_auto_rollback method of + # transaction object trans_obj.set_auto_commit(blueprint.auto_commit.get()) trans_obj.set_auto_rollback(blueprint.auto_rollback.get()) @@ -668,6 +707,12 @@ def poll(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None and session_obj is not None: status, result = conn.poll(formatted_exception_msg=True, no_result=True) if not status: @@ -846,6 +891,12 @@ def fetch(trans_id, fetch_all=None): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None and session_obj is not None: status, result = conn.async_fetchmany_2darray(fetch_row_cnt) if not status: @@ -959,6 +1010,12 @@ def save(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None \ and trans_obj is not None and session_obj is not None: @@ -1017,6 +1074,11 @@ def get_filter(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) if status and conn is not None \ and trans_obj is not None and session_obj is not None: @@ -1047,6 +1109,12 @@ def apply_filter(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None \ and trans_obj is not None and session_obj is not None: @@ -1082,6 +1150,12 @@ def append_filter_inclusive(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None \ and trans_obj is not None and session_obj is not None: @@ -1128,6 +1202,11 @@ def append_filter_exclusive(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) if status and conn is not None \ and trans_obj is not None and session_obj is not None: @@ -1171,6 +1250,12 @@ def remove_filter(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None \ and trans_obj is not None and session_obj is not None: @@ -1208,6 +1293,12 @@ def set_limit(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None \ and trans_obj is not None and session_obj is not None: @@ -1240,15 +1331,20 @@ def cancel_transaction(trans_id): trans_id: unique transaction id """ + if 'gridData' not in session: + return make_json_response( + success=0, + errormsg=gettext('Transaction ID not found in the session.'), + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + grid_data = session['gridData'] # Return from the function if transaction id not found if str(trans_id) not in grid_data: return make_json_response( - data={ - 'status': False, 'result': gettext('Transaction ID not found in the session.') - } - ) + success=0, + errormsg=gettext('Transaction ID not found in the session.'), + info='DATAGRID_TRANSACTION_REQUIRED', status=404) # Fetch the object for the specified transaction id. # Use pickle.loads function to get the command object @@ -1310,6 +1406,12 @@ def get_object_name(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None \ and trans_obj is not None and session_obj is not None: @@ -1340,6 +1442,12 @@ def set_auto_commit(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None \ and trans_obj is not None and session_obj is not None: @@ -1381,6 +1489,12 @@ def set_auto_rollback(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None \ and trans_obj is not None and session_obj is not None: @@ -1429,6 +1543,12 @@ def auto_complete(trans_id): # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) + + if error_msg == gettext( + 'Transaction ID not found in the session.'): + return make_json_response(success=0, errormsg=error_msg, + info='DATAGRID_TRANSACTION_REQUIRED', status=404) + if status and conn is not None \ and trans_obj is not None and session_obj is not None: diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py index 70db0c9..919c4dc 100644 --- a/web/pgadmin/tools/sqleditor/command.py +++ b/web/pgadmin/tools/sqleditor/command.py @@ -103,7 +103,9 @@ class BaseCommand(object): **kwargs : N number of parameters """ - # Save the server id and database id, namespace id, object id + # Save the server group id, server id and database id, namespace id, + # object id + self.sgid = kwargs['sgid'] if 'sgid' in kwargs else None self.sid = kwargs['sid'] if 'sid' in kwargs else None self.did = kwargs['did'] if 'did' in kwargs else None diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js index dbc416e..2154962 100644 --- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js @@ -17,18 +17,19 @@ define('tools.querytool', [ 'react', 'react-dom', 'sources/sqleditor/keyboard_shortcuts', 'sources/sqleditor/query_tool_actions', - 'sources/../bundle/slickgrid', + 'pgadmin.datagrid', + 'sources/../bundle/slickgrid', 'pgadmin.file_manager', 'backgrid.sizeable.columns', 'slick.pgadmin.formatters', 'slick.pgadmin.editors', - 'pgadmin.browser' + 'pgadmin.browser', + 'pgadmin.tools.user_management' ], function( babelPollyfill,gettext, url_for, $, _, S, alertify, pgAdmin, Backbone, codemirror, pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent, XCellSelectionModel, setStagedRows, SqlEditorUtils, HistoryBundle, queryHistory, React, ReactDOM, - keyboardShortcuts -, queryToolActions) { + keyboardShortcuts, queryToolActions, Datagrid) { /* Return back, this has been called more than once */ if (pgAdmin.SqlEditor) return pgAdmin.SqlEditor; @@ -41,6 +42,11 @@ define('tools.querytool', [ var is_query_running = false; + var is_new_transaction_required = function(xhr) { + return xhr.status == 404 && xhr.responseJSON && + xhr.responseJSON.info && + xhr.responseJSON.info == 'DATAGRID_TRANSACTION_REQUIRED' + } // Defining Backbone view for the sql grid. var SQLEditorView = Backbone.View.extend({ initialize: function (opts) { @@ -398,6 +404,14 @@ define('tools.querytool', [ from: {line: self.current_line, ch: start}, to: {line: self.current_line, ch: end} }); + }, + error:function(e) { + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } } }); }.bind(ctx) @@ -1010,7 +1024,10 @@ define('tools.querytool', [ self.update_msg_history(false, gettext('Not connected to the server or the connection to the server has been closed.') ); - return; + } + + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + pgAdmin.Browser.UserManagement.pga_login(); } } }); @@ -1580,14 +1597,128 @@ define('tools.querytool', [ Backbone.Events, { initialize: function (container, opts) { + self= this; this.container = container; + if (!alertify.dlgGetServerPass) { + alertify.dialog('dlgGetServerPass', function factory() { + return { + main: function( + title, message + ) { + this.set('title', title); + this.setting('message',message); + }, + setup:function() { + return { + buttons:[ + { + text: gettext("OK"), key: 13, className: "btn btn-primary" + }, + { + text: gettext("Cancel"), className: "btn btn-danger" + } + ], + focus: { element: '#password', select: true }, + options: { + modal: 0, resizable: false, maximizable: false, pinnable: false + } + }; + }, + build:function() {}, + settings:{ + message: null + }, + prepare:function() { + this.setContent(this.setting('message')); + }, + callback: function(closeEvent) { + if (closeEvent.button.text == gettext("OK")) { + var passdata = $(this.elements.content).find('#frmPassword').serialize(); + self.init_connection(false, passdata); + } + } + }; + }); + } + this.on("pgadmin-datagrid:transaction:created", function(trans_obj) { + self.transId = trans_obj.gridTransId; + if (self.is_query_tool) { + alertify.success(gettext('New SQL editor session created.')); + } else { + alertify.success(gettext('New Data grid session created.')); + } + }); + pgBrowser.Events.on('pgadmin:user:logged-in', function() { + self.init_transaction(); + }); + }, + + init_transaction: function() { + var url_endpoint; + if (this.is_query_tool) { + url_endpoint = 'datagrid.initialize_query_tool'; + + // If database not present then use Maintenance database + // We will handle this at server side + if (this.url_params.did) { + url_endpoint = 'datagrid.initialize_query_tool_with_did'; + } + + } else { + url_endpoint = 'datagrid.initialize_datagrid'; + } + + var baseUrl = url_for(url_endpoint, this.url_params); + + Datagrid.create_transaction(baseUrl, this, this.is_query_tool, + this.server_type, '', '', ''); }, + handle_connection_lost: function(create_transaction) { + var self= this; + alertify.confirm( + gettext('Connection lost'), + gettext('Would you like to reconnect to the server?'), + function() { + self.init_connection(create_transaction); + }, function() {}); + }, + + init_connection: function(create_transaction, passdata) { + var self = this; + $.post(url_for('NODE-server.connect_id', {'gid': this.url_params.sgid, + 'sid': this.url_params.sid}), + passdata) + .done(function(res) { + if (res.success == 1) { + alertify.success(res.info); + if (create_transaction) { + self.init_transaction(); + } + } + }) + .fail(function(xhr, status, error) { + if (pgAdmin.Browser.UserManagement.is_pga_login_required(xhr)) { + pgAdmin.Browser.UserManagement.pga_login(); + } else { + if(xhr.responseJSON && + xhr.responseJSON.result) { + alertify.dlgGetServerPass(gettext('Connect to Server'), + xhr.responseJSON.result); + } else { + alertify.dlgGetServerPass(gettext('Connect to Server'), + xhr.responseText); + } + } + }); + }, /* This function is used to create instance of SQLEditorView, * call the render method of the grid view to render the backgrid * header and loading icon and start execution of the sql query. */ - start: function (is_query_tool, editor_title, script_sql, is_new_browser_tab, server_type, prompt_save_changes) { + start: function (is_query_tool, editor_title, script_type_url, + is_new_browser_tab, server_type, prompt_save_changes, url_params + ) { var self = this; self.is_query_tool = is_query_tool; @@ -1603,6 +1734,8 @@ define('tools.querytool', [ self.close_on_save = false; self.server_type = server_type; self.prompt_save_changes = prompt_save_changes; + self.url_params = url_params; + self.script_type_url = script_type_url; // We do not allow to call the start multiple times. if (self.gridView) @@ -1620,6 +1753,59 @@ define('tools.querytool', [ // Render the header self.gridView.render(); + if (self.is_query_tool) { + // Fetch the SQL for Scripts (eg: CREATE/UPDATE/DELETE/SELECT) + // Call AJAX only if script type url is present + if (script_type_url) { + $.ajax({ + url: script_type_url, + type:'GET', + success: function(res) { + self.gridView.query_tool_obj.refresh(); + if (res && res !== '') { + self.gridView.query_tool_obj.setValue(res); + } + self.init_events(); + }, + error: function(jqx) { + var msg = jqx.responseText; + self.init_events(); + + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + /* Error from the server */ + if (jqx.status == 410 || jqx.status == 500) { + try { + var data = $.parseJSON(jqx.responseText); + msg = data.errormsg; + } catch (e) {} + } + pgBrowser.report_error( + S(gettext("Error fetching SQL for script: %s.")).sprintf(msg).value() + ); + } + }); + } else { + self.init_events(); + } + } + else { + // Disable codemirror by setting cursor to nocursor and background to dark. + self.init_events(); + self.gridView.query_tool_obj.setOption("readOnly", 'nocursor'); + var cm = self.gridView.query_tool_obj.getWrapperElement(); + if (cm) { + cm.className += ' bg-gray-1 opacity-5'; + } + self.disable_tool_buttons(true); + self.execute_data_query(); + } + }, + + init_events: function() { + var self = this; // Listen to the file manager button events pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:select_file', self._select_file_handler, self); pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:create_file', self._save_file_handler, self); @@ -1653,23 +1839,6 @@ define('tools.querytool', [ // Indentation related self.on('pgadmin-sqleditor:indent_selected_code', self._indent_selected_code, self); self.on('pgadmin-sqleditor:unindent_selected_code', self._unindent_selected_code, self); - - if (self.is_query_tool) { - self.gridView.query_tool_obj.refresh(); - if (script_sql && script_sql !== '') { - self.gridView.query_tool_obj.setValue(script_sql); - } - } - else { - // Disable codemirror by setting cursor to nocursor and background to dark. - self.gridView.query_tool_obj.setOption("readOnly", 'nocursor'); - var cm = self.gridView.query_tool_obj.getWrapperElement(); - if (cm) { - cm.className += ' bg-gray-1 opacity-5'; - } - self.disable_tool_buttons(true); - self.execute_data_query(); - } }, // This function checks if there is any dirty data in the grid before @@ -1778,11 +1947,27 @@ define('tools.querytool', [ ); return; } + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + self.init_transaction(); + } var msg = e.responseText; - if (e.responseJSON != undefined && - e.responseJSON.errormsg != undefined) - msg = e.responseJSON.errormsg; + if (e.responseJSON != undefined) { + if(e.responseJSON.errormsg != undefined) { + msg = e.responseJSON.errormsg; + } + + if(e.status == 503 && e.responseJSON.info != undefined && + e.responseJSON.info == "CONNECTION_LOST") { + setTimeout(function() { + self.handle_connection_lost(); + }); + } + } self.update_msg_history(false, msg); } @@ -1886,6 +2071,10 @@ define('tools.querytool', [ return; } + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + var msg = e.responseText; if (e.responseJSON != undefined && e.responseJSON.errormsg != undefined) @@ -2512,6 +2701,10 @@ define('tools.querytool', [ return; } + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + pgAdmin.Browser.UserManagement.pga_login(); + } + var msg = e.responseText; if (e.responseJSON != undefined && e.responseJSON.errormsg != undefined) @@ -2653,9 +2846,13 @@ define('tools.querytool', [ self.is_query_changed = false; }, error: function(e) { + self.trigger('pgadmin-sqleditor:loading-icon:hide'); + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + var errmsg = $.parseJSON(e.responseText).errormsg; alertify.error(errmsg); - self.trigger('pgadmin-sqleditor:loading-icon:hide'); // hide cursor $busy_icon_div.removeClass('show_progress'); } @@ -2699,6 +2896,9 @@ define('tools.querytool', [ }, error: function (e) { self.trigger('pgadmin-sqleditor:loading-icon:hide'); + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } var errmsg = $.parseJSON(e.responseText).errormsg; setTimeout( @@ -2806,6 +3006,14 @@ define('tools.querytool', [ error: function (e) { self.trigger('pgadmin-sqleditor:loading-icon:hide'); + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } + var msg; if (e.readyState == 0) { msg = @@ -2874,6 +3082,14 @@ define('tools.querytool', [ }, error: function (e) { self.trigger('pgadmin-sqleditor:loading-icon:hide'); + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } + setTimeout( function () { if (e.readyState == 0) { @@ -2944,6 +3160,13 @@ define('tools.querytool', [ }, error: function (e) { self.trigger('pgadmin-sqleditor:loading-icon:hide'); + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } setTimeout( function () { @@ -2995,6 +3218,14 @@ define('tools.querytool', [ }, error: function (e) { self.trigger('pgadmin-sqleditor:loading-icon:hide'); + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } + setTimeout( function () { if (e.readyState == 0) { @@ -3050,6 +3281,14 @@ define('tools.querytool', [ }, error: function (e) { self.trigger('pgadmin-sqleditor:loading-icon:hide'); + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } + setTimeout( function () { if (e.readyState == 0) { @@ -3184,6 +3423,14 @@ define('tools.querytool', [ }, error: function (e) { self.trigger('pgadmin-sqleditor:loading-icon:hide'); + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } + setTimeout( function () { if (e.readyState == 0) { @@ -3306,11 +3553,26 @@ define('tools.querytool', [ return; } + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + self.init_transaction(); + } var msg = e.responseText; - if (e.responseJSON != undefined && - e.responseJSON.errormsg != undefined) - msg = e.responseJSON.errormsg; + if (e.responseJSON != undefined) { + if(e.responseJSON.errormsg != undefined) { + msg = e.responseJSON.errormsg; + } + if(e.status == 503 && e.responseJSON.info != undefined && + e.responseJSON.info == "CONNECTION_LOST") { + setTimeout(function() { + self.handle_connection_lost(); + }); + } + } self.update_msg_history(false, msg); } }); @@ -3404,6 +3666,10 @@ define('tools.querytool', [ return; } + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + var msg = e.responseText; if (e.responseJSON != undefined && e.responseJSON.errormsg != undefined) @@ -3453,6 +3719,10 @@ define('tools.querytool', [ return; } + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + var msg = e.responseText; if (e.responseJSON != undefined && e.responseJSON.errormsg != undefined) @@ -3474,7 +3744,7 @@ define('tools.querytool', [ auto_commit = false; } - // Make ajax call to change the limit + // Make ajax call to toggle auto commit $.ajax({ url: url_for('sqleditor.auto_commit', {'trans_id': self.transId}), method: 'POST', @@ -3492,6 +3762,14 @@ define('tools.querytool', [ return; } + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } + var msg = e.responseText; if (e.responseJSON != undefined && e.responseJSON.errormsg != undefined) @@ -3532,6 +3810,15 @@ define('tools.querytool', [ } }, error: function (e) { + + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } + alertify.alert(gettext('Explain options error'), gettext("Error occurred while setting verbose option in explain.") ); @@ -3570,6 +3857,14 @@ define('tools.querytool', [ } }, error: function (e) { + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } + alertify.alert(gettext('Explain options error'), gettext("Error occurred while setting costs option in explain.") ); @@ -3607,6 +3902,14 @@ define('tools.querytool', [ } }, error: function (e) { + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } + alertify.alert(gettext('Explain options error'), gettext("Error occurred while setting buffers option in explain.") ); @@ -3643,6 +3946,14 @@ define('tools.querytool', [ } }, error: function (e) { + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } + + if(is_new_transaction_required(e)) { + return self.init_transaction(); + } + alertify.alert(gettext('Explain options error'), gettext("Error occurred while setting timing option in explain.") ); @@ -3739,6 +4050,9 @@ define('tools.querytool', [ } }, error: function (e) { + if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) { + return pgAdmin.Browser.UserManagement.pga_login(); + } updateUI(); alertify.alert(gettext('Get Preferences error'), gettext("Error occurred while getting query tool options.") @@ -3748,6 +4062,8 @@ define('tools.querytool', [ }, close: function () { var self = this; + + pgBrowser.Events.off('pgadmin:user:logged-in', this.init_transaction); _.each(window.top.pgAdmin.Browser.docker.findPanels('frm_datagrid'), function (panel) { if (panel.isVisible()) { window.onbeforeunload = null; diff --git a/web/pgadmin/tools/user_management/__init__.py b/web/pgadmin/tools/user_management/__init__.py index 77c1b4e..2cf4dfb 100644 --- a/web/pgadmin/tools/user_management/__init__.py +++ b/web/pgadmin/tools/user_management/__init__.py @@ -13,7 +13,7 @@ import simplejson as json import re from flask import render_template, request, \ - url_for, Response, abort + url_for, Response, abort, current_app from flask_babel import gettext as _ from flask_security import login_required, roles_required, current_user from flask_security.utils import encrypt_password @@ -73,7 +73,7 @@ class UserManagementModule(PgAdminModule): 'user_management.roles', 'user_management.role', 'user_management.update_user', 'user_management.delete_user', 'user_management.create_user', 'user_management.users', - 'user_management.user' + 'user_management.user', current_app.login_manager.login_view ] diff --git a/web/pgadmin/tools/user_management/static/js/user_management.js b/web/pgadmin/tools/user_management/static/js/user_management.js index d51d6b4..a55594b 100644 --- a/web/pgadmin/tools/user_management/static/js/user_management.js +++ b/web/pgadmin/tools/user_management/static/js/user_management.js @@ -122,6 +122,95 @@ define([ alertify.ChangePassword(title, url).resizeTo('75%','70%'); }, + is_pga_login_required(xhr) { + return xhr.status == 401 && xhr.responseJSON && + xhr.responseJSON.info && + xhr.responseJSON.info == 'PGADMIN_LOGIN_REQUIRED' + }, + + // Callback to draw pgAdmin4 login dialog. + pga_login: function(url) { + var title = gettext('pgAdmin 4 login'); + url = url || url_for('security.login'); + if(!alertify.PgaLogin) { + alertify.dialog('PgaLogin' ,function factory() { + return { + main: function(title, url) { + this.set({ + 'title': title, + 'url': url + }); + }, + build: function() { + alertify.pgDialogBuild.apply(this) + }, + settings:{ + url: undefined + }, + setup:function() { + return { + buttons: [{ + text: gettext('Close'), key: 27, + className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button', + attrs:{name:'close', type:'button'} + }], + // Set options for dialog + options: { + //disable both padding and overflow control. + padding : !1, + overflow: !1, + modal: true, + resizable: true, + maximizable: true, + pinnable: false, + closableByDimmer: false, + closable: false + } + }; + }, + hooks: { + // Triggered when the dialog is closed + onclose: function() { + // Clear the view + return setTimeout((function() { + return alertify.PgaLogin().destroy(); + })); + } + }, + prepare: function() { + // create the iframe element + var self = this, + iframe = document.createElement('iframe'), + url = this.setting('url'); + + iframe.onload = function() { + var doc = this.contentDocument || this.contentWindow.document; + if (doc.location.href.indexOf(url) == -1) { + // login successful. + + this.contentWindow.stop(); + this.onload = null; + + // close the dialog. + self.close(); + pgBrowser.Events.trigger('pgadmin:user:logged-in'); + } + }; + + iframe.frameBorder = "no"; + iframe.width = "100%"; + iframe.height = "100%"; + iframe.src = url; + // add it to the dialog + self.elements.content.appendChild(iframe); + } + }; + }); + } + + alertify.PgaLogin(title, url).resizeTo('75%','70%'); + }, + // Callback to draw User Management Dialog. show_users: function(action, item, params) { if (!userInfo['is_admin']) return; diff --git a/web/pgadmin/utils/exception.py b/web/pgadmin/utils/exception.py index dab0d09..04a7d4b 100644 --- a/web/pgadmin/utils/exception.py +++ b/web/pgadmin/utils/exception.py @@ -28,7 +28,7 @@ class ConnectionLost(HTTPException): @property def name(self): - return HTTP_STATUS_CODES.get(505, 'Service Unavailable') + return HTTP_STATUS_CODES.get(503, 'Service Unavailable') def get_response(self, environ=None): return service_unavailable( diff --git a/web/pgadmin/utils/session.py b/web/pgadmin/utils/session.py index 8f2ae61..84e7471 100644 --- a/web/pgadmin/utils/session.py +++ b/web/pgadmin/utils/session.py @@ -24,6 +24,9 @@ import random import string import time from uuid import uuid4 +from flask import current_app, request, flash, redirect +from flask_login import login_url +from pgadmin.utils.ajax import make_json_response try: from cPickle import dump, load @@ -298,3 +301,32 @@ def create_session_interface(app, skip_paths=[]): 1000 ), skip_paths, datetime.timedelta(days=1)) + + +def pga_unauthorised(): + + lm = current_app.login_manager + login_message = None + + if lm.login_message: + if lm.localize_callback is not None: + login_message = lm.localize_callback(lm.login_message) + else: + login_message = lm.login_message + + if not lm.login_view or request.is_xhr: + # Only 401 is not enough to distinguish pgAdmin login is required. + # There are other cases when we return 401. For eg. wrong password + # supplied while connecting to server. + # So send additional 'info' message. + return make_json_response( + status=401, + success=0, + errormsg=login_message, + info='PGADMIN_LOGIN_REQUIRED' + ) + + if login_message: + flash(login_message, category=lm.login_message_category) + + return redirect(login_url(lm.login_view, request.url))