From 8120b380bbbc2bb4635f29da44d1b0d7f43320ae Mon Sep 17 00:00:00 2001 From: Ashesh Vashi Date: Tue, 19 Jan 2016 18:01:14 +0530 Subject: [PATCH] Introduced a event manager for the browser, which will generate certain known events, when any activity happens on the browser layout. The following types of events will be triggered through the browser event manager. - pgadmin-browser:frame:* [1] - pgadmin-browser:frame-:* [1] - pgadmin-browser:panel:* [1] - pgadmin-browser:panel-:* [1] - pgadmin-browser:panel - pgadmin-browser:frame - pgadmin-browser:tree - pgadmin-browser:tree:* [2] [1] The '*' denotes some of the events generated by the wcDocker, which can be useful to do some operations. These events are: + wcDocker.EVENT.UPDATED + wcDocker.EVENT.VISIBILITY_CHANGED + wcDocker.EVENT.BEGIN_DOCK + wcDocker.EVENT.END_DOCK + wcDocker.EVENT.GAIN_FOCUS, + wcDocker.EVENT.LOST_FOCUS + wcDocker.EVENT.CLOSED + wcDocker.EVENT.BUTTON + wcDocker.EVENT.ATTACHED + wcDocker.EVENT.DETACHED + wcDocker.EVENT.MOVE_STARTED + wcDocker.EVENT.MOVE_ENDED + wcDocker.EVENT.MOVED + wcDocker.EVENT.RESIZE_STARTED + wcDocker.EVENT.RESIZE_ENDED + wcDocker.EVENT.RESIZED + wcDocker.EVENT.SCROLLED [2] The '*' denotes all the events generated by the Browser Tree (aciTree). The extension modules can utilize these events to do some operations on nodes, and panels. This patch includes showing 'Reversed Engineered Query' for the selected node (if allowed) using the above approch. The ShowNodeSQL module looks for two events. 1. SQL Panel Visibility change. - Based on the visibility of that panel, we start/stop listening the node selection event. 2. Node Selection in Browser tree - Based on the selected node type, it will look for 'hasSQL' parameter of the node, and fetch the Query from the server, and show it in the SQL editor. --- web/pgadmin/browser/static/js/datamodel.js | 4 +- web/pgadmin/browser/static/js/frame.js | 65 +++++++++++--- web/pgadmin/browser/static/js/panel.js | 32 ++++++- .../browser/templates/browser/js/browser.js | 58 +++++-------- .../browser/templates/browser/js/node.js | 2 + web/pgadmin/misc/sql/__init__.py | 29 +++++++ web/pgadmin/misc/sql/static/js/sql.js | 85 +++++++++++++++++++ 7 files changed, 222 insertions(+), 53 deletions(-) create mode 100644 web/pgadmin/misc/sql/__init__.py create mode 100644 web/pgadmin/misc/sql/static/js/sql.js diff --git a/web/pgadmin/browser/static/js/datamodel.js b/web/pgadmin/browser/static/js/datamodel.js index bf6b7eca0..5567cf341 100644 --- a/web/pgadmin/browser/static/js/datamodel.js +++ b/web/pgadmin/browser/static/js/datamodel.js @@ -884,7 +884,9 @@ function(_, pgAdmin, $, Backbone) { return true; } - }) + }); + + pgBrowser.Events = _.extend({}, Backbone.Events); return pgBrowser; }); diff --git a/web/pgadmin/browser/static/js/frame.js b/web/pgadmin/browser/static/js/frame.js index d5b7611f5..286edc851 100644 --- a/web/pgadmin/browser/static/js/frame.js +++ b/web/pgadmin/browser/static/js/frame.js @@ -2,7 +2,8 @@ define( ['underscore', 'pgadmin', 'wcdocker'], function(_, pgAdmin) { - pgAdmin.Browser = pgAdmin.Browser || {}; + var pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {}; + pgAdmin.Browser.Frame = function(options) { var defaults = [ 'name', 'title', 'width', 'height', 'showTitle', 'isCloseable', @@ -25,23 +26,61 @@ function(_, pgAdmin) { var that = this; if (!that.panel) { docker.registerPanelType(this.name, { - title: that.title, - isPrivate: that.isPrivate, - onCreate: function(myPanel) { - myPanel.initSize(that.width, that.height); - if (myPanel.showTitle == false) - myPanle.title(false); - myPanel.closeable(!!that.isCloseable); + title: that.title, + isPrivate: that.isPrivate, + onCreate: function(myPanel) { + $(myPanel).data('pgAdminName', that.name); + myPanel.initSize(that.width, that.height); + if (myPanel.showTitle == false) + myPanle.title(false); + myPanel.closeable(!!that.isCloseable); - var $frameArea = $('
'); - myPanel.layout().addItem($frameArea); - that.panel = myPanel; - that.frame = new wcIFrame($frameArea, myPanel); + var $frameArea = $('
'); + myPanel.layout().addItem($frameArea); + that.panel = myPanel; + var frame = new wcIFrame($frameArea, myPanel); + $(myPanel).data('embededFrame', that.frame); - setTimeout(function() { that.frame.openURL(that.url); }, 500); + setTimeout(function() { frame.openURL(that.url); }, 500); + + if (that.events && _.isObject(that.events)) { + _.each(that.events, function(v, k) { + if (v && _.isFunction(v)) { + myPanel.on(k, v); + } + }); + } + + _.each([ + wcDocker.EVENT.UPDATED, wcDocker.EVENT.VISIBILITY_CHANGED, + wcDocker.EVENT.BEGIN_DOCK, wcDocker.EVENT.END_DOCK, + wcDocker.EVENT.GAIN_FOCUS, wcDocker.EVENT.LOST_FOCUS, + wcDocker.EVENT.CLOSED, wcDocker.EVENT.BUTTON, + wcDocker.EVENT.ATTACHED, wcDocker.EVENT.DETACHED, + wcDocker.EVENT.MOVE_STARTED, wcDocker.EVENT.MOVE_ENDED, + wcDocker.EVENT.MOVED, wcDocker.EVENT.RESIZE_STARTED, + wcDocker.EVENT.RESIZE_ENDED, wcDocker.EVENT.RESIZED, + wcDocker.EVENT.SCROLLED], function(ev) { + myPanel.on(ev, that.eventFunc.bind(myPanel, ev)); + }); } }); } + }, + eventFunc: function(eventName) { + var name = $(this).data('pgAdminName'); + + try { + pgBrowser.Events.trigger('pgadmin-browser:frame', eventName, this, arguments); + pgBrowser.Events.trigger('pgadmin-browser:frame:' + eventName, this, arguments); + + if (name) { + pgBrowser.Events.trigger('pgadmin-browser:frame-' + name, eventName, this, arguments); + pgBrowser.Events.trigger('pgadmin-browser:frame-' + name + ':' + eventName, this, arguments); + } + } catch (e) { + console.log(e); + } } }); diff --git a/web/pgadmin/browser/static/js/panel.js b/web/pgadmin/browser/static/js/panel.js index 13a3b39f2..5324f5996 100644 --- a/web/pgadmin/browser/static/js/panel.js +++ b/web/pgadmin/browser/static/js/panel.js @@ -1,7 +1,9 @@ define( ['underscore', 'pgadmin', 'wcdocker'], function(_, pgAdmin) { - pgAdmin.Browser = pgAdmin.Browser || {}; + + var pgBrowser = pgAdmin.Browser = pgAdmin.Browser || {}; + pgAdmin.Browser.Panel = function(options) { var defaults = [ 'name', 'title', 'width', 'height', 'showTitle', 'isCloseable', @@ -26,6 +28,7 @@ function(_, pgAdmin) { title: that.title, isPrivate: that.isPrivate, onCreate: function(myPanel) { + $(myPanel).data('pgAdminName', that.name); myPanel.initSize(that.width, that.height); if (!that.showTitle) myPanel.title(false); @@ -46,9 +49,36 @@ function(_, pgAdmin) { } }); } + _.each([ + wcDocker.EVENT.UPDATED, wcDocker.EVENT.VISIBILITY_CHANGED, + wcDocker.EVENT.BEGIN_DOCK, wcDocker.EVENT.END_DOCK, + wcDocker.EVENT.GAIN_FOCUS, wcDocker.EVENT.LOST_FOCUS, + wcDocker.EVENT.CLOSED, wcDocker.EVENT.BUTTON, + wcDocker.EVENT.ATTACHED, wcDocker.EVENT.DETACHED, + wcDocker.EVENT.MOVE_STARTED, wcDocker.EVENT.MOVE_ENDED, + wcDocker.EVENT.MOVED, wcDocker.EVENT.RESIZE_STARTED, + wcDocker.EVENT.RESIZE_ENDED, wcDocker.EVENT.RESIZED, + wcDocker.EVENT.SCROLLED], function(ev) { + myPanel.on(ev, that.eventFunc.bind(myPanel, ev)); + }); } }); } + }, + eventFunc: function(eventName) { + var name = $(this).data('pgAdminName'); + + try { + pgBrowser.Events.trigger('pgadmin-browser:panel', eventName, this, arguments); + pgBrowser.Events.trigger('pgadmin-browser:panel:' + eventName, this, arguments); + + if (name) { + pgBrowser.Events.trigger('pgadmin-browser:panel-' + name, eventName, this, arguments); + pgBrowser.Events.trigger('pgadmin-browser:panel-' + name + ':' + eventName, this, arguments); + } + } catch (e) { + console.log(e); + } } }); diff --git a/web/pgadmin/browser/templates/browser/js/browser.js b/web/pgadmin/browser/templates/browser/js/browser.js index 6de28e6e8..ccf3217f8 100644 --- a/web/pgadmin/browser/templates/browser/js/browser.js +++ b/web/pgadmin/browser/templates/browser/js/browser.js @@ -16,34 +16,8 @@ function(require, $, _, S, Bootstrap, pgAdmin, alertify, CodeMirror) { pgAdmin.Browser = pgAdmin.Browser || {}; - // TODO:: Remove dmeo SQL (once completed) - var demoSql = '-- DROP TABLE tickets_detail; \n\ -\n\ -CREATE TABLE tickets_detail \n\ -( \n\ - id serial NOT NULL, \n\ - ticket_id integer NOT NULL, \n\ - logger_id integer NOT NULL, \n\ - added timestamp with time zone NOT NULL, \n\ - detail text NOT NULL, \n\ - msgid character varying(100), \n\ - CONSTRAINT tickets_detail_pkey PRIMARY KEY (id), \n\ - CONSTRAINT ticket_id_refs_id_6b8dc130 FOREIGN KEY (ticket_id) \n\ - REFERENCES tickets_ticket (id) MATCH SIMPLE \n\ - ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED, \n\ - CONSTRAINT tickets_detail_logger_id_fkey FOREIGN KEY (logger_id) \n\ - REFERENCES auth_user (id) MATCH SIMPLE \n\ - ON UPDATE NO ACTION ON DELETE NO ACTION DEFERRABLE INITIALLY DEFERRED \n\ -) \n\ -WITH ( \n\ - OIDS=FALSE \n\ -); \n\ -ALTER TABLE tickets_detail \n\ -OWNER TO helpdesk;\n'; - var panelEvents = {}; panelEvents[wcDocker.EVENT.VISIBILITY_CHANGED] = function() { - if (this.isVisible()) { var obj = pgAdmin.Browser, i = obj.tree ? obj.tree.selected() : undefined, @@ -57,13 +31,6 @@ OWNER TO helpdesk;\n'; } }; - var sqlPanelEvents = {}; - sqlPanelEvents[wcDocker.EVENT.VISIBILITY_CHANGED] = function() { - /* Update the SQL editor to show the latest value all the time */ - if (this.isVisible()) { - pgAdmin.Browser.editor.setValue($('#sql-textarea').val()); - } - }; // Extend the browser class attributes _.extend(pgAdmin.Browser, { // The base url for browser @@ -118,8 +85,7 @@ OWNER TO helpdesk;\n'; width: 500, isCloseable: false, isPrivate: true, - content: '', - events: sqlPanelEvents + content: '' }), // Dependencies of the object 'dependencies': new pgAdmin.Browser.Panel({ @@ -282,6 +248,11 @@ OWNER TO helpdesk;\n'; init: function() { var obj=this; + if (obj.initialized) { + return; + } + obj.initialized = true; + // Store the main browser layout $(window).bind('unload', function() { if(obj.docker) { @@ -340,8 +311,6 @@ OWNER TO helpdesk;\n'; mode: "text/x-sql", readOnly: true }); - // TODO:: Revove demoSql later - $('#sql-textarea').val(demoSql); // Initialise the treeview $('#tree').aciTree({ @@ -485,8 +454,10 @@ OWNER TO helpdesk;\n'; break; } + var node; + if (d && obj.Nodes[d._type]) { - var node = obj.Nodes[d._type]; + node = obj.Nodes[d._type]; /* If the node specific callback returns false, we will also return * false for further processing. @@ -507,6 +478,17 @@ OWNER TO helpdesk;\n'; console.log(e); } } + + try { + obj.Events.trigger( + 'pgadmin-browser:tree', eventName, item, d + ); + obj.Events.trigger( + 'pgadmin-browser:tree:' + eventName, item, d, node + ); + } catch (e) { + console.log(e); + } return true; }); diff --git a/web/pgadmin/browser/templates/browser/js/node.js b/web/pgadmin/browser/templates/browser/js/node.js index 380ba024e..bdda1ac5e 100644 --- a/web/pgadmin/browser/templates/browser/js/node.js +++ b/web/pgadmin/browser/templates/browser/js/node.js @@ -542,6 +542,8 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, pgBrowser, Backform) { // tab is active.) } } + + return true; }, refresh: function(i) { var self = this, diff --git a/web/pgadmin/misc/sql/__init__.py b/web/pgadmin/misc/sql/__init__.py new file mode 100644 index 000000000..131a3669f --- /dev/null +++ b/web/pgadmin/misc/sql/__init__.py @@ -0,0 +1,29 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2016, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +"""A blueprint module providing utility functions for the application.""" + +import datetime +from flask import session, current_app, url_for +from pgadmin.utils import PgAdminModule +import pgadmin.utils.driver as driver + +MODULE_NAME = 'sql' + +class SQLModule(PgAdminModule): + + def get_own_javascripts(self): + return [{ + 'name': 'pgadmin.browser.object_sql', + 'path': url_for('sql.static', filename='js/sql'), + 'when': None + }] + +# Initialise the module +blueprint = SQLModule(MODULE_NAME, __name__, url_prefix='/misc/sql') diff --git a/web/pgadmin/misc/sql/static/js/sql.js b/web/pgadmin/misc/sql/static/js/sql.js new file mode 100644 index 000000000..2a5a92309 --- /dev/null +++ b/web/pgadmin/misc/sql/static/js/sql.js @@ -0,0 +1,85 @@ +define( + ['underscore', 'jquery', 'pgadmin.browser'], +function(_, $, pgBrowser) { + + pgBrowser.ShowNodeSQL = pgBrowser.ShowNodeSQL || {}; + + if (pgBrowser.ShowNodeSQL.initialized) { + return pgBrowser.ShowNodeSQL; + } + + _.extend(pgBrowser.ShowNodeSQL, { + init: function() { + if (this.initialized) { + return; + } + this.initialized = true; + _.bindAll(this, 'showSQL', 'sqlPanelVisibilityChanged'); + + var sqlPanels = pgBrowser.docker.findPanels('sql'); + + // We will listend to the visibility change of the SQL panel + pgBrowser.Events.on('pgadmin-browser:panel-sql:' + wcDocker.EVENT.VISIBILITY_CHANGED, this.sqlPanelVisibilityChanged); + + // Hmm.. Did we find the SQL panel, and is it visible (openned)? + // If that is the case - we need to listen the browser tree selection + // events. + if ((sqlPanels.length == 1 && sqlPanels[0].isVisible()) || sqlPanels.length != 1) { + pgBrowser.Events.on('pgadmin-browser:tree:selected', this.showSQL); + } + }, + showSQL: function(item, data, node) { + /** + * We can't start fetching the SQL immediately, it is possible - the user + * is just using keyboards to select the node, and just traversing + * through. We will wait for some time before fetching the Reversed + * Engineering SQL. + **/ + if (this.timeout) { + clearTimeout(this.timeout); + } + this.timeout = setTimeout( + function() { + var sql = '-- No object selected!'; + if (node) { + sql = '-- No SQL generated for the selected object.'; + if (node.hasSQL) { + + sql = '-- Please wait while we fetch the SQL from the server.'; + var url = node.generate_url(item, 'sql', data, true); + + $.ajax({ + url: url, + type:'GET', + success: function(res) { + pgAdmin.Browser.editor.setValue(res); + }, + error: function() { + // TODO:: Report this + } + }); + } + } + pgAdmin.Browser.editor.setValue(sql); + }, 400); + }, + sqlPanelVisibilityChanged: function(panel) { + if (panel.isVisible()) { + var t = pgBrowser.tree, + i = t.selected(), + d = i && t.itemData(i), + n = i && d && pgBrowser.Nodes[d._type]; + + pgBrowser.ShowNodeSQL.showSQL.apply(pgBrowser.ShowNodeSQL, [i, d, n]); + + // We will start listening the tree selection event. + pgBrowser.Events.on('pgadmin-browser:tree:selected', pgBrowser.ShowNodeSQL.showSQL); + } else { + // We don't need to listen the tree item selection event. + pgBrowser.Events.off('pgadmin-browser:tree:selected', pgBrowser.ShowNodeSQL.showSQL); + } + } + }); + + return pgBrowser.ShowNodeSQL; +});