mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2025-02-25 18:55:31 -06:00
Miscellaneous fixes for icon sizing issue, missing tree nodes to be
webpacked, etc. - Fixed the background-size for the svg icons to 20x20 px such that it does not adjust it's size when a context menu height is different due to length of the node label, also - it allows us to fix the issue of icons sizes in the Grant Wizard. - Added two missing browser tree nodes in the webpack configuration i.e. Foreign Server, and User Mapping - Removed a redundant javascript file foreign-server.js - Fixed the missing context menu icons for Foreign Table, and Tablespace nodes. Thanks EDB development team to find the regression added after the icon changes in quick time.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
.icon-coll-domain_constraints {
|
||||
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/coll-domain_constraints.svg' )}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -9,6 +10,7 @@
|
||||
.icon-check-bad, .icon-domain_constraints-bad {
|
||||
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/domain_constraints-bad.png' )}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -17,6 +19,7 @@
|
||||
.icon-check, .icon-domain_constraints {
|
||||
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/domain_constraints.svg' )}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
|
||||
@@ -1,719 +0,0 @@
|
||||
/* Create and Register Foreign Table Collection and Node. */
|
||||
define('pgadmin.node.foreign-table', [
|
||||
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
|
||||
'underscore.string', 'sources/pgadmin', 'pgadmin.browser', 'alertify',
|
||||
'pgadmin.browser.collection'
|
||||
], function(gettext, url_for, $, _, S, pgAdmin, pgBrowser, alertify) {
|
||||
|
||||
if (!pgBrowser.Nodes['coll-foreign-table']) {
|
||||
var foreigntable = pgBrowser.Nodes['coll-foreign-table'] =
|
||||
pgBrowser.Collection.extend({
|
||||
node: 'foreign-table',
|
||||
label: gettext('Foreign Tables'),
|
||||
type: 'coll-foreign-table',
|
||||
columns: ['name', 'owner', 'description']
|
||||
});
|
||||
};
|
||||
|
||||
// Integer Cell for Columns Length and Precision
|
||||
var IntegerDepCell = Backgrid.IntegerCell.extend({
|
||||
initialize: function() {
|
||||
Backgrid.NumberCell.prototype.initialize.apply(this, arguments);
|
||||
Backgrid.Extension.DependentCell.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
dependentChanged: function () {
|
||||
this.$el.empty();
|
||||
var model = this.model;
|
||||
var column = this.column;
|
||||
var editable = this.column.get("editable");
|
||||
var is_editable = _.isFunction(editable) ? !!editable.apply(column, [model]) : !!editable;
|
||||
|
||||
if (is_editable){ this.$el.addClass("editable"); }
|
||||
else { this.$el.removeClass("editable"); }
|
||||
|
||||
this.delegateEvents();
|
||||
return this;
|
||||
},
|
||||
remove: Backgrid.Extension.DependentCell.prototype.remove
|
||||
});
|
||||
|
||||
|
||||
// Options Model
|
||||
var ColumnOptionsModel = pgBrowser.Node.Model.extend({
|
||||
idAttribute: 'option',
|
||||
defaults: {
|
||||
option: undefined,
|
||||
value: undefined
|
||||
},
|
||||
schema: [
|
||||
{id: 'option', label:'Option', type:'text', editable: true, cellHeaderClasses: 'width_percent_30'},
|
||||
{
|
||||
id: 'value', label:'Value', type: 'text', editable: true, cellHeaderClasses: 'width_percent_50'
|
||||
}
|
||||
],
|
||||
validate: function() {
|
||||
if (_.isUndefined(this.get('value')) ||
|
||||
_.isNull(this.get('value')) ||
|
||||
String(this.get('value')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
var msg = 'Please enter a value.';
|
||||
|
||||
this.errorModel.set('value', msg);
|
||||
|
||||
return msg;
|
||||
} else {
|
||||
this.errorModel.unset('value');
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
// Columns Model
|
||||
var ColumnsModel = pgBrowser.Node.Model.extend({
|
||||
idAttribute: 'attnum',
|
||||
defaults: {
|
||||
attname: undefined,
|
||||
datatype: undefined,
|
||||
typlen: undefined,
|
||||
precision: undefined,
|
||||
typdefault: undefined,
|
||||
attnotnull: undefined,
|
||||
collname: undefined,
|
||||
attnum: undefined,
|
||||
inheritedfrom: undefined,
|
||||
inheritedid: undefined,
|
||||
attstattarget: undefined,
|
||||
coloptions: []
|
||||
},
|
||||
type_options: undefined,
|
||||
schema: [{
|
||||
id: 'attname', label: gettext('Name'), cell: 'string', type: 'text',
|
||||
editable: 'is_editable_column', cellHeaderClasses: 'width_percent_40'
|
||||
},{
|
||||
id: 'datatype', label: gettext('Data Type'), cell: 'node-ajax-options',
|
||||
control: 'node-ajax-options', type: 'text', url: 'get_types',
|
||||
editable: 'is_editable_column', cellHeaderClasses: 'width_percent_0',
|
||||
group: gettext('Definition'),
|
||||
transform: function(d, self){
|
||||
self.model.type_options = d;
|
||||
return d;
|
||||
}
|
||||
},{
|
||||
id: 'typlen', label: gettext('Length'),
|
||||
cell: 'string', group: gettext('Definition'),
|
||||
type: 'int', deps: ['datatype'],
|
||||
disabled: function(m) {
|
||||
var val = m.get('typlen');
|
||||
// We will store type from selected from combobox
|
||||
if(!(_.isUndefined(m.get('inheritedid'))
|
||||
|| _.isNull(m.get('inheritedid'))
|
||||
|| _.isUndefined(m.get('inheritedfrom'))
|
||||
|| _.isNull(m.get('inheritedfrom')))) {
|
||||
|
||||
if (!_.isUndefined(val)) {
|
||||
setTimeout(function() {
|
||||
m.set('typlen', undefined);
|
||||
}, 10);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var of_type = m.get('datatype'),
|
||||
has_length = false;
|
||||
if(m.type_options) {
|
||||
m.set('is_tlength', false, {silent: true});
|
||||
|
||||
// iterating over all the types
|
||||
_.each(m.type_options, function(o) {
|
||||
// if type from selected from combobox matches in options
|
||||
if ( of_type == o.value ) {
|
||||
// if length is allowed for selected type
|
||||
if(o.length)
|
||||
{
|
||||
// set the values in model
|
||||
has_length = true;
|
||||
m.set('is_tlength', true, {silent: true});
|
||||
m.set('min_val', o.min_val, {silent: true});
|
||||
m.set('max_val', o.max_val, {silent: true});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!has_length && !_.isUndefined(val)) {
|
||||
setTimeout(function() {
|
||||
m.set('typlen', undefined);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
return !(m.get('is_tlength'));
|
||||
}
|
||||
if (!has_length && !_.isUndefined(val)) {
|
||||
setTimeout(function() {
|
||||
m.set('typlen', undefined);
|
||||
}, 10);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
cellHeaderClasses: 'width_percent_10'
|
||||
},{
|
||||
id: 'precision', label: gettext('Precision'),
|
||||
type: 'int', deps: ['datatype'],
|
||||
cell: 'string', group: gettext('Definition'),
|
||||
disabled: function(m) {
|
||||
var val = m.get('precision');
|
||||
if(!(_.isUndefined(m.get('inheritedid'))
|
||||
|| _.isNull(m.get('inheritedid'))
|
||||
|| _.isUndefined(m.get('inheritedfrom'))
|
||||
|| _.isNull(m.get('inheritedfrom')))) {
|
||||
|
||||
if (!_.isUndefined(val)) {
|
||||
setTimeout(function() {
|
||||
m.set('precision', undefined);
|
||||
}, 10);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var of_type = m.get('datatype'),
|
||||
has_precision = false;
|
||||
|
||||
if(m.type_options) {
|
||||
m.set('is_precision', false, {silent: true});
|
||||
// iterating over all the types
|
||||
_.each(m.type_options, function(o) {
|
||||
// if type from selected from combobox matches in options
|
||||
if ( of_type == o.value ) {
|
||||
// if precession is allowed for selected type
|
||||
if(o.precision)
|
||||
{
|
||||
has_precision = true;
|
||||
// set the values in model
|
||||
m.set('is_precision', true, {silent: true});
|
||||
m.set('min_val', o.min_val, {silent: true});
|
||||
m.set('max_val', o.max_val, {silent: true});
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!has_precision && !_.isUndefined(val)) {
|
||||
setTimeout(function() {
|
||||
m.set('precision', undefined);
|
||||
}, 10);
|
||||
}
|
||||
return !(m.get('is_precision'));
|
||||
}
|
||||
if (!has_precision && !_.isUndefined(val)) {
|
||||
setTimeout(function() {
|
||||
m.set('precision', undefined);
|
||||
}, 10);
|
||||
}
|
||||
return true;
|
||||
}, cellHeaderClasses: 'width_percent_10'
|
||||
},{
|
||||
id: 'typdefault', label: gettext('Default'), type: 'text',
|
||||
cell: 'string', min_version: 90300, group: gettext('Definition'),
|
||||
placeholder: "Enter an expression or a value.",
|
||||
cellHeaderClasses: 'width_percent_10',
|
||||
editable: function(m) {
|
||||
if(!(_.isUndefined(m.get('inheritedid'))
|
||||
|| _.isNull(m.get('inheritedid'))
|
||||
|| _.isUndefined(m.get('inheritedfrom'))
|
||||
|| _.isNull(m.get('inheritedfrom')))) { return false; }
|
||||
if (this.get('node_info').server.version < 90300){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
},{
|
||||
id: 'attnotnull', label: gettext('Not Null'),
|
||||
cell: 'boolean',type: 'switch', editable: 'is_editable_column',
|
||||
cellHeaderClasses: 'width_percent_10', group: gettext('Definition')
|
||||
},{
|
||||
id: 'attstattarget', label: gettext('Statistics'), min_version: 90200,
|
||||
cell: 'integer', type: 'int', group: gettext('Definition'),
|
||||
editable: function(m) {
|
||||
if (_.isUndefined(m.isNew) || m.isNew()) { return false; }
|
||||
if (this.get('node_info').server.version < 90200){
|
||||
return false;
|
||||
}
|
||||
return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
|
||||
|| _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
|
||||
}, cellHeaderClasses: 'width_percent_10'
|
||||
},{
|
||||
id: 'collname', label: gettext('Collation'), cell: 'node-ajax-options',
|
||||
control: 'node-ajax-options', type: 'text', url: 'get_collations',
|
||||
min_version: 90300, editable: function(m) {
|
||||
if (!(_.isUndefined(m.isNew)) && !m.isNew()) { return false; }
|
||||
return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
|
||||
|| _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
|
||||
},
|
||||
cellHeaderClasses: 'width_percent_20', group: gettext('Definition')
|
||||
},{
|
||||
id: 'attnum', cell: 'string',type: 'text', visible: false
|
||||
},{
|
||||
id: 'inheritedfrom', label: gettext('Inherited From'), cell: 'string',
|
||||
type: 'text', visible: false, mode: ['properties', 'edit'],
|
||||
cellHeaderClasses: 'width_percent_10'
|
||||
},{
|
||||
id: 'coloptions', label: gettext('Options'), cell: 'string',
|
||||
type: 'collection', group: gettext('Options'), mode: ['edit', 'create'],
|
||||
model: ColumnOptionsModel, canAdd: true, canDelete: true, canEdit: false,
|
||||
control: Backform.UniqueColCollectionControl, uniqueCol : ['option'],
|
||||
min_version: 90200
|
||||
}],
|
||||
validate: function() {
|
||||
var errmsg = null;
|
||||
|
||||
if (_.isUndefined(this.get('attname')) || String(this.get('attname')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
errmsg = gettext('Column Name cannot be empty!');
|
||||
this.errorModel.set('attname', errmsg);
|
||||
} else {
|
||||
this.errorModel.unset('attname');
|
||||
}
|
||||
|
||||
if (_.isUndefined(this.get('datatype')) || String(this.get('datatype'))
|
||||
.replace(/^\s+|\s+$/g, '') == '') {
|
||||
errmsg = gettext('Column Datatype cannot be empty!');
|
||||
this.errorModel.set('datatype', errmsg);
|
||||
} else {
|
||||
this.errorModel.unset('datatype');
|
||||
}
|
||||
|
||||
return errmsg;
|
||||
},
|
||||
is_editable_column: function(m) {
|
||||
return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
|
||||
|| _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
|
||||
},
|
||||
toJSON: Backbone.Model.prototype.toJSON
|
||||
});
|
||||
|
||||
|
||||
/* NodeAjaxOptionsMultipleControl is for multiple selection of Combobox.
|
||||
* This control is used to select Multiple Parent Tables to be inherited.
|
||||
* It also populates/vacates Columns on selection/deselection of the option (i.e. table name).
|
||||
* To populates the column, it calls the server and fetch the columns data
|
||||
* for the selected table.
|
||||
*/
|
||||
var NodeAjaxOptionsMultipleControl = Backform.NodeAjaxOptionsControl.extend({
|
||||
onChange: function(e) {
|
||||
var model = this.model,
|
||||
$el = $(e.target),
|
||||
attrArr = this.field.get("name").split('.'),
|
||||
name = attrArr.shift(),
|
||||
path = attrArr.join('.'),
|
||||
value = this.getValueFromDOM(),
|
||||
changes = {},
|
||||
columns = model.get('columns'),
|
||||
inherits = model.get(name);
|
||||
|
||||
if (this.model.errorModel instanceof Backbone.Model) {
|
||||
if (_.isEmpty(path)) {
|
||||
this.model.errorModel.unset(name);
|
||||
} else {
|
||||
var nestedError = this.model.errorModel.get(name);
|
||||
if (nestedError) {
|
||||
this.keyPathSetter(nestedError, path, null);
|
||||
this.model.errorModel.set(name, nestedError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
if (typeof(inherits) == "string"){ inherits = JSON.parse(inherits); }
|
||||
|
||||
// Remove Columns if inherit option is deselected from the combobox
|
||||
if(_.size(value) < _.size(inherits)) {
|
||||
var dif = _.difference(inherits, value);
|
||||
var rmv_columns = columns.where({inheritedid: parseInt(dif[0])});
|
||||
columns.remove(rmv_columns);
|
||||
}
|
||||
else
|
||||
{
|
||||
_.each(value, function(i) {
|
||||
// Fetch Columns from server
|
||||
var fnd_columns = columns.where({inheritedid: parseInt(i)});
|
||||
if (fnd_columns && fnd_columns.length <= 0) {
|
||||
inhted_columns = self.fetchColumns(i);
|
||||
columns.add(inhted_columns);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
changes[name] = _.isEmpty(path) ? value : _.clone(model.get(name)) || {};
|
||||
this.stopListening(this.model, "change:" + name, this.render);
|
||||
model.set(changes);
|
||||
this.listenTo(this.model, "change:" + name, this.render);
|
||||
},
|
||||
fetchColumns: function(table_id){
|
||||
var self = this,
|
||||
url = 'get_columns',
|
||||
m = self.model.top || self.model;
|
||||
|
||||
if (url) {
|
||||
var node = this.field.get('schema_node'),
|
||||
node_info = this.field.get('node_info'),
|
||||
full_url = node.generate_url.apply(
|
||||
node, [
|
||||
null, url, this.field.get('node_data'),
|
||||
this.field.get('url_with_id') || false, node_info
|
||||
]),
|
||||
cache_level = this.field.get('cache_level') || node.type,
|
||||
cache_node = this.field.get('cache_node');
|
||||
|
||||
cache_node = (cache_node && pgBrowser.Nodes['cache_node']) || node;
|
||||
|
||||
m.trigger('pgadmin:view:fetching', m, self.field);
|
||||
var data = {attrelid: table_id}
|
||||
|
||||
// Fetching Columns data for the selected table.
|
||||
$.ajax({
|
||||
async: false,
|
||||
url: full_url,
|
||||
data: data,
|
||||
success: function(res) {
|
||||
/*
|
||||
* We will cache this data for short period of time for avoiding
|
||||
* same calls.
|
||||
*/
|
||||
data = cache_node.cache(url, node_info, cache_level, res.data);
|
||||
|
||||
},
|
||||
error: function() {
|
||||
m.trigger('pgadmin:view:fetch:error', m, self.field);
|
||||
}
|
||||
});
|
||||
m.trigger('pgadmin:view:fetched', m, self.field);
|
||||
|
||||
// To fetch only options from cache, we do not need time from 'at'
|
||||
// attribute but only options.
|
||||
//
|
||||
// It is feasible that the data may not have been fetched.
|
||||
data = (data && data.data) || [];
|
||||
return data;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
// Constraints Model
|
||||
var ConstraintModel = pgBrowser.Node.Model.extend({
|
||||
idAttribute: 'conoid',
|
||||
initialize: function(attrs, args) {
|
||||
var isNew = (_.size(attrs) === 0);
|
||||
if (!isNew) {
|
||||
this.convalidated_default = this.get('convalidated')
|
||||
}
|
||||
pgBrowser.Node.Model.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
defaults: {
|
||||
conoid: undefined,
|
||||
conname: undefined,
|
||||
consrc: undefined,
|
||||
connoinherit: undefined,
|
||||
convalidated: true,
|
||||
conislocal: undefined
|
||||
},
|
||||
convalidated_default: true,
|
||||
schema: [{
|
||||
id: 'conoid', type: 'text', cell: 'string', visible: false
|
||||
},{
|
||||
id: 'conname', label: gettext('Name'), type: 'text', cell: 'string',
|
||||
editable: 'is_editable', cellHeaderClasses: 'width_percent_30'
|
||||
},{
|
||||
id: 'consrc', label: gettext('Check'), type: 'multiline',
|
||||
editable: 'is_editable', cell: Backgrid.Extension.TextareaCell,
|
||||
cellHeaderClasses: 'width_percent_30'
|
||||
},{
|
||||
id: 'connoinherit', label: gettext('No Inherit'), type: 'switch',
|
||||
cell: 'boolean', editable: 'is_editable',
|
||||
cellHeaderClasses: 'width_percent_20'
|
||||
},{
|
||||
id: 'convalidated', label: gettext('Validate?'), type: 'switch',
|
||||
cell: 'boolean', cellHeaderClasses: 'width_percent_20',
|
||||
editable: function(m) {
|
||||
var server = this.get('node_info').server;
|
||||
if (_.isUndefined(m.isNew)) { return true; }
|
||||
if (!m.isNew()) {
|
||||
if(m.get('convalidated') && m.convalidated_default) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
],
|
||||
validate: function() {
|
||||
var err = {},
|
||||
errmsg;
|
||||
|
||||
if (_.isUndefined(this.get('conname')) || String(this.get('conname')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
err['conname'] = gettext('Constraint Name cannot be empty!');
|
||||
errmsg = errmsg || err['conname'];
|
||||
}
|
||||
|
||||
if (_.isUndefined(this.get('consrc')) || String(this.get('consrc'))
|
||||
.replace(/^\s+|\s+$/g, '') == '') {
|
||||
err['consrc'] = gettext('Constraint Check cannot be empty!');
|
||||
errmsg = errmsg || err['consrc'];
|
||||
}
|
||||
|
||||
this.errorModel.clear().set(err);
|
||||
|
||||
return errmsg;
|
||||
},
|
||||
is_editable: function(m) {
|
||||
return _.isUndefined(m.isNew) ? true : m.isNew();
|
||||
},
|
||||
toJSON: Backbone.Model.prototype.toJSON
|
||||
});
|
||||
|
||||
|
||||
// Options Model
|
||||
var OptionsModel = pgBrowser.Node.Model.extend({
|
||||
defaults: {
|
||||
option: undefined,
|
||||
value: undefined
|
||||
},
|
||||
schema: [{
|
||||
id: 'option', label: gettext('Option'), cell: 'string', type: 'text',
|
||||
editable: true, cellHeaderClasses:'width_percent_50'
|
||||
},{
|
||||
id: 'value', label: gettext('Value'), cell: 'string',type: 'text',
|
||||
editable: true, cellHeaderClasses:'width_percent_50'
|
||||
}
|
||||
],
|
||||
validate: function() {
|
||||
// TODO: Add validation here
|
||||
},
|
||||
toJSON: Backbone.Model.prototype.toJSON
|
||||
});
|
||||
|
||||
|
||||
if (!pgBrowser.Nodes['foreign-table']) {
|
||||
pgBrowser.Nodes['foreign-table'] = pgBrowser.Node.extend({
|
||||
type: 'foreign-table',
|
||||
sqlAlterHelp: 'sql-alterforeigntable.html',
|
||||
sqlCreateHelp: 'sql-createforeigntable.html',
|
||||
dialogHelp: url_for('help.static', {'filename': 'foreign_table_dialog.html'}),
|
||||
label: gettext('Foreign Table'),
|
||||
collection_type: 'coll-foreign-table',
|
||||
hasSQL: true,
|
||||
hasDepends: true,
|
||||
hasScriptTypes: ['create', 'select', 'insert', 'update', 'delete'],
|
||||
parent_type: ['schema'],
|
||||
Init: function() {
|
||||
/* Avoid multiple registration of menus */
|
||||
if (this.initialized)
|
||||
return;
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_foreign-table_on_coll', node: 'coll-foreign-table', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('Foreign Table...'),
|
||||
icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: true},
|
||||
enable: 'canCreate'
|
||||
},{
|
||||
name: 'create_foreign-table', node: 'foreign-table', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('Foreign Table...'),
|
||||
icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: true},
|
||||
enable: 'canCreate'
|
||||
},{
|
||||
name: 'create_foreign-table', node: 'schema', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('Foreign Table...'),
|
||||
icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: false},
|
||||
enable: 'canCreate'
|
||||
}
|
||||
]);
|
||||
|
||||
},
|
||||
canDrop: pgBrowser.Nodes['schema'].canChildDrop,
|
||||
canDropCascade: pgBrowser.Nodes['schema'].canChildDrop,
|
||||
model: pgBrowser.Node.Model.extend({
|
||||
initialize: function(attrs, args) {
|
||||
var isNew = (_.size(attrs) === 0);
|
||||
if (isNew) {
|
||||
var schema = args.node_info.schema._label,
|
||||
userInfo = pgBrowser.serverInfo[args.node_info.server._id].user;
|
||||
|
||||
// Set Selected Schema and Current User
|
||||
this.set({
|
||||
'basensp': schema, 'owner': userInfo.name
|
||||
}, {silent: true});
|
||||
}
|
||||
pgBrowser.Node.Model.prototype.initialize.apply(this, arguments);
|
||||
},
|
||||
defaults: {
|
||||
name: undefined,
|
||||
oid: undefined,
|
||||
owner: undefined,
|
||||
basensp: undefined,
|
||||
description: undefined,
|
||||
ftsrvname: undefined,
|
||||
strftoptions: undefined,
|
||||
inherits: [],
|
||||
columns: [],
|
||||
constraints: [],
|
||||
ftoptions: [],
|
||||
relacl: [],
|
||||
stracl: [],
|
||||
seclabels: []
|
||||
},
|
||||
schema: [{
|
||||
id: 'name', label: gettext('Name'), cell: 'string',
|
||||
type: 'text', mode: ['properties', 'create', 'edit']
|
||||
},{
|
||||
id: 'oid', label: gettext('OID'), cell: 'string',
|
||||
type: 'text' , mode: ['properties']
|
||||
},{
|
||||
id: 'owner', label: gettext('Owner'), cell: 'string',
|
||||
control: Backform.NodeListByNameControl,
|
||||
node: 'role', type: 'text', select2: { allowClear: false }
|
||||
},{
|
||||
id: 'basensp', label: gettext('Schema'), cell: 'node-list-by-name',
|
||||
control: 'node-list-by-name', cache_level: 'database', type: 'text',
|
||||
node: 'schema', mode:['create', 'edit']
|
||||
},{
|
||||
id: 'description', label: gettext('Comment'), cell: 'string',
|
||||
type: 'multiline'
|
||||
},{
|
||||
id: 'ftsrvname', label: gettext('Foreign server'), cell: 'string', control: 'node-ajax-options',
|
||||
type: 'text', group: gettext('Definition'), url: 'get_foreign_servers', disabled: function(m) { return !m.isNew(); }
|
||||
},{
|
||||
id: 'inherits', label: gettext('Inherits'), group: gettext('Definition'),
|
||||
type: 'array', min_version: 90500, control: NodeAjaxOptionsMultipleControl,
|
||||
url: 'get_tables', select2: {multiple: true},
|
||||
'cache_level': 'database',
|
||||
transform: function(d, self){
|
||||
if (this.field.get('mode') == 'edit') {
|
||||
var oid = this.model.get('oid');
|
||||
var s = _.findWhere(d, {'id': oid});
|
||||
if (s) {
|
||||
d = _.reject(d, s);
|
||||
}
|
||||
}
|
||||
return d;
|
||||
}
|
||||
},{
|
||||
id: 'columns', label: gettext('Columns'), cell: 'string',
|
||||
type: 'collection', group: gettext('Columns'), visible: false, mode: ['edit', 'create'],
|
||||
model: ColumnsModel, canAdd: true, canDelete: true, canEdit: true,
|
||||
columns: ['attname', 'datatype', 'inheritedfrom'],
|
||||
canDeleteRow: function(m) {
|
||||
return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
|
||||
|| _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
|
||||
},
|
||||
canEditRow: function(m) {
|
||||
return (_.isUndefined(m.get('inheritedid')) || _.isNull(m.get('inheritedid'))
|
||||
|| _.isUndefined(m.get('inheritedfrom')) || _.isNull(m.get('inheritedfrom'))) ? true : false
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'constraints', label: gettext('Constraints'), cell: 'string',
|
||||
type: 'collection', group: gettext('Constraints'), visible: false, mode: ['edit', 'create'],
|
||||
model: ConstraintModel, canAdd: true, canDelete: true, columns: ['conname','consrc', 'connoinherit', 'convalidated'],
|
||||
canEdit: function(o) {
|
||||
if (o instanceof Backbone.Model) {
|
||||
if (o instanceof ConstraintModel) {
|
||||
return o.isNew();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}, min_version: 90500, canDeleteRow: function(m) {
|
||||
return (m.get('conislocal') == true || _.isUndefined(m.get('conislocal'))) ? true : false
|
||||
}
|
||||
},{
|
||||
id: 'strftoptions', label: gettext('Options'), cell: 'string',
|
||||
type: 'text', group: gettext('Definition'), mode: ['properties']
|
||||
},{
|
||||
id: 'ftoptions', label: gettext('Options'), cell: 'string',
|
||||
type: 'collection', group: gettext('Options'), mode: ['edit', 'create'],
|
||||
model: OptionsModel, canAdd: true, canDelete: true, canEdit: false,
|
||||
control: 'unique-col-collection', uniqueCol : ['option']
|
||||
},{
|
||||
id: 'relacl', label: gettext('Privileges'), cell: 'string',
|
||||
type: 'text', group: gettext('Security'),
|
||||
mode: ['properties'], min_version: 90200
|
||||
}, pgBrowser.SecurityGroupUnderSchema, {
|
||||
id: 'acl', label: gettext('Privileges'), model: pgAdmin
|
||||
.Browser.Node.PrivilegeRoleModel.extend(
|
||||
{privileges: ['a','r','w','x']}), uniqueCol : ['grantee', 'grantor'],
|
||||
editable: false, type: 'collection', group: 'security',
|
||||
mode: ['edit', 'create'],
|
||||
canAdd: true, canDelete: true, control: 'unique-col-collection',
|
||||
min_version: 90200
|
||||
},{
|
||||
id: 'seclabels', label: gettext('Security Labels'),
|
||||
model: pgBrowser.SecLabelModel, type: 'collection',
|
||||
group: 'security', mode: ['edit', 'create'],
|
||||
min_version: 90100, canAdd: true,
|
||||
canEdit: false, canDelete: true,
|
||||
control: 'unique-col-collection', uniqueCol : ['provider']
|
||||
}
|
||||
],
|
||||
validate: function()
|
||||
{
|
||||
var err = {},
|
||||
errmsg,
|
||||
seclabels = this.get('seclabels');
|
||||
|
||||
if (_.isUndefined(this.get('name')) || String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
err['name'] = gettext('Name cannot be empty.');
|
||||
errmsg = errmsg || err['name'];
|
||||
}
|
||||
|
||||
if (_.isUndefined(this.get('basensp')) || String(this.get('basensp'))
|
||||
.replace(/^\s+|\s+$/g, '') == '') {
|
||||
err['basensp'] = gettext('Schema cannot be empty.');
|
||||
errmsg = errmsg || err['basensp'];
|
||||
}
|
||||
|
||||
if (_.isUndefined(this.get('ftsrvname')) || String(this.get('ftsrvname')).replace(/^\s+|\s+$/g, '') == '') {
|
||||
err['ftsrvname'] = gettext('Foreign server cannot be empty.');
|
||||
errmsg = errmsg || err['ftsrvname'];
|
||||
}
|
||||
|
||||
this.errorModel.clear().set(err);
|
||||
|
||||
return null;
|
||||
}
|
||||
}),
|
||||
canCreate: function(itemData, item, data) {
|
||||
//If check is false then , we will allow create menu
|
||||
if (data && data.check == false)
|
||||
return true;
|
||||
|
||||
var t = pgBrowser.tree, i = item, d = itemData;
|
||||
// To iterate over tree to check parent node
|
||||
while (i) {
|
||||
// If it is schema then allow user to create foreign table
|
||||
if (_.indexOf(['schema'], d._type) > -1)
|
||||
return true;
|
||||
|
||||
if ('coll-foreign-table' == d._type) {
|
||||
//Check if we are not child of catalog
|
||||
var prev_i = t.hasParent(i) ? t.parent(i) : null,
|
||||
prev_d = prev_i ? t.itemData(prev_i) : null;
|
||||
if( prev_d._type == 'catalog') {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
i = t.hasParent(i) ? t.parent(i) : null;
|
||||
d = i ? t.itemData(i) : null;
|
||||
}
|
||||
// by default we do not want to allow create menu
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return pgBrowser.Nodes['foreign-table'];
|
||||
});
|
||||
@@ -511,19 +511,19 @@ define('pgadmin.node.foreign_table', [
|
||||
this.initialized = true;
|
||||
|
||||
pgBrowser.add_menus([{
|
||||
name: 'create_foreign-table_on_coll', node: 'coll-foreign_table', module: this,
|
||||
name: 'create_foreign_table_on_coll', node: 'coll-foreign_table', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('Foreign Table...'),
|
||||
icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: true},
|
||||
icon: 'wcTabIcon icon-foreign_table', data: {action: 'create', check: true},
|
||||
enable: 'canCreate'
|
||||
},{
|
||||
name: 'create_foreign-table', node: 'foreign_table', module: this,
|
||||
name: 'create_foreign_table', node: 'foreign_table', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('Foreign Table...'),
|
||||
icon: 'wcTabIcon icon-foreign-table', data: {action: 'create', check: true},
|
||||
icon: 'wcTabIcon icon-foreign_table', data: {action: 'create', check: true},
|
||||
enable: 'canCreate'
|
||||
},{
|
||||
name: 'create_foreign-table', node: 'schema', module: this,
|
||||
name: 'create_foreign_table', node: 'schema', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('Foreign Table...'),
|
||||
icon: 'wcTabIcon icon-foreign_table', data: {action: 'create', check: false},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-check_bad, .icon-check_constraints_bad {
|
||||
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/check-constraints-bad.svg' )}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -9,6 +10,7 @@
|
||||
.icon-check, .icon-check_constraints {
|
||||
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/check-constraints.svg' )}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-foreign_key {
|
||||
background-image: url('{{ url_for('NODE-foreign_key.static', filename='img/foreign_key.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -8,5 +9,10 @@
|
||||
|
||||
.icon-foreign_key_no_validate {
|
||||
background-image: url('{{ url_for('NODE-foreign_key.static', filename='img/foreign_key_no_validate.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
border-radius: 10px
|
||||
height: 1.3em;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-rule{
|
||||
background-image: url('{{ url_for('NODE-rule.static', filename='img/rule.svg') }}') !important;
|
||||
border-radius: 10px;
|
||||
background-size: 20px !important;
|
||||
background-repeat: no-repeat;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-coll-trigger {
|
||||
background-image: url('{{ url_for('NODE-trigger.static', filename='img/coll-trigger.svg' )}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -9,6 +10,7 @@
|
||||
.icon-trigger {
|
||||
background-image: url('{{ url_for('NODE-trigger.static', filename='img/trigger.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -16,5 +18,6 @@
|
||||
|
||||
.icon-trigger-bad {
|
||||
background-image: url('{{ url_for('NODE-trigger.static', filename='img/trigger-bad.svg') }}') !important;
|
||||
background-size: 20px !important;
|
||||
border-radius: 10px
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-mview{
|
||||
background-image: url('{{ url_for('NODE-mview.static', filename='img/mview.svg') }}') !important;
|
||||
border-radius: 10px;
|
||||
background-size: 20px !important;
|
||||
background-repeat: no-repeat;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.icon-view{
|
||||
background-image: url('{{ url_for('NODE-view.static', filename='img/view.svg') }}') !important;
|
||||
border-radius: 10px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.pg-icon-database {
|
||||
background-image: url('{{ url_for('NODE-database.static', filename='img/database.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -9,4 +10,5 @@
|
||||
.icon-database-not-connected {
|
||||
background-image: url('{{ url_for('NODE-database.static', filename='img/databasebad.svg') }}') !important;
|
||||
border-radius: 10px
|
||||
background-size: 20px !important;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-pga_schedule {
|
||||
background-image: url('{{ url_for('NODE-pga_schedule.static', filename='img/pga_schedule.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
|
||||
@@ -71,6 +71,23 @@ class JobStepModule(CollectionNodeModule):
|
||||
"""
|
||||
return 'pga_job'
|
||||
|
||||
@property
|
||||
def csssnippets(self):
|
||||
"""
|
||||
Returns a snippet of css to include in the page
|
||||
"""
|
||||
snippets = [
|
||||
render_template(
|
||||
"pga_jobstep/css/pga_step.css",
|
||||
node_type=self.node_type
|
||||
)
|
||||
]
|
||||
|
||||
for submodule in self.submodules:
|
||||
snippets.extend(submodule.csssnippets)
|
||||
|
||||
return snippets
|
||||
|
||||
@property
|
||||
def module_use_template_javascript(self):
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-pga_jobstep {
|
||||
background-image: url('{{ url_for('NODE-pga_jobstep.static', filename='img/pga_jobstep.png') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -9,6 +10,7 @@
|
||||
.icon-coll-pga_jobstep {
|
||||
background-image: url('{{ url_for('NODE-pga_jobstep.static', filename='img/coll-pga_jobstep.png') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-pga_job {
|
||||
background-image: url('{{ url_for('NODE-pga_job.static', filename='img/pga_job.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -9,6 +10,7 @@
|
||||
.icon-pga_job-disabled {
|
||||
background-image: url('{{ url_for('NODE-pga_job.static', filename='img/pga_job-disabled.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-role {
|
||||
background-image: url('{{ url_for('NODE-role.static', filename='img/role.svg')}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -9,6 +10,7 @@
|
||||
.icon-group {
|
||||
background-image: url('{{ url_for('NODE-role.static', filename='img/group.svg')}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -17,11 +19,13 @@
|
||||
.pgadmin-node-select option[node="role"] {
|
||||
background-image: url('{{ url_for('NODE-role.static', filename='img/role.svg')}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
background-position: center left;
|
||||
}
|
||||
|
||||
.pgadmin-node-select option[node="group"] {
|
||||
background-image: url('{{ url_for('NODE-role.static', filename='img/group.svg')}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
background-position: center left;
|
||||
}
|
||||
|
||||
@@ -47,13 +47,13 @@ define('pgadmin.node.tablespace', [
|
||||
name: 'create_tablespace_on_coll', node: 'coll-tablespace', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('Tablespace...'),
|
||||
icon: 'wcTabIcon pg-icon-tablespace', data: {action: 'create'},
|
||||
icon: 'wcTabIcon icon-tablespace', data: {action: 'create'},
|
||||
enable: 'can_create_tablespace'
|
||||
},{
|
||||
name: 'create_tablespace', node: 'tablespace', module: this,
|
||||
applies: ['object', 'context'], callback: 'show_obj_properties',
|
||||
category: 'create', priority: 4, label: gettext('Tablespace...'),
|
||||
icon: 'wcTabIcon pg-icon-tablespace', data: {action: 'create'},
|
||||
icon: 'wcTabIcon icon-tablespace', data: {action: 'create'},
|
||||
enable: 'can_create_tablespace'
|
||||
},{
|
||||
name: 'move_tablespace', node: 'tablespace', module: this,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-{{ server_type }} {
|
||||
background-image: url('{{ url_for('NODE-server.static', filename='img/%s' % (icon)) }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-server {
|
||||
background-image: url('{{ url_for('NODE-server.static', filename='img/server.png') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -9,6 +10,7 @@
|
||||
.icon-server-not-connected {
|
||||
background-image: url('{{ url_for('NODE-server.static', filename='img/serverbad.svg') }}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
@@ -17,6 +19,7 @@
|
||||
.icon-server-connecting {
|
||||
background-image: url('{{ url_for('static', filename='js/generated/img/load-node.gif')}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-coll-{{node_type}} {
|
||||
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/coll-%s.svg' % node_type )}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 1.3em;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
.icon-{{node_type}} {
|
||||
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/%s.svg' % node_type )}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
align-content: center;
|
||||
vertical-align: middle;
|
||||
height: 20px;
|
||||
@@ -9,5 +10,6 @@
|
||||
.pgadmin-node-select option[node="{{node_type}}"] {
|
||||
background-image: url('{{ url_for('NODE-%s.static' % node_type, filename='img/%s.svg' % node_type )}}') !important;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px !important;
|
||||
background-position: center left;
|
||||
}
|
||||
|
||||
@@ -176,6 +176,8 @@ module.exports = {
|
||||
',pgadmin.node.extension' +
|
||||
',pgadmin.node.language' +
|
||||
',pgadmin.node.foreign_data_wrapper' +
|
||||
',pgadmin.node.foreign_server' +
|
||||
',pgadmin.node.user_mapping' +
|
||||
',pgadmin.node.schema' +
|
||||
',pgadmin.node.catalog' +
|
||||
',pgadmin.node.catalog_object' +
|
||||
|
||||
Reference in New Issue
Block a user