pgadmin4/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js

1180 lines
43 KiB
JavaScript
Raw Normal View History

2019-01-02 04:24:12 -06:00
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2019, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
// Grant Wizard
2016-04-13 10:11:43 -05:00
define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'backbone',
'pgadmin.alertifyjs', 'pgadmin.backgrid', 'pgadmin.backform',
'pgadmin.browser', 'pgadmin.browser.node',
'tools/grant_wizard/static/js/menu_utils',
'sources/nodes/supported_database_node',
'backgrid.select.all',
'backgrid.filter', 'pgadmin.browser.server.privilege',
'pgadmin.browser.wizard',
], function(
gettext, url_for, $, _, Backbone, Alertify, Backgrid, Backform, pgBrowser,
pgNode, menuUtils, supportedNodes
) {
// if module is already initialized, refer to that.
if (pgBrowser.GrantWizard) {
return pgBrowser.GrantWizard;
}
/**
It is sub model for field "Objects". It has fields
for database object types such as Schemas, Views and
Sequence etc.
*/
var DatabaseObjectModel = pgNode.Model.extend({
defaults: {
selected: false,
icon: 'icon-unknown',
name: undefined,
name_with_args: undefined,
nspname: undefined,
proargs: undefined,
object_type: undefined,
object_id: undefined,
},
idAttribute: 'object_id', // to uniquely identify a model object
toJSON: function() {
var d = pgNode.Model.prototype.toJSON.apply(this);
delete d.icon;
return d;
},
parse: function(res) {
// Create unique object id
res.object_id = res.name_with_args;
// create name with args if its object is function
if (!_.isUndefined(res.object_type) &&
(res.object_type == 'Function' ||
res.object_type == 'Trigger Function' ||
res.object_type == 'Procedure'
))
res.name_with_args = res.name + '(' + (typeof(res.proargs) != 'undefined' ? res.proargs : '') + ')';
else
res.name_with_args = res.name;
2016-04-13 10:11:43 -05:00
return res;
},
2016-04-13 10:11:43 -05:00
validate: function() {
2016-04-13 10:11:43 -05:00
/*
* Triggers error messages for object types "selected"
* if it is empty/undefined/null
*/
var err = {},
errmsg,
node = this.get('objects').toJSON();
if (_.isEmpty(node)) {
err['selected'] = gettext('Please select any database object.');
errmsg = errmsg || err['selected'];
this.errorModel.set('selected', errmsg);
return errmsg;
} else {
this.errorModel.unset('selected');
}
return null;
},
});
2016-04-13 10:11:43 -05:00
// Define columns for the Db Object Types grid
var columns = [{
name: 'selected',
2016-04-13 10:11:43 -05:00
/*
Override render method of Backgrid.Extension.SelectRowCell
class. It has an issue: It doesn't mark rows checked if we move to next
page and then go back to previous page. but it must show.
so we handle this case by overriding the render method.
*/
cell: Backgrid.Extension.SelectRowCell.extend({
render: function() {
2016-04-13 10:11:43 -05:00
// Do not use parent's render function. It set's tabindex to -1 on
// checkboxes.
this.$el.empty().append('<input type="checkbox" />');
this.delegateEvents();
2016-04-13 10:11:43 -05:00
var col = this.column.get('name');
if (this.model && this.model.has(col)) {
if (this.model.get(col)) {
this.$el.parent().toggleClass('selected', true);
this.model.trigger('backgrid:selected', this.model, true);
}
2016-04-13 10:11:43 -05:00
}
return this;
},
}),
2016-04-13 10:11:43 -05:00
headerCell: 'select-all',
2016-04-13 10:11:43 -05:00
}, {
name: 'object_type',
label: gettext('Object Type'),
editable: false,
cell: Backgrid.Cell.extend({
render: function() {
2016-04-13 10:11:43 -05:00
// Override render to add icon to Db Object column
Backgrid.Cell.prototype.render.apply(this, arguments);
this.$el.addClass(this.model.get('icon')).css({
'padding-left': '24px',
});
2016-04-13 10:11:43 -05:00
return this;
},
}),
}, {
name: 'nspname',
label: gettext('Schema'),
cell: 'string',
editable: false,
}, {
name: 'name_with_args',
label: gettext('Name'),
cell: 'string',
editable: false,
}];
// Create an Object GrantWizard of pgBrowser class
pgBrowser.GrantWizard = {
init: function() {
if (this.initialized)
return;
this.initialized = true;
// Define the nodes on which the menus to be appear
var menus = [{
name: 'grant_wizard_schema',
module: this,
applies: ['tools'],
callback: 'start_grant_wizard',
priority: 14,
label: gettext('Grant Wizard...'),
icon: 'fa fa-unlock-alt',
enable: supportedNodes.enabled.bind(
null, pgBrowser.treeMenu, menuUtils.supportedNodes
),
}];
// Add supported menus into the menus list
for (var idx = 0; idx < menuUtils.supportedNodes.length; idx++) {
menus.push({
name: 'grant_wizard_schema_context_' + menuUtils.supportedNodes[idx],
node: menuUtils.supportedNodes[idx],
module: this,
applies: ['context'],
callback: 'start_grant_wizard',
priority: 14,
label: gettext('Grant Wizard...'),
icon: 'fa fa-unlock-alt',
enable: supportedNodes.enabled.bind(
null, pgBrowser.treeMenu, menuUtils.supportedNodes
),
});
}
pgBrowser.add_menus(menus);
return this;
},
// Callback to draw Wizard Dialog
start_grant_wizard: function() {
// Declare Wizard dialog
if (!Alertify.wizardDialog) {
Alertify.dialog('wizardDialog', function factory() {
// Generate wizard main container
var $container = $('<div class=\'wizard_dlg\'></div>');
return {
main: function(title) {
this.set('title', title);
},
setup: function() {
return {
// Set options for dialog
options: {
frameless: true,
resizable: true,
autoReset: false,
maximizable: false,
closable: false,
closableByDimmer: false,
modal: false,
pinnable: false,
},
};
},
/**
Returns a Paginator Class Object which is again to be rendered
2016-04-13 10:11:43 -05:00
@class {Backgrid.Extension.Paginator}
@param {Backbone.Collection} coll - from which data is fetched
@return {Object} paginator
*/
DbPaginator: function(coll) {
var paginator = this.paginator = new Backgrid.Extension.Paginator({
collection: coll,
windowSize: 8,
});
return paginator;
},
2016-04-13 10:11:43 -05:00
/**
Create new Filter which will filter the
rendered grid for Select Type Tabular Data
@param {Backbone.PageableCollection} coll
*/
DbObjectFilter: function(coll) {
var clientSideFilter = this.clientSideFilter = new Backgrid.Extension.ClientSideFilter({
collection: coll,
placeholder: _('Search by object type or name'),
// The model fields to search for matches
fields: ['object_type', 'name'],
// How long to wait after typing has stopped before searching can start
wait: 150,
});
return clientSideFilter;
},
//Enable Disable Next button of PrivilegePage
updateButtons: function(modified) {
if (!modified)
$('.wizard-next').prop('disabled', true);
else
$('.wizard-next').prop('disabled', false);
},
2016-04-13 10:11:43 -05:00
/**
Callback called when an errorModel is set
with invalid value and errormsg is set into
status bar element and next button is disabled
*/
onSessionInvalid: function(msg) {
$('.pg-prop-status-bar .alert-text').html(msg);
$('.pg-prop-status-bar').css('visibility', 'visible');
2016-04-13 10:11:43 -05:00
// Enable disable Next button
this.updateButtons(false);
return true;
},
2016-04-13 10:11:43 -05:00
/**
Callback called when anything is set into model
thus hide error msg element and enable next button
status bar element and next button is disabled
*/
onSessionValidated: function(sessHasChanged) {
$('.pg-prop-status-bar .alert-text').empty();
$('.pg-prop-status-bar').css('visibility', 'hidden');
2016-04-13 10:11:43 -05:00
// Enable disable Next button
this.updateButtons(sessHasChanged);
},
2016-04-13 10:11:43 -05:00
/**
Remove/Delete objects, attributes
in wizard on wizard close or finish
to reclaim memory
*/
releaseObjects: function() {
var self = this;
2016-04-13 10:11:43 -05:00
if (!_.isUndefined(self.dbObjectFilter)) {
self.dbObjectFilter.remove();
self.dbObjectFilter = undefined;
}
2016-04-13 10:11:43 -05:00
if (!_.isUndefined(self.clientSideFilter)) {
self.clientSideFilter.remove();
self.clientSideFilter = undefined;
}
2016-04-13 10:11:43 -05:00
// clear object priv array
if (!_.isNull(self.obj_priv) &&
!_.isUndefined(self.obj_priv)) {
self.obj_priv = [];
delete self.obj_priv;
}
2016-04-13 10:11:43 -05:00
// Delete Wizard Pages, clear model and cleanup view
if (!_.isUndefined(self.dbObjectTypePage) &&
!_.isNull(self.dbObjectTypePage)) {
if (!_.isUndefined(self.dbObjectTypePage.get('model')) &&
!_.isNull(self.dbObjectTypePage.get('model'))) {
self.dbObjectTypePage.get('model').clear();
self.dbObjectTypePage.get('view').cleanup();
self.dbObjectTypePage = undefined;
2016-04-13 10:11:43 -05:00
}
}
2016-04-13 10:11:43 -05:00
if (!_.isUndefined(self.privilegePage) &&
!_.isNull(self.privilegePage)) {
if (!_.isUndefined(self.privilegePage.get('model')) &&
!_.isNull(self.privilegePage.get('model'))) {
self.privilegePage.get('model').clear();
self.privilegePage.get('view').cleanup();
self.privilegePage = undefined;
2016-04-13 10:11:43 -05:00
}
}
2016-04-13 10:11:43 -05:00
if (!_.isUndefined(self.reviewSQLPage) &&
!_.isNull(self.reviewSQLPage)) {
if (!_.isUndefined(self.reviewSQLPage.get('model')) &&
!_.isNull(self.reviewSQLPage.get('model'))) {
self.reviewSQLPage.get('model').clear();
self.reviewSQLPage = undefined;
2016-04-13 10:11:43 -05:00
}
}
2016-04-13 10:11:43 -05:00
// Remove Sql control
if (!_.isUndefined(self.sqlControl)) {
self.sqlControl.remove();
}
2016-04-13 10:11:43 -05:00
// Clear privModel
if (!_.isNull(self.privModel) &&
!_.isUndefined(self.privModel)) {
self.privModel.clear();
}
2016-04-13 10:11:43 -05:00
// Remove collection containing db object data
if (!_.isNull(self.coll) &&
!_.isUndefined(self.coll)) {
self.coll.reset();
self.coll = undefined;
}
// Delete Wizard
if (!_.isNull(self.wizard) &&
!_.isUndefined(self.wizard)) {
self.wizard.collection.reset();
self.wizard.curr_page = undefined;
}
2016-04-13 10:11:43 -05:00
},
2016-04-13 10:11:43 -05:00
/**
Every time a wizard is opened, this function
is called everytime. It has Wizard Pages which
are rendered by the Wizard Class:
@class {pgBrowser.WizardPage} dbObjectType1 - This page
@extends {Backbone.Model}
renders a grid of Database Object Types such as
Schemas, Views and Sequences etc.
@class {pgBrowser.WizardPage} WizardPage2 - This page
@extends {Backbone.Model}
adds Privilege Control which provides grant privileges
such as "Create, Insert, Delete, Update" so on the
database objects selected on Wizard Pages.
@class {pgBrowser.WizardPage} WizardPage3 - This page
displays the generated GRANT SQL query for the Db
objects selected with the specific privileges added to it.
@extends {Backbone.Model}
@class {Backbone.Collection} WizardCollection - It is the
collection of wizard pages
@class {pgBrowser.Wizard} wizard - Its task is:
- Create a Wizard
- Add Buttons, Callbacks to it.
- Render WizardPages
@extends {Backbone.View}
2016-04-13 10:11:43 -05:00
*/
build: function() {
this.elements.content.appendChild($container.get(0));
Alertify.pgDialogBuild.apply(this);
},
//Returns list of Acls defined for nodes
get_json_data: function(gid, sid, did) {
return $.ajax({
async: false,
url: url_for(
'grant_wizard.acl', {
'sid': encodeURI(sid),
'did': encodeURI(did),
}
),
dataType: 'json',
});
2016-04-13 10:11:43 -05:00
},
prepare: function() {
var that = this;
$container.empty().append('<div class=\'grant_wizard_container\'></div>');
2016-04-13 10:11:43 -05:00
// Define el for wizard view
var el = $('.grant_wizard_container');
2016-04-13 10:11:43 -05:00
// Extract the data from the selected tree node
var t = pgBrowser.tree,
i = t.selected(),
d = this.d = i && i.length == 1 ? t.itemData(i) : undefined,
info = this.info = pgBrowser.Node.getTreeNodeHierarchy(i);
/**
Generate a URL using:
gid, did, sid(server id), node_id(node id),
node_(node name), node_type(node type)
and pass it to collection which will fetch Object Type properties.
2016-04-13 10:11:43 -05:00
*/
var gid = info['server_group']._id,
sid = info.server._id,
did = info.database._id,
node_id = d._id,
2016-04-13 10:11:43 -05:00
/**
get node name only. used in mapping with object types defined
in allowed_acl.json
*/
node_type = d._type.replace('coll-', '').replace(
'materialized_', ''
);
// Fetch privileges specific to nodes
var json_data = this.get_json_data(gid, sid, did);
var privDict = JSON.parse(json_data.responseText);
// Collection url to fetch database object types for objects field
var baseUrl = url_for(
'grant_wizard.objects', {
'sid': encodeURI(sid),
'did': encodeURI(did),
'node_id': encodeURI(node_id),
'node_type': encodeURI(node_type),
}),
// Model's save url
saveUrl = url_for(
'grant_wizard.apply', {
'sid': encodeURI(sid),
'did': encodeURI(did),
}),
Coll = Backbone.Collection.extend({
model: DatabaseObjectModel,
url: baseUrl,
}),
// Create instances of collection and filter
coll = this.coll = new Coll(),
self = this;
// Generate encoded url based on wizard type
this.msql_url = url_for(
'grant_wizard.modified_sql', {
'sid': encodeURI(sid),
'did': encodeURI(did),
2016-04-13 10:11:43 -05:00
});
coll.comparator = function(model) {
return model.get('object_type');
};
2016-04-13 10:11:43 -05:00
coll.sort();
this.dbObjectFilter = this.DbObjectFilter(coll);
2016-04-13 10:11:43 -05:00
/**
privArray holds objects selected which further helps
in creating privileges Model
*/
self.privArray = [];
2016-04-13 10:11:43 -05:00
/**
Override backgrid listener "backgrid:selected" to
Add/Remove model to/from objects collection
*/
coll.on('backgrid:selected', function(model, selected) {
model.set('selected', selected);
var object_type = model.get('object_type');
switch (object_type) {
case 'Function':
object_type = 'function';
break;
case 'Trigger Function':
object_type = 'function';
break;
case 'Procedure':
object_type = 'procedure';
break;
case 'Table':
object_type = 'table';
break;
case 'Sequence':
object_type = 'sequence';
break;
case 'View':
object_type = 'table';
break;
case 'Materialized View':
object_type = 'table';
break;
default:
break;
}
2016-04-13 10:11:43 -05:00
/**
if a row (checkbox) is checked, add that model
into collection, when unchecked remove it from
model.
2016-04-13 10:11:43 -05:00
Also push/pop object type in/from privArray
2016-04-13 10:11:43 -05:00
*/
if (selected) {
if (_.indexOf(self.privArray, object_type) == -1)
self.privArray.push(object_type);
newModel.get('objects').add(model, {
silent: true,
});
} else {
var idx = self.privArray.indexOf(object_type);
if (idx != -1)
self.privArray.splice(idx, 1);
newModel.get('objects').remove(model);
}
2016-04-13 10:11:43 -05:00
// validate model on checkbox check/uncheck
var msg = model.validate.call(newModel);
2016-04-13 10:11:43 -05:00
/**
If no object type is selected, set error msg
and disable next button, else enable next button
2016-04-13 10:11:43 -05:00
*/
if (msg)
self.onSessionInvalid.call(self, msg);
else
self.onSessionValidated.call(self, true);
});
2016-04-13 10:11:43 -05:00
/**
It is the main model with schema defined
Every time a new wizard is opened,
a new model should create.
*/
var GrantWizardModel = pgNode.Model.extend({
defaults: {
objects: undefined,
acl: undefined,
},
schema: [{
id: 'objects',
label: gettext('Objects'),
model: DatabaseObjectModel,
type: 'collection',
group: gettext('Objects'),
},
{
id: 'acl',
label: gettext('Privileges'),
model: pgBrowser.Node.PrivilegeRoleModel,
type: 'collection',
canAdd: true,
canDelete: true,
control: 'unique-col-collection',
},
],
urlRoot: saveUrl,
});
2016-04-13 10:11:43 -05:00
/**
Create instance of GrantWizard Model, provide urlRoot
node_info object, Generate fields objects
*/
var newModel = new GrantWizardModel({}, {
node_info: info,
});
2016-04-13 10:11:43 -05:00
/**
Fetch data from server and set into grid
and show/hide progress bar
*/
$('.wizard-progress-bar p').show();
coll.fetch({
success: function(c, xhr) {
$('.wizard-progress-bar p').html('');
$('.wizard-progress-bar').hide();
c.set(xhr.result, {parse: true});
// If some objects failed while fetching then we will notify the user
if (xhr && xhr.info && xhr.info !== '') {
$('.pg-prop-status-bar .alert-text').html(xhr.info);
$('.pg-prop-status-bar').css('visibility', 'visible');
}
},
error: function(m, xhr) {
// If the main request fails as whole then
let msg;
if (xhr && xhr.responseJSON && xhr.responseJSON.errormsg) {
msg = xhr.responseJSON.errormsg;
}
if(!msg) {
msg = gettext('Unable to fetch the database objects due to an error');
}
$('.wizard-progress-bar p').removeClass('alert-info').addClass('alert-danger');
$('.wizard-progress-bar p').text(msg);
},
reset: true,
}, this);
//////////////////////////////////////////////////////////////////////
// //
// Wizard Page for Db Object Type //
// //
//////////////////////////////////////////////////////////////////////
2016-04-13 10:11:43 -05:00
/**
Create wizard page. It renders a grid of
Database Object Types such as
Schemas, Views and Sequences etc.
Set default values
*/
var dbObjectTypePage = self.dbObjectTypePage = new pgBrowser.WizardPage({
id: 1,
page_title: _('Object Selection (step 1 of 3)'),
disable_prev: true,
disable_next: true,
show_description: '',
show_progress_bar: _('Please wait while fetching records...'),
model: newModel,
view: new(function() {
// Set page Instance
var pageView = this;
_.extend(pageView, {
// Remove grid if it is before render
cleanup: function() {
if (this.grid) {
this.grid.remove();
delete this.grid;
this.grid = null;
}
2016-04-13 10:11:43 -05:00
// Remove grid element if exists
if (this.el) {
$(this.el).remove();
delete this.el;
}
},
2016-04-13 10:11:43 -05:00
// Delete grid before render
grid: null,
2016-04-13 10:11:43 -05:00
render: function() {
2016-04-13 10:11:43 -05:00
// Create a grid container
var gridBody =
$(`
<div class="db_objects_container pg-el-xs-12">
<div class="db_objects_header d-flex py-1">
<div>${_('Please select the objects to grant privileges to from the list below.')}</div>
<div class="db_objects_filter ml-auto">
<div class="input-group">
<div class="input-group-prepend">
<span class="input-group-text fa fa-search" id="labelSearch"></span>
</div>
<input type="search" class="form-control" id="txtGridSearch" placeholder="Search" aria-label="Search" aria-describedby="labelSearch">
</div>
</div>
</div>
<div class="db_objects_grid"></div>
</div>`);
2016-04-13 10:11:43 -05:00
// Remove grid if exits before render
if (this.grid) {
this.cleanup();
}
2016-04-13 10:11:43 -05:00
// Initialize a new Grid instance
this.grid = new Backgrid.Grid({
columns: _.clone(columns),
collection: coll,
className: 'backgrid presentation table table-bordered table-hover object_type_table',
});
2016-04-13 10:11:43 -05:00
// Render selection Type grid and paginator
gridBody.find('.db_objects_grid').append(this.grid.render().$el);
2016-04-13 10:11:43 -05:00
// Render Search Filter
self.clientSideFilter.setCustomSearchBox(gridBody.find('#txtGridSearch'));
2016-04-13 10:11:43 -05:00
// Assign gridBody content to page element
this.el = gridBody;
2016-04-13 10:11:43 -05:00
/**
Fetch selected models from collection and
make rows checked in grid
*/
newModel.get('objects').each(function(m) {
var model = coll.get(m.get('object_id'));
if (model) {
coll.trigger('backgrid:selected', model, true);
}
});
2016-04-13 10:11:43 -05:00
// Refresh grid to re render rows.
coll.trigger('backgrid:refresh');
2016-04-13 10:11:43 -05:00
return this;
},
});
}),
2016-04-13 10:11:43 -05:00
beforeNext: function(obj) {
var self = this;
obj.options.disable_next = true;
2016-04-13 10:11:43 -05:00
/**
Enable/Disable next button of privilegePage if objects
are present in model
*/
if (!_.isNull(newModel.get('acl')) &&
!_.isUndefined(newModel.get('acl'))) {
if (newModel.get('acl').length > 0)
obj.collection.at(1).set('disable_next', false);
}
2016-04-13 10:11:43 -05:00
// Clean the view
if (self.view) {
self.view.cleanup();
delete self.view;
self.view = null;
}
return true;
},
});
//////////////////////////////////////////////////////////////////////
// //
// Wizard Page for Privilege Control //
// //
//////////////////////////////////////////////////////////////////////
// Wizard for Privelege control
var privilegePage = self.privilegePage = new pgBrowser.WizardPage({
id: 2,
page_title: _('Privilege Selection (step 2 of 3)'),
show_description: _('Please add the required privileges for the selected objects.'),
disable_next: true,
model: newModel,
// Create a view function object
view: new(function() {
var pageView = this;
_.extend(pageView, {
// Render Privelege control to generate its html markup
render: function() {
var obj_priv = [];
self.privArray = _.uniq(self.privArray);
_.each(self.privArray, function(priv) {
self.obj_priv = obj_priv = _.union(obj_priv, privDict[priv].acl);
});
2016-04-13 10:11:43 -05:00
/**
Define PrivModel and its instance.
Privileges array is generated based on
the type of nodes selected.
*/
var privModel = self.privModel;
var PrivModel = pgNode.Model.extend({
defaults: {
acl: undefined,
},
schema: [{
id: 'acl',
label: gettext('Privileges'),
model: pgBrowser.Node.PrivilegeRoleModel.extend({
// privileges are selected based on node clicked
privileges: obj_priv,
}),
uniqueCol: ['grantee', 'grantor'],
editable: true,
type: 'collection',
canAdd: true,
canDelete: true,
control: 'unique-col-collection',
} ],
});
2016-04-13 10:11:43 -05:00
/**
When privelege control is re-rendered, in order to
render privileges based on object type selected,
delete privileges from privModel which are now not
present in object privileges array(object_priv)
*/
var data = {};
if (privModel) {
data = privModel.toJSON();
var rolePrivs = data['acl'];
if (!_.isUndefined(rolePrivs) && rolePrivs.length > 0) {
for (var idx in rolePrivs) {
var rolePriv = (rolePrivs[idx])['privileges'],
removeIdx = [],
j;
for (j in rolePriv) {
var p = rolePriv[j];
if (_.indexOf(obj_priv, p['privilege_type']) == -1) {
removeIdx.push(j);
2016-04-13 10:11:43 -05:00
}
}
for (j in removeIdx) {
rolePriv.splice(j, 1);
}
2016-04-13 10:11:43 -05:00
}
} else {
console.warn('Acls are not defined');
2016-04-13 10:11:43 -05:00
}
}
// Instantiate privModel
privModel = self.privModel = new PrivModel(data, {
node_info: self.info,
2016-04-13 10:11:43 -05:00
});
/*
To track changes into model, start new session
and Add event listener for privileges control
*/
self.privModel.startNewSession();
self.privModel.on('pgadmin-session:valid', self.onSessionValidated.bind(self));
self.privModel.on('pgadmin-session:invalid', self.onSessionInvalid.bind(self));
/**
Create Field Object which has properties like
node_data, node_info which is required for rendering
Privilege control
*/
var fields = Backform.generateViewSchema(
self.info, self.privModel, 'create', self.d._type, self.d
);
var privilegesField = new Backform.Field(fields[0].fields[0]);
this.privControl = new(privilegesField.get('control'))({
field: privilegesField,
model: self.privModel,
});
return {
el: this.privControl.render().$el,
};
},
2016-04-13 10:11:43 -05:00
// Remove the privilege control
cleanup: function() {
if (this.privControl) {
this.privControl.remove();
delete this.privControl;
this.privControl = null;
}
},
});
}),
2016-04-13 10:11:43 -05:00
beforePrev: function(wizardObj) {
2016-04-13 10:11:43 -05:00
// Remove the privilege control
if (this.view) {
this.view.cleanup();
delete this.view;
this.view = null;
}
2016-04-13 10:11:43 -05:00
/**
Enable/Disable next button of DbObjectType page if objects
are present in model
*/
var objectsModel = newModel.get('objects');
2016-04-13 10:11:43 -05:00
if (!_.isUndefined(objectsModel) && !_.isEmpty(objectsModel) &&
objectsModel.length > 0) {
wizardObj.collection.at(0).set('disable_next', false);
2016-04-13 10:11:43 -05:00
// Don't show progress bar
wizardObj.collection.at(0).set('show_progress_bar', '');
}
2016-04-13 10:11:43 -05:00
/**
We're re-rendering the controls as they are deleted
before heading to next page
Refresh Backgrid to re-render the elements selected
re-render Filter
*/
newModel.trigger('backgrid:refresh', newModel, false);
self.clientSideFilter.render();
return true;
},
2016-04-13 10:11:43 -05:00
beforeNext: function() {
return true;
},
2016-04-13 10:11:43 -05:00
onNext: function() {
2016-04-13 10:11:43 -05:00
// Assign acls of privModel to main model newModel
if (!_.isUndefined(self.privModel)) {
newModel.set({
'acl': self.privModel.get('acl'),
});
}
2016-04-13 10:11:43 -05:00
// Remove the privilege control
if (this.view) {
this.view.cleanup();
delete this.view;
this.view = null;
2016-04-13 10:11:43 -05:00
}
// Enable finish button
self.wizard.options.disable_finish = false;
/**
triggers to get SQL queries data to render
into the reviewSQLPage
*/
newModel.trigger('pgadmin-wizard:nextpage:sql', {
'node_type': node_type,
});
},
});
2016-04-13 10:11:43 -05:00
//////////////////////////////////////////////////////////////////////
// //
// Review SQL Query Page //
// //
//////////////////////////////////////////////////////////////////////
2016-04-13 10:11:43 -05:00
//Create SqlField Object
var sqlField = new Backform.Field({
id: 'sqltab',
label: _('Sql Tab'),
2016-04-13 10:11:43 -05:00
/**
Extend 'SqlTabControl' to define new
function 'onWizardNextPageChange'
which gets triggered on next button
click to fetch generated SQL query
for the selected db objects.
*/
control: Backform.SqlTabControl.extend({
initialize: function() {
2016-04-13 10:11:43 -05:00
// Initialize parent class
Backform.SqlTabControl.prototype.initialize.apply(this, arguments);
2016-04-13 10:11:43 -05:00
this.msql_url = self.msql_url;
2016-04-13 10:11:43 -05:00
// define trigger events for prev and next page
this.model.on('pgadmin-wizard:nextpage:sql', this.onWizardNextPageChange, this);
this.model.on('pgadmin-wizard:prevpage:sql', this.onWizardPrevPageChange, this);
},
2016-04-13 10:11:43 -05:00
// This method fetches the modified SQL for the wizard
onWizardNextPageChange: function() {
2016-04-13 10:11:43 -05:00
var self = this;
2016-04-13 10:11:43 -05:00
// Fetches modified SQL
$.ajax({
url: this.msql_url,
type: 'GET',
cache: false,
data: self.model.toJSON(true, 'GET'),
dataType: 'json',
contentType: 'application/json',
}).done(function(res) {
self.sqlCtrl.clearHistory();
self.sqlCtrl.setValue(res.data);
self.sqlCtrl.refresh();
}).fail(function() {
self.model.trigger('pgadmin-view:msql:error');
}).always(function() {
self.model.trigger('pgadmin-view:msql:fetched');
});
},
2016-04-13 10:11:43 -05:00
remove: function() {
2016-04-13 10:11:43 -05:00
// Clear html dom elements of CodeMirror sql tab
self.sqlControl.unbind(); // Unbind all local event bindings
var cmElem = self.sqlControl.sqlCtrl.getWrapperElement();
$(cmElem).remove();
self.sqlControl.sqlCtrl = undefined;
},
2016-04-13 10:11:43 -05:00
}),
}),
2016-04-13 10:11:43 -05:00
/**
Create sqlField view instance
to render it into wizard page
*/
sqlControl = self.sqlControl = new(sqlField.get('control'))({
field: sqlField,
model: newModel,
});
2016-04-13 10:11:43 -05:00
// Wizard for SQL tab control
var reviewSQLPage = self.reviewSQLPage = new pgBrowser.WizardPage({
id: 3,
page_title: _('Final (Review Selection) (step 3 of 3)'),
show_description: _('The SQL below will be executed on the ' +
'database server to grant the selected privileges. ' +
'Please click on <b>Finish</b> to complete the process.'),
model: newModel,
view: new(function() {
// Render SqlTab control to generate its html markup
var sqlCtrlHtml = sqlControl.render().$el;
Fixed following: - Base font size changed from 0.815rem to 0.875rem, for navbar from 0.875rem to 0.925rem. - Dialog sizes made consistent throughout the application. Now there are 3 size options for width and height each - sm, md, lg. Combination of any of these to be used hereafter - Alignment fix for controls of Node properties dialogs which includes showing text and label in one line without dialog size change, checkbox alignment, switch control alignment at places and other minor improvements in other dialogs - Error message design change in dialogs validation - SQL Editor data grid editor popup design changes which were missed - Design change for dashboard server activity grid - Login page language dropdown color fix - Properties accordion collapse design fix - Help, Info icon fixed across all dialogs which were not working if clicked exactly on the text - Added missing icon with buttons at few places - Shadow behind the dialogs is increased to make it look clearly separated and depth. - Control Alignment fix in maintenance dialog - Min height of alertify dialogs set for better UX - File dialog design fix when no files found - Grant wizard fixes - Scroll bar visibility on first page, use full space for SQL generated on the last page - Browser toolbar buttons changed to sync with SQL editor toolbar buttons - Rounded corners for docker floating dialog (no properties) - Renaming file in file dialog should show original file name - SQL data grid text edit popup buttons behaviour was swapped. This is fixed. - Import/Export dialog changes as per new design.
2019-01-02 03:35:15 -06:00
sqlCtrlHtml.addClass('h-100');
this.render = function() {
return {
el: sqlCtrlHtml,
};
};
}),
2016-04-13 10:11:43 -05:00
beforePrev: function(wizardObj) {
2016-04-13 10:11:43 -05:00
/**
Enable next button if privilege
model is not empty else disable
next button
*/
var aclModel = newModel.get('acl');
if (!_.isUndefined(wizardObj.collection) &&
wizardObj.collection.models.length > 0) {
if (!_.isUndefined(aclModel) && !_.isEmpty(aclModel) &&
aclModel.length > 0) {
wizardObj.collection.at(1).set('disable_next', false);
} else {
wizardObj.collection.at(1).set('disable_next', true);
2016-04-13 10:11:43 -05:00
}
return true;
}
},
});
2016-04-13 10:11:43 -05:00
// Create Wizard Collection of Wizard Pages
var WizardCollection = Backbone.Collection.extend({
model: pgBrowser.WizardPage,
});
2016-04-13 10:11:43 -05:00
// It holds all the wizard pages to be rendered
this.wizardCollection = new WizardCollection(
[dbObjectTypePage, privilegePage, reviewSQLPage]
);
2016-04-13 10:11:43 -05:00
/**
Create wizard which has following operations:
- renders wizard pages
- defines the first page to render in wizard
- Save the model on finishbutton
- Remove wizard on cancel button
*/
self.wizard = new(pgBrowser.Wizard.extend({
options: {
title: _('Grant Wizard'),
/* Main Wizard Title */
width: '',
height: '',
curr_page: 0,
show_left_panel: false,
show_header_cancel_btn: true,
show_header_maximize_btn: true,
disable_finish: true,
dialog_api: that,
wizard_help: url_for(
'help.static', {
'filename': 'grant_wizard.html',
2016-04-13 10:11:43 -05:00
}
),
},
// Callback for finish button
onFinish: function() {
var m = newModel,
d = m.toJSON('GET');
// Save model
if (d && !_.isEmpty(d) && !_.isUndefined(d.objects)) {
m.save({}, {
attrs: d,
validate: false,
cache: false,
success: function() {
// Release wizard objects
self.releaseObjects();
self.close();
},
error: function(m, jqxhr) {
Alertify.pgNotifier(
'error', jqxhr,
gettext('Error saving properties')
);
// Release wizard objects
self.releaseObjects();
self.close();
},
});
2016-04-13 10:11:43 -05:00
}
},
// Callback for cancel button
onCancel: function() {
// Release wizard objects
self.releaseObjects();
self.close();
},
}))({
collection: this.wizardCollection,
el: el,
model: newModel,
});
// Render wizard
self.wizard.render();
},
};
});
2016-04-13 10:11:43 -05:00
}
// Call Grant Wizard Dialog and set dimensions for wizard
Fixed following: - Base font size changed from 0.815rem to 0.875rem, for navbar from 0.875rem to 0.925rem. - Dialog sizes made consistent throughout the application. Now there are 3 size options for width and height each - sm, md, lg. Combination of any of these to be used hereafter - Alignment fix for controls of Node properties dialogs which includes showing text and label in one line without dialog size change, checkbox alignment, switch control alignment at places and other minor improvements in other dialogs - Error message design change in dialogs validation - SQL Editor data grid editor popup design changes which were missed - Design change for dashboard server activity grid - Login page language dropdown color fix - Properties accordion collapse design fix - Help, Info icon fixed across all dialogs which were not working if clicked exactly on the text - Added missing icon with buttons at few places - Shadow behind the dialogs is increased to make it look clearly separated and depth. - Control Alignment fix in maintenance dialog - Min height of alertify dialogs set for better UX - File dialog design fix when no files found - Grant wizard fixes - Scroll bar visibility on first page, use full space for SQL generated on the last page - Browser toolbar buttons changed to sync with SQL editor toolbar buttons - Rounded corners for docker floating dialog (no properties) - Renaming file in file dialog should show original file name - SQL data grid text edit popup buttons behaviour was swapped. This is fixed. - Import/Export dialog changes as per new design.
2019-01-02 03:35:15 -06:00
Alertify.wizardDialog(true).resizeTo(pgBrowser.stdW.lg,pgBrowser.stdH.lg);
},
};
return pgBrowser.GrantWizard;
});