2019-01-02 10:24:12 +00:00
|
|
|
/////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// pgAdmin 4 - PostgreSQL Tools
|
|
|
|
//
|
|
|
|
// Copyright (C) 2013 - 2019, The pgAdmin Development Team
|
|
|
|
// This software is released under the PostgreSQL Licence
|
|
|
|
//
|
|
|
|
//////////////////////////////////////////////////////////////
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
define([
|
|
|
|
'sources/gettext', 'underscore', 'underscore.string', 'jquery',
|
2018-07-05 10:38:43 +00:00
|
|
|
'backbone', 'backform', 'backgrid', 'codemirror', 'sources/sqleditor_utils',
|
2019-02-04 06:01:48 +00:00
|
|
|
'spectrum', 'pgadmin.backgrid', 'select2', 'bootstrap.toggle',
|
2018-07-05 10:38:43 +00:00
|
|
|
], function(gettext, _, S, $, Backbone, Backform, Backgrid, CodeMirror, SqlEditorUtils) {
|
2015-06-30 05:51:55 +00:00
|
|
|
|
2018-07-05 10:38:43 +00:00
|
|
|
var pgAdmin = (window.pgAdmin = window.pgAdmin || {}),
|
|
|
|
pgBrowser = pgAdmin.Browser;
|
2015-11-03 07:06:32 +00:00
|
|
|
|
2015-12-16 08:13:04 +00:00
|
|
|
pgAdmin.editableCell = function() {
|
Resolved few intialization issue with Node model data, moved the
privileges functionality out of the backform.pgadmin.js to make it more
modular. Now - privileges will expect the privileges data in following
format:
<name_of_the_property> : [{
"privileges": [{
"privilege_type": <privilege_type>,
"privilege": true,
"with_grant": false
},
...
],
"grantee": <grantee>,
"grantor": <grantor>
},
...
]
Example:
acl": [{
"privileges": [{
"privilege_type": "CONNECT",
"privilege": true,
"with_grant": false
}],
"grantee": '',
"grantor": 'ashesh'
},{
"privileges": [{
"privilege_type": "CREATE",
"privilege": true,
"with_grant": false
},{
"privilege": true,
"privilege_type": "TEMPORARY",
"with_grant": false
}],
"grantee": test,
"grantor": ashesh
}]
2015-12-23 06:40:20 +00:00
|
|
|
if (this.attributes && !_.isUndefined(this.attributes.disabled) &&
|
2018-01-12 07:29:51 +00:00
|
|
|
!_.isNull(this.attributes.disabled)) {
|
|
|
|
if (_.isFunction(this.attributes.disabled)) {
|
2015-12-17 13:00:39 +00:00
|
|
|
return !(this.attributes.disabled.apply(this, arguments));
|
2015-11-03 07:06:32 +00:00
|
|
|
}
|
|
|
|
if (_.isBoolean(this.attributes.disabled)) {
|
|
|
|
return !this.attributes.disabled;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-06-30 05:51:55 +00:00
|
|
|
// HTML markup global class names. More can be added by individual controls
|
|
|
|
// using _.extend. Look at RadioControl as an example.
|
|
|
|
_.extend(Backform, {
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
controlLabelClassName: 'control-label pg-el-sm-3 pg-el-12',
|
|
|
|
controlsClassName: 'pgadmin-controls pg-el-sm-9 pg-el-12',
|
|
|
|
controlContainerClassName: 'pgadmin-controls pg-el-sm-9 pg-el-12',
|
|
|
|
groupClassName: 'pgadmin-control-group form-group row pg-el-12',
|
|
|
|
setGroupClassName: 'set-group pg-el-12',
|
|
|
|
tabClassName: 'backform-tab pg-el-12',
|
|
|
|
setGroupContentClassName: 'fieldset-content pg-el-12',
|
2019-01-03 14:50:24 +00:00
|
|
|
accordianGroupClassName: 'accordian-group pg-el-12',
|
|
|
|
accordianContentClassName: 'accordian-content pg-el-12',
|
2018-10-11 12:23:59 +00:00
|
|
|
hiddenClassName: 'd-none',
|
2019-01-22 05:52:32 +00:00
|
|
|
helpMessageClassName: 'form-text text-muted help-block',
|
2019-01-30 06:12:35 +00:00
|
|
|
helpBlockControlClass: 'pgadmin-controls offset-sm-3 pg-el-sm-9 pg-el-12',
|
2018-01-12 07:29:51 +00:00
|
|
|
});
|
2015-06-30 05:51:55 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.controlMapper = {
|
2017-08-07 08:32:59 +00:00
|
|
|
'int': ['uneditable-input', 'numeric', 'integer'],
|
2015-10-28 17:06:09 +00:00
|
|
|
'text': ['uneditable-input', 'input', 'string'],
|
2016-02-22 11:48:02 +00:00
|
|
|
'numeric': ['uneditable-input', 'numeric', 'numeric'],
|
2015-10-28 17:06:09 +00:00
|
|
|
'date': 'datepicker',
|
2016-09-22 14:27:59 +00:00
|
|
|
'datetime': 'datetimepicker',
|
2015-10-28 17:06:09 +00:00
|
|
|
'boolean': 'boolean',
|
|
|
|
'options': ['readonly-option', 'select', Backgrid.Extension.PGSelectCell],
|
|
|
|
'multiline': ['textarea', 'textarea', 'string'],
|
2015-12-04 10:19:08 +00:00
|
|
|
'collection': ['sub-node-collection', 'sub-node-collection', 'string'],
|
2015-12-17 13:00:39 +00:00
|
|
|
'uniqueColCollection': ['unique-col-collection', 'unique-col-collection', 'string'],
|
2018-01-12 07:29:51 +00:00
|
|
|
'switch': 'switch',
|
2016-04-28 06:22:04 +00:00
|
|
|
'select2': 'select2',
|
2017-11-21 16:28:01 +00:00
|
|
|
'note': 'note',
|
2018-01-12 07:29:51 +00:00
|
|
|
'color': 'color',
|
2015-10-28 17:06:09 +00:00
|
|
|
};
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.getMappedControl = function(type, mode) {
|
2015-10-28 17:06:09 +00:00
|
|
|
if (type in Backform.controlMapper) {
|
|
|
|
var m = Backform.controlMapper[type];
|
|
|
|
|
|
|
|
if (!_.isArray(m)) {
|
|
|
|
return m;
|
|
|
|
}
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
var idx = 1,
|
|
|
|
len = _.size(m);
|
2015-10-28 17:06:09 +00:00
|
|
|
|
|
|
|
switch (mode) {
|
2018-01-12 07:29:51 +00:00
|
|
|
case 'properties':
|
|
|
|
idx = 0;
|
|
|
|
break;
|
|
|
|
case 'edit':
|
|
|
|
case 'create':
|
|
|
|
case 'control':
|
|
|
|
idx = 1;
|
|
|
|
break;
|
|
|
|
case 'cell':
|
|
|
|
idx = 2;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
idx = 0;
|
|
|
|
break;
|
2015-10-28 17:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return m[idx > len ? 0 : idx];
|
|
|
|
}
|
2016-01-17 16:51:02 +00:00
|
|
|
return type;
|
2018-01-12 07:29:51 +00:00
|
|
|
};
|
2015-10-28 17:06:09 +00:00
|
|
|
|
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
var BackformControlInit = Backform.Control.prototype.initialize,
|
2018-01-12 07:29:51 +00:00
|
|
|
BackformControlRemove = Backform.Control.prototype.remove;
|
2016-01-09 12:29:56 +00:00
|
|
|
|
2015-07-14 03:59:44 +00:00
|
|
|
// Override the Backform.Control to allow to track changes in dependencies,
|
|
|
|
// and rerender the View element
|
2016-01-09 12:29:56 +00:00
|
|
|
_.extend(Backform.Control.prototype, {
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
defaults: _.extend(Backform.Control.prototype.defaults, {
|
|
|
|
helpMessage: null,
|
|
|
|
}),
|
2016-03-24 07:23:11 +00:00
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
initialize: function() {
|
|
|
|
BackformControlInit.apply(this, arguments);
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
// Listen to the dependent fields in the model for any change
|
|
|
|
var deps = this.field.get('deps');
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
if (deps && _.isArray(deps)) {
|
|
|
|
_.each(deps, function(d) {
|
2017-07-18 14:13:16 +00:00
|
|
|
var attrArr = d.split('.');
|
|
|
|
var name = attrArr.shift();
|
2018-01-12 07:29:51 +00:00
|
|
|
self.listenTo(self.model, 'change:' + name, self.render);
|
2016-01-09 12:29:56 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
remove: function() {
|
2016-03-07 10:35:01 +00:00
|
|
|
// Remove the events for the dependent fields in the model
|
2016-01-09 12:29:56 +00:00
|
|
|
var self = this,
|
2018-01-12 07:29:51 +00:00
|
|
|
deps = self.field.get('deps');
|
2018-08-08 06:23:48 +00:00
|
|
|
var attrArr = this.field.get('name').split('.');
|
|
|
|
var name = attrArr.shift();
|
2016-01-09 12:29:56 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
self.stopListening(self.model, 'change:' + name, self.render);
|
2018-08-08 06:23:48 +00:00
|
|
|
if (self.model.errorModel instanceof Backbone.Model) {
|
|
|
|
self.stopListening(
|
|
|
|
self.model.errorModel, 'change:' + name, self.updateInvalid
|
|
|
|
);
|
|
|
|
}
|
2016-01-09 12:29:56 +00:00
|
|
|
|
|
|
|
if (deps && _.isArray(deps)) {
|
|
|
|
_.each(deps, function(d) {
|
|
|
|
|
2017-07-18 14:13:16 +00:00
|
|
|
var attrArr = d.split('.');
|
|
|
|
var name = attrArr.shift();
|
2016-01-09 12:29:56 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
self.stopListening(self.model, 'change:' + name, self.render);
|
2016-01-09 12:29:56 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-03-07 10:35:01 +00:00
|
|
|
if (this.cleanup) {
|
|
|
|
this.cleanup.apply(this);
|
|
|
|
}
|
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
if (BackformControlRemove) {
|
|
|
|
BackformControlRemove.apply(self, arguments);
|
|
|
|
} else {
|
|
|
|
Backbone.View.prototype.remove.apply(self, arguments);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
template: _.template([
|
2018-01-12 07:29:51 +00:00
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
|
|
|
'<div class="<%=Backform.controlsClassName%>">',
|
2018-10-10 11:43:26 +00:00
|
|
|
' <span class="<%=Backform.controlClassName%> uneditable-input" <%=disabled ? "disabled readonly" : ""%>>',
|
2018-01-12 07:29:51 +00:00
|
|
|
' <%-value%>',
|
|
|
|
' </span>',
|
2019-01-24 16:39:55 +00:00
|
|
|
' <% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
' <% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
2016-01-09 12:29:56 +00:00
|
|
|
|
|
|
|
clearInvalid: function() {
|
|
|
|
this.$el.removeClass(Backform.errorClassName);
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$el.find('.pgadmin-control-error-message').remove();
|
2016-01-09 12:29:56 +00:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
|
|
|
|
updateInvalid: function() {
|
|
|
|
var self = this,
|
2018-01-12 07:29:51 +00:00
|
|
|
errorModel = this.model.errorModel;
|
2016-01-09 12:29:56 +00:00
|
|
|
|
|
|
|
if (!(errorModel instanceof Backbone.Model)) return this;
|
|
|
|
|
|
|
|
this.clearInvalid();
|
|
|
|
|
2016-02-05 08:06:57 +00:00
|
|
|
/*
|
2018-01-12 07:29:51 +00:00
|
|
|
* Find input which have name attribute.
|
|
|
|
*/
|
2016-02-05 08:06:57 +00:00
|
|
|
this.$el.find(':input[name]').not('button').each(function(ix, el) {
|
2018-01-12 07:29:51 +00:00
|
|
|
var error = self.keyPathAccessor(
|
|
|
|
errorModel.toJSON(), $(el).attr('name')
|
|
|
|
);
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
if (_.isEmpty(error)) return;
|
|
|
|
self.$el.addClass(Backform.errorClassName);
|
2016-01-09 12:29:56 +00:00
|
|
|
});
|
|
|
|
},
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
/*
|
|
|
|
* Overriding the render function of the control to allow us to eval the
|
|
|
|
* values properly.
|
|
|
|
*/
|
|
|
|
render: function() {
|
|
|
|
var field = _.defaults(this.field.toJSON(), this.defaults),
|
2018-01-12 07:29:51 +00:00
|
|
|
attributes = this.model.toJSON(),
|
|
|
|
attrArr = field.name.split('.'),
|
|
|
|
name = attrArr.shift(),
|
|
|
|
path = attrArr.join('.'),
|
|
|
|
rawValue = this.keyPathAccessor(attributes[name], path),
|
|
|
|
data = _.extend(field, {
|
|
|
|
rawValue: rawValue,
|
|
|
|
value: this.formatter.fromRaw(rawValue, this.model),
|
|
|
|
attributes: attributes,
|
|
|
|
formatter: this.formatter,
|
|
|
|
}),
|
|
|
|
evalF = function(f, d, m) {
|
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
|
|
|
};
|
2016-01-04 13:57:51 +00:00
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
// Evaluate the disabled, visible, and required option
|
|
|
|
_.extend(data, {
|
|
|
|
disabled: evalF(data.disabled, data, this.model),
|
2018-01-12 07:29:51 +00:00
|
|
|
visible: evalF(data.visible, data, this.model),
|
|
|
|
required: evalF(data.required, data, this.model),
|
2016-01-09 12:29:56 +00:00
|
|
|
});
|
2016-01-04 13:57:51 +00:00
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
// Clean up first
|
2017-07-26 11:55:46 +00:00
|
|
|
this.$el.removeClass(Backform.hiddenClassName);
|
2016-01-04 13:57:51 +00:00
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
if (!data.visible)
|
2017-07-26 11:55:46 +00:00
|
|
|
this.$el.addClass(Backform.hiddenClassName);
|
2016-01-04 13:57:51 +00:00
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
this.$el.html(this.template(data)).addClass(field.name);
|
|
|
|
this.updateInvalid();
|
2016-01-04 13:57:51 +00:00
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
return this;
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2016-01-09 12:29:56 +00:00
|
|
|
});
|
2016-01-04 12:22:30 +00:00
|
|
|
|
2016-01-12 08:10:32 +00:00
|
|
|
/*
|
|
|
|
* Override the input control events in order to reslove the issue related to
|
|
|
|
* not updating the value sometimes in the input control.
|
|
|
|
*/
|
2016-03-15 13:28:16 +00:00
|
|
|
_.extend(
|
|
|
|
Backform.InputControl.prototype, {
|
|
|
|
events: {
|
2018-01-12 07:29:51 +00:00
|
|
|
'change input': 'onChange',
|
|
|
|
'blur input': 'onChange',
|
|
|
|
'keyup input': 'onKeyUp',
|
|
|
|
'focus input': 'clearInvalid',
|
2016-03-15 13:28:16 +00:00
|
|
|
},
|
|
|
|
onKeyUp: function(ev) {
|
|
|
|
if (this.key_timeout) {
|
|
|
|
clearTimeout(this.key_timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.keyup_timeout = setTimeout(function() {
|
|
|
|
this.onChange(ev);
|
|
|
|
}.bind(this), 400);
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2016-03-15 13:28:16 +00:00
|
|
|
});
|
2016-01-12 08:10:32 +00:00
|
|
|
|
2016-02-05 08:06:57 +00:00
|
|
|
/*
|
|
|
|
* Override the textarea control events in order to resolve the issue related
|
|
|
|
* to not updating the value in model on certain browsers in few situations
|
|
|
|
* like copy/paste, deletion using backspace.
|
|
|
|
*
|
|
|
|
* Reference:
|
|
|
|
* http://stackoverflow.com/questions/11338592/how-can-i-bind-to-the-change-event-of-a-textarea-in-jquery
|
|
|
|
*/
|
2016-03-10 15:31:43 +00:00
|
|
|
_.extend(
|
2016-03-15 13:28:16 +00:00
|
|
|
Backform.TextareaControl.prototype, {
|
|
|
|
defaults: _.extend(
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.TextareaControl.prototype.defaults, {
|
|
|
|
rows: 5,
|
|
|
|
helpMessage: null,
|
|
|
|
maxlength: null,
|
|
|
|
}
|
2016-03-15 13:28:16 +00:00
|
|
|
),
|
2018-01-12 07:29:51 +00:00
|
|
|
events: {
|
|
|
|
'change textarea': 'onChange',
|
|
|
|
'keyup textarea': 'onKeyUp',
|
|
|
|
'paste textarea': 'onChange',
|
|
|
|
'selectionchange textarea': 'onChange',
|
|
|
|
'focus textarea': 'clearInvalid',
|
2016-03-15 13:28:16 +00:00
|
|
|
},
|
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
|
|
|
'<div class="<%=Backform.controlsClassName%>">',
|
|
|
|
' <textarea ',
|
|
|
|
' class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>"',
|
2017-11-21 11:02:21 +00:00
|
|
|
' <% if (maxlength) { %>',
|
|
|
|
' maxlength="<%=maxlength%>"',
|
|
|
|
' <% } %>',
|
|
|
|
' placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%>',
|
2016-03-15 13:28:16 +00:00
|
|
|
' rows=<%=rows ? rows : ""%>',
|
|
|
|
' <%=required ? "required" : ""%>><%-value%></textarea>',
|
|
|
|
' <% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
' <% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
2016-03-15 13:28:16 +00:00
|
|
|
onKeyUp: function(ev) {
|
|
|
|
if (this.key_timeout) {
|
|
|
|
clearTimeout(this.key_timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.keyup_timeout = setTimeout(function() {
|
|
|
|
this.onChange(ev);
|
|
|
|
}.bind(this), 400);
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2016-03-15 13:28:16 +00:00
|
|
|
});
|
2016-02-05 08:06:57 +00:00
|
|
|
|
2016-01-04 06:04:40 +00:00
|
|
|
/*
|
|
|
|
* Overriding the render function of the select control to allow us to use
|
|
|
|
* options as function, which should return array in the format of
|
|
|
|
* (label, value) pair.
|
|
|
|
*/
|
|
|
|
Backform.SelectControl.prototype.render = function() {
|
|
|
|
var field = _.defaults(this.field.toJSON(), this.defaults),
|
|
|
|
attributes = this.model.toJSON(),
|
|
|
|
attrArr = field.name.split('.'),
|
|
|
|
name = attrArr.shift(),
|
|
|
|
path = attrArr.join('.'),
|
|
|
|
rawValue = this.keyPathAccessor(attributes[name], path),
|
|
|
|
data = _.extend(field, {
|
|
|
|
rawValue: rawValue,
|
|
|
|
value: this.formatter.fromRaw(rawValue, this.model),
|
|
|
|
attributes: attributes,
|
2018-01-12 07:29:51 +00:00
|
|
|
formatter: this.formatter,
|
2016-01-04 06:04:40 +00:00
|
|
|
}),
|
2016-01-05 07:48:46 +00:00
|
|
|
evalF = function(f, d, m) {
|
2016-01-05 09:32:45 +00:00
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
2016-01-04 06:04:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Evaluate the disabled, visible, and required option
|
|
|
|
_.extend(data, {
|
2016-01-05 07:48:46 +00:00
|
|
|
disabled: evalF(data.disabled, data, this.model),
|
2018-01-12 07:29:51 +00:00
|
|
|
visible: evalF(data.visible, data, this.model),
|
|
|
|
required: evalF(data.required, data, this.model),
|
2016-01-04 06:04:40 +00:00
|
|
|
});
|
|
|
|
// Evaluation the options
|
|
|
|
if (_.isFunction(data.options)) {
|
|
|
|
try {
|
2018-01-12 07:29:51 +00:00
|
|
|
data.options = data.options(this);
|
|
|
|
} catch (e) {
|
2016-01-04 06:04:40 +00:00
|
|
|
// Do nothing
|
2018-01-12 07:29:51 +00:00
|
|
|
data.options = [];
|
Improvised the 'transform/options' function usage with the Select2Cell.
The current implementaton binds the cell/control object, and the ajax
data in the asychronous Cells/Controls with the 'options' functions
extended from the Select2Cell.
The problem starts when we try to fetch the current model from that
options/transform/filter function to do some operation, which does not
require in most of the cases. Except the privileges control - where we
needed the current model for omitting the existing selected object
during transformation, and filtering.
In order resolved the issue, we need a common object, which is shared
among the Cell. In backgrid, the 'Column' object is mong the cell,
hence - implementation logic has been changed to bid the 'Column' object
with the 'options' function and, passed the 'Cell' object as an
arguments.
Because - we do use the common function 'transform' between 'Control'
and 'Cell', we needed make changes in the Select2Control to pass the
Control object as an arguments.
And, make the changes in the privileges control to use the new
implementation. The same logic is also required in some of the
operations, we will be/are working on the table/column nodes.
2016-04-08 05:30:48 +00:00
|
|
|
this.model.trigger('pgadmin-view:transform:error', this.model, this.field, e);
|
2016-01-04 06:04:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up first
|
2017-07-26 11:55:46 +00:00
|
|
|
this.$el.removeClass(Backform.hiddenClassName);
|
2016-01-04 06:04:40 +00:00
|
|
|
|
|
|
|
if (!data.visible)
|
2017-07-26 11:55:46 +00:00
|
|
|
this.$el.addClass(Backform.hiddenClassName);
|
2016-01-04 06:04:40 +00:00
|
|
|
|
|
|
|
this.$el.html(this.template(data)).addClass(field.name);
|
|
|
|
this.updateInvalid();
|
|
|
|
|
|
|
|
return this;
|
|
|
|
};
|
2018-01-12 07:29:51 +00:00
|
|
|
_.extend(Backform.SelectControl.prototype.defaults, {
|
|
|
|
helpMessage: null,
|
|
|
|
});
|
2016-01-04 06:04:40 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.ReadonlyOptionControl = Backform.SelectControl.extend({
|
2015-10-20 07:03:18 +00:00
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
|
|
|
'<div class="<%=Backform.controlsClassName%>">',
|
|
|
|
'<% for (var i=0; i < options.length; i++) { %>',
|
|
|
|
' <% var option = options[i]; %>',
|
2015-10-28 17:06:09 +00:00
|
|
|
' <% if (option.value === rawValue) { %>',
|
2018-10-10 11:43:26 +00:00
|
|
|
' <span class="<%=Backform.controlClassName%> uneditable-input" disabled readonly><%-option.label%></span>',
|
2015-10-20 07:03:18 +00:00
|
|
|
' <% } %>',
|
|
|
|
'<% } %>',
|
2016-03-24 07:23:11 +00:00
|
|
|
'<% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
'<% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
2015-10-20 07:03:18 +00:00
|
|
|
events: {},
|
|
|
|
getValueFromDOM: function() {
|
2018-01-12 07:29:51 +00:00
|
|
|
return this.formatter.toRaw(this.$el.find('span').text(), this.model);
|
|
|
|
},
|
2015-10-20 07:03:18 +00:00
|
|
|
});
|
|
|
|
|
2016-02-22 11:30:17 +00:00
|
|
|
/*
|
|
|
|
* Override the function 'updateInvalid' of the radio control to resolve an
|
|
|
|
* issue, which will not render the error block multiple times for each
|
|
|
|
* options.
|
|
|
|
*/
|
|
|
|
_.extend(
|
|
|
|
Backform.RadioControl.prototype, {
|
|
|
|
updateInvalid: function() {
|
|
|
|
var self = this,
|
2018-01-12 07:29:51 +00:00
|
|
|
errorModel = this.model.errorModel;
|
2016-02-22 11:30:17 +00:00
|
|
|
|
|
|
|
if (!(errorModel instanceof Backbone.Model)) return this;
|
|
|
|
|
|
|
|
this.clearInvalid();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Find input which have name attribute.
|
|
|
|
*/
|
|
|
|
this.$el.find(':input[name]').not('button').each(function(ix, el) {
|
2018-01-12 07:29:51 +00:00
|
|
|
var error = self.keyPathAccessor(
|
|
|
|
errorModel.toJSON(), $(el).attr('name')
|
|
|
|
);
|
2016-02-22 11:30:17 +00:00
|
|
|
|
|
|
|
if (_.isEmpty(error)) return;
|
|
|
|
|
|
|
|
self.$el.addClass(Backform.errorClassName).find(
|
|
|
|
'[type="radio"]'
|
|
|
|
).append(
|
2018-01-12 07:29:51 +00:00
|
|
|
$('<div></div>').addClass(
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
'pgadmin-control-error-message pg-el-offset-4 pg-el-8 pg-el-8 help-block'
|
2018-01-12 07:29:51 +00:00
|
|
|
).text(error));
|
2016-02-22 11:30:17 +00:00
|
|
|
});
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2016-02-22 11:30:17 +00:00
|
|
|
});
|
|
|
|
|
2015-12-04 10:19:08 +00:00
|
|
|
// Requires the Bootstrap Switch to work.
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.SwitchControl = Backform.InputControl.extend({
|
2015-12-04 10:19:08 +00:00
|
|
|
defaults: {
|
2018-01-12 07:29:51 +00:00
|
|
|
label: '',
|
2015-12-17 13:11:36 +00:00
|
|
|
options: {
|
2017-11-07 00:49:11 +00:00
|
|
|
onText: gettext('Yes'),
|
|
|
|
offText: gettext('No'),
|
2015-12-17 13:11:36 +00:00
|
|
|
onColor: 'success',
|
2016-03-23 10:24:19 +00:00
|
|
|
offColor: 'primary',
|
2019-02-04 06:01:48 +00:00
|
|
|
size: 'mini',
|
|
|
|
width: null,
|
|
|
|
height: null,
|
2015-12-17 13:11:36 +00:00
|
|
|
},
|
2019-01-03 14:50:24 +00:00
|
|
|
controlLabelClassName: Backform.controlLabelClassName,
|
|
|
|
controlsClassName: Backform.controlsClassName,
|
2016-03-24 07:23:11 +00:00
|
|
|
extraClasses: [],
|
2018-01-12 07:29:51 +00:00
|
|
|
helpMessage: null,
|
2019-02-05 16:22:35 +00:00
|
|
|
extraToggleClasses: null,
|
2015-12-04 10:19:08 +00:00
|
|
|
},
|
|
|
|
template: _.template([
|
2019-01-03 14:50:24 +00:00
|
|
|
'<label class="<%=controlLabelClassName%>"><%=label%></label>',
|
2019-02-05 16:22:35 +00:00
|
|
|
'<div class="<%=controlsClassName%> <%=extraClasses.join(\' \')%>">',
|
2019-02-04 06:01:48 +00:00
|
|
|
' <input tabindex="0" type="checkbox" data-style="quick" data-toggle="toggle"',
|
|
|
|
' data-size="<%=options.size%>" data-height="<%=options.height%>" ',
|
|
|
|
' data-on="<%=options.onText%>" data-off="<%=options.offText%>" ',
|
|
|
|
' data-onstyle="<%=options.onColor%>" data-offstyle="<%=options.offColor%>" data-width="<%=options.width%>" ',
|
|
|
|
' name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
|
2016-09-26 09:04:49 +00:00
|
|
|
' <% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
' <% } %>',
|
2016-03-24 07:23:11 +00:00
|
|
|
'</div>',
|
2018-01-12 07:29:51 +00:00
|
|
|
].join('\n')),
|
2015-12-04 10:19:08 +00:00
|
|
|
getValueFromDOM: function() {
|
|
|
|
return this.formatter.toRaw(
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$input.prop('checked'),
|
|
|
|
this.model
|
|
|
|
);
|
|
|
|
},
|
|
|
|
events: {
|
2019-02-04 06:01:48 +00:00
|
|
|
'change input': 'onChange',
|
|
|
|
'keyup': 'toggleSwitch',
|
|
|
|
},
|
|
|
|
toggleSwitch: function(e) {
|
|
|
|
if (e.keyCode == 32) {
|
|
|
|
this.$el.find('input[type=checkbox]').bootstrapToggle('toggle');
|
|
|
|
e.preventDefault();
|
|
|
|
}
|
2015-12-04 10:19:08 +00:00
|
|
|
},
|
|
|
|
render: function() {
|
2016-03-22 14:28:13 +00:00
|
|
|
var field = _.defaults(this.field.toJSON(), this.defaults),
|
2018-01-12 07:29:51 +00:00
|
|
|
attributes = this.model.toJSON(),
|
|
|
|
attrArr = field.name.split('.'),
|
|
|
|
name = attrArr.shift(),
|
|
|
|
path = attrArr.join('.'),
|
|
|
|
rawValue = this.keyPathAccessor(attributes[name], path),
|
2019-02-04 06:01:48 +00:00
|
|
|
data = _.extend(field, {
|
|
|
|
rawValue: rawValue,
|
|
|
|
value: this.formatter.fromRaw(rawValue, this.model),
|
|
|
|
attributes: attributes,
|
|
|
|
formatter: this.formatter,
|
|
|
|
}),
|
2018-01-12 07:29:51 +00:00
|
|
|
evalF = function(f, d, m) {
|
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
2019-02-04 06:01:48 +00:00
|
|
|
};
|
2015-12-04 10:19:08 +00:00
|
|
|
|
2019-02-04 06:01:48 +00:00
|
|
|
// Evaluate the disabled, visible, and required option
|
|
|
|
_.extend(data, {
|
|
|
|
disabled: evalF(field.disabled, field, this.model),
|
|
|
|
visible: evalF(data.visible, field, this.model),
|
|
|
|
required: evalF(data.required, field, this.model),
|
|
|
|
});
|
2015-12-04 10:19:08 +00:00
|
|
|
|
2019-02-04 06:01:48 +00:00
|
|
|
// Clean up first
|
|
|
|
this.$el.removeClass(Backform.hiddenClassName);
|
|
|
|
|
|
|
|
if (!data.visible)
|
|
|
|
this.$el.addClass(Backform.hiddenClassName);
|
|
|
|
|
|
|
|
if(Backform.requiredInputClassName) {
|
|
|
|
this.$el.removeClass(Backform.requiredInputClassName);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data.required) {
|
|
|
|
this.$el.addClass(Backform.requiredInputClassName);
|
|
|
|
}
|
|
|
|
|
|
|
|
data.options = _.defaults({
|
|
|
|
disabled: evalF(field.disabled, field, this.model),
|
|
|
|
}, this.field.get('options'), this.defaults.options,
|
2019-03-14 15:11:16 +00:00
|
|
|
$.fn.bootstrapToggle.defaults);
|
2019-02-04 06:01:48 +00:00
|
|
|
|
|
|
|
this.$el.html(this.template(data)).addClass(field.name);
|
2019-02-05 16:22:35 +00:00
|
|
|
|
|
|
|
// Add Extra Toggle classes to render multiple toggles in a single row
|
|
|
|
if(!_.isNull(field.extraToggleClasses)) this.$el.addClass(field.extraToggleClasses);
|
|
|
|
|
2019-02-04 06:01:48 +00:00
|
|
|
this.$input = this.$el.find('input[type=checkbox]').first();
|
|
|
|
this.$input.bootstrapToggle();
|
|
|
|
this.updateInvalid();
|
2015-12-17 13:11:36 +00:00
|
|
|
|
2015-12-04 10:19:08 +00:00
|
|
|
return this;
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2015-12-04 10:19:08 +00:00
|
|
|
});
|
2015-10-20 07:03:18 +00:00
|
|
|
|
2015-06-30 05:51:55 +00:00
|
|
|
// Backform Dialog view (in bootstrap tabbular form)
|
|
|
|
// A collection of field models.
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.Dialog = Backform.Form.extend({
|
2015-06-30 05:51:55 +00:00
|
|
|
/* Array of objects having attributes [label, fields] */
|
|
|
|
schema: undefined,
|
2018-01-12 07:29:51 +00:00
|
|
|
tagName: 'div',
|
2016-03-07 11:48:24 +00:00
|
|
|
legend: true,
|
2015-06-30 05:51:55 +00:00
|
|
|
className: function() {
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
return 'pg-el-sm-12 pg-el-md-12 pg-el-lg-12 pg-el-12';
|
2015-06-30 05:51:55 +00:00
|
|
|
},
|
2015-10-29 20:32:40 +00:00
|
|
|
tabPanelClassName: function() {
|
|
|
|
return Backform.tabClassName;
|
|
|
|
},
|
2016-01-17 16:51:02 +00:00
|
|
|
tabIndex: 0,
|
2015-06-30 05:51:55 +00:00
|
|
|
initialize: function(opts) {
|
|
|
|
var s = opts.schema;
|
|
|
|
if (s && _.isArray(s)) {
|
|
|
|
this.schema = _.each(s, function(o) {
|
2015-07-14 03:59:44 +00:00
|
|
|
if (o.fields && !(o.fields instanceof Backbone.Collection))
|
2015-06-30 05:51:55 +00:00
|
|
|
o.fields = new Backform.Fields(o.fields);
|
|
|
|
o.cId = o.cId || _.uniqueId('pgC_');
|
|
|
|
o.hId = o.hId || _.uniqueId('pgH_');
|
|
|
|
o.disabled = o.disabled || false;
|
2016-03-07 11:48:24 +00:00
|
|
|
o.legend = opts.legend;
|
2015-06-30 05:51:55 +00:00
|
|
|
});
|
2015-10-29 20:32:40 +00:00
|
|
|
if (opts.tabPanelClassName && _.isFunction(opts.tabPanelClassName)) {
|
|
|
|
this.tabPanelClassName = opts.tabPanelClassName;
|
|
|
|
}
|
2015-06-30 05:51:55 +00:00
|
|
|
}
|
|
|
|
this.model.errorModel = opts.errorModel || this.model.errorModel || new Backbone.Model();
|
|
|
|
this.controls = [];
|
|
|
|
},
|
|
|
|
template: {
|
|
|
|
'header': _.template([
|
2018-10-10 11:43:26 +00:00
|
|
|
'<li class="nav-item" role="presentation" <%=disabled ? "disabled" : ""%>>',
|
|
|
|
' <a class="nav-link" data-toggle="tab" tabindex="-1" data-tab-index="<%=tabIndex%>" href="#<%=cId%>"',
|
2015-06-30 05:51:55 +00:00
|
|
|
' id="<%=hId%>" aria-controls="<%=cId%>">',
|
2018-01-12 07:29:51 +00:00
|
|
|
'<%=label%></a></li>',
|
|
|
|
].join(' ')),
|
2015-06-30 05:51:55 +00:00
|
|
|
'panel': _.template(
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
'<div role="tabpanel" tabindex="-1" class="tab-pane <%=label%> pg-el-sm-12 pg-el-md-12 pg-el-lg-12 pg-el-12 fade" id="<%=cId%>" aria-labelledby="<%=hId%>"></div>'
|
2018-01-12 07:29:51 +00:00
|
|
|
),
|
|
|
|
},
|
2015-06-30 05:51:55 +00:00
|
|
|
render: function() {
|
|
|
|
this.cleanup();
|
|
|
|
|
|
|
|
var c = this.$el
|
2019-03-14 15:11:16 +00:00
|
|
|
.children().first().children('.active')
|
|
|
|
.first().attr('id'),
|
2015-06-30 05:51:55 +00:00
|
|
|
m = this.model,
|
|
|
|
controls = this.controls,
|
2015-10-29 20:32:40 +00:00
|
|
|
tmpls = this.template,
|
|
|
|
self = this,
|
2018-01-12 07:29:51 +00:00
|
|
|
idx = (this.tabIndex * 100),
|
2016-03-06 13:20:05 +00:00
|
|
|
evalF = function(f, d, m) {
|
2018-01-12 07:29:51 +00:00
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
|
|
|
};
|
2015-06-30 05:51:55 +00:00
|
|
|
|
|
|
|
this.$el
|
2018-01-12 07:29:51 +00:00
|
|
|
.empty()
|
|
|
|
.attr('role', 'tabpanel')
|
|
|
|
.attr('class', _.result(this, 'tabPanelClassName'));
|
2015-10-30 07:37:09 +00:00
|
|
|
m.panelEl = this.$el;
|
2015-06-30 05:51:55 +00:00
|
|
|
|
|
|
|
var tabHead = $('<ul class="nav nav-tabs" role="tablist"></ul>')
|
|
|
|
.appendTo(this.$el);
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
var tabContent = $('<div class="tab-content pg-el-sm-12 pg-el-md-12 pg-el-lg-12 pg-el-12"></div>')
|
2015-06-30 05:51:55 +00:00
|
|
|
.appendTo(this.$el);
|
|
|
|
|
|
|
|
_.each(this.schema, function(o) {
|
2016-03-06 13:20:05 +00:00
|
|
|
idx++;
|
|
|
|
if (!o.version_compatible || !evalF(o.visible, o, m)) {
|
|
|
|
return;
|
|
|
|
}
|
2018-01-12 07:29:51 +00:00
|
|
|
var el = $((tmpls['panel'])(_.extend(o, {
|
|
|
|
'tabIndex': idx,
|
|
|
|
})))
|
|
|
|
.appendTo(tabContent)
|
|
|
|
.removeClass('collapse').addClass('collapse');
|
|
|
|
$((tmpls['header'])(o)).appendTo(tabHead);
|
2015-06-30 05:51:55 +00:00
|
|
|
|
|
|
|
o.fields.each(function(f) {
|
2018-01-12 07:29:51 +00:00
|
|
|
var cntr = new(f.get('control'))({
|
2015-06-30 05:51:55 +00:00
|
|
|
field: f,
|
2015-12-26 10:45:12 +00:00
|
|
|
model: m,
|
|
|
|
dialog: self,
|
2018-01-12 07:29:51 +00:00
|
|
|
tabIndex: idx,
|
2015-06-30 05:51:55 +00:00
|
|
|
});
|
|
|
|
el.append(cntr.render().$el);
|
|
|
|
controls.push(cntr);
|
|
|
|
});
|
2016-01-17 16:51:02 +00:00
|
|
|
|
2015-12-26 10:45:12 +00:00
|
|
|
tabHead.find('a[data-toggle="tab"]').off(
|
|
|
|
'shown.bs.tab'
|
|
|
|
).off('hidden.bs.tab').on(
|
2018-01-12 07:29:51 +00:00
|
|
|
'hidden.bs.tab',
|
|
|
|
function() {
|
2015-12-26 10:45:12 +00:00
|
|
|
self.hidden_tab = $(this).data('tabIndex');
|
2018-01-12 07:29:51 +00:00
|
|
|
}).on('shown.bs.tab', function() {
|
2019-03-14 15:11:16 +00:00
|
|
|
var self = this;
|
|
|
|
self.shown_tab = $(self).data('tabIndex');
|
|
|
|
m.trigger('pg-property-tab-changed', {
|
|
|
|
'model': m,
|
|
|
|
'shown': self.shown_tab,
|
|
|
|
'hidden': self.hidden_tab,
|
|
|
|
'tab': self,
|
2018-01-12 07:29:51 +00:00
|
|
|
});
|
2019-03-14 15:11:16 +00:00
|
|
|
});
|
2015-06-30 05:51:55 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
var makeActive = tabHead.find('[id="' + c + '"]').first();
|
|
|
|
if (makeActive.length == 1) {
|
2018-10-10 11:43:26 +00:00
|
|
|
makeActive.addClass('active show');
|
2018-01-12 07:29:51 +00:00
|
|
|
tabContent.find('#' + makeActive.attr('aria-controls'))
|
2018-10-10 11:43:26 +00:00
|
|
|
.addClass('active show');
|
2015-06-30 05:51:55 +00:00
|
|
|
} else {
|
2018-10-10 11:43:26 +00:00
|
|
|
tabHead.find('.nav-link').first().addClass('active show');
|
|
|
|
tabContent.find('.tab-pane').first().addClass('active show');
|
2015-06-30 05:51:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return this;
|
2016-04-15 10:44:01 +00:00
|
|
|
},
|
|
|
|
remove: function(opts) {
|
|
|
|
if (opts && opts.data) {
|
|
|
|
if (this.model) {
|
|
|
|
if (this.model.reset) {
|
2018-01-12 07:29:51 +00:00
|
|
|
this.model.reset({
|
|
|
|
validate: false,
|
|
|
|
silent: true,
|
|
|
|
stop: true,
|
|
|
|
});
|
2016-04-15 10:44:01 +00:00
|
|
|
}
|
2018-01-12 07:29:51 +00:00
|
|
|
this.model.clear({
|
|
|
|
validate: false,
|
|
|
|
silent: true,
|
|
|
|
stop: true,
|
|
|
|
});
|
|
|
|
delete(this.model);
|
2016-04-15 10:44:01 +00:00
|
|
|
}
|
|
|
|
if (this.errorModel) {
|
2018-01-12 07:29:51 +00:00
|
|
|
this.errorModel.clear({
|
|
|
|
validate: false,
|
|
|
|
silent: true,
|
|
|
|
stop: true,
|
|
|
|
});
|
|
|
|
delete(this.errorModel);
|
2016-04-15 10:44:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this.cleanup();
|
|
|
|
Backform.Form.prototype.remove.apply(this, arguments);
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2015-06-30 05:51:55 +00:00
|
|
|
});
|
|
|
|
|
2019-01-03 14:50:24 +00:00
|
|
|
Backform.Accordian = Backform.Dialog.extend({
|
|
|
|
className: function() {
|
|
|
|
return 'set-group pg-el-12';
|
|
|
|
},
|
|
|
|
tabPanelClassName: function() {
|
|
|
|
return Backform.tabClassName;
|
|
|
|
},
|
|
|
|
legendClass: 'badge',
|
|
|
|
contentClass: Backform.accordianContentClassName + ' collapse show',
|
|
|
|
template: {
|
|
|
|
'header': _.template([
|
|
|
|
'<div class="<%=Backform.accordianGroupClassName%>" <%=disabled ? "disabled" : ""%>>',
|
|
|
|
' <% if (legend != false) { %>',
|
|
|
|
' <div class="<%=legendClass%>" <%=collapse ? "data-toggle=\'collapse\'" : ""%> data-target="#<%=cId%>"><%=collapse ? "<span class=\'caret\'></span>" : "" %><%=label%></legend>',
|
|
|
|
' <% } %>',
|
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
|
|
|
'content': _.template(
|
|
|
|
' <div id="<%= cId %>" class="<%=contentClass%>"></div>'
|
|
|
|
),
|
|
|
|
},
|
|
|
|
collapse: true,
|
|
|
|
render: function() {
|
|
|
|
this.cleanup();
|
|
|
|
|
|
|
|
var m = this.model,
|
|
|
|
$el = this.$el,
|
|
|
|
tmpl = this.template,
|
|
|
|
controls = this.controls,
|
|
|
|
data = {
|
|
|
|
'className': _.result(this, 'className'),
|
|
|
|
'legendClass': _.result(this, 'legendClass'),
|
|
|
|
'contentClass': _.result(this, 'contentClass'),
|
|
|
|
'collapse': _.result(this, 'collapse'),
|
|
|
|
},
|
|
|
|
idx = (this.tabIndex * 100),
|
|
|
|
evalF = function(f, d, m) {
|
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
|
|
|
};
|
|
|
|
|
|
|
|
this.$el.empty();
|
|
|
|
|
|
|
|
_.each(this.schema, function(o) {
|
|
|
|
idx++;
|
|
|
|
if (!o.version_compatible || !evalF(o.visible, o, m)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!o.fields)
|
|
|
|
return;
|
|
|
|
|
|
|
|
var d = _.extend({}, data, o),
|
|
|
|
h = $((tmpl['header'])(d)).appendTo($el),
|
|
|
|
el = $((tmpl['content'])(d)).appendTo(h);
|
|
|
|
|
|
|
|
o.fields.each(function(f) {
|
|
|
|
var cntr = new(f.get('control'))({
|
|
|
|
field: f,
|
|
|
|
model: m,
|
|
|
|
tabIndex: idx,
|
|
|
|
});
|
|
|
|
el.append(cntr.render().$el);
|
|
|
|
controls.push(cntr);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
getValueFromDOM: function() {
|
|
|
|
return '';
|
|
|
|
},
|
|
|
|
events: {},
|
|
|
|
});
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.Fieldset = Backform.Dialog.extend({
|
2016-01-17 16:51:02 +00:00
|
|
|
className: function() {
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
return 'set-group pg-el-12';
|
2016-01-17 16:51:02 +00:00
|
|
|
},
|
|
|
|
tabPanelClassName: function() {
|
|
|
|
return Backform.tabClassName;
|
|
|
|
},
|
2018-01-12 07:29:51 +00:00
|
|
|
fieldsetClass: Backform.setGroupClassName,
|
2016-01-17 16:51:02 +00:00
|
|
|
legendClass: 'badge',
|
2018-10-10 11:43:26 +00:00
|
|
|
contentClass: Backform.setGroupContentClassName + ' collapse show',
|
2015-06-30 05:51:55 +00:00
|
|
|
template: {
|
|
|
|
'header': _.template([
|
2016-01-17 16:51:02 +00:00
|
|
|
'<fieldset class="<%=fieldsetClass%>" <%=disabled ? "disabled" : ""%>>',
|
2016-03-07 11:48:24 +00:00
|
|
|
' <% if (legend != false) { %>',
|
2019-01-03 14:50:24 +00:00
|
|
|
' <legend class="<%=legendClass%>" <%=collapse ? "data-toggle=\'collapse\'" : ""%> data-target="#<%=cId%>"><%=collapse ? "<span class=\'caret\'></span>" : "" %><%=label%></legend>',
|
2016-03-07 11:48:24 +00:00
|
|
|
' <% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</fieldset>',
|
|
|
|
].join('\n')),
|
2015-06-30 05:51:55 +00:00
|
|
|
'content': _.template(
|
2016-01-17 16:51:02 +00:00
|
|
|
' <div id="<%= cId %>" class="<%=contentClass%>"></div>'
|
2018-01-12 07:29:51 +00:00
|
|
|
),
|
|
|
|
},
|
2016-01-17 16:51:02 +00:00
|
|
|
collapse: true,
|
2015-06-30 05:51:55 +00:00
|
|
|
render: function() {
|
|
|
|
this.cleanup();
|
|
|
|
|
|
|
|
var m = this.model,
|
2018-01-12 07:29:51 +00:00
|
|
|
$el = this.$el,
|
|
|
|
tmpl = this.template,
|
|
|
|
controls = this.controls,
|
|
|
|
data = {
|
|
|
|
'className': _.result(this, 'className'),
|
|
|
|
'fieldsetClass': _.result(this, 'fieldsetClass'),
|
|
|
|
'legendClass': _.result(this, 'legendClass'),
|
|
|
|
'contentClass': _.result(this, 'contentClass'),
|
|
|
|
'collapse': _.result(this, 'collapse'),
|
|
|
|
},
|
|
|
|
idx = (this.tabIndex * 100),
|
|
|
|
evalF = function(f, d, m) {
|
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
|
|
|
};
|
2015-06-30 05:51:55 +00:00
|
|
|
|
|
|
|
this.$el.empty();
|
|
|
|
|
|
|
|
_.each(this.schema, function(o) {
|
2016-03-06 13:20:05 +00:00
|
|
|
idx++;
|
|
|
|
if (!o.version_compatible || !evalF(o.visible, o, m)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-06-30 05:51:55 +00:00
|
|
|
if (!o.fields)
|
|
|
|
return;
|
|
|
|
|
2016-01-17 16:51:02 +00:00
|
|
|
var d = _.extend({}, data, o),
|
2018-01-12 07:29:51 +00:00
|
|
|
h = $((tmpl['header'])(d)).appendTo($el),
|
|
|
|
el = $((tmpl['content'])(d)).appendTo(h);
|
2015-06-30 05:51:55 +00:00
|
|
|
|
|
|
|
o.fields.each(function(f) {
|
2018-01-12 07:29:51 +00:00
|
|
|
var cntr = new(f.get('control'))({
|
2015-06-30 05:51:55 +00:00
|
|
|
field: f,
|
2016-03-06 13:20:05 +00:00
|
|
|
model: m,
|
2018-01-12 07:29:51 +00:00
|
|
|
tabIndex: idx,
|
2015-06-30 05:51:55 +00:00
|
|
|
});
|
|
|
|
el.append(cntr.render().$el);
|
|
|
|
controls.push(cntr);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
getValueFromDOM: function() {
|
2018-01-12 07:29:51 +00:00
|
|
|
return '';
|
2015-06-30 05:51:55 +00:00
|
|
|
},
|
2018-01-12 07:29:51 +00:00
|
|
|
events: {},
|
2015-06-30 05:51:55 +00:00
|
|
|
});
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.generateGridColumnsFromModel =
|
2016-02-03 10:55:21 +00:00
|
|
|
function(node_info, m, type, cols, node) {
|
2018-01-12 07:29:51 +00:00
|
|
|
var groups = Backform.generateViewSchema(
|
|
|
|
node_info, m, type, node, true, true
|
|
|
|
),
|
|
|
|
schema = [],
|
|
|
|
columns = [],
|
|
|
|
func,
|
|
|
|
idx = 0;
|
2015-11-19 17:45:48 +00:00
|
|
|
|
2015-12-04 10:19:08 +00:00
|
|
|
// Create another array if cols is of type object & store its keys in that array,
|
|
|
|
// If cols is object then chances that we have custom width class attached with in.
|
2015-12-17 13:00:39 +00:00
|
|
|
if (_.isNull(cols) || _.isUndefined(cols)) {
|
|
|
|
func = function(f) {
|
|
|
|
f.cell_priority = idx;
|
|
|
|
idx = idx + 1;
|
2015-12-04 10:19:08 +00:00
|
|
|
|
2015-12-17 13:00:39 +00:00
|
|
|
// We can also provide custom header cell class in schema itself,
|
|
|
|
// But we will give priority to extraClass attached in cols
|
|
|
|
// If headerCell property is already set by cols then skip extraClass property from schema
|
|
|
|
if (!(f.headerCell) && f.cellHeaderClasses) {
|
|
|
|
f.headerCell = Backgrid.Extension.CustomHeaderCell;
|
2015-11-19 17:45:48 +00:00
|
|
|
}
|
2015-12-17 13:00:39 +00:00
|
|
|
};
|
|
|
|
} else if (_.isArray(cols)) {
|
|
|
|
func = function(f) {
|
|
|
|
f.cell_priority = _.indexOf(cols, f.name);
|
2015-12-04 10:19:08 +00:00
|
|
|
|
|
|
|
// We can also provide custom header cell class in schema itself,
|
|
|
|
// But we will give priority to extraClass attached in cols
|
|
|
|
// If headerCell property is already set by cols then skip extraClass property from schema
|
2015-12-17 13:00:39 +00:00
|
|
|
if ((!f.headerCell) && f.cellHeaderClasses) {
|
2015-12-04 10:19:08 +00:00
|
|
|
f.headerCell = Backgrid.Extension.CustomHeaderCell;
|
|
|
|
}
|
2015-12-17 13:00:39 +00:00
|
|
|
};
|
2018-01-12 07:29:51 +00:00
|
|
|
} else if (_.isObject(cols)) {
|
2015-12-17 13:00:39 +00:00
|
|
|
var tblCols = Object.keys(cols);
|
|
|
|
func = function(f) {
|
2018-01-12 07:29:51 +00:00
|
|
|
var val = (f.name in cols) && cols[f.name],
|
|
|
|
i;
|
2015-12-17 13:00:39 +00:00
|
|
|
|
|
|
|
if (_.isNull(val) || _.isUndefined(val)) {
|
|
|
|
f.cell_priority = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (_.isObject(val)) {
|
|
|
|
if ('index' in val) {
|
|
|
|
f.cell_priority = val['index'];
|
|
|
|
idx = (idx > val['index']) ? idx + 1 : val['index'];
|
|
|
|
} else {
|
2018-01-12 07:29:51 +00:00
|
|
|
i = _.indexOf(tblCols, f.name);
|
2015-12-17 13:00:39 +00:00
|
|
|
f.cell_priority = idx = ((i > idx) ? i : idx);
|
|
|
|
idx = idx + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We can also provide custom header cell class in schema itself,
|
|
|
|
// But we will give priority to extraClass attached in cols
|
|
|
|
// If headerCell property is already set by cols then skip extraClass property from schema
|
|
|
|
if (!f.headerCell) {
|
|
|
|
if (f.cellHeaderClasses) {
|
|
|
|
f.headerCell = Backgrid.Extension.CustomHeaderCell;
|
|
|
|
}
|
|
|
|
if ('class' in val && _.isString(val['class'])) {
|
|
|
|
f.headerCell = Backgrid.Extension.CustomHeaderCell;
|
|
|
|
f.cellHeaderClasses = (f.cellHeaderClasses || '') + ' ' + val['class'];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_.isString(val)) {
|
2018-01-12 07:29:51 +00:00
|
|
|
i = _.indexOf(tblCols, f.name);
|
2015-12-17 13:00:39 +00:00
|
|
|
|
|
|
|
f.cell_priority = idx = ((i > idx) ? i : idx);
|
|
|
|
idx = idx + 1;
|
|
|
|
|
|
|
|
if (!f.headerCell) {
|
|
|
|
f.headerCell = Backgrid.Extension.CustomHeaderCell;
|
|
|
|
}
|
|
|
|
f.cellHeaderClasses = (f.cellHeaderClasses || '') + ' ' + val;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2015-12-04 10:19:08 +00:00
|
|
|
|
2015-12-17 13:00:39 +00:00
|
|
|
// Prepare columns for backgrid
|
2018-01-12 07:29:51 +00:00
|
|
|
_.each(groups, function(group) {
|
2016-01-17 17:01:49 +00:00
|
|
|
_.each(group.fields, function(f) {
|
2016-01-17 16:51:02 +00:00
|
|
|
if (!f.cell) {
|
2015-12-17 13:00:39 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Check custom property in cols & if it is present then attach it to current cell
|
|
|
|
func(f);
|
|
|
|
if (f.cell_priority != -1) {
|
2015-11-19 17:45:48 +00:00
|
|
|
columns.push(f);
|
|
|
|
}
|
|
|
|
});
|
2016-01-17 17:01:49 +00:00
|
|
|
schema.push(group);
|
2015-11-17 06:21:09 +00:00
|
|
|
});
|
2015-11-19 17:45:48 +00:00
|
|
|
return {
|
|
|
|
'columns': _.sortBy(columns, function(c) {
|
|
|
|
return c.cell_priority;
|
|
|
|
}),
|
2018-01-12 07:29:51 +00:00
|
|
|
'schema': schema,
|
2015-11-19 17:45:48 +00:00
|
|
|
};
|
2015-11-17 06:21:09 +00:00
|
|
|
};
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.UniqueColCollectionControl = Backform.Control.extend({
|
2015-12-17 13:00:39 +00:00
|
|
|
initialize: function() {
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.Control.prototype.initialize.apply(this, arguments);
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
var uniqueCol = this.field.get('uniqueCol') || [],
|
|
|
|
m = this.field.get('model'),
|
|
|
|
schema = m.prototype.schema || m.__super__.schema,
|
|
|
|
columns = [],
|
|
|
|
self = this;
|
Resolved few intialization issue with Node model data, moved the
privileges functionality out of the backform.pgadmin.js to make it more
modular. Now - privileges will expect the privileges data in following
format:
<name_of_the_property> : [{
"privileges": [{
"privilege_type": <privilege_type>,
"privilege": true,
"with_grant": false
},
...
],
"grantee": <grantee>,
"grantor": <grantor>
},
...
]
Example:
acl": [{
"privileges": [{
"privilege_type": "CONNECT",
"privilege": true,
"with_grant": false
}],
"grantee": '',
"grantor": 'ashesh'
},{
"privileges": [{
"privilege_type": "CREATE",
"privilege": true,
"with_grant": false
},{
"privilege": true,
"privilege_type": "TEMPORARY",
"with_grant": false
}],
"grantee": test,
"grantor": ashesh
}]
2015-12-23 06:40:20 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
_.each(schema, function(s) {
|
|
|
|
columns.push(s.id);
|
|
|
|
});
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
// Check if unique columns provided are also in model attributes.
|
|
|
|
if (uniqueCol.length > _.intersection(columns, uniqueCol).length) {
|
|
|
|
var errorMsg = 'Developer: Unique columns [ ' + _.difference(uniqueCol, columns) + ' ] not found in collection model [ ' + columns + ' ].';
|
|
|
|
alert(errorMsg);
|
|
|
|
}
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
var collection = self.collection = self.model.get(self.field.get('name'));
|
2016-01-09 12:29:56 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
if (!collection) {
|
|
|
|
collection = self.collection = new(pgAdmin.Browser.Node.Collection)(
|
|
|
|
null, {
|
|
|
|
model: self.field.get('model'),
|
|
|
|
silent: true,
|
|
|
|
handler: self.model,
|
|
|
|
top: self.model.top || self.model,
|
|
|
|
attrName: self.field.get('name'),
|
|
|
|
});
|
|
|
|
self.model.set(self.field.get('name'), collection, {
|
|
|
|
silent: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.field.get('version_compatible')) {
|
|
|
|
self.listenTo(collection, 'add', self.collectionChanged);
|
|
|
|
self.listenTo(collection, 'change', self.collectionChanged);
|
|
|
|
}
|
2015-12-17 13:00:39 +00:00
|
|
|
},
|
2016-03-07 10:35:01 +00:00
|
|
|
cleanup: function() {
|
2018-01-12 07:29:51 +00:00
|
|
|
this.stopListening(this.collection, 'change', this.collectionChanged);
|
2016-01-09 12:29:56 +00:00
|
|
|
|
2016-02-10 06:42:06 +00:00
|
|
|
if (this.field.get('version_compatible')) {
|
2018-01-12 07:29:51 +00:00
|
|
|
this.stopListening(self.collection, 'add', this.collectionChanged);
|
|
|
|
this.stopListening(self.collection, 'change', this.collectionChanged);
|
2016-01-12 06:31:45 +00:00
|
|
|
}
|
2016-03-07 10:35:01 +00:00
|
|
|
if (this.grid) {
|
|
|
|
this.grid.remove();
|
|
|
|
delete this.grid;
|
|
|
|
}
|
|
|
|
this.$el.empty();
|
2016-01-09 12:29:56 +00:00
|
|
|
},
|
2015-12-17 13:00:39 +00:00
|
|
|
collectionChanged: function(newModel, coll, op) {
|
2016-01-09 12:29:56 +00:00
|
|
|
var uniqueCol = this.field.get('uniqueCol') || [],
|
2018-01-12 07:29:51 +00:00
|
|
|
uniqueChangedAttr = [],
|
|
|
|
self = this;
|
2016-01-09 12:29:56 +00:00
|
|
|
// Check if changed model attributes are also in unique columns. And then only check for uniqueness.
|
|
|
|
if (newModel.attributes) {
|
|
|
|
_.each(uniqueCol, function(col) {
|
2018-01-12 07:29:51 +00:00
|
|
|
if (_.has(newModel.attributes, col)) {
|
|
|
|
uniqueChangedAttr.push(col);
|
|
|
|
}
|
2016-01-09 12:29:56 +00:00
|
|
|
});
|
2018-01-12 07:29:51 +00:00
|
|
|
if (uniqueChangedAttr.length == 0) {
|
|
|
|
return;
|
2015-12-17 13:00:39 +00:00
|
|
|
}
|
2016-01-09 12:29:56 +00:00
|
|
|
} else {
|
|
|
|
return;
|
|
|
|
}
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
var collection = this.model.get(this.field.get('name'));
|
2018-01-12 07:29:51 +00:00
|
|
|
this.stopListening(collection, 'change', this.collectionChanged);
|
2016-01-09 12:29:56 +00:00
|
|
|
// Check if changed attribute's value of new/updated model also exist for another model in collection.
|
|
|
|
// If duplicate value exists then set the attribute's value of new/updated model to its previous values.
|
|
|
|
var m = undefined,
|
2018-01-12 07:29:51 +00:00
|
|
|
oldModel = undefined;
|
2016-01-09 12:29:56 +00:00
|
|
|
collection.each(function(model) {
|
|
|
|
if (newModel != model) {
|
2018-01-12 07:29:51 +00:00
|
|
|
var duplicateAttrValues = [];
|
2016-01-09 12:29:56 +00:00
|
|
|
_.each(uniqueCol, function(attr) {
|
2017-07-18 14:13:16 +00:00
|
|
|
var attrValue = newModel.get(attr);
|
2016-01-09 12:29:56 +00:00
|
|
|
if (!_.isUndefined(attrValue) && attrValue == model.get(attr)) {
|
2018-01-12 07:29:51 +00:00
|
|
|
duplicateAttrValues.push(attrValue);
|
2015-12-17 13:00:39 +00:00
|
|
|
}
|
2016-01-09 12:29:56 +00:00
|
|
|
});
|
|
|
|
if (duplicateAttrValues.length == uniqueCol.length) {
|
|
|
|
m = newModel;
|
|
|
|
// Keep reference of model to make it visible in dialog.
|
|
|
|
oldModel = model;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (m) {
|
|
|
|
if (op && op.add) {
|
|
|
|
// Remove duplicate model.
|
|
|
|
setTimeout(function() {
|
|
|
|
collection.remove(m);
|
|
|
|
}, 0);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Set model value to its previous value as its new value is
|
|
|
|
* conflicting with another model value.
|
|
|
|
*/
|
|
|
|
|
2016-01-15 13:40:29 +00:00
|
|
|
m.set(uniqueChangedAttr[0], m.previous(uniqueChangedAttr[0]));
|
2016-01-09 12:29:56 +00:00
|
|
|
}
|
|
|
|
if (oldModel) {
|
|
|
|
var idx = collection.indexOf(oldModel);
|
|
|
|
if (idx > -1) {
|
|
|
|
var newRow = self.grid.body.rows[idx].$el;
|
2018-01-12 07:29:51 +00:00
|
|
|
newRow.addClass('new');
|
2016-01-09 12:29:56 +00:00
|
|
|
$(newRow).pgMakeVisible('backform-tab');
|
|
|
|
setTimeout(function() {
|
2018-01-12 07:29:51 +00:00
|
|
|
newRow.removeClass('new');
|
|
|
|
}, 3000);
|
2016-01-09 12:29:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
this.listenTo(collection, 'change', this.collectionChanged);
|
2015-12-17 13:00:39 +00:00
|
|
|
},
|
|
|
|
render: function() {
|
2016-03-07 10:35:01 +00:00
|
|
|
// Clean up existing elements
|
2016-04-13 14:54:17 +00:00
|
|
|
|
2016-04-01 09:55:53 +00:00
|
|
|
this.undelegateEvents();
|
2016-04-13 14:54:17 +00:00
|
|
|
this.$el.empty();
|
2016-03-07 10:35:01 +00:00
|
|
|
|
2015-12-17 13:00:39 +00:00
|
|
|
var field = _.defaults(this.field.toJSON(), this.defaults),
|
2018-01-12 07:29:51 +00:00
|
|
|
attributes = this.model.toJSON(),
|
|
|
|
attrArr = field.name.split('.'),
|
|
|
|
name = attrArr.shift(),
|
|
|
|
path = attrArr.join('.'),
|
|
|
|
rawValue = this.keyPathAccessor(attributes[name], path),
|
|
|
|
data = _.extend(field, {
|
|
|
|
rawValue: rawValue,
|
|
|
|
value: this.formatter.fromRaw(rawValue, this.model),
|
|
|
|
attributes: attributes,
|
|
|
|
formatter: this.formatter,
|
|
|
|
}),
|
|
|
|
evalF = function(f, d, m) {
|
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
|
|
|
};
|
2015-12-17 13:00:39 +00:00
|
|
|
|
|
|
|
// Evaluate the disabled, visible, required, canAdd, & canDelete option
|
|
|
|
_.extend(data, {
|
2016-02-10 06:42:06 +00:00
|
|
|
disabled: (field.version_compatible &&
|
2018-01-12 07:29:51 +00:00
|
|
|
evalF.apply(this.field, [data.disabled, data, this.model])
|
|
|
|
),
|
|
|
|
visible: evalF.apply(this.field, [data.visible, data, this.model]),
|
2016-03-11 09:36:26 +00:00
|
|
|
required: evalF.apply(this.field, [data.required, data, this.model]),
|
2016-02-10 06:42:06 +00:00
|
|
|
canAdd: (field.version_compatible &&
|
2016-03-11 09:36:26 +00:00
|
|
|
evalF.apply(this.field, [data.canAdd, data, this.model])
|
2018-01-12 07:29:51 +00:00
|
|
|
),
|
2016-04-13 14:54:17 +00:00
|
|
|
canAddRow: data.canAddRow,
|
2016-03-11 09:36:26 +00:00
|
|
|
canDelete: evalF.apply(this.field, [data.canDelete, data, this.model]),
|
2018-01-12 07:29:51 +00:00
|
|
|
canEdit: evalF.apply(this.field, [data.canEdit, data, this.model]),
|
|
|
|
});
|
|
|
|
_.extend(data, {
|
|
|
|
add_label: '',
|
2015-12-17 13:00:39 +00:00
|
|
|
});
|
2016-01-12 06:31:45 +00:00
|
|
|
|
2016-03-07 10:35:01 +00:00
|
|
|
// This control is not visible, we should remove it.
|
|
|
|
if (!data.visible) {
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2016-03-11 09:36:26 +00:00
|
|
|
this.control_data = _.clone(data);
|
|
|
|
|
2015-12-17 13:00:39 +00:00
|
|
|
// Show Backgrid Control
|
2017-07-18 14:13:16 +00:00
|
|
|
var grid = this.showGridControl(data);
|
2015-12-17 13:00:39 +00:00
|
|
|
|
|
|
|
this.$el.html(grid).addClass(field.name);
|
|
|
|
this.updateInvalid();
|
|
|
|
|
2016-04-01 09:55:53 +00:00
|
|
|
this.delegateEvents();
|
2015-12-17 13:00:39 +00:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
showGridControl: function(data) {
|
2016-07-25 13:31:17 +00:00
|
|
|
var self = this,
|
2018-01-12 07:29:51 +00:00
|
|
|
gridHeader = _.template([
|
2016-01-12 06:31:45 +00:00
|
|
|
'<div class="subnode-header">',
|
2016-06-21 11:15:08 +00:00
|
|
|
' <label class="control-label pg-el-sm-10"><%-label%></label>',
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
' <button class="btn btn-sm-sq btn-secondary add fa fa-plus" <%=canAdd ? "" : "disabled=\'disabled\'"%> title="' + _('Add new row') + '"><%-add_label%></button>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
gridBody = $('<div class="pgadmin-control-group backgrid form-group pg-el-12 object subnode "></div>').append(
|
2018-01-12 07:29:51 +00:00
|
|
|
gridHeader(data)
|
|
|
|
);
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2016-07-25 13:31:17 +00:00
|
|
|
// Clean up existing grid if any (in case of re-render)
|
|
|
|
if (self.grid) {
|
|
|
|
self.grid.remove();
|
|
|
|
}
|
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
if (!(data.subnode)) {
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
2015-12-17 13:00:39 +00:00
|
|
|
var subnode = data.subnode.schema ? data.subnode : data.subnode.prototype,
|
2018-01-12 07:29:51 +00:00
|
|
|
gridSchema = Backform.generateGridColumnsFromModel(
|
|
|
|
data.node_info, subnode, this.field.get('mode'), data.columns
|
|
|
|
);
|
2015-12-17 13:00:39 +00:00
|
|
|
|
|
|
|
// Set visibility of Add button
|
2016-01-12 06:31:45 +00:00
|
|
|
if (data.mode == 'properties') {
|
2018-01-12 07:29:51 +00:00
|
|
|
$(gridBody).find('button.add').remove();
|
2015-12-17 13:00:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert Delete Cell into Grid
|
2016-01-12 06:31:45 +00:00
|
|
|
if (!data.disabled && data.canDelete) {
|
2018-01-12 07:29:51 +00:00
|
|
|
gridSchema.columns.unshift({
|
|
|
|
name: 'pg-backform-delete',
|
|
|
|
label: '',
|
|
|
|
cell: Backgrid.Extension.DeleteCell,
|
|
|
|
editable: false,
|
|
|
|
cell_priority: -1,
|
|
|
|
canDeleteRow: data.canDeleteRow,
|
|
|
|
});
|
2015-12-17 13:00:39 +00:00
|
|
|
}
|
|
|
|
|
2016-03-04 11:00:17 +00:00
|
|
|
// Insert Edit Cell into Grid
|
|
|
|
if (data.disabled == false && data.canEdit) {
|
2018-01-12 07:29:51 +00:00
|
|
|
var editCell = Backgrid.Extension.ObjectCell.extend({
|
2018-01-22 11:28:04 +00:00
|
|
|
schema: gridSchema.schema,
|
2018-01-12 07:29:51 +00:00
|
|
|
});
|
2016-03-04 11:00:17 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
gridSchema.columns.unshift({
|
|
|
|
name: 'pg-backform-edit',
|
|
|
|
label: '',
|
|
|
|
cell: editCell,
|
|
|
|
cell_priority: -2,
|
|
|
|
canEditRow: data.canEditRow,
|
|
|
|
});
|
2016-03-04 11:00:17 +00:00
|
|
|
}
|
|
|
|
|
2015-12-17 13:00:39 +00:00
|
|
|
var collection = this.model.get(data.name);
|
2016-01-12 06:31:45 +00:00
|
|
|
|
2017-06-29 13:31:29 +00:00
|
|
|
var cellEditing = function(args) {
|
|
|
|
var that = this,
|
2016-03-22 17:04:46 +00:00
|
|
|
cell = args[0];
|
|
|
|
// Search for any other rows which are open.
|
2018-01-12 07:29:51 +00:00
|
|
|
this.each(function(m) {
|
2016-03-22 17:04:46 +00:00
|
|
|
// Check if row which we are about to close is not current row.
|
|
|
|
if (cell.model != m) {
|
2017-06-29 13:31:29 +00:00
|
|
|
var idx = that.indexOf(m);
|
2016-03-22 17:04:46 +00:00
|
|
|
if (idx > -1) {
|
2017-06-29 13:31:29 +00:00
|
|
|
var row = self.grid.body.rows[idx],
|
2018-01-12 07:29:51 +00:00
|
|
|
editCell = row.$el.find('.subnode-edit-in-process').parent();
|
2016-03-22 17:04:46 +00:00
|
|
|
// Only close row if it's open.
|
2018-01-12 07:29:51 +00:00
|
|
|
if (editCell.length > 0) {
|
2016-03-22 17:04:46 +00:00
|
|
|
var event = new Event('click');
|
|
|
|
editCell[0].dispatchEvent(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2018-01-12 07:29:51 +00:00
|
|
|
};
|
2016-03-22 17:04:46 +00:00
|
|
|
// Listen for any row which is about to enter in edit mode.
|
2018-01-12 07:29:51 +00:00
|
|
|
collection.on('enteringEditMode', cellEditing, collection);
|
2016-03-22 17:04:46 +00:00
|
|
|
|
2015-12-17 13:00:39 +00:00
|
|
|
// Initialize a new Grid instance
|
2018-01-12 07:29:51 +00:00
|
|
|
self.grid = new Backgrid.Grid({
|
2016-01-09 12:29:56 +00:00
|
|
|
columns: gridSchema.columns,
|
|
|
|
collection: collection,
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
className: 'backgrid table presentation table-bordered table-noouter-border table-hover',
|
2015-12-17 13:00:39 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Render subNode grid
|
2017-07-18 14:13:16 +00:00
|
|
|
var subNodeGrid = self.grid.render().$el;
|
2015-12-17 13:00:39 +00:00
|
|
|
|
|
|
|
// Combine Edit and Delete Cell
|
|
|
|
if (data.canDelete && data.canEdit) {
|
2018-01-12 07:29:51 +00:00
|
|
|
$(subNodeGrid).find('th.pg-backform-delete').remove();
|
|
|
|
$(subNodeGrid).find('th.pg-backform-edit').attr('colspan', '2');
|
2015-12-17 13:00:39 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
var $dialog = gridBody.append(subNodeGrid);
|
2015-12-17 13:00:39 +00:00
|
|
|
|
|
|
|
// Add button callback
|
|
|
|
if (!(data.disabled || data.canAdd == false)) {
|
2018-05-25 15:26:37 +00:00
|
|
|
$dialog.find('button.add').first().on('click',(e) => {
|
2016-01-09 12:29:56 +00:00
|
|
|
e.preventDefault();
|
2016-04-13 14:54:17 +00:00
|
|
|
var canAddRow = _.isFunction(data.canAddRow) ?
|
2018-01-12 07:29:51 +00:00
|
|
|
data.canAddRow.apply(self, [self.model]) : true;
|
2016-04-13 14:54:17 +00:00
|
|
|
if (canAddRow) {
|
2018-01-12 07:29:51 +00:00
|
|
|
// Close any existing expanded row before adding new one.
|
|
|
|
_.each(self.grid.body.rows, function(row) {
|
|
|
|
var editCell = row.$el.find('.subnode-edit-in-process').parent();
|
|
|
|
// Only close row if it's open.
|
|
|
|
if (editCell.length > 0) {
|
|
|
|
var event = new Event('click');
|
|
|
|
editCell[0].dispatchEvent(event);
|
|
|
|
}
|
|
|
|
});
|
2016-03-22 17:04:46 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
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 && collection) {
|
|
|
|
var isEmpty = false;
|
|
|
|
collection.each(function(model) {
|
|
|
|
var modelValues = [];
|
|
|
|
_.each(model.attributes, function(val) {
|
|
|
|
modelValues.push(val);
|
2016-04-13 14:54:17 +00:00
|
|
|
});
|
2018-01-12 07:29:51 +00:00
|
|
|
if (!_.some(modelValues, _.identity)) {
|
|
|
|
isEmpty = true;
|
2016-04-13 14:54:17 +00:00
|
|
|
}
|
2018-01-12 07:29:51 +00:00
|
|
|
});
|
|
|
|
if (isEmpty) {
|
|
|
|
return false;
|
2016-01-09 12:29:56 +00:00
|
|
|
}
|
2018-01-12 07:29:51 +00:00
|
|
|
}
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
$(self.grid.body.$el.find($('tr.new'))).removeClass('new');
|
|
|
|
var m = new(data.model)(null, {
|
|
|
|
silent: true,
|
|
|
|
handler: collection,
|
|
|
|
top: self.model.top || self.model,
|
|
|
|
collection: collection,
|
|
|
|
node_info: self.model.node_info,
|
|
|
|
});
|
|
|
|
collection.add(m);
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
var idx = collection.indexOf(m),
|
|
|
|
newRow = self.grid.body.rows[idx].$el;
|
2016-01-09 12:29:56 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
newRow.addClass('new');
|
2019-04-23 12:02:00 +00:00
|
|
|
$(newRow).pgMakeBackgridVisible('.backform-tab');
|
2016-01-09 12:29:56 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
return false;
|
2016-04-13 14:54:17 +00:00
|
|
|
}
|
2015-12-17 13:00:39 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
return $dialog;
|
2016-01-11 16:02:03 +00:00
|
|
|
},
|
2016-04-07 10:51:16 +00:00
|
|
|
clearInvalid: function() {
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$el.removeClass('subnode-error');
|
|
|
|
this.$el.find('.pgadmin-control-error-message').remove();
|
2016-04-07 10:51:16 +00:00
|
|
|
return this;
|
|
|
|
},
|
2016-01-11 16:02:03 +00:00
|
|
|
updateInvalid: function() {
|
|
|
|
var self = this,
|
2018-01-12 07:29:51 +00:00
|
|
|
errorModel = this.model.errorModel;
|
2015-12-17 13:00:39 +00:00
|
|
|
|
2016-01-11 16:02:03 +00:00
|
|
|
if (!(errorModel instanceof Backbone.Model)) return this;
|
|
|
|
|
|
|
|
this.clearInvalid();
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$el.find('.subnode-body').each(function() {
|
|
|
|
var error = self.keyPathAccessor(
|
|
|
|
errorModel.toJSON(), self.field.get('name')
|
|
|
|
);
|
2016-01-11 16:02:03 +00:00
|
|
|
|
|
|
|
if (_.isEmpty(error)) return;
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
self.$el.addClass('subnode-error').append(
|
|
|
|
$('<div></div>').addClass(
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
'pgadmin-control-error-message pg-el-offset-4 pg-el-8 help-block'
|
2018-01-12 07:29:51 +00:00
|
|
|
).text(error)
|
|
|
|
);
|
2016-01-11 16:02:03 +00:00
|
|
|
});
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2015-12-17 13:00:39 +00:00
|
|
|
});
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.SubNodeCollectionControl = Backform.Control.extend({
|
2017-06-29 13:31:29 +00:00
|
|
|
row: Backgrid.Row,
|
2015-10-28 17:06:09 +00:00
|
|
|
render: function() {
|
|
|
|
var field = _.defaults(this.field.toJSON(), this.defaults),
|
2018-01-12 07:29:51 +00:00
|
|
|
attributes = this.model.toJSON(),
|
|
|
|
attrArr = field.name.split('.'),
|
|
|
|
name = attrArr.shift(),
|
|
|
|
path = attrArr.join('.'),
|
|
|
|
rawValue = this.keyPathAccessor(attributes[name], path),
|
|
|
|
data = _.extend(field, {
|
|
|
|
rawValue: rawValue,
|
|
|
|
value: this.formatter.fromRaw(rawValue, this.model),
|
|
|
|
attributes: attributes,
|
|
|
|
formatter: this.formatter,
|
|
|
|
}),
|
|
|
|
evalF = function(f, d, m) {
|
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
|
|
|
};
|
2015-10-28 17:06:09 +00:00
|
|
|
|
|
|
|
// Evaluate the disabled, visible, required, canAdd, cannEdit & canDelete option
|
|
|
|
_.extend(data, {
|
2016-03-23 07:20:49 +00:00
|
|
|
disabled: evalF(data.disabled, data, this.model),
|
2018-01-12 07:29:51 +00:00
|
|
|
visible: evalF(data.visible, data, this.model),
|
2016-03-23 07:20:49 +00:00
|
|
|
required: evalF(data.required, data, this.model),
|
|
|
|
canAdd: evalF(data.canAdd, data, this.model),
|
2016-04-13 14:54:17 +00:00
|
|
|
canAddRow: data.canAddRow,
|
2016-03-23 07:20:49 +00:00
|
|
|
canEdit: evalF(data.canEdit, data, this.model),
|
2018-01-12 07:29:51 +00:00
|
|
|
canDelete: evalF(data.canDelete, data, this.model),
|
2018-03-02 11:11:26 +00:00
|
|
|
showError: data.showError || true,
|
2015-10-28 17:06:09 +00:00
|
|
|
});
|
|
|
|
// Show Backgrid Control
|
2018-01-12 07:29:51 +00:00
|
|
|
var grid = (data.subnode == undefined) ? '' : this.showGridControl(data);
|
2015-10-28 17:06:09 +00:00
|
|
|
|
2017-06-29 13:31:29 +00:00
|
|
|
// Clean up first
|
2017-07-26 11:55:46 +00:00
|
|
|
this.$el.removeClass(Backform.hiddenClassName);
|
2017-06-29 13:31:29 +00:00
|
|
|
|
|
|
|
if (!data.visible)
|
2017-07-26 11:55:46 +00:00
|
|
|
this.$el.addClass(Backform.hiddenClassName);
|
2017-06-29 13:31:29 +00:00
|
|
|
|
2015-10-28 17:06:09 +00:00
|
|
|
this.$el.html(grid).addClass(field.name);
|
|
|
|
this.updateInvalid();
|
|
|
|
|
|
|
|
return this;
|
|
|
|
},
|
2015-12-17 13:00:39 +00:00
|
|
|
updateInvalid: function() {
|
|
|
|
var self = this;
|
|
|
|
var errorModel = this.model.errorModel;
|
|
|
|
if (!(errorModel instanceof Backbone.Model)) return this;
|
|
|
|
|
|
|
|
this.clearInvalid();
|
|
|
|
|
|
|
|
var attrArr = self.field.get('name').split('.'),
|
|
|
|
path = attrArr.join('.'),
|
|
|
|
error = self.keyPathAccessor(errorModel.toJSON(), path);
|
|
|
|
|
|
|
|
if (_.isEmpty(error)) return;
|
|
|
|
|
2018-03-02 11:11:26 +00:00
|
|
|
if (self.field.get('showError')) {
|
|
|
|
self.$el.addClass('subnode-error').append(
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
$('<div></div>').addClass('pgadmin-control-error-message pg-el-offset-4 pg-el-8 help-block').text(error)
|
2018-03-02 11:11:26 +00:00
|
|
|
);
|
|
|
|
}
|
2015-12-17 13:00:39 +00:00
|
|
|
},
|
2016-09-26 12:43:13 +00:00
|
|
|
cleanup: function() {
|
|
|
|
// Clean up existing grid if any (in case of re-render)
|
|
|
|
if (this.grid) {
|
|
|
|
this.grid.remove();
|
|
|
|
}
|
2017-01-20 13:24:37 +00:00
|
|
|
if (this.collection) {
|
2018-01-12 07:29:51 +00:00
|
|
|
this.collection.off('enteringEditMode');
|
2017-01-20 13:24:37 +00:00
|
|
|
}
|
2016-09-26 12:43:13 +00:00
|
|
|
},
|
2016-04-07 10:51:16 +00:00
|
|
|
clearInvalid: function() {
|
2018-03-02 11:11:26 +00:00
|
|
|
if (this.field.get('showError')) {
|
|
|
|
this.$el.removeClass('subnode-error');
|
|
|
|
this.$el.find('.pgadmin-control-error-message').remove();
|
|
|
|
}
|
2016-04-07 10:51:16 +00:00
|
|
|
return this;
|
|
|
|
},
|
2015-10-28 17:06:09 +00:00
|
|
|
showGridControl: function(data) {
|
2016-07-25 13:31:17 +00:00
|
|
|
var self = this,
|
2018-01-12 07:29:51 +00:00
|
|
|
gridHeader = ['<div class=\'subnode-header\'>',
|
|
|
|
' <label class=\'control-label pg-el-sm-10\'>' + data.label + '</label>',
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
' <button class=\'btn btn-sm-sq btn-secondary add fa fa-plus\' title=\'' + _('Add new row') + '\'></button>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n'),
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
gridBody = $('<div class=\'pgadmin-control-group backgrid form-group pg-el-12 object subnode\'></div>').append(gridHeader);
|
2015-10-28 17:06:09 +00:00
|
|
|
|
|
|
|
var subnode = data.subnode.schema ? data.subnode : data.subnode.prototype,
|
2018-01-12 07:29:51 +00:00
|
|
|
gridSchema = Backform.generateGridColumnsFromModel(
|
|
|
|
data.node_info, subnode, this.field.get('mode'), data.columns, data.schema_node
|
2018-07-05 10:38:43 +00:00
|
|
|
);
|
2015-10-28 17:06:09 +00:00
|
|
|
|
2016-07-25 13:31:17 +00:00
|
|
|
// Clean up existing grid if any (in case of re-render)
|
|
|
|
if (self.grid) {
|
|
|
|
self.grid.remove();
|
|
|
|
}
|
|
|
|
|
2015-10-28 17:06:09 +00:00
|
|
|
// Set visibility of Add button
|
|
|
|
if (data.disabled || data.canAdd == false) {
|
2018-01-12 07:29:51 +00:00
|
|
|
$(gridBody).find('button.add').remove();
|
2015-10-28 17:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert Delete Cell into Grid
|
|
|
|
if (data.disabled == false && data.canDelete) {
|
2018-01-12 07:29:51 +00:00
|
|
|
gridSchema.columns.unshift({
|
|
|
|
name: 'pg-backform-delete',
|
|
|
|
label: '',
|
|
|
|
cell: Backgrid.Extension.DeleteCell,
|
|
|
|
editable: false,
|
|
|
|
cell_priority: -1,
|
|
|
|
canDeleteRow: data.canDeleteRow,
|
|
|
|
customDeleteMsg: data.customDeleteMsg,
|
|
|
|
customDeleteTitle: data.customDeleteTitle,
|
|
|
|
});
|
2015-10-28 17:06:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Insert Edit Cell into Grid
|
|
|
|
if (data.disabled == false && data.canEdit) {
|
2018-01-12 07:29:51 +00:00
|
|
|
var editCell = Backgrid.Extension.ObjectCell.extend({
|
|
|
|
schema: gridSchema.schema,
|
2016-03-10 12:03:33 +00:00
|
|
|
}),
|
|
|
|
canEdit = self.field.has('canEdit') &&
|
|
|
|
self.field.get('canEdit') || true;
|
2015-10-28 17:06:09 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
gridSchema.columns.unshift({
|
|
|
|
name: 'pg-backform-edit',
|
|
|
|
label: '',
|
|
|
|
cell: editCell,
|
|
|
|
cell_priority: -2,
|
|
|
|
editable: canEdit,
|
|
|
|
canEditRow: data.canEditRow,
|
|
|
|
});
|
2015-10-28 17:06:09 +00:00
|
|
|
}
|
|
|
|
|
Resolved few intialization issue with Node model data, moved the
privileges functionality out of the backform.pgadmin.js to make it more
modular. Now - privileges will expect the privileges data in following
format:
<name_of_the_property> : [{
"privileges": [{
"privilege_type": <privilege_type>,
"privilege": true,
"with_grant": false
},
...
],
"grantee": <grantee>,
"grantor": <grantor>
},
...
]
Example:
acl": [{
"privileges": [{
"privilege_type": "CONNECT",
"privilege": true,
"with_grant": false
}],
"grantee": '',
"grantor": 'ashesh'
},{
"privileges": [{
"privilege_type": "CREATE",
"privilege": true,
"with_grant": false
},{
"privilege": true,
"privilege_type": "TEMPORARY",
"with_grant": false
}],
"grantee": test,
"grantor": ashesh
}]
2015-12-23 06:40:20 +00:00
|
|
|
var collection = self.model.get(data.name);
|
|
|
|
|
|
|
|
if (!collection) {
|
2018-01-12 07:29:51 +00:00
|
|
|
collection = new(pgBrowser.Node.Collection)(null, {
|
2016-09-22 14:27:59 +00:00
|
|
|
handler: self.model.handler || self.model,
|
2018-01-12 07:29:51 +00:00
|
|
|
model: data.model,
|
|
|
|
top: self.model.top || self.model,
|
|
|
|
silent: true,
|
|
|
|
});
|
|
|
|
self.model.set(data.name, collection, {
|
|
|
|
silent: true,
|
Resolved few intialization issue with Node model data, moved the
privileges functionality out of the backform.pgadmin.js to make it more
modular. Now - privileges will expect the privileges data in following
format:
<name_of_the_property> : [{
"privileges": [{
"privilege_type": <privilege_type>,
"privilege": true,
"with_grant": false
},
...
],
"grantee": <grantee>,
"grantor": <grantor>
},
...
]
Example:
acl": [{
"privileges": [{
"privilege_type": "CONNECT",
"privilege": true,
"with_grant": false
}],
"grantee": '',
"grantor": 'ashesh'
},{
"privileges": [{
"privilege_type": "CREATE",
"privilege": true,
"with_grant": false
},{
"privilege": true,
"privilege_type": "TEMPORARY",
"with_grant": false
}],
"grantee": test,
"grantor": ashesh
}]
2015-12-23 06:40:20 +00:00
|
|
|
});
|
|
|
|
}
|
2016-03-22 17:04:46 +00:00
|
|
|
|
2016-09-22 14:27:59 +00:00
|
|
|
var cellEditing = function(args) {
|
2016-03-22 17:04:46 +00:00
|
|
|
var self = this,
|
|
|
|
cell = args[0];
|
|
|
|
// Search for any other rows which are open.
|
2018-01-12 07:29:51 +00:00
|
|
|
this.each(function(m) {
|
2016-03-22 17:04:46 +00:00
|
|
|
// Check if row which we are about to close is not current row.
|
|
|
|
if (cell.model != m) {
|
|
|
|
var idx = self.indexOf(m);
|
|
|
|
if (idx > -1) {
|
|
|
|
var row = grid.body.rows[idx],
|
2018-01-12 07:29:51 +00:00
|
|
|
editCell = row.$el.find('.subnode-edit-in-process').parent();
|
2016-03-22 17:04:46 +00:00
|
|
|
// Only close row if it's open.
|
2018-01-12 07:29:51 +00:00
|
|
|
if (editCell.length > 0) {
|
2016-03-22 17:04:46 +00:00
|
|
|
var event = new Event('click');
|
|
|
|
editCell[0].dispatchEvent(event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
2018-01-12 07:29:51 +00:00
|
|
|
};
|
2016-03-22 17:04:46 +00:00
|
|
|
// Listen for any row which is about to enter in edit mode.
|
2018-01-12 07:29:51 +00:00
|
|
|
collection.on('enteringEditMode', cellEditing, collection);
|
2016-03-22 17:04:46 +00:00
|
|
|
|
2015-10-28 17:06:09 +00:00
|
|
|
// Initialize a new Grid instance
|
2016-01-09 12:29:56 +00:00
|
|
|
var grid = self.grid = new Backgrid.Grid({
|
2018-01-12 07:29:51 +00:00
|
|
|
columns: gridSchema.columns,
|
|
|
|
collection: collection,
|
|
|
|
row: this.row,
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
className: 'backgrid table presentation table-bordered table-noouter-border table-hover',
|
2015-10-28 17:06:09 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Render subNode grid
|
2017-07-18 14:13:16 +00:00
|
|
|
var subNodeGrid = grid.render().$el;
|
2015-10-28 17:06:09 +00:00
|
|
|
|
|
|
|
// Combine Edit and Delete Cell
|
|
|
|
if (data.canDelete && data.canEdit) {
|
2018-01-12 07:29:51 +00:00
|
|
|
$(subNodeGrid).find('th.pg-backform-delete').remove();
|
|
|
|
$(subNodeGrid).find('th.pg-backform-edit').attr('colspan', '2');
|
2015-10-28 17:06:09 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
var $dialog = gridBody.append(subNodeGrid);
|
2015-10-28 17:06:09 +00:00
|
|
|
|
|
|
|
// Add button callback
|
2018-05-25 15:26:37 +00:00
|
|
|
$dialog.find('button.add').on('click',(e) => {
|
2018-01-12 07:29:51 +00:00
|
|
|
e.preventDefault();
|
2016-04-13 14:54:17 +00:00
|
|
|
var canAddRow = _.isFunction(data.canAddRow) ?
|
2018-01-12 07:29:51 +00:00
|
|
|
data.canAddRow.apply(self, [self.model]) : true;
|
2016-04-13 14:54:17 +00:00
|
|
|
if (canAddRow) {
|
|
|
|
// Close any existing expanded row before adding new one.
|
2018-01-12 07:29:51 +00:00
|
|
|
_.each(grid.body.rows, function(row) {
|
|
|
|
var editCell = row.$el.find('.subnode-edit-in-process').parent();
|
2016-04-13 14:54:17 +00:00
|
|
|
// Only close row if it's open.
|
2018-01-12 07:29:51 +00:00
|
|
|
if (editCell.length > 0) {
|
2016-04-13 14:54:17 +00:00
|
|
|
var event = new Event('click');
|
|
|
|
editCell[0].dispatchEvent(event);
|
|
|
|
}
|
|
|
|
});
|
2016-03-22 17:04:46 +00:00
|
|
|
|
2016-04-13 14:54:17 +00:00
|
|
|
grid.insertRow({});
|
2016-03-22 17:04:46 +00:00
|
|
|
|
2016-04-13 14:54:17 +00:00
|
|
|
var newRow = $(grid.body.rows[collection.length - 1].$el);
|
2018-05-25 15:26:37 +00:00
|
|
|
newRow.attr('class', 'new').on('click',() => {
|
2018-01-12 07:29:51 +00:00
|
|
|
$(this).attr('class', 'editable');
|
2016-04-13 14:54:17 +00:00
|
|
|
});
|
2019-04-23 12:02:00 +00:00
|
|
|
$(newRow).pgMakeBackgridVisible('.backform-tab');
|
2016-04-13 14:54:17 +00:00
|
|
|
return false;
|
|
|
|
}
|
2015-10-28 17:06:09 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
return $dialog;
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2015-10-28 17:06:09 +00:00
|
|
|
});
|
|
|
|
|
2015-12-26 10:45:12 +00:00
|
|
|
/*
|
|
|
|
* SQL Tab Control for showing the modified SQL for the node with the
|
|
|
|
* property 'hasSQL' is set to true.
|
|
|
|
*
|
|
|
|
* When the user clicks on the SQL tab, we will send the modified data to the
|
|
|
|
* server and fetch the SQL for it.
|
|
|
|
*/
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.SqlTabControl = Backform.Control.extend({
|
2015-12-26 10:45:12 +00:00
|
|
|
defaults: {
|
2018-01-12 07:29:51 +00:00
|
|
|
label: '',
|
|
|
|
controlsClassName: 'pgadmin-controls pg-el-sm-12 SQL',
|
2015-12-26 10:45:12 +00:00
|
|
|
extraClasses: [],
|
2018-01-12 07:29:51 +00:00
|
|
|
helpMessage: null,
|
2015-12-26 10:45:12 +00:00
|
|
|
},
|
|
|
|
template: _.template([
|
|
|
|
'<div class="<%=controlsClassName%>">',
|
|
|
|
' <textarea class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%>><%-value%></textarea>',
|
|
|
|
' <% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
' <% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
2015-12-26 10:45:12 +00:00
|
|
|
/*
|
|
|
|
* Initialize the SQL Tab control properly
|
|
|
|
*/
|
|
|
|
initialize: function(o) {
|
|
|
|
Backform.Control.prototype.initialize.apply(this, arguments);
|
|
|
|
|
|
|
|
// Save the required information for using it later.
|
|
|
|
this.dialog = o.dialog;
|
|
|
|
this.tabIndex = o.tabIndex;
|
|
|
|
|
2016-09-26 09:04:49 +00:00
|
|
|
_.bindAll(this, 'onTabChange', 'onPanelResized');
|
2015-12-26 10:45:12 +00:00
|
|
|
},
|
|
|
|
getValueFromDOM: function() {
|
2018-01-12 07:29:51 +00:00
|
|
|
return this.formatter.toRaw(this.$el.find('textarea').val(), this.model);
|
2015-12-26 10:45:12 +00:00
|
|
|
},
|
2018-07-05 10:38:43 +00:00
|
|
|
|
|
|
|
reflectPreferences: function() {
|
|
|
|
var self = this;
|
|
|
|
/* self.sqlCtrl is null when SQL tab is not active */
|
|
|
|
if(self.sqlCtrl) {
|
|
|
|
let sqlEditPreferences = pgAdmin.Browser.get_preferences_for_module('sqleditor');
|
|
|
|
|
|
|
|
$(self.sqlCtrl.getWrapperElement()).css(
|
|
|
|
'font-size',SqlEditorUtils.calcFontSize(sqlEditPreferences.sql_font_size)
|
|
|
|
);
|
|
|
|
self.sqlCtrl.setOption('tabSize', sqlEditPreferences.tab_size);
|
|
|
|
self.sqlCtrl.setOption('lineWrapping', sqlEditPreferences.wrap_code);
|
|
|
|
self.sqlCtrl.setOption('autoCloseBrackets', sqlEditPreferences.insert_pair_brackets);
|
|
|
|
self.sqlCtrl.setOption('matchBrackets', sqlEditPreferences.brace_matching);
|
|
|
|
self.sqlCtrl.refresh();
|
|
|
|
}
|
|
|
|
},
|
2015-12-26 10:45:12 +00:00
|
|
|
render: function() {
|
2016-04-22 09:54:18 +00:00
|
|
|
if (this.sqlCtrl) {
|
2016-09-22 14:27:59 +00:00
|
|
|
this.sqlCtrl.toTextArea();
|
2016-04-22 09:54:18 +00:00
|
|
|
delete this.sqlCtrl;
|
|
|
|
this.sqlCtrl = null;
|
|
|
|
this.$el.empty();
|
2016-09-22 14:27:59 +00:00
|
|
|
this.model.off('pg-property-tab-changed', this.onTabChange, this);
|
2016-09-26 09:04:49 +00:00
|
|
|
this.model.off('pg-browser-resized', this.onPanelResized, this);
|
2016-04-22 09:54:18 +00:00
|
|
|
}
|
2015-12-26 10:45:12 +00:00
|
|
|
// Use the Backform Control's render function
|
|
|
|
Backform.Control.prototype.render.apply(this, arguments);
|
|
|
|
|
2016-04-22 09:54:18 +00:00
|
|
|
this.sqlCtrl = CodeMirror.fromTextArea(
|
2018-01-12 07:29:51 +00:00
|
|
|
(this.$el.find('textarea')[0]), {
|
|
|
|
lineNumbers: true,
|
|
|
|
mode: 'text/x-pgsql',
|
|
|
|
readOnly: true,
|
|
|
|
extraKeys: pgAdmin.Browser.editor_shortcut_keys,
|
|
|
|
});
|
2016-04-22 09:54:18 +00:00
|
|
|
|
2018-07-05 10:38:43 +00:00
|
|
|
this.reflectPreferences();
|
|
|
|
|
|
|
|
/* Check for sql editor preference changes */
|
|
|
|
let self = this;
|
|
|
|
pgBrowser.onPreferencesChange('sqleditor', function() {
|
|
|
|
self.reflectPreferences();
|
|
|
|
});
|
|
|
|
|
2016-09-22 14:27:59 +00:00
|
|
|
/*
|
|
|
|
* We will listen to the tab change event to check, if the SQL tab has
|
|
|
|
* been clicked or, not.
|
|
|
|
*/
|
|
|
|
this.model.on('pg-property-tab-changed', this.onTabChange, this);
|
2016-09-26 09:04:49 +00:00
|
|
|
this.model.on('pg-browser-resized', this.onPanelResized, this);
|
2016-09-22 14:27:59 +00:00
|
|
|
|
2015-12-26 10:45:12 +00:00
|
|
|
return this;
|
|
|
|
},
|
2016-01-15 11:17:17 +00:00
|
|
|
onTabChange: function(obj) {
|
2015-12-26 10:45:12 +00:00
|
|
|
|
|
|
|
// Fetch the information only if the SQL tab is visible at the moment.
|
2016-01-15 11:17:17 +00:00
|
|
|
if (this.dialog && obj.shown == this.tabIndex) {
|
2015-12-26 10:45:12 +00:00
|
|
|
|
2016-05-10 07:04:20 +00:00
|
|
|
// We will send a request to the sever only if something has changed
|
|
|
|
// in a model and also it does not contain any error.
|
2018-01-12 07:29:51 +00:00
|
|
|
if (this.model.sessChanged()) {
|
2016-05-10 07:04:20 +00:00
|
|
|
if (_.size(this.model.errorModel.attributes) == 0) {
|
|
|
|
var self = this,
|
2018-01-12 07:29:51 +00:00
|
|
|
node = self.field.get('schema_node'),
|
|
|
|
msql_url = node.generate_url.apply(
|
|
|
|
node, [
|
|
|
|
null, 'msql', this.field.get('node_data'), !self.model.isNew(),
|
|
|
|
this.field.get('node_info'),
|
|
|
|
]);
|
|
|
|
|
|
|
|
// Fetching the modified SQL
|
2016-05-10 07:04:20 +00:00
|
|
|
self.model.trigger('pgadmin-view:msql:fetching', self.method, node);
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
url: msql_url,
|
|
|
|
type: 'GET',
|
|
|
|
cache: false,
|
|
|
|
data: self.model.toJSON(true, 'GET'),
|
2018-01-12 07:29:51 +00:00
|
|
|
dataType: 'json',
|
|
|
|
contentType: 'application/json',
|
2016-05-10 07:04:20 +00:00
|
|
|
}).done(function(res) {
|
|
|
|
self.sqlCtrl.clearHistory();
|
|
|
|
self.sqlCtrl.setValue(res.data);
|
|
|
|
}).fail(function() {
|
|
|
|
self.model.trigger('pgadmin-view:msql:error', self.method, node, arguments);
|
|
|
|
}).always(function() {
|
|
|
|
self.model.trigger('pgadmin-view:msql:fetched', self.method, node, arguments);
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
this.sqlCtrl.clearHistory();
|
2017-11-01 12:58:19 +00:00
|
|
|
this.sqlCtrl.setValue('-- ' + gettext('Definition incomplete.'));
|
2016-05-10 07:04:20 +00:00
|
|
|
}
|
2015-12-26 10:45:12 +00:00
|
|
|
} else {
|
2016-04-22 09:54:18 +00:00
|
|
|
this.sqlCtrl.clearHistory();
|
2019-03-05 14:08:16 +00:00
|
|
|
this.sqlCtrl.setValue('-- ' + gettext('No updates.'));
|
2015-12-26 10:45:12 +00:00
|
|
|
}
|
2016-04-22 09:54:18 +00:00
|
|
|
this.sqlCtrl.refresh.apply(this.sqlCtrl);
|
2016-09-26 09:04:49 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
onPanelResized: function(o) {
|
|
|
|
if (o && o.container) {
|
|
|
|
var $tabContent = o.container.find(
|
2018-01-12 07:29:51 +00:00
|
|
|
'.backform-tab > .tab-content'
|
|
|
|
).first(),
|
|
|
|
$sqlPane = $tabContent.find(
|
|
|
|
'div[role=tabpanel].tab-pane.SQL'
|
|
|
|
);
|
2016-09-26 09:04:49 +00:00
|
|
|
if ($sqlPane.hasClass('active')) {
|
|
|
|
$sqlPane.find('.CodeMirror').css(
|
|
|
|
'cssText',
|
|
|
|
'height: ' + ($tabContent.height() + 8) + 'px !important;'
|
2018-01-12 07:29:51 +00:00
|
|
|
);
|
2016-09-26 09:04:49 +00:00
|
|
|
}
|
2015-12-26 10:45:12 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
remove: function() {
|
2016-04-22 09:54:18 +00:00
|
|
|
if (this.sqlCtrl) {
|
2016-09-22 14:27:59 +00:00
|
|
|
this.sqlCtrl.toTextArea();
|
2016-04-22 09:54:18 +00:00
|
|
|
delete this.sqlCtrl;
|
|
|
|
this.sqlCtrl = null;
|
2016-09-22 14:27:59 +00:00
|
|
|
|
2016-04-22 09:54:18 +00:00
|
|
|
this.$el.empty();
|
|
|
|
}
|
2015-12-26 10:45:12 +00:00
|
|
|
this.model.off('pg-property-tab-changed', this.onTabChange, this);
|
2016-09-26 09:04:49 +00:00
|
|
|
this.model.off('pg-browser-resized', this.onPanelResized, this);
|
|
|
|
|
2015-12-26 10:45:12 +00:00
|
|
|
Backform.Control.__super__.remove.apply(this, arguments);
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
/*
|
2016-02-22 11:48:02 +00:00
|
|
|
* Numeric input Control functionality just like backgrid
|
|
|
|
*/
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.NumericControl = Backform.InputControl.extend({
|
2016-02-22 11:48:02 +00:00
|
|
|
defaults: {
|
2018-01-12 07:29:51 +00:00
|
|
|
type: 'number',
|
|
|
|
label: '',
|
2016-02-22 11:48:02 +00:00
|
|
|
min: undefined,
|
|
|
|
max: undefined,
|
|
|
|
maxlength: 255,
|
|
|
|
extraClasses: [],
|
2018-01-12 07:29:51 +00:00
|
|
|
helpMessage: null,
|
2016-02-22 11:48:02 +00:00
|
|
|
},
|
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
|
|
|
'<div class="<%=Backform.controlsClassName%>">',
|
|
|
|
' <input type="<%=type%>" class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>" min="<%=min%>" max="<%=max%>"maxlength="<%=maxlength%>" value="<%-value%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
|
|
|
|
' <% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
' <% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
2016-01-05 11:19:58 +00:00
|
|
|
});
|
|
|
|
|
2015-10-28 17:06:09 +00:00
|
|
|
///////
|
|
|
|
// Generate a schema (as group members) based on the model's schema
|
|
|
|
//
|
|
|
|
// It will be used by the grid, properties, and dialog view generation
|
|
|
|
// functions.
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.generateViewSchema = function(
|
|
|
|
node_info, Model, mode, node, treeData, noSQL, subschema
|
|
|
|
) {
|
2015-10-28 17:06:09 +00:00
|
|
|
var proto = (Model && Model.prototype) || Model,
|
2018-01-12 07:29:51 +00:00
|
|
|
schema = subschema || (proto && proto.schema),
|
|
|
|
fields = [],
|
|
|
|
groupInfo = {};
|
2015-10-28 17:06:09 +00:00
|
|
|
|
|
|
|
// 'schema' has the information about how to generate the form.
|
|
|
|
if (schema && _.isArray(schema)) {
|
|
|
|
var evalASFunc = evalASFunc = function(prop) {
|
|
|
|
return ((prop && proto[prop] &&
|
2018-01-12 07:29:51 +00:00
|
|
|
typeof proto[prop] == 'function') ? proto[prop] : prop);
|
2015-10-28 17:06:09 +00:00
|
|
|
};
|
2016-01-17 16:51:02 +00:00
|
|
|
var groups = {},
|
2018-01-12 07:29:51 +00:00
|
|
|
server_info = node_info && ('server' in node_info) &&
|
|
|
|
pgBrowser.serverInfo && pgBrowser.serverInfo[node_info.server._id],
|
|
|
|
in_catalog = node_info && ('catalog' in node_info),
|
|
|
|
ver_in_limit;
|
2015-10-28 17:06:09 +00:00
|
|
|
|
|
|
|
_.each(schema, function(s) {
|
|
|
|
// Do we understand - what control, we're creating
|
|
|
|
// here?
|
2016-03-06 13:20:05 +00:00
|
|
|
if (s.type == 'group') {
|
2018-01-12 07:29:51 +00:00
|
|
|
var visible = true;
|
|
|
|
ver_in_limit = (_.isUndefined(server_info) ? true :
|
|
|
|
((_.isUndefined(s.server_type) ? true :
|
2019-03-14 15:11:16 +00:00
|
|
|
(server_info.type in s.server_type)) &&
|
2016-03-06 13:20:05 +00:00
|
|
|
(_.isUndefined(s.min_version) ? true :
|
2018-01-12 07:29:51 +00:00
|
|
|
(server_info.version >= s.min_version)) &&
|
2016-03-06 13:20:05 +00:00
|
|
|
(_.isUndefined(s.max_version) ? true :
|
2018-01-12 07:29:51 +00:00
|
|
|
(server_info.version <= s.max_version))));
|
2016-05-20 12:13:30 +00:00
|
|
|
|
|
|
|
if (s.mode && _.isObject(s.mode))
|
|
|
|
visible = (_.indexOf(s.mode, mode) > -1);
|
|
|
|
if (visible)
|
|
|
|
visible = evalASFunc(s.visible);
|
|
|
|
|
2016-03-06 13:20:05 +00:00
|
|
|
groupInfo[s.id] = {
|
|
|
|
label: s.label || s.id,
|
|
|
|
version_compatible: ver_in_limit,
|
2018-01-12 07:29:51 +00:00
|
|
|
visible: visible,
|
2016-03-06 13:20:05 +00:00
|
|
|
};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-28 17:06:09 +00:00
|
|
|
if (!s.mode || (s && s.mode && _.isObject(s.mode) &&
|
2018-01-12 07:29:51 +00:00
|
|
|
_.indexOf(s.mode, mode) != -1)) {
|
2015-10-28 17:06:09 +00:00
|
|
|
// Each field is kept in specified group, or in
|
|
|
|
// 'General' category.
|
2017-06-07 10:23:02 +00:00
|
|
|
var group = s.group || gettext('General'),
|
2018-01-12 07:29:51 +00:00
|
|
|
control = s.control || Backform.getMappedControl(s.type, mode),
|
|
|
|
cell = s.cell || Backform.getMappedControl(s.type, 'cell');
|
2015-10-28 17:06:09 +00:00
|
|
|
|
|
|
|
if (control == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate the empty group list (if not exists)
|
|
|
|
groups[group] = (groups[group] || []);
|
2018-01-12 07:29:51 +00:00
|
|
|
ver_in_limit = (_.isUndefined(server_info) ? true :
|
|
|
|
((_.isUndefined(s.server_type) ? true :
|
2019-03-14 15:11:16 +00:00
|
|
|
(server_info.type in s.server_type)) &&
|
2018-01-12 07:29:51 +00:00
|
|
|
(_.isUndefined(s.min_version) ? true :
|
|
|
|
(server_info.version >= s.min_version)) &&
|
|
|
|
(_.isUndefined(s.max_version) ? true :
|
|
|
|
(server_info.version <= s.max_version))));
|
|
|
|
|
|
|
|
var disabled = (
|
|
|
|
(mode == 'properties') || !ver_in_limit || in_catalog
|
|
|
|
),
|
|
|
|
schema_node = (s.node && _.isString(s.node) &&
|
|
|
|
s.node in pgBrowser.Nodes && pgBrowser.Nodes[s.node]) || node;
|
2015-10-28 17:06:09 +00:00
|
|
|
|
|
|
|
var o = _.extend(_.clone(s), {
|
|
|
|
name: s.id,
|
|
|
|
// This can be disabled in some cases (if not hidden)
|
2015-11-19 17:45:48 +00:00
|
|
|
|
|
|
|
disabled: (disabled ? true : evalASFunc(s.disabled)),
|
2016-03-03 14:03:59 +00:00
|
|
|
editable: _.isUndefined(s.editable) ?
|
2018-01-12 07:29:51 +00:00
|
|
|
pgAdmin.editableCell : evalASFunc(s.editable),
|
2015-11-19 17:45:48 +00:00
|
|
|
subnode: ((_.isString(s.model) && s.model in pgBrowser.Nodes) ?
|
2018-01-12 07:29:51 +00:00
|
|
|
pgBrowser.Nodes[s.model].model : s.model),
|
2015-11-19 17:45:48 +00:00
|
|
|
canAdd: (disabled ? false : evalASFunc(s.canAdd)),
|
2016-04-13 14:54:17 +00:00
|
|
|
canAddRow: (disabled ? false : evalASFunc(s.canAddRow)),
|
2015-11-19 17:45:48 +00:00
|
|
|
canEdit: (disabled ? false : evalASFunc(s.canEdit)),
|
|
|
|
canDelete: (disabled ? false : evalASFunc(s.canDelete)),
|
2016-04-13 14:54:17 +00:00
|
|
|
canEditRow: (disabled ? false : evalASFunc(s.canEditRow)),
|
|
|
|
canDeleteRow: (disabled ? false : evalASFunc(s.canDeleteRow)),
|
2016-02-01 11:07:05 +00:00
|
|
|
transform: evalASFunc(s.transform),
|
2015-10-28 17:06:09 +00:00
|
|
|
mode: mode,
|
|
|
|
control: control,
|
2015-11-19 17:45:48 +00:00
|
|
|
cell: cell,
|
|
|
|
node_info: node_info,
|
2015-12-26 10:45:12 +00:00
|
|
|
schema_node: schema_node,
|
2016-01-15 13:55:45 +00:00
|
|
|
// Do we need to show this control in this mode?
|
|
|
|
visible: evalASFunc(s.visible),
|
2016-01-09 12:29:56 +00:00
|
|
|
node: node,
|
2016-01-12 06:31:45 +00:00
|
|
|
node_data: treeData,
|
2018-01-12 07:29:51 +00:00
|
|
|
version_compatible: ver_in_limit,
|
2015-10-28 17:06:09 +00:00
|
|
|
});
|
|
|
|
delete o.id;
|
|
|
|
|
2016-01-09 12:29:56 +00:00
|
|
|
// Temporarily store in dictionary format for
|
2015-10-28 17:06:09 +00:00
|
|
|
// utilizing it later.
|
|
|
|
groups[group].push(o);
|
2016-01-17 16:51:02 +00:00
|
|
|
|
2016-01-19 12:23:33 +00:00
|
|
|
if (s.type == 'nested') {
|
2016-01-17 16:51:02 +00:00
|
|
|
delete o.name;
|
|
|
|
delete o.cell;
|
|
|
|
|
|
|
|
o.schema = Backform.generateViewSchema(
|
2018-01-12 07:29:51 +00:00
|
|
|
node_info, Model, mode, node, treeData, true, s.schema
|
|
|
|
);
|
2016-01-17 16:51:02 +00:00
|
|
|
o.control = o.control || 'tab';
|
|
|
|
}
|
2015-10-28 17:06:09 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Do we have fields to genreate controls, which we
|
|
|
|
// understand?
|
|
|
|
if (_.isEmpty(groups)) {
|
|
|
|
return null;
|
|
|
|
}
|
2016-01-17 16:51:02 +00:00
|
|
|
|
|
|
|
if (!noSQL && node && node.hasSQL && (mode == 'create' || mode == 'edit')) {
|
2017-06-07 10:23:02 +00:00
|
|
|
groups[gettext('SQL')] = [{
|
2018-01-12 07:29:51 +00:00
|
|
|
name: 'sql',
|
|
|
|
visible: true,
|
|
|
|
disabled: false,
|
|
|
|
type: 'text',
|
|
|
|
control: 'sql-tab',
|
|
|
|
node_info: node_info,
|
|
|
|
schema_node: node,
|
|
|
|
node_data: treeData,
|
2015-12-26 10:45:12 +00:00
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2016-01-17 16:51:02 +00:00
|
|
|
// Create an array from the dictionary with proper required
|
|
|
|
// structure.
|
|
|
|
_.each(groups, function(val, key) {
|
2016-05-20 12:13:30 +00:00
|
|
|
fields.push(
|
|
|
|
_.extend(
|
|
|
|
_.defaults(
|
2018-01-12 07:29:51 +00:00
|
|
|
groupInfo[key] || {
|
|
|
|
label: key,
|
|
|
|
}, {
|
|
|
|
version_compatible: true,
|
|
|
|
visible: true,
|
|
|
|
}
|
|
|
|
), {
|
|
|
|
fields: val,
|
|
|
|
})
|
|
|
|
);
|
2016-01-17 16:51:02 +00:00
|
|
|
});
|
2015-10-28 17:06:09 +00:00
|
|
|
}
|
2016-01-17 16:51:02 +00:00
|
|
|
|
|
|
|
return fields;
|
2016-01-09 12:29:56 +00:00
|
|
|
};
|
2015-10-28 17:06:09 +00:00
|
|
|
|
2016-05-21 10:11:35 +00:00
|
|
|
var Select2Formatter = function() {};
|
|
|
|
_.extend(Select2Formatter.prototype, {
|
2018-01-12 07:29:51 +00:00
|
|
|
fromRaw: function(rawData) {
|
2016-06-24 13:09:32 +00:00
|
|
|
return encodeURIComponent(rawData);
|
2016-05-21 10:11:35 +00:00
|
|
|
},
|
2018-01-12 07:29:51 +00:00
|
|
|
toRaw: function(formattedData) {
|
2016-07-05 11:58:48 +00:00
|
|
|
if (_.isArray(formattedData)) {
|
2019-01-21 11:54:46 +00:00
|
|
|
let tmpArr = _.map(formattedData, encodeURIComponent);
|
|
|
|
return _.map(tmpArr, decodeURIComponent);
|
2016-07-05 11:58:48 +00:00
|
|
|
} else {
|
2018-01-12 07:29:51 +00:00
|
|
|
if (!_.isNull(formattedData) && !_.isUndefined(formattedData)) {
|
2016-08-02 04:08:17 +00:00
|
|
|
return decodeURIComponent(formattedData);
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
2016-07-05 11:58:48 +00:00
|
|
|
}
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2016-05-21 10:11:35 +00:00
|
|
|
});
|
|
|
|
|
2016-01-09 12:38:44 +00:00
|
|
|
/*
|
|
|
|
* Backform Select2 control.
|
|
|
|
*/
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.Select2Control = Backform.SelectControl.extend({
|
2016-05-21 10:11:35 +00:00
|
|
|
defaults: _.extend({}, Backform.SelectControl.prototype.defaults, {
|
|
|
|
select2: {
|
|
|
|
first_empty: true,
|
2017-07-21 11:44:57 +00:00
|
|
|
multiple: false,
|
2018-01-12 07:29:51 +00:00
|
|
|
emptyOptions: false,
|
2018-10-31 10:09:40 +00:00
|
|
|
preserveSelectionOrder: false,
|
2019-04-09 06:39:25 +00:00
|
|
|
isDropdownParent: false,
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2016-05-21 10:11:35 +00:00
|
|
|
}),
|
2018-10-31 10:09:40 +00:00
|
|
|
|
|
|
|
events: function() {
|
|
|
|
// Inherit all default events of InputControl
|
|
|
|
return _.extend({}, Backform.SelectControl.prototype.events, {
|
|
|
|
'select2:select': 'onSelect',
|
|
|
|
});
|
|
|
|
},
|
|
|
|
|
|
|
|
onSelect: function (evt) {
|
|
|
|
var sel2Options = this.field.get('select2');
|
2019-03-20 13:24:49 +00:00
|
|
|
if (!_.isUndefined(sel2Options) && !_.isNull(sel2Options) &&
|
|
|
|
sel2Options.multiple && sel2Options.preserveSelectionOrder) {
|
2018-10-31 10:09:40 +00:00
|
|
|
var element = evt.params.data.element;
|
|
|
|
var $element = $(element);
|
|
|
|
|
|
|
|
$element.detach();
|
|
|
|
$(this.$sel).append($element);
|
|
|
|
$(this.$sel).trigger('change');
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-05-21 10:11:35 +00:00
|
|
|
formatter: Select2Formatter,
|
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
|
|
|
'<div class="<%=Backform.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++) {%>',
|
|
|
|
' <% var option = options[i]; %>',
|
|
|
|
' <option ',
|
|
|
|
' <% if (option.image) { %> data-image=<%=option.image%> <%}%>',
|
2017-11-20 13:50:47 +00:00
|
|
|
' value=<%- formatter.fromRaw(option.value) %>',
|
2016-09-22 14:27:59 +00:00
|
|
|
' <% if (option.selected) {%>selected="selected"<%} else {%>',
|
2016-05-21 10:11:35 +00:00
|
|
|
' <% 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)"<%}%>',
|
2016-09-22 14:27:59 +00:00
|
|
|
' <%}%>',
|
2016-05-21 10:11:35 +00:00
|
|
|
' <%= disabled ? "disabled" : ""%>><%-option.label%></option>',
|
|
|
|
' <%}%>',
|
|
|
|
' </select>',
|
|
|
|
' <% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
' <% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
2016-01-09 12:38:44 +00:00
|
|
|
render: function() {
|
2016-05-21 10:11:35 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
if (this.$sel && this.$sel.select2 &&
|
2016-10-21 15:16:10 +00:00
|
|
|
this.$sel.select2.hasOwnProperty('destroy')) {
|
2016-08-19 15:35:42 +00:00
|
|
|
this.$sel.select2('destroy');
|
2016-05-12 10:44:01 +00:00
|
|
|
}
|
2016-01-09 12:38:44 +00:00
|
|
|
|
2016-05-21 10:11:35 +00:00
|
|
|
var field = _.defaults(this.field.toJSON(), this.defaults),
|
2018-01-12 07:29:51 +00:00
|
|
|
attributes = this.model.toJSON(),
|
|
|
|
attrArr = field.name.split('.'),
|
|
|
|
name = attrArr.shift(),
|
|
|
|
path = attrArr.join('.'),
|
|
|
|
rawValue = this.keyPathAccessor(attributes[name], path),
|
|
|
|
data = _.extend(field, {
|
|
|
|
rawValue: rawValue,
|
|
|
|
value: this.formatter.fromRaw(rawValue, this.model),
|
|
|
|
attributes: attributes,
|
|
|
|
formatter: this.formatter,
|
|
|
|
}),
|
|
|
|
evalF = function(f, d, m) {
|
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
|
|
|
};
|
2016-05-21 10:11:35 +00:00
|
|
|
|
|
|
|
data.select2 = data.select2 || {};
|
|
|
|
_.defaults(data.select2, this.defaults.select2, {
|
|
|
|
first_empty: true,
|
2017-07-21 11:44:57 +00:00
|
|
|
multiple: false,
|
2018-01-12 07:29:51 +00:00
|
|
|
emptyOptions: false,
|
2018-10-31 10:09:40 +00:00
|
|
|
preserveSelectionOrder: false,
|
2019-04-09 06:39:25 +00:00
|
|
|
isDropdownParent: false,
|
2016-05-21 10:11:35 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Evaluate the disabled, visible, and required option
|
|
|
|
_.extend(data, {
|
|
|
|
disabled: evalF(data.disabled, data, this.model),
|
2018-01-12 07:29:51 +00:00
|
|
|
visible: evalF(data.visible, data, this.model),
|
|
|
|
required: evalF(data.required, data, this.model),
|
2016-05-21 10:11:35 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Evaluation the options
|
|
|
|
if (_.isFunction(data.options)) {
|
|
|
|
try {
|
2018-01-12 07:29:51 +00:00
|
|
|
data.options = data.options(this);
|
|
|
|
} catch (e) {
|
2016-05-21 10:11:35 +00:00
|
|
|
// Do nothing
|
2018-01-12 07:29:51 +00:00
|
|
|
data.options = [];
|
2016-05-21 10:11:35 +00:00
|
|
|
this.model.trigger(
|
|
|
|
'pgadmin-view:transform:error', this.model, this.field, e
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clean up first
|
2017-07-26 11:55:46 +00:00
|
|
|
this.$el.removeClass(Backform.hiddenClassName);
|
2016-05-21 10:11:35 +00:00
|
|
|
|
|
|
|
if (!data.visible)
|
2017-07-26 11:55:46 +00:00
|
|
|
this.$el.addClass(Backform.hiddenClassName);
|
2016-05-21 10:11:35 +00:00
|
|
|
|
|
|
|
this.$el.html(this.template(data)).addClass(field.name);
|
|
|
|
|
|
|
|
var select2Opts = _.extend({
|
2018-01-12 07:29:51 +00:00
|
|
|
disabled: data.disabled,
|
|
|
|
}, field.select2, {
|
|
|
|
options: (this.field.get('options') || this.defaults.options),
|
|
|
|
});
|
Improvised the 'transform/options' function usage with the Select2Cell.
The current implementaton binds the cell/control object, and the ajax
data in the asychronous Cells/Controls with the 'options' functions
extended from the Select2Cell.
The problem starts when we try to fetch the current model from that
options/transform/filter function to do some operation, which does not
require in most of the cases. Except the privileges control - where we
needed the current model for omitting the existing selected object
during transformation, and filtering.
In order resolved the issue, we need a common object, which is shared
among the Cell. In backgrid, the 'Column' object is mong the cell,
hence - implementation logic has been changed to bid the 'Column' object
with the 'options' function and, passed the 'Cell' object as an
arguments.
Because - we do use the common function 'transform' between 'Control'
and 'Cell', we needed make changes in the Select2Control to pass the
Control object as an arguments.
And, make the changes in the privileges control to use the new
implementation. The same logic is also required in some of the
operations, we will be/are working on the table/column nodes.
2016-04-08 05:30:48 +00:00
|
|
|
|
2019-04-30 11:57:17 +00:00
|
|
|
// Dropdown body can be render at user given location
|
|
|
|
// If isDropdownParent flag is set to true then, By default we will
|
|
|
|
// display it on the control itself.
|
|
|
|
if (data.select2.isDropdownParent) {
|
|
|
|
select2Opts.dropdownParent = data.select2.dropdownParent || this.$el;
|
|
|
|
}
|
|
|
|
|
2017-01-17 10:25:26 +00:00
|
|
|
// If disabled then no need to show placeholder
|
2018-01-12 07:29:51 +00:00
|
|
|
if (data.disabled || data.mode === 'properties') {
|
2017-01-17 10:25:26 +00:00
|
|
|
select2Opts['placeholder'] = '';
|
|
|
|
}
|
|
|
|
|
2016-01-09 12:38:44 +00:00
|
|
|
/*
|
|
|
|
* Add empty option as Select2 requires any empty '<option><option>' for
|
|
|
|
* some of its functionality to work and initialize select2 control.
|
|
|
|
*/
|
2017-07-21 11:44:57 +00:00
|
|
|
|
|
|
|
if (data.select2.tags && data.select2.emptyOptions) {
|
|
|
|
select2Opts.data = data.rawValue;
|
|
|
|
}
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$sel = this.$el.find('select').select2(select2Opts);
|
2016-05-21 10:11:35 +00:00
|
|
|
|
2017-07-21 11:44:57 +00:00
|
|
|
// Add or remove tags from select2 control
|
|
|
|
if (data.select2.tags && data.select2.emptyOptions) {
|
|
|
|
this.$sel.val(data.rawValue);
|
|
|
|
this.$sel.trigger('change.select2');
|
|
|
|
this.$sel.on('select2:unselect', function(evt) {
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
$(this).find('option[value="' + evt.params.data.text.replace('\'', '\\\'').replace('"', '\\"') + '"]').remove();
|
2017-07-21 11:44:57 +00:00
|
|
|
$(this).trigger('change.select2');
|
|
|
|
if ($(this).val() == null) {
|
|
|
|
$(this).empty();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-08-19 15:35:42 +00:00
|
|
|
// Select the highlighted item on Tab press.
|
|
|
|
if (this.$sel) {
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$sel.data('select2').on('keypress', function(ev) {
|
2016-08-19 15:35:42 +00:00
|
|
|
var self = this;
|
2016-09-22 14:27:59 +00:00
|
|
|
|
|
|
|
// keycode 9 is for TAB key
|
|
|
|
if (ev.which === 9 && self.isOpen()) {
|
2016-08-19 15:35:42 +00:00
|
|
|
ev.preventDefault();
|
|
|
|
self.trigger('results:select', {});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-05-21 10:11:35 +00:00
|
|
|
this.updateInvalid();
|
Improvised the 'transform/options' function usage with the Select2Cell.
The current implementaton binds the cell/control object, and the ajax
data in the asychronous Cells/Controls with the 'options' functions
extended from the Select2Cell.
The problem starts when we try to fetch the current model from that
options/transform/filter function to do some operation, which does not
require in most of the cases. Except the privileges control - where we
needed the current model for omitting the existing selected object
during transformation, and filtering.
In order resolved the issue, we need a common object, which is shared
among the Cell. In backgrid, the 'Column' object is mong the cell,
hence - implementation logic has been changed to bid the 'Column' object
with the 'options' function and, passed the 'Cell' object as an
arguments.
Because - we do use the common function 'transform' between 'Control'
and 'Cell', we needed make changes in the Select2Control to pass the
Control object as an arguments.
And, make the changes in the privileges control to use the new
implementation. The same logic is also required in some of the
operations, we will be/are working on the table/column nodes.
2016-04-08 05:30:48 +00:00
|
|
|
|
2016-01-09 12:38:44 +00:00
|
|
|
return this;
|
2016-05-21 10:11:35 +00:00
|
|
|
},
|
|
|
|
getValueFromDOM: function() {
|
2016-08-02 04:08:17 +00:00
|
|
|
var val = Backform.SelectControl.prototype.getValueFromDOM.apply(
|
2018-01-12 07:29:51 +00:00
|
|
|
this, arguments
|
|
|
|
),
|
|
|
|
select2Opts = _.extend({}, this.field.get('select2') || this.defaults.select2);
|
2016-08-02 04:08:17 +00:00
|
|
|
|
|
|
|
if (select2Opts.multiple && val == null) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
return val;
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2016-01-09 12:38:44 +00:00
|
|
|
});
|
2016-01-17 16:51:02 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.FieldsetControl = Backform.Fieldset.extend({
|
2016-01-17 16:51:02 +00:00
|
|
|
initialize: function(opts) {
|
2016-03-07 10:35:01 +00:00
|
|
|
Backform.Control.prototype.initialize.apply(
|
|
|
|
this, arguments
|
2018-01-12 07:29:51 +00:00
|
|
|
);
|
2016-01-17 16:51:02 +00:00
|
|
|
Backform.Dialog.prototype.initialize.apply(
|
2018-01-12 07:29:51 +00:00
|
|
|
this, [{
|
|
|
|
schema: opts.field.get('schema'),
|
|
|
|
}]
|
|
|
|
);
|
2016-01-17 16:51:02 +00:00
|
|
|
this.dialog = opts.dialog;
|
|
|
|
this.tabIndex = opts.tabIndex;
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
this.contentClass = opts.field.get('contentClass')?opts.field.get('contentClass'):'';
|
2016-03-07 10:35:01 +00:00
|
|
|
|
|
|
|
// Listen to the dependent fields in the model for any change
|
|
|
|
var deps = this.field.get('deps');
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
if (deps && _.isArray(deps)) {
|
|
|
|
_.each(deps, function(d) {
|
2017-07-18 14:13:16 +00:00
|
|
|
var attrArr = d.split('.'),
|
|
|
|
name = attrArr.shift();
|
2018-01-12 07:29:51 +00:00
|
|
|
self.listenTo(self.model, 'change:' + name, self.render);
|
2016-03-07 10:35:01 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
// Render using Backform.Fieldset (only if this control is visible)
|
|
|
|
orig_render: Backform.Fieldset.prototype.render,
|
|
|
|
render: function() {
|
|
|
|
var field = _.defaults(this.field.toJSON(), this.defaults),
|
2018-01-12 07:29:51 +00:00
|
|
|
evalF = function(f, d, m) {
|
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
|
|
|
};
|
2016-03-07 10:35:01 +00:00
|
|
|
|
|
|
|
if (!field.version_compatible ||
|
2018-01-12 07:29:51 +00:00
|
|
|
!evalF(field.visible, field, this.model)) {
|
2016-03-07 10:35:01 +00:00
|
|
|
this.cleanup();
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$el.empty();
|
2016-03-07 10:35:01 +00:00
|
|
|
} else {
|
|
|
|
this.orig_render.apply(this, arguments);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
formatter: function() {},
|
|
|
|
cleanup: function() {
|
|
|
|
Backform.Fieldset.prototype.cleanup.apply(this);
|
|
|
|
},
|
|
|
|
remove: function() {
|
|
|
|
Backform.Control.prototype.remove.apply(this, arguments);
|
|
|
|
Backform.Dialog.prototype.remove.apply(this, arguments);
|
2016-01-17 16:51:02 +00:00
|
|
|
},
|
|
|
|
className: function() {
|
|
|
|
return 'set-group';
|
|
|
|
},
|
|
|
|
tabPanelClassName: function() {
|
|
|
|
return Backform.tabClassName;
|
|
|
|
},
|
|
|
|
fieldsetClass: 'inline-fieldset',
|
|
|
|
legendClass: '',
|
2018-01-12 07:29:51 +00:00
|
|
|
collapse: false,
|
2016-01-17 16:51:02 +00:00
|
|
|
});
|
|
|
|
|
2016-03-07 10:35:01 +00:00
|
|
|
// Backform Tab Control (in bootstrap tabbular)
|
|
|
|
// A collection of field models.
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.TabControl = Backform.FieldsetControl.extend({
|
|
|
|
tagName: 'div',
|
2016-03-07 10:35:01 +00:00
|
|
|
className: 'inline-tab-panel',
|
|
|
|
tabPanelClassName: 'inline-tab-panel',
|
2019-01-16 06:25:08 +00:00
|
|
|
tabPanelExtraClasses: '',
|
2016-03-07 10:35:01 +00:00
|
|
|
initialize: function(opts) {
|
|
|
|
Backform.FieldsetControl.prototype.initialize.apply(
|
|
|
|
this, arguments
|
2018-01-12 07:29:51 +00:00
|
|
|
);
|
2016-03-07 10:35:01 +00:00
|
|
|
this.tabIndex = (opts.tabIndex || parseInt(Math.random() * 1000)) + 1;
|
2019-01-16 06:25:08 +00:00
|
|
|
if(opts.field.get('tabPanelExtraClasses')) {
|
|
|
|
this.tabPanelExtraClasses = opts.field.get('tabPanelExtraClasses');
|
|
|
|
}
|
|
|
|
this.tabPanelClassName = this.tabPanelClassName + ' ' + this.tabPanelExtraClasses;
|
2016-03-07 10:35:01 +00:00
|
|
|
},
|
|
|
|
// Render using Backform.Dialog (tabular UI) (only if this control is
|
|
|
|
// visible).
|
|
|
|
orig_render: Backform.Dialog.prototype.render,
|
2018-01-12 07:29:51 +00:00
|
|
|
template: Backform.Dialog.prototype.template,
|
2016-03-07 10:35:01 +00:00
|
|
|
});
|
|
|
|
|
2016-03-18 16:54:43 +00:00
|
|
|
// Backform Tab Control (in bootstrap tabbular)
|
|
|
|
// A collection of field models.
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.PlainFieldsetControl = Backform.FieldsetControl.extend({
|
|
|
|
initialize: function() {
|
|
|
|
Backform.FieldsetControl.prototype.initialize.apply(this, arguments);
|
2016-03-18 16:54:43 +00:00
|
|
|
},
|
|
|
|
template: {
|
|
|
|
'header': _.template([
|
|
|
|
'<fieldset class="<%=fieldsetClass%>" <%=disabled ? "disabled" : ""%>>',
|
|
|
|
' <% if (legend != false) { %>',
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
' <div><legend class="<%=legendClass%>" <%=collapse ? "data-toggle=\'collapse\'" : ""%> data-target="#<%=cId%>"><%=collapse ? "<span class=\'caret\'></span>" : "" %></legend></div>',
|
2016-03-18 16:54:43 +00:00
|
|
|
' <% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</fieldset>',
|
|
|
|
].join('\n')),
|
2016-03-18 16:54:43 +00:00
|
|
|
'content': _.template(
|
|
|
|
' <div id="<%= cId %>" class="<%=contentClass%>"></div>'
|
2018-01-12 07:29:51 +00:00
|
|
|
),
|
|
|
|
},
|
2016-03-18 16:54:43 +00:00
|
|
|
fieldsetClass: 'inline-fieldset-without-border',
|
|
|
|
legend: false,
|
|
|
|
});
|
|
|
|
|
2016-02-05 08:06:57 +00:00
|
|
|
/*
|
|
|
|
* Control For Code Mirror SQL text area.
|
|
|
|
*/
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.SqlFieldControl = Backform.TextareaControl.extend({
|
2016-02-05 08:06:57 +00:00
|
|
|
|
|
|
|
defaults: {
|
2018-01-12 07:29:51 +00:00
|
|
|
label: '',
|
2016-04-29 10:35:26 +00:00
|
|
|
extraClasses: [], // Add default control height
|
2016-02-05 08:06:57 +00:00
|
|
|
helpMessage: null,
|
2016-03-11 09:24:05 +00:00
|
|
|
maxlength: 4096,
|
2018-01-12 07:29:51 +00:00
|
|
|
rows: undefined,
|
2016-02-05 08:06:57 +00:00
|
|
|
},
|
|
|
|
|
2016-03-22 16:36:54 +00:00
|
|
|
// Customize template to add new styles
|
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
|
|
|
'<div class="<%=Backform.controlsClassName%> sql_field_layout <%=extraClasses.join(\' \')%>">',
|
|
|
|
' <textarea ',
|
|
|
|
' class="<%=Backform.controlClassName%> " name="<%=name%>"',
|
|
|
|
' maxlength="<%=maxlength%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%>',
|
|
|
|
' rows=<%=rows%>',
|
|
|
|
' <%=required ? "required" : ""%>><%-value%></textarea>',
|
|
|
|
' <% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
' <% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
2016-03-22 16:36:54 +00:00
|
|
|
|
2016-02-05 08:06:57 +00:00
|
|
|
/*
|
|
|
|
* Initialize the SQL Field control properly
|
|
|
|
*/
|
2018-01-12 07:29:51 +00:00
|
|
|
initialize: function() {
|
2016-02-05 08:06:57 +00:00
|
|
|
Backform.TextareaControl.prototype.initialize.apply(this, arguments);
|
2016-04-22 09:54:18 +00:00
|
|
|
this.sqlCtrl = null;
|
2016-02-05 08:06:57 +00:00
|
|
|
|
2016-09-22 14:27:59 +00:00
|
|
|
_.bindAll(this, 'onFocus', 'onBlur', 'refreshTextArea');
|
2016-02-05 08:06:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
getValueFromDOM: function() {
|
2016-04-22 09:54:18 +00:00
|
|
|
return this.sqlCtrl.getValue();
|
2016-02-05 08:06:57 +00:00
|
|
|
},
|
|
|
|
|
2018-07-05 10:38:43 +00:00
|
|
|
reflectPreferences: function() {
|
|
|
|
var self = this;
|
|
|
|
/* self.sqlCtrl is null when Definition tab is not active */
|
|
|
|
if(self.sqlCtrl) {
|
2018-07-09 13:08:41 +00:00
|
|
|
|
|
|
|
/* This control is used by filter dialog in query editor, so taking preferences from window
|
|
|
|
* SQL Editor can be in different tab
|
|
|
|
*/
|
|
|
|
let browser = window.opener ?
|
2019-03-14 15:11:16 +00:00
|
|
|
window.opener.pgAdmin.Browser : window.top.pgAdmin.Browser;
|
2018-07-09 13:08:41 +00:00
|
|
|
|
|
|
|
let sqlEditPreferences = browser.get_preferences_for_module('sqleditor');
|
2018-07-05 10:38:43 +00:00
|
|
|
|
|
|
|
$(self.sqlCtrl.getWrapperElement()).css(
|
|
|
|
'font-size',SqlEditorUtils.calcFontSize(sqlEditPreferences.sql_font_size)
|
|
|
|
);
|
|
|
|
self.sqlCtrl.setOption('indentWithTabs', !sqlEditPreferences.use_spaces);
|
|
|
|
self.sqlCtrl.setOption('indentUnit', sqlEditPreferences.tab_size);
|
|
|
|
self.sqlCtrl.setOption('tabSize', sqlEditPreferences.tab_size);
|
|
|
|
self.sqlCtrl.setOption('lineWrapping', sqlEditPreferences.wrap_code);
|
|
|
|
self.sqlCtrl.setOption('autoCloseBrackets', sqlEditPreferences.insert_pair_brackets);
|
|
|
|
self.sqlCtrl.setOption('matchBrackets', sqlEditPreferences.brace_matching);
|
2018-08-21 07:35:40 +00:00
|
|
|
setTimeout(function() {
|
|
|
|
self.sqlCtrl.refresh();
|
|
|
|
}, 500);
|
2018-07-05 10:38:43 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-02-05 08:06:57 +00:00
|
|
|
render: function() {
|
2016-04-22 09:54:18 +00:00
|
|
|
// Clean up the existing sql control
|
|
|
|
if (this.sqlCtrl) {
|
2016-09-22 14:27:59 +00:00
|
|
|
this.model.off('pg-property-tab-changed', this.refreshTextArea, this);
|
|
|
|
this.sqlCtrl.off('focus', this.onFocus);
|
|
|
|
this.sqlCtrl.off('blur', this.onBlur);
|
|
|
|
|
|
|
|
this.sqlCtrl.toTextArea();
|
2016-04-22 09:54:18 +00:00
|
|
|
delete this.sqlCtrl;
|
|
|
|
this.sqlCtrl = null;
|
|
|
|
this.$el.empty();
|
|
|
|
}
|
|
|
|
|
2016-02-05 08:06:57 +00:00
|
|
|
// Use the Backform TextareaControl's render function
|
|
|
|
Backform.TextareaControl.prototype.render.apply(this, arguments);
|
2016-03-04 11:00:17 +00:00
|
|
|
|
|
|
|
var field = _.defaults(this.field.toJSON(), this.defaults),
|
2018-01-12 07:29:51 +00:00
|
|
|
attributes = this.model.toJSON(),
|
|
|
|
attrArr = field.name.split('.'),
|
|
|
|
name = attrArr.shift(),
|
|
|
|
path = attrArr.join('.'),
|
|
|
|
rawValue = this.keyPathAccessor(attributes[name], path),
|
|
|
|
data = _.extend(field, {
|
|
|
|
rawValue: rawValue,
|
|
|
|
value: this.formatter.fromRaw(rawValue, this.model),
|
|
|
|
attributes: attributes,
|
|
|
|
formatter: this.formatter,
|
|
|
|
}),
|
|
|
|
evalF = function(f, d, m) {
|
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
|
|
|
};
|
2016-03-04 11:00:17 +00:00
|
|
|
|
|
|
|
// Evaluate the disabled, visible option
|
2016-04-22 09:54:18 +00:00
|
|
|
var isDisabled = evalF(data.disabled, data, this.model),
|
2018-01-12 07:29:51 +00:00
|
|
|
isVisible = evalF(data.visible, data, this.model),
|
|
|
|
self = this;
|
2016-03-04 11:00:17 +00:00
|
|
|
|
2016-04-22 17:25:50 +00:00
|
|
|
self.sqlCtrl = CodeMirror.fromTextArea(
|
2018-01-12 07:29:51 +00:00
|
|
|
(self.$el.find('textarea')[0]), {
|
|
|
|
lineNumbers: true,
|
|
|
|
mode: 'text/x-pgsql',
|
|
|
|
extraKeys: pgAdmin.Browser.editor_shortcut_keys,
|
|
|
|
});
|
2016-02-05 08:06:57 +00:00
|
|
|
|
2018-07-05 10:38:43 +00:00
|
|
|
self.reflectPreferences();
|
|
|
|
/* Check for sql editor preference changes */
|
|
|
|
pgBrowser.onPreferencesChange('sqleditor', function() {
|
|
|
|
self.reflectPreferences();
|
|
|
|
});
|
|
|
|
|
2016-08-29 07:51:45 +00:00
|
|
|
// Disable editor
|
|
|
|
if (isDisabled) {
|
2018-03-02 14:39:12 +00:00
|
|
|
// set read only mode to true instead of 'nocursor', and hide cursor using a class so that copying is enabled
|
|
|
|
self.sqlCtrl.setOption('readOnly', true);
|
2016-08-29 07:51:45 +00:00
|
|
|
var cm = self.sqlCtrl.getWrapperElement();
|
|
|
|
if (cm) {
|
2018-03-02 14:39:12 +00:00
|
|
|
cm.className += ' cm_disabled hide-cursor-workaround';
|
2016-08-29 07:51:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-04 11:00:17 +00:00
|
|
|
if (!isVisible)
|
2017-07-26 11:55:46 +00:00
|
|
|
self.$el.addClass(Backform.hiddenClassName);
|
2016-03-04 11:00:17 +00:00
|
|
|
|
2016-09-22 14:27:59 +00:00
|
|
|
// There is an issue with the Code Mirror SQL.
|
|
|
|
//
|
|
|
|
// It does not initialize the code mirror object completely when the
|
|
|
|
// referenced textarea is hidden (not visible), hence - we need to
|
|
|
|
// refresh the code mirror object on 'pg-property-tab-changed' event to
|
|
|
|
// make it work properly.
|
|
|
|
self.model.on('pg-property-tab-changed', this.refreshTextArea, this);
|
|
|
|
|
|
|
|
this.sqlCtrl.on('focus', this.onFocus);
|
|
|
|
this.sqlCtrl.on('blur', this.onBlur);
|
|
|
|
|
2016-02-05 08:06:57 +00:00
|
|
|
// Refresh SQL Field to refresh the control lazily after it renders
|
|
|
|
setTimeout(function() {
|
|
|
|
self.refreshTextArea.apply(self);
|
2016-04-29 10:11:24 +00:00
|
|
|
}, 0);
|
2016-02-05 08:06:57 +00:00
|
|
|
|
|
|
|
return self;
|
|
|
|
},
|
|
|
|
|
2016-09-22 14:27:59 +00:00
|
|
|
onFocus: function() {
|
|
|
|
var $ctrl = this.$el.find('.pgadmin-controls').first();
|
|
|
|
if (!$ctrl.hasClass('focused'))
|
|
|
|
$ctrl.addClass('focused');
|
|
|
|
},
|
|
|
|
|
|
|
|
onBlur: function() {
|
|
|
|
this.$el.find('.pgadmin-controls').first().removeClass('focused');
|
|
|
|
},
|
|
|
|
|
2016-02-05 08:06:57 +00:00
|
|
|
refreshTextArea: function() {
|
2016-04-29 10:11:24 +00:00
|
|
|
if (this.sqlCtrl) {
|
|
|
|
this.sqlCtrl.refresh();
|
|
|
|
}
|
2016-02-05 08:06:57 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
remove: function() {
|
2016-04-22 09:54:18 +00:00
|
|
|
// Clean up the sql control
|
|
|
|
if (this.sqlCtrl) {
|
2016-09-22 14:27:59 +00:00
|
|
|
this.sqlCtrl.off('focus', this.onFocus);
|
|
|
|
this.sqlCtrl.off('blur', this.onBlur);
|
2016-04-22 09:54:18 +00:00
|
|
|
delete this.sqlCtrl;
|
|
|
|
this.sqlCtrl = null;
|
2016-04-22 17:25:50 +00:00
|
|
|
this.$el.empty();
|
2016-04-22 09:54:18 +00:00
|
|
|
}
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
this.model.off('pg-property-tab-changed', this.refreshTextArea, this);
|
2016-02-05 08:06:57 +00:00
|
|
|
|
|
|
|
Backform.TextareaControl.prototype.remove.apply(this, arguments);
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2016-02-05 08:06:57 +00:00
|
|
|
});
|
|
|
|
|
2016-04-28 06:22:04 +00:00
|
|
|
// We will use this control just as a annotate in Backform
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.NoteControl = Backform.Control.extend({
|
2016-04-28 06:22:04 +00:00
|
|
|
defaults: {
|
2018-01-12 07:29:51 +00:00
|
|
|
label: gettext('Note'),
|
2016-04-28 06:22:04 +00:00
|
|
|
text: '',
|
2019-02-18 16:59:39 +00:00
|
|
|
extraClasses: ['pg-el-12', 'd-flex'],
|
|
|
|
noteClass: 'backform-note',
|
|
|
|
faIcon: 'fa-file-text-o',
|
|
|
|
faExtraClass: 'fa-rotate-180 fa-flip-vertical',
|
|
|
|
iconWidthClass: 'col-0 pr-2',
|
|
|
|
textWidthClass: 'col-sm',
|
2016-04-28 06:22:04 +00:00
|
|
|
},
|
|
|
|
template: _.template([
|
2019-02-18 16:59:39 +00:00
|
|
|
'<div class="<%=noteClass%> <%=extraClasses.join(\' \')%>">',
|
|
|
|
' <div class="icon <%=iconWidthClass%>">',
|
|
|
|
' <i class="fa <%=faIcon%> <%=faExtraClass%>" aria-hidden="true"></i>',
|
|
|
|
' </div>',
|
|
|
|
' <div class="<%=textWidthClass%>">',
|
|
|
|
' <span><%=text%></span>',
|
|
|
|
' </div>',
|
|
|
|
'</div>',
|
2018-01-12 07:29:51 +00:00
|
|
|
].join('\n')),
|
2016-04-28 06:22:04 +00:00
|
|
|
});
|
|
|
|
|
2016-05-12 18:34:28 +00:00
|
|
|
/*
|
2018-01-12 07:29:51 +00:00
|
|
|
* Input File Control: This control is used with Storage Manager Dialog,
|
|
|
|
* It allows user to perform following operations:
|
|
|
|
* - Select File
|
|
|
|
* - Select Folder
|
|
|
|
* - Create File
|
|
|
|
* - Opening Storage Manager Dialog itself.
|
|
|
|
*/
|
|
|
|
Backform.FileControl = Backform.InputControl.extend({
|
2016-05-12 18:34:28 +00:00
|
|
|
defaults: {
|
2018-01-12 07:29:51 +00:00
|
|
|
type: 'text',
|
|
|
|
label: '',
|
2016-05-12 18:34:28 +00:00
|
|
|
min: undefined,
|
|
|
|
max: undefined,
|
|
|
|
maxlength: 255,
|
|
|
|
extraClasses: [],
|
|
|
|
dialog_title: '',
|
|
|
|
btn_primary: '',
|
|
|
|
helpMessage: null,
|
2018-01-12 07:29:51 +00:00
|
|
|
dialog_type: 'select_file',
|
2016-05-12 18:34:28 +00:00
|
|
|
},
|
2018-01-12 07:29:51 +00:00
|
|
|
initialize: function() {
|
2016-05-12 18:34:28 +00:00
|
|
|
Backform.InputControl.prototype.initialize.apply(this, arguments);
|
|
|
|
},
|
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
|
|
|
'<div class="<%=Backform.controlsClassName%>">',
|
2018-10-10 11:43:26 +00:00
|
|
|
'<div class="input-group">',
|
|
|
|
'<input type="<%=type%>" class="form-control <%=extraClasses.join(\' \')%>" name="<%=name%>" min="<%=min%>" max="<%=max%>"maxlength="<%=maxlength%>" value="<%-value%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
|
|
|
|
'<div class="input-group-append">',
|
2019-01-02 09:35:15 +00:00
|
|
|
'<button class="btn btn-secondary fa fa-ellipsis-h select_item" <%=disabled ? "disabled" : ""%> ></button>',
|
2018-10-10 11:43:26 +00:00
|
|
|
'</div>',
|
|
|
|
'</div>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'<% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
'<span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
'<% } %>',
|
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
2016-05-20 12:13:30 +00:00
|
|
|
events: function() {
|
2016-05-21 09:31:47 +00:00
|
|
|
// Inherit all default events of InputControl
|
2016-05-20 12:13:30 +00:00
|
|
|
return _.extend({}, Backform.InputControl.prototype.events, {
|
2018-01-12 07:29:51 +00:00
|
|
|
'click .select_item': 'onSelect',
|
2016-05-21 09:31:47 +00:00
|
|
|
});
|
2016-05-12 18:34:28 +00:00
|
|
|
},
|
2018-01-12 07:29:51 +00:00
|
|
|
onSelect: function() {
|
2017-07-18 14:13:16 +00:00
|
|
|
var dialog_type = this.field.get('dialog_type'),
|
2018-01-12 07:29:51 +00:00
|
|
|
supp_types = this.field.get('supp_types'),
|
|
|
|
btn_primary = this.field.get('btn_primary'),
|
|
|
|
dialog_title = this.field.get('dialog_title'),
|
|
|
|
params = {
|
|
|
|
supported_types: supp_types,
|
|
|
|
dialog_type: dialog_type,
|
|
|
|
dialog_title: dialog_title,
|
|
|
|
btn_primary: btn_primary,
|
|
|
|
};
|
2016-05-21 09:31:47 +00:00
|
|
|
|
2016-05-12 18:34:28 +00:00
|
|
|
pgAdmin.FileManager.init();
|
|
|
|
pgAdmin.FileManager.show_dialog(params);
|
2017-11-21 15:59:44 +00:00
|
|
|
// Listen click events of Storage Manager dialog buttons
|
|
|
|
this.listen_file_dlg_events();
|
2016-05-12 18:34:28 +00:00
|
|
|
},
|
|
|
|
storage_dlg_hander: function(value) {
|
2018-01-12 07:29:51 +00:00
|
|
|
var attrArr = this.field.get('name').split('.'),
|
|
|
|
name = attrArr.shift();
|
2016-05-12 18:34:28 +00:00
|
|
|
|
2017-11-21 15:59:44 +00:00
|
|
|
this.remove_file_dlg_event_listeners();
|
|
|
|
|
2016-05-12 18:34:28 +00:00
|
|
|
// Set selected value into the model
|
|
|
|
this.model.set(name, decodeURI(value));
|
2019-01-21 09:57:55 +00:00
|
|
|
this.$el.find('input[type=text]').focus();
|
2017-08-09 11:14:29 +00:00
|
|
|
},
|
2017-11-21 15:59:44 +00:00
|
|
|
storage_close_dlg_hander: function() {
|
|
|
|
this.remove_file_dlg_event_listeners();
|
|
|
|
},
|
|
|
|
listen_file_dlg_events: function() {
|
2018-01-12 07:29:51 +00:00
|
|
|
pgAdmin.Browser.Events.on('pgadmin-storage:finish_btn:' + this.field.get('dialog_type'), this.storage_dlg_hander, this);
|
|
|
|
pgAdmin.Browser.Events.on('pgadmin-storage:cancel_btn:' + this.field.get('dialog_type'), this.storage_close_dlg_hander, this);
|
2017-11-21 15:59:44 +00:00
|
|
|
},
|
|
|
|
remove_file_dlg_event_listeners: function() {
|
2018-01-12 07:29:51 +00:00
|
|
|
pgAdmin.Browser.Events.off('pgadmin-storage:finish_btn:' + this.field.get('dialog_type'), this.storage_dlg_hander, this);
|
|
|
|
pgAdmin.Browser.Events.off('pgadmin-storage:cancel_btn:' + this.field.get('dialog_type'), this.storage_close_dlg_hander, this);
|
2017-11-21 15:59:44 +00:00
|
|
|
},
|
2017-08-09 11:14:29 +00:00
|
|
|
clearInvalid: function() {
|
|
|
|
Backform.InputControl.prototype.clearInvalid.apply(this, arguments);
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$el.removeClass('pgadmin-file-has-error');
|
2017-08-09 11:14:29 +00:00
|
|
|
return this;
|
|
|
|
},
|
|
|
|
updateInvalid: function() {
|
|
|
|
Backform.InputControl.prototype.updateInvalid.apply(this, arguments);
|
|
|
|
// Introduce a new class to fix the error icon placement on the control
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$el.addClass('pgadmin-file-has-error');
|
|
|
|
},
|
2018-06-29 14:14:37 +00:00
|
|
|
disable_button: function() {
|
|
|
|
this.$el.find('button.select_item').attr('disabled', 'disabled');
|
|
|
|
},
|
|
|
|
enable_button: function() {
|
|
|
|
this.$el.find('button.select_item').removeAttr('disabled');
|
|
|
|
},
|
2016-05-12 18:34:28 +00:00
|
|
|
});
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.DatetimepickerControl =
|
|
|
|
Backform.InputControl.extend({
|
|
|
|
defaults: {
|
|
|
|
type: 'text',
|
|
|
|
label: '',
|
|
|
|
options: {
|
|
|
|
format: 'YYYY-MM-DD HH:mm:ss Z',
|
2018-10-11 12:23:59 +00:00
|
|
|
icons: {
|
|
|
|
clear: 'fa fa-trash',
|
|
|
|
},
|
|
|
|
buttons: {
|
|
|
|
showToday: true,
|
|
|
|
},
|
2018-01-12 07:29:51 +00:00
|
|
|
toolbarPlacement: 'top',
|
|
|
|
widgetPositioning: {
|
|
|
|
horizontal: 'auto',
|
|
|
|
vertical: 'bottom',
|
|
|
|
},
|
2018-10-11 12:23:59 +00:00
|
|
|
keepOpen: false,
|
2017-08-23 05:56:24 +00:00
|
|
|
},
|
2018-01-12 07:29:51 +00:00
|
|
|
placeholder: 'YYYY-MM-DD HH:mm:ss Z',
|
|
|
|
extraClasses: [],
|
|
|
|
helpMessage: null,
|
2016-09-22 14:27:59 +00:00
|
|
|
},
|
2018-01-12 07:29:51 +00:00
|
|
|
events: {
|
|
|
|
'blur input': 'onChange',
|
|
|
|
'change input': 'onChange',
|
|
|
|
'changeDate input': 'onChange',
|
|
|
|
'focus input': 'clearInvalid',
|
2018-10-12 09:32:38 +00:00
|
|
|
'focusout input': 'closePicker',
|
|
|
|
'change.datetimepicker': 'onChange',
|
|
|
|
'click': 'togglePicker',
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2018-10-12 09:32:38 +00:00
|
|
|
togglePicker: function() {
|
2018-01-12 07:29:51 +00:00
|
|
|
if (this.has_datepicker) {
|
2018-10-11 12:23:59 +00:00
|
|
|
this.$el.find('input').datetimepicker('toggle');
|
2018-01-12 07:29:51 +00:00
|
|
|
}
|
|
|
|
},
|
2018-10-12 09:32:38 +00:00
|
|
|
closePicker: function() {
|
|
|
|
if (this.has_datepicker) {
|
|
|
|
this.$el.find('input').datetimepicker('hide');
|
|
|
|
}
|
|
|
|
},
|
2018-01-12 07:29:51 +00:00
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
|
|
|
'<div class="input-group <%=Backform.controlsClassName%>">',
|
2018-10-11 12:23:59 +00:00
|
|
|
' <input type="text" class="<%=Backform.controlClassName%> datetimepicker-input <%=extraClasses.join(\' \')%>" name="<%=name%>" value="<%-value%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> data-toggle="datetimepicker"/>',
|
|
|
|
' <div class="input-group-append">',
|
|
|
|
' <span class="input-group-text fa fa-calendar"></span>',
|
|
|
|
' </div>',
|
2019-01-30 06:12:35 +00:00
|
|
|
'</div>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'<% if (helpMessage && helpMessage.length) { %>',
|
2019-01-30 06:12:35 +00:00
|
|
|
'<div class="<%=Backform.helpBlockControlClass%>">',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
2019-01-22 05:52:32 +00:00
|
|
|
'</div>',
|
2019-01-30 06:12:35 +00:00
|
|
|
'<% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
].join('\n')),
|
|
|
|
render: function() {
|
|
|
|
var field = _.defaults(this.field.toJSON(), this.defaults),
|
2016-09-22 14:27:59 +00:00
|
|
|
attributes = this.model.toJSON(),
|
|
|
|
attrArr = field.name.split('.'),
|
|
|
|
name = attrArr.shift(),
|
|
|
|
path = attrArr.join('.'),
|
|
|
|
rawValue = this.keyPathAccessor(attributes[name], path),
|
|
|
|
data = _.extend(field, {
|
|
|
|
rawValue: rawValue,
|
|
|
|
value: this.formatter.fromRaw(rawValue, this.model),
|
|
|
|
attributes: attributes,
|
2018-01-12 07:29:51 +00:00
|
|
|
formatter: this.formatter,
|
2016-09-22 14:27:59 +00:00
|
|
|
}),
|
|
|
|
evalF = function(f, m) {
|
|
|
|
return (_.isFunction(f) ? !!f(m) : !!f);
|
|
|
|
};
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
// Evaluate the disabled, visible, and required option
|
|
|
|
_.extend(data, {
|
|
|
|
disabled: evalF(data.disabled, this.model),
|
|
|
|
visible: evalF(data.visible, this.model),
|
|
|
|
required: evalF(data.required, this.model),
|
|
|
|
});
|
|
|
|
if (!data.disabled) {
|
|
|
|
data.placeholder = data.placeholder || this.defaults.placeholder;
|
|
|
|
}
|
2016-09-22 14:27:59 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
// Clean up first
|
|
|
|
if (this.has_datepicker)
|
|
|
|
this.$el.find('input').datetimepicker('destroy');
|
|
|
|
this.$el.empty();
|
|
|
|
this.$el.removeClass(Backform.hiddenClassName);
|
2016-09-22 14:27:59 +00:00
|
|
|
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$el.html(this.template(data)).addClass(field.name);
|
2016-09-22 14:27:59 +00:00
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
if (!data.visible) {
|
|
|
|
this.has_datepicker = false;
|
|
|
|
this.$el.addClass(Backform.hiddenClassName);
|
|
|
|
} else {
|
|
|
|
this.has_datepicker = true;
|
|
|
|
var self = this;
|
2019-02-25 10:24:11 +00:00
|
|
|
if (!_.isUndefined(data.value) && !_.isNull(data.value)
|
|
|
|
&& data.value.toLowerCase() === 'infinity') {
|
|
|
|
data.value = null;
|
|
|
|
}
|
|
|
|
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$el.find('input').first().datetimepicker(
|
|
|
|
_.extend({
|
|
|
|
keyBinds: {
|
|
|
|
enter: function(widget) {
|
2016-09-22 14:27:59 +00:00
|
|
|
var picker = this;
|
2018-01-12 07:29:51 +00:00
|
|
|
if (widget) {
|
|
|
|
setTimeout(function() {
|
|
|
|
picker.toggle();
|
2018-05-25 15:26:37 +00:00
|
|
|
self.$el.find('input').first().trigger('blur');
|
2018-01-12 07:29:51 +00:00
|
|
|
}, 10);
|
|
|
|
} else {
|
|
|
|
setTimeout(function() {
|
|
|
|
picker.toggle();
|
|
|
|
}, 10);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
tab: function(widget) {
|
|
|
|
if (!widget) {
|
|
|
|
// blur the input
|
|
|
|
setTimeout(
|
|
|
|
function() {
|
2018-05-25 15:26:37 +00:00
|
|
|
self.$el.find('input').first().trigger('blur');
|
2018-01-12 07:29:51 +00:00
|
|
|
}, 10
|
|
|
|
);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
escape: function(widget) {
|
|
|
|
if (widget) {
|
|
|
|
var picker = this;
|
|
|
|
setTimeout(function() {
|
|
|
|
picker.toggle();
|
2018-05-25 15:26:37 +00:00
|
|
|
self.$el.find('input').first().trigger('blur');
|
2018-01-12 07:29:51 +00:00
|
|
|
}, 10);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}, this.defaults.options, this.field.get('options'), {
|
|
|
|
'date': data.value,
|
2018-03-02 11:11:26 +00:00
|
|
|
'minDate': data.value,
|
2018-01-12 07:29:51 +00:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
this.updateInvalid();
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
clearInvalid: function() {
|
|
|
|
Backform.InputControl.prototype.clearInvalid.apply(this, arguments);
|
|
|
|
this.$el.removeClass('pgadmin-datepicker-has-error');
|
|
|
|
return this;
|
|
|
|
},
|
|
|
|
updateInvalid: function() {
|
|
|
|
Backform.InputControl.prototype.updateInvalid.apply(this, arguments);
|
|
|
|
// Introduce a new class to fix the error icon placement on the control
|
|
|
|
this.$el.addClass('pgadmin-datepicker-has-error');
|
|
|
|
},
|
|
|
|
cleanup: function() {
|
|
|
|
if (this.has_datepicker)
|
|
|
|
this.$el.find('input').datetimepicker('destroy');
|
|
|
|
this.$el.empty();
|
|
|
|
},
|
|
|
|
});
|
2017-11-21 16:28:01 +00:00
|
|
|
|
|
|
|
// Color Picker control
|
2018-01-12 07:29:51 +00:00
|
|
|
Backform.ColorControl = Backform.InputControl.extend({
|
2017-11-21 16:28:01 +00:00
|
|
|
defaults: {
|
2018-01-12 07:29:51 +00:00
|
|
|
label: '',
|
2017-11-21 16:28:01 +00:00
|
|
|
extraClasses: [],
|
|
|
|
helpMessage: null,
|
|
|
|
showButtons: false,
|
|
|
|
showPalette: true,
|
|
|
|
allowEmpty: true,
|
2018-01-12 07:29:51 +00:00
|
|
|
colorFormat: 'hex',
|
|
|
|
defaultColor: '',
|
2017-11-21 16:28:01 +00:00
|
|
|
},
|
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
|
|
|
'<div class="<%=Backform.controlsClassName%>">',
|
|
|
|
' <input class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>" value="<%-value%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
|
|
|
|
' <% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
' <% } %>',
|
2018-01-12 07:29:51 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
2017-11-21 16:28:01 +00:00
|
|
|
render: function() {
|
|
|
|
// Clear first
|
2018-01-12 07:29:51 +00:00
|
|
|
if (this.$picker && this.$picker.hasOwnProperty('destroy')) {
|
2017-11-21 16:28:01 +00:00
|
|
|
this.$picker('destroy');
|
|
|
|
}
|
|
|
|
|
|
|
|
var field = _.defaults(this.field.toJSON(), this.defaults),
|
2018-01-12 07:29:51 +00:00
|
|
|
attributes = this.model.toJSON(),
|
|
|
|
attrArr = field.name.split('.'),
|
|
|
|
name = attrArr.shift(),
|
|
|
|
path = attrArr.join('.'),
|
|
|
|
rawValue = this.keyPathAccessor(attributes[name], path),
|
|
|
|
data = _.extend(field, {
|
|
|
|
rawValue: rawValue,
|
|
|
|
value: this.formatter.fromRaw(rawValue, this.model),
|
|
|
|
attributes: attributes,
|
|
|
|
formatter: this.formatter,
|
|
|
|
}),
|
|
|
|
evalF = function(f, d, m) {
|
|
|
|
return (_.isFunction(f) ? !!f.apply(d, [m]) : !!f);
|
|
|
|
};
|
2017-11-21 16:28:01 +00:00
|
|
|
|
|
|
|
// Evaluate the disabled, visible, and required option
|
|
|
|
_.extend(data, {
|
|
|
|
disabled: evalF(data.disabled, data, this.model),
|
2018-01-12 07:29:51 +00:00
|
|
|
visible: evalF(data.visible, data, this.model),
|
|
|
|
required: evalF(data.required, data, this.model),
|
2017-11-21 16:28:01 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Clean up first
|
|
|
|
this.$el.empty();
|
|
|
|
|
|
|
|
if (!data.visible)
|
|
|
|
this.$el.addClass(Backform.hiddenClassname);
|
|
|
|
|
|
|
|
this.$el.html(this.template(data)).addClass(field.name);
|
|
|
|
|
|
|
|
// Creating default Color picker
|
2018-01-12 07:29:51 +00:00
|
|
|
this.$picker = this.$el.find('input').spectrum({
|
2017-11-21 16:28:01 +00:00
|
|
|
allowEmpty: data.allowEmpty,
|
|
|
|
preferredFormat: data.colorFormat,
|
|
|
|
disabled: data.disabled,
|
2018-01-12 07:29:51 +00:00
|
|
|
hideAfterPaletteSelect: true,
|
2017-11-21 16:28:01 +00:00
|
|
|
clickoutFiresChange: true,
|
|
|
|
showButtons: data.showButtons,
|
|
|
|
showPaletteOnly: data.showPalette,
|
|
|
|
togglePaletteOnly: data.showPalette,
|
|
|
|
togglePaletteMoreText: gettext('More'),
|
|
|
|
togglePaletteLessText: gettext('Less'),
|
|
|
|
color: data.value || data.defaultColor,
|
|
|
|
// Predefined palette colors
|
|
|
|
palette: [
|
2018-01-12 07:29:51 +00:00
|
|
|
['#000', '#444', '#666', '#999', '#ccc', '#eee', '#f3f3f3', '#fff'],
|
|
|
|
['#f00', '#f90', '#ff0', '#0f0', '#0ff', '#00f', '#90f', '#f0f'],
|
|
|
|
['#f4cccc', '#fce5cd', '#fff2cc', '#d9ead3', '#d0e0e3', '#cfe2f3', '#d9d2e9', '#ead1dc'],
|
|
|
|
['#ea9999', '#f9cb9c', '#ffe599', '#b6d7a8', '#a2c4c9', '#9fc5e8', '#b4a7d6', '#d5a6bd'],
|
|
|
|
['#e06666', '#f6b26b', '#ffd966', '#93c47d', '#76a5af', '#6fa8dc', '#8e7cc3', '#c27ba0'],
|
|
|
|
['#c00', '#e69138', '#f1c232', '#6aa84f', '#45818e', '#3d85c6', '#674ea7', '#a64d79'],
|
|
|
|
['#900', '#b45f06', '#bf9000', '#38761d', '#134f5c', '#0b5394', '#351c75', '#741b47'],
|
|
|
|
['#600', '#783f04', '#7f6000', '#274e13', '#0c343d', '#073763', '#20124d', '#4c1130'],
|
|
|
|
],
|
2017-11-21 16:28:01 +00:00
|
|
|
|
|
|
|
});
|
|
|
|
this.updateInvalid();
|
|
|
|
return this;
|
2018-01-12 07:29:51 +00:00
|
|
|
},
|
2017-11-21 16:28:01 +00:00
|
|
|
});
|
|
|
|
|
2018-01-25 12:49:06 +00:00
|
|
|
var KeyCodeControlFormatter = Backform.KeyCodeControlFormatter = function() {};
|
|
|
|
|
|
|
|
_.extend(KeyCodeControlFormatter.prototype, {
|
|
|
|
fromRaw: function (rawData) {
|
|
|
|
return rawData['char'];
|
|
|
|
},
|
|
|
|
// we don't need toRaw
|
|
|
|
toRaw: undefined,
|
|
|
|
});
|
|
|
|
|
|
|
|
Backform.KeyCodeControl = Backform.InputControl.extend({
|
|
|
|
defaults: _.defaults({
|
|
|
|
escapeKeyCodes: [16, 17, 18, 27], // Shift, Ctrl, Alt/Option, Escape
|
|
|
|
}, Backform.InputControl.prototype.defaults),
|
|
|
|
|
|
|
|
events: {
|
|
|
|
'keydown input': 'onkeyDown',
|
|
|
|
'keyup input': 'preventEvent',
|
|
|
|
'focus select': 'clearInvalid',
|
|
|
|
},
|
|
|
|
|
|
|
|
formatter: KeyCodeControlFormatter,
|
|
|
|
|
|
|
|
preventEvent: function(e) {
|
|
|
|
var key_code = e.which || e.keyCode,
|
|
|
|
field = _.defaults(this.field.toJSON(), this.defaults);
|
|
|
|
|
|
|
|
if (field.escapeKeyCodes.indexOf(key_code) != -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
e.preventDefault();
|
|
|
|
e.stopImmediatePropagation();
|
|
|
|
e.stopPropagation();
|
|
|
|
},
|
|
|
|
|
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%> keyboard-shortcut-label"><%=label%></label>',
|
|
|
|
'<div class="<%=Backform.controlsClassName%>">',
|
|
|
|
' <input type="<%=type%>" class="<%=Backform.controlClassName%> <%=extraClasses.join(\' \')%>" name="<%=name%>" oncopy="return false; oncut="return false; onpaste="return false;" maxlength="<%=maxlength%>" value="<%-value%>" placeholder="<%-placeholder%>" <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
|
|
|
|
' <% if (helpMessage && helpMessage.length) { %>',
|
|
|
|
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
|
|
|
|
' <% } %>',
|
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
|
|
|
onkeyDown: function(e) {
|
|
|
|
var self = this,
|
|
|
|
model = this.model,
|
|
|
|
attrArr = this.field.get('name').split('.'),
|
|
|
|
name = attrArr.shift(),
|
|
|
|
changes = {},
|
|
|
|
key_code = e.which || e.keyCode,
|
|
|
|
key,
|
|
|
|
field = _.defaults(this.field.toJSON(), this.defaults);
|
|
|
|
|
|
|
|
if (field.escapeKeyCodes.indexOf(key_code) != -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.model.errorModel instanceof Backbone.Model) {
|
|
|
|
this.model.errorModel.unset(name);
|
|
|
|
}
|
|
|
|
key = gettext(e.key);
|
|
|
|
if (key_code == 32) {
|
|
|
|
key = gettext('Space');
|
|
|
|
}
|
|
|
|
changes = {
|
|
|
|
'key_code': e.which || e.keyCode,
|
|
|
|
'char': key,
|
|
|
|
};
|
|
|
|
|
|
|
|
this.stopListening(this.model, 'change:' + name, this.render);
|
|
|
|
model.set(name, changes);
|
|
|
|
this.listenTo(this.model, 'change:' + name, this.render);
|
|
|
|
setTimeout(function() {
|
|
|
|
self.$el.find('input').val(key);
|
|
|
|
});
|
|
|
|
e.preventDefault();
|
|
|
|
},
|
|
|
|
keyPathAccessor: function(obj, path) {
|
|
|
|
var res = obj;
|
|
|
|
path = path.split('.');
|
|
|
|
for (var i = 0; i < path.length; i++) {
|
|
|
|
if (_.isNull(res)) return null;
|
|
|
|
if (_.isEmpty(path[i])) continue;
|
|
|
|
if (!_.isUndefined(res[path[i]])) res = res[path[i]];
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
Backform.KeyboardShortcutControl = Backform.Control.extend({
|
|
|
|
|
|
|
|
initialize: function() {
|
|
|
|
|
|
|
|
Backform.Control.prototype.initialize.apply(this, arguments);
|
|
|
|
|
|
|
|
var fields = this.field.get('fields');
|
|
|
|
|
|
|
|
if (fields == null || fields == undefined) {
|
|
|
|
throw new ReferenceError('"fields" not found in keyboard shortcut');
|
|
|
|
}
|
|
|
|
|
|
|
|
this.innerModel = new Backbone.Model();
|
|
|
|
|
|
|
|
this.controls = [];
|
|
|
|
},
|
|
|
|
cleanup: function() {
|
|
|
|
|
|
|
|
this.stopListening(this.innerModel, 'change', this.onInnerModelChange);
|
|
|
|
|
|
|
|
_.each(this.controls, function(c) {
|
|
|
|
c.remove();
|
|
|
|
});
|
|
|
|
|
|
|
|
this.controls.length = 0;
|
|
|
|
},
|
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=label%></label>',
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
'<div class="<%=Backform.controlsClassName%> d-flex flex-row">',
|
2018-01-25 12:49:06 +00:00
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
|
|
|
|
|
|
|
onInnerModelChange: function() {
|
|
|
|
|
|
|
|
var name = this.field.get('name'),
|
|
|
|
val = $.extend(true, {}, this.model.get(name));
|
|
|
|
|
|
|
|
this.stopListening(this.model, 'change:' + name, this.render);
|
|
|
|
|
|
|
|
this.model.set(name,
|
|
|
|
$.extend(true, val, this.innerModel.toJSON())
|
|
|
|
);
|
|
|
|
|
|
|
|
this.listenTo(this.model, 'change:' + name, this.render);
|
|
|
|
},
|
|
|
|
|
|
|
|
render: function() {
|
|
|
|
this.cleanup();
|
|
|
|
this.$el.empty();
|
|
|
|
|
|
|
|
var self = this,
|
|
|
|
initial_value = {},
|
|
|
|
field = _.defaults(this.field.toJSON(), this.defaults),
|
|
|
|
value = self.model.get(field['name']),
|
|
|
|
innerFields = field['fields'];
|
|
|
|
|
|
|
|
this.$el.html(self.template(field)).addClass(field.name);
|
|
|
|
|
|
|
|
var $container = $(self.$el.find('.pgadmin-controls'));
|
|
|
|
|
|
|
|
_.each(innerFields, function(field) {
|
|
|
|
initial_value[field['name']] = value[field['name']];
|
|
|
|
});
|
|
|
|
|
|
|
|
self.innerModel.set(initial_value);
|
|
|
|
|
|
|
|
self.listenTo(self.innerModel, 'change', self.onInnerModelChange);
|
|
|
|
|
|
|
|
_.each(innerFields, function(fld) {
|
|
|
|
|
|
|
|
var f = new Backform.Field(
|
2019-03-14 15:11:16 +00:00
|
|
|
_.extend({}, {
|
|
|
|
id: fld['name'],
|
|
|
|
name: fld['name'],
|
|
|
|
control: fld['type'] == 'checkbox' ? 'checkboxWithBox' : fld['type'],
|
|
|
|
label: fld['label'],
|
|
|
|
})
|
2018-01-25 12:49:06 +00:00
|
|
|
),
|
|
|
|
cntr = new (f.get('control')) ({
|
|
|
|
field: f,
|
|
|
|
model: self.innerModel,
|
|
|
|
});
|
|
|
|
|
|
|
|
cntr.render();
|
|
|
|
|
|
|
|
if(fld['type'] == 'checkbox') {
|
2018-08-07 13:41:50 +00:00
|
|
|
// Remove control label for keyboard shortcuts to
|
|
|
|
// align it properly.
|
|
|
|
let label = cntr.$el.find('label.control-label').text().trim();
|
|
|
|
if (label.length <= 0) {
|
|
|
|
cntr.$el.find('label.control-label').remove();
|
|
|
|
}
|
|
|
|
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
if (fld['name'] == 'alt') {
|
|
|
|
$container.append($('<div class="pg-el-sm-3 pg-el-12"></div>').append(cntr.$el));
|
2018-01-25 12:49:06 +00:00
|
|
|
} else {
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
$container.append($('<div class="pg-el-sm-2 pg-el-12"></div>').append(cntr.$el));
|
2018-01-25 12:49:06 +00:00
|
|
|
}
|
|
|
|
} else {
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
$container.append($('<div class="pg-el-sm-5 pg-el-12"></div>').append(cntr.$el));
|
2018-01-25 12:49:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// We will keep track of all the controls rendered at the
|
|
|
|
// moment.
|
|
|
|
self.controls.push(cntr);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
return self;
|
|
|
|
},
|
|
|
|
remove: function() {
|
|
|
|
/* First do the clean up */
|
|
|
|
this.cleanup();
|
|
|
|
Backform.Control.prototype.remove.apply(this, arguments);
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
Improvement in the look and feel of the whole application
Changed the SCSS/CSS for the below third party libraries to adopt the
new look 'n' feel:
- wcDocker
- Alertify dialogs, and notifications
- AciTree
- Bootstrap Navbar
- Bootstrap Tabs
- Bootstrap Drop-Down menu
- Backgrid
- Select2
Adopated the new the look 'n' feel for the dialogs, wizard, properties,
tab panels, tabs, fieldset, subnode control, spinner control, HTML
table, and other form controls.
- Font is changed to Roboto
- Using SCSS variables to define the look 'n' feel
- Designer background images for the Login, and Forget password pages in
'web' mode
- Improved the look 'n' feel for the key selection in the preferences
dialog
- Table classes consistency changes across the application
- File Open and Save dialog list view changes
Author(s): Aditya Toshniwal & Khushboo Vashi
2018-12-21 11:44:55 +00:00
|
|
|
Backform.CheckboxWithBoxControl = Backform.CheckboxControl.extend({
|
|
|
|
events: _.extend({}, Backform.CheckboxControl.prototype.events, {
|
|
|
|
'click button': 'onButtonClick',
|
|
|
|
}),
|
|
|
|
template: _.template([
|
|
|
|
'<label class="<%=Backform.controlLabelClassName%>"><%=controlLabel%></label>',
|
|
|
|
'<div class="<%=Backform.controlContainerClassName%>">',
|
|
|
|
' <button class="btn btn-secondary btn-checkbox">',
|
|
|
|
' <input type="<%=type%>" class="<%=extraClasses.join(\' \')%>" id="<%=id%>" name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
|
|
|
|
' <%=label%>',
|
|
|
|
' </button>',
|
|
|
|
'</div>',
|
|
|
|
].join('\n')),
|
|
|
|
onButtonClick: function(e) {
|
|
|
|
if (e.target.nodeName !== 'BUTTON')
|
|
|
|
return;
|
|
|
|
var $el = this.$el.find('input[type=checkbox]');
|
|
|
|
$el.prop('checked', !$el.prop('checked'));
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2015-06-30 05:51:55 +00:00
|
|
|
return Backform;
|
2018-01-12 07:29:51 +00:00
|
|
|
});
|