Added support for the infrastructure for on demand access/create the

server connection.

The BaseDriver and BaseConnection are two abstract classes, which allows
us to replace the existing driver with the currently used. The current
implementation supports to connect the PostgreSQL and Postgres Plus
Advanced Server using the psycopg2 driver.
This commit is contained in:
Ashesh Vashi
2015-10-20 12:33:18 +05:30
parent b52d72f176
commit e27e39a8f3
34 changed files with 2625 additions and 417 deletions

View File

@@ -0,0 +1,7 @@
.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,17 @@
.icon-server {
background-image: url('{{ url_for('NODE-server.static', filename='img/server.png') }}') !important;
border-radius: 10px
}
.icon-server-not-connected {
background-image: url('{{ url_for('NODE-server.static', filename='img/serverbad.png') }}') !important;
border-radius: 10px
}
.icon-server-connecting {
background-image: url('{{ url_for('browser.static', filename='css/aciTree/image/load-node.gif')}}') !important;
background-repeat: no-repeat;
align-content: center;
vertical-align: middle;
height: 1.3em;
}

View File

@@ -0,0 +1,19 @@
<form name="frmPassword" id="frmPassword" style="height: 100%; width: 100%" onsubmit="return false;">
<div>{% if errmsg %}
<div class="highlight has-error">
<div class='control-label'>{{ errmsg }}</div>
</div>{% endif %}
<div><b>{{ _('Please enter the password for the user \'{0}\' to connect the server - "{1}"').format(username, server_label) }}</b></div>
<div style="padding: 5px; height: 1px;"></div>
<div style="width: 100%">
<span style="width: 25%;display: inline-table;">Password</span>
<span style="width: 73%;display: inline-block;">
<input style="width:100%" id="password" class="form-control" name="password" type="password">
</span>
<span style="margin-left: 25%; padding-top: 15px;width: 45%;display: inline-block;">
<input id="save_password" name="save_password" type="checkbox">&nbsp;&nbsp;Save Password
</span>
</div>
<div style="padding: 5px; height: 1px;"></div>
</div>
</form>

View File

