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 22function sysmap_element_types($type = null) { 23 $types = [ 24 SYSMAP_ELEMENT_TYPE_HOST => _('Host'), 25 SYSMAP_ELEMENT_TYPE_HOST_GROUP => _('Host group'), 26 SYSMAP_ELEMENT_TYPE_TRIGGER => _('Trigger'), 27 SYSMAP_ELEMENT_TYPE_MAP => _('Map'), 28 SYSMAP_ELEMENT_TYPE_IMAGE => _('Image') 29 ]; 30 31 if (is_null($type)) { 32 natsort($types); 33 return $types; 34 } 35 elseif (isset($types[$type])) { 36 return $types[$type]; 37 } 38 else { 39 return _('Unknown'); 40 } 41} 42 43function sysmapElementLabel($label = null) { 44 $labels = [ 45 MAP_LABEL_TYPE_LABEL => _('Label'), 46 MAP_LABEL_TYPE_IP => _('IP address'), 47 MAP_LABEL_TYPE_NAME => _('Element name'), 48 MAP_LABEL_TYPE_STATUS => _('Status only'), 49 MAP_LABEL_TYPE_NOTHING => _('Nothing'), 50 MAP_LABEL_TYPE_CUSTOM => _('Custom label') 51 ]; 52 53 if (is_null($label)) { 54 return $labels; 55 } 56 elseif (isset($labels[$label])) { 57 return $labels[$label]; 58 } 59 else { 60 return false; 61 } 62} 63 64/** 65 * Get actions (data for popup menu) for map elements. 66 * 67 * @param array $sysmap 68 * @param array $sysmap['selements'] 69 * @param array $options Options used to retrieve actions. 70 * @param int $options['severity_min'] Minimal severity used. 71 * @param int $options['unique_id'] 72 * 73 * @return array 74 */ 75function getActionsBySysmap(array $sysmap, array $options = []) { 76 $actions = []; 77 $severity_min = array_key_exists('severity_min', $options) 78 ? $options['severity_min'] 79 : TRIGGER_SEVERITY_NOT_CLASSIFIED; 80 81 foreach ($sysmap['selements'] as $selementid => $elem) { 82 if ($elem['permission'] < PERM_READ) { 83 continue; 84 } 85 86 if (array_key_exists('unique_id', $options)) { 87 $elem['unique_id'] = $options['unique_id']; 88 } 89 90 $hostid = ($elem['elementtype_orig'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP 91 && $elem['elementsubtype_orig'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS) 92 ? $elem['elements'][0]['hostid'] 93 : 0; 94 95 $map = CMenuPopupHelper::getMapElement($sysmap['sysmapid'], $elem, $severity_min, $hostid); 96 97 $actions[$selementid] = json_encode($map); 98 } 99 100 return $actions; 101} 102 103function get_png_by_selement($info) { 104 $image = get_image_by_imageid($info['iconid']); 105 106 return $image['image'] ? imagecreatefromstring($image['image']) : get_default_image(); 107} 108 109function get_map_elements($db_element, &$elements) { 110 switch ($db_element['elementtype']) { 111 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 112 $elements['hosts_groups'][] = $db_element['elements'][0]['groupid']; 113 break; 114 case SYSMAP_ELEMENT_TYPE_HOST: 115 $elements['hosts'][] = $db_element['elements'][0]['hostid']; 116 break; 117 case SYSMAP_ELEMENT_TYPE_TRIGGER: 118 foreach ($db_element['elements'] as $db_element) { 119 $elements['triggers'][] = $db_element['triggerid']; 120 } 121 break; 122 case SYSMAP_ELEMENT_TYPE_MAP: 123 $map = API::Map()->get([ 124 'output' => [], 125 'selectSelements' => ['selementid', 'elements', 'elementtype'], 126 'sysmapids' => $db_element['elements'][0]['sysmapid'], 127 'nopermissions' => true 128 ]); 129 130 if ($map) { 131 $map = reset($map); 132 133 foreach ($map['selements'] as $db_mapelement) { 134 get_map_elements($db_mapelement, $elements); 135 } 136 } 137 break; 138 } 139} 140 141/** 142 * Adds names to elements. Adds expression for SYSMAP_ELEMENT_TYPE_TRIGGER elements. 143 * 144 * @param array $selements 145 * @param array $selements[]['elements'] 146 * @param int $selements[]['elementtype'] 147 * @param int $selements[]['iconid_off'] 148 * @param int $selements[]['permission'] 149 */ 150function addElementNames(array &$selements) { 151 $hostids = []; 152 $triggerids = []; 153 $sysmapids = []; 154 $groupids = []; 155 $imageids = []; 156 157 foreach ($selements as $selement) { 158 if ($selement['permission'] < PERM_READ) { 159 continue; 160 } 161 162 switch ($selement['elementtype']) { 163 case SYSMAP_ELEMENT_TYPE_HOST: 164 $hostids[$selement['elements'][0]['hostid']] = $selement['elements'][0]['hostid']; 165 break; 166 167 case SYSMAP_ELEMENT_TYPE_MAP: 168 $sysmapids[$selement['elements'][0]['sysmapid']] = $selement['elements'][0]['sysmapid']; 169 break; 170 171 case SYSMAP_ELEMENT_TYPE_TRIGGER: 172 foreach ($selement['elements'] as $element) { 173 $triggerids[$element['triggerid']] = $element['triggerid']; 174 } 175 break; 176 177 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 178 $groupids[$selement['elements'][0]['groupid']] = $selement['elements'][0]['groupid']; 179 break; 180 181 case SYSMAP_ELEMENT_TYPE_IMAGE: 182 $imageids[$selement['iconid_off']] = $selement['iconid_off']; 183 break; 184 } 185 } 186 187 $hosts = $hostids 188 ? API::Host()->get([ 189 'output' => ['name'], 190 'hostids' => $hostids, 191 'preservekeys' => true 192 ]) 193 : []; 194 195 $maps = $sysmapids 196 ? API::Map()->get([ 197 'output' => ['name'], 198 'sysmapids' => $sysmapids, 199 'preservekeys' => true 200 ]) 201 : []; 202 203 $triggers = $triggerids 204 ? API::Trigger()->get([ 205 'output' => ['description', 'expression', 'priority'], 206 'selectHosts' => ['name'], 207 'triggerids' => $triggerids, 208 'preservekeys' => true 209 ]) 210 : []; 211 $triggers = CMacrosResolverHelper::resolveTriggerNames($triggers); 212 213 $groups = $groupids 214 ? API::HostGroup()->get([ 215 'output' => ['name'], 216 'hostgroupids' => $groupids, 217 'preservekeys' => true 218 ]) 219 : []; 220 221 $images = $imageids 222 ? API::Image()->get([ 223 'output' => ['name'], 224 'imageids' => $imageids, 225 'preservekeys' => true 226 ]) 227 : []; 228 229 foreach ($selements as $snum => &$selement) { 230 if ($selement['permission'] < PERM_READ) { 231 continue; 232 } 233 234 switch ($selement['elementtype']) { 235 case SYSMAP_ELEMENT_TYPE_HOST: 236 $selements[$snum]['elements'][0]['elementName'] = $hosts[$selement['elements'][0]['hostid']]['name']; 237 break; 238 239 case SYSMAP_ELEMENT_TYPE_MAP: 240 $selements[$snum]['elements'][0]['elementName'] = $maps[$selement['elements'][0]['sysmapid']]['name']; 241 break; 242 243 case SYSMAP_ELEMENT_TYPE_TRIGGER: 244 foreach ($selement['elements'] as $enum => &$element) { 245 if (array_key_exists($element['triggerid'], $triggers)) { 246 $trigger = $triggers[$element['triggerid']]; 247 $element['elementName'] = $trigger['hosts'][0]['name'].NAME_DELIMITER.$trigger['description']; 248 $element['priority'] = $trigger['priority']; 249 } 250 else { 251 unset($selement['elements'][$enum]); 252 } 253 } 254 unset($element); 255 $selement['elements'] = array_values($selement['elements']); 256 break; 257 258 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 259 $selements[$snum]['elements'][0]['elementName'] = $groups[$selement['elements'][0]['groupid']]['name']; 260 break; 261 262 case SYSMAP_ELEMENT_TYPE_IMAGE: 263 if (array_key_exists($selement['iconid_off'], $images)) { 264 $selements[$snum]['elements'][0]['elementName'] = $images[$selement['iconid_off']]['name']; 265 } 266 break; 267 } 268 } 269 unset($selement); 270} 271 272/** 273 * Returns selement icon rendering parameters. 274 * 275 * @param array $i 276 * @param int $i['elementtype'] Element type. Possible values: 277 * SYSMAP_ELEMENT_TYPE_HOST, SYSMAP_ELEMENT_TYPE_MAP, 278 * SYSMAP_ELEMENT_TYPE_TRIGGER, SYSMAP_ELEMENT_TYPE_HOST_GROUP, 279 * SYSMAP_ELEMENT_TYPE_IMAGE. 280 * @param int $i['disabled'] The number of disabled hosts. 281 * @param int $i['maintenance'] The number of hosts in maintenance. 282 * @param int $i['problem'] The number of problems. 283 * @param int $i['problem_unack'] The number of unacknowledged problems. 284 * @param int $i['iconid_off'] Icon ID for element without problems. 285 * @param int $i['iconid_on'] Icon ID for element with problems. 286 * @param int $i['iconid_maintenance'] Icon ID for element with hosts in maintenance. 287 * @param int $i['iconid_disabled'] Icon ID for disabled element. 288 * @param bool $i['latelyChanged'] Whether trigger status has changed recently. 289 * @param int $i['priority'] Problem severity. Possible values: 290 * TRIGGER_SEVERITY_NOT_CLASSIFIED, TRIGGER_SEVERITY_INFORMATION, 291 * TRIGGER_SEVERITY_WARNING, TRIGGER_SEVERITY_AVERAGE, TRIGGER_SEVERITY_HIGH, 292 * TRIGGER_SEVERITY_DISASTER. 293 * @param int $i['expandproblem'] Map "Display problems" option. Possible values: 294 * SYSMAP_SINGLE_PROBLEM, SYSMAP_PROBLEMS_NUMBER, 295 * SYSMAP_PROBLEMS_NUMBER_CRITICAL. 296 * @param string $i['problem_title'] (optional) The name of the most critical problem. 297 * @param int|null $show_unack (optional) Map "Problem display" option. Possible values: 298 * EXTACK_OPTION_ALL, EXTACK_OPTION_UNACK, EXTACK_OPTION_BOTH. 299 * 300 * @return array 301 */ 302function getSelementInfo(array $i, ?int $show_unack = null): array { 303 if ($i['elementtype'] == SYSMAP_ELEMENT_TYPE_IMAGE) { 304 return [ 305 'iconid' => $i['iconid_off'], 306 'icon_type' => SYSMAP_ELEMENT_ICON_OFF, 307 'name' => _('Image'), 308 'latelyChanged' => false 309 ]; 310 } 311 312 $info = [ 313 'latelyChanged' => $i['latelyChanged'], 314 'ack' => !$i['problem_unack'], 315 'priority' => $i['priority'], 316 'info' => [], 317 'iconid' => $i['iconid_off'], 318 'aria_label' => '' 319 ]; 320 321 if ($i['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST && $i['disabled']) { 322 $info['iconid'] = $i['iconid_disabled']; 323 $info['icon_type'] = SYSMAP_ELEMENT_ICON_DISABLED; 324 $info['info']['status'] = [ 325 'msg' => _('Disabled'), 326 'color' => '960000' 327 ]; 328 329 return $info; 330 } 331 332 $has_problem = false; 333 334 if ($i['problem']) { 335 if ($show_unack == EXTACK_OPTION_ALL || $show_unack == EXTACK_OPTION_BOTH) { 336 $msg = ''; 337 338 // Expand single problem. 339 if ($i['expandproblem'] == SYSMAP_SINGLE_PROBLEM) { 340 $msg = ($i['problem'] == 1) ? $i['problem_title'] : _n('%1$s problem', '%1$s problems', $i['problem']); 341 } 342 // Number of problems. 343 elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER) { 344 $msg = _n('%1$s problem', '%1$s problems', $i['problem']); 345 } 346 // Number of problems and expand most critical one. 347 elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER_CRITICAL) { 348 $msg = $i['problem_title']; 349 350 if ($i['problem'] > 1) { 351 $msg .= "\n"._n('%1$s problem', '%1$s problems', $i['problem']); 352 } 353 } 354 355 $info['info']['problem'] = [ 356 'msg' => $msg, 357 'color' => getSelementLabelColor(true, !$i['problem_unack']) 358 ]; 359 } 360 361 if ($i['problem_unack'] && ($show_unack == EXTACK_OPTION_UNACK || $show_unack == EXTACK_OPTION_BOTH)) { 362 $msg = ''; 363 364 if ($show_unack == EXTACK_OPTION_UNACK) { 365 if ($i['expandproblem'] == SYSMAP_SINGLE_PROBLEM) { 366 $msg = ($i['problem_unack'] == 1) 367 ? $i['problem_title'] 368 : _n('%1$s unacknowledged problem', '%1$s unacknowledged problems', $i['problem_unack']); 369 } 370 elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER) { 371 $msg = _n('%1$s unacknowledged problem', '%1$s unacknowledged problems', $i['problem_unack']); 372 } 373 elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER_CRITICAL) { 374 $msg = $i['problem_title']; 375 376 if ($i['problem_unack'] > 1) { 377 $msg .= "\n". 378 _n('%1$s unacknowledged problem', '%1$s unacknowledged problems', $i['problem_unack']); 379 } 380 } 381 } 382 elseif ($show_unack == EXTACK_OPTION_BOTH) { 383 $msg = _n('%1$s unacknowledged problem', '%1$s unacknowledged problems', $i['problem_unack']); 384 } 385 386 $info['info']['unack'] = [ 387 'msg' => $msg, 388 'color' => getSelementLabelColor(true, false) 389 ]; 390 } 391 392 // Set element to problem state if it has problem events. 393 if ($info['info']) { 394 $info['iconid'] = $i['iconid_on']; 395 $info['icon_type'] = SYSMAP_ELEMENT_ICON_ON; 396 $has_problem = true; 397 } 398 399 $info['aria_label'] = ($i['problem'] > 1) 400 ? _n('%1$s problem', '%1$s problems', $i['problem']) 401 : $i['problem_title']; 402 } 403 404 if ($i['maintenance']) { 405 $info['iconid'] = $i['iconid_maintenance']; 406 $info['icon_type'] = SYSMAP_ELEMENT_ICON_MAINTENANCE; 407 $info['info']['maintenance'] = [ 408 'msg' => ($i['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST) 409 ? _('In maintenance') 410 : _n('%1$s host in maintenance', '%1$s hosts in maintenance', $i['maintenance']), 411 'color' => 'EE9600' 412 ]; 413 } 414 415 if (!$has_problem) { 416 if (!$i['maintenance']) { 417 $info['iconid'] = $i['iconid_off']; 418 $info['icon_type'] = SYSMAP_ELEMENT_ICON_OFF; 419 } 420 421 $info['info']['ok'] = [ 422 'msg' => _('OK'), 423 'color' => getSelementLabelColor(false, $info['ack']) 424 ]; 425 } 426 427 return $info; 428} 429 430/** 431 * Prepare map elements data. 432 * Calculate problem triggers and priorities. Populate map elements with automatic icon mapping, acknowledging and 433 * recent change markers. 434 * 435 * @param array $sysmap 436 * @param array $options 437 * @param int $options['severity_min'] Minimum severity, default value is maximal (Disaster) 438 * 439 * @return array 440 */ 441function getSelementsInfo(array $sysmap, array $options = []): array { 442 if (!isset($options['severity_min'])) { 443 $options['severity_min'] = TRIGGER_SEVERITY_NOT_CLASSIFIED; 444 } 445 446 $triggerIdToSelementIds = []; 447 $subSysmapTriggerIdToSelementIds = []; 448 $hostGroupIdToSelementIds = []; 449 $hostIdToSelementIds = []; 450 451 if ($sysmap['sysmapid']) { 452 $iconMap = API::IconMap()->get([ 453 'output' => API_OUTPUT_EXTEND, 454 'selectMappings' => API_OUTPUT_EXTEND, 455 'sysmapids' => $sysmap['sysmapid'] 456 ]); 457 $iconMap = reset($iconMap); 458 } 459 $hostsToGetInventories = []; 460 461 $selements = $sysmap['selements']; 462 $selementIdToSubSysmaps = []; 463 foreach ($selements as $selementId => &$selement) { 464 $selement['hosts'] = []; 465 $selement['triggers'] = []; 466 467 if ($selement['permission'] < PERM_READ) { 468 continue; 469 } 470 471 switch ($selement['elementtype']) { 472 case SYSMAP_ELEMENT_TYPE_MAP: 473 $sysmapIds = [$selement['elements'][0]['sysmapid']]; 474 475 while (!empty($sysmapIds)) { 476 $subSysmaps = API::Map()->get([ 477 'output' => ['sysmapid'], 478 'selectSelements' => ['elementtype', 'elements', 'operator', 'tags'], 479 'sysmapids' => $sysmapIds, 480 'preservekeys' => true 481 ]); 482 483 if(!isset($selementIdToSubSysmaps[$selementId])) { 484 $selementIdToSubSysmaps[$selementId] = []; 485 } 486 $selementIdToSubSysmaps[$selementId] += $subSysmaps; 487 488 $sysmapIds = []; 489 foreach ($subSysmaps as $subSysmap) { 490 foreach ($subSysmap['selements'] as $subSysmapSelement) { 491 switch ($subSysmapSelement['elementtype']) { 492 case SYSMAP_ELEMENT_TYPE_MAP: 493 $sysmapIds[] = $subSysmapSelement['elements'][0]['sysmapid']; 494 break; 495 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 496 $hostGroupIdToSelementIds[$subSysmapSelement['elements'][0]['groupid']][$selementId] 497 = $selementId; 498 break; 499 case SYSMAP_ELEMENT_TYPE_HOST: 500 $hostIdToSelementIds[$subSysmapSelement['elements'][0]['hostid']][$selementId] 501 = $selementId; 502 break; 503 case SYSMAP_ELEMENT_TYPE_TRIGGER: 504 foreach ($subSysmapSelement['elements'] as $element) { 505 $subSysmapTriggerIdToSelementIds[$element['triggerid']][$selementId] 506 = $selementId; 507 } 508 break; 509 } 510 } 511 } 512 } 513 break; 514 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 515 $hostGroupId = $selement['elements'][0]['groupid']; 516 $hostGroupIdToSelementIds[$hostGroupId][$selementId] = $selementId; 517 break; 518 case SYSMAP_ELEMENT_TYPE_HOST: 519 $hostId = $selement['elements'][0]['hostid']; 520 $hostIdToSelementIds[$hostId][$selementId] = $selementId; 521 522 /** 523 * If we have icon map applied, we need to get inventories for all hosts, where automatic icon 524 * selection is enabled. 525 */ 526 if ($sysmap['iconmapid'] && $selement['use_iconmap']) { 527 $hostsToGetInventories[] = $hostId; 528 } 529 break; 530 case SYSMAP_ELEMENT_TYPE_TRIGGER: 531 foreach ($selement['elements'] as $element) { 532 $triggerIdToSelementIds[$element['triggerid']][$selementId] = $selementId; 533 } 534 break; 535 } 536 } 537 unset($selement); 538 539 // get host inventories 540 if ($sysmap['iconmapid']) { 541 $host_inventories = API::Host()->get([ 542 'output' => ['hostid', 'inventory_mode'], 543 'selectInventory' => API_OUTPUT_EXTEND, 544 'hostids' => $hostsToGetInventories, 545 'preservekeys' => true 546 ]); 547 } 548 549 $allHosts = []; 550 if ($hostIdToSelementIds) { 551 $allHosts = API::Host()->get([ 552 'output' => ['name', 'status', 'maintenance_status'], 553 'hostids' => array_keys($hostIdToSelementIds), 554 'preservekeys' => true 555 ]); 556 557 foreach ($allHosts as $hostId => $host) { 558 foreach ($hostIdToSelementIds[$hostId] as $selementId) { 559 $selements[$selementId]['hosts'][$hostId] = $hostId; 560 } 561 } 562 } 563 564 $hostsFromHostGroups = []; 565 if ($hostGroupIdToSelementIds) { 566 $hostsFromHostGroups = API::Host()->get([ 567 'output' => ['name', 'status', 'maintenance_status'], 568 'selectGroups' => ['groupid'], 569 'groupids' => array_keys($hostGroupIdToSelementIds), 570 'preservekeys' => true 571 ]); 572 573 foreach ($hostsFromHostGroups as $hostId => $host) { 574 foreach ($host['groups'] as $group) { 575 $groupId = $group['groupid']; 576 577 if (isset($hostGroupIdToSelementIds[$groupId])) { 578 foreach ($hostGroupIdToSelementIds[$groupId] as $selementId) { 579 $selement =& $selements[$selementId]; 580 581 $selement['hosts'][$hostId] = $hostId; 582 583 // Add hosts to hosts_map for trigger selection. 584 if (!isset($hostIdToSelementIds[$hostId])) { 585 $hostIdToSelementIds[$hostId] = []; 586 } 587 $hostIdToSelementIds[$hostId][$selementId] = $selementId; 588 589 unset($selement); 590 } 591 } 592 } 593 } 594 595 $allHosts = zbx_array_merge($allHosts, $hostsFromHostGroups); 596 } 597 598 // Get triggers data, triggers from current map and from submaps, select all. 599 if ($triggerIdToSelementIds || $subSysmapTriggerIdToSelementIds) { 600 $all_triggerid_to_selementids = []; 601 602 foreach ([$triggerIdToSelementIds, $subSysmapTriggerIdToSelementIds] as $triggerid_to_selementids) { 603 foreach ($triggerid_to_selementids as $triggerid => $selementids) { 604 if (!array_key_exists($triggerid, $all_triggerid_to_selementids)) { 605 $all_triggerid_to_selementids[$triggerid] = $selementids; 606 } 607 else { 608 $all_triggerid_to_selementids[$triggerid] += $selementids; 609 } 610 } 611 } 612 613 $triggers = API::Trigger()->get([ 614 'output' => ['triggerid', 'status', 'value', 'priority', 'description', 'expression'], 615 'selectHosts' => ['hostid', 'maintenance_status'], 616 'triggerids' => array_keys($all_triggerid_to_selementids), 617 'filter' => ['state' => null], 618 'preservekeys' => true 619 ]); 620 621 $monitored_triggers = API::Trigger()->get([ 622 'output' => [], 623 'triggerids' => array_keys($triggers), 624 'monitored' => true, 625 'skipDependent' => true, 626 'preservekeys' => true 627 ]); 628 629 foreach ($triggers as $triggerid => $trigger) { 630 if (!array_key_exists($triggerid, $monitored_triggers)) { 631 $trigger['status'] = TRIGGER_STATUS_DISABLED; 632 } 633 634 $trigger['source'][SYSMAP_ELEMENT_TYPE_TRIGGER] = true; 635 636 if (array_key_exists($triggerid, $all_triggerid_to_selementids)) { 637 foreach ($all_triggerid_to_selementids[$triggerid] as $selementid) { 638 $selements[$selementid]['triggers'][$triggerid] = $trigger; 639 } 640 } 641 } 642 unset($triggers, $monitored_triggers); 643 } 644 645 $monitored_hostids = []; 646 foreach ($allHosts as $hostid => $host) { 647 if ($host['status'] == HOST_STATUS_MONITORED) { 648 $monitored_hostids[$hostid] = true; 649 } 650 } 651 652 // triggers from all hosts/hostgroups, skip dependent 653 if ($monitored_hostids) { 654 $triggers = API::Trigger()->get([ 655 'output' => ['triggerid', 'status', 'value', 'priority', 'description', 'expression'], 656 'selectHosts' => ['hostid', 'maintenance_status'], 657 'selectItems' => ['itemid'], 658 'hostids' => array_keys($monitored_hostids), 659 'filter' => ['state' => null], 660 'monitored' => true, 661 'skipDependent' => true, 662 'only_true' => true, 663 'preservekeys' => true 664 ]); 665 666 foreach ($triggers as $triggerid => $trigger) { 667 $trigger['source'][SYSMAP_ELEMENT_TYPE_HOST] = true; 668 669 foreach ($trigger['hosts'] as $host) { 670 if (array_key_exists($host['hostid'], $hostIdToSelementIds)) { 671 foreach ($hostIdToSelementIds[$host['hostid']] as $selementid) { 672 if (!array_key_exists($triggerid, $selements[$selementid]['triggers'])) { 673 $selements[$selementid]['triggers'][$triggerid] = $trigger; 674 } 675 else { 676 $selements[$selementid]['triggers'][$triggerid]['status'] = $trigger['status']; 677 $selements[$selementid]['triggers'][$triggerid]['source'] += $trigger['source']; 678 $selements[$selementid]['triggers'][$triggerid]['items'] = $trigger['items']; 679 } 680 } 681 } 682 } 683 } 684 } 685 686 // Get problems by triggerids. 687 $triggerids = []; 688 foreach ($selements as $selement) { 689 foreach ($selement['triggers'] as $trigger) { 690 if ($trigger['status'] == TRIGGER_STATUS_ENABLED) { 691 $triggerids[$trigger['triggerid']] = true; 692 } 693 } 694 } 695 696 $problems = API::Problem()->get([ 697 'output' => ['eventid', 'objectid', 'name', 'acknowledged', 'clock', 'r_clock', 'severity'], 698 'selectTags' => ['tag', 'value'], 699 'objectids' => array_keys($triggerids), 700 'acknowledged' => ($sysmap['show_unack'] == EXTACK_OPTION_UNACK) ? false : null, 701 'severities' => range($options['severity_min'], TRIGGER_SEVERITY_COUNT - 1), 702 'suppressed' => ($sysmap['show_suppressed'] == ZBX_PROBLEM_SUPPRESSED_FALSE) ? false : null, 703 'recent' => true 704 ]); 705 706 foreach ($selements as &$selement) { 707 // Find problems originated from $selement triggers. 708 $filtered_problems = array_filter($problems, function ($problem) use ($selement) { 709 return array_key_exists($problem['objectid'], $selement['triggers']); 710 }); 711 712 // Check if $filtered_problems tags matches $selement filter tags. 713 if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST 714 || $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP) { 715 $filtered_problems = getProblemsMatchingTags($filtered_problems, $selement['tags'], $selement['evaltype']); 716 } 717 718 // Apply filtgered problems to elements they belong to. 719 foreach ($filtered_problems as $problem) { 720 $selement['triggers'][$problem['objectid']]['problems'][] = $problem; 721 } 722 } 723 unset($selement); 724 725 $info = []; 726 foreach ($selements as $selementId => $selement) { 727 $i = [ 728 'elementtype' => $selement['elementtype'], 729 'disabled' => 0, 730 'maintenance' => 0, 731 'problem' => 0, 732 'problem_unack' => 0, 733 'priority' => 0, 734 'latelyChanged' => false, 735 'expandproblem' => $sysmap['expandproblem'] 736 ]; 737 738 /* 739 * If user has no rights to see the details of particular selement, add only info that is needed to render map 740 * icons. 741 */ 742 if (PERM_READ > $selement['permission']) { 743 $info[$selementId] = getSelementInfo($i + ['iconid_off' => $selement['iconid_off']]); 744 745 continue; 746 } 747 748 if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER 749 || $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP) { 750 $trigger_hosts = []; 751 foreach ($selement['triggers'] as $trigger) { 752 foreach ($trigger['hosts'] as $host) { 753 if (!array_key_exists($host['hostid'], $trigger_hosts) 754 && !array_key_exists($host['hostid'], $selement['hosts'])) { 755 $trigger_hosts[$host['hostid']] = true; 756 757 if ($host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON 758 && ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER 759 || ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP 760 && array_key_exists(SYSMAP_ELEMENT_TYPE_TRIGGER, $trigger['source'])))) { 761 $i['maintenance']++; 762 } 763 } 764 } 765 } 766 } 767 768 foreach ($selement['hosts'] as $hostId) { 769 $host = $allHosts[$hostId]; 770 771 if ($host['status'] == HOST_STATUS_NOT_MONITORED) { 772 $i['disabled']++; 773 } 774 elseif ($host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON) { 775 $i['maintenance']++; 776 } 777 } 778 779 $critical_problem = []; 780 $trigger_order = ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) 781 ? zbx_objectValues($selement['elements'], 'triggerid') 782 : []; 783 $lately_changed = 0; 784 785 foreach ($selement['triggers'] as $trigger) { 786 if ($trigger['status'] == TRIGGER_STATUS_DISABLED) { 787 continue; 788 } 789 790 if (array_key_exists('problems', $trigger)) { 791 foreach ($trigger['problems'] as $problem) { 792 if ($problem['r_clock'] == 0) { 793 $i['problem']++; 794 795 if ($problem['acknowledged'] == EVENT_NOT_ACKNOWLEDGED) { 796 $i['problem_unack']++; 797 } 798 799 if (!$critical_problem || $critical_problem['severity'] < $problem['severity']) { 800 $critical_problem = $problem; 801 } 802 elseif ($critical_problem['severity'] === $problem['severity']) { 803 if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { 804 if ($critical_problem['objectid'] === $problem['objectid'] 805 && $critical_problem['eventid'] < $problem['eventid']) { 806 $critical_problem = $problem; 807 } 808 elseif (array_search($critical_problem['objectid'], $trigger_order) 809 > array_search($problem['objectid'], $trigger_order)) { 810 $critical_problem = $problem; 811 } 812 } 813 elseif ($critical_problem['eventid'] < $problem['eventid']) { 814 $critical_problem = $problem; 815 } 816 } 817 } 818 819 if ($problem['r_clock'] > $lately_changed) { 820 $lately_changed = $problem['r_clock']; 821 } 822 elseif ($problem['clock'] > $lately_changed) { 823 $lately_changed = $problem['clock']; 824 } 825 } 826 } 827 828 $i['latelyChanged'] |= ((time() - $lately_changed) < timeUnitToSeconds(CSettingsHelper::get( 829 CSettingsHelper::BLINK_PERIOD 830 ))); 831 } 832 833 if ($critical_problem) { 834 $i['priority'] = $critical_problem['severity']; 835 $i['problem_title'] = $critical_problem['name']; 836 } 837 838 // replace default icons 839 if (!$selement['iconid_on']) { 840 $selement['iconid_on'] = $selement['iconid_off']; 841 } 842 if (!$selement['iconid_maintenance']) { 843 $selement['iconid_maintenance'] = $selement['iconid_off']; 844 } 845 if (!$selement['iconid_disabled']) { 846 $selement['iconid_disabled'] = $selement['iconid_off']; 847 } 848 849 $i['iconid_off'] = $selement['iconid_off']; 850 $i['iconid_on'] = $selement['iconid_on']; 851 $i['iconid_maintenance'] = $selement['iconid_maintenance']; 852 $i['iconid_disabled'] = $selement['iconid_disabled']; 853 854 $info[$selementId] = getSelementInfo($i, $sysmap['show_unack']); 855 856 if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST && $sysmap['iconmapid'] && $selement['use_iconmap']) { 857 $host_inventory = $host_inventories[$selement['elements'][0]['hostid']]; 858 $info[$selementId]['iconid'] = getIconByMapping($iconMap, $host_inventory); 859 } 860 861 $info[$selementId]['problems_total'] = $i['problem']; 862 } 863 864 if ($sysmap['label_format'] == SYSMAP_LABEL_ADVANCED_OFF) { 865 $hlabel = ($sysmap['label_type'] == MAP_LABEL_TYPE_NAME); 866 $hglabel = ($sysmap['label_type'] == MAP_LABEL_TYPE_NAME); 867 $tlabel = ($sysmap['label_type'] == MAP_LABEL_TYPE_NAME); 868 $mlabel = ($sysmap['label_type'] == MAP_LABEL_TYPE_NAME); 869 } 870 else { 871 $hlabel = ($sysmap['label_type_host'] == MAP_LABEL_TYPE_NAME); 872 $hglabel = ($sysmap['label_type_hostgroup'] == MAP_LABEL_TYPE_NAME); 873 $tlabel = ($sysmap['label_type_trigger'] == MAP_LABEL_TYPE_NAME); 874 $mlabel = ($sysmap['label_type_map'] == MAP_LABEL_TYPE_NAME); 875 } 876 877 // get names if needed 878 $elems = separateMapElements($sysmap); 879 880 if ($elems['sysmaps'] && $mlabel) { 881 $sysmapids = []; 882 883 foreach ($elems['sysmaps'] as $selement) { 884 if ($selement['permission'] >= PERM_READ) { 885 $sysmapids[$selement['elements'][0]['sysmapid']] = true; 886 } 887 } 888 889 $db_sysmaps = API::Map()->get([ 890 'output' => ['name'], 891 'sysmapids' => array_keys($sysmapids), 892 'preservekeys' => true 893 ]); 894 895 foreach ($elems['sysmaps'] as $selement) { 896 if ($selement['permission'] >= PERM_READ) { 897 $info[$selement['selementid']]['name'] = 898 array_key_exists($selement['elements'][0]['sysmapid'], $db_sysmaps) 899 ? $db_sysmaps[$selement['elements'][0]['sysmapid']]['name'] 900 : ''; 901 } 902 } 903 } 904 905 if ($elems['hostgroups'] && $hglabel) { 906 $groupids = []; 907 908 foreach ($elems['hostgroups'] as $selement) { 909 if ($selement['permission'] >= PERM_READ) { 910 $groupids[$selement['elements'][0]['groupid']] = true; 911 } 912 } 913 914 $db_groups = $groupids 915 ? API::HostGroup()->get([ 916 'output' => ['name'], 917 'groupids' => array_keys($groupids), 918 'preservekeys' => true 919 ]) 920 : []; 921 922 foreach ($elems['hostgroups'] as $selement) { 923 if ($selement['permission'] >= PERM_READ) { 924 $info[$selement['selementid']]['name'] = 925 array_key_exists($selement['elements'][0]['groupid'], $db_groups) 926 ? $db_groups[$selement['elements'][0]['groupid']]['name'] 927 : ''; 928 } 929 } 930 } 931 932 if ($elems['triggers'] && $tlabel) { 933 $selements = zbx_toHash($selements, 'selementid'); 934 foreach ($elems['triggers'] as $selementid => $selement) { 935 foreach ($selement['elements'] as $element) { 936 if ($selement['permission'] >= PERM_READ) { 937 $trigger = array_key_exists($element['triggerid'], $selements[$selementid]['triggers']) 938 ? $selements[$selementid]['triggers'][$element['triggerid']] 939 : null; 940 $info[$selement['selementid']]['name'] = ($trigger != null) 941 ? CMacrosResolverHelper::resolveTriggerName($trigger) 942 : ''; 943 } 944 } 945 } 946 } 947 948 if ($elems['hosts'] && $hlabel) { 949 foreach ($elems['hosts'] as $selement) { 950 if ($selement['permission'] >= PERM_READ) { 951 $info[$selement['selementid']]['name'] = array_key_exists($selement['elements'][0]['hostid'], $allHosts) 952 ? $allHosts[$selement['elements'][0]['hostid']]['name'] 953 : []; 954 } 955 } 956 } 957 958 return $info; 959} 960 961function separateMapElements($sysmap) { 962 $elements = [ 963 'sysmaps' => [], 964 'hostgroups' => [], 965 'hosts' => [], 966 'triggers' => [], 967 'images' => [] 968 ]; 969 970 foreach ($sysmap['selements'] as $selement) { 971 switch ($selement['elementtype']) { 972 case SYSMAP_ELEMENT_TYPE_MAP: 973 $elements['sysmaps'][$selement['selementid']] = $selement; 974 break; 975 case SYSMAP_ELEMENT_TYPE_HOST_GROUP: 976 $elements['hostgroups'][$selement['selementid']] = $selement; 977 break; 978 case SYSMAP_ELEMENT_TYPE_HOST: 979 $elements['hosts'][$selement['selementid']] = $selement; 980 break; 981 case SYSMAP_ELEMENT_TYPE_TRIGGER: 982 $elements['triggers'][$selement['selementid']] = $selement; 983 break; 984 case SYSMAP_ELEMENT_TYPE_IMAGE: 985 default: 986 $elements['images'][$selement['selementid']] = $selement; 987 } 988 } 989 return $elements; 990} 991 992/** 993 * Calculates coordinates from elements inside areas 994 * 995 * @param array $map 996 * @param array $areas 997 * @param array $mapInfo 998 */ 999function processAreasCoordinates(array &$map, array $areas, array $mapInfo) { 1000 foreach ($areas as $area) { 1001 $rowPlaceCount = ceil(sqrt(count($area['selementids']))); 1002 1003 // offset from area borders 1004 $area['x'] += 5; 1005 $area['y'] += 5; 1006 $area['width'] -= 5; 1007 $area['height'] -= 5; 1008 1009 $xOffset = floor($area['width'] / $rowPlaceCount); 1010 $yOffset = floor($area['height'] / $rowPlaceCount); 1011 1012 $colNum = 0; 1013 $rowNum = 0; 1014 // some offset is required so that icon highlights are not drawn outside area 1015 $borderOffset = 20; 1016 foreach ($area['selementids'] as $selementId) { 1017 $selement = $map['selements'][$selementId]; 1018 1019 $image = get_png_by_selement($mapInfo[$selementId]); 1020 $iconX = imagesx($image); 1021 $iconY = imagesy($image); 1022 1023 $labelLocation = (is_null($selement['label_location']) || ($selement['label_location'] < 0)) 1024 ? $map['label_location'] : $selement['label_location']; 1025 switch ($labelLocation) { 1026 case MAP_LABEL_LOC_TOP: 1027 $newX = $area['x'] + ($xOffset / 2) - ($iconX / 2); 1028 $newY = $area['y'] + $yOffset - $iconY - ($iconY >= $iconX ? 0 : abs($iconX - $iconY) / 2) - $borderOffset; 1029 break; 1030 case MAP_LABEL_LOC_LEFT: 1031 $newX = $area['x'] + $xOffset - $iconX - $borderOffset; 1032 $newY = $area['y'] + ($yOffset / 2) - ($iconY / 2); 1033 break; 1034 case MAP_LABEL_LOC_RIGHT: 1035 $newX = $area['x'] + $borderOffset; 1036 $newY = $area['y'] + ($yOffset / 2) - ($iconY / 2); 1037 break; 1038 case MAP_LABEL_LOC_BOTTOM: 1039 $newX = $area['x'] + ($xOffset / 2) - ($iconX / 2); 1040 $newY = $area['y'] + abs($iconX - $iconY) / 2 + $borderOffset; 1041 break; 1042 } 1043 1044 $map['selements'][$selementId]['x'] = $newX + ($colNum * $xOffset); 1045 $map['selements'][$selementId]['y'] = $newY + ($rowNum * $yOffset); 1046 1047 $colNum++; 1048 if ($colNum == $rowPlaceCount) { 1049 $colNum = 0; 1050 $rowNum++; 1051 } 1052 } 1053 } 1054} 1055 1056/** 1057 * Calculates area connector point on area perimeter 1058 * 1059 * @param int $ax x area coordinate 1060 * @param int $ay y area coordinate 1061 * @param int $aWidth area width 1062 * @param int $aHeight area height 1063 * @param int $x2 x coordinate of connector second element 1064 * @param int $y2 y coordinate of connector second element 1065 * 1066 * @return array contains two values, x and y coordinates of new area connector point 1067 */ 1068function calculateMapAreaLinkCoord($ax, $ay, $aWidth, $aHeight, $x2, $y2) { 1069 $dY = abs($y2 - $ay); 1070 $dX = abs($x2 - $ax); 1071 1072 $halfHeight = $aHeight / 2; 1073 $halfWidth = $aWidth / 2; 1074 1075 if ($dY == 0) { 1076 $ay = $y2; 1077 $ax = ($x2 < $ax) ? $ax - $halfWidth : $ax + $halfWidth; 1078 } 1079 elseif ($dX == 0) { 1080 $ay = ($y2 > $ay) ? $ay + $halfHeight : $ay - $halfHeight; 1081 $ax = $x2; 1082 } 1083 else { 1084 $koef = $halfHeight / $dY; 1085 1086 $c = $dX * $koef; 1087 1088 // If point is further than area diagonal, we should use calculations with width instead of height. 1089 if (($halfHeight / $c) > ($halfHeight / $halfWidth)) { 1090 $ay = ($y2 > $ay) ? $ay + $halfHeight : $ay - $halfHeight; 1091 $ax = ($x2 < $ax) ? $ax - $c : $ax + $c; 1092 } 1093 else { 1094 $koef = $halfWidth / $dX; 1095 1096 $c = $dY * $koef; 1097 1098 $ay = ($y2 > $ay) ? $ay + $c : $ay - $c; 1099 $ax = ($x2 < $ax) ? $ax - $halfWidth : $ax + $halfWidth; 1100 } 1101 } 1102 1103 return [$ax, $ay]; 1104} 1105 1106/** 1107 * Get icon id by mapping. 1108 * 1109 * @param array $icon_map 1110 * @param array $host 1111 * @param int $host['inventory_mode'] 1112 * @param array $host['inventory'] 1113 * 1114 * @return int 1115 */ 1116function getIconByMapping(array $icon_map, array $host) { 1117 if ($host['inventory_mode'] == HOST_INVENTORY_DISABLED) { 1118 return $icon_map['default_iconid']; 1119 } 1120 1121 $inventories = getHostInventories(); 1122 1123 foreach ($icon_map['mappings'] as $mapping) { 1124 try { 1125 $expr = new CGlobalRegexp($mapping['expression']); 1126 if ($expr->match($host['inventory'][$inventories[$mapping['inventory_link']]['db_field']])) { 1127 return $mapping['iconid']; 1128 } 1129 } 1130 catch(Exception $e) { 1131 continue; 1132 } 1133 } 1134 1135 return $icon_map['default_iconid']; 1136} 1137 1138/** 1139 * Get parent maps for current map. 1140 * 1141 * @param int $sysmapid 1142 * 1143 * @return array 1144 */ 1145function get_parent_sysmaps($sysmapid) { 1146 $db_sysmaps_elements = DBselect( 1147 'SELECT DISTINCT se.sysmapid'. 1148 ' FROM sysmaps_elements se'. 1149 ' WHERE '.dbConditionInt('se.elementtype', [SYSMAP_ELEMENT_TYPE_MAP]). 1150 ' AND '.dbConditionInt('se.elementid', [$sysmapid]) 1151 ); 1152 1153 $sysmapids = []; 1154 1155 while ($db_sysmaps_element = DBfetch($db_sysmaps_elements)) { 1156 $sysmapids[] = $db_sysmaps_element['sysmapid']; 1157 } 1158 1159 if ($sysmapids) { 1160 $sysmaps = API::Map()->get([ 1161 'output' => ['sysmapid', 'name'], 1162 'sysmapids' => $sysmapids 1163 ]); 1164 1165 CArrayHelper::sort($sysmaps, ['name']); 1166 1167 return $sysmaps; 1168 } 1169 1170 return []; 1171} 1172 1173/** 1174 * Get labels for map elements. 1175 * 1176 * @param array $map Sysmap data array. 1177 * @param array $map_info Array of selements (@see getSelementsInfo). 1178 * 1179 * @return array 1180 */ 1181function getMapLabels($map, $map_info) { 1182 $selements = $map['selements']; 1183 1184 // Collect labels for each map element and apply appropriate values. 1185 $labels = []; 1186 foreach ($selements as $selementId => $selement) { 1187 if ($selement['permission'] < PERM_READ) { 1188 continue; 1189 } 1190 elseif ($selement['label_type'] == MAP_LABEL_TYPE_NOTHING) { 1191 $labels[$selementId] = []; 1192 continue; 1193 } 1194 1195 $label_lines = []; 1196 $msgs = explode("\n", $selement['label']); 1197 foreach ($msgs as $msg) { 1198 $label_lines[] = ['content' => $msg]; 1199 } 1200 1201 $status_lines = []; 1202 $element_info = $map_info[$selementId]; 1203 if (array_key_exists('info', $element_info)) { 1204 foreach (['problem', 'unack', 'maintenance', 'ok', 'status'] as $caption) { 1205 if (array_key_exists($caption, $element_info['info']) 1206 && $element_info['info'][$caption]['msg'] !== '') { 1207 $msgs = explode("\n", $element_info['info'][$caption]['msg']); 1208 foreach ($msgs as $msg) { 1209 $status_lines[] = [ 1210 'content' => $msg, 1211 'attributes' => [ 1212 'fill' => '#'.$element_info['info'][$caption]['color'] 1213 ] 1214 ]; 1215 } 1216 } 1217 } 1218 } 1219 1220 if ($selement['label_type'] == MAP_LABEL_TYPE_IP && $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST) { 1221 $label = array_merge([['content' => $selement['label']]], $status_lines); 1222 } 1223 elseif ($selement['label_type'] == MAP_LABEL_TYPE_STATUS) { 1224 $label = $status_lines; 1225 } 1226 elseif ($selement['label_type'] == MAP_LABEL_TYPE_NAME) { 1227 $label = array_merge([['content' => $element_info['name']]], $status_lines); 1228 } 1229 else { 1230 $label = array_merge($label_lines, $status_lines); 1231 } 1232 1233 $labels[$selementId] = $label; 1234 } 1235 1236 return $labels; 1237} 1238 1239/** 1240 * Get map element highlights (information about elements with marks or background). 1241 * 1242 * @param array $map Sysmap data array. 1243 * @param array $map_info Array of selements (@see getSelementsInfo). 1244 * 1245 * @return array 1246 */ 1247function getMapHighligts(array $map, array $map_info) { 1248 $highlights = []; 1249 1250 foreach ($map['selements'] as $id => $selement) { 1251 if ((($map['highlight'] % 2) != SYSMAP_HIGHLIGHT_ON) || (array_key_exists('elementtype', $selement) 1252 && $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP 1253 && array_key_exists('elementsubtype', $selement) 1254 && $selement['elementsubtype'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS)) { 1255 $highlights[$id] = null; 1256 continue; 1257 } 1258 1259 $hl_color = null; 1260 $st_color = null; 1261 $element_info = $map_info[$id]; 1262 1263 switch ($element_info['icon_type']) { 1264 case SYSMAP_ELEMENT_ICON_ON: 1265 $hl_color = getSeverityColor($element_info['priority']); 1266 break; 1267 1268 case SYSMAP_ELEMENT_ICON_MAINTENANCE: 1269 $st_color = 'FF9933'; 1270 break; 1271 1272 case SYSMAP_ELEMENT_ICON_DISABLED: 1273 $st_color = '999999'; 1274 break; 1275 } 1276 1277 if (array_key_exists('elementtype', $selement) 1278 && ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP 1279 || $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP) && $hl_color !== null) { 1280 $st_color = null; 1281 } 1282 elseif ($st_color !== null) { 1283 $hl_color = null; 1284 } 1285 1286 $highlights[$id] = [ 1287 'st' => $st_color, 1288 'hl' => $hl_color, 1289 'ack' => ($hl_color !== null && array_key_exists('ack', $element_info) && $element_info['ack']) 1290 ]; 1291 } 1292 1293 return $highlights; 1294} 1295 1296/** 1297 * Get trigger data for all linktriggers. 1298 * 1299 * @param array $sysmap 1300 * @param array $sysmap['show_suppressed'] Whether to show suppressed problems. 1301 * @param array $sysmap['show_unack'] Property specified in sysmap's 'Problem display' field. Used to determine 1302 * whether to show unacknowledged problems only. 1303 * @param array $options Options used to retrieve actions. 1304 * @param int $options['severity_min'] Minimal severity used. 1305 * 1306 * @return array 1307 */ 1308function getMapLinkTriggerInfo($sysmap, $options) { 1309 if (!array_key_exists('severity_min', $options)) { 1310 $options['severity_min'] = TRIGGER_SEVERITY_NOT_CLASSIFIED; 1311 } 1312 1313 $triggerids = []; 1314 1315 foreach ($sysmap['links'] as $link) { 1316 foreach ($link['linktriggers'] as $linktrigger) { 1317 $triggerids[$linktrigger['triggerid']] = true; 1318 } 1319 } 1320 1321 $trigger_options = [ 1322 'output' => ['status', 'value', 'priority'], 1323 'triggerids' => array_keys($triggerids), 1324 'monitored' => true, 1325 'preservekeys' => true 1326 ]; 1327 1328 $problem_options = [ 1329 'show_suppressed' => $sysmap['show_suppressed'], 1330 'acknowledged' => ($sysmap['show_unack'] == EXTACK_OPTION_UNACK) ? false : null 1331 ]; 1332 1333 return getTriggersWithActualSeverity($trigger_options, $problem_options); 1334} 1335 1336/** 1337 * Get map selement label color based on problem and acknowledgement state 1338 * as well as taking custom event status color settings into account. 1339 * 1340 * @throws APIException if the given table does not exist 1341 * 1342 * @param bool $is_problem 1343 * @param bool $is_ack 1344 * 1345 * @return string 1346 */ 1347function getSelementLabelColor($is_problem, $is_ack) { 1348 static $schema = null; 1349 1350 if ($schema === null) { 1351 $schema = DB::getSchema('config'); 1352 } 1353 1354 if ($is_problem) { 1355 $param = $is_ack ? CSettingsHelper::PROBLEM_ACK_COLOR : CSettingsHelper::PROBLEM_UNACK_COLOR; 1356 } 1357 else { 1358 $param = $is_ack ? CSettingsHelper::OK_ACK_COLOR : CSettingsHelper::OK_UNACK_COLOR; 1359 } 1360 1361 if (CSettingsHelper::get(CSettingsHelper::CUSTOM_COLOR) === '1') { 1362 return CSettingsHelper::get($param); 1363 } 1364 1365 return $schema['fields'][$param]['default']; 1366} 1367 1368/** 1369 * Filter problems by given tags. 1370 * 1371 * @param array $problems 1372 * @param array $problems[]['tags'] 1373 * @param string $problems[]['tags'][]['tag'] 1374 * @param string $problems[]['tags'][]['value'] 1375 * @param array $filter_tags 1376 * @param string $filter_tags[]['tag'] 1377 * @param string $filter_tags[]['value'] 1378 * @param int $filter_tags[]['operator'] 1379 * @param int $evaltype 1380 * 1381 * @return array 1382 */ 1383function getProblemsMatchingTags(array $problems, array $filter_tags, int $evaltype): array { 1384 if (!$problems) { 1385 return []; 1386 } 1387 1388 $tags = []; 1389 foreach ($filter_tags as $tag) { 1390 $tags[$tag['tag']][] = $tag; 1391 } 1392 1393 $filtered_problems = []; 1394 foreach ($problems as $problem) { 1395 $matching_tags = array_fill_keys(array_keys($tags), 0); 1396 array_walk($matching_tags, function (&$match, $key) use ($tags, $problem) { 1397 foreach ($tags[$key] as $tag) { 1398 if (checkIfProblemTagsMatches($tag, $problem['tags'])) { 1399 $match = 1; 1400 break; 1401 } 1402 } 1403 }); 1404 1405 $matching_tags = array_flip($matching_tags); 1406 if ($evaltype == TAG_EVAL_TYPE_OR && array_key_exists(1, $matching_tags)) { 1407 $filtered_problems[] = $problem; 1408 } 1409 elseif ($evaltype == TAG_EVAL_TYPE_AND_OR && !array_key_exists(0, $matching_tags)) { 1410 $filtered_problems[] = $problem; 1411 } 1412 } 1413 1414 return $filtered_problems; 1415} 1416 1417/** 1418 * Check if $filter_tag matches one of tags in $tags array. 1419 * 1420 * @param array $filter_tag 1421 * @param string $filter_tag['tag'] 1422 * @param string $filter_tag['value'] 1423 * @param int $filter_tag['operator'] 1424 * @param array $tags 1425 * @param string $tags[]['tag'] 1426 * @param string $tags[]['value'] 1427 * 1428 * @return bool 1429 */ 1430function checkIfProblemTagsMatches(array $filter_tag, array $tags): bool { 1431 if (in_array($filter_tag['operator'], [TAG_OPERATOR_NOT_LIKE, TAG_OPERATOR_NOT_EQUAL, TAG_OPERATOR_NOT_EXISTS]) 1432 && !$tags) { 1433 return true; 1434 } 1435 1436 if ($filter_tag['operator'] == TAG_OPERATOR_NOT_LIKE && $filter_tag['value'] === '') { 1437 $filter_tag['operator'] = TAG_OPERATOR_NOT_EXISTS; 1438 } 1439 elseif ($filter_tag['operator'] == TAG_OPERATOR_LIKE && $filter_tag['value'] === '') { 1440 $filter_tag['operator'] = TAG_OPERATOR_EXISTS; 1441 } 1442 1443 switch ($filter_tag['operator']) { 1444 case TAG_OPERATOR_LIKE: 1445 foreach ($tags as $tag) { 1446 if ($filter_tag['tag'] === $tag['tag'] && mb_stripos($tag['value'], $filter_tag['value']) !== false) { 1447 return true; 1448 } 1449 } 1450 break; 1451 1452 case TAG_OPERATOR_EQUAL: 1453 foreach ($tags as $tag) { 1454 if ($filter_tag['tag'] === $tag['tag'] && $filter_tag['value'] === $tag['value']) { 1455 return true; 1456 } 1457 } 1458 break; 1459 1460 case TAG_OPERATOR_NOT_LIKE: 1461 $tags_count = count($tags); 1462 $tags = array_filter($tags, function ($tag) use ($filter_tag) { 1463 return !($filter_tag['tag'] === $tag['tag'] 1464 && mb_stripos($tag['value'], $filter_tag['value']) !== false 1465 ); 1466 }); 1467 return (count($tags) == $tags_count); 1468 1469 case TAG_OPERATOR_NOT_EQUAL: 1470 $tags_count = count($tags); 1471 $tags = array_filter($tags, function ($tag) use ($filter_tag) { 1472 return !($filter_tag['tag'] === $tag['tag'] && $filter_tag['value'] === $tag['value']); 1473 }); 1474 return (count($tags) == $tags_count); 1475 1476 case TAG_OPERATOR_EXISTS: 1477 return array_key_exists($filter_tag['tag'], zbx_toHash($tags, 'tag')); 1478 1479 case TAG_OPERATOR_NOT_EXISTS: 1480 return !array_key_exists($filter_tag['tag'], zbx_toHash($tags, 'tag')); 1481 } 1482 1483 return false; 1484} 1485