diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py index b91a777c8..0606b37c6 100644 --- a/web/pgadmin/browser/__init__.py +++ b/web/pgadmin/browser/__init__.py @@ -180,7 +180,7 @@ class BrowserPluginModule(PgAdminModule): return scripts def generate_browser_node( - self, node_id, parent_id, label, icon, inode, node_type, **kwargs + self, node_id, label, icon, inode, node_type, **kwargs ): obj = { "id": "%s/%s" % (node_type, node_id), @@ -189,7 +189,6 @@ class BrowserPluginModule(PgAdminModule): "inode": inode, "_type": node_type, "_id": node_id, - "refid": parent_id, "module": 'pgadmin.node.%s' % node_type } for key in kwargs: diff --git a/web/pgadmin/browser/collection.py b/web/pgadmin/browser/collection.py index f5dd4aaa1..893940d6d 100644 --- a/web/pgadmin/browser/collection.py +++ b/web/pgadmin/browser/collection.py @@ -13,7 +13,7 @@ from flask import url_for, render_template from flask.ext.babel import gettext from pgadmin.browser.utils import PgAdminModule, PGChildModule from pgadmin.browser import BrowserPluginModule -from pgadmin.browser.utils import NodeView, PGChildNodeView +from pgadmin.browser.utils import NodeView, PGChildNode, PGChildNodeView @six.add_metaclass(ABCMeta) class CollectionNodeModule(PgAdminModule, PGChildModule): @@ -53,7 +53,7 @@ class CollectionNodeModule(PgAdminModule, PGChildModule): return scripts def generate_browser_node( - self, node_id, parents, label, icon, **kwargs + self, node_id, label, icon, **kwargs ): obj = { "id": "%s/%s" % (self.node_type, node_id), @@ -62,14 +62,13 @@ class CollectionNodeModule(PgAdminModule, PGChildModule): "inode": self.node_inode, "_type": self.node_type, "_id": node_id, - "refid": parents, "module": 'pgadmin.node.%s' % self.node_type } for key in kwargs: obj.setdefault(key, kwargs[key]) return obj - def generate_browser_collection_node(self, sid, parents, **kwargs): + def generate_browser_collection_node(self, sid, **kwargs): obj = { "id": "coll-%s/%d" % (self.node_type, sid), "label": self.collection_label, @@ -77,7 +76,6 @@ class CollectionNodeModule(PgAdminModule, PGChildModule): "inode": True, "_type": 'coll-%s' % (self.node_type), "_id": sid, - "refid": parents, "module": 'pgadmin.node.%s' % self.node_type } @@ -173,7 +171,7 @@ class CollectionNodeModule(PgAdminModule, PGChildModule): return [] -class CollectionNodeView(NodeView, PGChildNodeView): +class CollectionNodeView(NodeView, PGChildNode): """ A PostgreSQL Collection node has specific functions needs to address i.e. diff --git a/web/pgadmin/browser/server_groups/__init__.py b/web/pgadmin/browser/server_groups/__init__.py index 5ecfd3d7d..7fee85df3 100644 --- a/web/pgadmin/browser/server_groups/__init__.py +++ b/web/pgadmin/browser/server_groups/__init__.py @@ -33,7 +33,6 @@ class ServerGroupModule(BrowserPluginModule): for group in groups: yield self.generate_browser_node( "%d" % (group.id), - None, group.name, "icon-%s" % self.node_type, True, @@ -179,7 +178,6 @@ class ServerGroupView(NodeView): return jsonify( node=self.blueprint.generate_browser_node( "%d" % (sg.id), - None, sg.name, "icon-%s" % self.node_type, True, diff --git a/web/pgadmin/browser/server_groups/servers/PPAS/__init__.py b/web/pgadmin/browser/server_groups/servers/PPAS/__init__.py deleted file mode 100644 index 302005529..000000000 --- a/web/pgadmin/browser/server_groups/servers/PPAS/__init__.py +++ /dev/null @@ -1,44 +0,0 @@ -########################################################################## -# -# pgAdmin 4 - PostgreSQL Tools -# -# Copyright (C) 2013 - 2015, The pgAdmin Development Team -# This software is released under the PostgreSQL Licence -# -########################################################################## -from pgadmin.browser.server_groups.servers import ServerTypeModule -from pgadmin.browser.utils import PGChildModule -from flask.ext.babel import gettext - - -class PPASServer(ServerTypeModule, PGChildModule): - NODE_TYPE = "ppas" - - @property - def node_type(self): - return self.NODE_TYPE - - @property - def jssnippets(self): - return [] - - @property - def type(self): - return "PPAS" - - @property - def description(self): - return "Postgres Plus Advanced Server" - - @property - def driver(self): - return "psycopg2" - - @property - def priority(self): - return 1 - - def instanceOf(self, ver): - return ver.startswith("EnterpriseDB") - -blueprint = PPASServer(__name__, static_url_path='/static') diff --git a/web/pgadmin/browser/server_groups/servers/PostgreSQL/__init__.py b/web/pgadmin/browser/server_groups/servers/PostgreSQL/__init__.py deleted file mode 100644 index b11faecfc..000000000 --- a/web/pgadmin/browser/server_groups/servers/PostgreSQL/__init__.py +++ /dev/null @@ -1,43 +0,0 @@ -########################################################################## -# -# pgAdmin 4 - PostgreSQL Tools -# -# Copyright (C) 2013 - 2015, The pgAdmin Development Team -# This software is released under the PostgreSQL Licence -# -########################################################################## -from pgadmin.browser.server_groups.servers import ServerTypeModule -from pgadmin.browser.utils import PGChildModule - - -class PGServer(ServerTypeModule, PGChildModule): - NODE_TYPE = "pg" - - @property - def node_type(self): - return self.NODE_TYPE - - @property - def jssnippets(self): - return [] - - @property - def type(self): - return "PG" - - @property - def description(self): - return "PostgreSQL" - - @property - def driver(self): - return "psycopg2" - - @property - def priority(self): - return 10 - - def instanceOf(self, ver): - return True - -blueprint = PGServer(__name__, static_url_path='/static') diff --git a/web/pgadmin/browser/server_groups/servers/__init__.py b/web/pgadmin/browser/server_groups/servers/__init__.py index 323f2e3d1..430dc3af1 100644 --- a/web/pgadmin/browser/server_groups/servers/__init__.py +++ b/web/pgadmin/browser/server_groups/servers/__init__.py @@ -15,7 +15,7 @@ from pgadmin.utils.menu import MenuItem from pgadmin.utils.ajax import make_json_response, \ make_response as ajax_response, internal_server_error, success_return, \ unauthorized, bad_request, precondition_required, forbidden -from pgadmin.browser.utils import NodeView +from pgadmin.browser.utils import PGChildNodeView import traceback from flask.ext.babel import gettext import pgadmin.browser.server_groups as sg @@ -23,6 +23,7 @@ from pgadmin.utils.crypto import encrypt, decrypt from pgadmin.browser import BrowserPluginModule from config import PG_DEFAULT_DRIVER import six +from pgadmin.browser.server_groups.servers.types import ServerType class ServerModule(sg.ServerGroupPluginModule): NODE_TYPE = "server" @@ -50,19 +51,18 @@ class ServerModule(sg.ServerGroupPluginModule): for server in servers: manager = driver.connection_manager(server.id) conn = manager.connection() - module = getattr(manager, "module", None) connected = conn.connected() yield self.generate_browser_node( "%d" % (server.id), - "%d" % gid, server.name, "icon-server-not-connected" if not connected else - "icon-{0}".format(module.NODE_TYPE), + "icon-{0}".format(manager.server_type), True, self.NODE_TYPE, connected=connected, - server_type=module.type if module is not None else "PG" + server_type=manager.server_type if connected else "pg", + server_version=manager.sversion if connected else 0 ) @property @@ -79,6 +79,9 @@ class ServerModule(sg.ServerGroupPluginModule): for submodule in self.submodules: snippets.extend(submodule.csssnippets) + for st in ServerType.types(): + snippets.extend(st.csssnippets) + return snippets @@ -90,81 +93,7 @@ class ServerMenuItem(MenuItem): blueprint = ServerModule(__name__) -@six.add_metaclass(ABCMeta) -class ServerTypeModule(BrowserPluginModule): - """ - Base class for different server types. - """ - - - @abstractproperty - def type(self): - pass - - @abstractproperty - def description(self): - pass - - @abstractproperty - def priority(self): - pass - - def get_nodes(self, manager=None, sid=None): - assert(sid is not None) - - nodes = [] - - for module in self.submodules: - if isinstance(module, PGChildModule): - if manager and module.BackendSupported(manager): - nodes.extend(module.get_nodes(sid=sid, manager=manager)) - else: - nodes.extend(module.get_nodes(sid=sid)) - - return nodes - - @abstractmethod - def instanceOf(self, version): - pass - - def __str__(self): - return "Type: {0},Description:{1}".format(self.type, self.description) - - @property - def csssnippets(self): - """ - Returns a snippet of css to include in the page - """ - snippets = [ - render_template( - "css/node.css", - node_type=self.node_type - ) - ] - - for submodule in self.submodules: - snippets.extend(submodule.csssnippets) - - return snippets - - def get_own_javascripts(self): - scripts = [] - - for module in self.submodules: - scripts.extend(module.get_own_javascripts()) - - return scripts - - @property - def script_load(self): - """ - Load the module script for all server types, when a server node is - initialized. - """ - return ServerTypeModule.NODE_TYPE - - -class ServerNode(NodeView): +class ServerNode(PGChildNodeView): node_type = ServerModule.NODE_TYPE parent_ids = [{'type': 'int', 'id': 'gid'}] @@ -196,20 +125,19 @@ class ServerNode(NodeView): for server in servers: manager = driver.connection_manager(server.id) conn = manager.connection() - module = getattr(manager, "module", None) - connected = conn.connected() + res.append( self.blueprint.generate_browser_node( "%d" % (server.id), - "%d" % gid, server.name, "icon-server-not-connected" if not connected else - "icon-{0}".format(module.NODE_TYPE), + "icon-{0}".format(manager.server_type), True, self.node_type, connected=connected, - server_type=module.type if module is not None else 'PG' + server_type=manager.server_type if connected else 'PG', + version=manager.sversion ) ) return make_json_response(result=res) @@ -353,7 +281,6 @@ class ServerNode(NodeView): manager = driver.connection_manager(sid) conn = manager.connection() connected = conn.connected() - module = getattr(manager, 'module', None) return ajax_response( response={ @@ -368,8 +295,8 @@ class ServerNode(NodeView): 'comment': server.comment, 'role': server.role, 'connected': connected, - 'version': manager.ver, - 'server_type': module.type if module is not None else 'PG' + 'version': manager.sversion, + 'server_type': manager.server_type if connected else 'PG' } ) @@ -416,7 +343,6 @@ class ServerNode(NodeView): return jsonify( node=self.blueprint.generate_browser_node( "%d" % (server.id), - "%d" % gid, server.name, "icon-server-not-connected", True, @@ -433,31 +359,6 @@ class ServerNode(NodeView): errormsg=e.message ) - def nodes(self, gid, sid): - """Build a list of treeview nodes from the child nodes.""" - from pgadmin.utils.driver import get_driver - - manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) - conn = manager.connection() - - if not conn.connected(): - return precondition_required( - gettext( - "Please make a connection to the server first!" - ) - ) - - nodes = [] - - # We will rely on individual server type modules to generate nodes for - # them selves. - module = getattr(manager, 'module', None) - - if module: - nodes.extend(module.get_nodes(sid=sid, manager=manager)) - - return make_json_response(data=nodes) - def sql(self, gid, sid): return make_json_response(data='') @@ -481,13 +382,7 @@ class ServerNode(NodeView): return make_response( render_template( "servers/servers.js", - server_types=sorted( - [ - m for m in self.blueprint.submodules - if isinstance(m, ServerTypeModule) - ], - key=lambda x: x.priority - ), + server_types=ServerType.types(), _=gettext ), 200, {'Content-Type': 'application/x-javascript'} @@ -572,10 +467,7 @@ class ServerNode(NodeView): try: status, errmsg = conn.connect( password=password, - modules=[ - m for m in self.blueprint.submodules - if isinstance(m, ServerTypeModule) - ] + server_types=ServerType.types() ) except Exception as e: # TODO:: @@ -626,9 +518,11 @@ class ServerNode(NodeView): info=gettext("Server Connected."), data={ 'icon': 'icon-{0}'.format( - manager.module.NODE_TYPE + manager.server_type ), - 'connected': True + 'connected': True, + 'type': manager.server_type, + 'version': manager.sversion } ) diff --git a/web/pgadmin/browser/server_groups/servers/ppas.py b/web/pgadmin/browser/server_groups/servers/ppas.py new file mode 100644 index 000000000..a08b92468 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/ppas.py @@ -0,0 +1,19 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2015, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +from flask.ext.babel import gettext +from pgadmin.browser.server_groups.servers.types import ServerType + + +class PPAS(ServerType): + + def instanceOf(self, ver): + return ver.startswith("EnterpriseDB") + +# Default Server Type +PPAS('ppas', gettext("Postgres Plus Advanced Server"), 2) diff --git a/web/pgadmin/browser/server_groups/servers/PostgreSQL/static/img/pg.png b/web/pgadmin/browser/server_groups/servers/static/img/pg.png similarity index 100% rename from web/pgadmin/browser/server_groups/servers/PostgreSQL/static/img/pg.png rename to web/pgadmin/browser/server_groups/servers/static/img/pg.png diff --git a/web/pgadmin/browser/server_groups/servers/PPAS/static/img/ppas.png b/web/pgadmin/browser/server_groups/servers/static/img/ppas.png similarity index 100% rename from web/pgadmin/browser/server_groups/servers/PPAS/static/img/ppas.png rename to web/pgadmin/browser/server_groups/servers/static/img/ppas.png diff --git a/web/pgadmin/browser/server_groups/servers/templates/css/node.css b/web/pgadmin/browser/server_groups/servers/templates/css/node.css deleted file mode 100644 index eda34f497..000000000 --- a/web/pgadmin/browser/server_groups/servers/templates/css/node.css +++ /dev/null @@ -1,7 +0,0 @@ -.icon-{{node_type}} { - background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/%s.png' % node_type )}}') !important; - background-repeat: no-repeat; - align-content: center; - vertical-align: middle; - height: 1.3em; -} diff --git a/web/pgadmin/browser/server_groups/servers/templates/css/server_type.css b/web/pgadmin/browser/server_groups/servers/templates/css/server_type.css new file mode 100644 index 000000000..799271f06 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/templates/css/server_type.css @@ -0,0 +1,7 @@ +.icon-{{ server_type }} { + background-image: url('{{ url_for('NODE-server.static', filename='img/%s.png' % server_type)}}') !important; + background-repeat: no-repeat; + align-content: center; + vertical-align: middle; + height: 1.3em; +} diff --git a/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js b/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js index eec45f0d9..33924943f 100644 --- a/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js +++ b/web/pgadmin/browser/server_groups/servers/templates/servers/servers.js @@ -69,7 +69,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { var input = args || {}; obj = this, t = pgBrowser.tree, - i = input.item || t.selected(), + i = 'item' in input ? input.item : t.selected(), d = i && i.length == 1 ? t.itemData(i) : undefined; if (!d) @@ -80,17 +80,21 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { S('{{ _('Are you sure you want to disconnect the server - %%s ?') }}').sprintf(d.label).value(), function(evt) { $.ajax({ - url: obj.generate_url('connect', d, true), + url: obj.generate_url(i, 'connect', d, true), type:'DELETE', success: function(res) { if (res.success == 1) { alertify.success("{{ _('" + res.info + "') }}"); + d = t.itemData(i); t.removeIcon(i); d.connected = false; d.icon = 'icon-server-not-connected'; t.addIcon(i, {icon: d.icon}); t.unload(i); t.setInode(i); + if (pgBrowser.serverInfo && d._id in pgBrowser.serverInfo) { + delete pgBrowser.serverInfo[d._id] + } } }, error: function(xhr, status, error) { @@ -112,17 +116,21 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { return false; }, /* Connect the server (if not connected), before opening this node */ - beforeopen: function(item, data, browser) { + beforeopen: function(item, data) { if(!data || data._type != 'server') { return false; } - browser.tree.addIcon(item, {icon: data.icon}); + pgBrowser.tree.addIcon(item, {icon: data.icon}); if (!data.connected) { - connect_to_server(this, data, browser.tree, item); + connect_to_server(this, data, pgBrowser.tree, item); return false; } return true; + }, + opened: function(item, data) { + pgBrowser.serverInfo = pgBrowser.serverInfo || {}; + pgBrowser.serverInfo[data._id] = _.extend({}, data); } }, model: pgAdmin.Browser.Node.Model.extend({ @@ -218,18 +226,16 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { tree.setInode(item); if (res && res.data) { - if(typeof res.data.connected == 'boolean') { - data.connected = res.data.connected; - } + if (typeof res.data.icon == 'string') { tree.removeIcon(item); data.icon = res.data.icon; tree.addIcon(item, {icon: data.icon}); } + _.extend(data, res.data); alertify.success(res.info); - setTimeout(function() {tree.select(item);}, 10); - setTimeout(function() {tree.open(item);}, 100); + setTimeout(function() { tree.select(item); tree.open(item); }, 10); } }; @@ -273,7 +279,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { if (closeEvent.button.text == "{{ _('OK') }}") { - var _url = _model.generate_url('connect', _sdata, true); + var _url = _model.generate_url(_item, 'connect', _sdata, true); _tree.setLeaf(_item); _tree.removeIcon(_item); @@ -309,7 +315,7 @@ function($, _, S, pgAdmin, pgBrowser, alertify) { '{{ _('Connect to server') }}', '{{ _('Do you want to connect the server?') }}', function(evt) { - url = obj.generate_url("connect", data, true); + url = obj.generate_url(item, "connect", data, true); $.post(url) .done( function(res) { diff --git a/web/pgadmin/browser/server_groups/servers/types.py b/web/pgadmin/browser/server_groups/servers/types.py new file mode 100644 index 000000000..040e8a465 --- /dev/null +++ b/web/pgadmin/browser/server_groups/servers/types.py @@ -0,0 +1,75 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2015, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## +import six +from abc import ABCMeta, abstractmethod, abstractproperty +from flask import render_template +from flask.ext.babel import gettext + + +class ServerType: + """ + Server Type + + Create an instance of this class to define new type of the server support, + In order to define new type of instance, you may want to override this + class with overriden function - instanceOf for type checking for + identification based on the version. + """ + registry = dict() + + def __init__(self, server_type, description, priority): + self.stype = server_type + self.description = description + self.priority = priority + + assert(server_type not in ServerType.registry) + ServerType.registry[server_type] = self + + @property + def server_type(self): + return self.stype + + @property + def description(self): + return self.description + + @property + def priority(self): + return self.priority + + def __str__(self): + return "Type: {0}, Description:{1}, Priority: {2}".format( + self.stype, self.description, self.priority + ) + + def instanceOf(self, version): + return True + + @property + def csssnippets(self): + """ + Returns a snippet of css to include in the page + """ + return [ + render_template( + "css/server_type.css", + server_type=self.stype + ) + ] + + @classmethod + def types(cls): + return sorted( + ServerType.registry.values(), + key=lambda x: x.priority, + reverse=True + ) + +# Default Server Type +ServerType('pg', gettext("PostgreSQL"), -1) diff --git a/web/pgadmin/browser/templates/browser/js/browser.js b/web/pgadmin/browser/templates/browser/js/browser.js index ee4984d64..dbd728096 100644 --- a/web/pgadmin/browser/templates/browser/js/browser.js +++ b/web/pgadmin/browser/templates/browser/js/browser.js @@ -358,7 +358,7 @@ OWNER TO helpdesk;\n'; var d = this.itemData(item); n = obj.Nodes[d._type]; if (n) - settings.url = n.generate_url('nodes', d, true); + settings.url = n.generate_url(item, 'nodes', d, true); } } }); diff --git a/web/pgadmin/browser/templates/browser/js/collection.js b/web/pgadmin/browser/templates/browser/js/collection.js index 96c49fc59..819fb856a 100644 --- a/web/pgadmin/browser/templates/browser/js/collection.js +++ b/web/pgadmin/browser/templates/browser/js/collection.js @@ -33,6 +33,7 @@ function($, _, S, pgAdmin, Backbone, Alertify, Backform) { icon: 'fa fa-refresh' }]); }, + hasId: false, showProperties: function(item, data, panel) { var that = this, j = panel.$container.find('.obj_properties').first(), @@ -41,13 +42,14 @@ function($, _, S, pgAdmin, Backbone, Alertify, Backform) { .addClass('pg-prop-content col-xs-12'), node = pgBrowser.Nodes[that.node], // This will be the URL, used for object manipulation. - urlBase = this.generate_url('properties', data), + urlBase = this.generate_url(item, 'properties', data), collections = new (node.Collection.extend({ url: urlBase, model: node.model }))(), + info = this.getTreeNodeHierarchy.apply(this, [item]), gridSchema = Backform.generateGridColumnsFromModel( - node.model, 'prorperties', that.columns + info, node.model, 'prorperties', that.columns ), // Initialize a new Grid instance grid = new Backgrid.Grid({ @@ -86,28 +88,36 @@ function($, _, S, pgAdmin, Backbone, Alertify, Backform) { // Fetch Data collections.fetch({reset: true}); }, - generate_url: function(type, d) { + generate_url: function(item, type, d) { var url = pgAdmin.Browser.URL + '{TYPE}/{REDIRECT}{REF}', - ref = S('/%s/').sprintf(d._id).value(), /* * Using list, and collections functions of a node to get the nodes * under the collection, and properties of the collection respectively. */ opURL = { 'nodes': 'obj', 'properties': 'coll' - }; - if (d._type == this.type) { - if (d.refid) - ref = S('/%s/').sprintf(d.refid).value(); - } + }, + ref = '', self = this; - var TYPE = d.module.split("."); - var args = {'TYPE': TYPE[TYPE.length-1], 'REDIRECT': '', 'REF': ref}; - if (type in opURL) { - args.REDIRECT = opURL[type]; - } else { - args.REDIRECT = type; - } + _.each( + _.sortBy( + _.values( + _.pick( + this.getTreeNodeHierarchy(item), function(v, k, o) { + return (k != self.type); + }) + ), + function(o) { return o.priority; } + ), + function(o) { + ref = S('%s/%s').sprintf(ref, o.id).value(); + }); + + var args = { + 'TYPE': self.type, + 'REDIRECT': (type in opURL ? opURL[type] : type), + 'REF': S('%s/').sprintf(ref).value() + }; return url.replace(/{(\w+)}/g, function(match, arg) { return args[arg]; diff --git a/web/pgadmin/browser/templates/browser/js/node.js b/web/pgadmin/browser/templates/browser/js/node.js index c38771825..16529a4ca 100644 --- a/web/pgadmin/browser/templates/browser/js/node.js +++ b/web/pgadmin/browser/templates/browser/js/node.js @@ -54,6 +54,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) { title: function(d) { return d ? d.label : ''; }, + hasId: true, /////// // Initialization function // Generally - used to register the menus for this type of node. @@ -83,7 +84,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) { // // Used to generate view for the particular node properties, edit, // creation. - getView: function(type, el, node, formType, callback, data) { + getView: function(item, type, el, node, formType, callback, data) { if (!this.type || this.type == '') // We have no information, how to generate view for this type. @@ -92,7 +93,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) { if (this.model) { // This will be the URL, used for object manipulation. // i.e. Create, Update in these cases - var urlBase = this.generate_url(type, node); + var urlBase = this.generate_url(item, type, node, false); if (!urlBase) // Ashamed of myself, I don't know how to manipulate this @@ -112,7 +113,8 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) { onChangeData: data, onChangeCallback: callback }), - groups = Backform.generateViewSchema(newModel, type); + info = this.getTreeNodeHierarchy.apply(this, [item]), + groups = Backform.generateViewSchema(info, newModel, type); // 'schema' has the information about how to generate the form. if (groups) { @@ -366,7 +368,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) { .sprintf(obj.label, d.label).value(), function() { $.ajax({ - url: obj.generate_url('drop', d, true), + url: obj.generate_url(i, 'drop', d, true), type:'DELETE', success: function(res) { if (res.success == 0) { @@ -534,7 +536,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) { // Make sure the HTML element is empty. j.empty(); // Create a view to show the properties in fieldsets - view = that.getView('properties', content, data, 'fieldset'); + view = that.getView(item, 'properties', content, data, 'fieldset'); if (view) { // Save it for release it later j.data('obj-view', view); @@ -592,7 +594,7 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) { }; // Create a view to edit/create the properties in fieldsets - view = that.getView(action, content, data, 'dialog', modelChanged, j); + view = that.getView(item, action, content, data, 'dialog', modelChanged, j); if (view) { // Save it to release it later j.data('obj-view', view); @@ -797,32 +799,38 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) { * Supports url generation for create, drop, edit, properties, sql, * depends, statistics */ - generate_url: function(type, d, with_id) { + generate_url: function(item, type, d, with_id) { var url = pgAdmin.Browser.URL + '{TYPE}/{REDIRECT}{REF}', - ref = S('/%s/').sprintf(d._id).value(), opURL = { 'create': 'obj', 'drop': 'obj', 'edit': 'obj', 'properties': 'obj', 'depends': 'deps', 'statistics': 'stats', 'nodes': 'nodes' - }; - if (d._type == this.type) { - ref = ''; - if (d.refid) - ref = S('/%s').sprintf(d.refid).value(); - if (with_id) - ref = S('%s/%s').sprintf(ref, d._id).value(); - } + }, + ref = '', self = this; - var args = { 'TYPE': this.type, 'REDIRECT': '', 'REF': ref }; + _.each( + _.sortBy( + _.values( + _.pick( + this.getTreeNodeHierarchy(item), function(v, k, o) { + return (k != self.type); + }) + ), + function(o) { return o.priority; } + ), + function(o) { + ref = S('%s/%s').sprintf(ref, o.id).value(); + }); - if (type in opURL) { - args.REDIRECT = opURL[type]; - if (type == 'create' && !this.parent_type) { - args.REF = '/'; - } - } else { - args.REDIRECT = type; - } + ref = S('%s/%s').sprintf( + ref, with_id && d._type == self.type ? d._id : '' + ).value(); + + var args = { + 'TYPE': self.type, + 'REDIRECT': (type in opURL ? opURL[type] : type), + 'REF': ref + }; return url.replace(/{(\w+)}/g, function(match, arg) { return args[arg]; @@ -1337,7 +1345,25 @@ function($, _, S, pgAdmin, Menu, Backbone, Alertify, Backform) { } return false; } - }) + }), + getTreeNodeHierarchy: function(i) { + var idx = 0, + res = {}, + t = pgBrowser.tree; + do { + d = t.itemData(i); + if (d._type in pgBrowser.Nodes && pgBrowser.Nodes[d._type].hasId) { + res[d._type] = { + 'id': d._id, + 'priority': idx + }; + idx += 1; + } + i = t.hasParent(i) ? t.parent(i) : null; + } while (i); + + return res; + } }); return pgAdmin.Browser.Node; diff --git a/web/pgadmin/browser/utils.py b/web/pgadmin/browser/utils.py index 4b5669564..addacc141 100644 --- a/web/pgadmin/browser/utils.py +++ b/web/pgadmin/browser/utils.py @@ -34,9 +34,9 @@ class NodeAttr(object): pass -class PGChildModule(object): +class PGChildModule: """ - class PGChildModule(ServerChildModule) + class PGChildModule This is a base class for children/grand-children of PostgreSQL, and all Postgres Plus version (i.e. Postgres Plus Advanced Server, Green Plum, @@ -71,10 +71,12 @@ class PGChildModule(object): assert(self.max_ver is None or isinstance(self.max_ver, int)) assert(self.min_ver is None or isinstance(self.min_ver, int)) + assert(self.server_type is None or isinstance(self.server_type, list)) - if self.min_ver is None or self.min_ver <= sversion: - if self.max_ver is None or self.max_ver >= sversion: - return True + if self.server_type is None or manager.server_type in self.server_type: + if self.min_ver is None or self.min_ver <= sversion: + if self.max_ver is None or self.max_ver >= sversion: + return True return False @@ -319,17 +321,32 @@ class NodeView(with_metaclass(MethodViewType, View)): return make_json_response(data=nodes) -class PGChildNodeView(object): +class PGChildNode(object): def nodes(self, sid, **kwargs): """Build a list of treeview nodes from the child nodes.""" from pgadmin.utils.driver import get_driver - manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(sid) + manager = get_driver(PG_DEFAULT_DRIVER).connection_manager( + sid=sid + ) + + did = None + if 'did' in kwargs: + did = kwargs['did'] + + conn = manager.connection(did=did) + + if not conn.connected(): + return precondition_required( + gettext( + "Please make a connection to the server first!" + ) + ) nodes = [] for module in self.blueprint.submodules: - if isinstance(module, ServerChildModule): + if isinstance(module, PGChildModule): if sid is not None and manager is not None and \ module.BackendSupported(manager): nodes.extend(module.get_nodes(sid=sid, **kwargs)) @@ -337,3 +354,8 @@ class PGChildNodeView(object): nodes.extend(module.get_nodes(sid=sid, **kwargs)) return make_json_response(data=nodes) + + +class PGChildNodeView(NodeView, PGChildNode): + + pass diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 2de7cc86f..1ca7cbe10 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -229,7 +229,7 @@ self.shown_tab = $(this).data('tabIndex'); m.trigger('pg-property-tab-changed', { 'collection': m.collection, 'model': m, - 'index': _.indexOf(m.collection.models, m), + 'index': m.collection && m.collection.models ? _.indexOf(m.collection.models, m) : 0, 'shown': self.shown_tab, 'hidden': self.hidden_tab }); }); @@ -296,32 +296,33 @@ events: {} }); - var generateGridColumnsFromModel = Backform.generateGridColumnsFromModel = function(m, type, cols) { - var groups = Backform.generateViewSchema(m, type), - schema = [], - columns = [], - addAll = _.isUndefined(cols) || _.isNull(cols); + var generateGridColumnsFromModel = Backform.generateGridColumnsFromModel = + function(node_info, m, type, cols) { + var groups = Backform.generateViewSchema(node_info, m, type), + schema = [], + columns = [], + addAll = _.isUndefined(cols) || _.isNull(cols); - // Prepare columns for backgrid - _.each(groups, function(fields, key) { - _.each(fields, function(f) { - if (!f.control && !f.cell) { - return; - } - f.cell_priority = _.indexOf(cols, f.name); - if (addAll || f.cell_priority != -1) { - columns.push(f); - } + // Prepare columns for backgrid + _.each(groups, function(fields, key) { + _.each(fields, function(f) { + if (!f.control && !f.cell) { + return; + } + f.cell_priority = _.indexOf(cols, f.name); + if (addAll || f.cell_priority != -1) { + columns.push(f); + } + }); + schema.push({label: key, fields: fields}); }); - schema.push({label: key, fields: fields}); - }); - return { - 'columns': _.sortBy(columns, function(c) { - return c.cell_priority; - }), - 'schema': schema + return { + 'columns': _.sortBy(columns, function(c) { + return c.cell_priority; + }), + 'schema': schema + }; }; - } var SubNodeCollectionControl = Backform.SubNodeCollectionControl = Backform.Control.extend({ render: function() { @@ -367,7 +368,7 @@ var subnode = data.subnode.schema ? data.subnode : data.subnode.prototype, gridSchema = Backform.generateGridColumnsFromModel( - subnode, this.field.get('mode'), data.columns + data.node_info, subnode, this.field.get('mode'), data.columns ); // Set visibility of Add button @@ -435,7 +436,7 @@ // // It will be used by the grid, properties, and dialog view generation // functions. - var generateViewSchema = Backform.generateViewSchema = function(Model, mode) { + var generateViewSchema = Backform.generateViewSchema = function(node_info, Model, mode) { var proto = (Model && Model.prototype) || Model, schema = (proto && proto.schema), groups, pgBrowser = window.pgAdmin.Browser; @@ -446,7 +447,9 @@ return ((prop && proto[prop] && typeof proto[prop] == "function") ? proto[prop] : prop); }; - groups = {}; + groups = {}, + server_info = node_info && ('server' in node_info) && + pgBrowser.serverInfo && pgBrowser.serverInfo[node_info.server.id]; _.each(schema, function(s) { // Do we understand - what control, we're creating @@ -465,22 +468,30 @@ // Generate the empty group list (if not exists) groups[group] = (groups[group] || []); + var disabled = ((mode == 'properties') || + (server_info && + (s.server_type && _.indexOf(server_info.type in s.server_type) == -1) || + (s.min_version && s.min_version < server_info.version) || + (s.max_version && s.max_version > server_info.version) + )); var o = _.extend(_.clone(s), { name: s.id, // Do we need to show this control in this mode? visible: evalASFunc(s.show), // This can be disabled in some cases (if not hidden) - disabled: (mode == 'properties' ? true : evalASFunc(s.disabled)), - editable: (mode == 'properties' ? false : pgAdmin.editableCell), - subnode: (_.isString(s.model) && s.model in pgBrowser.Nodes) ? - pgBrowser.Nodes[s.model].model : s.model, - canAdd: (mode == 'properties' ? false : evalASFunc(s.canAdd)), - canEdit: (mode == 'properties' ? false : evalASFunc(s.canEdit)), - canDelete: (mode == 'properties' ? false : evalASFunc(s.canDelete)), + + disabled: (disabled ? true : evalASFunc(s.disabled)), + editable: (disabled ? false : pgAdmin.editableCell), + subnode: ((_.isString(s.model) && s.model in pgBrowser.Nodes) ? + pgBrowser.Nodes[s.model].model : s.model), + canAdd: (disabled ? false : evalASFunc(s.canAdd)), + canEdit: (disabled ? false : evalASFunc(s.canEdit)), + canDelete: (disabled ? false : evalASFunc(s.canDelete)), mode: mode, control: control, - cell: cell + cell: cell, + node_info: node_info, }); delete o.id; diff --git a/web/pgadmin/utils/driver/psycopg2/__init__.py b/web/pgadmin/utils/driver/psycopg2/__init__.py index abe2c6c2c..2d18af7b7 100644 --- a/web/pgadmin/utils/driver/psycopg2/__init__.py +++ b/web/pgadmin/utils/driver/psycopg2/__init__.py @@ -155,7 +155,7 @@ SET bytea_output=escape; if status: mgr.ver = res - mgr.sversion = int(pg_conn.server_version) + mgr.sversion = pg_conn.server_version else: self.conn.close() self.conn = None @@ -178,13 +178,10 @@ WHERE db.datname = current_database()""") if 'password' in kwargs: mgr.password = kwargs['password'] - if 'modules' in kwargs and isinstance(kwargs['modules'], list): - for m in sorted( - kwargs['modules'], key=lambda module: module.priority - ): - if m.instanceOf(mgr.ver): - mgr.server_type = m.type - mgr.module = m + if 'server_types' in kwargs and isinstance(kwargs['server_types'], list): + for st in kwargs['server_types']: + if st.instanceOf(mgr.ver): + mgr.server_type = st.stype break return True, None @@ -380,9 +377,6 @@ class ServerManager(object): And, acts as connection manager for that particular session. """ def __init__(self, server): - self.module = None - self.ver = None - self.sversion = None self.connections = dict() self.update(server) @@ -394,10 +388,10 @@ class ServerManager(object): self.module = None self.ver = None self.sversion = None + self.server_type = None self.password = None self.sid = server.id - self.stype = None self.host = server.host self.port = server.port self.db = server.maintenance_db