diff --git a/web/pgadmin/__init__.py b/web/pgadmin/__init__.py index a1dc33c..74a3124 100644 --- a/web/pgadmin/__init__.py +++ b/web/pgadmin/__init__.py @@ -26,7 +26,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 @@ -352,6 +352,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 89ae779..08d139e 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/static/js/sqleditor_utils.js b/web/pgadmin/static/js/sqleditor_utils.js index 6e2ae4e..d62a8e5 100644 --- a/web/pgadmin/static/js/sqleditor_utils.js +++ b/web/pgadmin/static/js/sqleditor_utils.js @@ -8,8 +8,8 @@ ////////////////////////////////////////////////////////////////////////// // This file contains common utilities functions used in sqleditor modules -define(['jquery', 'sources/gettext'], - function ($, gettext) { +define(['jquery', 'sources/gettext', 'sources/url_for'], + function ($, gettext, url_for) { var sqlEditorUtils = { /* Reference link http://stackoverflow.com/questions/105034/create-guid-uuid-in-javascript * Modified as per requirement. @@ -61,9 +61,13 @@ define(['jquery', 'sources/gettext'], previousStatus: null, // This function will fetch the connection status via ajax - fetchConnectionStatus: function(url, $el, $status_el) { + fetchConnectionStatus: function(target, $el, $status_el) { // If user has switch the browser Tab or Minimized window or // if wcDocker panel is not in focus then don't fire AJAX + var url = url_for('sqleditor.connection_status', { + 'trans_id': target.transId, + }); + if (document.visibilityState !== 'visible' || $el.data('panel-visible') !== 'visible' ) { return; @@ -169,19 +173,19 @@ define(['jquery', 'sources/gettext'], }, // This function will update the connection status - updateConnectionStatus: function(url, poll_time) { - var $el = $('.connection_status'), - $status_el = $($($el).find('.fa-custom')); + updateConnectionStatus: function(target, poll_time) { + var $el = $(target.gridView.$el.find('.connection_status')), + $status_el = $($el.find('.fa-custom')); // Apply popover on element $el.popover(); - // To set initial connection status - sqlEditorUtils.fetchConnectionStatus(url, $el, $status_el); + // To set initial connection statussource$el.popover() + sqlEditorUtils.fetchConnectionStatus(target, $el, $status_el); // Calling it again in specified interval setInterval( - sqlEditorUtils.fetchConnectionStatus.bind(null, url, $el, $status_el), + sqlEditorUtils.fetchConnectionStatus.bind(null, target, $el, $status_el), poll_time * 1000 ); }, diff --git a/web/pgadmin/tools/datagrid/__init__.py b/web/pgadmin/tools/datagrid/__init__.py index f16ecc6..a935bfa 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,8 @@ 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): @@ -93,13 +94,13 @@ def show_filter(): @blueprint.route( - '/initialize/datagrid/////' - '', + '/initialize/datagrid/////' + '/', 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 @@ -110,7 +111,7 @@ def initialize_datagrid(cmd_type, obj_type, sid, did, obj_id): 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 @@ -125,15 +126,27 @@ 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, + auto_reconnect=False, 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: @@ -143,11 +156,12 @@ def initialize_datagrid(cmd_type, obj_type, sid, did, obj_id): # Get the object as per the object type command_obj = ObjectRegistry.get_object( - obj_type, conn_id=conn_id, sid=sid, + 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 @@ -236,17 +250,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() - - display_connection_status = pref.preference('connection_status').get() - # Fetch the server details bgcolor = None fgcolor = None @@ -264,6 +267,27 @@ 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 + + display_connection_status = pref.preference('connection_status').get() + return render_template( "datagrid/index.html", _=gettext, @@ -281,50 +305,62 @@ def panel(trans_id, is_query_tool, editor_title): # 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', - display_connection_status=display_connection_status + display_connection_status=display_connection_status, + url_params=json.dumps(url_params) ) @blueprint.route( - '/initialize/query_tool//', + '/initialize/query_tool///', methods=["POST"], endpoint='initialize_query_tool_with_did' ) @blueprint.route( - '/initialize/query_tool/', + '/initialize/query_tool//', 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 """ + connect = True + if 'recreate' in request.args and \ + request.args['recreate'] == '1': + connect = False + # Create asynchronous connection using random connection id. + conn_id = str(random.randint(1, 9999999)) - 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.' - ) - ) + # Use Maintenance database OID + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) + if did is None: + did = manager.did try: command_obj = ObjectRegistry.get_object( - 'query_tool', sid=sid, did=did + 'query_tool', conn_id=conn_id, sgid=sgid, sid=sid, did=did ) except Exception as e: + app.logger.error(e) + return internal_server_error(errormsg=str(e)) + + try: + conn = manager.connection(did=did, conn_id=conn_id, + auto_reconnect=False, + use_binary_placeholder=True, + array_to_string=True) + if connect: + conn.connect() + except ConnectionLost as e: + raise + except Exception as e: + app.logger.error(e) return internal_server_error(errormsg=str(e)) # Create a unique id for the transaction @@ -366,6 +402,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 @@ -384,6 +422,7 @@ def close(trans_id): 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 @@ -424,6 +463,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 628fe0d..473f20a 100644 --- a/web/pgadmin/tools/datagrid/static/js/datagrid.js +++ b/web/pgadmin/tools/datagrid/static/js/datagrid.js @@ -1,9 +1,10 @@ define('pgadmin.datagrid', [ 'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'pgadmin.alertifyjs', 'sources/pgadmin', 'bundled_codemirror', - 'sources/sqleditor_utils', 'wcdocker', + 'sources/sqleditor_utils', 'backbone', 'wcdocker', ], function( - gettext, url_for, $, _, Alertify, pgAdmin, codemirror, sqlEditorUtils + 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. @@ -15,520 +16,489 @@ define('pgadmin.datagrid', [ if (pgAdmin.DataGrid) return pgAdmin.DataGrid; - pgAdmin.DataGrid = { - init: function() { - if (this.initialized) - return; - this.initialized = true; - this.title_index = 1; - - this.spinner_el = '
' + - '
' + - '
' + - '' + - '
' + - '
'; - // Define list of nodes on which view data option appears - var supported_nodes = [ - 'table', 'view', 'mview', - 'foreign_table', 'catalog_object', 'partition', - ], + pgAdmin.DataGrid = + _.extend( + { + init: function() { + if (this.initialized) + return; + this.initialized = true; + this.title_index = 1; + + this.spinner_el = '
'+ + '
'+ + '
'+ + ''+ + '
'+ + '
'; + // Define list of nodes on which view data option appears + var supported_nodes = [ + 'table', 'view', 'mview', + 'foreign_table', 'catalog_object', 'partition', + ], /* Enable/disable View data menu in tools based * on node selected. if selected node is present * in supported_nodes, menu will be enabled * otherwise disabled. */ - view_menu_enabled = function(obj) { - if (!_.isUndefined(obj) && !_.isNull(obj)) - return (_.indexOf(supported_nodes, obj._type) !== -1 ? true : false); - else - return false; - }, + view_menu_enabled = function(obj) { + if (!_.isUndefined(obj) && !_.isNull(obj)) + return (_.indexOf(supported_nodes, obj._type) !== -1 ? true : false); + else + return false; + }, /* Enable/disable Query tool menu in tools based * on node selected. if selected node is present * in unsupported_nodes, menu will be disabled * otherwise enabled. */ - query_tool_menu_enabled = function(obj) { - if (!_.isUndefined(obj) && !_.isNull(obj)) { - if (_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) { - if (obj._type == 'database' && obj.allowConn) - return true; - else if (obj._type != 'database') - return true; - else + query_tool_menu_enabled = function(obj) { + if (!_.isUndefined(obj) && !_.isNull(obj)) { + if (_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) { + if (obj._type == 'database' && obj.allowConn) + return true; + else if (obj._type != 'database') + return true; + else + return false; + } else { return false; + } } else { return false; } - } else { - return false; - } - }; + }; - // Define the nodes on which the menus to be appear - var menus = [{ - name: 'query_tool', - module: this, - applies: ['tools'], - callback: 'show_query_tool', - enable: query_tool_menu_enabled, - priority: 1, - label: gettext('Query Tool'), - icon: 'fa fa-bolt', - }]; - - // Create context menu - for (var idx = 0; idx < supported_nodes.length; idx++) { - menus.push({ - name: 'view_all_rows_context_' + supported_nodes[idx], - node: supported_nodes[idx], + // Define the nodes on which the menus to be appear + var menus = [{ + name: 'query_tool', module: this, - data: { - mnuid: 3, - }, - applies: ['context', 'object'], - callback: 'show_data_grid', - enable: view_menu_enabled, - category: 'view_data', - priority: 101, - label: gettext('All Rows'), - }, { - name: 'view_first_100_rows_context_' + supported_nodes[idx], - node: supported_nodes[idx], - module: this, - data: { - mnuid: 1, - }, - applies: ['context', 'object'], - callback: 'show_data_grid', - enable: view_menu_enabled, - category: 'view_data', - priority: 102, - label: gettext('First 100 Rows'), - }, { - name: 'view_last_100_rows_context_' + supported_nodes[idx], - node: supported_nodes[idx], - module: this, - data: { - mnuid: 2, - }, - applies: ['context', 'object'], - callback: 'show_data_grid', - enable: view_menu_enabled, - category: 'view_data', - priority: 103, - label: gettext('Last 100 Rows'), - }, { - name: 'view_filtered_rows_context_' + supported_nodes[idx], - node: supported_nodes[idx], - module: this, - data: { - mnuid: 4, - }, - applies: ['context', 'object'], - callback: 'show_filtered_row', - enable: view_menu_enabled, - category: 'view_data', - priority: 104, - label: gettext('Filtered Rows...'), - }); - } - - pgAdmin.Browser.add_menu_category('view_data', gettext('View/Edit Data'), 100, ''); - pgAdmin.Browser.add_menus(menus); - - // Creating a new pgAdmin.Browser frame to show the data. - var dataGridFrameType = new pgAdmin.Browser.Frame({ - name: 'frm_datagrid', - showTitle: true, - isCloseable: true, - isPrivate: true, - url: 'about:blank', - }); - - // Load the newly created frame - dataGridFrameType.load(pgBrowser.docker); - }, - - // This is a callback function to show data when user click on menu item. - show_data_grid: function(data, i) { - var self = this, - d = pgAdmin.Browser.tree.itemData(i); - if (d === undefined) { - Alertify.alert( - gettext('Data Grid Error'), - gettext('No object selected.') - ); - return; - } - - // Get the parent data from the tree node hierarchy. - var node = pgBrowser.Nodes[d._type], - parentData = node.getTreeNodeHierarchy(i); - - // If server, database or schema is undefined then return from the function. - if (parentData.server === undefined || parentData.database === undefined) { - return; - } - // If schema, view, catalog object all are undefined then return from the function. - if (parentData.schema === undefined && parentData.view === undefined && - parentData.catalog === undefined) { - return; - } - - var nsp_name = ''; - - if (parentData.schema != undefined) { - nsp_name = parentData.schema.label; - } else if (parentData.view != undefined) { - nsp_name = parentData.view.label; - } else if (parentData.catalog != undefined) { - nsp_name = parentData.catalog.label; - } - var url_params = { - 'cmd_type': data.mnuid, - 'obj_type': d._type, - 'sid': parentData.server._id, - 'did': parentData.database._id, - 'obj_id': d._id, - }; - - var baseUrl = url_for('datagrid.initialize_datagrid', url_params); - 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); - }, - - // This is a callback function to show filtered data when user click on menu item. - show_filtered_row: function(data, i) { - var self = this, - d = pgAdmin.Browser.tree.itemData(i); - if (d === undefined) { - Alertify.alert( - gettext('Data Grid Error'), - gettext('No object selected.') - ); - return; - } - - // Get the parent data from the tree node hierarchy. - var node = pgBrowser.Nodes[d._type], - parentData = node.getTreeNodeHierarchy(i); - - // If server or database is undefined then return from the function. - if (parentData.server === undefined || parentData.database === undefined) { - return; - } - - // If schema, view, catalog object all are undefined then return from the function. - if (parentData.schema === undefined && parentData.view === undefined && - parentData.catalog === undefined) { - return; - } - - var nsp_name = ''; - - if (parentData.schema != undefined) { - nsp_name = parentData.schema.label; - } else if (parentData.view != undefined) { - nsp_name = parentData.view.label; - } else if (parentData.catalog != undefined) { - nsp_name = parentData.catalog.label; - } - - var url_params = { - 'cmd_type': data.mnuid, - 'obj_type': d._type, - 'sid': parentData.server._id, - 'did': parentData.database._id, - 'obj_id': d._id, - - }; - - var baseUrl = url_for('datagrid.initialize_datagrid', url_params); - - // Create url to validate the SQL filter - var validateUrl = url_for('datagrid.filter_validate', { - 'sid': url_params['sid'], - 'did': url_params['did'], - 'obj_id': url_params['obj_id'], - }); - var grid_title = parentData.server.label + '-' + parentData.database.label + '-' + - nsp_name + '.' + d.label; - - // Create filter dialog using alertify - if (!Alertify.filterDialog) { - Alertify.dialog('filterDialog', function factory() { - return { - main: function(title, message, baseUrl, validateUrl) { - this.set('title', title); - this.message = message; - this.baseUrl = baseUrl; - this.validateUrl = validateUrl; + applies: ['tools'], + callback: 'show_query_tool', + enable: query_tool_menu_enabled, + priority: 1, + label: gettext('Query Tool'), + icon: 'fa fa-bolt', + }]; + + // Create context menu + for (var idx = 0; idx < supported_nodes.length; idx++) { + menus.push({ + name: 'view_all_rows_context_' + supported_nodes[idx], + node: supported_nodes[idx], + module: this, + data: { + mnuid: 3, }, - - setup: function() { - return { - buttons: [{ - text: gettext('OK'), - className: 'btn btn-primary', - }, - { - text: gettext('Cancel'), - className: 'btn btn-danger', - }, - ], - options: { - modal: 0, - resizable: true, - maximizable: false, - pinnable: false, - autoReset: false, - }, - }; + applies: ['context', 'object'], + callback: 'show_data_grid', + enable: view_menu_enabled, + category: 'view_data', + priority: 101, + label: gettext('All Rows'), + }, { + name: 'view_first_100_rows_context_' + supported_nodes[idx], + node: supported_nodes[idx], + module: this, + data: { + mnuid: 1, + }, + applies: ['context', 'object'], + callback: 'show_data_grid', + enable: view_menu_enabled, + category: 'view_data', + priority: 102, + label: gettext('First 100 Rows'), + }, { + name: 'view_last_100_rows_context_' + supported_nodes[idx], + node: supported_nodes[idx], + module: this, + data: { + mnuid: 2, }, - build: function() { - Alertify.pgDialogBuild.apply(this); + applies: ['context', 'object'], + callback: 'show_data_grid', + enable: view_menu_enabled, + category: 'view_data', + priority: 103, + label: gettext('Last 100 Rows'), + }, { + name: 'view_filtered_rows_context_' + supported_nodes[idx], + node: supported_nodes[idx], + module: this, + data: { + mnuid: 4, }, - prepare: function() { - var self = this, - $content = $(this.message), - $sql_filter = $content.find('#sql_filter'); + applies: ['context', 'object'], + callback: 'show_filtered_row', + enable: view_menu_enabled, + category: 'view_data', + priority: 104, + label: gettext('Filtered Rows...'), + }); + } - $(this.elements.body.childNodes[0]).addClass( - 'dataview_filter_dialog' - ); + pgAdmin.Browser.add_menu_category('view_data', gettext('View/Edit Data'), 100, ''); + pgAdmin.Browser.add_menus(menus); - this.setContent($content.get(0)); - - // Apply CodeMirror to filter text area. - this.filter_obj = CodeMirror.fromTextArea($sql_filter.get(0), { - tabindex: 0, - lineNumbers: true, - mode: 'text/x-pgsql', - extraKeys: pgBrowser.editor_shortcut_keys, - indentWithTabs: pgAdmin.Browser.editor_options.indent_with_tabs, - indentUnit: pgAdmin.Browser.editor_options.tabSize, - tabSize: pgBrowser.editor_options.tabSize, - lineWrapping: pgAdmin.Browser.editor_options.wrapCode, - autoCloseBrackets: pgAdmin.Browser.editor_options.insert_pair_brackets, - matchBrackets: pgAdmin.Browser.editor_options.brace_matching, - }); - - setTimeout(function() { - // Set focus on editor - self.filter_obj.focus(); - }, 500); - }, + // Creating a new pgAdmin.Browser frame to show the data. + var dataGridFrameType = new pgAdmin.Browser.Frame({ + name: 'frm_datagrid', + showTitle: true, + isCloseable: true, + isPrivate: true, + url: 'about:blank', + }); - callback: function(closeEvent) { - - if (closeEvent.button.text == gettext('OK')) { - var sql = this.filter_obj.getValue(); - var that = this; - - // Make ajax call to include the filter by selection - $.ajax({ - url: that.validateUrl, - method: 'POST', - async: false, - contentType: 'application/json', - data: JSON.stringify(sql), - success: function(res) { - if (res.data.status) { - // Initialize the data grid. - self.initialize_data_grid(that.baseUrl, grid_title, sql, parentData.server.server_type); - } else { - Alertify.alert( - gettext('Validation Error'), - res.data.result - ); - } - }, - error: function(e) { - Alertify.alert( - gettext('Validation Error'), - e - ); - }, - }); - } - }, - }; + // 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. + show_data_grid: function(data, i) { + var self = this, + d = pgAdmin.Browser.tree.itemData(i); + if (d === undefined) { + alertify.alert( + gettext('Data Grid Error'), + gettext('No object selected.') + ); + return; + } - $.get(url_for('datagrid.filter'), - function(data) { - Alertify.filterDialog('Data Filter', data, baseUrl, validateUrl) - .resizeTo(300, 200); + // Get the parent data from the tree node hierarchy. + var node = pgBrowser.Nodes[d._type], + parentData = node.getTreeNodeHierarchy(i); + + // If server, database or schema is undefined then return from the function. + if (parentData.server === undefined || parentData.database === undefined) { + return; + } + // If schema, view, catalog object all are undefined then return from the function. + if (parentData.schema === undefined && parentData.view === undefined && + parentData.catalog === undefined) { + return; } - ); - }, - get_panel_title: function() { - // Get the parent data from the tree node hierarchy. - var tree = pgAdmin.Browser.tree, - selected_item = tree.selected(), - item_data = tree.itemData(selected_item), - node = pgBrowser.Nodes[item_data._type], - parentData = node.getTreeNodeHierarchy(selected_item); - - // If server, database is undefined then return from the function. - if (parentData.server === undefined) { - return; - } - // If Database is not available then use default db - var db_label = parentData.database ? parentData.database.label : - parentData.server.db; - - var grid_title = db_label + ' on ' + parentData.server.user.name + '@' + - parentData.server.label; - return grid_title; - }, + var nsp_name = ''; + + if (parentData.schema != undefined) { + nsp_name = parentData.schema.label; + } + else if (parentData.view != undefined) { + nsp_name = parentData.view.label; + } + else if (parentData.catalog != undefined) { + nsp_name = parentData.catalog.label; + } + 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, + }; - 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) { + var baseUrl = url_for('datagrid.initialize_datagrid', url_params); + var grid_title = parentData.server.label + ' - ' + parentData.database.label + ' - ' + + nsp_name + '.' + d.label; + + 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. + show_filtered_row: function(data, i) { + var self = this, + d = pgAdmin.Browser.tree.itemData(i); + if (d === undefined) { + alertify.alert( + gettext('Data Grid Error'), + gettext('No object selected.') + ); + return; + } - /* 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), - }; + // Get the parent data from the tree node hierarchy. + var node = pgBrowser.Nodes[d._type], + parentData = node.getTreeNodeHierarchy(i); - 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'); + // If server or database is undefined then return from the function. + if (parentData.server === undefined || parentData.database === undefined) { + return; + } - // 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('' + grid_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.close', { - 'trans_id': res.data.gridTransId, - }), - method: 'GET', - }); - }); + // If schema, view, catalog object all are undefined then return from the function. + if (parentData.schema === undefined && parentData.view === undefined && + parentData.catalog === undefined) { + return; + } + + var nsp_name = ''; + + if (parentData.schema != undefined) { + nsp_name = parentData.schema.label; + } + else if (parentData.view != undefined) { + nsp_name = parentData.view.label; + } + else if (parentData.catalog != undefined) { + nsp_name = parentData.catalog.label; + } + + 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); + + // Create url to validate the SQL filter + var validateUrl = url_for('datagrid.filter_validate', { + 'sid': url_params['sid'], + 'did': url_params['did'], + 'obj_id': url_params['obj_id'], + }); + var grid_title = parentData.server.label + '-' + parentData.database.label + '-' + + nsp_name + '.' + d.label; + + // Create filter dialog using alertify + if (!alertify.filterDialog) { + alertify.dialog('filterDialog', function factory() { + return { + main: function(title, message, baseUrl, validateUrl) { + this.set('title', title); + this.message = message; + this.baseUrl = baseUrl; + this.validateUrl = validateUrl; + }, + + setup:function() { + return { + buttons:[{ + text: gettext('OK'), + className: 'btn btn-primary', + }, + { + text: gettext('Cancel'), + className: 'btn btn-danger', + }, + ], + options: { + modal: 0, + resizable: true, + maximizable: false, + pinnable: false, + autoReset: false, + }, + }; + }, + build: function() { + alertify.pgDialogBuild.apply(this); + }, + prepare:function() { + var self = this, + $content = $(this.message), + $sql_filter = $content.find('#sql_filter'); + + $(this.elements.body.childNodes[0]).addClass( + 'dataview_filter_dialog' + ); + + this.setContent($content.get(0)); + + // Apply CodeMirror to filter text area. + this.filter_obj = CodeMirror.fromTextArea($sql_filter.get(0), { + lineNumbers: true, + mode: 'text/x-pgsql', + extraKeys: pgBrowser.editor_shortcut_keys, + indentWithTabs: pgAdmin.Browser.editor_options.indent_with_tabs, + indentUnit: pgAdmin.Browser.editor_options.tabSize, + tabSize: pgBrowser.editor_options.tabSize, + lineWrapping: pgAdmin.Browser.editor_options.wrapCode, + autoCloseBrackets: pgAdmin.Browser.editor_options.insert_pair_brackets, + matchBrackets: pgAdmin.Browser.editor_options.brace_matching, + }); - 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); + setTimeout(function() { + // Set focus on editor + self.filter_obj.focus(); + }, 500); + }, + + callback: function(closeEvent) { + + if (closeEvent.button.text == gettext('OK')) { + var sql = this.filter_obj.getValue(); + var that = this; + + // Make ajax call to include the filter by selection + $.ajax({ + url: that.validateUrl, + method: 'POST', + async: false, + contentType: 'application/json', + data: JSON.stringify(sql), + success: function(res) { + if (res.data.status) { + // Initialize the data grid. + self.create_transaction(that.baseUrl, null, 'false', parentData.server.server_type, '', grid_title, sql, false); + } + else { + alertify.alert( + gettext('Validation Error'), + res.data.result + ); + } + }, + error: function(e) { + alertify.alert( + gettext('Validation Error'), + e + ); + }, + }); } - }, 100); + }, }; + }); + } - openDataGridURL(dataGridPanel); + $.get(url_for('datagrid.filter'), + function(data) { + alertify.filterDialog('Data Filter', data, baseUrl, validateUrl) + .resizeTo(300, 200); } - }, - error: function() { - Alertify.alert( - gettext('Error'), - gettext('Query Tool Initialization Error') + ); + }, + + get_panel_title: function() { + // Get the parent data from the tree node hierarchy. + var tree = pgAdmin.Browser.tree, + selected_item = tree.selected(), + item_data = tree.itemData(selected_item); + + var node = pgBrowser.Nodes[item_data._type], + parentData = node.getTreeNodeHierarchy(selected_item); + + // If server, database is undefined then return from the function. + if (parentData.server === undefined) { + return; + } + // If Database is not available then use default db + var db_label = parentData.database ? parentData.database.label + : parentData.server.db; + + var grid_title = db_label + ' on ' + parentData.server.user.name + '@' + + parentData.server.label; + return grid_title; + }, + // This is a callback function to show query tool when user click on menu item. + show_query_tool: function(url, i, panel_title) { + var sURL = url || '', + d = pgAdmin.Browser.tree.itemData(i); + + panel_title = panel_title || ''; + if (d === undefined) { + alertify.alert( + gettext('Query Tool Error'), + gettext('No object selected.') ); - }, - }); - }, + return; + } - // 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, - sURL = url || '', - d = pgAdmin.Browser.tree.itemData(i); + // Get the parent data from the tree node hierarchy. + var node = pgBrowser.Nodes[d._type], + parentData = node.getTreeNodeHierarchy(i); - panel_title = panel_title || ''; + // If server, database is undefined then return from the function. + if (parentData.server === undefined) { + return; + } - if (d === undefined) { - Alertify.alert( - gettext('Query Tool Error'), - gettext('No object selected.') - ); - return; - } - - // Get the parent data from the tree node hierarchy. - var node = pgBrowser.Nodes[d._type], - parentData = node.getTreeNodeHierarchy(i); - - // If server, database is undefined then return from the function. - if (parentData.server === undefined) { - return; - } - - var url_params = { - 'sid': parentData.server._id, - }; - var url_endpoint = 'datagrid.initialize_query_tool'; - // If database not present then use Maintenance database - // We will handle this at server side - if (parentData.database) { - url_params['did'] = parentData.database._id; - url_endpoint = 'datagrid.initialize_query_tool_with_did'; - } - var baseUrl = url_for(url_endpoint, url_params); - - $.ajax({ - url: baseUrl, - method: 'POST', - dataType: 'json', - contentType: 'application/json', - success: function(res) { - var grid_title = self.get_panel_title(); + var url_params = { + '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) { + url_params['did'] = parentData.database._id; + url_endpoint = 'datagrid.initialize_query_tool_with_did'; + } + var baseUrl = url_for(url_endpoint, url_params); + + this.create_transaction(baseUrl, null, 'true', parentData.server.server_type, sURL, panel_title, '', false); + }, + create_transaction: function(baseUrl, target, is_query_tool, server_type, sURL, panel_title, sql_filter, recreate) { + var self = this; + target = target || self; + if (recreate) { + baseUrl += '?recreate=1'; + } + $.ajax({ + url: baseUrl, + method: 'POST', + dataType: 'json', + data: JSON.stringify(sql_filter), + contentType: 'application/json', + success: function(res) { + 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) { + if (target !== self) { + if(xhr.status == 503 && xhr.responseJSON.info != undefined && + xhr.responseJSON.info == 'CONNECTION_LOST') { + setTimeout(function() { + target.handle_connection_lost(true, xhr); + }); + return; + } + } + + 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 - var url_params = { - 'trans_id': res.data.gridTransId, - 'is_query_tool': 'true', + url_params = { + 'trans_id': trans_obj.gridTransId, + 'is_query_tool': trans_obj.is_query_tool, 'editor_title': encodeURIComponent(grid_title), - }; - - var baseUrl = url_for('datagrid.panel', url_params) + - '?' + 'query_url=' + encodeURI(sURL) + '&server_type=' + encodeURIComponent(parentData.server.server_type); + }, + 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 = @@ -536,64 +506,57 @@ define('pgadmin.datagrid', [ } else { panel_title = gettext('Query - ') + grid_title; } + } - if (res.data.newBrowserTab) { - var newWin = window.open(baseUrl, '_blank'); + if (trans_obj.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('' + 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.close', { - 'trans_id': res.data.gridTransId, - }), - method: 'GET', - }); + // 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(''+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.close', {'trans_id': trans_obj.gridTransId}), + method: 'GET', }); - - 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); + }); + + 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); } - }, 100); - }; + } else { + openQueryToolURL(j); + } + }, 100); + }; - openQueryToolURL(queryToolPanel); - } - }, - error: function() { - Alertify.alert( - gettext('Query Tool Initialize Error') - ); - }, - }); + 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 ee62ed1..73c8ba0 100644 --- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html +++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html @@ -24,7 +24,7 @@ -
+