Changed the logic to identify the PostgreSQL server variant, when we

connect to the database server. Also, added modified the way, we do
check the node is supported by the server.

Instead of creating separate blueprint for the server types, they will
be independently works. In order to add different variant of the
PostgreSQL, we need to extend the ServerType class, and override the
'instanceOf' function for identification using version string. Please
take a look at the ppas.py for the example.

During checking the back-end support for the node, we will also check
the server type (variant) along with the version within the range of
maximum and minimum version for the node. And, the same support added
for the schema attributes in front-end (JavaScript).

Really thankful to Khushboo Vashi for her initial work in front-end. I
took it further from there.
This commit is contained in:
Ashesh Vashi 2015-11-19 23:15:48 +05:30
parent 3323543b5a
commit 77834ccdda
19 changed files with 306 additions and 341 deletions

View File

@ -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:

View File

@ -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.

View File

@ -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,

View File

@ -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')

View File

@ -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')

View File

@ -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
}
)

View File

@ -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)

View File

Before

Width:  |  Height:  |  Size: 850 B

After

Width:  |  Height:  |  Size: 850 B

View File

Before

Width:  |  Height:  |  Size: 676 B

After

Width:  |  Height:  |  Size: 676 B

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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) {

View File

@ -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)

View File

@ -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);
}
}
});

View File

@ -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];

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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