mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Fixed an issue where shared server entries not getting deleted from SQLite database if the user gets deleted. Fixes #6143
This commit is contained in:
parent
6683522491
commit
02c3863e8c
@ -26,6 +26,7 @@ Bug fixes
|
||||
| `Issue #5871 <https://redmine.postgresql.org/issues/5871>`_ - Ensure that username should be visible in the 'Connect to Server' popup when service and user name both specified.
|
||||
| `Issue #6045 <https://redmine.postgresql.org/issues/6045>`_ - Fixed autocomplete issue where it is not showing any suggestions if the schema name contains escape characters.
|
||||
| `Issue #6087 <https://redmine.postgresql.org/issues/6087>`_ - Fixed an issue where the dependencies tab showing multiple owners for the objects having shared dependencies.
|
||||
| `Issue #6143 <https://redmine.postgresql.org/issues/6143>`_ - Fixed an issue where shared server entries not getting deleted from SQLite database if the user gets deleted.
|
||||
| `Issue #6163 <https://redmine.postgresql.org/issues/6163>`_ - Fixed an issue where Zoom to fit button only works if the diagram is larger than the canvas.
|
||||
| `Issue #6164 <https://redmine.postgresql.org/issues/6164>`_ - Ensure that the diagram should not vanish entirely if zooming out too far in ERD.
|
||||
| `Issue #6177 <https://redmine.postgresql.org/issues/6177>`_ - Fixed an issue while downloading ERD images in Safari and Firefox.
|
||||
|
@ -177,6 +177,17 @@ class ServerGroupView(NodeView):
|
||||
# if server group id is 1 we won't delete it.
|
||||
sg = groups.first()
|
||||
|
||||
shared_servers = Server.query.filter_by(servergroup_id=sg.id,
|
||||
shared=True).all()
|
||||
if shared_servers:
|
||||
return make_json_response(
|
||||
status=417,
|
||||
success=0,
|
||||
errormsg=gettext(
|
||||
'The specified server group cannot be deleted.'
|
||||
)
|
||||
)
|
||||
|
||||
if sg.id == gid:
|
||||
return make_json_response(
|
||||
status=417,
|
||||
|
@ -100,352 +100,353 @@ export default function newConnectionDialogModel(response, sgid, sid, handler, c
|
||||
server_name: server_name,
|
||||
database_name: database_name,
|
||||
},
|
||||
schema: [{
|
||||
id: 'server',
|
||||
name: 'server',
|
||||
label: gettext('Server'),
|
||||
type: 'text',
|
||||
editable: true,
|
||||
disabled: false,
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: 'style',
|
||||
templateResult: formatNode,
|
||||
templateSelection: formatNode,
|
||||
},
|
||||
events: {
|
||||
'focus select': 'clearInvalid',
|
||||
'keydown :input': 'processTab',
|
||||
'select2:select': 'onSelect',
|
||||
'select2:selecting': 'beforeSelect',
|
||||
'select2:clear': 'onChange',
|
||||
},
|
||||
transform: function(data) {
|
||||
let group_template_options = [];
|
||||
for (let key in data) {
|
||||
if (data.hasOwnProperty(key)) {
|
||||
group_template_options.push({'group': key, 'optval': data[key]});
|
||||
}
|
||||
}
|
||||
return group_template_options;
|
||||
},
|
||||
control: Backform.Select2Control.extend({
|
||||
template: _.template([
|
||||
'<% if(label == false) {} else {%>',
|
||||
' <label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
||||
'<% }%>',
|
||||
'<div class="<%=controlsClassName%>">',
|
||||
' <select class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>"',
|
||||
' name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%>',
|
||||
' <%=required ? "required" : ""%><%= select2.multiple ? " multiple>" : ">" %>',
|
||||
' <%=select2.first_empty ? " <option></option>" : ""%>',
|
||||
' <% for (var i=0; i < options.length; i++) {%>',
|
||||
' <% if (options[i].group) { %>',
|
||||
' <% var group = options[i].group; %>',
|
||||
' <% if (options[i].optval) { %> <% var option_length = options[i].optval.length; %>',
|
||||
' <optgroup label="<%=group%>">',
|
||||
' <% for (var subindex=0; subindex < option_length; subindex++) {%>',
|
||||
' <% var option = options[i].optval[subindex]; %>',
|
||||
' <option ',
|
||||
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
|
||||
' <% if (option.connected) { %> data-connected=connected <%}%>',
|
||||
' value=<%- formatter.fromRaw(option.value) %>',
|
||||
' <% if (option.selected) {%>selected="selected"<%} else {%>',
|
||||
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
|
||||
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
|
||||
' <%}%>',
|
||||
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
|
||||
' <%}%>',
|
||||
' </optgroup>',
|
||||
' <%}%>',
|
||||
' <%} else {%>',
|
||||
' <% var option = options[i]; %>',
|
||||
' <option ',
|
||||
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
|
||||
' <% if (option.connected) { %> data-connected=connected <%}%>',
|
||||
' value=<%- formatter.fromRaw(option.value) %>',
|
||||
' <% if (option.selected) {%>selected="selected"<%} else {%>',
|
||||
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
|
||||
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
|
||||
' <%}%>',
|
||||
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
|
||||
' <%}%>',
|
||||
' <%}%>',
|
||||
' </select>',
|
||||
' <% if (helpMessage && helpMessage.length) { %>',
|
||||
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
||||
' <% } %>',
|
||||
'</div>',
|
||||
].join('\n')),
|
||||
beforeSelect: function() {
|
||||
var selVal = arguments[0].params.args.data.id;
|
||||
|
||||
if(this.field.get('connect') && this.$el.find('option[value="'+selVal+'"]').attr('data-connected') !== 'connected') {
|
||||
this.field.get('connect').apply(this, [selVal, this.changeIcon.bind(this)]);
|
||||
} else {
|
||||
$(this.$sel).trigger('change');
|
||||
setTimeout(function(){ this.onChange.apply(this); }.bind(this), 200);
|
||||
}
|
||||
schema: [
|
||||
{
|
||||
id: 'server',
|
||||
name: 'server',
|
||||
label: gettext('Server'),
|
||||
type: 'text',
|
||||
editable: true,
|
||||
disabled: false,
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: 'style',
|
||||
templateResult: formatNode,
|
||||
templateSelection: formatNode,
|
||||
},
|
||||
changeIcon: function(data) {
|
||||
let span = this.$el.find('.select2-selection .select2-selection__rendered span.wcTabIcon'),
|
||||
selSpan = this.$el.find('option:selected');
|
||||
|
||||
if (span.hasClass('icon-server-not-connected') || span.hasClass('icon-shared-server-not-connected')) {
|
||||
let icon = (data.icon) ? data.icon : 'icon-pg';
|
||||
span.removeClass('icon-server-not-connected');
|
||||
span.addClass(icon);
|
||||
span.attr('data-connected', 'connected');
|
||||
|
||||
selSpan.data().image = icon;
|
||||
selSpan.attr('data-connected', 'connected');
|
||||
alertify.connectServer().destroy();
|
||||
this.onChange.apply(this);
|
||||
}
|
||||
else if (span.hasClass('icon-database-not-connected')) {
|
||||
let icon = (data.icon) ? data.icon : 'pg-icon-database';
|
||||
|
||||
span.removeClass('icon-database-not-connected');
|
||||
span.addClass(icon);
|
||||
span.attr('data-connected', 'connected');
|
||||
|
||||
selSpan.removeClass('icon-database-not-connected');
|
||||
selSpan.data().image = icon;
|
||||
selSpan.attr('data-connected', 'connected');
|
||||
alertify.connectServer().destroy();
|
||||
this.onChange.apply(this);
|
||||
}
|
||||
events: {
|
||||
'focus select': 'clearInvalid',
|
||||
'keydown :input': 'processTab',
|
||||
'select2:select': 'onSelect',
|
||||
'select2:selecting': 'beforeSelect',
|
||||
'select2:clear': 'onChange',
|
||||
},
|
||||
connect: function(self) {
|
||||
let local_self = self;
|
||||
|
||||
if(alertify.connectServer) {
|
||||
delete alertify.connectServer;
|
||||
transform: function(data) {
|
||||
let group_template_options = [];
|
||||
for (let key in data) {
|
||||
if (data.hasOwnProperty(key)) {
|
||||
group_template_options.push({'group': key, 'optval': data[key]});
|
||||
}
|
||||
}
|
||||
return group_template_options;
|
||||
},
|
||||
control: Backform.Select2Control.extend({
|
||||
template: _.template([
|
||||
'<% if(label == false) {} else {%>',
|
||||
' <label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
||||
'<% }%>',
|
||||
'<div class="<%=controlsClassName%>">',
|
||||
' <select class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>"',
|
||||
' name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%>',
|
||||
' <%=required ? "required" : ""%><%= select2.multiple ? " multiple>" : ">" %>',
|
||||
' <%=select2.first_empty ? " <option></option>" : ""%>',
|
||||
' <% for (var i=0; i < options.length; i++) {%>',
|
||||
' <% if (options[i].group) { %>',
|
||||
' <% var group = options[i].group; %>',
|
||||
' <% if (options[i].optval) { %> <% var option_length = options[i].optval.length; %>',
|
||||
' <optgroup label="<%=group%>">',
|
||||
' <% for (var subindex=0; subindex < option_length; subindex++) {%>',
|
||||
' <% var option = options[i].optval[subindex]; %>',
|
||||
' <option ',
|
||||
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
|
||||
' <% if (option.connected) { %> data-connected=connected <%}%>',
|
||||
' value=<%- formatter.fromRaw(option.value) %>',
|
||||
' <% if (option.selected) {%>selected="selected"<%} else {%>',
|
||||
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
|
||||
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
|
||||
' <%}%>',
|
||||
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
|
||||
' <%}%>',
|
||||
' </optgroup>',
|
||||
' <%}%>',
|
||||
' <%} else {%>',
|
||||
' <% var option = options[i]; %>',
|
||||
' <option ',
|
||||
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
|
||||
' <% if (option.connected) { %> data-connected=connected <%}%>',
|
||||
' value=<%- formatter.fromRaw(option.value) %>',
|
||||
' <% if (option.selected) {%>selected="selected"<%} else {%>',
|
||||
' <% if (!select2.multiple && option.value === rawValue) {%>selected="selected"<%}%>',
|
||||
' <% if (select2.multiple && rawValue && rawValue.indexOf(option.value) != -1){%>selected="selected" data-index="rawValue.indexOf(option.value)"<%}%>',
|
||||
' <%}%>',
|
||||
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
|
||||
' <%}%>',
|
||||
' <%}%>',
|
||||
' </select>',
|
||||
' <% if (helpMessage && helpMessage.length) { %>',
|
||||
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
||||
' <% } %>',
|
||||
'</div>',
|
||||
].join('\n')),
|
||||
beforeSelect: function() {
|
||||
var selVal = arguments[0].params.args.data.id;
|
||||
|
||||
alertify.dialog('connectServer', function factory() {
|
||||
return {
|
||||
main: function(
|
||||
title, message, server_id, submit_password=true, connect_server=null,
|
||||
) {
|
||||
this.set('title', title);
|
||||
this.message = message;
|
||||
this.server_id = server_id;
|
||||
this.submit_password = submit_password;
|
||||
this.connect_server = connect_server;
|
||||
},
|
||||
setup:function() {
|
||||
return {
|
||||
buttons:[{
|
||||
text: gettext('Cancel'), className: 'btn btn-secondary fa fa-times pg-alertify-button',
|
||||
key: 27,
|
||||
},{
|
||||
text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-check pg-alertify-button',
|
||||
}],
|
||||
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 loadingDiv = $('#show_filter_progress');
|
||||
if (closeEvent.button.text == gettext('OK')) {
|
||||
if(this.submit_password) {
|
||||
var _url = url_for('sqleditor.connect_server', {'sid': this.server_id});
|
||||
if(this.field.get('connect') && this.$el.find('option[value="'+selVal+'"]').attr('data-connected') !== 'connected') {
|
||||
this.field.get('connect').apply(this, [selVal, this.changeIcon.bind(this)]);
|
||||
} else {
|
||||
$(this.$sel).trigger('change');
|
||||
setTimeout(function(){ this.onChange.apply(this); }.bind(this), 200);
|
||||
}
|
||||
},
|
||||
changeIcon: function(data) {
|
||||
let span = this.$el.find('.select2-selection .select2-selection__rendered span.wcTabIcon'),
|
||||
selSpan = this.$el.find('option:selected');
|
||||
|
||||
loadingDiv.removeClass('d-none');
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
timeout: 30000,
|
||||
url: _url,
|
||||
data: $('#frmPassword').serialize(),
|
||||
})
|
||||
.done(function(res) {
|
||||
if (span.hasClass('icon-server-not-connected') || span.hasClass('icon-shared-server-not-connected')) {
|
||||
let icon = (data.icon) ? data.icon : 'icon-pg';
|
||||
span.removeClass('icon-server-not-connected');
|
||||
span.addClass(icon);
|
||||
span.attr('data-connected', 'connected');
|
||||
|
||||
local_self.model.attributes.database = null;
|
||||
local_self.changeIcon(res.data);
|
||||
local_self.model.attributes.user = null;
|
||||
local_self.model.attributes.role = null;
|
||||
Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
|
||||
Object.keys(response.server_list).forEach(key => {
|
||||
response.server_list[key].forEach(option => {
|
||||
if (option.value == local_self.getValueFromDOM()) {
|
||||
response.server_name = option.label;
|
||||
}
|
||||
});
|
||||
});
|
||||
selSpan.data().image = icon;
|
||||
selSpan.attr('data-connected', 'connected');
|
||||
alertify.connectServer().destroy();
|
||||
this.onChange.apply(this);
|
||||
}
|
||||
else if (span.hasClass('icon-database-not-connected')) {
|
||||
let icon = (data.icon) ? data.icon : 'pg-icon-database';
|
||||
|
||||
loadingDiv.addClass('d-none');
|
||||
alertify.connectServer().destroy();
|
||||
span.removeClass('icon-database-not-connected');
|
||||
span.addClass(icon);
|
||||
span.attr('data-connected', 'connected');
|
||||
|
||||
selSpan.removeClass('icon-database-not-connected');
|
||||
selSpan.data().image = icon;
|
||||
selSpan.attr('data-connected', 'connected');
|
||||
alertify.connectServer().destroy();
|
||||
this.onChange.apply(this);
|
||||
}
|
||||
},
|
||||
connect: function(self) {
|
||||
let local_self = self;
|
||||
|
||||
if(alertify.connectServer) {
|
||||
delete alertify.connectServer;
|
||||
}
|
||||
|
||||
alertify.dialog('connectServer', function factory() {
|
||||
return {
|
||||
main: function(
|
||||
title, message, server_id, submit_password=true, connect_server=null,
|
||||
) {
|
||||
this.set('title', title);
|
||||
this.message = message;
|
||||
this.server_id = server_id;
|
||||
this.submit_password = submit_password;
|
||||
this.connect_server = connect_server;
|
||||
},
|
||||
setup:function() {
|
||||
return {
|
||||
buttons:[{
|
||||
text: gettext('Cancel'), className: 'btn btn-secondary fa fa-times pg-alertify-button',
|
||||
key: 27,
|
||||
},{
|
||||
text: gettext('OK'), key: 13, className: 'btn btn-primary fa fa-check pg-alertify-button',
|
||||
}],
|
||||
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 loadingDiv = $('#show_filter_progress');
|
||||
if (closeEvent.button.text == gettext('OK')) {
|
||||
if(this.submit_password) {
|
||||
var _url = url_for('sqleditor.connect_server', {'sid': this.server_id});
|
||||
|
||||
loadingDiv.removeClass('d-none');
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
timeout: 30000,
|
||||
url: _url,
|
||||
data: $('#frmPassword').serialize(),
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
loadingDiv.addClass('d-none');
|
||||
alertify.connectServer().destroy();
|
||||
alertify.connectServer('Connect to server', xhr.responseJSON.result, local_self.getValueFromDOM());
|
||||
});
|
||||
} else {
|
||||
if(Object.keys(this.connect_server).length > 0) {
|
||||
this.connect_server['password'] = $('#password').val();
|
||||
this.connect_server['is_selected'] = false;
|
||||
handler.gridView.on_change_connection(this.connect_server, conn_self, false);
|
||||
loadingDiv = $('#fetching_data');
|
||||
loadingDiv.addClass('d-none');
|
||||
.done(function(res) {
|
||||
|
||||
local_self.model.attributes.database = null;
|
||||
local_self.changeIcon(res.data);
|
||||
local_self.model.attributes.user = null;
|
||||
local_self.model.attributes.role = null;
|
||||
Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
|
||||
Object.keys(response.server_list).forEach(key => {
|
||||
response.server_list[key].forEach(option => {
|
||||
if (option.value == local_self.getValueFromDOM()) {
|
||||
response.server_name = option.label;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
loadingDiv.addClass('d-none');
|
||||
alertify.connectServer().destroy();
|
||||
})
|
||||
.fail(function(xhr) {
|
||||
loadingDiv.addClass('d-none');
|
||||
alertify.connectServer().destroy();
|
||||
alertify.connectServer('Connect to server', xhr.responseJSON.result, local_self.getValueFromDOM());
|
||||
});
|
||||
} else {
|
||||
response.password = $('#password').val();
|
||||
loadingDiv.addClass('d-none');
|
||||
if(Object.keys(this.connect_server).length > 0) {
|
||||
this.connect_server['password'] = $('#password').val();
|
||||
this.connect_server['is_selected'] = false;
|
||||
handler.gridView.on_change_connection(this.connect_server, conn_self, false);
|
||||
loadingDiv = $('#fetching_data');
|
||||
loadingDiv.addClass('d-none');
|
||||
} else {
|
||||
response.password = $('#password').val();
|
||||
loadingDiv.addClass('d-none');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
local_self.model.attributes.database = null;
|
||||
local_self.model.attributes.user = null;
|
||||
local_self.model.attributes.role = null;
|
||||
Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
|
||||
loadingDiv.addClass('d-none');
|
||||
alertify.connectServer().destroy();
|
||||
|
||||
}
|
||||
} else {
|
||||
local_self.model.attributes.database = null;
|
||||
local_self.model.attributes.user = null;
|
||||
local_self.model.attributes.role = null;
|
||||
Backform.Select2Control.prototype.onChange.apply(local_self, arguments);
|
||||
loadingDiv.addClass('d-none');
|
||||
alertify.connectServer().destroy();
|
||||
|
||||
}
|
||||
closeEvent.close = true;
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
let self = this;
|
||||
self.connect(self);
|
||||
Object.keys(response.server_list).forEach(key => {
|
||||
response.server_list[key].forEach(option => {
|
||||
if (option.value == parseInt(sid)) {
|
||||
response.server_name = option.label;
|
||||
}
|
||||
closeEvent.close = true;
|
||||
},
|
||||
};
|
||||
});
|
||||
});
|
||||
var transform = self.field.get('transform') || self.defaults.transform;
|
||||
if (transform && _.isFunction(transform)) {
|
||||
self.field.set('options', transform.bind(self, response.server_list));
|
||||
} else {
|
||||
self.field.set('options', response.server_list);
|
||||
}
|
||||
return Backform.Select2Control.prototype.render.apply(self, arguments);
|
||||
},
|
||||
onChange: function() {
|
||||
this.model.attributes.database = null;
|
||||
this.model.attributes.user = null;
|
||||
let self = this;
|
||||
self.connect(self);
|
||||
|
||||
let url = url_for('sqleditor.connect_server', {
|
||||
'sid': self.getValueFromDOM(),
|
||||
'usr': self.model.attributes.user,
|
||||
});
|
||||
var loadingDiv = $('#show_filter_progress');
|
||||
loadingDiv.removeClass('d-none');
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
headers: {
|
||||
'Cache-Control' : 'no-cache',
|
||||
},
|
||||
}).done(function () {
|
||||
Backform.Select2Control.prototype.onChange.apply(self, arguments);
|
||||
},
|
||||
render: function() {
|
||||
let self = this;
|
||||
self.connect(self);
|
||||
Object.keys(response.server_list).forEach(key => {
|
||||
response.server_list[key].forEach(option => {
|
||||
if (option.value == self.getValueFromDOM()) {
|
||||
if (option.value == parseInt(sid)) {
|
||||
response.server_name = option.label;
|
||||
}
|
||||
});
|
||||
});
|
||||
loadingDiv.addClass('d-none');
|
||||
}).fail(function(xhr){
|
||||
loadingDiv.addClass('d-none');
|
||||
alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
|
||||
});
|
||||
var transform = self.field.get('transform') || self.defaults.transform;
|
||||
if (transform && _.isFunction(transform)) {
|
||||
self.field.set('options', transform.bind(self, response.server_list));
|
||||
} else {
|
||||
self.field.set('options', response.server_list);
|
||||
}
|
||||
return Backform.Select2Control.prototype.render.apply(self, arguments);
|
||||
},
|
||||
onChange: function() {
|
||||
this.model.attributes.database = null;
|
||||
this.model.attributes.user = null;
|
||||
let self = this;
|
||||
self.connect(self);
|
||||
|
||||
let url = url_for('sqleditor.connect_server', {
|
||||
'sid': self.getValueFromDOM(),
|
||||
'usr': self.model.attributes.user,
|
||||
});
|
||||
var loadingDiv = $('#show_filter_progress');
|
||||
loadingDiv.removeClass('d-none');
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
headers: {
|
||||
'Cache-Control' : 'no-cache',
|
||||
},
|
||||
}).done(function () {
|
||||
Backform.Select2Control.prototype.onChange.apply(self, arguments);
|
||||
Object.keys(response.server_list).forEach(key => {
|
||||
response.server_list[key].forEach(option => {
|
||||
if (option.value == self.getValueFromDOM()) {
|
||||
response.server_name = option.label;
|
||||
}
|
||||
});
|
||||
});
|
||||
loadingDiv.addClass('d-none');
|
||||
}).fail(function(xhr){
|
||||
loadingDiv.addClass('d-none');
|
||||
alertify.connectServer('Connect to server', xhr.responseJSON.result, self.getValueFromDOM());
|
||||
});
|
||||
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'database',
|
||||
name: 'database',
|
||||
label: gettext('Database'),
|
||||
type: 'text',
|
||||
editable: true,
|
||||
disabled: function(m) {
|
||||
let self_local = this;
|
||||
if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
|
||||
&& m.get('server') !== '') {
|
||||
setTimeout(function() {
|
||||
if(self_local.options.length) {
|
||||
m.set('database', self_local.options[0].value);
|
||||
}
|
||||
}, 10);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
},
|
||||
{
|
||||
id: 'database',
|
||||
name: 'database',
|
||||
label: gettext('Database'),
|
||||
type: 'text',
|
||||
editable: true,
|
||||
disabled: function(m) {
|
||||
let self_local = this;
|
||||
if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
|
||||
deps: ['server'],
|
||||
url: 'sqleditor.get_new_connection_database',
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: '100%',
|
||||
first_empty: true,
|
||||
select_first: false,
|
||||
},
|
||||
extraClasses:['new-connection-dialog-style'],
|
||||
control: NewConnectionSelect2Control,
|
||||
transform: function(data) {
|
||||
response.database_list = data;
|
||||
return data;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'user',
|
||||
name: 'user',
|
||||
label: gettext('User'),
|
||||
type: 'text',
|
||||
editable: true,
|
||||
deps: ['server'],
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: '100%',
|
||||
},
|
||||
control: NewConnectionSelect2Control,
|
||||
url: 'sqleditor.get_new_connection_user',
|
||||
disabled: function(m) {
|
||||
let self_local = this;
|
||||
if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
|
||||
&& m.get('server') !== '') {
|
||||
setTimeout(function() {
|
||||
if(self_local.options.length) {
|
||||
m.set('database', self_local.options[0].value);
|
||||
}
|
||||
}, 10);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
setTimeout(function() {
|
||||
if(self_local.options.length) {
|
||||
m.set('user', self_local.options[0].value);
|
||||
}
|
||||
}, 10);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},{
|
||||
id: 'role',
|
||||
name: 'role',
|
||||
label: gettext('Role'),
|
||||
type: 'text',
|
||||
editable: true,
|
||||
deps: ['server'],
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: '100%',
|
||||
first_empty: true,
|
||||
},
|
||||
control: NewConnectionSelect2Control,
|
||||
url: 'sqleditor.get_new_connection_role',
|
||||
disabled: false,
|
||||
},
|
||||
deps: ['server'],
|
||||
url: 'sqleditor.get_new_connection_database',
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: '100%',
|
||||
first_empty: true,
|
||||
select_first: false,
|
||||
},
|
||||
extraClasses:['new-connection-dialog-style'],
|
||||
control: NewConnectionSelect2Control,
|
||||
transform: function(data) {
|
||||
response.database_list = data;
|
||||
return data;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'user',
|
||||
name: 'user',
|
||||
label: gettext('User'),
|
||||
type: 'text',
|
||||
editable: true,
|
||||
deps: ['server'],
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: '100%',
|
||||
},
|
||||
control: NewConnectionSelect2Control,
|
||||
url: 'sqleditor.get_new_connection_user',
|
||||
disabled: function(m) {
|
||||
let self_local = this;
|
||||
if (!_.isUndefined(m.get('server')) && !_.isNull(m.get('server'))
|
||||
&& m.get('server') !== '') {
|
||||
setTimeout(function() {
|
||||
if(self_local.options.length) {
|
||||
m.set('user', self_local.options[0].value);
|
||||
}
|
||||
}, 10);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
},{
|
||||
id: 'role',
|
||||
name: 'role',
|
||||
label: gettext('Role'),
|
||||
type: 'text',
|
||||
editable: true,
|
||||
deps: ['server'],
|
||||
select2: {
|
||||
allowClear: false,
|
||||
width: '100%',
|
||||
first_empty: true,
|
||||
},
|
||||
control: NewConnectionSelect2Control,
|
||||
url: 'sqleditor.get_new_connection_role',
|
||||
disabled: false,
|
||||
},
|
||||
],
|
||||
validate: function() {
|
||||
let msg = null;
|
||||
|
@ -28,7 +28,7 @@ from pgadmin.utils.constants import MIMETYPE_APP_JS, INTERNAL,\
|
||||
SUPPORTED_AUTH_SOURCES, KERBEROS
|
||||
from pgadmin.utils.validation_utils import validate_email
|
||||
from pgadmin.model import db, Role, User, UserPreference, Server, \
|
||||
ServerGroup, Process, Setting
|
||||
ServerGroup, Process, Setting, roles_users, SharedServer
|
||||
|
||||
# set template path for sql scripts
|
||||
MODULE_NAME = 'user_management'
|
||||
@ -78,7 +78,9 @@ class UserManagementModule(PgAdminModule):
|
||||
'user_management.update_user', 'user_management.delete_user',
|
||||
'user_management.create_user', 'user_management.users',
|
||||
'user_management.user', current_app.login_manager.login_view,
|
||||
'user_management.auth_sources', 'user_management.auth_sources'
|
||||
'user_management.auth_sources', 'user_management.auth_sources',
|
||||
'user_management.shared_servers', 'user_management.admin_users',
|
||||
'user_management.change_owner',
|
||||
]
|
||||
|
||||
|
||||
@ -336,6 +338,8 @@ def delete(uid):
|
||||
abort(404)
|
||||
|
||||
try:
|
||||
server_groups = ServerGroup.query.filter_by(user_id=uid).all()
|
||||
sg = [server_group.id for server_group in server_groups]
|
||||
|
||||
Setting.query.filter_by(user_id=uid).delete()
|
||||
|
||||
@ -346,6 +350,11 @@ def delete(uid):
|
||||
ServerGroup.query.filter_by(user_id=uid).delete()
|
||||
|
||||
Process.query.filter_by(user_id=uid).delete()
|
||||
# Delete Shared servers for current user.
|
||||
SharedServer.query.filter_by(user_id=uid).delete()
|
||||
|
||||
SharedServer.query.filter(SharedServer.servergroup_id.in_(sg)).delete(
|
||||
synchronize_session=False)
|
||||
|
||||
# Finally delete user
|
||||
db.session.delete(usr)
|
||||
@ -361,6 +370,174 @@ def delete(uid):
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
|
||||
@blueprint.route('/change_owner', methods=['POST'], endpoint='change_owner')
|
||||
@roles_required('Administrator')
|
||||
def change_owner():
|
||||
"""
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
|
||||
data = request.form if request.form else json.loads(
|
||||
request.data, encoding='utf-8'
|
||||
)
|
||||
try:
|
||||
old_user_servers = Server.query.filter_by(shared=True, user_id=data[
|
||||
'old_owner']).all()
|
||||
server_group_ids = [server.servergroup_id for server in
|
||||
old_user_servers]
|
||||
server_groups = ServerGroup.query.filter(
|
||||
ServerGroup.id.in_(server_group_ids)).all()
|
||||
|
||||
new_owner_sg = ServerGroup.query.filter_by(
|
||||
user_id=data['new_owner']).all()
|
||||
old_owner_sg = ServerGroup.query.filter_by(
|
||||
user_id=data['old_owner']).all()
|
||||
sg_data = {sg.name: sg.id for sg in new_owner_sg}
|
||||
old_sg_data = {sg.id: sg.name for sg in old_owner_sg}
|
||||
|
||||
deleted_sg = []
|
||||
# Change server user.
|
||||
for server in old_user_servers:
|
||||
if old_sg_data[server.servergroup_id] in sg_data:
|
||||
sh_servers = SharedServer.query.filter_by(
|
||||
servergroup_id=server.servergroup_id).all()
|
||||
for sh in sh_servers:
|
||||
sh.servergroup_id = sg_data[
|
||||
old_sg_data[server.servergroup_id]]
|
||||
# Update Server user and server group to prevent deleting
|
||||
# shared server associated with deleting user.
|
||||
Server.query.filter_by(
|
||||
servergroup_id=server.servergroup_id, shared=True,
|
||||
user_id=data['old_owner']
|
||||
).update(
|
||||
{
|
||||
'servergroup_id': sg_data[old_sg_data[
|
||||
server.servergroup_id]],
|
||||
'user_id': data['new_owner']
|
||||
}
|
||||
)
|
||||
ServerGroup.query.filter_by(id=server.servergroup_id).delete()
|
||||
deleted_sg.append(server.servergroup_id)
|
||||
else:
|
||||
server.user_id = data['new_owner']
|
||||
|
||||
# Change server group user.
|
||||
for server_group in server_groups:
|
||||
if server_group.id not in deleted_sg:
|
||||
server_group.user_id = data['new_owner']
|
||||
|
||||
db.session.commit()
|
||||
# Delete old owner records.
|
||||
delete(data['old_owner'])
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=_("Owner changed successfully."),
|
||||
data={}
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(
|
||||
errormsg='Unable to update shared server owner')
|
||||
|
||||
|
||||
@blueprint.route(
|
||||
'/shared_servers/<int:uid>', methods=['GET'], endpoint='shared_servers'
|
||||
)
|
||||
@roles_required('Administrator')
|
||||
def get_shared_servers(uid):
|
||||
"""
|
||||
|
||||
Args:
|
||||
uid:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
usr = User.query.get(uid)
|
||||
|
||||
if not usr:
|
||||
abort(404)
|
||||
|
||||
try:
|
||||
shared_servers_count = 0
|
||||
admin_role = Role.query.filter_by(name='Administrator')[0]
|
||||
# Check user has admin role.
|
||||
for role in usr.roles:
|
||||
if role.id == admin_role.id:
|
||||
# get all server created by user.
|
||||
servers = Server.query.filter_by(user_id=usr.id).all()
|
||||
for server in servers:
|
||||
if server.shared:
|
||||
shared_servers_count += 1
|
||||
break
|
||||
|
||||
if shared_servers_count:
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=_(
|
||||
"{0} Shared servers are associated with this user."
|
||||
"".format(shared_servers_count)
|
||||
),
|
||||
data={
|
||||
'shared_servers': shared_servers_count
|
||||
}
|
||||
)
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=_("No shared servers found"),
|
||||
data={'shared_servers': 0}
|
||||
)
|
||||
except Exception as e:
|
||||
return internal_server_error(errormsg=str(e))
|
||||
|
||||
|
||||
# @blueprint.route(
|
||||
# '/admin_users', methods=['GET'], endpoint='admin_users'
|
||||
# )
|
||||
@blueprint.route(
|
||||
'/admin_users/<int:uid>', methods=['GET'], endpoint='admin_users'
|
||||
)
|
||||
@roles_required('Administrator')
|
||||
def admin_users(uid=None):
|
||||
"""
|
||||
|
||||
Args:
|
||||
uid:
|
||||
|
||||
Returns:
|
||||
|
||||
"""
|
||||
admin_role = Role.query.filter_by(name='Administrator')[0]
|
||||
|
||||
admin_users = db.session.query(roles_users).filter_by(
|
||||
role_id=admin_role.id).all()
|
||||
|
||||
if uid:
|
||||
admin_users = [user[0] for user in admin_users if user[0] != uid]
|
||||
else:
|
||||
admin_users = [user[0] for user in admin_users]
|
||||
|
||||
admin_list = User.query.filter(User.id.in_(admin_users)).all()
|
||||
|
||||
user_list = [{'value': admin.id, 'label': admin.username} for admin in
|
||||
admin_list]
|
||||
|
||||
return make_json_response(
|
||||
success=1,
|
||||
info=_("No shared servers found"),
|
||||
data={
|
||||
'status': 'success',
|
||||
'msg': 'Admin user list',
|
||||
'result': {
|
||||
'data': user_list,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@blueprint.route('/user/<int:uid>', methods=['PUT'], endpoint='update_user')
|
||||
@roles_required('Administrator')
|
||||
def update(uid):
|
||||
|
@ -604,7 +604,223 @@ define([
|
||||
}),
|
||||
gridSchema = Backform.generateGridColumnsFromModel(
|
||||
null, UserModel, 'edit'),
|
||||
|
||||
|
||||
deleteUserCell = Backgrid.Extension.DeleteCell.extend({
|
||||
changeOwnership: function(res, uid) {
|
||||
let self = this;
|
||||
|
||||
let ownershipSelect2Control = Backform.Select2Control.extend({
|
||||
fetchData: function(){
|
||||
let self = this;
|
||||
let url = self.field.get('url');
|
||||
|
||||
url = url_for(url, {'uid': uid});
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
headers: {
|
||||
'Cache-Control' : 'no-cache',
|
||||
},
|
||||
}).done(function (res) {
|
||||
var transform = self.field.get('transform');
|
||||
if(res.data.status){
|
||||
let data = res.data.result.data;
|
||||
|
||||
if (transform && _.isFunction(transform)) {
|
||||
self.field.set('options', transform.bind(self, data));
|
||||
} else {
|
||||
self.field.set('options', data);
|
||||
}
|
||||
} else {
|
||||
if (transform && _.isFunction(transform)) {
|
||||
self.field.set('options', transform.bind(self, []));
|
||||
} else {
|
||||
self.field.set('options', []);
|
||||
}
|
||||
}
|
||||
Backform.Select2Control.prototype.render.apply(self, arguments);
|
||||
}).fail(function(e){
|
||||
let msg = '';
|
||||
if(e.status == 404) {
|
||||
msg = 'Unable to find url.';
|
||||
} else {
|
||||
msg = e.responseJSON.errormsg;
|
||||
}
|
||||
alertify.error(msg);
|
||||
});
|
||||
},
|
||||
render: function() {
|
||||
this.fetchData();
|
||||
return Backform.Select2Control.prototype.render.apply(this, arguments);
|
||||
},
|
||||
onChange: function() {
|
||||
Backform.Select2Control.prototype.onChange.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
|
||||
let ownershipModel = pgBrowser.DataModel.extend({
|
||||
schema: [
|
||||
{
|
||||
id: 'note_text_ch_owner',
|
||||
control: Backform.NoteControl,
|
||||
text: 'Select the user that will take ownership of the shared servers created by <b>' + self.model.get('username') + '</b>. <b>' + res['data'].shared_servers + '</b> shared servers are currently owned by this user.',
|
||||
group: gettext('General'),
|
||||
},
|
||||
{
|
||||
id: 'user',
|
||||
name: 'user',
|
||||
label: gettext('User'),
|
||||
type: 'text',
|
||||
editable: true,
|
||||
select2: {
|
||||
allowClear: true,
|
||||
width: '100%',
|
||||
first_empty: true,
|
||||
},
|
||||
control: ownershipSelect2Control,
|
||||
url: 'user_management.admin_users',
|
||||
helpMessage: gettext('Note: If no user is selected, the shared servers will be deleted.'),
|
||||
}],
|
||||
});
|
||||
// Change shared server ownership before deleting the admin user
|
||||
if (!alertify.changeOwnershipDialog) {
|
||||
alertify.dialog('changeOwnershipDialog', function factory() {
|
||||
let $container = $('<div class=\'change-ownership\'></div>');
|
||||
return {
|
||||
main: function(message) {
|
||||
this.msg = message;
|
||||
},
|
||||
build: function() {
|
||||
this.elements.content.appendChild($container.get(0));
|
||||
alertify.pgDialogBuild.apply(this);
|
||||
},
|
||||
setup: function(){
|
||||
return {
|
||||
buttons: [
|
||||
{
|
||||
text: gettext('Cancel'),
|
||||
key: 27,
|
||||
className: 'btn btn-secondary fa fa-times pg-alertify-button',
|
||||
'data-btn-name': 'cancel',
|
||||
}, {
|
||||
text: gettext('OK'),
|
||||
key: 13,
|
||||
className: 'btn btn-primary fa fa-check pg-alertify-button',
|
||||
'data-btn-name': 'ok',
|
||||
},
|
||||
],
|
||||
// Set options for dialog
|
||||
options: {
|
||||
title: 'Change ownership',
|
||||
//disable both padding and overflow control.
|
||||
padding: !1,
|
||||
overflow: !1,
|
||||
model: 0,
|
||||
resizable: true,
|
||||
maximizable: false,
|
||||
pinnable: false,
|
||||
closableByDimmer: false,
|
||||
modal: false,
|
||||
autoReset: false,
|
||||
closable: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
prepare: function() {
|
||||
let self = this;
|
||||
$container.html('');
|
||||
|
||||
self.ownershipModel = new ownershipModel();
|
||||
let fields = pgBackform.generateViewSchema(null, self.ownershipModel, 'create', null, null, true, null);
|
||||
|
||||
let view = this.view = new pgBackform.Dialog({
|
||||
el: '<div></div>',
|
||||
model: self.ownershipModel,
|
||||
schema: fields,
|
||||
});
|
||||
//Render change ownership dialog.
|
||||
$container.append(view.render().$el[0]);
|
||||
},
|
||||
callback: function(e) {
|
||||
if(e.button['data-btn-name'] === 'ok') {
|
||||
e.cancel = true; // Do not close dialog
|
||||
let ownershipModel = this.ownershipModel.toJSON();
|
||||
if (ownershipModel.user == '' || ownershipModel.user == undefined) {
|
||||
alertify.confirm(
|
||||
gettext('Delete user?'),
|
||||
gettext('The shared servers owned by <b>'+ self.model.get('username') +'</b> will be deleted. Do you wish to continue?'),
|
||||
function() {
|
||||
|
||||
self.model.destroy({
|
||||
wait: true,
|
||||
success: function() {
|
||||
alertify.success(gettext('User deleted.'));
|
||||
alertify.changeOwnershipDialog().destroy();
|
||||
alertify.UserManagement().destroy();
|
||||
},
|
||||
error: function() {
|
||||
alertify.error(
|
||||
gettext('Error during deleting user.')
|
||||
);
|
||||
},
|
||||
});
|
||||
alertify.changeOwnershipDialog().destroy();
|
||||
},
|
||||
function() {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
self.changeOwner(ownershipModel.user, uid);
|
||||
}
|
||||
} else {
|
||||
alertify.changeOwnershipDialog().destroy();
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
alertify.changeOwnershipDialog('Change ownership').resizeTo(pgBrowser.stdW.md, pgBrowser.stdH.md);
|
||||
},
|
||||
changeOwner: function(user_id, old_user) {
|
||||
$.ajax({
|
||||
url: url_for('user_management.change_owner'),
|
||||
method: 'POST',
|
||||
data:{'new_owner': user_id, 'old_owner': old_user},
|
||||
})
|
||||
.done(function(res) {
|
||||
alertify.changeOwnershipDialog().destroy();
|
||||
alertify.UserManagement().destroy();
|
||||
alertify.success(gettext(res.info));
|
||||
})
|
||||
.fail(function() {
|
||||
alertify.error(gettext('Unable to change owner.'));
|
||||
});
|
||||
},
|
||||
deleteUser: function() {
|
||||
let self = this;
|
||||
alertify.confirm(
|
||||
gettext('Delete user?'),
|
||||
gettext('Are you sure you wish to delete this user?'),
|
||||
function() {
|
||||
self.model.destroy({
|
||||
wait: true,
|
||||
success: function() {
|
||||
alertify.success(gettext('User deleted.'));
|
||||
},
|
||||
error: function() {
|
||||
alertify.error(
|
||||
gettext('Error during deleting user.')
|
||||
);
|
||||
},
|
||||
});
|
||||
},
|
||||
function() {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
},
|
||||
deleteRow: function(e) {
|
||||
var self = this;
|
||||
e.preventDefault();
|
||||
@ -629,26 +845,27 @@ define([
|
||||
if (self.model.isNew()) {
|
||||
self.model.destroy();
|
||||
} else {
|
||||
alertify.confirm(
|
||||
gettext('Delete user?'),
|
||||
gettext('Are you sure you wish to delete this user?'),
|
||||
function() {
|
||||
self.model.destroy({
|
||||
wait: true,
|
||||
success: function() {
|
||||
alertify.success(gettext('User deleted.'));
|
||||
},
|
||||
error: function() {
|
||||
alertify.error(
|
||||
gettext('Error during deleting user.')
|
||||
);
|
||||
},
|
||||
if(self.model.get('role') == 1){
|
||||
$.ajax({
|
||||
url: url_for('user_management.shared_servers', {'uid': self.model.get('id'),
|
||||
}),
|
||||
method: 'GET',
|
||||
async: false,
|
||||
})
|
||||
.done(function(res) {
|
||||
if(res['data'].shared_servers > 0) {
|
||||
self.changeOwnership(res, self.model.get('id'));
|
||||
} else {
|
||||
self.deleteUser();
|
||||
}
|
||||
})
|
||||
.fail(function() {
|
||||
self.deleteUser();
|
||||
});
|
||||
},
|
||||
function() {
|
||||
return true;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
self.deleteUser();
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
alertify.alert(
|
||||
|
Loading…
Reference in New Issue
Block a user