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:
Ashesh Vashi
2017-09-11 12:55:39 +05:30
parent 4018562bc7
commit 8c8c0e78ca
21 changed files with 61 additions and 727 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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):
"""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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