You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2242 lines
59 KiB
2242 lines
59 KiB
1 year ago
|
/*
|
||
|
** Zabbix
|
||
|
** Copyright (C) 2001-2023 Zabbix SIA
|
||
|
**
|
||
|
** This program is free software; you can redistribute it and/or modify
|
||
|
** it under the terms of the GNU General Public License as published by
|
||
|
** the Free Software Foundation; either version 2 of the License, or
|
||
|
** (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program; if not, write to the Free Software
|
||
|
** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||
|
**/
|
||
|
|
||
|
|
||
|
const ZBX_STYLE_BTN_DASHBOARD_PAGE_PROPERTIES = 'btn-dashboard-page-properties';
|
||
|
const ZBX_STYLE_DASHBOARD_IS_MULTIPAGE = 'dashboard-is-multipage';
|
||
|
const ZBX_STYLE_DASHBOARD_IS_EDIT_MODE = 'dashboard-is-edit-mode';
|
||
|
const ZBX_STYLE_DASHBOARD_NAVIGATION_IS_SCROLLABLE = 'is-scrollable';
|
||
|
const ZBX_STYLE_DASHBOARD_SELECTED_TAB = 'selected-tab';
|
||
|
|
||
|
const DASHBOARD_STATE_INITIAL = 'initial';
|
||
|
const DASHBOARD_STATE_ACTIVE = 'active';
|
||
|
|
||
|
const DASHBOARD_CLIPBOARD_TYPE_WIDGET = 'widget';
|
||
|
const DASHBOARD_CLIPBOARD_TYPE_DASHBOARD_PAGE = 'dashboard-page';
|
||
|
|
||
|
const DASHBOARD_EVENT_BUSY = 'dashboard-busy';
|
||
|
const DASHBOARD_EVENT_IDLE = 'dashboard-idle';
|
||
|
const DASHBOARD_EVENT_EDIT = 'dashboard-edit';
|
||
|
const DASHBOARD_EVENT_APPLY_PROPERTIES = 'dashboard-apply-properties';
|
||
|
const DASHBOARD_EVENT_CONFIGURATION_OUTDATED = 'dashboard-configuration-outdated';
|
||
|
|
||
|
class CDashboard {
|
||
|
|
||
|
constructor(target, {
|
||
|
containers,
|
||
|
buttons,
|
||
|
data,
|
||
|
max_dashboard_pages,
|
||
|
cell_width,
|
||
|
cell_height,
|
||
|
max_columns,
|
||
|
max_rows,
|
||
|
widget_min_rows,
|
||
|
widget_max_rows,
|
||
|
widget_defaults,
|
||
|
widget_last_type = null,
|
||
|
configuration_hash = null,
|
||
|
is_editable,
|
||
|
is_edit_mode,
|
||
|
can_edit_dashboards,
|
||
|
is_kiosk_mode,
|
||
|
time_period,
|
||
|
dynamic_hostid,
|
||
|
csrf_token = null
|
||
|
}) {
|
||
|
this._target = target;
|
||
|
|
||
|
this._containers = {
|
||
|
grid: containers.grid,
|
||
|
navigation: containers.navigation,
|
||
|
navigation_tabs: containers.navigation_tabs
|
||
|
}
|
||
|
|
||
|
this._buttons = {
|
||
|
previous_page: buttons.previous_page,
|
||
|
next_page: buttons.next_page,
|
||
|
slideshow: buttons.slideshow
|
||
|
};
|
||
|
|
||
|
this._data = {
|
||
|
dashboardid: data.dashboardid,
|
||
|
name: data.name,
|
||
|
userid: data.userid,
|
||
|
templateid: data.templateid,
|
||
|
display_period: data.display_period,
|
||
|
auto_start: data.auto_start
|
||
|
};
|
||
|
|
||
|
this._max_dashboard_pages = max_dashboard_pages;
|
||
|
this._cell_width = cell_width;
|
||
|
this._cell_height = cell_height;
|
||
|
this._max_columns = max_columns;
|
||
|
this._max_rows = max_rows;
|
||
|
this._widget_min_rows = widget_min_rows;
|
||
|
this._widget_max_rows = widget_max_rows;
|
||
|
this._widget_defaults = {...widget_defaults};
|
||
|
this._widget_last_type = widget_last_type;
|
||
|
this._configuration_hash = configuration_hash;
|
||
|
this._is_editable = is_editable;
|
||
|
this._is_edit_mode = is_edit_mode;
|
||
|
this._can_edit_dashboards = can_edit_dashboards;
|
||
|
this._is_kiosk_mode = is_kiosk_mode;
|
||
|
this._time_period = time_period;
|
||
|
this._dynamic_hostid = dynamic_hostid;
|
||
|
this._csrf_token = csrf_token;
|
||
|
|
||
|
this._init();
|
||
|
this._registerEvents();
|
||
|
}
|
||
|
|
||
|
_init() {
|
||
|
this._state = DASHBOARD_STATE_INITIAL;
|
||
|
|
||
|
this._dashboard_pages = new Map();
|
||
|
this._selected_dashboard_page = null;
|
||
|
|
||
|
this._busy_conditions = new Set();
|
||
|
|
||
|
this._async_timeout_ms = 50;
|
||
|
|
||
|
this._unique_id_index = 0;
|
||
|
|
||
|
this._new_widget_dashboard_page = null;
|
||
|
this._new_widget_pos = null;
|
||
|
this._new_widget_pos_reserved = null;
|
||
|
|
||
|
this._warning_message_box = null;
|
||
|
|
||
|
this._reserve_header_lines = 0;
|
||
|
this._reserve_header_lines_timeout_id = null;
|
||
|
this._is_edit_widget_properties_cancel_subscribed = false;
|
||
|
|
||
|
this._header_lines_steady_period = 2000;
|
||
|
|
||
|
this._slideshow_steady_period = 5000;
|
||
|
this._slideshow_switch_time = null;
|
||
|
this._slideshow_timeout_id = null;
|
||
|
|
||
|
this._configuration_check_period = 60000;
|
||
|
this._configuration_check_steady_period = 2000;
|
||
|
this._configuration_check_time = null;
|
||
|
this._configuration_check_timeout_id = null;
|
||
|
|
||
|
this._is_unsaved = false;
|
||
|
|
||
|
if (!this._is_kiosk_mode) {
|
||
|
const sortable = document.createElement('div');
|
||
|
|
||
|
this._containers.navigation_tabs.appendChild(sortable);
|
||
|
|
||
|
this._tabs = new CSortable(sortable, {
|
||
|
is_vertical: false,
|
||
|
is_sorting_enabled: this._is_edit_mode
|
||
|
});
|
||
|
|
||
|
this._tabs_dashboard_pages = new Map();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Logical state control methods.
|
||
|
|
||
|
activate() {
|
||
|
if (this._dashboard_pages.size == 0) {
|
||
|
throw new Error('Cannot activate dashboard without dashboard pages.');
|
||
|
}
|
||
|
|
||
|
this._state = DASHBOARD_STATE_ACTIVE;
|
||
|
|
||
|
this._activateEvents();
|
||
|
|
||
|
this._announceWidgets();
|
||
|
|
||
|
const dashboard_page = this._getInitialDashboardPage();
|
||
|
|
||
|
this._selectDashboardPage(dashboard_page);
|
||
|
|
||
|
if (this._is_edit_mode) {
|
||
|
this._target.classList.add(ZBX_STYLE_DASHBOARD_IS_EDIT_MODE);
|
||
|
}
|
||
|
|
||
|
if (!this._is_edit_mode) {
|
||
|
this._startConfigurationChecker();
|
||
|
|
||
|
if (this._data.auto_start == 1 && this._dashboard_pages.size > 1) {
|
||
|
this._startSlideshow();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// External events management methods.
|
||
|
|
||
|
isEditMode() {
|
||
|
return this._is_edit_mode;
|
||
|
}
|
||
|
|
||
|
setEditMode({is_internal_call = false} = {}) {
|
||
|
this._is_edit_mode = true;
|
||
|
|
||
|
for (const dashboard_page of this._dashboard_pages.keys()) {
|
||
|
if (!dashboard_page.isEditMode()) {
|
||
|
dashboard_page.setEditMode();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!this._is_kiosk_mode) {
|
||
|
this._tabs.enableSorting();
|
||
|
}
|
||
|
|
||
|
this._stopConfigurationChecker();
|
||
|
this._stopSlideshow();
|
||
|
|
||
|
this._target.classList.add(ZBX_STYLE_DASHBOARD_IS_EDIT_MODE);
|
||
|
|
||
|
if (is_internal_call) {
|
||
|
this.fire(DASHBOARD_EVENT_EDIT);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
setDynamicHost(dynamic_hostid) {
|
||
|
this._dynamic_hostid = dynamic_hostid;
|
||
|
|
||
|
for (const dashboard_page of this._dashboard_pages.keys()) {
|
||
|
dashboard_page.setDynamicHost(this._dynamic_hostid);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_startSlideshow() {
|
||
|
if (this._slideshow_timeout_id !== null) {
|
||
|
clearTimeout(this._slideshow_timeout_id);
|
||
|
}
|
||
|
|
||
|
if (this._buttons.slideshow !== null) {
|
||
|
if (this._is_kiosk_mode) {
|
||
|
this._buttons.slideshow.classList.remove(ZBX_ICON_PLAY);
|
||
|
this._buttons.slideshow.classList.add(ZBX_ICON_PAUSE);
|
||
|
}
|
||
|
|
||
|
this._buttons.slideshow.classList.remove('slideshow-state-stopped');
|
||
|
this._buttons.slideshow.classList.add('slideshow-state-started');
|
||
|
|
||
|
if (this._buttons.slideshow.title !== '') {
|
||
|
this._buttons.slideshow.title = t('Stop slideshow');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let timeout_ms = this._selected_dashboard_page.getDisplayPeriod() * 1000;
|
||
|
|
||
|
if (timeout_ms == 0) {
|
||
|
timeout_ms = this._data.display_period * 1000;
|
||
|
}
|
||
|
|
||
|
this._slideshow_switch_time = Date.now() + timeout_ms;
|
||
|
this._slideshow_timeout_id = setTimeout(() => this._switchSlideshow(), timeout_ms);
|
||
|
}
|
||
|
|
||
|
_stopSlideshow() {
|
||
|
if (this._slideshow_timeout_id === null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (this._buttons.slideshow !== null) {
|
||
|
if (this._is_kiosk_mode) {
|
||
|
this._buttons.slideshow.classList.remove(ZBX_ICON_PAUSE);
|
||
|
this._buttons.slideshow.classList.add(ZBX_ICON_PLAY);
|
||
|
}
|
||
|
|
||
|
this._buttons.slideshow.classList.remove('slideshow-state-started');
|
||
|
this._buttons.slideshow.classList.add('slideshow-state-stopped');
|
||
|
|
||
|
if (this._buttons.slideshow.title !== '') {
|
||
|
this._buttons.slideshow.title = t('Start slideshow');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
clearTimeout(this._slideshow_timeout_id);
|
||
|
|
||
|
this._slideshow_switch_time = null;
|
||
|
this._slideshow_timeout_id = null;
|
||
|
}
|
||
|
|
||
|
_switchSlideshow() {
|
||
|
this._slideshow_timeout_id = null;
|
||
|
|
||
|
if (this._isUserInteracting()) {
|
||
|
this._slideshow_switch_time = Date.now() + this._slideshow_steady_period;
|
||
|
this._slideshow_timeout_id = setTimeout(() => this._switchSlideshow(), this._slideshow_steady_period);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const dashboard_pages = [...this._dashboard_pages.keys()];
|
||
|
const dashboard_page_index = dashboard_pages.indexOf(this._selected_dashboard_page);
|
||
|
|
||
|
this._selectDashboardPage(
|
||
|
dashboard_pages[dashboard_page_index < dashboard_pages.length - 1 ? dashboard_page_index + 1 : 0]
|
||
|
)
|
||
|
|
||
|
let timeout_ms = this._selected_dashboard_page.getDisplayPeriod() * 1000;
|
||
|
|
||
|
if (timeout_ms == 0) {
|
||
|
timeout_ms = this._data.display_period * 1000;
|
||
|
}
|
||
|
|
||
|
this._slideshow_switch_time = Math.max(Date.now() + this._slideshow_steady_period,
|
||
|
this._slideshow_switch_time + timeout_ms
|
||
|
);
|
||
|
|
||
|
this._slideshow_timeout_id = setTimeout(() => this._switchSlideshow(),
|
||
|
this._slideshow_switch_time - Date.now()
|
||
|
);
|
||
|
}
|
||
|
|
||
|
_isSlideshowRunning() {
|
||
|
return this._slideshow_timeout_id !== null;
|
||
|
}
|
||
|
|
||
|
_keepSteadySlideshow() {
|
||
|
if (this._slideshow_timeout_id === null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (this._slideshow_switch_time - Date.now() < this._slideshow_steady_period) {
|
||
|
clearTimeout(this._slideshow_timeout_id);
|
||
|
|
||
|
this._slideshow_switch_time = Date.now() + this._slideshow_steady_period;
|
||
|
|
||
|
this._slideshow_timeout_id = setTimeout(() => this._switchSlideshow(),
|
||
|
this._slideshow_switch_time - Date.now()
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_startConfigurationChecker() {
|
||
|
if (this._configuration_check_timeout_id !== null) {
|
||
|
clearTimeout(this._configuration_check_timeout_id);
|
||
|
}
|
||
|
|
||
|
this._configuration_check_time = Date.now() + this._configuration_check_period;
|
||
|
this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
|
||
|
this._configuration_check_period
|
||
|
);
|
||
|
}
|
||
|
|
||
|
_stopConfigurationChecker() {
|
||
|
if (this._configuration_check_timeout_id === null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
clearTimeout(this._configuration_check_timeout_id);
|
||
|
|
||
|
this._configuration_check_time = null;
|
||
|
this._configuration_check_timeout_id = null;
|
||
|
}
|
||
|
|
||
|
_checkConfiguration() {
|
||
|
this._configuration_check_timeout_id = null;
|
||
|
|
||
|
if (this._isUserInteracting()) {
|
||
|
this._configuration_check_time = Date.now() + this._configuration_check_steady_period;
|
||
|
this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
|
||
|
this._configuration_check_steady_period
|
||
|
);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const busy_condition = this._createBusyCondition();
|
||
|
|
||
|
Promise.resolve()
|
||
|
.then(() => this._promiseCheckConfiguration())
|
||
|
.catch((exception) => {
|
||
|
console.log('Could not check the dashboard configuration', exception);
|
||
|
})
|
||
|
.finally(() => {
|
||
|
this._configuration_check_time = Math.max(Date.now() + this._configuration_check_steady_period,
|
||
|
this._configuration_check_time + this._configuration_check_period
|
||
|
);
|
||
|
|
||
|
this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
|
||
|
this._configuration_check_time - Date.now()
|
||
|
);
|
||
|
|
||
|
this._deleteBusyCondition(busy_condition);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_promiseCheckConfiguration() {
|
||
|
const curl = new Curl('zabbix.php');
|
||
|
|
||
|
curl.setArgument('action', 'dashboard.config.hash');
|
||
|
|
||
|
return fetch(curl.getUrl(), {
|
||
|
method: 'POST',
|
||
|
headers: {'Content-Type': 'application/json'},
|
||
|
body: JSON.stringify({
|
||
|
templateid: this._data.templateid ?? undefined,
|
||
|
dashboardid: this._data.dashboardid
|
||
|
})
|
||
|
})
|
||
|
.then((response) => response.json())
|
||
|
.then((response) => {
|
||
|
if ('error' in response) {
|
||
|
throw {error: response.error};
|
||
|
}
|
||
|
|
||
|
if (response.configuration_hash !== null && this._configuration_hash !== response.configuration_hash) {
|
||
|
this.fire(DASHBOARD_EVENT_CONFIGURATION_OUTDATED);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_keepSteadyConfigurationChecker() {
|
||
|
if (this._configuration_check_timeout_id === null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (this._configuration_check_time - Date.now() < this._configuration_check_steady_period) {
|
||
|
clearTimeout(this._configuration_check_timeout_id);
|
||
|
|
||
|
this._configuration_check_time = Date.now() + this._configuration_check_steady_period;
|
||
|
|
||
|
this._configuration_check_timeout_id = setTimeout(() => this._checkConfiguration(),
|
||
|
this._configuration_check_time - Date.now()
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_announceWidgets() {
|
||
|
const dashboard_pages = Array.from(this._dashboard_pages.keys());
|
||
|
|
||
|
for (const dashboard_page of dashboard_pages) {
|
||
|
dashboard_page.announceWidgets(dashboard_pages);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_createBusyCondition() {
|
||
|
if (this._busy_conditions.size == 0) {
|
||
|
this.fire(DASHBOARD_EVENT_BUSY);
|
||
|
}
|
||
|
|
||
|
const busy_condition = {};
|
||
|
|
||
|
this._busy_conditions.add(busy_condition);
|
||
|
|
||
|
return busy_condition;
|
||
|
}
|
||
|
|
||
|
_deleteBusyCondition(busy_condition) {
|
||
|
this._busy_conditions.delete(busy_condition);
|
||
|
|
||
|
if (this._busy_conditions.size == 0) {
|
||
|
this.fire(DASHBOARD_EVENT_IDLE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
isUnsaved() {
|
||
|
if (this._is_unsaved) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
for (const dashboard_page of this._dashboard_pages.keys()) {
|
||
|
if (dashboard_page.isUnsaved()) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// Data interface methods.
|
||
|
|
||
|
getData() {
|
||
|
return this._data;
|
||
|
}
|
||
|
|
||
|
addNewDashboardPage() {
|
||
|
if (this._dashboard_pages.size >= this._max_dashboard_pages) {
|
||
|
this._warnDashboardExhausted();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.editDashboardPageProperties();
|
||
|
}
|
||
|
|
||
|
addNewWidget() {
|
||
|
this.editWidgetProperties();
|
||
|
}
|
||
|
|
||
|
addDashboardPage({dashboard_pageid, name, display_period, widgets}) {
|
||
|
const dashboard_page = new CDashboardPage(this._containers.grid, {
|
||
|
data: {
|
||
|
dashboard_pageid,
|
||
|
name,
|
||
|
display_period
|
||
|
},
|
||
|
dashboard: {
|
||
|
templateid: this._data.templateid,
|
||
|
dashboardid: this._data.dashboardid
|
||
|
},
|
||
|
cell_width: this._cell_width,
|
||
|
cell_height: this._cell_height,
|
||
|
max_columns: this._max_columns,
|
||
|
max_rows: this._max_rows,
|
||
|
widget_min_rows: this._widget_min_rows,
|
||
|
widget_max_rows: this._widget_max_rows,
|
||
|
widget_defaults: this._widget_defaults,
|
||
|
is_editable: this._is_editable,
|
||
|
is_edit_mode: this._is_edit_mode,
|
||
|
can_edit_dashboards: this._can_edit_dashboards,
|
||
|
time_period: this._time_period,
|
||
|
dynamic_hostid: this._dynamic_hostid,
|
||
|
csrf_token: this._csrf_token,
|
||
|
unique_id: this._createUniqueId()
|
||
|
});
|
||
|
|
||
|
this._dashboard_pages.set(dashboard_page, {});
|
||
|
|
||
|
for (const widget_data of widgets) {
|
||
|
dashboard_page.addWidget({
|
||
|
...widget_data,
|
||
|
is_new: false,
|
||
|
unique_id: this._createUniqueId()
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (this._state === DASHBOARD_STATE_ACTIVE) {
|
||
|
this._announceWidgets();
|
||
|
}
|
||
|
|
||
|
if (!this._is_kiosk_mode) {
|
||
|
this._addTab(dashboard_page);
|
||
|
}
|
||
|
|
||
|
this._target.classList.toggle(ZBX_STYLE_DASHBOARD_IS_MULTIPAGE, this._dashboard_pages.size > 1);
|
||
|
|
||
|
if (dashboard_pageid === null) {
|
||
|
this._is_unsaved = true;
|
||
|
}
|
||
|
|
||
|
return dashboard_page;
|
||
|
}
|
||
|
|
||
|
deleteDashboardPage(dashboard_page) {
|
||
|
if (this._dashboard_pages.size == 1) {
|
||
|
throw new Error('Cannot delete the last dashboard page.');
|
||
|
}
|
||
|
|
||
|
if (dashboard_page === this._selected_dashboard_page) {
|
||
|
if (this._is_kiosk_mode) {
|
||
|
for (const select_dashboard_page of this._dashboard_pages.keys()) {
|
||
|
if (select_dashboard_page !== dashboard_page) {
|
||
|
this._selectDashboardPage(select_dashboard_page);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
const tabs = [...this._tabs.getList().children];
|
||
|
const tab_index = tabs.indexOf(this._dashboard_pages.get(dashboard_page).tab);
|
||
|
|
||
|
this._selectDashboardPage(
|
||
|
this._tabs_dashboard_pages.get(tabs[tab_index > 0 ? tab_index - 1 : tab_index + 1])
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (dashboard_page.getState() !== DASHBOARD_PAGE_STATE_INITIAL) {
|
||
|
dashboard_page.destroy();
|
||
|
}
|
||
|
|
||
|
if (!this._is_kiosk_mode) {
|
||
|
this._deleteTab(dashboard_page);
|
||
|
}
|
||
|
|
||
|
this._dashboard_pages.delete(dashboard_page);
|
||
|
|
||
|
this._announceWidgets();
|
||
|
|
||
|
this._target.classList.toggle(ZBX_STYLE_DASHBOARD_IS_MULTIPAGE, this._dashboard_pages.size > 1);
|
||
|
|
||
|
this._is_unsaved = true;
|
||
|
}
|
||
|
|
||
|
getDashboardPage(unique_id) {
|
||
|
for (const dashboard_page of this._dashboard_pages.keys()) {
|
||
|
if (dashboard_page.getUniqueId() === unique_id) {
|
||
|
return dashboard_page;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
getDashboardPages() {
|
||
|
return [...this._dashboard_pages.keys()];
|
||
|
}
|
||
|
|
||
|
pasteDashboardPage(new_dashboard_page_data) {
|
||
|
this._clearWarnings();
|
||
|
|
||
|
if (this._dashboard_pages.size >= this._max_dashboard_pages) {
|
||
|
this._warnDashboardExhausted();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const widgets = [];
|
||
|
|
||
|
for (const widget of new_dashboard_page_data.widgets) {
|
||
|
if (widget.type in this._widget_defaults) {
|
||
|
widgets.push(widget);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const busy_condition = this._createBusyCondition();
|
||
|
|
||
|
return Promise.resolve()
|
||
|
.then(() => this._promiseDashboardWidgetsSanitize(widgets))
|
||
|
.then((response) => {
|
||
|
if (this._dashboard_pages.size >= this._max_dashboard_pages) {
|
||
|
this._warnDashboardExhausted();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (response.widgets.length < new_dashboard_page_data.widgets.length) {
|
||
|
this._warn(t('Inaccessible widgets were not pasted.'));
|
||
|
}
|
||
|
|
||
|
const sane_widgets = [];
|
||
|
|
||
|
for (let i = 0; i < response.widgets.length; i++) {
|
||
|
if (response.widgets[i] !== null) {
|
||
|
sane_widgets.push({
|
||
|
...widgets[i],
|
||
|
fields: response.widgets[i].fields
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const used_references = this._getUsedReferences();
|
||
|
const reference_substitution = new Map();
|
||
|
|
||
|
for (const widget of sane_widgets) {
|
||
|
const widget_class = eval(this._widget_defaults[widget.type].js_class);
|
||
|
|
||
|
if (widget_class.hasReferenceField()) {
|
||
|
const old_reference = widget.fields.reference;
|
||
|
const new_reference = this._createReference({used_references});
|
||
|
|
||
|
widget.fields.reference = new_reference;
|
||
|
|
||
|
used_references.add(new_reference);
|
||
|
reference_substitution.set(old_reference, new_reference);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (const widget of sane_widgets) {
|
||
|
const widget_class = eval(this._widget_defaults[widget.type].js_class);
|
||
|
|
||
|
for (const reference_field of widget_class.getForeignReferenceFields()) {
|
||
|
const old_reference = widget.fields[reference_field];
|
||
|
|
||
|
if (reference_substitution.has(old_reference)) {
|
||
|
widget.fields[reference_field] = reference_substitution.get(old_reference);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const dashboard_page = this.addDashboardPage({
|
||
|
dashboard_pageid: null,
|
||
|
name: new_dashboard_page_data.name,
|
||
|
display_period: new_dashboard_page_data.display_period,
|
||
|
widgets: sane_widgets
|
||
|
});
|
||
|
|
||
|
this._selectDashboardPage(dashboard_page, {is_async: true});
|
||
|
})
|
||
|
.catch((exception) => {
|
||
|
clearMessages();
|
||
|
|
||
|
let title;
|
||
|
let messages = [];
|
||
|
|
||
|
if (typeof exception === 'object' && 'error' in exception) {
|
||
|
title = exception.error.title;
|
||
|
messages = exception.error.messages;
|
||
|
}
|
||
|
else {
|
||
|
title = t('Failed to paste dashboard page.');
|
||
|
}
|
||
|
|
||
|
const message_box = makeMessageBox('bad', messages, title);
|
||
|
|
||
|
addMessage(message_box);
|
||
|
})
|
||
|
.finally(() => this._deleteBusyCondition(busy_condition))
|
||
|
}
|
||
|
|
||
|
pasteWidget(new_widget_data, {widget = null, new_widget_pos = null} = {}) {
|
||
|
this._clearWarnings();
|
||
|
|
||
|
if (!(new_widget_data.type in this._widget_defaults)) {
|
||
|
this._warn(t('Cannot paste inaccessible widget.'));
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const dashboard_page = this._selected_dashboard_page;
|
||
|
|
||
|
if (widget !== null) {
|
||
|
new_widget_pos = widget.getPos();
|
||
|
}
|
||
|
else if (new_widget_pos !== null) {
|
||
|
new_widget_pos = {
|
||
|
...new_widget_data.pos,
|
||
|
...new_widget_pos
|
||
|
};
|
||
|
|
||
|
new_widget_pos.width = Math.min(new_widget_pos.width, this._max_columns - new_widget_pos.x);
|
||
|
new_widget_pos.height = Math.min(new_widget_pos.height, this._max_rows - new_widget_pos.y);
|
||
|
new_widget_pos = dashboard_page.accommodatePos(new_widget_pos);
|
||
|
}
|
||
|
else {
|
||
|
new_widget_pos = dashboard_page.findFreePos(new_widget_data.pos);
|
||
|
}
|
||
|
|
||
|
if (new_widget_pos === null) {
|
||
|
this._warnDashboardPageExhausted();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
let old_widget_data = null;
|
||
|
|
||
|
if (widget !== null) {
|
||
|
old_widget_data = widget.getDataCopy({is_single_copy: false});
|
||
|
|
||
|
dashboard_page.deleteWidget(widget, {is_batch_mode: true});
|
||
|
}
|
||
|
|
||
|
const new_widget_class = eval(this._widget_defaults[new_widget_data.type].js_class);
|
||
|
|
||
|
if (new_widget_class.hasReferenceField()) {
|
||
|
new_widget_data.fields.reference = this._createReference();
|
||
|
}
|
||
|
|
||
|
let references = [];
|
||
|
|
||
|
for (const widget of dashboard_page.getWidgets()) {
|
||
|
if (widget.constructor.hasReferenceField()) {
|
||
|
references.push(widget.getFields()['reference']);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (const reference_field of new_widget_class.getForeignReferenceFields()) {
|
||
|
if (reference_field in new_widget_data.fields
|
||
|
&& !references.includes(new_widget_data.fields[reference_field])) {
|
||
|
new_widget_data.fields[reference_field] = '';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const paste_placeholder_widget = dashboard_page.addPastePlaceholderWidget({
|
||
|
type: new_widget_data.type,
|
||
|
name: new_widget_data.name,
|
||
|
view_mode: new_widget_data.view_mode,
|
||
|
pos: new_widget_pos,
|
||
|
unique_id: this._createUniqueId()
|
||
|
});
|
||
|
|
||
|
dashboard_page.resetWidgetPlaceholder();
|
||
|
|
||
|
const busy_condition = this._createBusyCondition();
|
||
|
|
||
|
dashboard_page.promiseScrollIntoView(new_widget_pos)
|
||
|
.then(() => this._promiseDashboardWidgetsSanitize([new_widget_data]))
|
||
|
.then((response) => {
|
||
|
if (dashboard_page.getState() === DASHBOARD_PAGE_STATE_DESTROYED) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (response.widgets[0] === null) {
|
||
|
if (widget !== null) {
|
||
|
dashboard_page.replaceWidget(paste_placeholder_widget, {
|
||
|
...old_widget_data,
|
||
|
widgetid: widget.getWidgetId(),
|
||
|
is_new: false,
|
||
|
unique_id: widget.getUniqueId()
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
dashboard_page.deleteWidget(paste_placeholder_widget);
|
||
|
}
|
||
|
|
||
|
this._warn(t('Cannot paste inaccessible widget.'));
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dashboard_page.replaceWidget(paste_placeholder_widget, {
|
||
|
...new_widget_data,
|
||
|
fields: response.widgets[0].fields,
|
||
|
widgetid: null,
|
||
|
pos: new_widget_pos,
|
||
|
is_new: true,
|
||
|
unique_id: this._createUniqueId()
|
||
|
});
|
||
|
})
|
||
|
.catch((exception) => {
|
||
|
dashboard_page.deleteWidget(paste_placeholder_widget);
|
||
|
|
||
|
clearMessages();
|
||
|
|
||
|
let title, messages;
|
||
|
|
||
|
if (typeof exception === 'object' && 'error' in exception) {
|
||
|
title = exception.error.title;
|
||
|
messages = exception.error.messages;
|
||
|
}
|
||
|
else {
|
||
|
title = t('Failed to paste widget.');
|
||
|
}
|
||
|
|
||
|
const message_box = makeMessageBox('bad', messages, title);
|
||
|
|
||
|
addMessage(message_box);
|
||
|
})
|
||
|
.finally(() => this._deleteBusyCondition(busy_condition));
|
||
|
}
|
||
|
|
||
|
_promiseDashboardWidgetsSanitize(widgets_data) {
|
||
|
let request_widgets_data = [];
|
||
|
|
||
|
for (const widget_data of widgets_data) {
|
||
|
request_widgets_data.push({
|
||
|
type: widget_data.type,
|
||
|
fields: widget_data.fields
|
||
|
});
|
||
|
}
|
||
|
|
||
|
const curl = new Curl('zabbix.php');
|
||
|
|
||
|
curl.setArgument('action', 'dashboard.widgets.sanitize');
|
||
|
|
||
|
return fetch(curl.getUrl(), {
|
||
|
method: 'POST',
|
||
|
headers: {'Content-Type': 'application/json'},
|
||
|
body: JSON.stringify({widgets: request_widgets_data})
|
||
|
})
|
||
|
.then((response) => response.json())
|
||
|
.then((response) => {
|
||
|
if ('error' in response) {
|
||
|
throw {error: response.error};
|
||
|
}
|
||
|
|
||
|
return response;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_storeDashboardObject(data) {
|
||
|
localStorage.setItem('dashboard.clipboard', JSON.stringify({
|
||
|
zbx_session_name: window.ZBX_SESSION_NAME,
|
||
|
data
|
||
|
}));
|
||
|
}
|
||
|
|
||
|
_getStoredDashboardObject() {
|
||
|
let clipboard = localStorage.getItem('dashboard.clipboard');
|
||
|
|
||
|
if (clipboard === null) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
clipboard = JSON.parse(clipboard);
|
||
|
|
||
|
if (clipboard.zbx_session_name !== window.ZBX_SESSION_NAME) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return clipboard.data;
|
||
|
}
|
||
|
|
||
|
_storeWidgetDataCopy(widget_data) {
|
||
|
this._storeDashboardObject({
|
||
|
type: DASHBOARD_CLIPBOARD_TYPE_WIDGET,
|
||
|
data: widget_data
|
||
|
});
|
||
|
}
|
||
|
|
||
|
getStoredWidgetDataCopy() {
|
||
|
const data = this._getStoredDashboardObject();
|
||
|
|
||
|
if (data === null || data.type !== DASHBOARD_CLIPBOARD_TYPE_WIDGET) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
const widget_data = data.data;
|
||
|
|
||
|
if (widget_data.dashboard.templateid !== this._data.templateid) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return widget_data;
|
||
|
}
|
||
|
|
||
|
_storeDashboardPageDataCopy(dashboard_page_data) {
|
||
|
this._storeDashboardObject({
|
||
|
type: DASHBOARD_CLIPBOARD_TYPE_DASHBOARD_PAGE,
|
||
|
data: dashboard_page_data
|
||
|
});
|
||
|
}
|
||
|
|
||
|
getStoredDashboardPageDataCopy() {
|
||
|
const data = this._getStoredDashboardObject();
|
||
|
|
||
|
if (data === null || data.type !== DASHBOARD_CLIPBOARD_TYPE_DASHBOARD_PAGE) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
const dashboard_page_data = data.data;
|
||
|
|
||
|
if (dashboard_page_data.dashboard.templateid !== this._data.templateid) {
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
return dashboard_page_data;
|
||
|
}
|
||
|
|
||
|
_selectDashboardPage(dashboard_page, {is_async = false} = {}) {
|
||
|
if (this._data.templateid === null || !this._is_edit_mode) {
|
||
|
this._setInitialDashboardPage(dashboard_page);
|
||
|
}
|
||
|
|
||
|
if (!this._is_edit_mode) {
|
||
|
this._keepSteadyConfigurationChecker();
|
||
|
|
||
|
if (this._isSlideshowRunning()) {
|
||
|
this._keepSteadySlideshow();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this._promiseSelectDashboardPage(dashboard_page, {is_async})
|
||
|
.then(() => {
|
||
|
if (!this._is_edit_mode) {
|
||
|
this._keepSteadyConfigurationChecker();
|
||
|
|
||
|
if (this._isSlideshowRunning()) {
|
||
|
this._startSlideshow();
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_promiseSelectDashboardPage(dashboard_page, {is_async = false} = {}) {
|
||
|
return new Promise((resolve) => {
|
||
|
if (this._is_kiosk_mode) {
|
||
|
this._doSelectDashboardPage(dashboard_page);
|
||
|
|
||
|
resolve();
|
||
|
}
|
||
|
else {
|
||
|
this._selectTab(dashboard_page);
|
||
|
|
||
|
if (is_async) {
|
||
|
setTimeout(() => {
|
||
|
this._doSelectDashboardPage(dashboard_page);
|
||
|
|
||
|
resolve();
|
||
|
}, this._async_timeout_ms);
|
||
|
}
|
||
|
else {
|
||
|
this._doSelectDashboardPage(dashboard_page);
|
||
|
|
||
|
resolve();
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_doSelectDashboardPage(dashboard_page) {
|
||
|
if (this._selected_dashboard_page !== null) {
|
||
|
this._deactivatePage(this._selected_dashboard_page);
|
||
|
}
|
||
|
|
||
|
this._selected_dashboard_page = dashboard_page;
|
||
|
|
||
|
if (this._selected_dashboard_page.getState() === DASHBOARD_PAGE_STATE_INITIAL) {
|
||
|
this._selected_dashboard_page.start();
|
||
|
}
|
||
|
|
||
|
this._activatePage(this._selected_dashboard_page);
|
||
|
|
||
|
if (this._is_kiosk_mode) {
|
||
|
this._resetHeaderLines();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_activatePage(dashboard_page) {
|
||
|
dashboard_page.activate();
|
||
|
dashboard_page
|
||
|
.on(DASHBOARD_PAGE_EVENT_EDIT, this._events.dashboardPageEdit)
|
||
|
.on(DASHBOARD_PAGE_EVENT_WIDGET_ADD, this._events.dashboardPageWidgetAdd)
|
||
|
.on(DASHBOARD_PAGE_EVENT_WIDGET_ADD_NEW, this._events.dashboardPageWidgetAddNew)
|
||
|
.on(DASHBOARD_PAGE_EVENT_WIDGET_DELETE, this._events.dashboardPageWidgetDelete)
|
||
|
.on(DASHBOARD_PAGE_EVENT_WIDGET_POSITION, this._events.dashboardPageWidgetPosition)
|
||
|
.on(DASHBOARD_PAGE_EVENT_WIDGET_ACTIONS, this._events.dashboardPageWidgetActions)
|
||
|
.on(DASHBOARD_PAGE_EVENT_WIDGET_EDIT, this._events.dashboardPageWidgetEdit)
|
||
|
.on(DASHBOARD_PAGE_EVENT_WIDGET_COPY, this._events.dashboardPageWidgetCopy)
|
||
|
.on(DASHBOARD_PAGE_EVENT_WIDGET_PASTE, this._events.dashboardPageWidgetPaste)
|
||
|
.on(DASHBOARD_PAGE_EVENT_ANNOUNCE_WIDGETS, this._events.dashboardPageAnnounceWidgets);
|
||
|
|
||
|
if (this._is_kiosk_mode) {
|
||
|
dashboard_page.on(DASHBOARD_PAGE_EVENT_RESERVE_HEADER_LINES, this._events.dashboardPageReserveHeaderLines);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_deactivatePage(dashboard_page) {
|
||
|
dashboard_page.deactivate();
|
||
|
dashboard_page
|
||
|
.off(DASHBOARD_PAGE_EVENT_EDIT, this._events.dashboardPageEdit)
|
||
|
.off(DASHBOARD_PAGE_EVENT_WIDGET_ADD, this._events.dashboardPageWidgetAdd)
|
||
|
.off(DASHBOARD_PAGE_EVENT_WIDGET_ADD_NEW, this._events.dashboardPageWidgetAddNew)
|
||
|
.off(DASHBOARD_PAGE_EVENT_WIDGET_DELETE, this._events.dashboardPageWidgetDelete)
|
||
|
.off(DASHBOARD_PAGE_EVENT_WIDGET_POSITION, this._events.dashboardPageWidgetPosition)
|
||
|
.off(DASHBOARD_PAGE_EVENT_WIDGET_ACTIONS, this._events.dashboardPageWidgetActions)
|
||
|
.off(DASHBOARD_PAGE_EVENT_WIDGET_EDIT, this._events.dashboardPageWidgetEdit)
|
||
|
.off(DASHBOARD_PAGE_EVENT_WIDGET_COPY, this._events.dashboardPageWidgetCopy)
|
||
|
.off(DASHBOARD_PAGE_EVENT_WIDGET_PASTE, this._events.dashboardPageWidgetPaste)
|
||
|
.off(DASHBOARD_PAGE_EVENT_ANNOUNCE_WIDGETS, this._events.dashboardPageAnnounceWidgets);
|
||
|
|
||
|
if (this._is_kiosk_mode) {
|
||
|
dashboard_page.off(DASHBOARD_PAGE_EVENT_RESERVE_HEADER_LINES, this._events.dashboardPageReserveHeaderLines);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_setInitialDashboardPage(dashboard_page) {
|
||
|
const dashboard_page_index = this.getDashboardPageIndex(dashboard_page);
|
||
|
|
||
|
const url = new URL(location.href);
|
||
|
|
||
|
if (dashboard_page_index > 0) {
|
||
|
url.searchParams.set('page', dashboard_page_index + 1);
|
||
|
}
|
||
|
else {
|
||
|
url.searchParams.delete('page');
|
||
|
}
|
||
|
|
||
|
history.replaceState(null, null, url);
|
||
|
}
|
||
|
|
||
|
_getInitialDashboardPage() {
|
||
|
const url = new URL(location.href);
|
||
|
|
||
|
if (url.searchParams.has('page')) {
|
||
|
const dashboard_pages = [...this._dashboard_pages.keys()];
|
||
|
const dashboard_page_index = parseInt(url.searchParams.get('page')) - 1;
|
||
|
|
||
|
if (dashboard_page_index in dashboard_pages) {
|
||
|
return dashboard_pages[dashboard_page_index];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this._dashboard_pages.keys().next().value;
|
||
|
}
|
||
|
|
||
|
getSelectedDashboardPage() {
|
||
|
return this._selected_dashboard_page;
|
||
|
}
|
||
|
|
||
|
getDashboardPageIndex(dashboard_page) {
|
||
|
if (this._is_kiosk_mode) {
|
||
|
const dashboard_pages = [...this._dashboard_pages.keys()];
|
||
|
|
||
|
return dashboard_pages.indexOf(dashboard_page);
|
||
|
}
|
||
|
|
||
|
const tabs = [...this._tabs.getList().children];
|
||
|
const data = this._dashboard_pages.get(dashboard_page);
|
||
|
|
||
|
return tabs.indexOf(data.tab);
|
||
|
}
|
||
|
|
||
|
save() {
|
||
|
const data = {
|
||
|
dashboardid: this._data.dashboardid ?? undefined,
|
||
|
name: this._data.name,
|
||
|
userid: this._data.userid ?? undefined,
|
||
|
templateid: this._data.templateid ?? undefined,
|
||
|
display_period: this._data.display_period,
|
||
|
auto_start: this._data.auto_start,
|
||
|
pages: []
|
||
|
};
|
||
|
|
||
|
let dashboard_pages = [];
|
||
|
|
||
|
if (this._is_kiosk_mode) {
|
||
|
for (const dashboard_page of this._dashboard_pages.keys()) {
|
||
|
dashboard_pages.push(dashboard_page);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
for (const tab of this._tabs.getList().children) {
|
||
|
dashboard_pages.push(this._tabs_dashboard_pages.get(tab));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (const dashboard_page of dashboard_pages) {
|
||
|
data.pages.push(dashboard_page.save());
|
||
|
}
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
editProperties() {
|
||
|
const properties = {
|
||
|
template: this._data.templateid !== null ? 1 : undefined,
|
||
|
userid: this._data.templateid === null ? this._data.userid : undefined,
|
||
|
name: this._data.name,
|
||
|
display_period: this._data.display_period,
|
||
|
auto_start: this._data.auto_start
|
||
|
};
|
||
|
|
||
|
PopUp('dashboard.properties.edit', properties, {
|
||
|
dialogueid: 'dashboard_properties',
|
||
|
dialogue_class: 'modal-popup-generic'
|
||
|
});
|
||
|
}
|
||
|
|
||
|
applyProperties() {
|
||
|
const overlay = overlays_stack.getById('dashboard_properties');
|
||
|
const form = overlay.$dialogue.$body[0].querySelector('form');
|
||
|
const properties = getFormFields(form);
|
||
|
|
||
|
overlay.setLoading();
|
||
|
|
||
|
const busy_condition = this._createBusyCondition();
|
||
|
|
||
|
return new Promise((resolve) => resolve(this._promiseApplyProperties(properties)))
|
||
|
.then(() => {
|
||
|
this._is_unsaved = true;
|
||
|
|
||
|
overlayDialogueDestroy(overlay.dialogueid);
|
||
|
|
||
|
this.fire(DASHBOARD_EVENT_APPLY_PROPERTIES);
|
||
|
})
|
||
|
.catch((exception) => {
|
||
|
for (const element of form.parentNode.children) {
|
||
|
if (element.matches('.msg-good, .msg-bad, .msg-warning')) {
|
||
|
element.parentNode.removeChild(element);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let title, messages;
|
||
|
|
||
|
if (typeof exception === 'object' && 'error' in exception) {
|
||
|
title = exception.error.title;
|
||
|
messages = exception.error.messages;
|
||
|
}
|
||
|
else {
|
||
|
messages = [t('Failed to update dashboard properties.')];
|
||
|
}
|
||
|
|
||
|
const message_box = makeMessageBox('bad', messages, title)[0];
|
||
|
|
||
|
form.parentNode.insertBefore(message_box, form);
|
||
|
})
|
||
|
.finally(() => {
|
||
|
overlay.unsetLoading();
|
||
|
this._deleteBusyCondition(busy_condition);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_promiseApplyProperties(properties) {
|
||
|
properties.template = this._data.templateid !== null ? 1 : undefined;
|
||
|
properties.name = properties.name.trim();
|
||
|
|
||
|
const curl = new Curl('zabbix.php');
|
||
|
|
||
|
curl.setArgument('action', 'dashboard.properties.check');
|
||
|
|
||
|
return fetch(curl.getUrl(), {
|
||
|
method: 'POST',
|
||
|
headers: {'Content-Type': 'application/json'},
|
||
|
body: JSON.stringify(properties)
|
||
|
})
|
||
|
.then((response) => response.json())
|
||
|
.then((response) => {
|
||
|
if ('error' in response) {
|
||
|
throw {error: response.error};
|
||
|
}
|
||
|
|
||
|
this._data.name = properties.name;
|
||
|
this._data.userid = this._data.templateid === null ? properties.userid : null;
|
||
|
this._data.display_period = properties.display_period;
|
||
|
this._data.auto_start = properties.auto_start === '1' ? '1' : '0';
|
||
|
});
|
||
|
}
|
||
|
|
||
|
editDashboardPageProperties(properties = {}) {
|
||
|
properties.dashboard_display_period = this._data.display_period;
|
||
|
|
||
|
PopUp('dashboard.page.properties.edit', properties, {
|
||
|
dialogueid: 'dashboard_page_properties',
|
||
|
dialogue_class: 'modal-popup-generic'
|
||
|
});
|
||
|
}
|
||
|
|
||
|
applyDashboardPageProperties() {
|
||
|
const overlay = overlays_stack.getById('dashboard_page_properties');
|
||
|
const form = overlay.$dialogue.$body[0].querySelector('form');
|
||
|
const properties = getFormFields(form);
|
||
|
|
||
|
overlay.setLoading();
|
||
|
|
||
|
const busy_condition = this._createBusyCondition();
|
||
|
|
||
|
return Promise.resolve()
|
||
|
.then(() => this._promiseApplyDashboardPageProperties(properties, overlay.data))
|
||
|
.then(() => {
|
||
|
this._is_unsaved = true;
|
||
|
|
||
|
overlayDialogueDestroy(overlay.dialogueid);
|
||
|
})
|
||
|
.catch((exception) => {
|
||
|
for (const element of form.parentNode.children) {
|
||
|
if (element.matches('.msg-good, .msg-bad, .msg-warning')) {
|
||
|
element.parentNode.removeChild(element);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let title, messages;
|
||
|
|
||
|
if (typeof exception === 'object' && 'error' in exception) {
|
||
|
title = exception.error.title;
|
||
|
messages = exception.error.messages;
|
||
|
}
|
||
|
else {
|
||
|
messages = [t('Failed to update dashboard page properties.')];
|
||
|
}
|
||
|
|
||
|
const message_box = makeMessageBox('bad', messages, title)[0];
|
||
|
|
||
|
form.parentNode.insertBefore(message_box, form);
|
||
|
})
|
||
|
.finally(() => {
|
||
|
overlay.unsetLoading();
|
||
|
this._deleteBusyCondition(busy_condition);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_promiseApplyDashboardPageProperties(properties, data) {
|
||
|
properties.name = properties.name.trim();
|
||
|
|
||
|
const curl = new Curl('zabbix.php');
|
||
|
|
||
|
curl.setArgument('action', 'dashboard.page.properties.check');
|
||
|
|
||
|
return fetch(curl.getUrl(), {
|
||
|
method: 'POST',
|
||
|
headers: {'Content-Type': 'application/json'},
|
||
|
body: JSON.stringify(properties)
|
||
|
})
|
||
|
.then((response) => response.json())
|
||
|
.then((response) => {
|
||
|
if ('error' in response) {
|
||
|
throw {error: response.error};
|
||
|
}
|
||
|
|
||
|
if (data.unique_id !== null) {
|
||
|
const dashboard_page = this.getDashboardPage(data.unique_id);
|
||
|
|
||
|
if (dashboard_page === null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const dashboard_page_name = dashboard_page.getName();
|
||
|
|
||
|
dashboard_page.setName(properties.name);
|
||
|
dashboard_page.setDisplayPeriod(properties.display_period);
|
||
|
|
||
|
if (properties.name !== dashboard_page_name && !this._is_kiosk_mode) {
|
||
|
this._updateTab(dashboard_page);
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
const dashboard_page = this.addDashboardPage({
|
||
|
dashboard_pageid: null,
|
||
|
name: properties.name,
|
||
|
display_period: properties.display_period,
|
||
|
widgets: []
|
||
|
});
|
||
|
|
||
|
this._selectDashboardPage(dashboard_page);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
editWidgetProperties(properties = {}, {new_widget_pos = null} = {}) {
|
||
|
this._clearWarnings();
|
||
|
|
||
|
if (properties.type === undefined) {
|
||
|
properties.type = this._widget_last_type;
|
||
|
|
||
|
if (properties.type === null) {
|
||
|
this._warn(t('Cannot add widget: no widgets available.'));
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const overlay = PopUp(`widget.${properties.type}.edit`, {
|
||
|
templateid: this._data.templateid ?? undefined,
|
||
|
...properties
|
||
|
}, {
|
||
|
dialogueid: 'widget_properties',
|
||
|
dialogue_class: 'modal-widget-configuration'
|
||
|
});
|
||
|
|
||
|
overlay.xhr.then(() => {
|
||
|
const form = overlay.$dialogue.$body[0].querySelector('form');
|
||
|
const original_properties = overlay.data.original_properties;
|
||
|
|
||
|
if (original_properties.unique_id === null) {
|
||
|
this._new_widget_dashboard_page = this._selected_dashboard_page;
|
||
|
this._new_widget_pos = new_widget_pos;
|
||
|
|
||
|
const default_widget_size = this._widget_defaults[original_properties.type].size;
|
||
|
|
||
|
if (this._new_widget_pos === null) {
|
||
|
this._new_widget_pos_reserved = this._new_widget_dashboard_page.findFreePos(default_widget_size);
|
||
|
}
|
||
|
else {
|
||
|
this._new_widget_pos_reserved = {
|
||
|
...default_widget_size,
|
||
|
...this._new_widget_pos
|
||
|
};
|
||
|
|
||
|
this._new_widget_pos_reserved.width = Math.min(this._new_widget_pos_reserved.width,
|
||
|
this._max_columns - this._new_widget_pos_reserved.x
|
||
|
);
|
||
|
|
||
|
this._new_widget_pos_reserved.height = Math.min(this._new_widget_pos_reserved.height,
|
||
|
this._max_rows - this._new_widget_pos_reserved.y
|
||
|
);
|
||
|
|
||
|
this._new_widget_pos_reserved = this._new_widget_dashboard_page.accommodatePos(
|
||
|
this._new_widget_pos_reserved
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (this._new_widget_pos_reserved === null) {
|
||
|
for (const el of form.parentNode.children) {
|
||
|
if (el.matches('.msg-warning')) {
|
||
|
el.parentNode.removeChild(el);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
const message_box = makeMessageBox('warning', [],
|
||
|
t('Cannot add widget: not enough free space on the dashboard.')
|
||
|
)[0];
|
||
|
|
||
|
form.parentNode.insertBefore(message_box, form);
|
||
|
|
||
|
overlay.$btn_submit[0].disabled = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
document.getElementById('type').addEventListener('change', () => this.reloadWidgetProperties());
|
||
|
|
||
|
form.addEventListener('change', (e) => {
|
||
|
const do_trim = e.target.matches(
|
||
|
'input[type="text"]:not([data-no-trim="1"]), textarea:not([data-no-trim="1"])'
|
||
|
);
|
||
|
|
||
|
if (do_trim) {
|
||
|
e.target.value = e.target.value.trim();
|
||
|
}
|
||
|
}, {capture: true});
|
||
|
|
||
|
for (const fieldset of
|
||
|
form.querySelectorAll(`fieldset.${CFormFieldsetCollapsible.ZBX_STYLE_COLLAPSIBLE}`)) {
|
||
|
new CFormFieldsetCollapsible(fieldset);
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
new TabIndicators();
|
||
|
}
|
||
|
catch (error) {
|
||
|
}
|
||
|
});
|
||
|
|
||
|
if (!this._is_edit_widget_properties_cancel_subscribed) {
|
||
|
this._is_edit_widget_properties_cancel_subscribed = true;
|
||
|
|
||
|
overlay.$dialogue[0].addEventListener('dialogue.close', this._events.editWidgetPropertiesCancel,
|
||
|
{once: true}
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
reloadWidgetProperties() {
|
||
|
const overlay = overlays_stack.getById('widget_properties');
|
||
|
const form = overlay.$dialogue.$body[0].querySelector('form');
|
||
|
const fields = getFormFields(form);
|
||
|
|
||
|
const properties = {
|
||
|
type: fields.type,
|
||
|
unique_id: overlay.data.original_properties.unique_id ?? undefined,
|
||
|
dashboard_page_unique_id: overlay.data.original_properties.dashboard_page_unique_id ?? undefined
|
||
|
};
|
||
|
|
||
|
if (properties.type === overlay.data.original_properties.type) {
|
||
|
properties.name = fields.name;
|
||
|
properties.view_mode = fields.show_header == 1
|
||
|
? ZBX_WIDGET_VIEW_MODE_NORMAL
|
||
|
: ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER;
|
||
|
|
||
|
delete fields.type;
|
||
|
delete fields.name;
|
||
|
delete fields.show_header;
|
||
|
|
||
|
properties.fields = fields;
|
||
|
}
|
||
|
|
||
|
overlay.$dialogue[0].dispatchEvent(new CustomEvent('overlay.reload'));
|
||
|
|
||
|
this.editWidgetProperties(properties, {new_widget_pos: this._new_widget_pos});
|
||
|
}
|
||
|
|
||
|
applyWidgetProperties() {
|
||
|
const overlay = overlays_stack.getById('widget_properties');
|
||
|
const form = overlay.$dialogue.$body[0].querySelector('form');
|
||
|
const fields = getFormFields(form);
|
||
|
|
||
|
const templateid = this._data.templateid ?? undefined;
|
||
|
const type = fields.type;
|
||
|
const name = fields.name;
|
||
|
const view_mode = fields.show_header == 1
|
||
|
? ZBX_WIDGET_VIEW_MODE_NORMAL
|
||
|
: ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER;
|
||
|
|
||
|
delete fields.type;
|
||
|
delete fields.name;
|
||
|
delete fields.show_header;
|
||
|
|
||
|
const dashboard_page = overlay.data.original_properties.dashboard_page_unique_id !== null
|
||
|
? this.getDashboardPage(overlay.data.original_properties.dashboard_page_unique_id)
|
||
|
: null;
|
||
|
|
||
|
const widget = dashboard_page !== null
|
||
|
? dashboard_page.getWidget(overlay.data.original_properties.unique_id)
|
||
|
: null;
|
||
|
|
||
|
const busy_condition = this._createBusyCondition();
|
||
|
|
||
|
return Promise.resolve()
|
||
|
.then(() => this._promiseDashboardWidgetCheck({templateid, type, name, view_mode, fields}))
|
||
|
.then(() => {
|
||
|
this._is_unsaved = true;
|
||
|
|
||
|
overlayDialogueDestroy(overlay.dialogueid);
|
||
|
|
||
|
if (widget !== null && widget.getType() === type) {
|
||
|
widget.updateProperties({name, view_mode, fields});
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (type !== this._widget_last_type) {
|
||
|
this._widget_last_type = type;
|
||
|
updateUserProfile('web.dashboard.last_widget_type', type, [], PROFILE_TYPE_STR);
|
||
|
}
|
||
|
|
||
|
const widget_class = eval(this._widget_defaults[type].js_class);
|
||
|
|
||
|
if (widget_class.hasReferenceField()) {
|
||
|
fields.reference = this._createReference();
|
||
|
}
|
||
|
|
||
|
const widget_data = {
|
||
|
type,
|
||
|
name,
|
||
|
view_mode,
|
||
|
fields,
|
||
|
widgetid: null,
|
||
|
pos: widget === null ? this._new_widget_pos_reserved : widget.getPos(),
|
||
|
is_new: widget === null,
|
||
|
rf_rate: 0,
|
||
|
unique_id: this._createUniqueId()
|
||
|
};
|
||
|
|
||
|
if (widget === null) {
|
||
|
this._new_widget_dashboard_page.promiseScrollIntoView(widget_data.pos)
|
||
|
.then(() => {
|
||
|
this._new_widget_dashboard_page.addWidget(widget_data);
|
||
|
this._new_widget_dashboard_page.resetWidgetPlaceholder();
|
||
|
this._new_widget_dashboard_page = null;
|
||
|
this._new_widget_pos = null;
|
||
|
this._new_widget_pos_reserved = null;
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
if (dashboard_page.getState() === DASHBOARD_PAGE_STATE_DESTROYED) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
dashboard_page.promiseScrollIntoView(widget_data.pos)
|
||
|
.then(() => {
|
||
|
dashboard_page.replaceWidget(widget, widget_data);
|
||
|
dashboard_page.resetWidgetPlaceholder();
|
||
|
});
|
||
|
}
|
||
|
})
|
||
|
.catch((exception) => {
|
||
|
for (const element of form.parentNode.children) {
|
||
|
if (element.matches('.msg-good, .msg-bad, .msg-warning')) {
|
||
|
element.parentNode.removeChild(element);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
let title, messages;
|
||
|
|
||
|
if (typeof exception === 'object' && 'error' in exception) {
|
||
|
title = exception.error.title;
|
||
|
messages = exception.error.messages;
|
||
|
}
|
||
|
else {
|
||
|
messages = [t('Failed to update widget properties.')];
|
||
|
}
|
||
|
|
||
|
const message_box = makeMessageBox('bad', messages, title)[0];
|
||
|
|
||
|
form.parentNode.insertBefore(message_box, form);
|
||
|
})
|
||
|
.finally(() => {
|
||
|
overlay.unsetLoading();
|
||
|
this._deleteBusyCondition(busy_condition);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_isEditingWidgetProperties() {
|
||
|
return this._is_edit_widget_properties_cancel_subscribed;
|
||
|
}
|
||
|
|
||
|
_promiseDashboardWidgetCheck({templateid, type, name, view_mode, fields}) {
|
||
|
const curl = new Curl('zabbix.php');
|
||
|
|
||
|
curl.setArgument('action', 'dashboard.widget.check');
|
||
|
|
||
|
return fetch(curl.getUrl(), {
|
||
|
method: 'POST',
|
||
|
headers: {'Content-Type': 'application/json'},
|
||
|
body: JSON.stringify({templateid, type, name, view_mode, fields})
|
||
|
})
|
||
|
.then((response) => response.json())
|
||
|
.then((response) => {
|
||
|
if ('error' in response) {
|
||
|
throw {error: response.error};
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_cancelEditingWidgetProperties() {
|
||
|
this._selected_dashboard_page.resetWidgetPlaceholder();
|
||
|
}
|
||
|
|
||
|
_getDashboardPageActionsContextMenu(dashboard_page) {
|
||
|
let menu = [];
|
||
|
let menu_actions = [];
|
||
|
|
||
|
if (this._can_edit_dashboards) {
|
||
|
menu_actions.push({
|
||
|
label: t('Copy'),
|
||
|
clickCallback: () => {
|
||
|
this._clearWarnings();
|
||
|
|
||
|
const data_copy = dashboard_page.getDataCopy();
|
||
|
const data_copy_widgets = data_copy.widgets;
|
||
|
|
||
|
data_copy.widgets = [];
|
||
|
|
||
|
for (const widget of data_copy_widgets) {
|
||
|
if (widget.type in this._widget_defaults) {
|
||
|
data_copy.widgets.push(widget);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this._storeDashboardPageDataCopy(data_copy);
|
||
|
|
||
|
if (data_copy.widgets.length < data_copy_widgets.length) {
|
||
|
this._warn(t('Inaccessible widgets were not copied.'));
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (this._is_edit_mode) {
|
||
|
menu_actions.push({
|
||
|
label: t('Delete'),
|
||
|
disabled: this._dashboard_pages.size == 1,
|
||
|
clickCallback: () => this.deleteDashboardPage(dashboard_page)
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (menu_actions.length > 0) {
|
||
|
menu.push({
|
||
|
label: t('Actions'),
|
||
|
items: menu_actions
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (this._is_editable) {
|
||
|
menu.push({
|
||
|
items: [
|
||
|
{
|
||
|
label: t('Properties'),
|
||
|
clickCallback: () => {
|
||
|
if (!this._is_edit_mode) {
|
||
|
this.setEditMode({is_internal_call: true});
|
||
|
}
|
||
|
|
||
|
this.editDashboardPageProperties({
|
||
|
name: dashboard_page.getName(),
|
||
|
display_period: dashboard_page.getDisplayPeriod(),
|
||
|
unique_id: dashboard_page.getUniqueId()
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
]
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return menu;
|
||
|
}
|
||
|
|
||
|
// Dashboard view methods.
|
||
|
|
||
|
_warn(warning) {
|
||
|
this._clearWarnings();
|
||
|
|
||
|
this._warning_message_box = makeMessageBox('warning', [], warning);
|
||
|
|
||
|
addMessage(this._warning_message_box);
|
||
|
}
|
||
|
|
||
|
_warnDashboardExhausted() {
|
||
|
this._warn(sprintf(
|
||
|
t('Cannot add dashboard page: maximum number of %1$d dashboard pages has been added.'),
|
||
|
this._max_dashboard_pages
|
||
|
));
|
||
|
}
|
||
|
|
||
|
_warnDashboardPageExhausted() {
|
||
|
this._warn(t('Cannot add widget: not enough free space on the dashboard.'));
|
||
|
}
|
||
|
|
||
|
_clearWarnings() {
|
||
|
if (this._warning_message_box !== null) {
|
||
|
this._warning_message_box.remove();
|
||
|
this._warning_message_box = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_isUserInteracting() {
|
||
|
if (this._selected_dashboard_page.isUserInteracting()) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (!this._is_kiosk_mode) {
|
||
|
const has_aria_expanded = this._tabs
|
||
|
.getList()
|
||
|
.querySelector(`.${ZBX_STYLE_BTN_DASHBOARD_PAGE_PROPERTIES}[aria-expanded="true"]`) !== null;
|
||
|
|
||
|
if (has_aria_expanded) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
_addTab(dashboard_page) {
|
||
|
const tab = document.createElement('li');
|
||
|
const tab_contents = document.createElement('div');
|
||
|
const tab_contents_name = document.createElement('span');
|
||
|
|
||
|
tab.appendChild(tab_contents);
|
||
|
tab_contents.appendChild(tab_contents_name);
|
||
|
|
||
|
const data = this._dashboard_pages.get(dashboard_page);
|
||
|
const name = dashboard_page.getName();
|
||
|
|
||
|
data.tab = tab;
|
||
|
|
||
|
if (name !== '') {
|
||
|
data.index = null;
|
||
|
tab_contents_name.textContent = name;
|
||
|
tab_contents_name.title = name;
|
||
|
}
|
||
|
else {
|
||
|
let max_index = this._dashboard_pages.size - 1;
|
||
|
|
||
|
for (const dashboard_page_data of this._dashboard_pages.values()) {
|
||
|
if (dashboard_page_data.index !== null && dashboard_page_data.index > max_index) {
|
||
|
max_index = dashboard_page_data.index;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
data.index = max_index + 1;
|
||
|
|
||
|
const name = sprintf(t('Page %1$d'), data.index);
|
||
|
|
||
|
tab_contents_name.textContent = name;
|
||
|
tab_contents_name.title = name;
|
||
|
}
|
||
|
|
||
|
if (this._getDashboardPageActionsContextMenu(dashboard_page).length > 0) {
|
||
|
const properties_button = document.createElement('button');
|
||
|
|
||
|
properties_button.type = 'button';
|
||
|
properties_button.title = t('Actions');
|
||
|
properties_button.setAttribute('aria-expanded', 'false');
|
||
|
properties_button.setAttribute('aria-haspopup', 'true');
|
||
|
properties_button.classList.add(ZBX_STYLE_BTN_ICON, ZBX_ICON_MORE, ZBX_STYLE_BTN_DASHBOARD_PAGE_PROPERTIES);
|
||
|
|
||
|
tab_contents.append(properties_button);
|
||
|
}
|
||
|
|
||
|
this._tabs.insertItemBefore(tab);
|
||
|
this._tabs_dashboard_pages.set(tab, dashboard_page);
|
||
|
}
|
||
|
|
||
|
_updateTab(dashboard_page) {
|
||
|
const name = dashboard_page.getName();
|
||
|
const data = this._dashboard_pages.get(dashboard_page);
|
||
|
const tab_contents_name = data.tab.firstElementChild.firstElementChild;
|
||
|
|
||
|
data.index = null;
|
||
|
|
||
|
if (name !== '') {
|
||
|
tab_contents_name.textContent = name;
|
||
|
tab_contents_name.title = name;
|
||
|
}
|
||
|
else {
|
||
|
const tab_index = [...this._tabs.getList().children].indexOf(data.tab) + 1;
|
||
|
|
||
|
let max_index = this._dashboard_pages.size - 1;
|
||
|
let is_tab_index_available = true;
|
||
|
|
||
|
for (const dashboard_page_data of this._dashboard_pages.values()) {
|
||
|
if (dashboard_page_data.index !== null) {
|
||
|
if (dashboard_page_data.index === tab_index) {
|
||
|
is_tab_index_available = false;
|
||
|
}
|
||
|
|
||
|
if (dashboard_page_data.index > max_index) {
|
||
|
max_index = dashboard_page_data.index;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
data.index = is_tab_index_available ? tab_index : max_index + 1;
|
||
|
|
||
|
const name = sprintf(t('Page %1$d'), data.index);
|
||
|
|
||
|
tab_contents_name.textContent = name;
|
||
|
tab_contents_name.title = name;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_deleteTab(dashboard_page) {
|
||
|
const data = this._dashboard_pages.get(dashboard_page);
|
||
|
|
||
|
this._tabs.removeItem(data.tab);
|
||
|
this._tabs_dashboard_pages.delete(data.tab);
|
||
|
}
|
||
|
|
||
|
_selectTab(dashboard_page) {
|
||
|
this._tabs.getList().querySelectorAll(`.${ZBX_STYLE_DASHBOARD_SELECTED_TAB}`).forEach((el) => {
|
||
|
el.classList.remove(ZBX_STYLE_DASHBOARD_SELECTED_TAB);
|
||
|
})
|
||
|
|
||
|
const data = this._dashboard_pages.get(dashboard_page);
|
||
|
|
||
|
data.tab.firstElementChild.classList.add(ZBX_STYLE_DASHBOARD_SELECTED_TAB);
|
||
|
this._updateNavigationButtons(dashboard_page);
|
||
|
this._tabs.scrollItemIntoView(data.tab);
|
||
|
}
|
||
|
|
||
|
_updateNavigationButtons(dashboard_page = null) {
|
||
|
this._containers.navigation.classList.toggle(ZBX_STYLE_DASHBOARD_NAVIGATION_IS_SCROLLABLE,
|
||
|
this._tabs.isScrollable()
|
||
|
);
|
||
|
|
||
|
if (dashboard_page !== null) {
|
||
|
const tab = this._dashboard_pages.get(dashboard_page).tab;
|
||
|
|
||
|
this._buttons.previous_page.disabled = tab.previousElementSibling === null;
|
||
|
this._buttons.next_page.disabled = tab.nextElementSibling === null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_reserveHeaderLines(num_lines) {
|
||
|
this._reserve_header_lines = num_lines;
|
||
|
|
||
|
if (this._reserve_header_lines_timeout_id !== null) {
|
||
|
clearTimeout(this._reserve_header_lines_timeout_id);
|
||
|
this._reserve_header_lines_timeout_id = null;
|
||
|
}
|
||
|
|
||
|
let old_num_lines = 0;
|
||
|
|
||
|
for (let i = 2; i > 0; i--) {
|
||
|
if (this._containers.grid.classList.contains(`reserve-header-lines-${i}`)) {
|
||
|
old_num_lines = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (num_lines > old_num_lines) {
|
||
|
if (old_num_lines > 0) {
|
||
|
this._containers.grid.classList.remove(`reserve-header-lines-${old_num_lines}`);
|
||
|
}
|
||
|
this._containers.grid.classList.add(`reserve-header-lines-${num_lines}`);
|
||
|
}
|
||
|
else if (num_lines < old_num_lines) {
|
||
|
this._reserve_header_lines_timeout_id = setTimeout(() => {
|
||
|
this._reserve_header_lines_timeout_id = null;
|
||
|
|
||
|
this._containers.grid.classList.remove(`reserve-header-lines-${old_num_lines}`);
|
||
|
|
||
|
if (num_lines > 0) {
|
||
|
this._containers.grid.classList.add(`reserve-header-lines-${num_lines}`);
|
||
|
}
|
||
|
}, this._header_lines_steady_period);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_keepSteadyHeaderLines() {
|
||
|
if (this._reserve_header_lines_timeout_id !== null) {
|
||
|
this._reserveHeaderLines(this._reserve_header_lines);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_resetHeaderLines() {
|
||
|
if (this._reserve_header_lines_timeout_id !== null) {
|
||
|
clearTimeout(this._reserve_header_lines_timeout_id);
|
||
|
this._reserve_header_lines_timeout_id = null;
|
||
|
}
|
||
|
|
||
|
this._containers.grid.classList.remove('reserve-header-lines-1', 'reserve-header-lines-2');
|
||
|
}
|
||
|
|
||
|
_createUniqueId() {
|
||
|
return 'U' + (this._unique_id_index++).toString(36).toUpperCase().padStart(6, '0');
|
||
|
}
|
||
|
|
||
|
_createReference({used_references = null} = {}) {
|
||
|
if (used_references === null) {
|
||
|
used_references = this._getUsedReferences();
|
||
|
}
|
||
|
|
||
|
let reference;
|
||
|
|
||
|
do {
|
||
|
reference = '';
|
||
|
|
||
|
for (let i = 0; i < 5; i++) {
|
||
|
reference += String.fromCharCode(65 + Math.floor(Math.random() * 26));
|
||
|
}
|
||
|
}
|
||
|
while (used_references.has(reference));
|
||
|
|
||
|
return reference;
|
||
|
}
|
||
|
|
||
|
_getUsedReferences() {
|
||
|
const used_references = new Set();
|
||
|
|
||
|
for (const dashboard_page of this._dashboard_pages.keys()) {
|
||
|
for (const widget of dashboard_page.getWidgets()) {
|
||
|
const fields = widget.getFields();
|
||
|
|
||
|
if (widget.constructor.hasReferenceField()) {
|
||
|
used_references.add(fields.reference);
|
||
|
}
|
||
|
|
||
|
for (const reference_field of widget.constructor.getForeignReferenceFields()) {
|
||
|
used_references.add(fields[reference_field]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return used_references;
|
||
|
}
|
||
|
|
||
|
// Internal events management methods.
|
||
|
|
||
|
_registerEvents() {
|
||
|
let wrapper_scrollbar_width = 0;
|
||
|
let user_interaction_animation_frame = null;
|
||
|
|
||
|
this._events = {
|
||
|
dashboardPageEdit: () => {
|
||
|
this.setEditMode({is_internal_call: true});
|
||
|
},
|
||
|
|
||
|
dashboardPageWidgetAdd: (e) => {
|
||
|
const dashboard_page = this._selected_dashboard_page;
|
||
|
|
||
|
const new_widget_data = this.getStoredWidgetDataCopy();
|
||
|
const new_widget_pos = e.detail.new_widget_pos;
|
||
|
|
||
|
if (new_widget_data !== null) {
|
||
|
const menu = [
|
||
|
{
|
||
|
label: t('Actions'),
|
||
|
items: [
|
||
|
{
|
||
|
label: t('Add widget'),
|
||
|
clickCallback: () => this.editWidgetProperties({}, {new_widget_pos})
|
||
|
},
|
||
|
{
|
||
|
label: t('Paste widget'),
|
||
|
clickCallback: () => this.pasteWidget(new_widget_data, {new_widget_pos})
|
||
|
}
|
||
|
]
|
||
|
}
|
||
|
];
|
||
|
|
||
|
const placeholder = e.detail.placeholder;
|
||
|
const placeholder_event = new jQuery.Event(e.detail.mouse_event);
|
||
|
|
||
|
placeholder_event.target = placeholder;
|
||
|
|
||
|
jQuery(placeholder).menuPopup(menu, placeholder_event, {
|
||
|
closeCallback: () => {
|
||
|
if (!this._isEditingWidgetProperties()) {
|
||
|
dashboard_page.resetWidgetPlaceholder();
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
this.editWidgetProperties({}, {new_widget_pos});
|
||
|
|
||
|
if (!this._isEditingWidgetProperties()) {
|
||
|
dashboard_page.resetWidgetPlaceholder();
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
dashboardPageWidgetAddNew: () => {
|
||
|
this.editWidgetProperties();
|
||
|
},
|
||
|
|
||
|
dashboardPageWidgetDelete: () => {
|
||
|
this._clearWarnings();
|
||
|
},
|
||
|
|
||
|
dashboardPageWidgetPosition: () => {
|
||
|
this._clearWarnings();
|
||
|
},
|
||
|
|
||
|
dashboardPageWidgetActions: (e) => {
|
||
|
const menu = e.detail.widget.getActionsContextMenu({
|
||
|
can_paste_widget: this.getStoredWidgetDataCopy() !== null
|
||
|
});
|
||
|
|
||
|
jQuery(e.detail.mouse_event.target).menuPopup(menu, new jQuery.Event(e.detail.mouse_event));
|
||
|
},
|
||
|
|
||
|
dashboardPageWidgetEdit: (e) => {
|
||
|
const dashboard_page = e.detail.target;
|
||
|
const widget = e.detail.widget;
|
||
|
|
||
|
this.editWidgetProperties({
|
||
|
type: widget.getType(),
|
||
|
name: widget.getName(),
|
||
|
view_mode: widget.getViewMode(),
|
||
|
fields: widget.getFields(),
|
||
|
unique_id: widget.getUniqueId(),
|
||
|
dashboard_page_unique_id: dashboard_page.getUniqueId()
|
||
|
});
|
||
|
},
|
||
|
|
||
|
dashboardPageWidgetCopy: (e) => {
|
||
|
const widget = e.detail.widget;
|
||
|
|
||
|
this._storeWidgetDataCopy(widget.getDataCopy({is_single_copy: true}));
|
||
|
},
|
||
|
|
||
|
dashboardPageWidgetPaste: (e) => {
|
||
|
const widget = e.detail.widget;
|
||
|
|
||
|
this.pasteWidget(this.getStoredWidgetDataCopy(), {widget});
|
||
|
},
|
||
|
|
||
|
dashboardPageAnnounceWidgets: () => {
|
||
|
this._announceWidgets();
|
||
|
},
|
||
|
|
||
|
dashboardPageReserveHeaderLines: (e) => {
|
||
|
this._reserveHeaderLines(e.detail.num_lines);
|
||
|
},
|
||
|
|
||
|
gridResize: () => {
|
||
|
const wrapper = document.querySelector('.wrapper');
|
||
|
|
||
|
if (wrapper.offsetWidth > wrapper.clientWidth) {
|
||
|
wrapper_scrollbar_width = wrapper.offsetWidth - wrapper.clientWidth;
|
||
|
|
||
|
this._buttons.next_page.style.marginRight = '0';
|
||
|
}
|
||
|
else {
|
||
|
this._buttons.next_page.style.marginRight = `${wrapper_scrollbar_width}px`;
|
||
|
}
|
||
|
},
|
||
|
|
||
|
tabsResize: () => {
|
||
|
this._updateNavigationButtons();
|
||
|
},
|
||
|
|
||
|
tabsDragStart: () => {
|
||
|
this._selected_dashboard_page.blockInteraction();
|
||
|
},
|
||
|
|
||
|
tabsDragEnd: () => {
|
||
|
this._selected_dashboard_page.unblockInteraction();
|
||
|
|
||
|
this._updateNavigationButtons();
|
||
|
},
|
||
|
|
||
|
tabsSort: () => {
|
||
|
this._setInitialDashboardPage(this._selected_dashboard_page);
|
||
|
|
||
|
this._is_unsaved = true;
|
||
|
},
|
||
|
|
||
|
tabsClick: (e) => {
|
||
|
const tab = e.target.closest(`.${ZBX_STYLE_SORTABLE_ITEM}`);
|
||
|
|
||
|
if (tab !== null && tab.parentNode.classList.contains(ZBX_STYLE_SORTABLE_LIST)) {
|
||
|
const dashboard_page = this._tabs_dashboard_pages.get(tab);
|
||
|
|
||
|
if (dashboard_page !== this._selected_dashboard_page) {
|
||
|
this._selectDashboardPage(dashboard_page, {is_async: true});
|
||
|
}
|
||
|
else if (e.target.classList.contains(ZBX_STYLE_BTN_DASHBOARD_PAGE_PROPERTIES)) {
|
||
|
jQuery(e.target).menuPopup(this._getDashboardPageActionsContextMenu(dashboard_page),
|
||
|
new jQuery.Event(e)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
tabsKeyDown: (e) => {
|
||
|
if (e.key === 'Enter') {
|
||
|
const tab = e.target.closest(`.${ZBX_STYLE_SORTABLE_ITEM}`);
|
||
|
|
||
|
if (tab !== null) {
|
||
|
const dashboard_page = this._tabs_dashboard_pages.get(tab);
|
||
|
|
||
|
if (dashboard_page !== this._selected_dashboard_page) {
|
||
|
this._selectDashboardPage(dashboard_page, {is_async: true});
|
||
|
}
|
||
|
else if (e.target.classList.contains(ZBX_STYLE_BTN_DASHBOARD_PAGE_PROPERTIES)) {
|
||
|
jQuery(e.target).menuPopup(this._getDashboardPageActionsContextMenu(dashboard_page),
|
||
|
new jQuery.Event(e)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
},
|
||
|
|
||
|
tabsPreviousPageClick: () => {
|
||
|
const tab = this._dashboard_pages.get(this._selected_dashboard_page).tab;
|
||
|
|
||
|
this._selectDashboardPage(this._tabs_dashboard_pages.get(tab.previousElementSibling), {is_async: true});
|
||
|
},
|
||
|
|
||
|
tabsNextPageClick: () => {
|
||
|
const tab = this._dashboard_pages.get(this._selected_dashboard_page).tab;
|
||
|
|
||
|
this._selectDashboardPage(this._tabs_dashboard_pages.get(tab.nextElementSibling), {is_async: true});
|
||
|
},
|
||
|
|
||
|
slideshowToggle: () => {
|
||
|
if (this._is_edit_mode) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (this._isSlideshowRunning()) {
|
||
|
this._stopSlideshow();
|
||
|
}
|
||
|
else {
|
||
|
this._startSlideshow();
|
||
|
}
|
||
|
},
|
||
|
|
||
|
userInteract: () => {
|
||
|
if (user_interaction_animation_frame !== null) {
|
||
|
cancelAnimationFrame(user_interaction_animation_frame);
|
||
|
}
|
||
|
|
||
|
user_interaction_animation_frame = requestAnimationFrame(() => {
|
||
|
user_interaction_animation_frame = null;
|
||
|
|
||
|
if (this._is_kiosk_mode) {
|
||
|
this._keepSteadyHeaderLines();
|
||
|
}
|
||
|
|
||
|
if (!this._is_edit_mode) {
|
||
|
this._keepSteadyConfigurationChecker();
|
||
|
|
||
|
if (this._isSlideshowRunning()) {
|
||
|
this._keepSteadySlideshow();
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
},
|
||
|
|
||
|
kioskModePreviousPageClick: () => {
|
||
|
const dashboard_pages = [...this._dashboard_pages.keys()];
|
||
|
const dashboard_page_index = dashboard_pages.indexOf(this._selected_dashboard_page);
|
||
|
|
||
|
this._selectDashboardPage(
|
||
|
dashboard_pages[dashboard_page_index > 0 ? dashboard_page_index - 1 : dashboard_pages.length - 1]
|
||
|
);
|
||
|
},
|
||
|
|
||
|
kioskModeNextPageClick: () => {
|
||
|
const dashboard_pages = [...this._dashboard_pages.keys()];
|
||
|
const dashboard_page_index = dashboard_pages.indexOf(this._selected_dashboard_page);
|
||
|
|
||
|
this._selectDashboardPage(
|
||
|
dashboard_pages[dashboard_page_index < dashboard_pages.length - 1 ? dashboard_page_index + 1 : 0]
|
||
|
);
|
||
|
},
|
||
|
|
||
|
timeSelectorRangeUpdate: (e, time_period) => {
|
||
|
this._time_period = {
|
||
|
from: time_period.from,
|
||
|
from_ts: time_period.from_ts,
|
||
|
to: time_period.to,
|
||
|
to_ts: time_period.to_ts
|
||
|
};
|
||
|
|
||
|
for (const dashboard_page of this._dashboard_pages.keys()) {
|
||
|
dashboard_page.setTimePeriod(this._time_period);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
editWidgetPropertiesCancel: () => {
|
||
|
this._cancelEditingWidgetProperties();
|
||
|
|
||
|
this._is_edit_widget_properties_cancel_subscribed = false;
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
|
||
|
_activateEvents() {
|
||
|
if (!this._is_kiosk_mode) {
|
||
|
new ResizeObserver(this._events.gridResize).observe(this._containers.grid);
|
||
|
new ResizeObserver(this._events.tabsResize).observe(this._containers.navigation_tabs);
|
||
|
|
||
|
this._tabs.on(SORTABLE_EVENT_DRAG_START, this._events.tabsDragStart);
|
||
|
this._tabs.on(SORTABLE_EVENT_DRAG_END, this._events.tabsDragEnd);
|
||
|
this._tabs.on(SORTABLE_EVENT_SORT, this._events.tabsSort);
|
||
|
|
||
|
this._containers.navigation_tabs.addEventListener('click', this._events.tabsClick);
|
||
|
this._containers.navigation_tabs.addEventListener('keydown', this._events.tabsKeyDown);
|
||
|
|
||
|
this._buttons.previous_page.addEventListener('click', this._events.tabsPreviousPageClick);
|
||
|
this._buttons.next_page.addEventListener('click', this._events.tabsNextPageClick);
|
||
|
}
|
||
|
|
||
|
if (this._buttons.slideshow !== null && !this._is_edit_mode && this._dashboard_pages.size > 1) {
|
||
|
this._buttons.slideshow.addEventListener('click', this._events.slideshowToggle);
|
||
|
}
|
||
|
|
||
|
for (const event_name of ['mousemove', 'mousedown', 'keydown', 'wheel']) {
|
||
|
window.addEventListener(event_name, this._events.userInteract);
|
||
|
}
|
||
|
|
||
|
if (this._is_kiosk_mode) {
|
||
|
if (this._buttons.previous_page !== null) {
|
||
|
this._buttons.previous_page.addEventListener('click', this._events.kioskModePreviousPageClick);
|
||
|
}
|
||
|
|
||
|
if (this._buttons.next_page !== null) {
|
||
|
this._buttons.next_page.addEventListener('click', this._events.kioskModeNextPageClick);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (this._time_period !== null) {
|
||
|
jQuery.subscribe('timeselector.rangeupdate', this._events.timeSelectorRangeUpdate);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Attach event listener to dashboard events.
|
||
|
*
|
||
|
* @param {string} type
|
||
|
* @param {function} listener
|
||
|
* @param {Object|false} options
|
||
|
*
|
||
|
* @returns {CDashboard}
|
||
|
*/
|
||
|
on(type, listener, options = false) {
|
||
|
this._target.addEventListener(type, listener, options);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Detach event listener from dashboard events.
|
||
|
*
|
||
|
* @param {string} type
|
||
|
* @param {function} listener
|
||
|
* @param {Object|false} options
|
||
|
*
|
||
|
* @returns {CDashboard}
|
||
|
*/
|
||
|
off(type, listener, options = false) {
|
||
|
this._target.removeEventListener(type, listener, options);
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Dispatch dashboard event.
|
||
|
*
|
||
|
* @param {string} type
|
||
|
* @param {Object} detail
|
||
|
* @param {Object} options
|
||
|
*
|
||
|
* @returns {boolean}
|
||
|
*/
|
||
|
fire(type, detail = {}, options = {}) {
|
||
|
return this._target.dispatchEvent(new CustomEvent(type, {...options, detail: {target: this, ...detail}}));
|
||
|
}
|
||
|
}
|