get([ 'output' => ['sysmapid', 'name', 'width', 'height', 'backgroundid', 'label_type', 'label_location', 'highlight', 'expandproblem', 'markelements', 'show_unack', 'label_format', 'label_type_host', 'label_type_hostgroup', 'label_type_trigger', 'label_type_map', 'label_type_image', 'label_string_host', 'label_string_hostgroup', 'label_string_trigger', 'label_string_map', 'label_string_image', 'iconmapid', 'severity_min', 'show_suppressed' ], 'selectShapes' => ['sysmap_shapeid', 'type', 'x', 'y', 'width', 'height', 'text', 'font', 'font_size', 'font_color', 'text_halign', 'text_valign', 'border_type', 'border_width', 'border_color', 'background_color', 'zindex' ], 'selectLines' => ['sysmap_shapeid', 'x1', 'x2', 'y1', 'y2', 'line_type', 'line_width', 'line_color', 'zindex' ], 'selectSelements' => ['selementid', 'elements', 'elementtype', 'iconid_off', 'iconid_on', 'label', 'label_location', 'x', 'y', 'iconid_disabled', 'iconid_maintenance', 'elementsubtype', 'areatype', 'width', 'height', 'viewtype', 'use_iconmap', 'permission', 'evaltype', 'tags' ], 'selectLinks' => ['linkid', 'selementid1', 'selementid2', 'drawtype', 'color', 'label', 'linktriggers', 'permission' ], 'sysmapids' => $sysmapids, 'preservekeys' => true ]); $map = reset($maps); $theme = getUserGraphTheme(); if (!$map) { $map = [ 'sysmapid' => -1, 'width' => 320, 'height' => 150, 'backgroundid' => null, 'severity_min' => 0, 'show_unack' => EXTACK_OPTION_ALL, 'label_location' => MAP_LABEL_LOC_BOTTOM, 'selements' => [], 'links' => [], 'shapes' => [[ 'type' => SYSMAP_SHAPE_TYPE_RECTANGLE, 'x' => 0, 'y' => 0, 'width' => 320, 'height' => 150, 'font' => 9, 'font_size' => 11, 'font_color' => 'FF0000', 'text' => _('No permissions to referred object or it does not exist!') ]], 'aria_label' => '' ]; } else { if (array_key_exists('severity_min', $options)) { $map['severity_min'] = $options['severity_min']; } else { $options['severity_min'] = $map['severity_min']; } // Populate host group elements of subtype 'SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS' with hosts. $areas = self::populateHostGroupsWithHosts($map, $theme); // Apply inherited label options. $map = self::applyMapElementLabelProperties($map); // Resolve macros in map element labels. $resolve_opt = ['resolve_element_label' => true]; $map['selements'] = CMacrosResolverHelper::resolveMacrosInMapElements($map['selements'], $resolve_opt); self::resolveMapState($map, $areas, $options); } return [ 'id' => $map['sysmapid'], 'theme' => $theme, 'canvas' => [ 'width' => $map['width'], 'height' => $map['height'] ], 'refresh' => 'map.php?sysmapid='.$map['sysmapid'].'&severity_min='.$map['severity_min'] .(array_key_exists('unique_id', $options) ? '&unique_id='.$options['unique_id'] : ''), 'background' => $map['backgroundid'], 'label_location' => $map['label_location'], 'elements' => array_values($map['selements']), 'links' => array_values($map['links']), 'shapes' => array_values($map['shapes']), 'aria_label' => $map['aria_label'], 'timestamp' => zbx_date2str(DATE_TIME_FORMAT_SECONDS) ]; } /** * Function applies element labels inherited from map properties. * * @param array $sysmap Map data. * @param array $sysmap[selements] Array of map elements. * @param int $sysmap[selements][][elementtype] Map element type. * @param int $sysmap[label_format] Map label format property. * @param int $sysmap[label_type] Map label type property. * @param int $sysmap[label_type_hostgroup] Map host group element label type. * @param string $sysmap[label_string_hostgroup] Map host group element custom label. * @param int $sysmap[label_type_host] Map host element label type. * @param string $sysmap[label_string_host] Map host element custom label. * @param int $sysmap[label_type_trigger] Map trigger element label type. * @param string $sysmap[label_string_trigger] Map trigger element custom label. * @param int $sysmap[label_type_map] Map submap element label type. * @param string $sysmap[label_string_map] Map submap element custom label. * @param int $sysmap[label_type_image] Map image element label type. * @param string $sysmap[label_string_image] Map image element custom label. * * @return array */ public static function applyMapElementLabelProperties(array $sysmap) { // Define which $sysmap property holds value for each type of element. $label_properties = [ SYSMAP_ELEMENT_TYPE_HOST_GROUP => [ 'field_label_type' => 'label_type_hostgroup', 'field_custom_label' => 'label_string_hostgroup' ], SYSMAP_ELEMENT_TYPE_HOST => [ 'field_label_type' => 'label_type_host', 'field_custom_label' => 'label_string_host' ], SYSMAP_ELEMENT_TYPE_TRIGGER => [ 'field_label_type' => 'label_type_trigger', 'field_custom_label' => 'label_string_trigger' ], SYSMAP_ELEMENT_TYPE_MAP => [ 'field_label_type' => 'label_type_map', 'field_custom_label' => 'label_string_map' ], SYSMAP_ELEMENT_TYPE_IMAGE => [ 'field_label_type' => 'label_type_image', 'field_custom_label' => 'label_string_image' ] ]; // Apply properties to each sysmap element. foreach ($sysmap['selements'] as &$selement) { $prop = $label_properties[$selement['elementtype']]; $elmnt_label_type = ($sysmap['label_format'] == SYSMAP_LABEL_ADVANCED_ON) ? $sysmap[$prop['field_label_type']] : $sysmap['label_type']; $inherited_label = null; if ($elmnt_label_type == MAP_LABEL_TYPE_NOTHING) { $inherited_label = ''; } elseif ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST && $elmnt_label_type == MAP_LABEL_TYPE_IP) { $inherited_label = '{HOST.IP}'; } elseif ($sysmap['label_format'] == SYSMAP_LABEL_ADVANCED_ON && $sysmap[$prop['field_label_type']] == MAP_LABEL_TYPE_CUSTOM) { $inherited_label = $sysmap[$prop['field_custom_label']]; } $selement['label_type'] = $elmnt_label_type; if ($inherited_label !== null) { $selement['label'] = $inherited_label; } } unset($selement); return $sysmap; } /** * Resolve map element (selements and links) state. * * @param array $sysmap Map data. * @param array $areas Areas representing array containing host group element IDs and dimension * properties of area. * @param array $options Options used to retrieve actions. * @param int $options['severity_min'] Minimum severity. * @param int $options['unique_id'] */ protected static function resolveMapState(array &$sysmap, array $areas, array $options) { $map_info = getSelementsInfo($sysmap, ['severity_min' => $options['severity_min']]); processAreasCoordinates($sysmap, $areas, $map_info); // Adding element names and removing inaccessible triggers from readable elements. addElementNames($sysmap['selements']); foreach ($sysmap['selements'] as $id => &$element) { if ($element['permission'] < PERM_READ) { continue; } switch ($element['elementtype']) { case SYSMAP_ELEMENT_TYPE_IMAGE: $map_info[$id]['name'] = _('Image'); break; case SYSMAP_ELEMENT_TYPE_TRIGGER: // Move the trigger with problem and highest priority to the beginning of the trigger list. if (array_key_exists('triggerid', $map_info[$id])) { $trigger_pos = 0; foreach ($element['elements'] as $i => $trigger) { if ($trigger['triggerid'] == $map_info[$id]['triggerid']) { $trigger_pos = $i; break; } } if ($trigger_pos > 0) { $trigger = $element['elements'][$trigger_pos]; unset($element['elements'][$trigger_pos]); array_unshift($element['elements'], $trigger); } } // break; is not missing here default: $map_info[$id]['name'] = $element['elements'][0]['elementName']; } } unset($element); $labels = getMapLabels($sysmap, $map_info); $highlights = getMapHighligts($sysmap, $map_info); $actions = getActionsBySysmap($sysmap, $options); $link_triggers_info = getMapLinkTriggerInfo($sysmap, $options); $problems_total = 0; $status_problems = []; $status_other = []; foreach ($sysmap['selements'] as $id => &$element) { $element['icon'] = (array_key_exists($id, $map_info) && array_key_exists('iconid', $map_info[$id])) ? $map_info[$id]['iconid'] : null; unset($element['width'], $element['height']); if ($element['permission'] >= PERM_READ) { $label = str_replace(['.', ','], ' ', $element['label']); if ($map_info[$id]['problems_total'] > 0) { $problems_total += $map_info[$id]['problems_total']; $problem_desc = str_replace(['.', ','], ' ', $map_info[$id]['aria_label']); $status_problems[] = sprintf('%1$s, %2$s, %3$s, %4$s. ', sysmap_element_types($element['elementtype']), _('Status problem'), $label, $problem_desc ); } else { $element_status = _('Status ok'); if (array_key_exists('info', $map_info[$id]) && array_key_exists('maintenance', $map_info[$id]['info'])) { $element_status = _('Status maintenance'); } elseif (array_key_exists('info', $map_info[$id]) && array_key_exists('status', $map_info[$id]['info'])) { $element_status = _('Status disabled'); } $status_other[] = sprintf('%1$s, %2$s, %3$s. ', sysmap_element_types($element['elementtype']), $element_status, $label ); } $element['highlight'] = $highlights[$id]; $element['actions'] = $actions[$id]; $element['label'] = $labels[$id]; } else { $element['highlight'] = ''; $element['actions'] = null; $element['label'] = ''; } if ($sysmap['markelements']) { $element['latelyChanged'] = $map_info[$id]['latelyChanged']; } } unset($element); $sysmap['aria_label'] = str_replace(['.', ','], ' ', $sysmap['name']).', '. _n('%1$s of %2$s element in problem state', '%1$s of %2$s elements in problem state', count($status_problems), count($sysmap['selements'])). ', '. _n('%1$s problem in total', '%1$s problems in total', $problems_total). '. '. implode('', array_merge($status_problems, $status_other)); $sysmap['shapes'] = CMacrosResolverHelper::resolveMapShapeLabelMacros($sysmap['name'], $sysmap['shapes']); $sysmap['links'] = CMacrosResolverHelper::resolveMapLinkLabelMacros($sysmap['links']); foreach ($sysmap['lines'] as $line) { $sysmap['shapes'][] = self::convertLineToShape($line); } foreach ($sysmap['links'] as &$link) { if ($link['permission'] >= PERM_READ && $link['linktriggers']) { $link_triggers = array_filter($link['linktriggers'], function ($link_trigger) use ($link_triggers_info, $options) { return (array_key_exists($link_trigger['triggerid'], $link_triggers_info) && $link_triggers_info[$link_trigger['triggerid']]['status'] == TRIGGER_STATUS_ENABLED && $link_triggers_info[$link_trigger['triggerid']]['value'] == TRIGGER_VALUE_TRUE && $link_triggers_info[$link_trigger['triggerid']]['priority'] >= $options['severity_min'] ); } ); // Link-trigger with highest severity or lower triggerid defines link color and drawtype. if ($link_triggers) { $link_triggers = array_map(function ($link_trigger) use ($link_triggers_info) { return [ 'priority' => $link_triggers_info[$link_trigger['triggerid']]['priority'] ] + $link_trigger; }, $link_triggers); CArrayHelper::sort($link_triggers, [ ['field' => 'priority', 'order' => ZBX_SORT_DOWN], ['field' => 'triggerid', 'order' => ZBX_SORT_UP] ]); $styling_link_triggers = reset($link_triggers); $link['color'] = $styling_link_triggers['color']; $link['drawtype'] = $styling_link_triggers['drawtype']; } } } unset($link); } /** * Convert map shape to line (apply mapping to attribute set). * * @param array $shape Map shape. * * @return array */ public static function convertShapeToLine($shape) { $mapping = [ 'sysmap_shapeid', 'zindex', 'x' => 'x1', 'y' => 'y1', 'width' => 'x2', 'height' => 'y2', 'border_type' => 'line_type', 'border_width' => 'line_width', 'border_color' => 'line_color' ]; $line = []; foreach ($mapping as $source_key => $target_key) { $source_key = (is_numeric($source_key)) ? $target_key : $source_key; if (array_key_exists($source_key, $shape)) { $line[$target_key] = $shape[$source_key]; } } return $line; } /** * Convert map line to shape (apply mapping to attribute set). * * @param array $line Map line. * * @return array */ public static function convertLineToShape($line) { $mapping = [ 'sysmap_shapeid', 'zindex', 'x1' => 'x', 'y1' => 'y', 'x2' => 'width', 'y2' => 'height', 'line_type' => 'border_type', 'line_width' => 'border_width', 'line_color' => 'border_color' ]; $shape = []; foreach ($mapping as $source_key => $target_key) { $source_key = (is_numeric($source_key)) ? $target_key : $source_key; if (array_key_exists($source_key, $line)) { $shape[$target_key] = $line[$source_key]; } } $shape['type'] = SYSMAP_SHAPE_TYPE_LINE; return $shape; } /** * Checks that the user has read permissions to objects used in the map elements. * * @param array $selements selements to check * * @return boolean */ public static function checkSelementPermissions(array $selements) { $groupids = []; $hostids = []; $triggerids = []; $sysmapids = []; foreach ($selements as $selement) { switch ($selement['elementtype']) { case SYSMAP_ELEMENT_TYPE_HOST_GROUP: $groupids[$selement['elements'][0]['groupid']] = true; break; case SYSMAP_ELEMENT_TYPE_HOST: $hostids[$selement['elements'][0]['hostid']] = true; break; case SYSMAP_ELEMENT_TYPE_TRIGGER: foreach ($selement['elements'] as $element) { $triggerids[$element['triggerid']] = true; } break; case SYSMAP_ELEMENT_TYPE_MAP: $sysmapids[$selement['elements'][0]['sysmapid']] = true; break; } } return self::checkHostGroupsPermissions(array_keys($groupids)) && self::checkHostsPermissions(array_keys($hostids)) && self::checkTriggersPermissions(array_keys($triggerids)) && self::checkMapsPermissions(array_keys($sysmapids)); } /** * Checks if the current user has access to the given host groups. * * @param array $groupids * * @return boolean */ private static function checkHostGroupsPermissions(array $groupids) { if ($groupids) { $count = API::HostGroup()->get([ 'countOutput' => true, 'groupids' => $groupids ]); return ($count == count($groupids)); } return true; } /** * Checks if the current user has access to the given hosts. * * @param array $hostids * * @return boolean */ private static function checkHostsPermissions(array $hostids) { if ($hostids) { $count = API::Host()->get([ 'countOutput' => true, 'hostids' => $hostids ]); return ($count == count($hostids)); } return true; } /** * Checks if the current user has access to the given triggers. * * @param array $triggerids * * @return boolean */ private static function checkTriggersPermissions(array $triggerids) { if ($triggerids) { $count = API::Trigger()->get([ 'countOutput' => true, 'triggerids' => $triggerids ]); return ($count == count($triggerids)); } return true; } /** * Checks if the current user has access to the given maps. * * @param array $sysmapids * * @return boolean */ private static function checkMapsPermissions(array $sysmapids) { if ($sysmapids) { $count = API::Map()->get([ 'countOutput' => true, 'sysmapids' => $sysmapids ]); return ($count == count($sysmapids)); } return true; } /** * Replace host groups of subtype = SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP by hosts that belongs to group. * * @param array $sysmap Map data. * @param array $theme Theme used to create missing elements (like hostgroup frame). * * @return array Array representing areas with area coordinates and selementids. */ protected static function populateHostGroupsWithHosts(array &$sysmap, array $theme) { // Collect host groups to populate with hosts. $groupids = []; foreach ($sysmap['selements'] as &$selement) { $selement['selementid_orig'] = $selement['selementid']; $selement['elementtype_orig'] = $selement['elementtype']; $selement['elementsubtype_orig'] = $selement['elementsubtype']; if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP && $selement['elementsubtype'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS) { if ($selement['permission'] >= PERM_READ) { $groupids[$selement['elements'][0]['groupid']] = true; } else { // If user has no access to whole host group, always show it as a SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP. $selement['elementsubtype'] = SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP; } } } unset($selement); $areas = []; if ($groupids) { $groups = API::HostGroup()->get([ 'output' => [], 'selectHosts' => ['hostid', 'name'], 'groupids' => array_keys($groupids), 'preservekeys' => true ]); $new_selementid = (count($sysmap['selements']) > 0) ? (int) max(zbx_objectValues($sysmap['selements'], 'selementid')) : 0; $new_linkid = (count($sysmap['links']) > 0) ? (int) max(array_keys($sysmap['links'])) : 0; foreach ($sysmap['selements'] as $selement_key => &$selement) { if ($selement['elementtype'] != SYSMAP_ELEMENT_TYPE_HOST_GROUP || $selement['elementsubtype'] != SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS) { continue; } $groupid = $selement['elements'][0]['groupid']; $group = $groups[$groupid]; $original_selement = $selement; // Jump to the next host group if current host group doesn't contain accessible hosts. if (!$group['hosts']) { continue; } // Sort hosts by name. CArrayHelper::sort($group['hosts'], ['field' => 'name', 'order' => ZBX_SORT_UP]); // Define area in which to locate hosts. if ($selement['areatype'] == SYSMAP_ELEMENT_AREA_TYPE_CUSTOM) { $area = [ 'selementids' => [], 'width' => $selement['width'], 'height' => $selement['height'], 'x' => $selement['x'], 'y' => $selement['y'] ]; $sysmap['shapes'][] = [ 'sysmap_shapeid' => 'e-'.$selement['selementid'], 'type' => SYSMAP_SHAPE_TYPE_RECTANGLE, 'x' => $selement['x'], 'y' => $selement['y'], 'width' => $selement['width'], 'height' => $selement['height'], 'border_type' => SYSMAP_SHAPE_BORDER_TYPE_SOLID, 'border_width' => 3, 'border_color' => $theme['maingridcolor'], 'background_color' => '', 'text' => '', 'zindex' => -1 ]; } else { $area = [ 'selementids' => [], 'width' => $sysmap['width'], 'height' => $sysmap['height'], 'x' => 0, 'y' => 0 ]; } // Add selected hosts as map selements. foreach ($group['hosts'] as $host) { $new_selementid++; $area['selementids'][] = $new_selementid; $sysmap['selements'][$new_selementid] = [ 'elementtype' => SYSMAP_ELEMENT_TYPE_HOST, 'elementsubtype' => SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP, 'elements' => [ ['hostid' => $host['hostid']] ], 'selementid' => $new_selementid ] + $selement; } $areas[] = $area; // Make links. $selements = zbx_toHash($sysmap['selements'], 'selementid'); foreach ($sysmap['links'] as $link) { // Do not multiply links between two areas. $id1 = $link['selementid1']; $id2 = $link['selementid2']; if ($selements[$id1]['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP && $selements[$id1]['elementsubtype'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS && $selements[$id2]['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP && $selements[$id2]['elementsubtype'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS) { continue; } if ($id1 == $original_selement['selementid']) { $id_number = 'selementid1'; } elseif ($id2 == $original_selement['selementid']) { $id_number = 'selementid2'; } else { continue; } foreach ($area['selementids'] as $selement_id) { $new_linkid++; $link['linkid'] = -$new_linkid; $link[$id_number] = $selement_id; $sysmap['links'][$new_linkid] = $link; } } } } // Remove host group elements that are replaced by hosts. $selements = []; foreach ($sysmap['selements'] as $key => $element) { if ($element['elementtype'] != SYSMAP_ELEMENT_TYPE_HOST_GROUP || $element['elementsubtype'] != SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS) { $selements[$key] = $element; } } $sysmap['selements'] = $selements; return $areas; } }