alnoda-workspaces/workspaces/base-workspace/Cronicle-0.8.61/htdocs/js/pages/admin/Categories.js
2021-07-30 12:18:29 +00:00

495 lines
21 KiB
JavaScript
Executable file

// Cronicle Admin Page -- Categories
Class.add( Page.Admin, {
gosub_categories: function(args) {
// show category list
this.div.removeClass('loading');
app.setWindowTitle( "Categories" );
var size = get_inner_window_size();
var col_width = Math.floor( ((size.width * 0.9) + 200) / 5 );
var html = '';
html += this.getSidebarTabs( 'categories',
[
['activity', "Activity Log"],
['api_keys', "API Keys"],
['categories', "Categories"],
['plugins', "Plugins"],
['servers', "Servers"],
['users', "Users"]
]
);
var cols = ['Title', 'Description', 'Assigned Events', 'Max Concurrent', 'Actions'];
html += '<div style="padding:20px 20px 30px 20px">';
html += '<div class="subtitle">';
html += 'Event Categories';
// html += '<div class="clear"></div>';
html += '</div>';
// sort by title ascending
this.categories = app.categories.sort( function(a, b) {
// return (b.title < a.title) ? 1 : -1;
return a.title.toLowerCase().localeCompare( b.title.toLowerCase() );
} );
// render table
var self = this;
html += this.getBasicTable( this.categories, cols, 'category', function(cat, idx) {
var actions = [
'<span class="link" onMouseUp="$P().edit_category('+idx+')"><b>Edit</b></span>',
'<span class="link" onMouseUp="$P().delete_category('+idx+')"><b>Delete</b></span>'
];
var cat_events = find_objects( app.schedule, { category: cat.id } );
var num_events = cat_events.length;
var tds = [
'<div class="td_big"><span class="link" onMouseUp="$P().edit_category('+idx+')">' + self.getNiceCategory(cat, col_width) + '</span></div>',
'<div class="ellip" style="max-width:'+col_width+'px;">' + encode_entities(cat.description || '(No description)') + '</div>',
num_events ? commify( num_events ) : '(None)',
cat.max_children ? commify(cat.max_children) : '(No limit)',
actions.join(' | ')
];
if (cat && cat.color) {
if (tds.className) tds.className += ' '; else tds.className = '';
tds.className += cat.color;
}
if (!cat.enabled) {
if (tds.className) tds.className += ' '; else tds.className = '';
tds.className += 'disabled';
}
return tds;
} );
html += '<div style="height:30px;"></div>';
html += '<center><table><tr>';
html += '<td><div class="button" style="width:130px;" onMouseUp="$P().edit_category(-1)"><i class="fa fa-plus-circle">&nbsp;&nbsp;</i>Add Category...</div></td>';
html += '</tr></table></center>';
html += '</div>'; // padding
html += '</div>'; // sidebar tabs
this.div.html( html );
},
edit_category: function(idx) {
// jump to edit sub
if (idx > -1) Nav.go( '#Admin?sub=edit_category&id=' + this.categories[idx].id );
else Nav.go( '#Admin?sub=new_category' );
},
delete_category: function(idx) {
// delete key from search results
this.category = this.categories[idx];
this.show_delete_category_dialog();
},
gosub_new_category: function(args) {
// create new Category
var html = '';
app.setWindowTitle( "New Category" );
this.div.removeClass('loading');
html += this.getSidebarTabs( 'new_category',
[
['activity', "Activity Log"],
['api_keys', "API Keys"],
['categories', "Categories"],
['new_category', "New Category"],
['plugins', "Plugins"],
['servers', "Servers"],
['users', "Users"]
]
);
html += '<div style="padding:20px;"><div class="subtitle">Add New Category</div></div>';
html += '<div style="padding:0px 20px 50px 20px">';
html += '<center><table style="margin:0;">';
this.category = {
title: "",
description: "",
max_children: 0,
enabled: 1
};
html += this.get_category_edit_html();
// buttons at bottom
html += '<tr><td colspan="2" align="center">';
html += '<div style="height:30px;"></div>';
html += '<table><tr>';
html += '<td><div class="button" style="width:120px; font-weight:normal;" onMouseUp="$P().cancel_category_edit()">Cancel</div></td>';
html += '<td width="50">&nbsp;</td>';
html += '<td><div class="button" style="width:120px;" onMouseUp="$P().do_new_category()"><i class="fa fa-plus-circle">&nbsp;&nbsp;</i>Add Category</div></td>';
html += '</tr></table>';
html += '</td></tr>';
html += '</table></center>';
html += '</div>'; // table wrapper div
html += '</div>'; // sidebar tabs
this.div.html( html );
setTimeout( function() {
$('#fe_ec_title').focus();
}, 1 );
},
cancel_category_edit: function() {
// cancel editing category and return to list
Nav.go( 'Admin?sub=categories' );
},
do_new_category: function(force) {
// create new category
app.clearError();
var category = this.get_category_form_json();
if (!category) return; // error
// pro-tip: embed id in title as bracketed prefix
if (category.title.match(/^\[(\w+)\]\s*(.+)$/)) {
category.id = RegExp.$1;
category.title = RegExp.$2;
}
this.category = category;
app.showProgress( 1.0, "Creating category..." );
app.api.post( 'app/create_category', category, this.new_category_finish.bind(this) );
},
new_category_finish: function(resp) {
// new Category created successfully
app.hideProgress();
// Can't nav to edit_category yet, websocket may not have received update yet
// Nav.go('Admin?sub=edit_category&id=' + resp.id);
Nav.go('Admin?sub=categories');
setTimeout( function() {
app.showMessage('success', "The new category was created successfully.");
}, 150 );
},
gosub_edit_category: function(args) {
// edit existing Category
var html = '';
this.category = find_object( app.categories, { id: args.id } );
app.setWindowTitle( "Editing Category \"" + (this.category.title) + "\"" );
this.div.removeClass('loading');
html += this.getSidebarTabs( 'edit_category',
[
['activity', "Activity Log"],
['api_keys', "API Keys"],
['categories', "Categories"],
['edit_category', "Edit Category"],
['plugins', "Plugins"],
['servers', "Servers"],
['users', "Users"]
]
);
html += '<div style="padding:20px;"><div class="subtitle">Editing Category &ldquo;' + (this.category.title) + '&rdquo;</div></div>';
html += '<div style="padding:0px 20px 50px 20px">';
html += '<center>';
html += '<table style="margin:0;">';
html += this.get_category_edit_html();
html += '<tr><td colspan="2" align="center">';
html += '<div style="height:30px;"></div>';
html += '<table><tr>';
html += '<td><div class="button" style="width:130px; font-weight:normal;" onMouseUp="$P().cancel_category_edit()">Cancel</div></td>';
html += '<td width="50">&nbsp;</td>';
html += '<td><div class="button" style="width:130px; font-weight:normal;" onMouseUp="$P().show_delete_category_dialog()">Delete Category...</div></td>';
html += '<td width="50">&nbsp;</td>';
html += '<td><div class="button" style="width:130px;" onMouseUp="$P().do_save_category()"><i class="fa fa-floppy-o">&nbsp;&nbsp;</i>Save Changes</div></td>';
html += '</tr></table>';
html += '</td></tr>';
html += '</table>';
html += '</center>';
html += '</div>'; // table wrapper div
html += '</div>'; // sidebar tabs
this.div.html( html );
},
do_save_category: function() {
// save changes to category
app.clearError();
var category = this.get_category_form_json();
if (!category) return; // error
this.category = category;
app.showProgress( 1.0, "Saving category..." );
app.api.post( 'app/update_category', category, this.save_category_finish.bind(this) );
},
save_category_finish: function(resp, tx) {
// new category saved successfully
var self = this;
var category = this.category;
app.hideProgress();
app.showMessage('success', "The category was saved successfully.");
window.scrollTo( 0, 0 );
// copy active jobs to array
var jobs = [];
for (var id in app.activeJobs) {
var job = app.activeJobs[id];
if ((job.category == category.id) && !job.detached) jobs.push( job );
}
// if the cat was disabled and there are running jobs, ask user to abort them
if (!category.enabled && jobs.length) {
app.confirm( '<span style="color:red">Abort Jobs</span>', "There " + ((jobs.length != 1) ? 'are' : 'is') + " currently still " + jobs.length + " active " + pluralize('job', jobs.length) + " using the disabled category <b>"+category.title+"</b>. Do you want to abort " + ((jobs.length != 1) ? 'these' : 'it') + " now?", "Abort", function(result) {
if (result) {
app.showProgress( 1.0, "Aborting " + pluralize('Job', jobs.length) + "..." );
app.api.post( 'app/abort_jobs', { category: category.id }, function(resp) {
app.hideProgress();
if (resp.count > 0) {
app.showMessage('success', "The " + pluralize('job', resp.count) + " " + ((resp.count != 1) ? 'were' : 'was') + " aborted successfully.");
}
else {
app.showMessage('warning', "No jobs were aborted. It is likely they completed while the dialog was up.");
}
} );
} // clicked Abort
} ); // app.confirm
} // disabled + jobs
},
show_delete_category_dialog: function() {
// show dialog confirming category delete action
var self = this;
var category = this.category;
var cat = this.category;
// check for events first
var cat_events = find_objects( app.schedule, { category: cat.id } );
var num_events = cat_events.length;
if (num_events) return app.doError("Sorry, you cannot delete a category that has events assigned to it.");
// proceed with delete
var self = this;
app.confirm( '<span style="color:red">Delete Category</span>', "Are you sure you want to delete the category <b>"+cat.title+"</b>? There is no way to undo this action.", "Delete", function(result) {
if (result) {
app.showProgress( 1.0, "Deleting Category..." );
app.api.post( 'app/delete_category', cat, self.delete_category_finish.bind(self) );
}
} );
},
delete_category_finish: function(resp, tx) {
// finished deleting category
var self = this;
app.hideProgress();
Nav.go('Admin?sub=categories', 'force');
setTimeout( function() {
app.showMessage('success', "The category '"+self.category.title+"' was deleted successfully.");
}, 150 );
},
get_category_edit_html: function() {
// get html for editing a category (or creating a new one)
var html = '';
var category = this.category;
var cat = this.category;
// Internal ID
if (cat.id && this.isAdmin()) {
html += get_form_table_row( 'Category ID', '<div style="font-size:14px;">' + cat.id + '</div>' );
html += get_form_table_caption( "The internal Category ID used for API calls. This cannot be changed." );
html += get_form_table_spacer();
}
// title
html += get_form_table_row('Category Title:', '<input type="text" id="fe_ec_title" size="25" value="'+escape_text_field_value(cat.title)+'"/>') +
get_form_table_caption("Enter a title for the category, short and sweet.") +
get_form_table_spacer();
// cat enabled
html += get_form_table_row( 'Active', '<input type="checkbox" id="fe_ec_enabled" value="1" ' + (cat.enabled ? 'checked="checked"' : '') + '/><label for="fe_ec_enabled">Category Enabled</label>' );
html += get_form_table_caption( "Select whether events in this category should be enabled or disabled in the schedule." );
html += get_form_table_spacer();
// description
html += get_form_table_row('Description:', '<textarea id="fe_ec_desc" style="width:500px; height:50px; resize:vertical;">'+escape_text_field_value(cat.description)+'</textarea>') +
get_form_table_caption("Optionally enter a description for the category.") +
get_form_table_spacer();
// max concurrent
html += get_form_table_row('Max Concurrent:', '<select id="fe_ec_max_children">' + render_menu_options([ [0,'No Limit'], 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 ], cat.max_children, true) + '</select>') +
get_form_table_caption("Select the maximum number of jobs allowed to run concurrently in this category.");
html += get_form_table_spacer();
// color
var current_color = cat.color || 'plain';
var swatch_html = '';
var colors = ['plain', 'red', 'green', 'blue', 'skyblue', 'yellow', 'purple', 'orange'];
for (var idx = 0, len = colors.length; idx < len; idx++) {
var color = colors[idx];
swatch_html += '<div class="swatch ' + color + ' ' + ((current_color == color) ? 'active' : '') + '" onMouseUp="$P().select_color(\''+color+'\')"></div>';
}
swatch_html += '<div class="clear"></div>';
html += get_form_table_row( 'Highlight Color', swatch_html );
html += get_form_table_caption( "Optionally select a highlight color for the category, which will show on the schedule." );
html += get_form_table_spacer();
// default notification options
var notif_expanded = !!(cat.notify_success || cat.notify_fail || cat.web_hook);
html += get_form_table_row( 'Notification',
'<div style="font-size:13px;'+(notif_expanded ? 'display:none;' : '')+'"><span class="link addme" onMouseUp="$P().expand_fieldset($(this))"><i class="fa fa-plus-square-o">&nbsp;</i>Default Notification Options</span></div>' +
'<fieldset style="padding:10px 10px 0 10px; margin-bottom:5px;'+(notif_expanded ? '' : 'display:none;')+'"><legend class="link addme" onMouseUp="$P().collapse_fieldset($(this))"><i class="fa fa-minus-square-o">&nbsp;</i>Default Notification Options</legend>' +
'<div class="plugin_params_label">Default Email on Success:</div>' +
'<div class="plugin_params_content"><input type="text" id="fe_ec_notify_success" size="50" value="'+escape_text_field_value(cat.notify_success)+'" placeholder="email@sample.com" spellcheck="false" onChange="$P().update_add_remove_me($(this))"/><span class="link addme" onMouseUp="$P().add_remove_me($(this).prev())"></span></div>' +
'<div class="plugin_params_label">Default Email on Failure:</div>' +
'<div class="plugin_params_content"><input type="text" id="fe_ec_notify_fail" size="50" value="'+escape_text_field_value(cat.notify_fail)+'" placeholder="email@sample.com" spellcheck="false" onChange="$P().update_add_remove_me($(this))"/><span class="link addme" onMouseUp="$P().add_remove_me($(this).prev())"></span></div>' +
'<div class="plugin_params_label">Default Web Hook URL:</div>' +
'<div class="plugin_params_content"><input type="text" id="fe_ec_web_hook" size="60" value="'+escape_text_field_value(cat.web_hook)+'" placeholder="http://" spellcheck="false"/></div>' +
'</fieldset>'
);
html += get_form_table_caption( "Optionally enter default e-mail addresses for notification, and/or a web hook URL.<br/>Note that events can override any of these notification settings." );
html += get_form_table_spacer();
// default resource limits
var res_expanded = !!(cat.memory_limit || cat.memory_sustain || cat.cpu_limit || cat.cpu_sustain || cat.log_max_size);
html += get_form_table_row( 'Limits',
'<div style="font-size:13px;'+(res_expanded ? 'display:none;' : '')+'"><span class="link addme" onMouseUp="$P().expand_fieldset($(this))"><i class="fa fa-plus-square-o">&nbsp;</i>Default Resource Limits</span></div>' +
'<fieldset style="padding:10px 10px 0 10px; margin-bottom:5px;'+(res_expanded ? '' : 'display:none;')+'"><legend class="link addme" onMouseUp="$P().collapse_fieldset($(this))"><i class="fa fa-minus-square-o">&nbsp;</i>Default Resource Limits</legend>' +
'<div class="plugin_params_label">Default CPU Limit:</div>' +
'<div class="plugin_params_content"><table cellspacing="0" cellpadding="0" class="fieldset_params_table"><tr>' +
'<td style="padding-right:2px"><input type="checkbox" id="fe_ec_cpu_enabled" value="1" '+(cat.cpu_limit ? 'checked="checked"' : '')+' /></td>' +
'<td><label for="fe_ec_cpu_enabled">Abort job if CPU exceeds</label></td>' +
'<td><input type="text" id="fe_ec_cpu_limit" style="width:30px;" value="'+(cat.cpu_limit || 0)+'"/>%</td>' +
'<td>for</td>' +
'<td>' + this.get_relative_time_combo_box( 'fe_ec_cpu_sustain', cat.cpu_sustain, 'fieldset_params_table' ) + '</td>' +
'</tr></table></div>' +
'<div class="plugin_params_label">Default Memory Limit:</div>' +
'<div class="plugin_params_content"><table cellspacing="0" cellpadding="0" class="fieldset_params_table"><tr>' +
'<td style="padding-right:2px"><input type="checkbox" id="fe_ec_memory_enabled" value="1" '+(cat.memory_limit ? 'checked="checked"' : '')+' /></td>' +
'<td><label for="fe_ec_memory_enabled">Abort job if memory exceeds</label></td>' +
'<td>' + this.get_relative_size_combo_box( 'fe_ec_memory_limit', cat.memory_limit, 'fieldset_params_table' ) + '</td>' +
'<td>for</td>' +
'<td>' + this.get_relative_time_combo_box( 'fe_ec_memory_sustain', cat.memory_sustain, 'fieldset_params_table' ) + '</td>' +
'</tr></table></div>' +
'<div class="plugin_params_label">Default Log Size Limit:</div>' +
'<div class="plugin_params_content"><table cellspacing="0" cellpadding="0" class="fieldset_params_table"><tr>' +
'<td style="padding-right:2px"><input type="checkbox" id="fe_ec_log_enabled" value="1" '+(cat.log_max_size ? 'checked="checked"' : '')+' /></td>' +
'<td><label for="fe_ec_log_enabled">Abort job if log file exceeds</label></td>' +
'<td>' + this.get_relative_size_combo_box( 'fe_ec_log_limit', cat.log_max_size, 'fieldset_params_table' ) + '</td>' +
'</tr></table></div>' +
'</fieldset>'
);
html += get_form_table_caption(
"Optionally set default CPU load, memory usage and log size limits for the category.<br/>Note that events can override any of these limits."
);
html += get_form_table_spacer();
setTimeout( function() {
$P().update_add_remove_me( $('#fe_ec_notify_success, #fe_ec_notify_fail') );
}, 1 );
return html;
},
select_color: function(color) {
// click on a color swatch
this.category.color = (color == 'plain') ? '' : color;
$('.swatch').removeClass('active');
$('.swatch.'+color).addClass('active');
},
get_category_form_json: function() {
// get category elements from form, used for new or edit
var category = this.category;
category.title = $('#fe_ec_title').val();
if (!category.title.length) {
return app.badField('#fe_ec_title', "Please enter a title for the category.");
}
category.enabled = $('#fe_ec_enabled').is(':checked') ? 1 : 0;
category.description = $('#fe_ec_desc').val();
category.max_children = parseInt( $('#fe_ec_max_children').val() );
category.notify_success = $('#fe_ec_notify_success').val();
category.notify_fail = $('#fe_ec_notify_fail').val();
category.web_hook = $('#fe_ec_web_hook').val();
// cpu limit
if ($('#fe_ec_cpu_enabled').is(':checked')) {
category.cpu_limit = parseInt( $('#fe_ec_cpu_limit').val() );
if (isNaN(category.cpu_limit)) return app.badField('fe_ec_cpu_limit', "Please enter an integer value for the CPU limit.");
if (category.cpu_limit < 0) return app.badField('fe_ec_cpu_limit', "Please enter a positive integer for the CPU limit.");
category.cpu_sustain = parseInt( $('#fe_ec_cpu_sustain').val() ) * parseInt( $('#fe_ec_cpu_sustain_units').val() );
if (isNaN(category.cpu_sustain)) return app.badField('fe_ec_cpu_sustain', "Please enter an integer value for the CPU sustain period.");
if (category.cpu_sustain < 0) return app.badField('fe_ec_cpu_sustain', "Please enter a positive integer for the CPU sustain period.");
}
else {
category.cpu_limit = 0;
category.cpu_sustain = 0;
}
// mem limit
if ($('#fe_ec_memory_enabled').is(':checked')) {
category.memory_limit = parseInt( $('#fe_ec_memory_limit').val() ) * parseInt( $('#fe_ec_memory_limit_units').val() );
if (isNaN(category.memory_limit)) return app.badField('fe_ec_memory_limit', "Please enter an integer value for the memory limit.");
if (category.memory_limit < 0) return app.badField('fe_ec_memory_limit', "Please enter a positive integer for the memory limit.");
category.memory_sustain = parseInt( $('#fe_ec_memory_sustain').val() ) * parseInt( $('#fe_ec_memory_sustain_units').val() );
if (isNaN(category.memory_sustain)) return app.badField('fe_ec_memory_sustain', "Please enter an integer value for the memory sustain period.");
if (category.memory_sustain < 0) return app.badField('fe_ec_memory_sustain', "Please enter a positive integer for the memory sustain period.");
}
else {
category.memory_limit = 0;
category.memory_sustain = 0;
}
// job log file size limit
if ($('#fe_ec_log_enabled').is(':checked')) {
category.log_max_size = parseInt( $('#fe_ec_log_limit').val() ) * parseInt( $('#fe_ec_log_limit_units').val() );
if (isNaN(category.log_max_size)) return app.badField('fe_ec_log_limit', "Please enter an integer value for the log size limit.");
if (category.log_max_size < 0) return app.badField('fe_ec_log_limit', "Please enter a positive integer for the log size limit.");
}
else {
category.log_max_size = 0;
}
return category;
}
});