Files
pgadmin4/web/pgadmin/tools/maintenance/static/js/maintenance.js

431 lines
16 KiB
JavaScript

define([
'sources/gettext', 'sources/url_for', 'jquery', 'underscore',
'underscore.string', 'pgadmin.alertifyjs', 'pgadmin', 'pgadmin.browser', 'backbone',
'backgrid', 'backform',
'pgadmin.backform', 'pgadmin.backgrid',
'pgadmin.browser.node.ui'
], function(
gettext, url_for, $, _, S, Alertify, pgAdmin, pgBrowser, Backbone, Backgrid,
Backform
) {
pgAdmin = pgAdmin || window.pgAdmin || {};
var pgTools = pgAdmin.Tools = pgAdmin.Tools || {};
// Return back, this has been called more than once
if (pgAdmin.Tools.maintenance)
return pgAdmin.Tools.maintenance;
var CustomSwitchControl = Backform.CustomSwitchControl = Backform.SwitchControl.extend({
template: _.template([
'<label class="<%=Backform.controlLabelClassName%> custom_switch_label_class"><%=label%></label>',
'<div class="<%=Backform.controlsClassName%> custom_switch_control_class">',
' <div class="checkbox">',
' <label>',
' <input type="checkbox" class="<%=extraClasses.join(\' \')%>" name="<%=name%>" <%=value ? "checked=\'checked\'" : ""%> <%=disabled ? "disabled" : ""%> <%=required ? "required" : ""%> />',
' </label>',
' </div>',
'</div>',
'<% if (helpMessage && helpMessage.length) { %>',
' <span class="<%=Backform.helpMessageClassName%>"><%=helpMessage%></span>',
'<% } %>'
].join("\n")),
className: 'pgadmin-control-group form-group col-xs-6'
});
// Main model for Maintenance functionality
var MaintenanceModel = Backbone.Model.extend({
defaults: {
op: 'VACUUM',
vacuum_full: false,
vacuum_freeze: false,
vacuum_analyze: false,
verbose: true
},
initialize: function() {
var node_info = arguments[1]['node_info'];
// If node is Unique or Primary key then set op to reindex
if ('primary_key' in node_info || 'unique_constraint' in node_info
|| 'index' in node_info) {
this.set('op', 'REINDEX');
this.set('verbose', false);
}
},
schema: [
{
id: 'op', label: gettext('Maintenance operation'), cell: 'string',
type: 'text', group: gettext('Options'),
options:[
{'label': "VACUUM", 'value': "VACUUM"},
{'label': "ANALYZE", 'value': "ANALYZE"},
{'label': "REINDEX", 'value': "REINDEX"},
{'label': "CLUSTER", 'value': "CLUSTER"},
],
control: Backform.RadioControl.extend({
template: _.template([
'<label class="control-label col-sm-4 col-xs-12"><%=label%></label>',
'<div class="pgadmin-controls col-xs-12 col-sm-8 btn-group pg-maintenance-op" data-toggle="buttons">',
' <% for (var i=0; i < options.length; i++) { %>',
' <% var option = options[i]; %>',
' <label class="btn btn-primary<% if (i == 0) { %> active<%}%>">',
' <input type="radio" name="op" id="op" autocomplete="off" value=<%-formatter.fromRaw(option.value)%><% if (i == 0) { %> selected<%}%> > <%-option.label%>',
' </label>',
' <% } %>',
'</div>'
].join("\n"))
}),
select2: {
allowClear: false,
width: "100%",
placeholder: gettext('Select from list...')
},
},
{
type: 'nested', control: 'fieldset', label: gettext('Vacuum'), group: gettext('Options'),
schema:[{
id: 'vacuum_full', group: gettext('Vacuum'), disabled: 'isDisabled',
control: Backform.CustomSwitchControl, label: gettext('FULL'), deps: ['op']
},{
id: 'vacuum_freeze', deps: ['op'], disabled: 'isDisabled',
control: Backform.CustomSwitchControl, label: gettext('FREEZE'), group: gettext('Vacuum')
},{
id: 'vacuum_analyze', deps: ['op'], disabled: 'isDisabled',
control: Backform.CustomSwitchControl, label: gettext('ANALYZE'), group: gettext('Vacuum')
}]
},
{
id: 'verbose', group: gettext('Options'), deps: ['op'],
control: Backform.CustomSwitchControl, label: gettext('Verbose Messages'), disabled: 'isDisabled'
}
],
// Enable/Disable the items based on the user maintenance operation selection
isDisabled: function(m) {
var name = this.name,
node_info = this.node_info;
switch(name) {
case 'vacuum_full':
case 'vacuum_freeze':
case 'vacuum_analyze':
if (m.get('op') != 'VACUUM') {
return true;
}
else {
return false;
}
break;
case 'verbose':
if ('primary_key' in node_info || 'unique_constraint' in node_info ||
'index' in node_info ) {
if (m.get('op') == 'REINDEX') {
setTimeout(function() { m.set('verbose', false); }, 10);
return true;
}
}
if (m.get('op') == 'REINDEX') {
return true;
}
else {
return false;
}
break;
default:
return false;
}
return false;
}
});
pgTools.maintenance = {
init: function() {
// We do not want to initialize the module multiple times.
if (this.initialized)
return;
this.initialized = true;
var maintenance_supported_nodes = [
'database', 'table', 'primary_key',
'unique_constraint', 'index', 'partition'
];
/**
Enable/disable Maintenance menu in tools based on node selected.
Maintenance menu will be enabled only when user select table and database node.
*/
var menu_enabled = function(itemData, item, data) {
var t = pgBrowser.tree, i = item, d = itemData;
var parent_item = t.hasParent(i) ? t.parent(i): null,
parent_data = parent_item ? t.itemData(parent_item) : null;
if(!_.isUndefined(d) && !_.isNull(d) && !_.isNull(parent_data)) {
if (_.indexOf(maintenance_supported_nodes, d._type) !== -1 &&
parent_data._type != 'catalog') {
if (d._type == 'database' && d.allowConn)
return true;
else if(d._type != 'database')
return true;
else
return false;
}
else
return false;
}
else
return false;
};
var menus = [{
name: 'maintenance', module: this,
applies: ['tools'], callback: 'callback_maintenance',
priority: 10, label: gettext('Maintenance...'),
icon: 'fa fa-wrench', enable: menu_enabled
}];
// Add supported menus into the menus list
for (var idx = 0; idx < maintenance_supported_nodes.length; idx++) {
menus.push({
name: 'maintenance_context_' + maintenance_supported_nodes[idx],
node: maintenance_supported_nodes[idx], module: this,
applies: ['context'], callback: 'callback_maintenance',
priority: 10, label: gettext('Maintenance...'),
icon: 'fa fa-wrench', enable: menu_enabled
});
}
pgBrowser.add_menus(menus);
},
/*
Open the dialog for the maintenance functionality
*/
callback_maintenance: function(args, item) {
var i = item || pgBrowser.tree.selected(),
server_data = null;
while (i) {
var node_data = pgBrowser.tree.itemData(i);
if (node_data._type == 'server') {
server_data = node_data;
break;
}
if (pgBrowser.tree.hasParent(i)) {
i = $(pgBrowser.tree.parent(i));
} else {
Alertify.alert(gettext("Please select server or child node from tree."));
break;
}
}
if (!server_data) {
return;
}
var module = 'paths',
preference_name = 'pg_bin_dir',
msg = gettext('Please configure the PostgreSQL Binary Path in the Preferences dialog.');
if ((server_data.type && server_data.type == 'ppas') ||
server_data.server_type == 'ppas') {
preference_name = 'ppas_bin_dir';
msg = gettext('Please configure the EDB Advanced Server Binary Path in the Preferences dialog.');
}
var preference = pgBrowser.get_preference(module, preference_name);
if(preference) {
if (!preference.value) {
Alertify.alert(gettext('Configuration required'), msg);
return;
}
} else {
Alertify.alert(S(gettext('Failed to load preference %s of module %s')).sprintf(preference_name, module).value());
return;
}
var self = this,
input = args || {},
t = pgBrowser.tree,
i = item || t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined,
node = d && pgBrowser.Nodes[d._type];
if (!d)
return;
var objName = d.label;
var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
if (!Alertify.MaintenanceDialog) {
Alertify.dialog('MaintenanceDialog', function factory() {
return {
main: function(title) {
this.set('title', title);
},
setup: function() {
return {
buttons:[{
text: '', className: 'btn btn-default pull-left fa fa-lg fa-info',
attrs:{name:'object_help', type:'button', url: 'maintenance.html', label: gettext('Maintenance')}
},{
text: '', key: 112,
className: 'btn btn-default pull-left fa fa-lg fa-question',
attrs:{
name:'dialog_help', type:'button',
label: gettext('Maintenance'),
url: url_for(
'help.static', {'filename': 'maintenance_dialog.html'}
)
}
},{
text: gettext("OK"), key: 13, className: "btn btn-primary fa fa-lg fa-save pg-alertify-button",
'data-btn-name': 'ok',
},{
text: gettext("Cancel"), key: 27, className: "btn btn-danger fa fa-lg fa-times pg-alertify-button",
'data-btn-name': 'cancel',
}],
options: { modal: 0, pinnable: false}
};
},
// Callback functions when click on the buttons of the Alertify dialogs
callback: function(e) {
var i = pgBrowser.tree.selected(),
d = i && i.length == 1 ? pgBrowser.tree.itemData(i) : undefined,
node = d && pgBrowser.Nodes[d._type];
if (e.button.element.name == "dialog_help" || e.button.element.name == "object_help") {
e.cancel = true;
pgBrowser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
node, i, e.button.element.getAttribute('label'));
return;
}
if (e.button['data-btn-name'] === "ok") {
var schema = undefined,
table = undefined,
primary_key = undefined,
unique_constraint = undefined,
index = undefined;
if (!d)
return;
var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
if (treeInfo.schema != undefined) {
schema = treeInfo.schema._label;
}
if (treeInfo.partition != undefined) {
table = treeInfo.partition._label;
} else if (treeInfo.table != undefined) {
table = treeInfo.table._label;
}
if (treeInfo.primary_key != undefined) {
primary_key = treeInfo.primary_key._label;
} else if (treeInfo.unique_constraint != undefined) {
unique_constraint = treeInfo.unique_constraint._label;
} else if (treeInfo.index != undefined) {
index = treeInfo.index._label;
}
this.view.model.set({'database': treeInfo.database._label,
'schema': schema,
'table': table,
'primary_key': primary_key,
'unique_constraint': unique_constraint,
'index': index})
$.ajax({
url: url_for(
'maintenance.create_job', {
'sid': treeInfo.server._id,
'did': treeInfo.database._id
}),
method: 'POST',
data:{'data': JSON.stringify(this.view.model.toJSON())},
success: function(res) {
if (res.data && res.data.status) {
//Do nothing as we are creating the job and exiting from the main dialog
Alertify.success(res.data.info);
pgBrowser.Events.trigger('pgadmin-bgprocess:created', self);
}
else {
Alertify.error(res.data.errmsg);
}
},
error: function(e) {
Alertify.alert(
gettext("Maintenance job creation failed.")
);
}
});
}
},
build: function() {
Alertify.pgDialogBuild.apply(this)
},
hooks: {
onclose: function() {
if (this.view) {
this.view.remove({data: true, internal: true, silent: true});
}
}
},
prepare: function() {
// Main maintenance tool dialog container
var $container = $("<div class='maintenance_dlg'></div>");
var t = pgBrowser.tree,
i = t.selected(),
d = i && i.length == 1 ? t.itemData(i) : undefined,
node = d && pgBrowser.Nodes[d._type];
if (!d)
return;
var treeInfo = node.getTreeNodeHierarchy.apply(node, [i]);
var newModel = new MaintenanceModel (
{}, {node_info: treeInfo}
),
fields = Backform.generateViewSchema(
treeInfo, newModel, 'create', node, treeInfo.server, true
);
var view = this.view = new Backform.Dialog({
el: $container, model: newModel, schema: fields
});
$(this.elements.body.childNodes[0]).addClass('alertify_tools_dialog_properties obj_properties');
view.render();
// If node is Index, Unique or Primary key then disable vacuum & analyze button
if (d._type == 'primary_key' || d._type == 'unique_constraint'
|| d._type == 'index') {
var vacuum_analyze_btns = $container.find(
'.pgadmin-controls label:lt(2)'
).removeClass('active').addClass('disabled');
// Find reindex button element & add active class to it
var reindex_btn = vacuum_analyze_btns[1].nextElementSibling;
$(reindex_btn).addClass('active');
}
this.elements.content.appendChild($container.get(0));
}
};
});
}
// Open the Alertify dialog
Alertify.MaintenanceDialog('Maintenance...').set('resizable',true).resizeTo('60%','80%');
},
};
return pgAdmin.Tools.maintenance;
});