1<?php 2/* 3** Zabbix 4** Copyright (C) 2001-2021 Zabbix SIA 5** 6** This program is free software; you can redistribute it and/or modify 7** it under the terms of the GNU General Public License as published by 8** the Free Software Foundation; either version 2 of the License, or 9** (at your option) any later version. 10** 11** This program is distributed in the hope that it will be useful, 12** but WITHOUT ANY WARRANTY; without even the implied warranty of 13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14** GNU General Public License for more details. 15** 16** You should have received a copy of the GNU General Public License 17** along with this program; if not, write to the Free Software 18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 19**/ 20 21 22/** 23 * Returns the names of supported event sources. 24 * 25 * If the $source parameter is passed, returns the name of the specific source, otherwise - returns an array of all 26 * supported sources. 27 * 28 * @param int $source 29 * 30 * @return array|string 31 */ 32function eventSource($source = null) { 33 $sources = [ 34 EVENT_SOURCE_TRIGGERS => _('trigger'), 35 EVENT_SOURCE_DISCOVERY => _('discovery'), 36 EVENT_SOURCE_AUTOREGISTRATION => _('autoregistration'), 37 EVENT_SOURCE_INTERNAL => _x('internal', 'event source') 38 ]; 39 40 if ($source === null) { 41 return $sources; 42 } 43 44 return array_key_exists($source, $sources) ? $sources[$source] : _('Unknown'); 45} 46 47/** 48 * Returns the names of supported event objects. 49 * 50 * If the $source parameter is passed, returns the name of the specific object, otherwise - returns an array of all 51 * supported objects. 52 * 53 * @param int $object 54 * 55 * @return array|string 56 */ 57function eventObject($object = null) { 58 $objects = [ 59 EVENT_OBJECT_TRIGGER => _('trigger'), 60 EVENT_OBJECT_DHOST => _('discovered host'), 61 EVENT_OBJECT_DSERVICE => _('discovered service'), 62 EVENT_OBJECT_AUTOREGHOST => _('autoregistered host'), 63 EVENT_OBJECT_ITEM => _('item'), 64 EVENT_OBJECT_LLDRULE => _('low-level discovery rule') 65 ]; 66 67 if ($object === null) { 68 return $objects; 69 } 70 elseif (isset($objects[$object])) { 71 return $objects[$object]; 72 } 73 else { 74 return _('Unknown'); 75 } 76} 77 78/** 79 * Returns all supported event source-object pairs. 80 * 81 * @return array 82 */ 83function eventSourceObjects() { 84 return [ 85 ['source' => EVENT_SOURCE_TRIGGERS, 'object' => EVENT_OBJECT_TRIGGER], 86 ['source' => EVENT_SOURCE_DISCOVERY, 'object' => EVENT_OBJECT_DHOST], 87 ['source' => EVENT_SOURCE_DISCOVERY, 'object' => EVENT_OBJECT_DSERVICE], 88 ['source' => EVENT_SOURCE_AUTOREGISTRATION, 'object' => EVENT_OBJECT_AUTOREGHOST], 89 ['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_TRIGGER], 90 ['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_ITEM], 91 ['source' => EVENT_SOURCE_INTERNAL, 'object' => EVENT_OBJECT_LLDRULE] 92 ]; 93} 94 95function get_events_unacknowledged($db_element, $value_trigger = null, $value_event = null, $ack = false) { 96 $elements = ['hosts' => [], 'hosts_groups' => [], 'triggers' => []]; 97 get_map_elements($db_element, $elements); 98 99 if (empty($elements['hosts_groups']) && empty($elements['hosts']) && empty($elements['triggers'])) { 100 return 0; 101 } 102 103 $config = select_config(); 104 $options = [ 105 'output' => ['triggerid'], 106 'monitored' => 1, 107 'skipDependent' => 1, 108 'limit' => $config['search_limit'] + 1 109 ]; 110 if (!is_null($value_trigger)) { 111 $options['filter'] = ['value' => $value_trigger]; 112 } 113 if (!empty($elements['hosts_groups'])) { 114 $options['groupids'] = array_unique($elements['hosts_groups']); 115 } 116 if (!empty($elements['hosts'])) { 117 $options['hostids'] = array_unique($elements['hosts']); 118 } 119 if (!empty($elements['triggers'])) { 120 $options['triggerids'] = array_unique($elements['triggers']); 121 } 122 $triggerids = API::Trigger()->get($options); 123 124 return API::Event()->get([ 125 'source' => EVENT_SOURCE_TRIGGERS, 126 'object' => EVENT_OBJECT_TRIGGER, 127 'countOutput' => true, 128 'objectids' => zbx_objectValues($triggerids, 'triggerid'), 129 'filter' => [ 130 'value' => $value_event, 131 'acknowledged' => $ack ? 1 : 0 132 ] 133 ]); 134} 135 136/** 137 * 138 * @param array $event An array of event data. 139 * @param string $event['eventid'] Event ID. 140 * @param string $event['objectid'] Object ID. 141 * @param string $event['correlationid'] OK Event correlation ID. 142 * @param string $event['userid'] User ID who generated the OK event. 143 * @param string $event['name'] Event name. 144 * @param string $event['acknowledged'] State of acknowledgement. 145 * @param CCOl $event['opdata'] Operational data with expanded macros. 146 * @param string $event['comments'] Trigger description with expanded macros. 147 * 148 * @return CTableInfo 149 */ 150function make_event_details(array $event) { 151 $config = select_config(); 152 $is_acknowledged = ($event['acknowledged'] == EVENT_ACKNOWLEDGED); 153 154 $table = (new CTableInfo()) 155 ->addRow([ 156 _('Event'), 157 (new CCol($event['name']))->addClass(ZBX_STYLE_WORDWRAP) 158 ]) 159 ->addRow([ 160 _('Operational data'), 161 $event['opdata'] 162 ]) 163 ->addRow([ 164 _('Severity'), 165 getSeverityCell($event['severity'], $config) 166 ]) 167 ->addRow([ 168 _('Time'), 169 zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['clock']) 170 ]) 171 ->addRow([ 172 _('Acknowledged'), 173 (new CLink($is_acknowledged ? _('Yes') : _('No'))) 174 ->addClass($is_acknowledged ? ZBX_STYLE_GREEN : ZBX_STYLE_RED) 175 ->addClass(ZBX_STYLE_LINK_ALT) 176 ->onClick('acknowledgePopUp('.json_encode(['eventids' => [$event['eventid']]]).', this);') 177 ]); 178 179 if ($event['r_eventid'] != 0) { 180 if ($event['correlationid'] != 0) { 181 $correlations = API::Correlation()->get([ 182 'output' => ['correlationid', 'name'], 183 'correlationids' => [$event['correlationid']] 184 ]); 185 186 if ($correlations) { 187 if (CWebUser::getType() == USER_TYPE_SUPER_ADMIN) { 188 $correlation_name = (new CLink($correlations[0]['name'], 189 (new CUrl('correlation.php')) 190 ->setArgument('correlationid', $correlations[0]['correlationid']) 191 ->getUrl() 192 ))->addClass(ZBX_STYLE_LINK_ALT); 193 } 194 else { 195 $correlation_name = $correlations[0]['name']; 196 } 197 } 198 else { 199 $correlation_name = _('Correlation rule'); 200 } 201 202 $table->addRow([_('Resolved by'), $correlation_name]); 203 } 204 elseif ($event['userid'] != 0) { 205 if ($event['userid'] == CWebUser::$data['userid']) { 206 $table->addRow([_('Resolved by'), getUserFullname([ 207 'alias' => CWebUser::$data['alias'], 208 'name' => CWebUser::$data['name'], 209 'surname' => CWebUser::$data['surname'] 210 ])]); 211 } 212 else { 213 $user = API::User()->get([ 214 'output' => ['alias', 'name', 'surname'], 215 'userids' => [$event['userid']] 216 ]); 217 218 if ($user) { 219 $table->addRow([_('Resolved by'), getUserFullname($user[0])]); 220 } 221 else { 222 $table->addRow([_('Resolved by'), _('Inaccessible user')]); 223 } 224 } 225 } 226 else { 227 $table->addRow([_('Resolved by'), _('Trigger')]); 228 } 229 } 230 231 $tags = makeTags([$event]); 232 233 $table 234 ->addRow([_('Tags'), $tags[$event['eventid']]]) 235 ->addRow([_('Description'), (new CDiv(zbx_str2links($event['comments'])))]); 236 237 return $table; 238} 239 240function make_small_eventlist(array $startEvent) { 241 $config = select_config(); 242 243 $table = (new CTableInfo()) 244 ->setHeader([ 245 _('Time'), 246 _('Recovery time'), 247 _('Status'), 248 _('Age'), 249 _('Duration'), 250 _('Ack'), 251 _('Actions') 252 ]); 253 254 $events = API::Event()->get([ 255 'output' => ['eventid', 'source', 'object', 'objectid', 'acknowledged', 'clock', 'ns', 'severity', 'r_eventid'], 256 'select_acknowledges' => ['userid', 'clock', 'message', 'action', 'old_severity', 'new_severity'], 257 'source' => EVENT_SOURCE_TRIGGERS, 258 'object' => EVENT_OBJECT_TRIGGER, 259 'value' => TRIGGER_VALUE_TRUE, 260 'objectids' => $startEvent['objectid'], 261 'eventid_till' => $startEvent['eventid'], 262 'sortfield' => ['clock', 'eventid'], 263 'sortorder' => ZBX_SORT_DOWN, 264 'limit' => 20, 265 'preservekeys' => true 266 ]); 267 268 $r_eventids = []; 269 270 foreach ($events as $event) { 271 $r_eventids[$event['r_eventid']] = true; 272 } 273 unset($r_eventids[0]); 274 275 $r_events = $r_eventids 276 ? API::Event()->get([ 277 'output' => ['clock'], 278 'source' => EVENT_SOURCE_TRIGGERS, 279 'object' => EVENT_OBJECT_TRIGGER, 280 'eventids' => array_keys($r_eventids), 281 'preservekeys' => true 282 ]) 283 : []; 284 285 $triggerids = []; 286 foreach ($events as &$event) { 287 $triggerids[] = $event['objectid']; 288 289 $event['r_clock'] = array_key_exists($event['r_eventid'], $r_events) 290 ? $r_events[$event['r_eventid']]['clock'] 291 : 0; 292 } 293 unset($event); 294 295 // Get trigger severities. 296 $triggers = $triggerids 297 ? API::Trigger()->get([ 298 'output' => ['priority'], 299 'triggerids' => $triggerids, 300 'preservekeys' => true 301 ]) 302 : []; 303 304 $severity_config = [ 305 'severity_name_0' => $config['severity_name_0'], 306 'severity_name_1' => $config['severity_name_1'], 307 'severity_name_2' => $config['severity_name_2'], 308 'severity_name_3' => $config['severity_name_3'], 309 'severity_name_4' => $config['severity_name_4'], 310 'severity_name_5' => $config['severity_name_5'] 311 ]; 312 $actions = getEventsActionsIconsData($events, $triggers); 313 $users = API::User()->get([ 314 'output' => ['alias', 'name', 'surname'], 315 'userids' => array_keys($actions['userids']), 316 'preservekeys' => true 317 ]); 318 319 foreach ($events as $event) { 320 $duration = ($event['r_eventid'] != 0) 321 ? zbx_date2age($event['clock'], $event['r_clock']) 322 : zbx_date2age($event['clock']); 323 324 if ($event['r_eventid'] == 0) { 325 $in_closing = false; 326 327 foreach ($event['acknowledges'] as $acknowledge) { 328 if (($acknowledge['action'] & ZBX_PROBLEM_UPDATE_CLOSE) == ZBX_PROBLEM_UPDATE_CLOSE) { 329 $in_closing = true; 330 break; 331 } 332 } 333 334 $value = $in_closing ? TRIGGER_VALUE_FALSE : TRIGGER_VALUE_TRUE; 335 $value_str = $in_closing ? _('CLOSING') : _('PROBLEM'); 336 $value_clock = $in_closing ? time() : $event['clock']; 337 } 338 else { 339 $value = TRIGGER_VALUE_FALSE; 340 $value_str = _('RESOLVED'); 341 $value_clock = $event['r_clock']; 342 } 343 344 $is_acknowledged = ($event['acknowledged'] == EVENT_ACKNOWLEDGED); 345 $cell_status = new CSpan($value_str); 346 347 /* 348 * Add colors to span depending on configuration and trigger parameters. No blinking added to status, 349 * since the page is not on autorefresh. 350 */ 351 addTriggerValueStyle($cell_status, $value, $value_clock, $is_acknowledged); 352 353 // Create acknowledge link. 354 $problem_update_link = (new CLink($is_acknowledged ? _('Yes') : _('No'))) 355 ->addClass($is_acknowledged ? ZBX_STYLE_GREEN : ZBX_STYLE_RED) 356 ->addClass(ZBX_STYLE_LINK_ALT) 357 ->onClick('acknowledgePopUp('.json_encode(['eventids' => [$event['eventid']]]).', this);'); 358 359 $table->addRow([ 360 (new CLink(zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['clock']), 361 'tr_events.php?triggerid='.$event['objectid'].'&eventid='.$event['eventid'] 362 ))->addClass('action'), 363 ($event['r_eventid'] == 0) 364 ? '' 365 : (new CLink(zbx_date2str(DATE_TIME_FORMAT_SECONDS, $event['r_clock']), 366 'tr_events.php?triggerid='.$event['objectid'].'&eventid='.$event['eventid'] 367 ))->addClass('action'), 368 $cell_status, 369 zbx_date2age($event['clock']), 370 $duration, 371 $problem_update_link, 372 makeEventActionsIcons($event['eventid'], $actions['data'], $users, $severity_config) 373 ]); 374 } 375 376 return $table; 377} 378 379/** 380 * Place filter tags at the beginning of tags array. 381 * 382 * @param array $event_tags 383 * @param string $event_tags[]['tag'] 384 * @param string $event_tags[]['value'] 385 * @param array $f_tags 386 * @param int $f_tags[<tag>][]['operator'] 387 * @param string $f_tags[<tag>][]['value'] 388 * 389 * @return array 390 */ 391function orderEventTags(array $event_tags, array $f_tags) { 392 $first_tags = []; 393 394 foreach ($event_tags as $i => $tag) { 395 if (array_key_exists($tag['tag'], $f_tags)) { 396 foreach ($f_tags[$tag['tag']] as $f_tag) { 397 if (($f_tag['operator'] == TAG_OPERATOR_EQUAL && $tag['value'] === $f_tag['value']) 398 || ($f_tag['operator'] == TAG_OPERATOR_LIKE 399 && ($f_tag['value'] === '' || stripos($tag['value'], $f_tag['value']) !== false))) { 400 $first_tags[] = $tag; 401 unset($event_tags[$i]); 402 break; 403 } 404 } 405 } 406 } 407 408 return array_merge($first_tags, $event_tags); 409} 410 411/** 412 * Place priority tags at the beginning of tags array. 413 * 414 * @param array $event_tags An array of event tags. 415 * @param string $event_tags[]['tag'] Tag name. 416 * @param string $event_tags[]['value'] Tag value. 417 * @param array $priorities An array of priority tag names. 418 * 419 * @return array 420 */ 421function orderEventTagsByPriority(array $event_tags, array $priorities) { 422 $first_tags = []; 423 424 foreach ($priorities as $priority) { 425 foreach ($event_tags as $i => $tag) { 426 if ($tag['tag'] === $priority) { 427 $first_tags[] = $tag; 428 unset($event_tags[$i]); 429 } 430 } 431 } 432 433 return array_merge($first_tags, $event_tags); 434} 435 436/** 437 * Create element with tags. 438 * 439 * @param array $list 440 * @param string $list[][$key] 441 * @param array $list[]['tags'] 442 * @param string $list[]['tags'][]['tag'] 443 * @param string $list[]['tags'][]['value'] 444 * @param bool $html 445 * @param string $key Name of tag source ID. Possible values: 446 * - 'eventid' - for events and problems (default); 447 * - 'hostid' - for hosts; 448 * - 'templateid' - for templates; 449 * - 'triggerid' - for triggers. 450 * @param int $list_tag_count Maximum number of tags to display. 451 * @param array $filter_tags An array of tag filtering data. 452 * @param string $filter_tags[]['tag'] 453 * @param int $filter_tags[]['operator'] 454 * @param string $filter_tags[]['value'] 455 * @param int $tag_name_format Tag name format. Possible values: 456 * - PROBLEMS_TAG_NAME_FULL (default); 457 * - PROBLEMS_TAG_NAME_SHORTENED; 458 * - PROBLEMS_TAG_NAME_NONE. 459 * @param string $tag_priority A list of comma-separated tag names. 460 * 461 * @return array 462 */ 463function makeTags(array $list, $html = true, $key = 'eventid', $list_tag_count = ZBX_TAG_COUNT_DEFAULT, 464 array $filter_tags = [], $tag_name_format = PROBLEMS_TAG_NAME_FULL, $tag_priority = '') { 465 $tags = []; 466 467 if ($html) { 468 // Convert $filter_tags to a more usable format. 469 470 $f_tags = []; 471 472 foreach ($filter_tags as $tag) { 473 $f_tags[$tag['tag']][] = [ 474 'operator' => $tag['operator'], 475 'value' => $tag['value'] 476 ]; 477 } 478 } 479 480 if ($tag_priority !== '') { 481 $p_tags = explode(',', $tag_priority); 482 $p_tags = array_map('trim', $p_tags); 483 } 484 485 foreach ($list as $element) { 486 $tags[$element[$key]] = []; 487 488 if (!$element['tags']) { 489 continue; 490 } 491 492 CArrayHelper::sort($element['tags'], ['tag', 'value']); 493 494 if ($html) { 495 // Show first n tags and "..." with hint box if there are more. 496 497 $e_tags = $f_tags ? orderEventTags($element['tags'], $f_tags) : $element['tags']; 498 499 if ($tag_priority !== '') { 500 $e_tags = orderEventTagsByPriority($e_tags, $p_tags); 501 } 502 503 $tags_shown = 0; 504 505 foreach ($e_tags as $tag) { 506 $value = getTagString($tag, $tag_name_format); 507 508 if ($value !== '') { 509 $tags[$element[$key]][] = (new CSpan($value)) 510 ->addClass(ZBX_STYLE_TAG) 511 ->setHint(getTagString($tag)); 512 513 $tags_shown++; 514 515 if ($tags_shown >= $list_tag_count) { 516 break; 517 } 518 } 519 } 520 521 if (count($element['tags']) > $tags_shown) { 522 // Display all tags in hint box. 523 524 $hint_content = []; 525 526 foreach ($element['tags'] as $tag) { 527 $value = getTagString($tag); 528 $hint_content[$element[$key]][] = (new CSpan($value)) 529 ->addClass(ZBX_STYLE_TAG) 530 ->setHint($value); 531 } 532 533 $tags[$element[$key]][] = (new CSpan( 534 (new CButton(null)) 535 ->addClass(ZBX_STYLE_ICON_WZRD_ACTION) 536 ->setHint(new CDiv($hint_content), '', true, 'max-width: 500px') 537 ))->addClass(ZBX_STYLE_REL_CONTAINER); 538 } 539 } 540 else { 541 // Show all and uncut for CSV. 542 543 foreach ($element['tags'] as $tag) { 544 $tags[$element[$key]][] = getTagString($tag); 545 } 546 } 547 } 548 549 return $tags; 550} 551 552/** 553 * Returns tag name in selected format. 554 * 555 * @param array $tag 556 * @param string $tag['tag'] 557 * @param string $tag['value'] 558 * @param int $tag_name_format PROBLEMS_TAG_NAME_* 559 * 560 * @return string 561 */ 562function getTagString(array $tag, $tag_name_format = PROBLEMS_TAG_NAME_FULL) { 563 switch ($tag_name_format) { 564 case PROBLEMS_TAG_NAME_NONE: 565 return $tag['value']; 566 567 case PROBLEMS_TAG_NAME_SHORTENED: 568 return substr($tag['tag'], 0, 3).(($tag['value'] === '') ? '' : ': '.$tag['value']); 569 570 default: 571 return $tag['tag'].(($tag['value'] === '') ? '' : ': '.$tag['value']); 572 } 573} 574