pgadmin4/web/pgadmin/tools/grant_wizard/static/js/grant_wizard.js
Libor M 94864104e4 Added and fixed gettext usage for better translation coverage. Fixes #5284
1) Fixed usage gettext('') instead of _('') in javascript files.
  2) Fixed usage gettext('') instead of `${gettext('')}` in javascript files,
     because "pybabel extract" not support extracting from this syntax.
2020-03-24 11:14:05 +05:30

1229 lines
45 KiB
JavaScript

/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2020, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
// Grant Wizard
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/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, commonUtils, 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;
return res;
},
validate: function() {
/*
* 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;
},
});
// Define columns for the Db Object Types grid
var columns = [{
name: 'selected',
/*
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() {
// Do not use parent's render function. It set's tabindex to -1 on
// checkboxes.
var col = this.column.get('name');
let id = `row-${_.uniqueId(col)}`;
this.$el.empty().append(`
<div class="custom-control custom-checkbox custom-checkbox-no-label">
<input tabindex="-1" type="checkbox" class="custom-control-input" id="${id}" />
<label class="custom-control-label" for="${id}">
<span class="sr-only">Select All<span>
</label>
</div>
`);
this.delegateEvents();
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);
}
}
return this;
},
}),
headerCell: 'select-all',
}, {
name: 'object_type',
label: gettext('Object Type'),
editable: false,
cell: Backgrid.Cell.extend({
render: function() {
// 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',
});
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
@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;
},
/**
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: gettext('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);
},
/**
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');
// Enable disable Next button
this.updateButtons(false);
return true;
},
/**
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');
// Enable disable Next button
this.updateButtons(sessHasChanged);
},
/**
Remove/Delete objects, attributes
in wizard on wizard close or finish
to reclaim memory
*/
releaseObjects: function() {
var self = this;
if (!_.isUndefined(self.dbObjectFilter)) {
self.dbObjectFilter.remove();
self.dbObjectFilter = undefined;
}
if (!_.isUndefined(self.clientSideFilter)) {
self.clientSideFilter.remove();
self.clientSideFilter = undefined;
}
// clear object priv array
if (!_.isNull(self.obj_priv) &&
!_.isUndefined(self.obj_priv)) {
self.obj_priv = [];
delete self.obj_priv;
}
// 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;
}
}
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;
}
}
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;
}
}
// Remove Sql control
if (!_.isUndefined(self.sqlControl)) {
self.sqlControl.remove();
}
// Clear privModel
if (!_.isNull(self.privModel) &&
!_.isUndefined(self.privModel)) {
self.privModel.clear();
}
// 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;
}
},
/**
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}
*/
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',
});
},
hooks: {
onshow: function() {
commonUtils.findAndSetFocus($(this.elements.body));
let self = this;
let containerFooter = $(this.elements.content).find('.wizard-buttons').find('.ml-auto');
//To get last header button
let lastHeaderButton = $(this.elements.content).find('.wizard-header').find('.ml-auto').find('button:first');
$(containerFooter).on('keydown', 'button', function(event) {
if (!event.shiftKey && event.keyCode == 9 && $(this).nextAll('button:not([disabled])').length == 0) {
// set focus back to first editable input element of current active tab once we cycle through all enabled buttons.
let container = $(self.elements.content).find('.wizard-header');
commonUtils.findAndSetFocus(container.find('button:not([disabled]):first'));
return false;
}
});
$(lastHeaderButton).on('keydown', function(event) {
if (event.shiftKey && event.keyCode == 9) {
// set focus back to first element of current active tab once we cycle through all enabled buttons.
let container = $(self.elements.content).find('.wizard-footer');
commonUtils.findAndSetFocus(container.find('button:not([disabled]):last'));
return false;
}
});
},
},
prepare: function() {
var that = this;
$container.empty().append('<div class=\'grant_wizard_container\'></div>');
// Define el for wizard view
var el = $('.grant_wizard_container');
// 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.
*/
var gid = info['server_group']._id,
sid = info.server._id,
did = info.database._id,
node_id = d._id,
/**
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),
});
coll.comparator = function(model) {
return model.get('object_type');
};
coll.sort();
this.dbObjectFilter = this.DbObjectFilter(coll);
/**
privArray holds objects selected which further helps
in creating privileges Model
*/
self.privArray = [];
/**
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;
}
/**
if a row (checkbox) is checked, add that model
into collection, when unchecked remove it from
model.
Also push/pop object type in/from privArray
*/
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);
}
// validate model on checkbox check/uncheck
var msg = model.validate.call(newModel);
/**
If no object type is selected, set error msg
and disable next button, else enable next button
*/
if (msg)
self.onSessionInvalid.call(self, msg);
else
self.onSessionValidated.call(self, true);
});
/**
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,
});
/**
Create instance of GrantWizard Model, provide urlRoot
node_info object, Generate fields objects
*/
var newModel = new GrantWizardModel({}, {
node_info: info,
});
/**
Fetch data from server and set into grid
and show/hide progress bar
*/
$('.wizard-progress-bar p').show();
var fetchAjaxHook = function() {
$('.wizard-progress-bar p').removeClass('alert-danger').addClass('alert-info');
$('.wizard-progress-bar p').text(gettext('Please wait while fetching records...'));
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(model, xhr, options) {
// If the main request fails as whole then
$('.wizard-progress-bar p').removeClass('alert-info').addClass('alert-danger');
$('.wizard-progress-bar p').text(gettext('Unable to fetch the database objects'));
Alertify.pgNotifier(
options.textStatus, xhr,
gettext('Unable to fetch the database objects'),
function(msg) {
if(msg === 'CRYPTKEY_SET') {
fetchAjaxHook();
} else {
$('.wizard-progress-bar p').removeClass('alert-info').addClass('alert-danger');
$('.wizard-progress-bar p').text(msg);
}
}
);
},
reset: true,
}, this);
};
fetchAjaxHook();
//////////////////////////////////////////////////////////////////////
// //
// Wizard Page for Db Object Type //
// //
//////////////////////////////////////////////////////////////////////
/**
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: gettext('Object Selection (step 1 of 3)'),
disable_prev: true,
disable_next: true,
show_description: '',
show_progress_bar: gettext('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;
}
// Remove grid element if exists
if (this.el) {
$(this.el).remove();
delete this.el;
}
},
// Delete grid before render
grid: null,
render: function() {
// 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>` + gettext('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="` + gettext('Search') + `" aria-label="Search" aria-describedby="labelSearch">
</div>
</div>
</div>
<div class="db_objects_grid"></div>
</div>`);
// Remove grid if exits before render
if (this.grid) {
this.cleanup();
}
// 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',
});
// Render selection Type grid and paginator
gridBody.find('.db_objects_grid').append(this.grid.render().$el);
// Render Search Filter
self.clientSideFilter.setCustomSearchBox(gridBody.find('#txtGridSearch'));
// Assign gridBody content to page element
this.el = gridBody;
/**
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);
}
});
// Refresh grid to re render rows.
coll.trigger('backgrid:refresh');
return this;
},
});
}),
beforeNext: function(obj) {
var self = this;
obj.options.disable_next = true;
/**
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);
}
// 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: gettext('Privilege Selection (step 2 of 3)'),
show_description: gettext('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);
});
/**
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',
} ],
});
/**
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);
}
}
for (j in removeIdx) {
rolePriv.splice(j, 1);
}
}
} else {
console.warn('Acls are not defined');
}
}
// Instantiate privModel
privModel = self.privModel = new PrivModel(data, {
node_info: self.info,
});
/*
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,
};
},
// Remove the privilege control
cleanup: function() {
if (this.privControl) {
this.privControl.remove();
delete this.privControl;
this.privControl = null;
}
},
});
}),
beforePrev: function(wizardObj) {
// Remove the privilege control
if (this.view) {
this.view.cleanup();
delete this.view;
this.view = null;
}
/**
Enable/Disable next button of DbObjectType page if objects
are present in model
*/
var objectsModel = newModel.get('objects');
if (!_.isUndefined(objectsModel) && !_.isEmpty(objectsModel) &&
objectsModel.length > 0) {
wizardObj.collection.at(0).set('disable_next', false);
// Don't show progress bar
wizardObj.collection.at(0).set('show_progress_bar', '');
}
/**
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;
},
beforeNext: function() {
return true;
},
onNext: function() {
// Assign acls of privModel to main model newModel
if (!_.isUndefined(self.privModel)) {
newModel.set({
'acl': self.privModel.get('acl'),
});
}
// Remove the privilege control
if (this.view) {
this.view.cleanup();
delete this.view;
this.view = null;
}
// 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,
});
},
});
//////////////////////////////////////////////////////////////////////
// //
// Review SQL Query Page //
// //
//////////////////////////////////////////////////////////////////////
//Create SqlField Object
var sqlField = new Backform.Field({
id: 'sqltab',
label: gettext('Sql Tab'),
/**
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() {
// Initialize parent class
Backform.SqlTabControl.prototype.initialize.apply(this, arguments);
this.msql_url = self.msql_url;
// 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);
},
// This method fetches the modified SQL for the wizard
onWizardNextPageChange: function() {
var self = this;
// Fetches modified SQL
$.ajax({
url: this.msql_url,
type: 'POST',
cache: false,
data: JSON.stringify(self.model.toJSON(true)),
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');
});
},
remove: function() {
// 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;
},
}),
}),
/**
Create sqlField view instance
to render it into wizard page
*/
sqlControl = self.sqlControl = new(sqlField.get('control'))({
field: sqlField,
model: newModel,
});
// Wizard for SQL tab control
var reviewSQLPage = self.reviewSQLPage = new pgBrowser.WizardPage({
id: 3,
page_title: gettext('Final (Review Selection) (step 3 of 3)'),
show_description: gettext('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;
sqlCtrlHtml.addClass('h-100');
this.render = function() {
return {
el: sqlCtrlHtml,
};
};
}),
beforePrev: function(wizardObj) {
/**
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);
}
return true;
}
},
});
// Create Wizard Collection of Wizard Pages
var WizardCollection = Backbone.Collection.extend({
model: pgBrowser.WizardPage,
});
// It holds all the wizard pages to be rendered
this.wizardCollection = new WizardCollection(
[dbObjectTypePage, privilegePage, reviewSQLPage]
);
/**
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: gettext('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',
}
),
},
// 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();
},
});
}
},
// 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();
},
};
});
}
// Call Grant Wizard Dialog and set dimensions for wizard
Alertify.wizardDialog(true).resizeTo(pgBrowser.stdW.lg,pgBrowser.stdH.lg);
},
};
return pgBrowser.GrantWizard;
});