mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2024-12-01 04:49:11 -06:00
Make the Parameter grid use a workflow consistent with other grids. Fixes #1241
1. Altered variable control to make its UI consistent with privileges and Security labels. 2. Changed datamodel.js to handle duplicate rows at datamodel level and not UI/Control level. (See variable control for example)
This commit is contained in:
parent
5560d5b334
commit
c7d25c33f2
@ -284,7 +284,7 @@ function($, _, S, pgAdmin, pgBrowser, Alertify) {
|
||||
canAdd: true, canDelete: true, control: 'unique-col-collection',
|
||||
},{
|
||||
id: 'variables', label: '{{ _('Parameters') }}', type: 'collection',
|
||||
model: pgBrowser.Node.VariableModel, editable: false,
|
||||
model: pgBrowser.Node.VariableModel.extend({keys:['name', 'role']}), editable: false,
|
||||
group: '{{ _('Parameters') }}', mode: ['edit', 'create'],
|
||||
canAdd: true, canEdit: false, canDelete: true, hasRole: true,
|
||||
control: Backform.VariableCollectionControl, node: 'role'
|
||||
|
@ -476,7 +476,8 @@ function($, _, S, pgAdmin, pgBrowser, alertify, Backform) {
|
||||
},{
|
||||
id: 'variables', label: '{{ _('Parameters') }}', type: 'collection',
|
||||
group: '{{ _('Parameters') }}', hasDatabase: true, url: 'variables',
|
||||
model: pgBrowser.Node.VariableModel, control: 'variable-collection',
|
||||
model: pgBrowser.Node.VariableModel.extend({keys:['name', 'database']}),
|
||||
control: 'variable-collection',
|
||||
mode: [ 'edit', 'create'], canAdd: true, canDelete: true,
|
||||
url: "variables", disabled: 'readonly'
|
||||
},{
|
||||
|
@ -20,6 +20,7 @@
|
||||
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.
|
||||
@ -38,8 +39,17 @@
|
||||
var cellFunction = function(model) {
|
||||
var self = this,
|
||||
name = model.get("name"),
|
||||
availVariables = self.get('availVariables'),
|
||||
variable = availVariables[name];
|
||||
availVariables = {};
|
||||
|
||||
self.collection.each(function(col) {
|
||||
if (col.get("name") == "name") {
|
||||
availVariables = col.get('availVariables');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var variable = name ? availVariables[name]: undefined,
|
||||
value = model.get("value");
|
||||
|
||||
switch(variable && variable.vartype) {
|
||||
case "bool":
|
||||
@ -47,13 +57,13 @@
|
||||
* bool cell and variable can not be stateless (i.e undefined).
|
||||
* It should be either true or false.
|
||||
*/
|
||||
if (_.isUndefined(model.get("value"))) {
|
||||
model.set("value", false);
|
||||
}
|
||||
|
||||
model.set("value", !!model.get("value"), {silent: true});
|
||||
|
||||
return Backgrid.Extension.SwitchCell;
|
||||
break;
|
||||
case "enum":
|
||||
model.set({'value': undefined}, {silent:true});
|
||||
var options = [],
|
||||
enumVals = variable.enumvals;
|
||||
|
||||
@ -64,40 +74,138 @@
|
||||
return Backgrid.Extension.Select2Cell.extend({optionValues: options});
|
||||
break;
|
||||
case "integer":
|
||||
if (!_.isNaN(parseInt(value))) {
|
||||
model.set({'value': parseInt(value)}, {silent:true});
|
||||
} else {
|
||||
model.set({'value': undefined}, {silent:true});
|
||||
}
|
||||
return Backgrid.IntegerCell;
|
||||
break;
|
||||
case "real":
|
||||
if (!_.isNaN(parseFloat(value))) {
|
||||
model.set({'value': parseFloat(value)}, {silent:true});
|
||||
} else {
|
||||
model.set({'value': undefined}, {silent:true});
|
||||
}
|
||||
return Backgrid.NumberCell.extend({decimals: 0});
|
||||
break;
|
||||
case "string":
|
||||
return Backgrid.StringCell;
|
||||
break;
|
||||
default:
|
||||
model.set({'value': undefined}, {silent:true});
|
||||
return Backgrid.Cell;
|
||||
break;
|
||||
}
|
||||
model.set({'value': undefined}, {silent:true});
|
||||
return Backgrid.Cell;
|
||||
}
|
||||
|
||||
/*
|
||||
* This row will define behaviour or value column cell depending upon
|
||||
* variable name.
|
||||
*/
|
||||
var VariableRow = Backgrid.Row.extend({
|
||||
modelDuplicateColor: "lightYellow",
|
||||
|
||||
modelUniqueColor: "#fff",
|
||||
|
||||
initialize: function () {
|
||||
Backgrid.Row.prototype.initialize.apply(this, arguments);
|
||||
var self = this;
|
||||
self.model.on("change:name", function() {
|
||||
setTimeout(function() {
|
||||
self.columns.each(function(col) {
|
||||
if (col.get('name') == 'value') {
|
||||
|
||||
var idx = self.columns.indexOf(col),
|
||||
cf = col.get("cellFunction"),
|
||||
cell = new (cf.apply(col, [self.model]))({
|
||||
column: col,
|
||||
model: self.model
|
||||
}),
|
||||
oldCell = self.cells[idx];
|
||||
oldCell.remove();
|
||||
self.cells[idx] = cell;
|
||||
self.render();
|
||||
}
|
||||
|
||||
});
|
||||
}, 10);
|
||||
});
|
||||
self.listenTo(self.model, 'pgadmin-session:model:duplicate', self.modelDuplicate);
|
||||
self.listenTo(self.model, 'pgadmin-session:model:unique', self.modelUnique);
|
||||
},
|
||||
modelDuplicate: function() {
|
||||
$(this.el).removeClass("new");
|
||||
this.el.style.backgroundColor = this.modelDuplicateColor;
|
||||
},
|
||||
modelUnique: function() {
|
||||
this.el.style.backgroundColor = this.modelUniqueColor;
|
||||
}
|
||||
|
||||
})
|
||||
/**
|
||||
* VariableModel used to represent configuration parameters (variables tab)
|
||||
* for database objects.
|
||||
**/
|
||||
var VariableModel = pgNode.VariableModel = pgNode.Model.extend({
|
||||
keys: ['name'],
|
||||
defaults: {
|
||||
name: undefined,
|
||||
value: undefined,
|
||||
role: undefined,
|
||||
database: undefined,
|
||||
role: null,
|
||||
database: null,
|
||||
},
|
||||
keys: ['name', 'role', 'database'],
|
||||
schema: [
|
||||
{id: 'name', label:'Name', type:'text', editable: false, cellHeaderClasses: 'width_percent_30'},
|
||||
{
|
||||
id: 'name', label:'Name', type:'text', editable: true, cellHeaderClasses: 'width_percent_30',
|
||||
editable: function(m) {
|
||||
return (m instanceof Backbone.Collection) ? true : m.isNew();
|
||||
},
|
||||
cell: Backgrid.Extension.NodeAjaxOptionsCell.extend({
|
||||
initialize: function() {
|
||||
Backgrid.Extension.NodeAjaxOptionsCell.prototype.initialize.apply(this, arguments);
|
||||
|
||||
// Immediately process options as we need them before render.
|
||||
|
||||
var opVals = _.clone(this.optionValues ||
|
||||
(_.isFunction(this.column.get('options')) ?
|
||||
(this.column.get('options'))(this) :
|
||||
this.column.get('options')));
|
||||
|
||||
this.column.set('options', opVals);
|
||||
}
|
||||
}),
|
||||
url: 'vopts',
|
||||
select2: { allowClear: false },
|
||||
transform: function(vars, cell) {
|
||||
var self = this,
|
||||
res = [],
|
||||
availVariables = {};
|
||||
|
||||
_.each(vars, function(v) {
|
||||
res.push({
|
||||
'value': v.name,
|
||||
'image': undefined,
|
||||
'label': v.name
|
||||
});
|
||||
availVariables[v.name] = v;
|
||||
});
|
||||
|
||||
cell.column.set("availVariables", availVariables);
|
||||
return res;
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'value', label:'Value', type: 'text', editable: true,
|
||||
cellFunction: cellFunction, cellHeaderClasses: 'width_percent_50'
|
||||
cellFunction: cellFunction, cellHeaderClasses: 'width_percent_40'
|
||||
},
|
||||
{id: 'database', label:'Database', type: 'text', editable: false},
|
||||
{id: 'role', label:'Role', type: 'text', editable: false}
|
||||
{id: 'database', label:'Database', type: 'text', editable: true,
|
||||
node: 'database', cell: Backgrid.Extension.NodeListByNameCell
|
||||
},
|
||||
{id: 'role', label:'Role', type: 'text', editable: true,
|
||||
node: 'role', cell: Backgrid.Extension.NodeListByNameCell}
|
||||
],
|
||||
toJSON: function() {
|
||||
var d = Backbone.Model.prototype.toJSON.apply(this);
|
||||
@ -116,15 +224,24 @@
|
||||
return d;
|
||||
},
|
||||
validate: function() {
|
||||
if (_.isUndefined(this.get('value')) ||
|
||||
if (_.isUndefined(this.get('name')) ||
|
||||
_.isNull(this.get('name')) ||
|
||||
String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = 'Please select a parameter name.';
|
||||
|
||||
this.errorModel.set('name', msg);
|
||||
|
||||
return msg;
|
||||
} else if (_.isUndefined(this.get('value')) ||
|
||||
_.isNull(this.get('value')) ||
|
||||
String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = 'Please enter some value!';
|
||||
var msg = 'Please enter a value for the parameter.';
|
||||
|
||||
this.errorModel.set('value', msg);
|
||||
|
||||
return msg;
|
||||
} else {
|
||||
this.errorModel.unset('name');
|
||||
this.errorModel.unset('value');
|
||||
}
|
||||
|
||||
@ -144,7 +261,7 @@
|
||||
|
||||
initialize: function(opts) {
|
||||
var self = this,
|
||||
uniqueCols = ['name'];
|
||||
keys = ['name'];
|
||||
|
||||
/*
|
||||
* Read from field schema whether user wants to use database and role
|
||||
@ -155,24 +272,22 @@
|
||||
|
||||
// Update unique coll field based on above flag status.
|
||||
if (self.hasDatabase) {
|
||||
uniqueCols.push('database')
|
||||
keys.push('database');
|
||||
} else if (self.hasRole) {
|
||||
uniqueCols.push('role')
|
||||
keys.push('role');
|
||||
}
|
||||
// Overriding the uniqueCol in the field
|
||||
if (opts && opts.field) {
|
||||
if (opts.field instanceof Backform.Field) {
|
||||
opts.field.set({
|
||||
uniqueCol: uniqueCols,
|
||||
model: pgNode.VariableModel
|
||||
model: pgNode.VariableModel.extend({keys:keys})
|
||||
},
|
||||
{
|
||||
silent: true
|
||||
});
|
||||
} else {
|
||||
opts.field.extend({
|
||||
uniqueCol: uniqueCols,
|
||||
model: pgNode.VariableModel
|
||||
model: pgNode.VariableModel.extend({keys:keys})
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -181,94 +296,25 @@
|
||||
self, arguments
|
||||
);
|
||||
|
||||
|
||||
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,
|
||||
cache_level: 'server',
|
||||
select2: {
|
||||
allowClear: false, width: 'style'
|
||||
},
|
||||
availVariables: self.availVariables,
|
||||
node: node, first_empty: false,
|
||||
version_compatible: self.field.get('version_compatible'),
|
||||
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', cache_level: 'server',
|
||||
control: Backform.NodeListByNameControl, node: 'database',
|
||||
version_compatible: self.field.get('version_compatible')
|
||||
});
|
||||
headerDefaults['database'] = null;
|
||||
gridCols.push('database');
|
||||
}
|
||||
|
||||
if (self.hasRole) {
|
||||
headerSchema.push({
|
||||
id: 'role', label:'', type: 'text', cache_level: 'server',
|
||||
control: Backform.NodeListByNameControl, node: 'role',
|
||||
version_compatible: self.field.get('version_compatible')
|
||||
});
|
||||
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')
|
||||
),
|
||||
fields = [];
|
||||
|
||||
_.each(headerGroups, function(o) {
|
||||
fields = fields.concat(o.fields);
|
||||
});
|
||||
|
||||
self.headerFields = new Backform.Fields(fields);
|
||||
self.gridSchema = Backform.generateGridColumnsFromModel(
|
||||
null, VariableModel, 'edit', gridCols
|
||||
self.field.get('node_info'), VariableModel.extend({keys:keys}), 'edit', gridCols, self.field.get('schema_node')
|
||||
);
|
||||
|
||||
// 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 data for this node.
|
||||
@ -321,101 +367,19 @@
|
||||
}
|
||||
},
|
||||
|
||||
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_label%></label>',
|
||||
' </div>',
|
||||
' <div class="col-md-4" header="name"></div>',
|
||||
' <div class="col-md-4">',
|
||||
' <button class="btn-sm btn-default add" <%=canAdd ? "" : "disabled=\'disabled\'"%> ><%-add_label%></buttton>',
|
||||
' </div>',
|
||||
' </div>'];
|
||||
|
||||
if(this.hasDatabase) {
|
||||
header.push([
|
||||
' <div class="row">',
|
||||
' <div class="col-md-4">',
|
||||
' <label class="control-label"><%-database_label%></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%></label>',
|
||||
' </div>',
|
||||
' <div class="col-md-4" header="role"></div>',
|
||||
' </div>'].join("\n")
|
||||
);
|
||||
}
|
||||
|
||||
header.push([
|
||||
' </div>',
|
||||
'</div>'].join("\n"));
|
||||
|
||||
// TODO:: Do the i18n
|
||||
_.extend(data, {
|
||||
variable_label: "Parameter name",
|
||||
add_label: "ADD",
|
||||
database_label: "Database",
|
||||
role_label: "Role"
|
||||
});
|
||||
|
||||
var self = this,
|
||||
headerTmpl = _.template(header.join("\n")),
|
||||
$header = $(headerTmpl(data)),
|
||||
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);
|
||||
});
|
||||
|
||||
// We should not show add but in properties mode
|
||||
if (data.mode == 'properties') {
|
||||
$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>",
|
||||
"<button class='btn-sm btn-default add' <%=canAdd ? '' : 'disabled=\"disabled\"'%>>Add</buttton>",
|
||||
"</div>"].join("\n")),
|
||||
$gridBody =
|
||||
$("<div class='pgadmin-control-group backgrid form-group col-xs-12 object subnode'></div>").append(
|
||||
titleTmpl({label: data.label})
|
||||
titleTmpl(data)
|
||||
);
|
||||
|
||||
$gridBody.append(self.generateHeader(data));
|
||||
|
||||
var gridSchema = _.clone(this.gridSchema);
|
||||
|
||||
_.each(gridSchema.columns, function(col) {
|
||||
@ -460,18 +424,61 @@
|
||||
var grid = self.grid = new Backgrid.Grid({
|
||||
columns: gridSchema.columns,
|
||||
collection: self.collection,
|
||||
row: VariableRow,
|
||||
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()
|
||||
);
|
||||
// Add button callback
|
||||
if (!(data.disabled || data.canAdd == false)) {
|
||||
$gridBody.find('button.add').first().click(function(e) {
|
||||
e.preventDefault();
|
||||
var canAddRow = _.isFunction(data.canAddRow) ?
|
||||
data.canAddRow.apply(self, [self.model]) : true;
|
||||
if (canAddRow) {
|
||||
|
||||
var allowMultipleEmptyRows = !!self.field.get('allowMultipleEmptyRows');
|
||||
|
||||
// If allowMultipleEmptyRows is not set or is false then don't allow second new empty row.
|
||||
// There should be only one empty row.
|
||||
if (!allowMultipleEmptyRows && self.collection) {
|
||||
var isEmpty = false;
|
||||
self.collection.each(function(model) {
|
||||
var modelValues = [];
|
||||
_.each(model.attributes, function(val, key) {
|
||||
modelValues.push(val);
|
||||
})
|
||||
if(!_.some(modelValues, _.identity)) {
|
||||
isEmpty = true;
|
||||
}
|
||||
});
|
||||
if(isEmpty) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$(grid.body.$el.find($("tr.new"))).removeClass("new")
|
||||
var m = new (data.model) (null, {
|
||||
silent: true,
|
||||
handler: self.collection,
|
||||
top: self.model.top || self.model,
|
||||
collection: self.collection,
|
||||
node_info: self.model.node_info
|
||||
});
|
||||
self.collection.add(m);
|
||||
|
||||
var idx = self.collection.indexOf(m),
|
||||
newRow = grid.body.rows[idx].$el;
|
||||
|
||||
newRow.addClass("new");
|
||||
$(newRow).pgMakeVisible('backform-tab');
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Render node grid
|
||||
return $gridBody;
|
||||
@ -505,83 +512,7 @@
|
||||
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.hasRole) {
|
||||
checkVars.push('role');
|
||||
}
|
||||
|
||||
if (self.control_data.canAdd) {
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
inSelected = true;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Remove header controls.
|
||||
_.each(this.controls, function(control) {
|
||||
control.remove();
|
||||
});
|
||||
|
||||
VariableCollectionControl.__super__.remove.apply(this, arguments);
|
||||
|
||||
// Remove the header model
|
||||
delete (this.headerData);
|
||||
|
||||
// Clear the available Variables object
|
||||
self.availVariables = {};
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -3,10 +3,12 @@
|
||||
{####################################################}
|
||||
{% macro APPLY(conn, database, role, param, value) -%}
|
||||
ALTER {% if role %}ROLE {{ self.conn|qtIdent(role) }}{% if database %} IN DATABASE {{ conn|qtIdent(database) }}{% endif %}{% else %}DATABASE {{ conn|qtIdent(database) }}{% endif %}
|
||||
|
||||
SET {{ conn|qtIdent(param) }} TO {{ value|qtLiteral }};
|
||||
{%- endmacro %}
|
||||
{% macro RESET(conn, database, role, param) -%}
|
||||
ALTER {% if role %}ROLE {{ self.conn|qtIdent(role) }}{% if database %} IN DATABASE {{ conn|qtIdent(database) }}{% endif %}{% else %}DATABASE {{ conn|qtIdent(database) }}{% endif %}
|
||||
|
||||
RESET {{ conn|qtIdent(param) }};
|
||||
{%- endmacro %}
|
||||
{################################################}
|
||||
@ -14,11 +16,13 @@ ALTER {% if role %}ROLE {{ self.conn|qtIdent(role) }}{% if database %} IN DATABA
|
||||
{################################################}
|
||||
{% macro SET(conn, object_type, object_name, options) -%}
|
||||
ALTER {{object_type}} {{ conn|qtIdent(object_name) }}
|
||||
|
||||
SET ({% for opt in options %}{% if loop.index != 1 %}
|
||||
, {% endif %}{{ conn|qtIdent(opt.name) }}={{ opt.value|qtLiteral }}{% endfor %});
|
||||
{%- endmacro %}
|
||||
{% macro UNSET(conn, object_type, object_name, options) -%}
|
||||
ALTER {{object_type}} {{ conn|qtIdent(object_name) }}
|
||||
|
||||
RESET ({% for opt in options %}{% if loop.index != 1 %}
|
||||
, {% endif %}{{ conn|qtIdent(opt.name) }}{% endfor %});
|
||||
{%- endmacro %}
|
@ -7,6 +7,7 @@ function(_, pgAdmin, $, Backbone) {
|
||||
/*
|
||||
* Parsing the existing data
|
||||
*/
|
||||
on_server: false,
|
||||
parse: function(res) {
|
||||
var self = this;
|
||||
if (res && _.isObject(res) && 'node' in res && res['node']) {
|
||||
@ -33,9 +34,21 @@ function(_, pgAdmin, $, Backbone) {
|
||||
silent: true,
|
||||
attrName: s.id
|
||||
});
|
||||
self.set(s.id, obj, {silent: true, parse: true});
|
||||
|
||||
/*
|
||||
* Nested collection models may or may not have idAttribute.
|
||||
* So to decide whether model is new or not set 'on_server'
|
||||
* flag on such models.
|
||||
*/
|
||||
|
||||
self.set(s.id, obj, {silent: true, parse: true, on_server : true});
|
||||
} else {
|
||||
obj.reset(val, {silent: true, parse: true});
|
||||
/*
|
||||
* Nested collection models may or may not have idAttribute.
|
||||
* So to decide whether model is new or not set 'on_server'
|
||||
* flag on such models.
|
||||
*/
|
||||
obj.reset(val, {silent: true, parse: true, on_server : true});
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -89,6 +102,12 @@ function(_, pgAdmin, $, Backbone) {
|
||||
|
||||
return res;
|
||||
},
|
||||
isNew: function() {
|
||||
if (this.has(this.idAttribute)) {
|
||||
return !this.has(this.idAttribute);
|
||||
}
|
||||
return !this.on_server;
|
||||
},
|
||||
primary_key: function() {
|
||||
if (this.keys && _.isArray(this.keys)) {
|
||||
var res = {}, self = this;
|
||||
@ -103,6 +122,11 @@ function(_, pgAdmin, $, Backbone) {
|
||||
},
|
||||
initialize: function(attributes, options) {
|
||||
var self = this;
|
||||
self._previous_key_values = {};
|
||||
|
||||
if ('on_server' in options && options.on_server) {
|
||||
self.on_server = true;
|
||||
}
|
||||
|
||||
Backbone.Model.prototype.initialize.apply(self, arguments);
|
||||
|
||||
@ -198,6 +222,13 @@ function(_, pgAdmin, $, Backbone) {
|
||||
self.startNewSession();
|
||||
}
|
||||
|
||||
if ('keys' in self && _.isArray(self.keys)) {
|
||||
_.each(self.keys, function(key) {
|
||||
self.on("change:" + key, function(m) {
|
||||
self._previous_key_values[key] = m.previous(key);
|
||||
})
|
||||
})
|
||||
}
|
||||
return self;
|
||||
},
|
||||
// Create a reset function, which allow us to remove the nested object.
|
||||
@ -725,13 +756,19 @@ function(_, pgAdmin, $, Backbone) {
|
||||
invalidModels = self.sessAttrs['invalid'];
|
||||
|
||||
if (self.trackChanges) {
|
||||
// Find the object the invalid list, if found remove it from the list
|
||||
// Now check uniqueness of current model with other models.
|
||||
var isUnique = self.checkDuplicateWithModel(m);
|
||||
|
||||
// If unique then find the object the invalid list, if found remove it from the list
|
||||
// and inform the parent that - I am a valid object now.
|
||||
if (m.cid in invalidModels) {
|
||||
|
||||
if (isUnique && m.cid in invalidModels) {
|
||||
delete invalidModels[m.cid];
|
||||
}
|
||||
|
||||
this.triggerValidationEvent.apply(this);
|
||||
if (isUnique) {
|
||||
this.triggerValidationEvent.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -837,7 +874,6 @@ function(_, pgAdmin, $, Backbone) {
|
||||
return (_.findIndex(this.sessAttrs[type], comparator));
|
||||
},
|
||||
onModelAdd: function(obj) {
|
||||
|
||||
if (!this.trackChanges)
|
||||
return true;
|
||||
|
||||
@ -871,25 +907,21 @@ function(_, pgAdmin, $, Backbone) {
|
||||
(self.sessAttrs['invalid'])[obj.cid] = msg;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ('validate' in obj && typeof(obj.validate) === 'function') {
|
||||
msg = obj.validate();
|
||||
|
||||
// Let the parent/listener know about my status (valid/invalid).
|
||||
this.triggerValidationEvent.apply(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
if ('validate' in obj && typeof(obj.validate) === 'function') {
|
||||
msg = obj.validate();
|
||||
|
||||
if (msg) {
|
||||
(self.sessAttrs['invalid'])[obj.cid] = msg;
|
||||
if (msg) {
|
||||
(self.sessAttrs['invalid'])[obj.cid] = msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.sessAttrs['added'].push(obj);
|
||||
self.sessAttrs['added'].push(obj);
|
||||
|
||||
/*
|
||||
* Session has been changed
|
||||
*/
|
||||
(self.handler || self).trigger('pgadmin-session:added', self, obj);
|
||||
/*
|
||||
* Session has been changed
|
||||
*/
|
||||
(self.handler || self).trigger('pgadmin-session:added', self, obj);
|
||||
}
|
||||
|
||||
// Let the parent/listener know about my status (valid/invalid).
|
||||
this.triggerValidationEvent.apply(this);
|
||||
@ -897,7 +929,6 @@ function(_, pgAdmin, $, Backbone) {
|
||||
return true;
|
||||
},
|
||||
onModelRemove: function(obj) {
|
||||
|
||||
if (!this.trackChanges)
|
||||
return true;
|
||||
|
||||
@ -917,6 +948,8 @@ function(_, pgAdmin, $, Backbone) {
|
||||
|
||||
(self.handler || self).trigger('pgadmin-session:removed', self, copy);
|
||||
|
||||
self.checkDuplicateWithModel(copy);
|
||||
|
||||
// Let the parent/listener know about my status (valid/invalid).
|
||||
this.triggerValidationEvent.apply(this);
|
||||
|
||||
@ -936,6 +969,8 @@ function(_, pgAdmin, $, Backbone) {
|
||||
|
||||
self.sessAttrs['deleted'].push(obj);
|
||||
|
||||
self.checkDuplicateWithModel(obj);
|
||||
|
||||
// Let the parent/listener know about my status (valid/invalid).
|
||||
this.triggerValidationEvent.apply(this);
|
||||
|
||||
@ -985,12 +1020,12 @@ function(_, pgAdmin, $, Backbone) {
|
||||
}
|
||||
},
|
||||
onModelChange: function(obj) {
|
||||
var self = this;
|
||||
|
||||
if (!this.trackChanges || !(obj instanceof pgBrowser.Node.Model))
|
||||
return true;
|
||||
|
||||
var self = this,
|
||||
idx = self.objFindInSession(obj, 'added');
|
||||
var idx = self.objFindInSession(obj, 'added');
|
||||
|
||||
// It was newly added model, we don't need to add into the changed
|
||||
// list.
|
||||
@ -1034,6 +1069,74 @@ function(_, pgAdmin, $, Backbone) {
|
||||
(self.handler || self).trigger('pgadmin-session:changed', self, obj);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/*
|
||||
* This function will check if given model is unique or duplicate in
|
||||
* collection and set/clear duplicate errors on models.
|
||||
*/
|
||||
checkDuplicateWithModel: function(model) {
|
||||
if (!('keys' in model) || _.isEmpty(model.keys)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var self = this,
|
||||
condition = {},
|
||||
previous_condition = {};
|
||||
|
||||
_.each(model.keys, function(key) {
|
||||
condition[key] = model.get(key);
|
||||
if(key in model._previous_key_values) {
|
||||
previous_condition[key] = model._previous_key_values[key];
|
||||
} else {
|
||||
previous_condition[key] = model.previous(key);
|
||||
}
|
||||
});
|
||||
|
||||
// Reset previously changed values.
|
||||
model._previous_key_values = {};
|
||||
|
||||
var old_conflicting_models = self.where(previous_condition);
|
||||
|
||||
if (old_conflicting_models.length == 1) {
|
||||
var m = old_conflicting_models[0];
|
||||
self.clearInvalidSessionIfModelValid(m);
|
||||
}
|
||||
|
||||
new_conflicting_models = self.where(condition);
|
||||
if (new_conflicting_models.length == 0) {
|
||||
self.clearInvalidSessionIfModelValid(model);
|
||||
} else if (new_conflicting_models.length == 1) {
|
||||
self.clearInvalidSessionIfModelValid(model);
|
||||
self.clearInvalidSessionIfModelValid(new_conflicting_models[0]);
|
||||
} else {
|
||||
var msg = "Duplicate rows.";
|
||||
setTimeout(function() {
|
||||
_.each(new_conflicting_models, function(m) {
|
||||
self.trigger(
|
||||
'pgadmin-session:model:invalid', msg, m, self.handler
|
||||
);
|
||||
m.trigger(
|
||||
'pgadmin-session:model:duplicate', m, msg
|
||||
);
|
||||
});
|
||||
}, 10);
|
||||
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
clearInvalidSessionIfModelValid: function(m) {
|
||||
var errors = m.errorModel.attributes,
|
||||
invalidModels = this.sessAttrs['invalid'];
|
||||
|
||||
m.trigger('pgadmin-session:model:unique', m
|
||||
);
|
||||
if (_.size(errors) == 0) {
|
||||
delete invalidModels[m.cid];
|
||||
} else {
|
||||
invalidModels[m.cid] = errors[Object.keys(errors)[0]];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -86,7 +86,7 @@ define(
|
||||
if (_.isUndefined(this.get('value')) ||
|
||||
_.isNull(this.get('value')) ||
|
||||
String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = '{{ _('Please enter some value!') }}';
|
||||
var msg = '{{ _('Please enter a value for the parameter.') }}';
|
||||
this.errorModel.set('value', msg);
|
||||
return msg;
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user