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.
1030 lines
30 KiB
1030 lines
30 KiB
1 year ago
|
<?php declare(strict_types = 0);
|
||
|
/*
|
||
|
** 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.
|
||
|
**/
|
||
|
|
||
|
|
||
|
class CControllerMenuPopup extends CController {
|
||
|
|
||
|
protected function init() {
|
||
|
$this->disableCsrfValidation();
|
||
|
}
|
||
|
|
||
|
protected function checkInput() {
|
||
|
$fields = [
|
||
|
'type' => 'required|in history,host,item,item_prototype,map_element,trigger,trigger_macro',
|
||
|
'data' => 'array'
|
||
|
];
|
||
|
|
||
|
$ret = $this->validateInput($fields) && $this->validateInputData();
|
||
|
|
||
|
if (!$ret) {
|
||
|
$this->setResponse(
|
||
|
new CControllerResponseData(['main_block' => json_encode([
|
||
|
'error' => [
|
||
|
'messages' => array_column(get_and_clear_messages(), 'message')
|
||
|
]
|
||
|
])])
|
||
|
);
|
||
|
}
|
||
|
|
||
|
return $ret;
|
||
|
}
|
||
|
|
||
|
protected function validateInputData(): bool {
|
||
|
$type = $this->getInput('type');
|
||
|
|
||
|
switch ($type) {
|
||
|
case 'host':
|
||
|
$rules = [
|
||
|
'hostid' => 'required|db hosts.hostid',
|
||
|
'has_goto' => 'in 0'
|
||
|
];
|
||
|
break;
|
||
|
|
||
|
case 'history':
|
||
|
$rules = [
|
||
|
'itemid' => 'required|db items.itemid'
|
||
|
];
|
||
|
break;
|
||
|
|
||
|
case 'item':
|
||
|
case 'item_prototype':
|
||
|
$rules = [
|
||
|
'itemid' => 'required|db items.itemid',
|
||
|
'backurl' => 'required|string'
|
||
|
];
|
||
|
break;
|
||
|
|
||
|
case 'map_element':
|
||
|
$rules = [
|
||
|
'sysmapid' => 'required|db sysmaps.sysmapid',
|
||
|
'selementid' => 'required|db sysmaps_elements.selementid',
|
||
|
'unique_id' => 'string',
|
||
|
'severity_min' => 'in '.implode(',', [TRIGGER_SEVERITY_NOT_CLASSIFIED, TRIGGER_SEVERITY_INFORMATION, TRIGGER_SEVERITY_WARNING, TRIGGER_SEVERITY_AVERAGE, TRIGGER_SEVERITY_HIGH, TRIGGER_SEVERITY_DISASTER]),
|
||
|
'hostid' => 'db hosts.hostid'
|
||
|
];
|
||
|
break;
|
||
|
|
||
|
case 'trigger':
|
||
|
$rules = [
|
||
|
'triggerid' => 'required|db triggers.triggerid',
|
||
|
'backurl' => 'required|string',
|
||
|
'eventid' => 'db events.eventid',
|
||
|
'show_update_problem' => 'in 0,1',
|
||
|
'show_rank_change_cause' => 'in 0,1',
|
||
|
'show_rank_change_symptom' => 'in 0,1',
|
||
|
'ids' => 'array_db events.eventid'
|
||
|
];
|
||
|
break;
|
||
|
|
||
|
case 'trigger_macro':
|
||
|
$rules = [];
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
$this->input['data'] = array_intersect_key($this->getInput('data', []), $rules);
|
||
|
|
||
|
$validator = new CNewValidator($this->input['data'], $rules);
|
||
|
array_map('error', $validator->getAllErrors());
|
||
|
|
||
|
return !$validator->isError() && !$validator->isErrorFatal();
|
||
|
}
|
||
|
|
||
|
protected function checkPermissions() {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepare data for history context menu popup.
|
||
|
*
|
||
|
* @param array $data
|
||
|
* @param string $data['itemid']
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
private static function getMenuDataHistory(array $data) {
|
||
|
$db_items = API::Item()->get([
|
||
|
'output' => ['value_type'],
|
||
|
'itemids' => $data['itemid'],
|
||
|
'webitems' => true
|
||
|
]);
|
||
|
|
||
|
if ($db_items) {
|
||
|
$db_item = $db_items[0];
|
||
|
|
||
|
return [
|
||
|
'type' => 'history',
|
||
|
'itemid' => $data['itemid'],
|
||
|
'hasLatestGraphs' => in_array($db_item['value_type'], [ITEM_VALUE_TYPE_UINT64, ITEM_VALUE_TYPE_FLOAT]),
|
||
|
'allowed_ui_latest_data' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_LATEST_DATA)
|
||
|
];
|
||
|
}
|
||
|
|
||
|
error(_('No permissions to referred object or it does not exist!'));
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepare data for host context menu popup.
|
||
|
*
|
||
|
* @param array $data
|
||
|
* @param string $data['hostid']
|
||
|
* @param bool $data['has_goto'] (optional) Can be used to hide "GO TO" menu section. Default: true.
|
||
|
* @param int $data['severity_min'] (optional)
|
||
|
* @param bool $data['show_suppressed'] (optional)
|
||
|
* @param array $data['tags'] (optional)
|
||
|
* @param array $data['evaltype'] (optional)
|
||
|
* @param array $data['urls'] (optional)
|
||
|
* @param string $data['urls']['label']
|
||
|
* @param string $data['urls']['url']
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
private static function getMenuDataHost(array $data) {
|
||
|
$has_goto = !array_key_exists('has_goto', $data) || $data['has_goto'];
|
||
|
|
||
|
$db_hosts = $has_goto
|
||
|
? API::Host()->get([
|
||
|
'output' => ['hostid', 'status'],
|
||
|
'selectGraphs' => API_OUTPUT_COUNT,
|
||
|
'selectHttpTests' => API_OUTPUT_COUNT,
|
||
|
'hostids' => $data['hostid']
|
||
|
])
|
||
|
: API::Host()->get([
|
||
|
'output' => ['hostid'],
|
||
|
'hostids' => $data['hostid']
|
||
|
]);
|
||
|
|
||
|
if ($db_hosts) {
|
||
|
$db_host = $db_hosts[0];
|
||
|
$rw_hosts = false;
|
||
|
|
||
|
if ($has_goto && CWebUser::getType() > USER_TYPE_ZABBIX_USER) {
|
||
|
$rw_hosts = (bool) API::Host()->get([
|
||
|
'output' => [],
|
||
|
'hostids' => $db_host['hostid'],
|
||
|
'editable' => true
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
$all_scripts = CWebUser::checkAccess(CRoleHelper::ACTIONS_EXECUTE_SCRIPTS)
|
||
|
? API::Script()->getScriptsByHosts([$data['hostid']])[$data['hostid']]
|
||
|
: [];
|
||
|
|
||
|
$scripts = [];
|
||
|
$urls = [];
|
||
|
|
||
|
if (array_key_exists('urls', $data)) {
|
||
|
foreach ($data['urls'] as &$url) {
|
||
|
$url['new_window'] = ZBX_SCRIPT_URL_NEW_WINDOW_YES;
|
||
|
$url['confirmation'] = '';
|
||
|
$url['menu_path'] = '';
|
||
|
$url['name'] = $url['label'];
|
||
|
|
||
|
unset($url['label']);
|
||
|
}
|
||
|
unset($url);
|
||
|
|
||
|
$urls = $data['urls'];
|
||
|
}
|
||
|
|
||
|
if ($all_scripts) {
|
||
|
foreach ($all_scripts as $num => $script) {
|
||
|
// Filter only host scope scripts, get rid of excess spaces and unify slashes in menu path.
|
||
|
if ($script['scope'] != ZBX_SCRIPT_SCOPE_HOST) {
|
||
|
unset($all_scripts[$num]);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Split scripts and URLs.
|
||
|
if ($script['type'] == ZBX_SCRIPT_TYPE_URL) {
|
||
|
$urls[] = $script;
|
||
|
}
|
||
|
else {
|
||
|
$scripts[] = $script;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$scripts = self::sortEntitiesByMenuPath($scripts);
|
||
|
$urls = self::sortEntitiesByMenuPath($urls);
|
||
|
}
|
||
|
|
||
|
$menu_data = [
|
||
|
'type' => 'host',
|
||
|
'hostid' => $data['hostid'],
|
||
|
'hasGoTo' => (bool) $has_goto,
|
||
|
'allowed_ui_inventory' => CWebUser::checkAccess(CRoleHelper::UI_INVENTORY_HOSTS),
|
||
|
'allowed_ui_latest_data' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_LATEST_DATA),
|
||
|
'allowed_ui_problems' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_PROBLEMS),
|
||
|
'allowed_ui_hosts' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_HOSTS),
|
||
|
'allowed_ui_conf_hosts' => CWebUser::checkAccess(CRoleHelper::UI_CONFIGURATION_HOSTS),
|
||
|
'csrf_token' => CCsrfTokenHelper::get('scriptexec')
|
||
|
];
|
||
|
|
||
|
if ($has_goto) {
|
||
|
$menu_data['showGraphs'] = (bool) $db_host['graphs'];
|
||
|
$menu_data['showDashboards'] = (bool) getHostDashboards($data['hostid']);
|
||
|
$menu_data['showWeb'] = (bool) $db_host['httpTests'];
|
||
|
$menu_data['isWriteable'] = $rw_hosts;
|
||
|
$menu_data['showTriggers'] = ($db_host['status'] == HOST_STATUS_MONITORED);
|
||
|
if (array_key_exists('severity_min', $data)) {
|
||
|
$menu_data['severities'] = array_column(
|
||
|
CSeverityHelper::getSeverities((int) $data['severity_min']),
|
||
|
'value'
|
||
|
);
|
||
|
}
|
||
|
if (array_key_exists('show_suppressed', $data)) {
|
||
|
$menu_data['show_suppressed'] = $data['show_suppressed'];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach (array_values($scripts) as $script) {
|
||
|
$menu_data['scripts'][] = [
|
||
|
'name' => $script['name'],
|
||
|
'menu_path' => $script['menu_path'],
|
||
|
'scriptid' => $script['scriptid'],
|
||
|
'confirmation' => $script['confirmation']
|
||
|
];
|
||
|
}
|
||
|
|
||
|
foreach (array_values($urls) as $url) {
|
||
|
$menu_data['urls'][] = [
|
||
|
'label' => $url['name'],
|
||
|
'menu_path' => $url['menu_path'],
|
||
|
'url' => $url['url'],
|
||
|
'target' => $url['new_window'] == ZBX_SCRIPT_URL_NEW_WINDOW_YES ? '_blank' : '',
|
||
|
'confirmation' => $url['confirmation'],
|
||
|
'rel' => 'noopener'.(ZBX_NOREFERER ? ' noreferrer' : '')
|
||
|
];
|
||
|
}
|
||
|
|
||
|
if (array_key_exists('urls', $menu_data)) {
|
||
|
foreach ($menu_data['urls'] as &$url) {
|
||
|
if (!CHtmlUrlValidator::validate($url['url'], ['allow_user_macro' => false])) {
|
||
|
$url['url'] = 'javascript: alert('.
|
||
|
json_encode(_s('Provided URL "%1$s" is invalid.', $url['url'])).
|
||
|
');';
|
||
|
unset($url['target'], $url['rel']);
|
||
|
}
|
||
|
}
|
||
|
unset($url);
|
||
|
}
|
||
|
|
||
|
if (array_key_exists('tags', $data)) {
|
||
|
$menu_data['tags'] = $data['tags'];
|
||
|
$menu_data['evaltype'] = $data['evaltype'];
|
||
|
}
|
||
|
|
||
|
return $menu_data;
|
||
|
}
|
||
|
|
||
|
error(_('No permissions to referred object or it does not exist!'));
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepare data for item latest data context menu popup.
|
||
|
*
|
||
|
* @param array $data
|
||
|
* @param string $data['itemid']
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
private static function getMenuDataItem(array $data) {
|
||
|
$db_items = API::Item()->get([
|
||
|
'output' => ['hostid', 'key_', 'name', 'flags', 'type', 'value_type', 'history', 'trends'],
|
||
|
'selectHosts' => ['host'],
|
||
|
'selectTriggers' => ['triggerid', 'description'],
|
||
|
'itemids' => $data['itemid'],
|
||
|
'webitems' => true
|
||
|
]);
|
||
|
|
||
|
if ($db_items) {
|
||
|
$db_item = $db_items[0];
|
||
|
$is_writable = false;
|
||
|
$is_executable = false;
|
||
|
|
||
|
if ($db_item['type'] != ITEM_TYPE_HTTPTEST) {
|
||
|
if (CWebUser::getType() == USER_TYPE_SUPER_ADMIN) {
|
||
|
$is_writable = true;
|
||
|
}
|
||
|
elseif (CWebUser::getType() == USER_TYPE_ZABBIX_ADMIN) {
|
||
|
$is_writable = (bool) API::Host()->get([
|
||
|
'output' => ['hostid'],
|
||
|
'hostids' => $db_item['hostid'],
|
||
|
'editable' => true
|
||
|
]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (in_array($db_item['type'], checkNowAllowedTypes())) {
|
||
|
$is_executable = $is_writable ? true : CWebUser::checkAccess(CRoleHelper::ACTIONS_INVOKE_EXECUTE_NOW);
|
||
|
}
|
||
|
|
||
|
return [
|
||
|
'type' => 'item',
|
||
|
'backurl' => $data['backurl'],
|
||
|
'itemid' => $data['itemid'],
|
||
|
'name' => $db_item['name'],
|
||
|
'key' => $db_item['key_'],
|
||
|
'hostid' => $db_item['hostid'],
|
||
|
'host' => $db_item['hosts'][0]['host'],
|
||
|
'triggers' => $db_item['triggers'],
|
||
|
'showGraph' => ($db_item['value_type'] == ITEM_VALUE_TYPE_FLOAT
|
||
|
|| $db_item['value_type'] == ITEM_VALUE_TYPE_UINT64
|
||
|
),
|
||
|
'history' => $db_item['history'] != 0,
|
||
|
'trends' => $db_item['trends'] != 0,
|
||
|
'isDiscovery' => $db_item['flags'] == ZBX_FLAG_DISCOVERY_CREATED,
|
||
|
'isExecutable' => $is_executable,
|
||
|
'isWriteable' => $is_writable,
|
||
|
'allowed_ui_latest_data' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_LATEST_DATA),
|
||
|
'allowed_ui_conf_hosts' => CWebUser::checkAccess(CRoleHelper::UI_CONFIGURATION_HOSTS),
|
||
|
'binary_value_type' => $db_item['value_type'] == ITEM_VALUE_TYPE_BINARY
|
||
|
];
|
||
|
}
|
||
|
|
||
|
error(_('No permissions to referred object or it does not exist!'));
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepare data for item prototype configuration context menu popup.
|
||
|
*
|
||
|
* @param array $data
|
||
|
* @param string $data['itemid']
|
||
|
* @param string $data['backurl']
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
private static function getMenuDataItemPrototype(array $data) {
|
||
|
$db_item_prototypes = API::ItemPrototype()->get([
|
||
|
'output' => ['name', 'key_'],
|
||
|
'selectDiscoveryRule' => ['itemid'],
|
||
|
'selectHosts' => ['host'],
|
||
|
'selectTriggers' => ['triggerid', 'description'],
|
||
|
'itemids' => $data['itemid']
|
||
|
]);
|
||
|
|
||
|
if ($db_item_prototypes) {
|
||
|
$db_item_prototype = $db_item_prototypes[0];
|
||
|
|
||
|
$menu_data = [
|
||
|
'type' => 'item_prototype',
|
||
|
'backurl' => $data['backurl'],
|
||
|
'itemid' => $data['itemid'],
|
||
|
'name' => $db_item_prototype['name'],
|
||
|
'key' => $db_item_prototype['key_'],
|
||
|
'host' => $db_item_prototype['hosts'][0]['host'],
|
||
|
'parent_discoveryid' => $db_item_prototype['discoveryRule']['itemid'],
|
||
|
'trigger_prototypes' => $db_item_prototype['triggers']
|
||
|
];
|
||
|
|
||
|
return $menu_data;
|
||
|
}
|
||
|
|
||
|
error(_('No permissions to referred object or it does not exist!'));
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Validates URLs for supported schemes.
|
||
|
*
|
||
|
* @param array $urls
|
||
|
* @param array $urls[]['url']
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private static function sanitizeMapElementUrls(array $urls): array {
|
||
|
foreach ($urls as &$url) {
|
||
|
if (CHtmlUrlValidator::validate($url['url'], ['allow_user_macro' => false]) === false) {
|
||
|
$url['url'] = 'javascript: alert('.json_encode(_s('Provided URL "%1$s" is invalid.', $url['url'])).');';
|
||
|
}
|
||
|
}
|
||
|
unset($url);
|
||
|
|
||
|
return $urls;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Combines map URLs with element's URLs and performs other modifications with the URLs.
|
||
|
*
|
||
|
* @param array $selement
|
||
|
* @param array $map_urls
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private static function prepareMapElementUrls(array $selement, array $map_urls) {
|
||
|
// Remove unused selement url data.
|
||
|
foreach ($selement['urls'] as &$url) {
|
||
|
unset($url['sysmapelementurlid'], $url['selementid']);
|
||
|
}
|
||
|
unset($url);
|
||
|
|
||
|
// Add map urls to element's urls based on their type.
|
||
|
foreach ($map_urls as $map_url) {
|
||
|
if ($selement['elementtype'] == $map_url['elementtype']) {
|
||
|
unset($map_url['elementtype']);
|
||
|
$selement['urls'][] = $map_url;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$selement = CMacrosResolverHelper::resolveMacrosInMapElements([$selement], ['resolve_element_urls' => true])[0];
|
||
|
|
||
|
// Unset URLs with empty name or value.
|
||
|
foreach ($selement['urls'] as $url_nr => $url) {
|
||
|
if ($url['name'] === '' || $url['url'] === '') {
|
||
|
unset($selement['urls'][$url_nr]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CArrayHelper::sort($selement['urls'], ['name']);
|
||
|
$selement['urls'] = array_values($selement['urls']);
|
||
|
|
||
|
// Prepare urls for processing in menupopup.js.
|
||
|
$selement['urls'] = CArrayHelper::renameObjectsKeys($selement['urls'], ['name' => 'label']);
|
||
|
|
||
|
return $selement;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepare data for map element context menu popup.
|
||
|
*
|
||
|
* @param array $data
|
||
|
* @param string $data['sysmapid']
|
||
|
* @param string $data['selementid']
|
||
|
* @param int $data['severity_min'] (optional)
|
||
|
* @param int $data['unique_id'] (optional)
|
||
|
* @param string $data['hostid'] (optional)
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
private static function getMenuDataMapElement(array $data) {
|
||
|
$db_maps = API::Map()->get([
|
||
|
'output' => ['show_suppressed'],
|
||
|
'selectSelements' => ['selementid', 'elementtype', 'elementsubtype', 'elements', 'urls', 'tags',
|
||
|
'evaltype'
|
||
|
],
|
||
|
'selectUrls' => ['name', 'url', 'elementtype'],
|
||
|
'sysmapids' => $data['sysmapid']
|
||
|
]);
|
||
|
|
||
|
if ($db_maps) {
|
||
|
$db_map = $db_maps[0];
|
||
|
$selement = null;
|
||
|
|
||
|
foreach ($db_map['selements'] as $db_selement) {
|
||
|
if (bccomp($db_selement['selementid'], $data['selementid']) == 0) {
|
||
|
$selement = $db_selement;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($selement !== null) {
|
||
|
if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP
|
||
|
&& $selement['elementsubtype'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS
|
||
|
&& array_key_exists('hostid', $data) && $data['hostid'] != 0) {
|
||
|
$selement['elementtype'] = SYSMAP_ELEMENT_TYPE_HOST;
|
||
|
$selement['elementsubtype'] = SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP;
|
||
|
$selement['elements'][0]['hostid'] = $data['hostid'];
|
||
|
}
|
||
|
|
||
|
$selement = self::prepareMapElementUrls($selement, $db_map['urls']);
|
||
|
|
||
|
switch ($selement['elementtype']) {
|
||
|
case SYSMAP_ELEMENT_TYPE_MAP:
|
||
|
$menu_data = [
|
||
|
'type' => 'map_element_submap',
|
||
|
'sysmapid' => $selement['elements'][0]['sysmapid'],
|
||
|
'allowed_ui_maps' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_MAPS)
|
||
|
];
|
||
|
if (array_key_exists('severity_min', $data)) {
|
||
|
$menu_data['severity_min'] = $data['severity_min'];
|
||
|
}
|
||
|
if (array_key_exists('unique_id', $data)) {
|
||
|
$menu_data['unique_id'] = $data['unique_id'];
|
||
|
}
|
||
|
|
||
|
if ($selement['urls']) {
|
||
|
$menu_data['urls'] = self::sanitizeMapElementUrls($selement['urls']);
|
||
|
}
|
||
|
return $menu_data;
|
||
|
|
||
|
case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
|
||
|
$menu_data = [
|
||
|
'type' => 'map_element_group',
|
||
|
'groupid' => $selement['elements'][0]['groupid'],
|
||
|
'allowed_ui_problems' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_PROBLEMS)
|
||
|
];
|
||
|
if (array_key_exists('severity_min', $data)) {
|
||
|
$menu_data['severities'] = array_column(
|
||
|
CSeverityHelper::getSeverities((int) $data['severity_min']),
|
||
|
'value'
|
||
|
);
|
||
|
}
|
||
|
if ($db_map['show_suppressed']) {
|
||
|
$menu_data['show_suppressed'] = true;
|
||
|
}
|
||
|
if ($selement['urls']) {
|
||
|
$menu_data['urls'] = self::sanitizeMapElementUrls($selement['urls']);
|
||
|
}
|
||
|
if ($selement['tags']) {
|
||
|
$menu_data['evaltype'] = $selement['evaltype'];
|
||
|
$menu_data['tags'] = $selement['tags'];
|
||
|
}
|
||
|
return $menu_data;
|
||
|
|
||
|
case SYSMAP_ELEMENT_TYPE_HOST:
|
||
|
$host_data = [
|
||
|
'hostid' => $selement['elements'][0]['hostid']
|
||
|
];
|
||
|
if (array_key_exists('severity_min', $data)) {
|
||
|
$host_data['severity_min'] = $data['severity_min'];
|
||
|
}
|
||
|
if ($db_map['show_suppressed']) {
|
||
|
$host_data['show_suppressed'] = true;
|
||
|
}
|
||
|
if ($selement['urls']) {
|
||
|
$host_data['urls'] = $selement['urls'];
|
||
|
}
|
||
|
if ($selement['tags']) {
|
||
|
$host_data['evaltype'] = $selement['evaltype'];
|
||
|
$host_data['tags'] = $selement['tags'];
|
||
|
}
|
||
|
return self::getMenuDataHost($host_data);
|
||
|
|
||
|
case SYSMAP_ELEMENT_TYPE_TRIGGER:
|
||
|
$items = [];
|
||
|
$triggers = [];
|
||
|
$hosts = [];
|
||
|
$show_events = true;
|
||
|
$unique_itemids = [];
|
||
|
|
||
|
$db_triggers = API::Trigger()->get([
|
||
|
'output' => ['triggerid', 'description'],
|
||
|
'selectHosts' => ['hostid', 'name', 'status'],
|
||
|
'selectItems' => ['itemid', 'hostid', 'name', 'value_type', 'type'],
|
||
|
'triggerids' => array_column($selement['elements'], 'triggerid'),
|
||
|
'preservekeys' => true
|
||
|
]);
|
||
|
|
||
|
foreach ($db_triggers as $db_trigger) {
|
||
|
foreach ($db_trigger['hosts'] as $host) {
|
||
|
$hosts[$host['hostid']] = $host['name'];
|
||
|
}
|
||
|
|
||
|
foreach ($db_trigger['items'] as &$item) {
|
||
|
$item['hostname'] = $hosts[$item['hostid']];
|
||
|
|
||
|
if ($host['status'] != HOST_STATUS_MONITORED) {
|
||
|
$show_events = false;
|
||
|
}
|
||
|
}
|
||
|
unset($item);
|
||
|
|
||
|
CArrayHelper::sort($db_trigger['items'], ['name', 'hostname', 'itemid']);
|
||
|
|
||
|
$with_hostname = count($hosts) > 1;
|
||
|
|
||
|
foreach ($db_trigger['items'] as $item) {
|
||
|
if (in_array($item['itemid'], $unique_itemids)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$items[] = [
|
||
|
'name' => $with_hostname
|
||
|
? $item['hostname'].NAME_DELIMITER.$item['name']
|
||
|
: $item['name'],
|
||
|
'params' => [
|
||
|
'itemid' => $item['itemid'],
|
||
|
'action' => in_array(
|
||
|
$item['value_type'], [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64]
|
||
|
)
|
||
|
? HISTORY_GRAPH
|
||
|
: HISTORY_VALUES,
|
||
|
'is_webitem' => $item['type'] == ITEM_TYPE_HTTPTEST
|
||
|
]
|
||
|
];
|
||
|
|
||
|
$unique_itemids[] = $item['itemid'];
|
||
|
}
|
||
|
|
||
|
$triggers[] = [
|
||
|
'triggerid' => $db_trigger['triggerid'],
|
||
|
'description' => $db_trigger['description']
|
||
|
];
|
||
|
}
|
||
|
|
||
|
$menu_data = [
|
||
|
'type' => 'map_element_trigger',
|
||
|
'triggers' => $triggers,
|
||
|
'items' => $items,
|
||
|
'show_events' => $show_events,
|
||
|
'allowed_ui_problems' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_PROBLEMS),
|
||
|
'allowed_ui_conf_hosts' => CWebUser::checkAccess(CRoleHelper::UI_CONFIGURATION_HOSTS),
|
||
|
'allowed_ui_latest_data' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_LATEST_DATA)
|
||
|
];
|
||
|
|
||
|
if (array_key_exists('severity_min', $data)) {
|
||
|
$menu_data['severities'] = array_column(
|
||
|
CSeverityHelper::getSeverities((int) $data['severity_min']),
|
||
|
'value'
|
||
|
);
|
||
|
}
|
||
|
if ($db_map['show_suppressed']) {
|
||
|
$menu_data['show_suppressed'] = true;
|
||
|
}
|
||
|
|
||
|
if ($selement['urls']) {
|
||
|
$menu_data['urls'] = self::sanitizeMapElementUrls($selement['urls']);
|
||
|
}
|
||
|
|
||
|
return $menu_data;
|
||
|
|
||
|
case SYSMAP_ELEMENT_TYPE_IMAGE:
|
||
|
$menu_data = [
|
||
|
'type' => 'map_element_image'
|
||
|
];
|
||
|
if ($selement['urls']) {
|
||
|
$menu_data['urls'] = self::sanitizeMapElementUrls($selement['urls']);
|
||
|
}
|
||
|
return $menu_data;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
error(_('No permissions to referred object or it does not exist!'));
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepare data for trigger context menu popup.
|
||
|
*
|
||
|
* @param array $data
|
||
|
* string $data['triggerid'] Trigger ID.
|
||
|
* string $data['backurl'] URL from where the menu popup was called.
|
||
|
* string $data['eventid'] (optional) Mandatory for "Update problem", "Mark as cause"
|
||
|
* and "Mark selected as symptoms" context menus.
|
||
|
* array $data['ids'] (optional) Event IDs that are used in event rank change to
|
||
|
* symptom.
|
||
|
* bool $data['show_update_problem'] (optional) Whether to show "Update problem".
|
||
|
* bool $data['show_rank_change_cause'] (optional) Whether to show "Mark as cause".
|
||
|
* bool $data['show_rank_change_symptom'] (optional) Whether to show "Mark selected as symptoms".
|
||
|
*
|
||
|
* @return array|null
|
||
|
*/
|
||
|
private static function getMenuDataTrigger(array $data): ?array {
|
||
|
$db_triggers = API::Trigger()->get([
|
||
|
'output' => ['expression', 'url_name', 'url', 'comments', 'manual_close'],
|
||
|
'selectHosts' => ['hostid', 'name', 'status'],
|
||
|
'selectItems' => ['itemid', 'hostid', 'name', 'value_type', 'type'],
|
||
|
'triggerids' => $data['triggerid'],
|
||
|
'preservekeys' => true
|
||
|
]);
|
||
|
|
||
|
if ($db_triggers) {
|
||
|
$db_trigger = reset($db_triggers);
|
||
|
|
||
|
if (array_key_exists('eventid', $data)) {
|
||
|
$db_trigger['eventid'] = $data['eventid'];
|
||
|
}
|
||
|
|
||
|
$db_trigger['url'] = CMacrosResolverHelper::resolveTriggerUrl($db_trigger, $url) ? $url : '';
|
||
|
|
||
|
if ($db_trigger['url'] !== '') {
|
||
|
$db_trigger['url_name'] = CMacrosResolverHelper::resolveTriggerUrlName($db_trigger, $url_name)
|
||
|
? $url_name
|
||
|
: '';
|
||
|
}
|
||
|
|
||
|
$hosts = [];
|
||
|
$show_events = true;
|
||
|
|
||
|
foreach ($db_trigger['hosts'] as $host) {
|
||
|
$hosts[$host['hostid']] = $host['name'];
|
||
|
|
||
|
if ($host['status'] != HOST_STATUS_MONITORED) {
|
||
|
$show_events = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
foreach ($db_trigger['items'] as &$item) {
|
||
|
$item['hostname'] = $hosts[$item['hostid']];
|
||
|
}
|
||
|
unset($item);
|
||
|
|
||
|
CArrayHelper::sort($db_trigger['items'], ['name', 'hostname', 'itemid']);
|
||
|
|
||
|
$with_hostname = count($hosts) > 1;
|
||
|
$items = [];
|
||
|
|
||
|
foreach ($db_trigger['items'] as $item) {
|
||
|
$items[] = [
|
||
|
'name' => $with_hostname
|
||
|
? $item['hostname'].NAME_DELIMITER.$item['name']
|
||
|
: $item['name'],
|
||
|
'params' => [
|
||
|
'itemid' => $item['itemid'],
|
||
|
'action' => in_array($item['value_type'], [ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64])
|
||
|
? HISTORY_GRAPH
|
||
|
: HISTORY_VALUES,
|
||
|
'is_webitem' => $item['type'] == ITEM_TYPE_HTTPTEST
|
||
|
]
|
||
|
];
|
||
|
}
|
||
|
|
||
|
$menu_data = [
|
||
|
'type' => 'trigger',
|
||
|
'triggerid' => $data['triggerid'],
|
||
|
'backurl' => $data['backurl'],
|
||
|
'items' => $items,
|
||
|
'show_events' => $show_events,
|
||
|
'allowed_ui_problems' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_PROBLEMS),
|
||
|
'allowed_ui_conf_hosts' => CWebUser::checkAccess(CRoleHelper::UI_CONFIGURATION_HOSTS),
|
||
|
'allowed_ui_latest_data' => CWebUser::checkAccess(CRoleHelper::UI_MONITORING_LATEST_DATA),
|
||
|
'allowed_actions_change_problem_ranking' =>
|
||
|
CWebUser::checkAccess(CRoleHelper::ACTIONS_CHANGE_PROBLEM_RANKING)
|
||
|
];
|
||
|
|
||
|
$can_be_closed = ($db_trigger['manual_close'] == ZBX_TRIGGER_MANUAL_CLOSE_ALLOWED
|
||
|
&& CWebUser::checkAccess(CRoleHelper::ACTIONS_CLOSE_PROBLEMS)
|
||
|
);
|
||
|
$event = [];
|
||
|
|
||
|
if (array_key_exists('eventid', $data)) {
|
||
|
$menu_data['eventid'] = $data['eventid'];
|
||
|
|
||
|
$events = API::Event()->get([
|
||
|
'output' => ['eventid', 'r_eventid', 'urls', 'cause_eventid'],
|
||
|
'selectAcknowledges' => ['action'],
|
||
|
'eventids' => $data['eventid']
|
||
|
]);
|
||
|
|
||
|
if ($events) {
|
||
|
$event = $events[0];
|
||
|
|
||
|
if ($can_be_closed) {
|
||
|
$can_be_closed = !isEventClosed($event);
|
||
|
}
|
||
|
|
||
|
if (CWebUser::checkAccess(CRoleHelper::ACTIONS_CHANGE_PROBLEM_RANKING)) {
|
||
|
// Can change rank to cause if event is not already cause.
|
||
|
$menu_data['mark_as_cause'] = ($event['cause_eventid'] != 0);
|
||
|
|
||
|
// Check if selected events can change rank to symptom for given cause.
|
||
|
$menu_data['mark_selected_as_symptoms'] = array_key_exists('ids', $data) && $data['ids']
|
||
|
? (bool) validateEventRankChangeToSymptom($data['ids'], $data['eventid'])
|
||
|
: false;
|
||
|
|
||
|
$menu_data['eventids'] = array_key_exists('ids', $data) ? $data['ids'] : [];
|
||
|
|
||
|
// Show individual menus depending on location.
|
||
|
$menu_data['show_rank_change_cause'] = array_key_exists('show_rank_change_cause', $data)
|
||
|
&& $data['show_rank_change_cause'];
|
||
|
$menu_data['show_rank_change_symptom'] = array_key_exists('show_rank_change_symptom', $data)
|
||
|
&& $data['show_rank_change_symptom'];
|
||
|
$menu_data['csrf_tokens']['acknowledge'] = CCsrfTokenHelper::get('acknowledge');
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (array_key_exists('show_update_problem', $data)) {
|
||
|
$menu_data['show_update_problem'] = $data['show_update_problem']
|
||
|
&& (CWebUser::checkAccess(CRoleHelper::ACTIONS_ADD_PROBLEM_COMMENTS)
|
||
|
|| CWebUser::checkAccess(CRoleHelper::ACTIONS_CHANGE_SEVERITY)
|
||
|
|| CWebUser::checkAccess(CRoleHelper::ACTIONS_ACKNOWLEDGE_PROBLEMS)
|
||
|
|| $can_be_closed
|
||
|
|| CWebUser::checkAccess(CRoleHelper::ACTIONS_SUPPRESS_PROBLEMS)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$scripts_by_events = [];
|
||
|
|
||
|
if (CWebUser::checkAccess(CRoleHelper::ACTIONS_EXECUTE_SCRIPTS) && $event) {
|
||
|
$scripts_by_events = API::Script()->getScriptsByEvents([$event['eventid']]);
|
||
|
}
|
||
|
|
||
|
// Filter only event scope scripts and get rid of excess spaces and create full name with menu path included.
|
||
|
$scripts = [];
|
||
|
$urls = [];
|
||
|
|
||
|
foreach ($scripts_by_events as &$event_scripts) {
|
||
|
foreach ($event_scripts as $num => &$event_script) {
|
||
|
if ($event_script['scope'] != ZBX_SCRIPT_SCOPE_EVENT) {
|
||
|
unset($event_script[$num]);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$scriptid = $event_script['scriptid'];
|
||
|
|
||
|
// Split scripts and URLs.
|
||
|
if ($event_script['type'] == ZBX_SCRIPT_TYPE_URL) {
|
||
|
if (!array_key_exists($scriptid, $urls)) {
|
||
|
$urls[$scriptid] = $event_script;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (!array_key_exists($scriptid, $scripts)) {
|
||
|
$scripts[$scriptid] = $event_script;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
unset($event_script);
|
||
|
}
|
||
|
unset($event_scripts);
|
||
|
|
||
|
if ($event) {
|
||
|
foreach ($event['urls'] as &$url) {
|
||
|
$url['new_window'] = ZBX_SCRIPT_URL_NEW_WINDOW_YES;
|
||
|
$url['confirmation'] = '';
|
||
|
$url['menu_path'] = '';
|
||
|
}
|
||
|
unset($url);
|
||
|
|
||
|
$urls = array_merge($urls, $event['urls']);
|
||
|
}
|
||
|
|
||
|
if ($db_trigger['url'] !== '') {
|
||
|
$urls = array_merge($urls, [[
|
||
|
'name' => $db_trigger['url_name'] !== '' ? $db_trigger['url_name'] : _('Trigger URL'),
|
||
|
'url' => $db_trigger['url'],
|
||
|
'menu_path' => '',
|
||
|
'new_window' => ZBX_SCRIPT_URL_NEW_WINDOW_NO,
|
||
|
'confirmation' => ''
|
||
|
]]);
|
||
|
}
|
||
|
|
||
|
$scripts = self::sortEntitiesByMenuPath($scripts);
|
||
|
$urls = self::sortEntitiesByMenuPath($urls);
|
||
|
|
||
|
foreach (array_values($scripts) as $script) {
|
||
|
$menu_data['scripts'][] = [
|
||
|
'name' => $script['name'],
|
||
|
'menu_path' => $script['menu_path'],
|
||
|
'scriptid' => $script['scriptid'],
|
||
|
'confirmation' => $script['confirmation']
|
||
|
];
|
||
|
}
|
||
|
|
||
|
if ($scripts) {
|
||
|
$menu_data['csrf_tokens']['scriptexec'] = CCsrfTokenHelper::get('scriptexec');
|
||
|
}
|
||
|
|
||
|
foreach (array_values($urls) as $url) {
|
||
|
$menu_data['urls'][] = [
|
||
|
'label' => $url['name'],
|
||
|
'menu_path' => $url['menu_path'],
|
||
|
'url' => $url['url'],
|
||
|
'target' => $url['new_window'] == ZBX_SCRIPT_URL_NEW_WINDOW_YES ? '_blank' : '',
|
||
|
'confirmation' => $url['confirmation'],
|
||
|
'rel' => 'noopener'.(ZBX_NOREFERER ? ' noreferrer' : '')
|
||
|
];
|
||
|
}
|
||
|
|
||
|
if (array_key_exists('urls', $menu_data)) {
|
||
|
foreach ($menu_data['urls'] as &$url) {
|
||
|
if (!CHtmlUrlValidator::validate($url['url'], ['allow_user_macro' => false])) {
|
||
|
$url['url'] = 'javascript: alert('.
|
||
|
json_encode(_s('Provided URL "%1$s" is invalid.', $url['url'])).
|
||
|
');';
|
||
|
unset($url['target'], $url['rel']);
|
||
|
}
|
||
|
}
|
||
|
unset($url);
|
||
|
}
|
||
|
|
||
|
return $menu_data;
|
||
|
}
|
||
|
|
||
|
error(_('No permissions to referred object or it does not exist!'));
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Process menu path and sort scripts or URLs according to it.
|
||
|
*
|
||
|
* @param array $entities Scripts and URLs.
|
||
|
* @param string $entities[]['name'] Name of the ccript or URL.
|
||
|
* @param string $entities[]['menu_path'] Menu path of the ccript or URL.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private static function sortEntitiesByMenuPath(array $entities): array {
|
||
|
if ($entities) {
|
||
|
foreach ($entities as &$entity) {
|
||
|
$entity['menu_path'] = trimPath($entity['menu_path']);
|
||
|
$entity['sort'] = '';
|
||
|
|
||
|
if (strlen($entity['menu_path']) > 0) {
|
||
|
// First or only slash from beginning is trimmed.
|
||
|
if (substr($entity['menu_path'], 0, 1) === '/') {
|
||
|
$entity['menu_path'] = substr($entity['menu_path'], 1);
|
||
|
}
|
||
|
|
||
|
$entity['sort'] = $entity['menu_path'];
|
||
|
|
||
|
// If there is something more, check if last slash is present.
|
||
|
if (strlen($entity['menu_path']) > 0) {
|
||
|
if (substr($entity['menu_path'], -1) !== '/'
|
||
|
&& substr($entity['menu_path'], -2) === '\\/') {
|
||
|
$entity['sort'] = $entity['menu_path'].'/';
|
||
|
}
|
||
|
else {
|
||
|
$entity['sort'] = $entity['menu_path'];
|
||
|
}
|
||
|
|
||
|
if (substr($entity['menu_path'], -1) === '/'
|
||
|
&& substr($entity['menu_path'], -2) !== '\\/') {
|
||
|
$entity['menu_path'] = substr($entity['menu_path'], 0, -1);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$entity['sort'] = $entity['sort'].$entity['name'];
|
||
|
}
|
||
|
unset($entity);
|
||
|
|
||
|
CArrayHelper::sort($entities, ['sort']);
|
||
|
}
|
||
|
|
||
|
return $entities;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Prepare data for trigger macro context menu popup.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
private static function getMenuDataTriggerMacro() {
|
||
|
return ['type' => 'trigger_macro'];
|
||
|
}
|
||
|
|
||
|
protected function doAction() {
|
||
|
$data = $this->hasInput('data') ? $this->getInput('data') : [];
|
||
|
|
||
|
switch ($this->getInput('type')) {
|
||
|
case 'history':
|
||
|
$menu_data = self::getMenuDataHistory($data);
|
||
|
break;
|
||
|
|
||
|
case 'host':
|
||
|
$menu_data = self::getMenuDataHost($data);
|
||
|
break;
|
||
|
|
||
|
case 'item':
|
||
|
$menu_data = self::getMenuDataItem($data);
|
||
|
break;
|
||
|
|
||
|
case 'item_prototype':
|
||
|
$menu_data = self::getMenuDataItemPrototype($data);
|
||
|
break;
|
||
|
|
||
|
case 'map_element':
|
||
|
$menu_data = self::getMenuDataMapElement($data);
|
||
|
break;
|
||
|
|
||
|
case 'trigger':
|
||
|
$menu_data = self::getMenuDataTrigger($data);
|
||
|
break;
|
||
|
|
||
|
case 'trigger_macro':
|
||
|
$menu_data = self::getMenuDataTriggerMacro();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
$output = [];
|
||
|
|
||
|
if ($menu_data !== null) {
|
||
|
$output['data'] = $menu_data;
|
||
|
}
|
||
|
|
||
|
if ($messages = get_and_clear_messages()) {
|
||
|
$output['error']['messages'] = array_column($messages, 'message');
|
||
|
}
|
||
|
|
||
|
$this->setResponse(new CControllerResponseData(['main_block' => json_encode($output)]));
|
||
|
}
|
||
|
}
|