mirror of
https://github.com/ntop/ntopng.git
synced 2026-04-30 07:59:35 +00:00
449 lines
17 KiB
Text
449 lines
17 KiB
Text
{#
|
|
(C) 2020-21 - ntop.org
|
|
|
|
This is the template to generate the Manage Pool page.
|
|
#}
|
|
<div class="row">
|
|
<div class="col-md-12 mb-4">
|
|
<div class="card card-shadow">
|
|
<div class="card-header">
|
|
{# import the navbar inside the pool page to navigate through the pool tables #}
|
|
{* template_utils.gen("pages/components/nav_tabs.template", menu) *}
|
|
</div>
|
|
<div class="card-body">
|
|
<table class="table table-bordered table-striped w-100" id="table-pools">
|
|
<thead>
|
|
<tr>
|
|
<th>{{ i18n("pools.name") }}</th>
|
|
{% if pool.is_all_pool then %}
|
|
<th>{{ i18n("pools.family") }}</th>
|
|
{% end %}
|
|
<th>{{ i18n("pools.members") }}</th>
|
|
<th>{{ i18n("actions") }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div class="card-footer">
|
|
{* ui_utils.render_configuration_footer('pool', _GET["page"]) *}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{# include the modal to add a new pool #}
|
|
{* template_utils.gen("pages/modals/pools/pool_modal.template", {
|
|
modal_type = "add",
|
|
pool = pool,
|
|
}) *}
|
|
{# include the modal to edit an existing pool #}
|
|
{* template_utils.gen("pages/modals/pools/pool_modal.template", {
|
|
modal_type = "edit",
|
|
pool = pool,
|
|
}) *}
|
|
{* template_utils.gen("pages/modals/pools/remove_pool.template", {
|
|
modal_type = "remove",
|
|
pool = pool
|
|
}) *}
|
|
|
|
{* template_utils.gen("pages/modals/factory-reset.template", {
|
|
body = i18n("endpoint_notifications.factory_reset.body", { reset_element = i18n("pools.pools") }),
|
|
title = i18n("endpoint_notifications.factory_reset.title", { reset_element = i18n("pools.pools") })
|
|
}) *}
|
|
|
|
|
|
<script type="text/javascript">
|
|
|
|
let addCsrf = "{{ ntop.getRandomCSRFValue() }}";
|
|
let editCsrf = "{{ ntop.getRandomCSRFValue() }}";
|
|
let removeCsrf = "{{ ntop.getRandomCSRFValue() }}";
|
|
|
|
const all_members = {* json.encode(pool.all_members) *};
|
|
const endpoints = {* json.encode(pool.endpoints) *};
|
|
const poolType = "{{ pool.name }}";
|
|
|
|
const IS_ALL_POOL = {{ pool.is_all_pool }};
|
|
const DEFAULT_POOL_ID = {{ (pool.instance.DEFAULT_POOL_ID or 0) }};
|
|
const IS_NEDGE = {{ is_nedge or false }};
|
|
|
|
Object.freeze(all_members);
|
|
Object.freeze(endpoints);
|
|
|
|
i18n_ext.warning = "{{ i18n('warning', {}) }}";
|
|
i18n_ext.showing_x_to_y_rows = "{{ i18n('showing_x_to_y_rows', {x='_START_', y='_END_', tot='_TOTAL_'}) }}";
|
|
i18n_ext.search = "{{ i18n('search') }}";
|
|
i18n_ext.delete = "{{ i18n('delete') }}";
|
|
i18n_ext.edit = "{{ i18n('edit') }}";
|
|
i18n_ext.all = "{{ i18n('all') }}";
|
|
i18n_ext.used_by = "{{ i18n('used_by') }}";
|
|
i18n_ext.others = "{{ i18n('others') }}";
|
|
i18n_ext.other = "{{ i18n('other') }}";
|
|
i18n_ext.and = "{{ i18n('and') }}";
|
|
i18n_ext.manage = "{{ i18n('host_pools.manage_pool') }}"
|
|
i18n_ext.unbounded_members = "{{ i18n('pools.unbounded_members') }}";
|
|
|
|
i18n_ext.poolFamilies = {* json.encode(pool.pool_families) *};
|
|
|
|
i18n_ext.rest = {
|
|
INVALID_HOST: "{{ i18n('rest_consts.INVALID_HOST') }}",
|
|
INVALID_ARGUMENTS: "{{ i18n('rest_consts.INVALID_ARGUMENTS') }}",
|
|
NOT_GRANTED: "{{ i18n('rest_consts.NOT_GRANTED') }}",
|
|
ADD_POOL_FAILED: "{{ i18n('rest_consts.ADD_POOL_FAILED') }}",
|
|
EDIT_POOL_FAILED: "{{ i18n('rest_consts.EDIT_POOL_FAILED') }}",
|
|
DELETE_POOL_FAILED: "{{ i18n('rest_consts.DELETE_POOL_FAILED') }}",
|
|
};
|
|
|
|
|
|
const MAX_RECIPIENTS_TO_SHOW = 10;
|
|
|
|
const renableDisabledOptions = (selector, members) => {
|
|
members.forEach(m => {
|
|
|
|
if (!all_members || !all_members[m]) return m;
|
|
// if the member name is not defined the use the member's key
|
|
const name = all_members[m].name || m;
|
|
|
|
$(`${selector} option[value='${m}']`)
|
|
.removeAttr("disabled").removeAttr("data-pool-id").text(name);
|
|
});
|
|
$(`${selector}`).selectpicker('refresh');
|
|
}
|
|
|
|
const markUsedOptions = (selector, members, poolName, poolId) => {
|
|
members.forEach(m => {
|
|
$(`${selector} option[value='${m}']`)
|
|
.attr("disabled", "disabled")
|
|
.attr("data-pool-id", poolId)
|
|
.text(`${all_members[m].name || m} (${poolName})`);
|
|
});
|
|
$(`${selector}`).selectpicker('refresh');
|
|
}
|
|
|
|
const makeDataTableColumns = () => {
|
|
|
|
const columns = [
|
|
{
|
|
data: 'name',
|
|
width: "10%",
|
|
render: function(name, type, pool) {
|
|
|
|
if (type == "display" && pool.pool_id == DEFAULT_POOL_ID) {
|
|
return `<i>${name}</i>`;
|
|
}
|
|
|
|
return name;
|
|
}
|
|
},
|
|
{
|
|
data: null,
|
|
orderable: false,
|
|
width: "40%",
|
|
render: function(data, type, row) {
|
|
|
|
/* if it's the default pool then show an unbounded members message */
|
|
if (type == "display" && row.pool_id == DEFAULT_POOL_ID) return i18n_ext.unbounded_members;
|
|
|
|
if (type == "display" && row.members.length == 0) return "";
|
|
// show only the first 10 members, append some dots
|
|
// if the members are more than 10
|
|
const memberNames = row.members.map((memberId) => {
|
|
|
|
const member = row.member_details[memberId];
|
|
if (member.name == undefined && member.hostkey == undefined) return memberId;
|
|
if (!all_members || !all_members[memberId]) {
|
|
return member.hostkey || member.name;
|
|
}
|
|
|
|
if (all_members[memberId].name == undefined) return memberId;
|
|
return all_members[memberId].name;
|
|
});
|
|
|
|
if (type == "display") {
|
|
return NtopUtils.arrayToListString(memberNames, MAX_RECIPIENTS_TO_SHOW);
|
|
}
|
|
}
|
|
},
|
|
{
|
|
data: null, targets: -1, className: 'text-center',
|
|
width: "10%",
|
|
render: function(_, type, pool) {
|
|
let changable_pool = true;
|
|
|
|
for(const pool_names of unchangable_pool_names) {
|
|
if(pool_names === pool.name) {
|
|
changable_pool = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* disable actions for ALL_POOL page */
|
|
if (IS_ALL_POOL) return;
|
|
|
|
const buttons = [
|
|
{ class: 'btn-info', icon: 'fa-edit', modal: '#edit-pool', title: `${i18n_ext.edit}` },
|
|
{ class: `btn-danger ${((pool.pool_id == DEFAULT_POOL_ID || IS_NEDGE) || !changable_pool) ? 'disabled' : '' }`, icon: 'fa-trash', modal: '#remove-pool', title: `${i18n_ext.delete}`}
|
|
];
|
|
|
|
if (poolType == "host") {
|
|
buttons.unshift(
|
|
{
|
|
class: `btn-info ${(pool.pool_id == DEFAULT_POOL_ID) ? 'disabled' : '' }`,
|
|
icon: 'fa-layer-group',
|
|
href: `${http_prefix}/lua/admin/manage_host_members.lua?pool=${pool.pool_id}`,
|
|
title: `${i18n_ext.manage}`
|
|
}
|
|
);
|
|
}
|
|
|
|
return DataTableUtils.createActionButtons(buttons);
|
|
}
|
|
}
|
|
];
|
|
|
|
// if the ALL_POOL page is selected then show the pool family
|
|
if (IS_ALL_POOL) {
|
|
columns.splice(1, 0, {
|
|
data: 'key',
|
|
render: (key, type, pool) => {
|
|
if (type == "display") return i18n_ext.poolFamilies[key];
|
|
return key;
|
|
}
|
|
});
|
|
}
|
|
|
|
return columns;
|
|
}
|
|
|
|
let dtConfig = DataTableUtils.getStdDatatableConfig( [
|
|
{
|
|
text: '<i class="fas fa-plus"></i>',
|
|
enabled: !IS_ALL_POOL && !IS_NEDGE,
|
|
action: () => { $(`#add-pool`).modal('show'); }
|
|
},
|
|
{
|
|
text: '<i class="fas fa-sync"></i>',
|
|
enabled: !IS_ALL_POOL,
|
|
action: function(e, dt, node, config) {
|
|
$poolTable.ajax.reload();
|
|
}
|
|
}
|
|
]);
|
|
|
|
dtConfig = DataTableUtils.setAjaxConfig(dtConfig, endpoints.get_all_pools, 'rsp');
|
|
dtConfig = DataTableUtils.extendConfig(dtConfig, {
|
|
stateSave: true,
|
|
columns: makeDataTableColumns(),
|
|
initComplete: function(settings, json) {
|
|
|
|
const tableAPI = settings.oInstance.api();
|
|
// when the data has been fetched check if the url has a pool params
|
|
DataTableUtils.openEditModalByQuery({
|
|
paramName: 'pool_id',
|
|
datatableInstance: tableAPI,
|
|
modalHandler: $editModalHandler,
|
|
});
|
|
}
|
|
});
|
|
|
|
const $poolTable = $(`#table-pools`).DataTable(dtConfig);
|
|
DataTableUtils.addToggleColumnsDropdown($poolTable);
|
|
|
|
$(`#add-pool form`).modalHandler({
|
|
method: 'post',
|
|
csrf: addCsrf,
|
|
resetAfterSubmit: false,
|
|
endpoint: endpoints.add_pool,
|
|
beforeSumbit: function() {
|
|
const members = $(`#add-pool form select[name='members']`).val() || [];
|
|
|
|
$(`#add-modal-feedback`).hide();
|
|
|
|
return {
|
|
pool_name: $(`#add-pool form input[name='name']`).val().trim(),
|
|
pool_members: members.join(',')
|
|
};
|
|
},
|
|
onSubmitSuccess: function (response, textStatus, modalHandler) {
|
|
if (response.rc < 0) {
|
|
$(`#add-modal-feedback`).html(response.rc_str_hr).fadeIn();
|
|
return;
|
|
}
|
|
|
|
const poolName = $(`#add-pool form input[name='name']`).val().trim();
|
|
|
|
if (poolType != "host") {
|
|
// get the new members array
|
|
const members = $(`#add-pool form select[name='members']`).val();
|
|
// mark select entries with the new pool created
|
|
markUsedOptions(`#add-pool form select[name='members']`, members, poolName, response.rsp.pool_id);
|
|
markUsedOptions(`#edit-pool form select[name='members']`, members, poolName, response.rsp.pool_id);
|
|
}
|
|
|
|
$poolTable.ajax.reload();
|
|
$(`#add-pool`).modal('hide');
|
|
}
|
|
}).invokeModalInit();
|
|
|
|
const $editModalHandler = $(`#edit-pool form`).modalHandler({
|
|
method: 'post',
|
|
csrf: editCsrf,
|
|
endpoint: endpoints.edit_pool,
|
|
resetAfterSubmit: false,
|
|
onModalInit: (pool) => {
|
|
|
|
let changable_pool = true;
|
|
|
|
for(const pool_names of unchangable_pool_names) {
|
|
if(pool_names === pool.name) {
|
|
changable_pool = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// disable pool name field if we are editing the default pool
|
|
// also hide the members multiselect
|
|
if (pool.pool_id == DEFAULT_POOL_ID || !changable_pool) {
|
|
$(`#edit-pool form input[name='name']`).attr("readonly", "true");
|
|
$(`#edit-pool .members-container`).hide();
|
|
}
|
|
else {
|
|
$(`#edit-pool form input[name='name']`).removeAttr("readonly");
|
|
$(`#edit-pool .members-container`).show();
|
|
}
|
|
|
|
// disable all the options whose have a data-pool-id attribute
|
|
$(`#edit-pool form select[name='members'] option[data-pool-id]`).attr("disabled", "disabled");
|
|
|
|
// enable only the option used by the pool
|
|
$(`#edit-pool form select[name='members'] option`).each(function() {
|
|
|
|
const id = $(this).val();
|
|
// if the id belong to the pool then remove the disabled attribute
|
|
if (pool.members.indexOf(id) > -1) {
|
|
$(this).removeAttr("disabled");
|
|
}
|
|
});
|
|
|
|
$(`#edit-pool-name-input`).off('keyup').on('keyup', function() {
|
|
const name = $(this).val()
|
|
$(`[data-pool-id='${pool.pool_id}']`).each(function() {
|
|
const m = $(this).val();
|
|
$(this).text(`${all_members[m].name || m} (${name})`);
|
|
});
|
|
});
|
|
|
|
// load the modal with the pool data
|
|
$(`#edit-pool form input[name='name']`).val(pool.name);
|
|
$(`#edit-pool form select[name='members']`).val(pool.members);
|
|
$(`#edit-pool form select[name='members']`).selectpicker('refresh');
|
|
|
|
if (poolType == "host") {
|
|
const href = $(`#edit-link`).attr('href').replace(/pool\=[0-9]+/, `pool=${pool.pool_id}`);
|
|
$(`#edit-link`).attr('href', href);
|
|
}
|
|
},
|
|
beforeSumbit: (pool) => {
|
|
|
|
const members = $(`#edit-pool form select[name='members']`).val() || [];
|
|
|
|
const data = {
|
|
pool: pool.pool_id,
|
|
pool_name: $(`#edit-pool form input[name='name']`).val().trim()
|
|
};
|
|
|
|
if (poolType != "host") {
|
|
data.pool_members = members.join(',');
|
|
}
|
|
else {
|
|
data.pool_members = pool.members.join(',');
|
|
}
|
|
|
|
return data;
|
|
},
|
|
onSubmitSuccess: (response, dataSent, modalHandler) => {
|
|
|
|
const oldPoolData = modalHandler.data;
|
|
|
|
if (response.rc < 0) {
|
|
$(`#edit-modal-feedback`).html(response.rc_str_hr).fadeIn();
|
|
return;
|
|
}
|
|
|
|
const newPoolName = $(`#edit-pool form input[name='name']`).val();
|
|
// update the pool name inside the selects if changed
|
|
if (newPoolName != oldPoolData.name) {
|
|
$(`option[data-pool-id='${oldPoolData.pool_id}']`).each(function() {
|
|
const value = $(this).val();
|
|
if (poolType != "host")
|
|
$(this).text(`${all_members[value].name || value} (${i18n_ext.used_by} ${newPoolName})`)
|
|
});
|
|
}
|
|
|
|
// the host pool modals don't have any members
|
|
if (poolType != "host" && oldPoolData.pool_id != DEFAULT_POOL_ID) {
|
|
// get the newMembers and the oldMembers and create two subset of them
|
|
const oldMembers = oldPoolData.members;
|
|
const newMembers = $(`#edit-pool form select[name='members']`).val();
|
|
// this subset contains the removed members from the pool
|
|
const oldToRenable = oldMembers.filter((m1) => !newMembers.find(m2 => m1 == m2));
|
|
// this subset contains the new members added to the pool
|
|
const newToMark = newMembers.filter((m1) => !oldMembers.find(m2 => m1 == m2));
|
|
|
|
renableDisabledOptions(`#add-pool form select[name='members']`, oldToRenable);
|
|
renableDisabledOptions(`#edit-pool form select[name='members']`, oldToRenable);
|
|
|
|
markUsedOptions(`#add-pool form select[name='members']`, newToMark, newPoolName, oldPoolData.pool_id);
|
|
markUsedOptions(`#edit-pool form select[name='members']`, newToMark, newPoolName, oldPoolData.pool_id);
|
|
}
|
|
|
|
// clean the form and reload the table
|
|
$poolTable.ajax.reload();
|
|
$(`#edit-pool`).modal('hide');
|
|
}
|
|
});
|
|
|
|
const $removeModalHandler = $(`#remove-pool form`).modalHandler({
|
|
method: 'post',
|
|
csrf: removeCsrf,
|
|
endpoint: endpoints.delete_pool,
|
|
resetAfterSubmit: false,
|
|
onModalInit: (pool) => {
|
|
$(`#remove-pool form button[type='submit']`).removeAttr("disabled");
|
|
$(`.delete-pool-name`).text(pool.name);
|
|
},
|
|
beforeSumbit: (pool) => {
|
|
return { pool: pool.pool_id };
|
|
},
|
|
onSubmitSuccess: (response, textStatus, modalHandler) => {
|
|
|
|
const oldPoolData = modalHandler.data;
|
|
|
|
if (response.rc < 0) {
|
|
$(`#remove-modal-feedback`).html(response.rc_str_hr).fadeIn();
|
|
return;
|
|
}
|
|
|
|
// renable the members removed from the pool inside the modal
|
|
renableDisabledOptions(`#add-pool form select[name='members']`, oldPoolData.members);
|
|
renableDisabledOptions(`#edit-pool form select[name='members']`, oldPoolData.members);
|
|
|
|
$poolTable.ajax.reload();
|
|
$(`#remove-pool`).modal('hide');
|
|
}
|
|
});
|
|
|
|
$(`#table-pools`).on('click', `a[href='#edit-pool']`, function (e) {
|
|
const selectedPool = $poolTable.row($(this).parent().parent().parent().parent()).data();
|
|
$editModalHandler.invokeModalInit(selectedPool);
|
|
$(`#edit-pool`).modal('show')
|
|
});
|
|
|
|
$(`#table-pools`).on('click', `a[href='#remove-pool']`, function (e) {
|
|
const selectedPool = $poolTable.row($(this).parent().parent().parent().parent()).data();
|
|
$removeModalHandler.invokeModalInit(selectedPool);
|
|
$(`#remove-pool`).modal('show')
|
|
});
|
|
|
|
</script>
|