@@ -1,6 +1,6 @@
define(
['jquery', 'underscore', 'pgadmin', 'pgadmin.browser', 'alertify'],
function($, _, pgAdmin, pgBrowser, alertify) {
['jquery', 'underscore', 'underscore.string', 'pgadmin', 'pgadmin.browser', 'alertify'],
function($, _, S, pgAdmin, pgBrowser, alertify) {
if (!pgBrowser.Nodes['server']) {
pgAdmin.Browser.Nodes['server'] = pgAdmin.Browser.Node.extend({
@@ -29,76 +29,99 @@ function($, _, pgAdmin, pgBrowser, alertify) {
name: 'drop_server', node: 'server', module: this,
applies: ['object', 'context'], callback: 'delete_obj',
category: 'drop', priority: 3, label: '{{ _('Drop Server...') }}',
icon: 'fa fa-trash'
icon: 'fa fa-trash', enable: 'is_not_connected'
},{
name: 'connect_server', node: 'server', module: this,
applies: ['object', 'context'], callback: 'connect_server',
category: 'connect', priority: 4, label: '{{ _('Connect Server...') }}',
icon: 'fa fa-link', enable : 'is_not_connected'
},
{
name: 'disconnect_server', node: 'server', module: this,
applies: ['object', 'context'], callback: 'disconnect_server',
category: 'drop', priority: 5, label: '{{ _('Disconnect Server...') }}',
icon: 'fa fa-chain-broken', enable : 'is_connected'
}]);
},
is_not_connected: function(node) {
return (node && node.connected != true);
},
is_connected: function(node) {
return (node && node.connected == true);
},
callbacks: {
// Add a server
create_server: function (item) {
var alert = alertify.prompt(
'{{ _('Create a server') }}',
'{{ _('Enter a name for the new server') }}',
'',
function(evt, value) {
var d = tree.itemData(item);
if (d._type != 'server-group') {
d = tree.itemData(tree.parent(item));
}
$.post(
"{{ url_for('browser.index') }}server/obj/" + d.refid + '/' + d.id + '/',
{ name: value }
)
.done(function(data) {
if (data.success == 0) {
report_error(data.errormsg, data.info);
} else {
var item = {
id: data.data.id,
label: data.data.name,
inode: true,
open: false,
icon: 'icon-server-not-connected'
}
tree.append(null, {
itemData: item
});
/* Connect the server */
connect_server: function(args){
var input = args || {};
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return false;
connect_to_server(obj, d, t, i);
return false;
},
/* Disconnect the server */
disconnect_server: function(args) {
var input = args || {};
obj = this,
t = pgBrowser.tree,
i = input.item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined;
if (!d)
return false;
alertify.confirm(
'{{ _('Disconnect the server') }}',
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),
type:'DELETE',
success: function(res) {
if (res.success == 1) {
alertify.success("{{ _('" + res.info + "') }}");
t.removeIcon(i);
d.connected = false;
d.icon = 'icon-server-not-connected';
t.addIcon(i, {icon: d.icon});
t.unload(i);
t.setInode(i);
}
});
},
null
);
alert.show();
},
error: function(xhr, status, error) {
try {
var err = $.parseJSON(xhr.responseText);
if (err.success == 0) {
msg = S('{{ _(' + err.errormsg + ')}}').value();
alertify.error("{{ _('" + err.errormsg + "') }}");
}
} catch (e) {}
t.unload(i);
}
});
},
function(evt) {
return true;
});
return false;
},
/* Connect the server (if not connected), before opening this node */
beforeopen: function(o) {
o.browser.tree.removeIcon(o.item);
if (o.data.connected) {
o.browser.tree.addIcon(o.item, {icon: 'icon-server-connected'});
} else {
o.browser.tree.addIcon(o.item, {icon: 'icon-server-not-connected'});
}
var data = o.data;
if(!data || data._type != 'server') {
return false;
}
o.browser.tree.addIcon(o.item, {icon: data.icon});
if (!data.connected) {
alertify.confirm(
'{{ _('Connect to server') }}',
'{{ _('Do you want to connect the server?') }}',
function(evt) {
$.post(
"{{ url_for('browser.index') }}server/connect/" + data.refid + '/'
).done(function(data) {
if (data.success == 0) {
report_error(data.errormsg, data.info);
}
}).fail(function() {});
return true;
},
function(evt) {
return true;
}
);
connect_to_server(this, data, o.browser.tree, o.item);
return false;
}
return true;
@@ -107,40 +130,61 @@ function($, _, pgAdmin, pgBrowser, alertify) {
model: pgAdmin.Browser.Node.Model.extend({
defaults: {
id: undefined,
name: undefined,
sslmode: 'prefer'
name: null,
sslmode: 'prefer',
host: null,
port: 5432,
db: null,
username: null,
role: null
},
schema: [{
id: 'id', label: 'ID', type: 'int', group: null,
id: 'id', label: '{{ _('ID') }}', type: 'int', group: null,
mode: ['properties']
},{
id: 'name', label:'Name', type: 'text', group: null,
id: 'name', label:'{{ _('Name') }}', type: 'text', group: null,
mode: ['properties', 'edit', 'create']
},{
id: 'connected', label:'Connected', type: 'text', group: null,
id: 'connected', label:'{{ _('Connected') }}', type: 'text', group: null,
mode: ['properties']
},{
id: 'version', label:'Version', type: 'text', group: null,
id: 'version', label:'{{ _('Version') }}', type: 'text', group: null,
mode: ['properties'], show: 'isConnected'
},{
id: 'comment', label:'Comments:', type: 'multiline', group: null,
mode: ['properties', 'edit', 'create'], disable: 'notEditMode'
id: 'comment', label:'{{ _('Comments:') }}', type: 'multiline', group: null,
mode: ['properties', 'edit', 'create'], disabled: 'notEditMode'
},{
id: 'host', label:'Host Name/Address', type: 'text', group: "Connection",
mode: ['properties', 'edit', 'create']
id: 'host', label:'{{ _('Host Name/Address') }}', type: 'text', group: "Connection",
mode: ['properties', 'edit', 'create'], disabled: 'isConnected'
},{
id: 'port', label:'Port', type: 'int', group: "Connection",
mode: ['properties', 'edit', 'create']
id: 'port', label:'{{ _('Port') }}', type: 'int', group: "Connection",
mode: ['properties', 'edit', 'create'], disabled: 'isConnected'
},{
id: 'db', label:'Maintenance Database', type: 'text', group: "Connection",
mode: ['properties', 'edit', 'create']
id: 'db', label:'{{ _('Maintenance Database') }}', type: 'text', group: "Connection",
mode: ['properties', 'edit', 'create'], disabled: 'isConnected'
},{
id: 'username', label:'User Name', type: 'text', group: "Connection",
mode: ['properties', 'edit', 'create']
id: 'username', label:'{{ _('User Name') }}', type: 'text', group: "Connection",
mode: ['properties', 'edit', 'create'], disabled: 'isConnected'
},{
id: 'sslmode', label:'SSL Mode', type: 'options', group: "Connection",
mode: ['properties', 'edit', 'create'],
'options': [{label:'Allow', value:'allow'}, {label: 'Prefer', value:'prefer'}, {label: 'Require', value: 'require'}, {label: 'Disable', value:'disable'}, {label:'Verify-CA', value: 'verify-ca'}, {label:'Verify-Full', value:'verify-full'}]
id: 'role', label:'{{ _('Role') }}', type: 'text', group: "Connection",
mode: ['properties', 'edit', 'create'], disabled: 'isConnected'
},{
id: 'sslmode', label:'{{ _('SSL Mode') }}', type: 'options', group: "Connection",
mode: ['properties', 'edit', 'create'], disabled: 'isConnected',
'options': [
{label: 'Allow', value: 'allow'},
{label: 'Prefer', value: 'prefer'},
{label: 'Require', value: 'require'},
{label: 'Disable', value: 'disable'},
{label: 'Verify-CA', value: 'verify-ca'},
{label: 'Verify-Full', value: 'verify-full'}
]
},{
id: 'server_type', label: '{{ _('Server Type') }}', type: 'options',
mode: ['properties'], show: 'isConnected',
'options': [{% set cnt = 1 %}{% for server_type in server_types %}{% if cnt != 1 %},{% endif %}
{label: '{{ server_type.description }}', value: '{{ server_type.type}}'}{% set cnt = cnt + 1 %}{% endfor %}
]
}],
validate: function(attrs, options) {
if (!this.isNew() && 'id' in this.changed) {
@@ -151,13 +195,145 @@ function($, _, pgAdmin, pgBrowser, alertify) {
}
return null;
},
isConnected: function(mode) {
return mode == 'properties' && this.get('connected');
isConnected: function(model) {
return model.get('connected');
}
})
});
function connect_to_server(obj, data, tree, item) {
var onFailure = function(xhr, status, error, _model, _data, _tree, _item) {
tree.setInode(_item);
tree.addIcon(_item, {icon: 'icon-server-not-connected'});
alertify.pgNotifier('error', xhr, error, function(msg) {
setTimeout(function() {
alertify.dlgServerPass(
'{{ _('Connect to Server') }}',
msg, _model, _data, _tree, _item
).resizeTo();
}, 100);
});
},
onSuccess = function(res, model, data, tree, item) {
tree.deselect(item);
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});
}
alertify.success(res.info);
setTimeout(function() {tree.select(item);}, 10);
setTimeout(function() {tree.open(item);}, 100);
}
};
// Ask Password and send it back to the connect server
if (!alertify.dlgServerPass) {
alertify.dialog('dlgServerPass', function factory() {
return {
main: function(title, message, model, data, tree, item) {
this.set('title', title);
this.message = message;
this.tree = tree;
this.nodeData = data;
this.nodeItem = item;
this.nodeModel = model;
},
setup:function() {
return {
buttons:[
{
text: "{{ _('OK') }}", key: 13, className: "btn btn-primary"
},
{
text: "{{ _('Cancel') }}", className: "btn btn-danger"
}
],
focus: { element: '#password', select: true },
options: {
modal: 0, resizable: false, maximizable: false, pinnable: false
}
};
},
build:function() {},
prepare:function() {
this.setContent(this.message);
},
callback: function(closeEvent) {
var _sdata = this.nodeData,
_tree = this.tree,
_item = this.nodeItem,
_model = this.nodeModel;
if (closeEvent.button.text == "{{ _('OK') }}") {
var _url = _model.generate_url('connect', _sdata, true);
_tree.setLeaf(_item);
_tree.removeIcon(_item);
_tree.addIcon(_item, {icon: 'icon-server-connecting'});
$.ajax({
type: 'POST',
timeout: 30000,
url: _url,
data: $('#frmPassword').serialize(),
success: function(res) {
return onSuccess(
res, _model, _sdata, _tree, _item
);
},
error: function(xhr, status, error) {
return onFailure(
xhr, status, error, _model, _sdata, _tree, _item
);
}
});
} else {
_tree.setInode(_item);
_tree.removeIcon(_item);
_tree.addIcon(_item, {icon: 'icon-server-not-connected'});
}
}
};
});
}
alertify.confirm(
'{{ _('Connect to server') }}',
'{{ _('Do you want to connect the server?') }}',
function(evt) {
url = obj.generate_url("connect", data, true);
$.post(url)
.done(
function(res) {
if (res.success == 1) {
return onSuccess(res, obj, data, tree, item);
}
})
.fail(
function(xhr, status, error) {
return onFailure(xhr, status, error, obj, data, tree, item);
});
},
notEditMode: function(mode) {
return mode != 'edit';
}})
});
function() {});
}
/* Send PING to indicate that session is alive */
function server_status(server_id)
{
url = "/ping";
$.post(url)
.done(function(data) { return true})
.fail(function(xhr, status, error) { return false})
}
}
return pgBrowser.Nodes['server'];