/* ** 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. **/ /** * Get menu popup history section data. * * @param string options['itemid'] Item ID. * @param bool options['hasLatestGraphs'] Link to history page with showgraph action (optional). * * @return array */ function getMenuPopupHistory(options) { var items = [], url = new Curl('history.php'); if (!options.allowed_ui_latest_data) { return []; } url.setArgument('itemids[]', options.itemid); // latest graphs if (typeof options.hasLatestGraphs !== 'undefined' && options.hasLatestGraphs) { url.setArgument('action', 'showgraph'); url.setArgument('to', 'now'); url.setArgument('from', 'now-1h'); items.push({ label: t('Last hour graph'), url: url.getUrl() }); url.setArgument('from', 'now-7d'); items.push({ label: t('Last week graph'), url: url.getUrl() }); url.setArgument('from', 'now-1M'); items.push({ label: t('Last month graph'), url: url.getUrl() }); } // latest values url.setArgument('action', 'showvalues'); url.setArgument('from', 'now-1h'); items.push({ label: t('Latest values'), url: url.getUrl() }); return [{ label: t('History'), items: items }]; } /** * Get menu popup host section data. * * @param {string} options['hostid'] Host ID. * @param {array} options['scripts'] Host scripts (optional). * @param {string} options['csrf_token'] CSRF token. * @param {string} options[]['name'] Script name. * @param {string} options[]['scriptid'] Script ID. * @param {string} options[]['confirmation'] Confirmation text. * @param {bool} options['showGraphs'] Link to Monitoring->Hosts->Graphs page. * @param {bool} options['showDashboards'] Link to Monitoring->Hosts->Dashboards page. * @param {bool} options['showWeb'] Link to Monitoring->Hosts->Web page. * @param {bool} options['showTriggers'] Link to Monitoring->Problems page. * @param {bool} options['hasGoTo'] "Go to" block in popup. * @param {array} options['severities'] (optional) * @param {bool} options['show_suppressed'] (optional) * @param {array} options['urls'] (optional) * @param {string} options['url'][]['label'] * @param {string} options['url'][]['url'] * @param {array} options['tags'] (optional) * @param {string} options['tags'][]['tag'] * @param {string} options['tags'][]['value'] * @param {number} options['tags'][]['operator'] * @param {number} options['evaltype'] (optional) * @param {bool} options['allowed_ui_inventory'] Whether user has access to inventory hosts page. * @param {bool} options['allowed_ui_latest_data'] Whether user has access to latest data page. * @param {bool} options['allowed_ui_problems'] Whether user has access to problems page. * @param {bool} options['allowed_ui_hosts'] Whether user has access to monitoring hosts pages. * @param {bool} options['allowed_ui_conf_hosts'] Whether user has access to configuration hosts page. * @param {Node} trigger_element UI element which triggered opening of overlay dialogue. * * @return array */ function getMenuPopupHost(options, trigger_element) { const sections = []; const items = []; const configuration = []; let url; // go to section if (options.hasGoTo) { // dashboard if (options.allowed_ui_hosts) { url = new Curl('zabbix.php'); url.setArgument('action', 'host.dashboard.view') url.setArgument('hostid', options.hostid) items.push({ label: t('Dashboards'), disabled: !options.showDashboards, url: url.getUrl() }); } // problems if (options.allowed_ui_problems) { url = new Curl('zabbix.php'); url.setArgument('action', 'problem.view'); url.setArgument('hostids[]', options.hostid); url.setArgument('filter_set', '1'); if ('severities' in options) { url.setArgument('severities[]', options.severities); } if ('show_suppressed' in options) { url.setArgument('show_suppressed', '1'); } if ('tags' in options) { url.setArgument('tags', options.tags); url.setArgument('evaltype', options.evaltype); } items.push({ label: t('Problems'), disabled: !options.showTriggers, url: url.getUrl() }); } // latest data if (options.allowed_ui_latest_data) { url = new Curl('zabbix.php'); url.setArgument('action', 'latest.view'); if ('tags' in options) { url.setArgument('tags', options.tags); url.setArgument('evaltype', options.evaltype); } url.setArgument('hostids[]', options.hostid); url.setArgument('filter_set', '1'); items.push({ label: t('Latest data'), url: url.getUrl() }); } // graphs if (options.allowed_ui_hosts) { url = new Curl('zabbix.php'); url.setArgument('action', 'charts.view') url.setArgument('filter_hostids[]', options.hostid); url.setArgument('filter_set', '1'); items.push({ label: t('Graphs'), disabled: !options.showGraphs, url: url.getUrl() }); } // web if (options.allowed_ui_hosts) { url = new Curl('zabbix.php'); url.setArgument('action', 'web.view'); url.setArgument('filter_hostids[]', options.hostid); url.setArgument('filter_set', '1'); items.push({ label: t('Web'), disabled: !options.showWeb, url: url.getUrl() }); } // inventory link if (options.allowed_ui_inventory) { url = new Curl('hostinventories.php'); url.setArgument('hostid', options.hostid); items.push({ label: t('Inventory'), url: url.getUrl() }); } if (items.length) { sections.push({ label: t('View'), items: items }); } // Configuration if (options.allowed_ui_conf_hosts) { // host url = new Curl('zabbix.php'); url.setArgument('action', 'host.edit'); url.setArgument('hostid', options.hostid); configuration.push({ label: t('Host'), disabled: !options.isWriteable, url: url.getUrl(), clickCallback: function(e) { e.preventDefault(); jQuery(this).closest('.menu-popup').menuPopup('close', null); view.editHost(options.hostid); } }); // items url = new Curl('items.php'); url.setArgument('filter_set', '1'); url.setArgument('filter_hostids[]', options.hostid); url.setArgument('context', 'host'); configuration.push({ label: t('Items'), disabled: !options.isWriteable, url: url.getUrl() }); // triggers url = new Curl('triggers.php'); url.setArgument('filter_set', '1'); url.setArgument('filter_hostids[]', options.hostid); url.setArgument('context', 'host'); configuration.push({ label: t('Triggers'), disabled: !options.isWriteable, url: url.getUrl() }); // graphs url = new Curl('graphs.php'); url.setArgument('filter_set', '1'); url.setArgument('filter_hostids[]', options.hostid); url.setArgument('context', 'host'); configuration.push({ label: t('Graphs'), disabled: !options.isWriteable, url: url.getUrl() }); // discovery url = new Curl('host_discovery.php'); url.setArgument('filter_set', '1'); url.setArgument('filter_hostids[]', options.hostid); url.setArgument('context', 'host'); configuration.push({ label: t('Discovery'), disabled: !options.isWriteable, url: url.getUrl() }); // web scenario url = new Curl('httpconf.php'); url.setArgument('filter_set', '1'); url.setArgument('filter_hostids[]', options.hostid); url.setArgument('context', 'host'); configuration.push({ label: t('Web'), disabled: !options.isWriteable, url: url.getUrl() }); sections.push({ label: t('Configuration'), items: configuration }); } } // urls if ('urls' in options) { sections.push({ label: t('Links'), items: getMenuPopupURLData(options.urls, trigger_element) }); } // scripts if ('scripts' in options) { sections.push({ label: t('Scripts'), items: getMenuPopupScriptData(options.scripts, trigger_element, options.hostid, options.eventid, options.csrf_token) }); } return sections; } /** * Get menu popup submap map element section data. * * @param {array} options['sysmapid'] * @param {int} options['severity_min'] (optional) * @param {int} options['is_widget'] (optional) * @param {array} options['urls'] (optional) * @param {string} options['url'][]['label'] * @param {string} options['url'][]['url'] * * @return array */ function getMenuPopupMapElementSubmap(options) { const sections = []; const item = {label: t('Submap')}; if (options.unique_id !== undefined) { item.clickCallback = ()=> { ZABBIX.Dashboard.getDashboardPages().forEach((page) => { const widget = page.getWidget(options.unique_id); if (widget !== null) { widget.navigateToSubmap(options.sysmapid); } }); }; } else { if (!options.allowed_ui_maps) { return []; } const submap_url = new Curl('zabbix.php'); submap_url.setArgument('action', 'map.view'); submap_url.setArgument('sysmapid', options.sysmapid); if (typeof options.severity_min !== 'undefined') { submap_url.setArgument('severity_min', options.severity_min); } item.url = submap_url.getUrl(); } sections.push({ label: t('Go to'), items: [item] }); // urls if (typeof options.urls !== 'undefined') { sections.push({ label: t('Links'), items: options.urls }); } return sections; } /** * Get menu popup host group map element section data. * * @param {string} options['groupid'] * @param {array} options['severities'] (optional) * @param {bool} options['show_suppressed'] (optional) * @param {array} options['urls'] (optional) * @param {string} options['url'][]['label'] * @param {string} options['url'][]['url'] * @param {array} options['tags'] (optional) * @param {string} options['tags'][]['tag'] * @param {string} options['tags'][]['value'] * @param {number} options['tags'][]['operator'] * @param {number} options['evaltype'] (optional) * * @return array */ function getMenuPopupMapElementGroup(options) { if (!options.allowed_ui_problems) { return []; } var sections = [], problems_url = new Curl('zabbix.php'); problems_url.setArgument('action', 'problem.view'); problems_url.setArgument('filter_set', '1'); problems_url.setArgument('groupids[]', options.groupid); if (typeof options.severities !== 'undefined') { problems_url.setArgument('severities[]', options.severities); } if (typeof options.show_suppressed !== 'undefined' && options.show_suppressed) { problems_url.setArgument('show_suppressed', '1'); } if (typeof options.tags !== 'undefined') { problems_url.setArgument('tags', options.tags); problems_url.setArgument('evaltype', options.evaltype); } sections.push({ label: t('Go to'), items: [{ label: t('Problems'), url: problems_url.getUrl() }] }); // urls if (typeof options.urls !== 'undefined') { sections.push({ label: t('Links'), items: options.urls }); } return sections; } /** * Get menu popup trigger map element section data. * * @param {array} options['triggerids'] * @param {array} options['severities'] (optional) * @param {bool} options['show_suppressed'] (optional) * @param {array} options['urls'] (optional) * @param {string} options['url'][]['label'] * @param {string} options['url'][]['url'] * * @return array */ function getMenuPopupMapElementTrigger(options) { const sections = []; const items = []; let url; if (options.allowed_ui_problems) { url = new Curl('zabbix.php'); url.setArgument('action', 'problem.view'); url.setArgument('filter_set', '1'); url.setArgument('triggerids', options.triggers.map((value) => value.triggerid)); if ('severities' in options) { url.setArgument('severities[]', options.severities); } if ('show_suppressed' in options) { url.setArgument('show_suppressed', '1'); } items.push({ label: t('Problems'), disabled: !options.show_events, url: url.getUrl() }); } // items problems if (options.allowed_ui_latest_data && options.items.length) { const history = []; for (const item of options.items) { url = new Curl('history.php'); url.setArgument('action', item.params.action); url.setArgument('itemids[]', item.params.itemid); history.push({ label: item.name, url: url.getUrl() }); } items.push({ label: t('History'), items: history }); } if (items.length) { sections.push({ label: t('View'), items: items }); } // configuration if (options.allowed_ui_conf_hosts) { const config_urls = []; const trigger_urls = []; const item_urls = []; for (const value of options.triggers) { url = new Curl('triggers.php'); url.setArgument('form', 'update'); url.setArgument('triggerid', value.triggerid); url.setArgument('context', 'host'); trigger_urls.push({ label: value.description, url: url.getUrl() }); } config_urls.push({ label: t('Triggers'), items: trigger_urls }); if (options.items.length) { for (const item of options.items) { url = new Curl('items.php'); url.setArgument('form', 'update'); url.setArgument('itemid', item.params.itemid); url.setArgument('context', 'host'); item_urls.push({ label: item.name, disabled: item.params.is_webitem, url: url.getUrl() }); } config_urls.push({ label: t('Items'), items: item_urls }); } sections.push({ label: t('Configuration'), items: config_urls }); } // urls if ('urls' in options) { sections.push({ label: t('Links'), items: options.urls }); } return sections; } /** * Get menu popup image map element section data. * * @param {array} options['urls'] (optional) * @param {string} options['url'][]['label'] * @param {string} options['url'][]['url'] * * @return array */ function getMenuPopupMapElementImage(options) { // urls if (typeof options.urls !== 'undefined') { return [{ label: t('Links'), items: options.urls }]; } return []; } /** * Get menu popup dashboard actions data. * * @param {string} options['dashboardid'] * @param {bool} options['editable'] * @param {bool} options['has_related_reports'] * @param {bool} options['can_edit_dashboards'] * @param {bool} options['can_view_reports'] * @param {bool} options['can_create_reports'] * @param {string} options['csrf_token'] * @param {object} trigger_element UI element which triggered opening of overlay dialogue. * * @return array */ function getMenuPopupDashboard(options, trigger_element) { const sections = []; const parameters = {dashboardid: options.dashboardid}; // Dashboard actions. if (options.can_edit_dashboards) { const url_create = new Curl('zabbix.php'); url_create.setArgument('action', 'dashboard.view'); url_create.setArgument('new', '1'); const url_clone = new Curl('zabbix.php'); url_clone.setArgument('action', 'dashboard.view'); url_clone.setArgument('dashboardid', options.dashboardid); url_clone.setArgument('clone', '1'); const url_delete = new Curl('zabbix.php'); url_delete.setArgument('action', 'dashboard.delete'); url_delete.setArgument('dashboardids', [options.dashboardid]); url_delete.setArgument('_csrf_token', options.csrf_token); sections.push({ label: t('Actions'), items: [ { label: t('Sharing'), clickCallback: function () { jQuery(this).closest('.menu-popup').menuPopup('close', null); PopUp('popup.dashboard.share.edit', parameters, { dialogueid: 'dashboard_share_edit', dialogue_class: 'modal-popup-generic', trigger_element }); }, disabled: !options.editable }, { label: t('Create new'), url: url_create.getUrl() }, { label: t('Clone'), url: url_clone.getUrl() }, { label: t('Delete'), clickCallback: function () { jQuery(this).closest('.menu-popup').menuPopup('close', null); if (!confirm(t('Delete dashboard?'))) { return false; } redirect(url_delete.getUrl(), 'post', '_csrf_token', true); }, disabled: !options.editable } ] }); } // Report actions. if (options.can_view_reports) { const report_actions = [ { label: t('View related reports'), clickCallback: function () { jQuery(this).closest('.menu-popup').menuPopup('close', null); PopUp('popup.scheduledreport.list', parameters, {trigger_element}); }, disabled: !options.has_related_reports } ]; if (options.can_create_reports) { report_actions.unshift({ label: t('Create new report'), clickCallback: function () { jQuery(this).closest('.menu-popup').menuPopup('close', null); PopUp('popup.scheduledreport.edit', parameters, {trigger_element}); } }); } sections.push({ label: options.can_edit_dashboards ? null : t('Actions'), items: report_actions }) } return sections; } /** * Get menu popup trigger section data. * * @param {object} options * {string} options['triggerid'] Trigger ID. * {bool} options['allowed_actions_change_problem_ranking'] * Whether user is allowed to change event rank. * {bool} options['allowed_ui_conf_hosts'] Whether user has access to Configuration > Hosts. * {bool} options['allowed_ui_latest_data'] Whether user has access to Monitoring > Latest data. * {bool} options['allowed_ui_problems'] Whether user has access to Monitoring > Problems. * {bool} options['backurl'] URL from where the menu popup was called. * {bool} options['show_events'] Show Problems item enabled. Default: false. * {string} options['eventid'] (optional) Required for "Update problem" section and event * rank change. * {array} options['eventids'] (optional) * {array} options['csrf_tokens'] (optional) CSRF tokens. * {string} options['csrf_tokens']['acknowledge'] (optional) CSRF token for acknowledge action. * {string} options['csrf_tokens']['scriptexec'] (optional) CSRF token for script execution. * {array} options['items'] (optional) Link to trigger item history page. * {string} options['items'][]['name'] Item name. * {object} options['items'][]['params'] Item URL parameters ("name" => "value"). * {bool} options['mark_as_cause'] (optional) Whether to enable "Mark as cause". * {bool} options['mark_selected_as_symptoms'] (optional) Whether to enable "Mark selected as symptoms". * {bool} options['show_update_problem'] (optional) Whether to show "Update problem". * {bool} options['show_rank_change_cause'] (optional) Whether to show "Mark as cause". * {bool} options['show_rank_change_symptom'] (optional) Whether to show "Mark selected as symptoms". * {array} options['urls'] (optional) Links. * @param {object} trigger_element UI element which triggered opening of overlay dialogue. * * @return array */ function getMenuPopupTrigger(options, trigger_element) { const sections = []; const items = []; let url; if (options.allowed_ui_problems) { // events url = new Curl('zabbix.php'); url.setArgument('action', 'problem.view'); url.setArgument('filter_set', '1'); url.setArgument('triggerids[]', options.triggerid); items.push({ label: t('Problems'), disabled: !options.show_events, url: url.getUrl() }); } // items problems if (options.allowed_ui_latest_data && options.items.length) { const history = []; for (const item of options.items) { url = new Curl('history.php'); url.setArgument('action', item.params.action); url.setArgument('itemids[]', item.params.itemid); history.push({ label: item.name, url: url.getUrl() }); } items.push({ label: t('History'), items: history }); } if (items.length) { sections.push({ label: t('View'), items: items }); } if ('show_update_problem' in options && options.show_update_problem) { sections.push({ label: t('Actions'), items: [{ label: t('Update problem'), clickCallback: function() { jQuery(this).closest('.menu-popup-top').menuPopup('close', null); acknowledgePopUp({eventids: [options.eventid]}, trigger_element); } }] }); } // configuration if (options.allowed_ui_conf_hosts) { const config_urls = []; const item_urls = []; url = new Curl('triggers.php'); url.setArgument('form', 'update'); url.setArgument('triggerid', options.triggerid); url.setArgument('context', 'host'); url.setArgument('backurl', options.backurl); config_urls.push({ label: t('Trigger'), url: url.getUrl() }); if (options.items.length) { for (const item of options.items) { url = new Curl('items.php'); url.setArgument('form', 'update'); url.setArgument('itemid', item.params.itemid); url.setArgument('context', 'host'); url.setArgument('backurl', options.backurl); item_urls.push({ label: item.name, disabled: item.params.is_webitem, url: url.getUrl() }); } config_urls.push({ label: t('Items'), items: item_urls }); } sections.push({ label: t('Configuration'), items: config_urls }); } // Check if user role allows to change event rank and if one of the options to show individual menu are true. if (options.allowed_actions_change_problem_ranking && ((typeof options.show_rank_change_cause !== 'undefined' && options.show_rank_change_cause) || (typeof options.show_rank_change_symptom !== 'undefined' && options.show_rank_change_symptom))) { let items = []; const curl = new Curl('zabbix.php'); curl.setArgument('action', 'popup.acknowledge.create'); /* * Some widgets cannot show symptoms. So it is not possible to convert to symptoms cause if only cause events * are displayed. */ if (typeof options.show_rank_change_cause !== 'undefined' && options.show_rank_change_cause) { // Must be synced with PHP ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE. const ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE = 0x80; items[items.length] = { label: t('Mark as cause'), clickCallback: function () { jQuery(this).closest('.menu-popup').menuPopup('close', null); fetch(curl.getUrl(), { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, body: urlEncodeData({ eventids: [options.eventid], change_rank: ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE, _csrf_token: options.csrf_tokens['acknowledge'] }) }) .then((response) => response.json()) .then((response) => { clearMessages(); // Show message directly that comes from controller. if ('error' in response) { addMessage(makeMessageBox('bad', [response.error.messages], response.error.title, true, true )); } else if ('success' in response) { addMessage(makeMessageBox('good', [], response.success.title, true, false)); $.publish('event.rank_change'); } }) .catch(() => { const title = t('Unexpected server error.'); const message_box = makeMessageBox('bad', [], title)[0]; clearMessages(); addMessage(message_box); }); }, disabled: !options.mark_as_cause }; } // Dashboard does not have checkboxes. So it is not possible to mark problems and change rank to symptom. if (typeof options.show_rank_change_symptom !== 'undefined' && options.show_rank_change_symptom) { // Must be synced with PHP ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM. const ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM = 0x100; items[items.length] = { label: t('Mark selected as symptoms'), clickCallback: function () { jQuery(this).closest('.menu-popup').menuPopup('close', null); fetch(curl.getUrl(), { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}, body: urlEncodeData({ eventids: options.eventids, cause_eventid: options.eventid, change_rank: ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM, _csrf_token: options.csrf_tokens['acknowledge'] }) }) .then((response) => response.json()) .then((response) => { clearMessages(); // Show message directly that comes from controller. if ('error' in response) { addMessage(makeMessageBox('bad', [response.error.messages], response.error.title, true, true )); } else if ('success' in response) { addMessage(makeMessageBox('good', [], response.success.title, true, false)); const uncheckids = Object.keys(chkbxRange.getSelectedIds()); uncheckTableRows('problem', []); chkbxRange.checkObjects('eventids', uncheckids, false); chkbxRange.update('eventids'); $.publish('event.rank_change'); } }) .catch(() => { const title = t('Unexpected server error.'); const message_box = makeMessageBox('bad', [], title)[0]; clearMessages(); addMessage(message_box); }); }, disabled: !options.mark_selected_as_symptoms }; } sections[sections.length] = { label: t('Problem'), items: items }; } // urls if ('urls' in options) { sections.push({ label: t('Links'), items: getMenuPopupURLData(options.urls, trigger_element) }); } // scripts if ('scripts' in options) { sections.push({ label: t('Scripts'), items: getMenuPopupScriptData(options.scripts, trigger_element, null, options.eventid, options.csrf_tokens['scriptexec'] ) }); } return sections; } /** * Get menu popup latest data item log section data. * * @param string options['itemid'] * @param string options['hostid'] * @param bool options['showGraph'] Link to Monitoring->Items->Graphs page. * @param bool options['history'] Is history available. * @param bool options['trends'] Are trends available. * @param bool options['allowed_ui_conf_hosts'] Whether user has access to configuration hosts pages. * @param bool options['isWriteable'] Whether user has read and write access to host and its items. * @param string options['context'] Determines whether the menu is made for host or template item. * * @return array */ function getMenuPopupItem(options) { const sections = []; const actions = []; const items = []; let url; if (options.context !== 'template') { // latest data link if (options.allowed_ui_latest_data) { url = new Curl('zabbix.php'); url.setArgument('action', 'latest.view'); url.setArgument('hostids[]', options.hostid); url.setArgument('name', options.name); url.setArgument('filter_set', '1'); items.push({ label: t('Latest data'), url: url.getUrl() }); } url = new Curl('history.php'); url.setArgument('action', 'showgraph'); url.setArgument('itemids[]', options.itemid); items.push({ label: t('Graph'), url: url.getUrl(), disabled: !options.showGraph }); url = new Curl('history.php'); url.setArgument('action', 'showvalues'); url.setArgument('itemids[]', options.itemid); items.push({ label: t('Values'), url: url.getUrl(), disabled: !options.history && !options.trends }); url = new Curl('history.php'); url.setArgument('action', 'showlatest'); url.setArgument('itemids[]', options.itemid); items.push({ label: t('500 latest values'), url: url.getUrl(), disabled: !options.history && !options.trends }); sections.push({ label: t('View'), items: items }); } if (options.allowed_ui_conf_hosts) { const config_urls = []; const config_triggers = { label: t('Triggers'), disabled: options.binary_value_type || options.triggers.length === 0 }; if (options.isWriteable) { url = new Curl('items.php'); url.setArgument('form', 'update'); url.setArgument('hostid', options.hostid); url.setArgument('itemid', options.itemid); url.setArgument('backurl', options.backurl); url.setArgument('context', options.context); config_urls.push({ label: t('Item'), url: url.getUrl() }); } if (!config_triggers.disabled) { const trigger_items = []; for (const value of options.triggers) { url = new Curl('triggers.php'); url.setArgument('form', 'update'); url.setArgument('triggerid', value.triggerid); url.setArgument('backurl', options.backurl); url.setArgument('context', options.context); trigger_items.push({ label: value.description, url: url.getUrl() }); } config_triggers.items = trigger_items; } config_urls.push(config_triggers); url = new Curl('triggers.php'); url.setArgument('form', 'create'); url.setArgument('hostid', options.hostid); url.setArgument('description', options.name); url.setArgument('expression', 'func(/' + options.host + '/' + options.key + ')'); url.setArgument('backurl', options.backurl); url.setArgument('context', options.context); config_urls.push({ label: t('Create trigger'), url: url.getUrl(), disabled: options.binary_value_type }); url = new Curl('items.php'); url.setArgument('form', 'create'); url.setArgument('hostid', options.hostid); url.setArgument('type', 18); // ITEM_TYPE_DEPENDENT url.setArgument('master_itemid', options.itemid); url.setArgument('backurl', options.backurl); url.setArgument('context', options.context); config_urls.push({ label: t('Create dependent item'), url: url.getUrl(), disabled: options.isDiscovery }); url = new Curl('host_discovery.php'); url.setArgument('form', 'create'); url.setArgument('hostid', options.hostid); url.setArgument('type', 18); // ITEM_TYPE_DEPENDENT url.setArgument('master_itemid', options.itemid); url.setArgument('backurl', options.backurl); url.setArgument('context', options.context); config_urls.push({ label: t('Create dependent discovery rule'), url: url.getUrl(), disabled: options.isDiscovery }); sections.push({ label: t('Configuration'), items: config_urls }); } if (options.context !== 'template') { const execute = { label: t('Execute now'), disabled: !options.isExecutable }; if (options.isExecutable) { execute.clickCallback = function() { jQuery(this).closest('.menu-popup').menuPopup('close', null); view.checkNow(options.itemid); }; } actions.push(execute); sections.push({ label: t('Actions'), items: actions }); } return sections; } /** * Get menu structure for item prototypes. * * @param array options['name'] * @param string options['backurl'] Url from where the popup menu was called. * @param array options['key'] Item prototype key. * @param array options['host'] Host name. * @param array options['itemid'] * @param array options['trigger_prototypes'] (optional) * @param string options['trigger_prototypes'][n]['triggerid'] * @param string options['trigger_prototypes'][n]['name'] * @param array options['parent_discoveryid'] * @param string options['context'] Additional parameter in URL to identify main section. * * @return array */ function getMenuPopupItemPrototype(options) { const sections = []; const config_urls = []; let config_triggers = { label: t('Trigger prototypes'), disabled: true }; let url; url = new Curl('disc_prototypes.php'); url.setArgument('form', 'update'); url.setArgument('parent_discoveryid', options.parent_discoveryid); url.setArgument('itemid', options.itemid); url.setArgument('context', options.context); config_urls.push({ label: t('Item prototype'), url: url.getUrl() }); if (options.trigger_prototypes.length) { const trigger_prototypes = []; for (const value of options.trigger_prototypes) { url = new Curl('trigger_prototypes.php'); url.setArgument('form', 'update'); url.setArgument('parent_discoveryid', options.parent_discoveryid); url.setArgument('triggerid', value.triggerid) url.setArgument('context', options.context); url.setArgument('backurl', options.backurl); trigger_prototypes.push({ label: value.description, url: url.getUrl() }); } if (trigger_prototypes.length) { config_triggers = {...config_triggers, ...{items: trigger_prototypes, disabled: false}}; } } config_urls.push(config_triggers); url = new Curl('trigger_prototypes.php'); url.setArgument('parent_discoveryid', options.parent_discoveryid); url.setArgument('form', 'create'); url.setArgument('description', options.name); url.setArgument('expression', 'func(/' + options.host + '/' + options.key + ')'); url.setArgument('context', options.context); url.setArgument('backurl', options.backurl); config_urls.push({ label: t('Create trigger prototype'), url: url.getUrl() }); url = new Curl('disc_prototypes.php'); url.setArgument('form', 'create'); url.setArgument('parent_discoveryid', options.parent_discoveryid); url.setArgument('type', 18); // ITEM_TYPE_DEPENDENT url.setArgument('master_itemid', options.itemid); url.setArgument('context', options.context); config_urls.push({ label: t('Create dependent item'), url: url.getUrl() }); sections.push({ label: t('Configuration'), items: config_urls }); return sections; } /** * Get dropdown section data. * * @param {array} options * @param {object} trigger_elem UI element that was clicked to open overlay dialogue. * * @returns array */ function getMenuPopupDropdown(options, trigger_elem) { var items = []; jQuery.each(options.items, function(i, item) { var row = { label: item.label, url: item.url || 'javascript:void(0);' }; if (item.class) { row.class = item.class; } if (options.toggle_class) { row.clickCallback = () => { jQuery(trigger_elem) .removeClass() .addClass(['btn-alt', options.toggle_class, item.class].join(' ')); jQuery('input[type=hidden]', jQuery(trigger_elem).parent()) .val(item.value) .trigger('change'); } } else if (options.submit_form) { row.url = 'javascript:void(0);'; row.clickCallback = () => { const form = trigger_elem.closest('form').get(0); if (!form.dataset.action) { form.dataset.action = form.getAttribute('action'); } form.setAttribute('action', item.url); form.submit(); } } items.push(row); }); return [{ items: items }]; } /** * Get menu popup submenu section data. * * @param object options['submenu'] List of menu sections. * @param object options['submenu'][section] An individual section definition. * @param string options['submenu'][section]['label'] Non-clickable section label. * @param object options['submenu'][section]['items'] List of menu items of the section. * @param string options['submenu'][section]['items'][url] Menu item label for the given url. * @param object options['submenu'][section]['items'][key] Menu item with a submenu. * @param object options['submenu'][section]['items'][key]['label'] Non-clickable subsection label. * @param object options['submenu'][section]['items'][key]['items'] List of menu items of the subsection. * @param object options['submenu'][section]['items'][key]['items'][] More levels of submenu. * * @returns array */ function getMenuPopupSubmenu(options) { var transform = function(sections) { var result = []; for (var key in sections) { if (typeof sections[key] === 'object') { var item = {}; for (var item_key in sections[key]) { if (item_key === 'items') { item[item_key] = transform(sections[key][item_key]); } else { item[item_key] = sections[key][item_key]; } } result.push(item); } else { result.push({ 'label': sections[key], 'url': key }); } } return result; }; return transform(options.submenu); } /** * Get data for the "Insert expression" menu in the trigger expression constructor. * * @return array */ function getMenuPopupTriggerMacro(options) { var items = [], expressions = [ { label: t('Trigger status "OK"'), string: '{TRIGGER.VALUE}=0' }, { label: t('Trigger status "Problem"'), string: '{TRIGGER.VALUE}=1' } ]; jQuery.each(expressions, function(key, expression) { items[items.length] = { label: expression.label, clickCallback: function() { var expressionInput = jQuery('#expr_temp'); if (expressionInput.val().length > 0 && !confirm(t('Do you wish to replace the conditional expression?'))) { return false; } expressionInput.val(expression.string); jQuery(this).closest('.menu-popup').menuPopup('close', null); } }; }); return [{ label: t('Insert expression'), items: items }]; } /** * Build script menu tree. * * @param {array} scripts Script names and nenu paths. * @param {Node} trigger_element UI element which triggered opening of overlay dialogue. * @param {array} hostid Host ID. * @param {array} eventid Event ID. * @param {string} csrf_token CSRF token for script execution. * * @return {array} */ function getMenuPopupScriptData(scripts, trigger_element, hostid, eventid, csrf_token) { let tree = {}; // Parse scripts and create tree. for (let key in scripts) { const script = scripts[key]; if (typeof script.scriptid !== 'undefined') { const items = (script.menu_path.length > 0) ? splitPath(script.menu_path) : []; appendTreeItem(tree, script.name, items, { scriptid: script.scriptid, confirmation: script.confirmation, hostid: hostid, eventid: eventid }); } } return getMenuPopupScriptItems(tree, trigger_element, csrf_token); } /** * Build URL menu tree. * * @param {array} urls URL names and nenu paths. * @param {Node} trigger_element UI element which triggered opening of overlay dialogue. * * @return {array} */ function getMenuPopupURLData(urls, trigger_element) { let tree = {}; // Parse URLs and create tree. for (let key in urls) { const url = urls[key]; if (typeof url.menu_path !== 'undefined') { const items = (url.menu_path.length > 0) ? splitPath(url.menu_path) : []; appendTreeItem(tree, url.label, items, { url: url.url, target: url.target, confirmation: url.confirmation, rel: url.rel, }); } } return getMenuPopupURLItems(tree, trigger_element); } /** * Add a menu item to tree. * * @param {object} tree Menu tree object to where menu items will be added. * @param {string} name Menu element label (name). * @param {array} items List of menu items to add. * @param {object} params Additional menu item parameters like URL, target, clickcallback etc. */ function appendTreeItem(tree, name, items, params) { if (items.length > 0) { const item = items.shift(); if (typeof tree[item] === 'undefined') { tree[item] = { name: item, items: {} }; } appendTreeItem(tree[item].items, name, items, params); } else { tree['/' + name] = { name: name, params: params, items: {} }; } } /** * Build URL menu items from tree. * * @param {object} tree Menu tree object to where menu items are. * @param {Node} trigger_elm UI element which triggered opening of overlay dialogue. * * @return {array} */ function getMenuPopupURLItems(tree, trigger_elm) { let items = []; if (objectSize(tree) > 0) { Object.values(tree).map((data) => { const item = {label: data.name}; if (typeof data.items !== 'undefined' && objectSize(data.items) > 0) { item.items = getMenuPopupURLItems(data.items, trigger_elm); } if (typeof data.params !== 'undefined') { item.url = data.params.url; item.target = data.params.target; item.rel = data.params.rel; if (data.params.confirmation !== '') { item.clickCallback = function(e) { return confirm(data.params.confirmation); } } } items[items.length] = item; }); } return items; } /** * Build script menu items from tree. * * @param {object} tree Menu tree object to where menu items are. * @param {Node} trigger_elm UI element which triggered opening of overlay dialogue. * @param {string} csrf_token CSRF token for script execution. * * @return {array} */ function getMenuPopupScriptItems(tree, trigger_elm, csrf_token) { let items = []; if (objectSize(tree) > 0) { Object.values(tree).map((data) => { const item = {label: data.name}; if (typeof data.items !== 'undefined' && objectSize(data.items) > 0) { item.items = getMenuPopupScriptItems(data.items, trigger_elm, csrf_token); } if (typeof data.params !== 'undefined' && typeof data.params.scriptid !== 'undefined') { item.clickCallback = function(e) { jQuery(this) .closest('.menu-popup-top') .menuPopup('close', trigger_elm, false); executeScript(data.params.scriptid, data.params.confirmation, trigger_elm, data.params.hostid, data.params.eventid, csrf_token ); cancelEvent(e); }; } items[items.length] = item; }); } return items; } jQuery(function($) { /** * Menu popup. * * @param array sections Menu sections. * @param string sections[n]['label'] Section title (optional). * @param array sections[n]['items'] Section menu data (see createMenuItem() for available options). * @param object event Menu popup call event. * @param object options Menu popup options (optional). * @param object options['class'] Menu popup additional class name (optional). * @param object options['position'] Menu popup position object (optional). * * @see createMenuItem() */ $.fn.menuPopup = function(method) { if (methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else { return methods.init.apply(this, arguments); } }; /** * Created popup menu item nodes and append to $menu_popup. * * @param {object} $menu_popup jQuery node of popup menu. * @param {array} sections Array of menu popup sections. */ function addMenuPopupItems($menu_popup, sections) { // Create menu sections. $.each(sections, function(i, section) { // Add a separator between menu item sections. if (i > 0) { $menu_popup.append($('
  • ').append($('
    '))); } var section_label = null; if (typeof section.label === 'string' && section.label.length) { section_label = section.label; } // Add menu item section label, if provided. if (section_label !== null) { $menu_popup.append($('
  • ').append($('

    ').text(section_label))); } // Add individual menu items of the section. $.each(section.items, function(i, item) { item = $.extend({}, item); if (sections.length > 1 && section_label !== null) { item.ariaLabel = section_label + ', ' + item['label']; } $menu_popup.append(createMenuItem(item)); }); }); if (sections.length == 1) { if (typeof sections[0].label === 'string' && sections[0].label.length) { $menu_popup.attr({'aria-label': sections[0].label}); } } } var defaultOptions = { closeCallback: function(){}, background_layer: true }; var methods = { init: function(sections, event, options) { // Don't display empty menu. if (!sections.length || !sections[0]['items'].length) { return; } var $opener = $(this); options = $.extend({ position: { /* * Please note that click event is also triggered by hitting spacebar on the keyboard, * in which case the number of mouse clicks (stored in event.originalEvent.detail) will be zero. */ of: (['click', 'mouseup', 'mousedown'].includes(event.type) && event.originalEvent.detail) ? event : event.target, my: 'left top', at: 'left bottom', using: (pos, data) => { let max_left = data.horizontal === 'left' ? document.querySelector('.wrapper').clientWidth : document.querySelector('.wrapper').clientWidth - data.element.width; pos.top = Math.max(0, pos.top); pos.left = Math.max(0, Math.min(max_left, pos.left)); data.element.element[0].style.top = `${pos.top}px`; data.element.element[0].style.left = `${pos.left}px`; } } }, defaultOptions, options || {}); // Close other action menus and prevent focus jumping before opening a new popup. $('.menu-popup-top').menuPopup('close', null, false); $opener.attr('aria-expanded', 'true'); var $menu_popup = $('