mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Introduced the VariableControl, which will be used in role(s),
database(s), and tablespace(s). Thanks Harshal Dhumal for sharing the original patch. I've modified a lot to work by fetching the variables later by giving a url. Also, - Introduced the template macros for SECURITY LABELS and VARIABLES. - Improvised the Backform.Control with better syntactic approach. - Introduced a jquery function pgMakeVisible(..) to make it visible under any obj which can be identified by unique class.
This commit is contained in:
562
web/pgadmin/browser/server_groups/servers/static/js/variable.js
Normal file
562
web/pgadmin/browser/server_groups/servers/static/js/variable.js
Normal file
@@ -0,0 +1,562 @@
|
||||
(function(root, factory) {
|
||||
// Set up Backform appropriately for the environment. Start with AMD.
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
define([
|
||||
'underscore', 'jquery', 'backbone', 'backform', 'backgrid', 'alertify',
|
||||
'pgadmin', 'pgadmin.browser.node', 'pgadmin.browser.node.ui'
|
||||
],
|
||||
function(_, $, Backbone, Backform, Backgrid, Alertify, pgAdmin, pgNode) {
|
||||
// Export global even in AMD case in case this script is loaded with
|
||||
// others that may still expect a global Backform.
|
||||
return factory(root, _, $, Backbone, Backform, Alertify, pgAdmin, pgNode);
|
||||
});
|
||||
|
||||
// Next for Node.js or CommonJS. jQuery may not be needed as a module.
|
||||
} else if (typeof exports !== 'undefined') {
|
||||
var _ = require('underscore') || root._,
|
||||
$ = root.jQuery || root.$ || root.Zepto || root.ender,
|
||||
Backbone = require('backbone') || root.Backbone,
|
||||
Backform = require('backform') || root.Backform;
|
||||
Alertify = require('alertify') || root.Alertify;
|
||||
pgAdmin = require('pgadmin') || root.pgAdmin,
|
||||
pgNode = require('pgadmin.browser.node') || root.pgAdmin.Browser.Node;
|
||||
factory(root, _, $, Backbone, Backform, Alertify, pgAdmin, pgNode);
|
||||
|
||||
// Finally, as a browser global.
|
||||
} else {
|
||||
factory(
|
||||
root, root._, (root.jQuery || root.Zepto || root.ender || root.$),
|
||||
root.Backbone, root.Backform, root.pgAdmin.Browser.Node
|
||||
);
|
||||
}
|
||||
} (this, function(root, _, $, Backbone, Backform, Alertify, pgAdmin, pgNode) {
|
||||
|
||||
/**
|
||||
* VariableModel used to represent configuration parameters (variables tab)
|
||||
* for database objects.
|
||||
**/
|
||||
var VariableModel = pgNode.VariableModel = pgNode.Model.extend({
|
||||
defaults: {
|
||||
name: undefined,
|
||||
value: undefined,
|
||||
role: undefined,
|
||||
database: undefined,
|
||||
},
|
||||
schema: [
|
||||
{id: 'name', label:'Name', type:'text', editable: false, cellHeaderClasses: 'width_percent_30'},
|
||||
{
|
||||
id: 'value', label:'Value', type: 'text', cell: 'dynamic-variable',
|
||||
editable: true, cellHeaderClasses: 'width_percent_50'
|
||||
},
|
||||
{id: 'database', label:'Database', type: 'text', editable: false},
|
||||
{id: 'role', label:'Role', type: 'text', editable: false}
|
||||
],
|
||||
toJSON: function() {
|
||||
var d = Backbone.Model.prototype.toJSON.apply(this);
|
||||
|
||||
// Remove not defined values from model values.
|
||||
// i.e.
|
||||
// role, database
|
||||
if (_.isUndefined(d.database)) {
|
||||
delete d.database;
|
||||
}
|
||||
|
||||
if (_.isUndefined(d.role) || _.isNull(d.role)) {
|
||||
delete d.role;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* Dynamic Variable cell. Used for variable data type column in Variables tab.
|
||||
* Behaviour of cell depends on variable data type.
|
||||
*/
|
||||
var DynamicVariableCell = Backgrid.Extension.DynamicVariableCell = Backgrid.Cell.extend({
|
||||
/*
|
||||
* Mapping of postgres data type to backgrid cell type.
|
||||
*/
|
||||
variableCellMapper: {
|
||||
"bool":Backgrid.Extension.SwitchCell,
|
||||
"enum":Backgrid.Extension.Select2Cell,
|
||||
"string":Backgrid.Cell,
|
||||
"integer":Backgrid.IntegerCell,
|
||||
"real":Backgrid.NumberCell
|
||||
},
|
||||
initialize: function (opts) {
|
||||
|
||||
var self = this,
|
||||
name = opts.model.get("name");
|
||||
self.availVariables = opts.column.get('availVariables');
|
||||
|
||||
var variable = (self.availVariables[name]),
|
||||
cell = self.variableCellMapper[variable.vartype] || Backgrid.Cell;
|
||||
|
||||
/*
|
||||
* Set properties for dynamic cell.
|
||||
*/
|
||||
_.each(cell.prototype, function(v,k) {
|
||||
self[k] = v;
|
||||
});
|
||||
|
||||
DynamicVariableCell.__super__.initialize.apply(self, arguments);
|
||||
|
||||
switch(variable.vartype) {
|
||||
case "bool":
|
||||
// There are no specific properties for BooleanCell.
|
||||
break;
|
||||
|
||||
case "enum":
|
||||
var options = [],
|
||||
name = self.model.get("name"),
|
||||
enumVals = variable.enumvals;
|
||||
|
||||
_.each(enumVals, function(enumVal) {
|
||||
options.push([enumVal, enumVal]);
|
||||
});
|
||||
|
||||
self.optionValues = options;
|
||||
self.multiple = cell.prototype.multiple;
|
||||
self.delimiter = cell.prototype.delimiter;
|
||||
|
||||
self.listenTo(
|
||||
self.model, "backgrid:edit",
|
||||
function (model, column, cell, editor) {
|
||||
if (column.get("name") == self.column.get("name")) {
|
||||
editor.setOptionValues(self.optionValues);
|
||||
editor.setMultiple(self.multiple);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case "integer":
|
||||
|
||||
self.decimals = 0;
|
||||
self.decimalSeparator = cell.prototype.decimalSeparator;
|
||||
self.orderSeparator = cell.prototype.orderSeparator;
|
||||
var formatter = self.formatter;
|
||||
|
||||
formatter.decimals = self.decimals;
|
||||
formatter.decimalSeparator = self.decimalSeparator;
|
||||
formatter.orderSeparator = self.orderSeparator;
|
||||
|
||||
break;
|
||||
|
||||
case "real":
|
||||
|
||||
self.decimals = cell.prototype.decimals;
|
||||
self.decimalSeparator = cell.prototype.decimalSeparator;
|
||||
self.orderSeparator = cell.prototype.orderSeparator;
|
||||
|
||||
var formatter = self.formatter;
|
||||
|
||||
formatter.decimals = self.decimals;
|
||||
formatter.decimalSeparator = self.decimalSeparator;
|
||||
formatter.orderSeparator = self.orderSeparator;
|
||||
|
||||
break;
|
||||
|
||||
case "string":
|
||||
default:
|
||||
// There are no specific properties for StringCell and Cell.
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Variable Tab Control to set/update configuration values for database object.
|
||||
*
|
||||
**/
|
||||
var VariableCollectionControl = Backform.VariableCollectionControl =
|
||||
Backform.UniqueColCollectionControl.extend({
|
||||
|
||||
hasDatabase: false,
|
||||
hasRole: false,
|
||||
|
||||
defaults: _.extend({
|
||||
uniqueCol: ['name', 'role', 'database']
|
||||
},
|
||||
Backform.UniqueColCollectionControl.prototype.defaults
|
||||
),
|
||||
|
||||
initialize: function(opts) {
|
||||
var self = this;
|
||||
|
||||
// Overriding the uniqueCol in the field
|
||||
if (opts && opts.field) {
|
||||
if (opts.field instanceof Backform.Field) {
|
||||
opts.field.set({
|
||||
uniqueCol: ['name', 'role', 'database'],
|
||||
model: pgNode.VariableModel
|
||||
},
|
||||
{
|
||||
silent: true
|
||||
});
|
||||
} else {
|
||||
opts.field.extend({
|
||||
uniqueCol: ['name', 'role', 'database'],
|
||||
model: pgNode.VariableModel
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Backform.UniqueColCollectionControl.prototype.initialize.apply(
|
||||
self, arguments
|
||||
);
|
||||
|
||||
self.hasDatabase = self.field.get('hasDatabase');
|
||||
self.hasRole = self.field.get('hasRole');
|
||||
self.availVariables = {};
|
||||
|
||||
var node = self.field.get('node').type,
|
||||
headerSchema = [{
|
||||
id: 'name', label:'', type:'text',
|
||||
url: self.field.get('variable_opts') || 'vopts',
|
||||
control: Backform.NodeAjaxOptionsControl,
|
||||
select2: {
|
||||
allowClear: false, width: 'style'
|
||||
},
|
||||
availVariables: self.availVariables,
|
||||
node: node, first_empty: false,
|
||||
transform: function(vars) {
|
||||
var self = this,
|
||||
opts = self.field.get('availVariables');
|
||||
|
||||
res = [];
|
||||
|
||||
for (var prop in opts) {
|
||||
if (opts.hasOwnProperty(prop)) {
|
||||
delete opts[prop];
|
||||
}
|
||||
}
|
||||
|
||||
_.each(vars, function(v) {
|
||||
opts[v.name] = _.extend({}, v);
|
||||
res.push({
|
||||
'label': v.name,
|
||||
'value': v.name
|
||||
});
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
}],
|
||||
headerDefaults = {name: null},
|
||||
gridCols = ['name', 'value'];
|
||||
|
||||
if (self.hasDatabase) {
|
||||
headerSchema.push({
|
||||
id: 'database', label:'', type: 'text',
|
||||
control: Backform.NodeListByNameControl, node: 'database'
|
||||
});
|
||||
headerDefaults['database'] = null;
|
||||
gridCols.push('database');
|
||||
}
|
||||
|
||||
if (self.hasRole) {
|
||||
headerSchema.push({
|
||||
id: 'role', label:'', type: 'text',
|
||||
control: Backform.NodeListByNameControl, node: 'role'
|
||||
});
|
||||
headerDefaults['role'] = null;
|
||||
gridCols.push('role');
|
||||
}
|
||||
|
||||
self.headerData = new (Backbone.Model.extend({
|
||||
defaults: headerDefaults,
|
||||
schema: headerSchema
|
||||
}))({});
|
||||
|
||||
var headerGroups = Backform.generateViewSchema(
|
||||
self.field.get('node_info'), self.headerData, 'create',
|
||||
node, self.field.get('node_data')
|
||||
);
|
||||
|
||||
var fields = [];
|
||||
|
||||
_.each(headerGroups, function(val, key) {
|
||||
fields = fields.concat(headerGroups[key]);
|
||||
});
|
||||
|
||||
self.headerFields = new Backform.Fields(fields);
|
||||
self.gridSchema = Backform.generateGridColumnsFromModel(
|
||||
null, VariableModel, 'edit', gridCols
|
||||
);
|
||||
|
||||
// Make sure - we do have the data for variables
|
||||
self.getVariables();
|
||||
|
||||
self.controls = [];
|
||||
self.listenTo(self.headerData, "change", self.headerDataChanged);
|
||||
self.listenTo(self.headerData, "select2", self.headerDataChanged);
|
||||
self.listenTo(self.collection, "remove", self.onRemoveVariable);
|
||||
},
|
||||
|
||||
/*
|
||||
* Get the variable options for this control.
|
||||
*/
|
||||
getVariables: function() {
|
||||
var self = this,
|
||||
url = this.field.get('url'),
|
||||
m = self.model;
|
||||
|
||||
if (url && !m.isNew()) {
|
||||
var node = self.field.get('node'),
|
||||
node_data = self.field.get('node_data'),
|
||||
node_info = self.field.get('node_info'),
|
||||
full_url = node.generate_url.apply(
|
||||
node, [
|
||||
null, url, node_data, true, node_info
|
||||
]),
|
||||
data;
|
||||
|
||||
m.trigger('pgadmin:view:fetching', m, self.field);
|
||||
$.ajax({
|
||||
async: false,
|
||||
url: full_url,
|
||||
success: function (res) {
|
||||
data = res.data;
|
||||
},
|
||||
error: function() {
|
||||
m.trigger('pgadmin:view:fetch:error', m, self.field);
|
||||
}
|
||||
});
|
||||
m.trigger('pgadmin:view:fetched', m, self.field);
|
||||
|
||||
if (data && _.isArray(data)) {
|
||||
self.collection.reset(data, {silent: true});
|
||||
/*
|
||||
* Make sure - new data will be taken care by the session management
|
||||
*/
|
||||
self.collection.startNewSession();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
generateHeader: function(data) {
|
||||
var header = [
|
||||
"<div class='subnode-header-form'>",
|
||||
" <div class='container-fluid'>",
|
||||
" <div class='row'>",
|
||||
" <div class='col-md-4'>",
|
||||
" <label class='control-label'>Variable name</label>",
|
||||
" </div>",
|
||||
" <div class='col-md-4' header='name'></div>",
|
||||
" <div class='col-md-4'>",
|
||||
" <button class='btn-sm btn-default add'>Add</buttton>",
|
||||
" </div>",
|
||||
" </div>"];
|
||||
|
||||
if(this.hasDatabase) {
|
||||
header.push([
|
||||
" <div class='row'>",
|
||||
" <div class='col-md-4'>",
|
||||
" <label class='control-label'>Database</label>",
|
||||
" </div>",
|
||||
" <div class='col-md-4' header='database'></div>",
|
||||
" </div>"].join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
if (this.hasRole) {
|
||||
header.push([
|
||||
" <div class='row'>",
|
||||
" <div class='col-md-4'>",
|
||||
" <label class='control-label'>Role</label>",
|
||||
" </div>",
|
||||
" <div class='col-md-4' header='role'></div>",
|
||||
" </div>"].join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
header.push([
|
||||
" </div>",
|
||||
"</div>"].join("\n"));
|
||||
|
||||
var self = this,
|
||||
$header = $(header.join("\n")),
|
||||
controls = this.controls;
|
||||
|
||||
this.headerFields.each(function(field) {
|
||||
var control = new (field.get("control"))({
|
||||
field: field,
|
||||
model: self.headerData
|
||||
});
|
||||
|
||||
$header.find('div[header="' + field.get('name') + '"]').append(
|
||||
control.render().$el
|
||||
);
|
||||
|
||||
controls.push(control);
|
||||
});
|
||||
|
||||
// Set visibility of Add button
|
||||
if (data.disabled || data.canAdd == false) {
|
||||
$header.find("button.add").remove();
|
||||
}
|
||||
|
||||
self.$header = $header;
|
||||
|
||||
return $header;
|
||||
},
|
||||
|
||||
events: _.extend(
|
||||
{},
|
||||
Backform.UniqueColCollectionControl.prototype.events,
|
||||
{
|
||||
'click button.add': 'addVariable'
|
||||
}
|
||||
),
|
||||
|
||||
showGridControl: function(data) {
|
||||
|
||||
var self = this,
|
||||
titleTmpl = _.template([
|
||||
"<div class='subnode-header'>",
|
||||
"<label class='control-label'><%-label%></label>",
|
||||
"</div>"].join("\n")),
|
||||
$gridBody =
|
||||
$("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode'></div>").append(
|
||||
titleTmpl({label: data.label})
|
||||
);
|
||||
|
||||
$gridBody.append(self.generateHeader(data));
|
||||
|
||||
var gridSchema = _.clone(this.gridSchema);
|
||||
|
||||
_.each(gridSchema.columns, function(col) {
|
||||
if (col.name == 'value') {
|
||||
col.availVariables = self.availVariables;
|
||||
}
|
||||
});
|
||||
|
||||
// Insert Delete Cell into Grid
|
||||
if (data.disabled == false && data.canDelete) {
|
||||
gridSchema.columns.unshift({
|
||||
name: "pg-backform-delete", label: "",
|
||||
cell: Backgrid.Extension.DeleteCell,
|
||||
editable: false, cell_priority: -1
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize a new Grid instance
|
||||
var grid = self.grid = new Backgrid.Grid({
|
||||
columns: gridSchema.columns,
|
||||
collection: self.collection,
|
||||
className: "backgrid table-bordered"
|
||||
});
|
||||
self.$grid = grid.render().$el;
|
||||
|
||||
$gridBody.append(self.$grid);
|
||||
|
||||
self.headerData.set(
|
||||
'name',
|
||||
self.$header.find(
|
||||
'div[header="name"] select option:first'
|
||||
).val()
|
||||
);
|
||||
|
||||
// Render node grid
|
||||
return $gridBody;
|
||||
},
|
||||
|
||||
addVariable: function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
var self = this,
|
||||
m = new (self.field.get('model'))(
|
||||
self.headerData.toJSON(), {silent: true}
|
||||
),
|
||||
coll = self.model.get(self.field.get('name'));
|
||||
|
||||
coll.add(m);
|
||||
|
||||
var idx = coll.indexOf(m);
|
||||
|
||||
// idx may not be always > -1 because our UniqueColCollection may
|
||||
// remove 'm' if duplicate value found.
|
||||
if (idx > -1) {
|
||||
self.$grid.find('.new').removeClass('new');
|
||||
|
||||
var newRow = self.grid.body.rows[idx].$el;
|
||||
|
||||
newRow.addClass("new");
|
||||
$(newRow).pgMakeVisible('backform-tab');
|
||||
} else {
|
||||
delete m;
|
||||
}
|
||||
|
||||
this.headerDataChanged();
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
headerDataChanged: function() {
|
||||
var self = this, val,
|
||||
data = this.headerData.toJSON(),
|
||||
inSelected = false,
|
||||
checkVars = ['name'];
|
||||
|
||||
if (!self.$header) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.hasDatabase) {
|
||||
checkVars.push('database');
|
||||
}
|
||||
|
||||
if (self.role) {
|
||||
checkVars.push('role');
|
||||
}
|
||||
|
||||
self.collection.each(function(m) {
|
||||
if (!inSelected) {
|
||||
var has = true;
|
||||
_.each(checkVars, function(v) {
|
||||
val = m.get(v);
|
||||
has = has && ((
|
||||
(_.isUndefined(val) || _.isNull(val)) &&
|
||||
(_.isUndefined(data[v]) || _.isNull(data[v]))
|
||||
) ||
|
||||
(val == data[v]));
|
||||
});
|
||||
|
||||
inSelected = has;
|
||||
}
|
||||
});
|
||||
|
||||
self.$header.find('button.add').prop('disabled', inSelected);
|
||||
},
|
||||
|
||||
onRemoveVariable: function() {
|
||||
var self = this;
|
||||
|
||||
// Wait for collection to be updated before checking for the button to be
|
||||
// enabled, or not.
|
||||
setTimeout(function() {
|
||||
self.headerDataChanged();
|
||||
}, 10);
|
||||
},
|
||||
|
||||
remove: function() {
|
||||
/*
|
||||
* Stop listening the events registered by this control.
|
||||
*/
|
||||
this.stopListening(this.headerData, "change", this.headerDataChanged);
|
||||
this.listenTo(this.headerData, "select2", this.headerDataChanged);
|
||||
this.listenTo(this.collection, "remove", this.onRemoveVariable);
|
||||
|
||||
VariableCollectionControl.__super__.remove.apply(this, arguments);
|
||||
|
||||
// Remove the header model
|
||||
delete (this.headerData);
|
||||
|
||||
// Clear the available Variables object
|
||||
self.availVariables = {};
|
||||
}
|
||||
});
|
||||
|
||||
return VariableModel;
|
||||
}));
|
||||
Reference in New Issue
Block a user