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.
874 lines
27 KiB
874 lines
27 KiB
<?php
|
|
/*
|
|
** 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.
|
|
**/
|
|
|
|
|
|
/**
|
|
* Returns all supported event source-object pairs.
|
|
*/
|
|
function eventSourceObjects(): array {
|
|
return [
|
|
['source' => EVENT_SOURCE_TRIGGERS, 'object' => EVENT_OBJECT_TRIGGER],
|
|
['source' => EVENT_SOURCE_DISCOVERY, 'object' => EVENT_OBJECT_DHOST],
|
|
['source' => EVENT_SOURCE_DISCOVERY, 'object' => EVENT_OBJECT_DSERVICE],
|
|
['source' => EVENT_SOURCE_AUTOREGISTRATION, 'object' => EVENT_OBJECT_AUTOREGHOST],
|
|
['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_TRIGGER],
|
|
['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_ITEM],
|
|
['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_LLDRULE],
|
|
['source' => EVENT_SOURCE_SERVICE, 'object' => EVENT_OBJECT_SERVICE]
|
|
];
|
|
}
|
|
|
|
function get_events_unacknowledged($db_element, $value_trigger = null, $value_event = null, $ack = false) {
|
|
$elements = ['hosts' => [], 'hosts_groups' => [], 'triggers' => []];
|
|
get_map_elements($db_element, $elements);
|
|
|
|
if (empty($elements['hosts_groups']) && empty($elements['hosts']) && empty($elements['triggers'])) {
|
|
return 0;
|
|
}
|
|
|
|
$options = [
|
|
'output' => ['triggerid'],
|
|
'monitored' => 1,
|
|
'skipDependent' => 1,
|
|
'limit' => CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT) + 1
|
|
];
|
|
if (!is_null($value_trigger)) {
|
|
$options['filter'] = ['value' => $value_trigger];
|
|
}
|
|
if (!empty($elements['hosts_groups'])) {
|
|
$options['groupids'] = array_unique($elements['hosts_groups']);
|
|
}
|
|
if (!empty($elements['hosts'])) {
|
|
$options['hostids'] = array_unique($elements['hosts']);
|
|
}
|
|
if (!empty($elements['triggers'])) {
|
|
$options['triggerids'] = array_unique($elements['triggers']);
|
|
}
|
|
$triggerids = API::Trigger()->get($options);
|
|
|
|
return API::Event()->get([
|
|
'source' => EVENT_SOURCE_TRIGGERS,
|
|
'object' => EVENT_OBJECT_TRIGGER,
|
|
'countOutput' => true,
|
|
'objectids' => zbx_objectValues($triggerids, 'triggerid'),
|
|
'filter' => [
|
|
'value' => $value_event,
|
|
'acknowledged' => $ack ? 1 : 0
|
|
]
|
|
]);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param array $event An array of event data.
|
|
* @param string $event['eventid'] Event ID.
|
|
* @param string $event['r_eventid'] OK event ID.
|
|
* @param string $event['cause_eventid'] Cause event ID.
|
|
* @param string $event['correlationid'] OK Event correlation ID.
|
|
* @param string $event['userid'] User ID who generated the OK event.
|
|
* @param string $event['name'] Event name.
|
|
* @param string $event['acknowledged'] State of acknowledgement.
|
|
* @param CCOl $event['opdata'] Operational data with expanded macros.
|
|
* @param string $event['comments'] Trigger description with expanded macros.
|
|
* @param array $allowed An array of user role rules.
|
|
* @param bool $allowed['ui_correlation'] Whether user is allowed to visit event correlation page.
|
|
*
|
|
* @return CTableInfo
|
|
*/
|
|
function make_event_details(array $event, array $allowed) {
|
|
$is_acknowledged = ($event['acknowledged'] == EVENT_ACKNOWLEDGED);
|
|
|
|
$table = (new CTableInfo())
|
|
->addRow([
|
|
_('Event'),
|
|
(new CCol($event['name']))->addClass(ZBX_STYLE_WORDWRAP)
|
|
])
|
|
->addRow([
|
|
_('Operational data'),
|
|
$event['opdata']->addClass(ZBX_STYLE_WORDBREAK)
|
|
])
|
|
->addRow([
|
|
_('Severity'),
|
|
CSeverityHelper::makeSeverityCell((int) $event['severity'])
|
|
])
|
|
->addRow([
|
|
_('Time'),
|
|
zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['clock'])
|
|
])
|
|
->addRow([
|
|
_('Acknowledged'),
|
|
(new CSpan($is_acknowledged ? _('Yes') : _('No')))->addClass(
|
|
$is_acknowledged ? ZBX_STYLE_GREEN : ZBX_STYLE_RED
|
|
)
|
|
]);
|
|
|
|
if ($event['r_eventid'] != 0) {
|
|
if ($event['correlationid'] != 0) {
|
|
$correlations = API::Correlation()->get([
|
|
'output' => ['correlationid', 'name'],
|
|
'correlationids' => [$event['correlationid']]
|
|
]);
|
|
|
|
if ($correlations) {
|
|
if ($allowed['ui_correlation']) {
|
|
$correlation_name = (new CLink($correlations[0]['name'],
|
|
(new CUrl('zabbix.php'))
|
|
->setArgument('action', 'correlation.edit')
|
|
->setArgument('correlationid', $correlations[0]['correlationid'])
|
|
->getUrl()
|
|
))->addClass(ZBX_STYLE_LINK_ALT);
|
|
}
|
|
else {
|
|
$correlation_name = $correlations[0]['name'];
|
|
}
|
|
}
|
|
else {
|
|
$correlation_name = _('Event correlation rule');
|
|
}
|
|
|
|
$table->addRow([_('Resolved by'), $correlation_name]);
|
|
}
|
|
elseif ($event['userid'] != 0) {
|
|
if ($event['userid'] == CWebUser::$data['userid']) {
|
|
$table->addRow([_('Resolved by'), getUserFullname([
|
|
'username' => CWebUser::$data['username'],
|
|
'name' => CWebUser::$data['name'],
|
|
'surname' => CWebUser::$data['surname']
|
|
])]);
|
|
}
|
|
else {
|
|
$user = API::User()->get([
|
|
'output' => ['username', 'name', 'surname'],
|
|
'userids' => [$event['userid']]
|
|
]);
|
|
|
|
if ($user) {
|
|
$table->addRow([_('Resolved by'), getUserFullname($user[0])]);
|
|
}
|
|
else {
|
|
$table->addRow([_('Resolved by'), _('Inaccessible user')]);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
$table->addRow([_('Resolved by'), _('Trigger')]);
|
|
}
|
|
}
|
|
|
|
$tags = makeTags([$event]);
|
|
|
|
$table
|
|
->addRow([_('Tags'), $tags[$event['eventid']]])
|
|
->addRow([_('Description'), (new CDiv(zbx_str2links($event['comments'])))->addClass(ZBX_STYLE_WORDBREAK)]);
|
|
|
|
if ($event['cause_eventid'] == 0) {
|
|
$table->addRow([_('Rank'), _('Cause')]);
|
|
}
|
|
else {
|
|
$cause_event = API::Event()->get([
|
|
'output' => ['name', 'objectid'],
|
|
'eventids' => $event['cause_eventid']
|
|
]);
|
|
|
|
if ($cause_event) {
|
|
$cause_event = reset($cause_event);
|
|
|
|
$has_trigger = (bool) API::Trigger()->get([
|
|
'output' => [],
|
|
'triggerids' => [$cause_event['objectid']]
|
|
]);
|
|
|
|
$table->addRow([_('Rank'),
|
|
[
|
|
_('Symptom'),
|
|
' (',
|
|
$has_trigger
|
|
? new CLink(
|
|
$cause_event['name'],
|
|
(new CUrl('tr_events.php'))
|
|
->setArgument('triggerid', $cause_event['objectid'])
|
|
->setArgument('eventid', $event['cause_eventid'])
|
|
)
|
|
: $cause_event['name'],
|
|
')'
|
|
]
|
|
]);
|
|
}
|
|
}
|
|
|
|
return $table;
|
|
}
|
|
|
|
/**
|
|
* Check if event status is UPDATING depending on acknowledges task ID.
|
|
*
|
|
* @param bool $in_closing True if problem is in CLOSING state.
|
|
* @param array $event Event data.
|
|
* @param array $event['acknowledges'] List of event acknowledges.
|
|
* @param int $event['acknowledges'][]['action'] Event action type.
|
|
* @param string $event['acknowledges'][]['taskid'] Task ID.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function isEventUpdating(bool $in_closing, array $event): bool {
|
|
$in_updating = false;
|
|
|
|
if (!$in_closing) {
|
|
foreach ($event['acknowledges'] as $acknowledge) {
|
|
if (($acknowledge['action'] & ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE) ==
|
|
ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE
|
|
|| ($acknowledge['action'] & ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM) ==
|
|
ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM) {
|
|
|
|
// If currently is cause or symptom and there is an active task.
|
|
if ($acknowledge['taskid'] != 0) {
|
|
$in_updating = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return $in_updating;
|
|
}
|
|
|
|
/**
|
|
* Calculate and return a rank change icon depending on current event rank change. If event is currently a cause event
|
|
* and it is undergoing a rank change, return cause event icon. If event is currently a symptom event and it is
|
|
* undergoing a rank change, return symptom event icon. Icon can be displayed regarless if current status is in closing.
|
|
*
|
|
* @param array $event Event data.
|
|
* @param array $event['acknowledges'] List of event acknowledges.
|
|
* @param int $event['acknowledges'][]['action'] Event action type.
|
|
* @param string $event['acknowledges'][]['taskid'] Task ID.
|
|
*
|
|
* @return Ctag|null
|
|
*/
|
|
function getEventStatusUpdateIcon(array $event): ?Ctag {
|
|
$icon = null;
|
|
$icon_class = '';
|
|
|
|
foreach ($event['acknowledges'] as $acknowledge) {
|
|
// If currently is symptom and there is an active task to convert to cause, set icon style to cause.
|
|
if (($acknowledge['action'] & ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE) == ZBX_PROBLEM_UPDATE_RANK_TO_CAUSE
|
|
&& $acknowledge['taskid'] != 0) {
|
|
$icon_class = ZBX_ICON_ARROW_RIGHT_TOP;
|
|
break;
|
|
}
|
|
|
|
// If currently is cause and there is an active task to convert to symptom, set icon style to symptom.
|
|
if (($acknowledge['action'] & ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM) ==
|
|
ZBX_PROBLEM_UPDATE_RANK_TO_SYMPTOM && $acknowledge['taskid'] != 0) {
|
|
$icon_class = ZBX_ICON_ARROW_TOP_RIGHT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($icon_class !== '') {
|
|
$icon = (new CSpan())
|
|
->addClass($icon_class)
|
|
->addClass('js-blink')
|
|
->setTitle(_('Updating'));
|
|
}
|
|
|
|
return $icon;
|
|
}
|
|
|
|
/**
|
|
* Calculate and return event status string: PROBLEM, RESOLVED, CLOSING or UPDATING depending on acknowledges task ID.
|
|
*
|
|
* @param bool $in_closing True if problem is in CLOSING state.
|
|
* @param array $event Event data.
|
|
*
|
|
* @return string
|
|
*/
|
|
function getEventStatusString(bool $in_closing, array $event): string {
|
|
if ($event['r_eventid'] != 0) {
|
|
$value_str = isEventUpdating($in_closing, $event) ? _('UPDATING') : _('RESOLVED');
|
|
}
|
|
else {
|
|
if ($in_closing) {
|
|
$value_str = _('CLOSING');
|
|
}
|
|
else {
|
|
$value_str = isEventUpdating($in_closing, $event) ? _('UPDATING') : _('PROBLEM');
|
|
}
|
|
}
|
|
|
|
return $value_str;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param array $startEvent An array of event data.
|
|
* @param string $startEvent['eventid'] Event ID.
|
|
* @param string $startEvent['objectid'] Object ID.
|
|
* @param array $allowed An array of user role rules.
|
|
* @param bool $allowed['add_comments'] Whether user is allowed to add problems comments.
|
|
* @param bool $allowed['change_severity'] Whether user is allowed to change problems severity.
|
|
* @param bool $allowed['acknowledge'] Whether user is allowed to acknowledge problems.
|
|
* @param bool $allowed['close'] Whether user is allowed to close problems.
|
|
* @param bool $allowed['suppress_problems'] Whether user is allowed to suppress/unsuppress problems.
|
|
* @param bool $allowed['rank_change'] Whether user is allowed to change problem ranking.
|
|
*
|
|
* @return CTableInfo
|
|
*/
|
|
function make_small_eventlist(array $startEvent, array $allowed) {
|
|
$table = (new CTableInfo())
|
|
->setHeader([
|
|
_('Time'),
|
|
_('Recovery time'),
|
|
_('Status'),
|
|
_('Age'),
|
|
_('Duration'),
|
|
_('Update'),
|
|
_('Actions')
|
|
]);
|
|
|
|
$events = API::Event()->get([
|
|
'output' => ['eventid', 'source', 'object', 'objectid', 'acknowledged', 'clock', 'ns', 'severity', 'r_eventid',
|
|
'cause_eventid'
|
|
],
|
|
'selectAcknowledges' => ['userid', 'clock', 'message', 'action', 'old_severity', 'new_severity',
|
|
'suppress_until', 'taskid'
|
|
],
|
|
'source' => EVENT_SOURCE_TRIGGERS,
|
|
'object' => EVENT_OBJECT_TRIGGER,
|
|
'value' => TRIGGER_VALUE_TRUE,
|
|
'objectids' => $startEvent['objectid'],
|
|
'eventid_till' => $startEvent['eventid'],
|
|
'sortfield' => ['clock', 'eventid'],
|
|
'sortorder' => ZBX_SORT_DOWN,
|
|
'limit' => 20,
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
$r_eventids = [];
|
|
|
|
foreach ($events as $event) {
|
|
$r_eventids[$event['r_eventid']] = true;
|
|
}
|
|
unset($r_eventids[0]);
|
|
|
|
$r_events = $r_eventids
|
|
? API::Event()->get([
|
|
'output' => ['clock'],
|
|
'source' => EVENT_SOURCE_TRIGGERS,
|
|
'object' => EVENT_OBJECT_TRIGGER,
|
|
'eventids' => array_keys($r_eventids),
|
|
'preservekeys' => true
|
|
])
|
|
: [];
|
|
|
|
$triggerids = [];
|
|
foreach ($events as &$event) {
|
|
$triggerids[] = $event['objectid'];
|
|
|
|
$event['r_clock'] = array_key_exists($event['r_eventid'], $r_events)
|
|
? $r_events[$event['r_eventid']]['clock']
|
|
: 0;
|
|
}
|
|
unset($event);
|
|
|
|
// Get trigger severities.
|
|
$triggers = $triggerids
|
|
? API::Trigger()->get([
|
|
'output' => ['priority', 'manual_close'],
|
|
'triggerids' => $triggerids,
|
|
'preservekeys' => true
|
|
])
|
|
: [];
|
|
|
|
$actions = getEventsActionsIconsData($events, $triggers);
|
|
$users = API::User()->get([
|
|
'output' => ['username', 'name', 'surname'],
|
|
'userids' => array_keys($actions['userids']),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
foreach ($events as $event) {
|
|
$duration = ($event['r_eventid'] != 0)
|
|
? zbx_date2age($event['clock'], $event['r_clock'])
|
|
: zbx_date2age($event['clock']);
|
|
|
|
$can_be_closed = $allowed['close'];
|
|
$in_closing = false;
|
|
|
|
if ($event['r_eventid'] != 0) {
|
|
$value = TRIGGER_VALUE_FALSE;
|
|
$value_clock = $event['r_clock'];
|
|
$can_be_closed = false;
|
|
}
|
|
else {
|
|
if (hasEventCloseAction($event['acknowledges'])) {
|
|
$in_closing = true;
|
|
$can_be_closed = false;
|
|
}
|
|
|
|
$value = $in_closing ? TRIGGER_VALUE_FALSE : TRIGGER_VALUE_TRUE;
|
|
$value_clock = $in_closing ? time() : $event['clock'];
|
|
}
|
|
|
|
$value_str = getEventStatusString($in_closing, $event);
|
|
$is_acknowledged = ($event['acknowledged'] == EVENT_ACKNOWLEDGED);
|
|
$cell_status = new CSpan($value_str);
|
|
|
|
if (isEventUpdating($in_closing, $event)) {
|
|
$cell_status->addClass('js-blink');
|
|
}
|
|
|
|
/*
|
|
* Add colors to span depending on configuration and trigger parameters. No blinking added to status,
|
|
* since the page is not on autorefresh.
|
|
*/
|
|
addTriggerValueStyle($cell_status, $value, $value_clock, $is_acknowledged);
|
|
|
|
// Create acknowledge link.
|
|
$problem_update_link = ($allowed['add_comments'] || $allowed['change_severity'] || $allowed['acknowledge']
|
|
|| $can_be_closed || $allowed['suppress_problems'] || $allowed['rank_change'])
|
|
? (new CLink(_('Update')))
|
|
->addClass(ZBX_STYLE_LINK_ALT)
|
|
->setAttribute('data-eventid', $event['eventid'])
|
|
->onClick('acknowledgePopUp({eventids: [this.dataset.eventid]}, this);')
|
|
: new CSpan(_('Update'));
|
|
|
|
$table->addRow([
|
|
(new CLink(zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['clock']),
|
|
'tr_events.php?triggerid='.$event['objectid'].'&eventid='.$event['eventid']
|
|
))->addClass('action'),
|
|
($event['r_eventid'] == 0)
|
|
? ''
|
|
: (new CLink(zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['r_clock']),
|
|
'tr_events.php?triggerid='.$event['objectid'].'&eventid='.$event['eventid']
|
|
))->addClass('action'),
|
|
$cell_status,
|
|
zbx_date2age($event['clock']),
|
|
$duration,
|
|
$problem_update_link,
|
|
makeEventActionsIcons($event['eventid'], $actions['data'], $users, $is_acknowledged)
|
|
]);
|
|
}
|
|
|
|
return $table;
|
|
}
|
|
|
|
/**
|
|
* Checks if event is closed.
|
|
*
|
|
* @param array $event Event object.
|
|
* @param array $event['r_eventid'] OK event id. 0 if not resolved.
|
|
* @param array $event['acknowledges'] List of problem updates.
|
|
* @param int $event['acknowledges'][]['action'] Action performed in update.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function isEventClosed(array $event): bool {
|
|
if ($event['r_eventid'] != 0) {
|
|
return true;
|
|
}
|
|
else {
|
|
return hasEventCloseAction($event['acknowledges']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if event has manual close action.
|
|
*
|
|
* @param array $event Event object.
|
|
* @param array $event['acknowledges'] List of problem updates.
|
|
* @param int $event['acknowledges'][]['action'] Action performed in update.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function hasEventCloseAction(array $acknowledges): bool {
|
|
foreach ($acknowledges as $acknowledge) {
|
|
if (($acknowledge['action'] & ZBX_PROBLEM_UPDATE_CLOSE) == ZBX_PROBLEM_UPDATE_CLOSE) {
|
|
// If at least one manual close update was found, event is closing.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns true if event is suppressed and not unsuppressed after that.
|
|
*
|
|
* @param array $acknowledges
|
|
* @param int $acknowledges['action']
|
|
* @param ?array $unsuppression_action [OUT] Variable to store suppression action data.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function isEventRecentlySuppressed(array $acknowledges, &$suppression_action = null): bool {
|
|
CArrayHelper::sort($acknowledges, [['field' => 'clock', 'order' => ZBX_SORT_DOWN]]);
|
|
|
|
foreach ($acknowledges as $ack) {
|
|
if (!array_key_exists('suppress_until', $ack)) {
|
|
continue;
|
|
}
|
|
|
|
if (($ack['action'] & ZBX_PROBLEM_UPDATE_UNSUPPRESS) == ZBX_PROBLEM_UPDATE_UNSUPPRESS) {
|
|
return false;
|
|
}
|
|
elseif (($ack['action'] & ZBX_PROBLEM_UPDATE_SUPPRESS) == ZBX_PROBLEM_UPDATE_SUPPRESS) {
|
|
if ($ack['suppress_until'] == ZBX_PROBLEM_SUPPRESS_TIME_INDEFINITE || $ack['suppress_until'] > time()) {
|
|
$suppression_action = $ack;
|
|
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns true if event is unsuppressed and not suppressed after that.
|
|
*
|
|
* @param array $acknowledges
|
|
* @param int $acknowledges['action']
|
|
* @param ?array $unsuppression_action [OUT] Variable to store unsuppression action data.
|
|
*
|
|
* @return bool
|
|
*/
|
|
function isEventRecentlyUnsuppressed(array $acknowledges, &$unsuppression_action = null): bool {
|
|
CArrayHelper::sort($acknowledges, [['field' => 'clock', 'order' => ZBX_SORT_DOWN]]);
|
|
|
|
foreach ($acknowledges as $ack) {
|
|
if (($ack['action'] & ZBX_PROBLEM_UPDATE_SUPPRESS) == ZBX_PROBLEM_UPDATE_SUPPRESS) {
|
|
return false;
|
|
}
|
|
elseif (($ack['action'] & ZBX_PROBLEM_UPDATE_UNSUPPRESS) == ZBX_PROBLEM_UPDATE_UNSUPPRESS) {
|
|
$unsuppression_action = $ack;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Place filter tags at the beginning of tags array.
|
|
*
|
|
* @param array $event_tags
|
|
* @param string $event_tags[]['tag']
|
|
* @param string $event_tags[]['value']
|
|
* @param array $f_tags
|
|
* @param int $f_tags[<tag>][]['operator']
|
|
* @param string $f_tags[<tag>][]['value']
|
|
*
|
|
* @return array
|
|
*/
|
|
function orderEventTags(array $event_tags, array $f_tags) {
|
|
$first_tags = [];
|
|
|
|
foreach ($event_tags as $i => $tag) {
|
|
if (array_key_exists($tag['tag'], $f_tags)) {
|
|
foreach ($f_tags[$tag['tag']] as $f_tag) {
|
|
if (($f_tag['operator'] == TAG_OPERATOR_EQUAL && $tag['value'] === $f_tag['value'])
|
|
|| ($f_tag['operator'] == TAG_OPERATOR_LIKE
|
|
&& ($f_tag['value'] === '' || stripos($tag['value'], $f_tag['value']) !== false))) {
|
|
$first_tags[] = $tag;
|
|
unset($event_tags[$i]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return array_merge($first_tags, $event_tags);
|
|
}
|
|
|
|
/**
|
|
* Place priority tags at the beginning of tags array.
|
|
*
|
|
* @param array $event_tags An array of event tags.
|
|
* @param string $event_tags[]['tag'] Tag name.
|
|
* @param string $event_tags[]['value'] Tag value.
|
|
* @param array $priorities An array of priority tag names.
|
|
*
|
|
* @return array
|
|
*/
|
|
function orderEventTagsByPriority(array $event_tags, array $priorities) {
|
|
$first_tags = [];
|
|
|
|
foreach ($priorities as $priority) {
|
|
foreach ($event_tags as $i => $tag) {
|
|
if ($tag['tag'] === $priority) {
|
|
$first_tags[] = $tag;
|
|
unset($event_tags[$i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return array_merge($first_tags, $event_tags);
|
|
}
|
|
|
|
/**
|
|
* Create element with tags.
|
|
*
|
|
* @param array $list
|
|
* @param string $list[][$key]
|
|
* @param array $list[]['tags']
|
|
* @param string $list[]['tags'][]['tag']
|
|
* @param string $list[]['tags'][]['value']
|
|
* @param bool $html
|
|
* @param string $key Name of tag source ID. Possible values:
|
|
* - 'eventid' - for events and problems (default);
|
|
* - 'hostid' - for hosts and host prototypes;
|
|
* - 'templateid' - for templates;
|
|
* - 'triggerid' - for triggers;
|
|
* - 'httptestid' - for web scenarios.
|
|
* @param int $list_tag_count Maximum number of tags to display.
|
|
* @param array $filter_tags An array of tag filtering data.
|
|
* @param ?array $subfilter_tags Array of selected sub-filter tags. Null when tags are not clickable.
|
|
* @param array $subfilter_tags[<tag>]
|
|
* @param array $subfilter_tags[<tag>][<value1>]
|
|
* @param array $subfilter_tags[<tag>][<value2>]
|
|
* @param array $subfilter_tags[<tag>][<value...>]
|
|
* @param string $filter_tags[]['tag']
|
|
* @param int $filter_tags[]['operator']
|
|
* @param string $filter_tags[]['value']
|
|
* @param int $tag_name_format Tag name format. Possible values:
|
|
* - TAG_NAME_FULL (default);
|
|
* - TAG_NAME_SHORTENED;
|
|
* - TAG_NAME_NONE.
|
|
* @param string $tag_priority A list of comma-separated tag names.
|
|
*
|
|
* @return array
|
|
*/
|
|
function makeTags(array $list, bool $html = true, string $key = 'eventid', int $list_tag_count = ZBX_TAG_COUNT_DEFAULT,
|
|
array $filter_tags = [], ?array $subfilter_tags = null, int $tag_name_format = TAG_NAME_FULL,
|
|
string $tag_priority = ''): array {
|
|
$tags = [];
|
|
|
|
if ($html) {
|
|
// Convert $filter_tags to a more usable format.
|
|
|
|
$f_tags = [];
|
|
|
|
foreach ($filter_tags as $tag) {
|
|
$f_tags[$tag['tag']][] = [
|
|
'operator' => $tag['operator'],
|
|
'value' => $tag['value']
|
|
];
|
|
}
|
|
}
|
|
|
|
if ($tag_priority !== '') {
|
|
$p_tags = explode(',', $tag_priority);
|
|
$p_tags = array_map('trim', $p_tags);
|
|
}
|
|
|
|
foreach ($list as $element) {
|
|
$tags[$element[$key]] = [];
|
|
|
|
if (!$element['tags']) {
|
|
continue;
|
|
}
|
|
|
|
CArrayHelper::sort($element['tags'], ['tag', 'value']);
|
|
|
|
if ($html) {
|
|
// Show first n tags and "..." with hint box if there are more.
|
|
|
|
$e_tags = $f_tags ? orderEventTags($element['tags'], $f_tags) : $element['tags'];
|
|
|
|
if ($tag_priority !== '') {
|
|
$e_tags = orderEventTagsByPriority($e_tags, $p_tags);
|
|
}
|
|
|
|
$tags_shown = 0;
|
|
|
|
foreach ($e_tags as $tag) {
|
|
$value = getTagString($tag, $tag_name_format);
|
|
|
|
if ($value !== '') {
|
|
if ($subfilter_tags !== null
|
|
&& !(array_key_exists($tag['tag'], $subfilter_tags)
|
|
&& array_key_exists($tag['value'], $subfilter_tags[$tag['tag']]))) {
|
|
$tags[$element[$key]][] = (new CSimpleButton($value))
|
|
->setAttribute('data-key', $tag['tag'])
|
|
->setAttribute('data-value', $tag['value'])
|
|
->onClick(
|
|
'view.setSubfilter([`subfilter_tags[${encodeURIComponent(this.dataset.key)}][]`,'.
|
|
'this.dataset.value'.
|
|
']);'
|
|
)
|
|
->addClass(ZBX_STYLE_BTN_TAG)
|
|
->setHint(getTagString($tag), '', false);
|
|
}
|
|
else {
|
|
$tags[$element[$key]][] = (new CSpan($value))
|
|
->addClass(ZBX_STYLE_TAG)
|
|
->setHint(getTagString($tag));
|
|
}
|
|
|
|
$tags_shown++;
|
|
|
|
if ($tags_shown >= $list_tag_count) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count($element['tags']) > $tags_shown) {
|
|
// Display all tags in hint box.
|
|
|
|
$hint_content = [];
|
|
|
|
foreach ($element['tags'] as $tag) {
|
|
$value = getTagString($tag);
|
|
|
|
if ($subfilter_tags !== null
|
|
&& !(array_key_exists($tag['tag'], $subfilter_tags)
|
|
&& array_key_exists($tag['value'], $subfilter_tags[$tag['tag']]))) {
|
|
$hint_content[$element[$key]][] = (new CSimpleButton($value))
|
|
->setAttribute('data-key', $tag['tag'])
|
|
->setAttribute('data-value', $tag['value'])
|
|
->onClick(
|
|
'view.setSubfilter([`subfilter_tags[${encodeURIComponent(this.dataset.key)}][]`,'.
|
|
'this.dataset.value'.
|
|
']);'
|
|
)
|
|
->addClass(ZBX_STYLE_BTN_TAG)
|
|
->setHint(getTagString($tag), '', false);
|
|
}
|
|
else {
|
|
$hint_content[$element[$key]][] = (new CSpan($value))
|
|
->addClass(ZBX_STYLE_TAG)
|
|
->setHint($value);
|
|
}
|
|
}
|
|
|
|
$tags[$element[$key]][] = (new CButtonIcon(ZBX_ICON_MORE))
|
|
->setHint($hint_content, ZBX_STYLE_HINTBOX_WRAP);
|
|
}
|
|
}
|
|
else {
|
|
// Show all and uncut for CSV.
|
|
|
|
foreach ($element['tags'] as $tag) {
|
|
$tags[$element[$key]][] = getTagString($tag);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $tags;
|
|
}
|
|
|
|
/**
|
|
* Returns tag name in selected format.
|
|
*
|
|
* @param array $tag
|
|
* @param string $tag['tag']
|
|
* @param string $tag['value']
|
|
* @param int $tag_name_format TAG_NAME_*
|
|
*
|
|
* @return string
|
|
*/
|
|
function getTagString(array $tag, $tag_name_format = TAG_NAME_FULL) {
|
|
switch ($tag_name_format) {
|
|
case TAG_NAME_NONE:
|
|
return $tag['value'];
|
|
|
|
case TAG_NAME_SHORTENED:
|
|
return mb_substr($tag['tag'], 0, 3).(($tag['value'] === '') ? '' : ': '.$tag['value']);
|
|
|
|
default:
|
|
return $tag['tag'].(($tag['value'] === '') ? '' : ': '.$tag['value']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Validate if the given events can change the rank by moving to a new cause. Linking a cause event with its symptoms
|
|
* (or only cause or only symptoms) to another different cause or symptom is allowed and will switch to the new cause as
|
|
* a result. Linking a cause to one of its own symptoms is also allowed and will simply switch the cause and symptom as
|
|
* a result. Linking a symptom to same cause is not allowed and that event ID is skipped. Linking a symptom to symptom
|
|
* of same cause is also not allowed and is skipped.
|
|
*
|
|
* @param array $eventids Array of event IDs that should be converted to symptom events.
|
|
* @param string $cause_eventid Event ID that will be the new cause ID for given $eventids.
|
|
*
|
|
* @return array Returns event IDs that are allowed to change rank.
|
|
*/
|
|
function validateEventRankChangeToSymptom(array $eventids, string $cause_eventid): array {
|
|
$eventids = array_fill_keys($eventids, true);
|
|
$all_eventids = $eventids;
|
|
$all_eventids[$cause_eventid] = true;
|
|
|
|
// Get all the events that were given in the request to check permissions.
|
|
$events = API::Event()->get([
|
|
'output' => ['eventid', 'cause_eventid'],
|
|
'eventids' => array_keys($all_eventids),
|
|
'preservekeys' => true
|
|
]);
|
|
|
|
// Early return. In case one of the events are missing, no rank change can occur.
|
|
if (count($events) != count($all_eventids)) {
|
|
return [];
|
|
}
|
|
|
|
// Early return. No matter if cause or symptom, source and destination cannot be the same.
|
|
if (count($eventids) == 1 && bccomp(key($eventids), $cause_eventid) == 0) {
|
|
return [];
|
|
}
|
|
|
|
$dst_event = $events[$cause_eventid];
|
|
|
|
foreach (array_keys($eventids) as $eventid) {
|
|
$event = $events[$eventid];
|
|
|
|
// Given cause is being moved.
|
|
if ($event['cause_eventid'] == 0) {
|
|
// Destination is cause. Cause is moved to same cause. Skip this event ID.
|
|
if ($dst_event['cause_eventid'] == 0 && bccomp($eventid, $dst_event['eventid']) == 0) {
|
|
unset($eventids[$eventid]);
|
|
}
|
|
}
|
|
// Given symptom is moved.
|
|
else {
|
|
// Destination is cause.
|
|
if ($dst_event['cause_eventid'] == 0) {
|
|
// Symptom current cause is the same as new cause. Skip this event ID.
|
|
if (bccomp($event['cause_eventid'], $dst_event['eventid']) == 0) {
|
|
unset($eventids[$eventid]);
|
|
}
|
|
}
|
|
// Destination is symptom.
|
|
else {
|
|
// Symptom destination is self. Skip this Event ID.
|
|
if (bccomp($eventid, $dst_event['eventid']) == 0) {
|
|
unset($eventids[$eventid]);
|
|
}
|
|
|
|
// If given symptom cause is not also in the list, skip this Event ID.
|
|
if (!array_key_exists($event['cause_eventid'], $eventids)) {
|
|
unset($eventids[$eventid]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return array_keys($eventids);
|
|
}
|