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:
Harshal Dhumal 2016-07-18 11:50:42 +01:00 committed by Dave Page
parent 5560d5b334
commit c7d25c33f2
6 changed files with 324 additions and 285 deletions

View File

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

View File

@ -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'
},{

View File

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

View File

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

View File

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

View File

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