$element_types]); if ($update) { $db_selements = $this->fetchSelementsByIds(zbx_objectValues($selements, 'selementid')); $selements = $this->extendFromObjects(zbx_toHash($selements, 'selementid'), $db_selements, ['elementtype', 'elements']); } foreach ($selements as &$selement) { if (!is_array($selement)) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } if ($create) { // Check required parameters. $missing_keys = array_diff(['sysmapid', 'elementtype', 'iconid_off'], array_keys($selement)); if ($missing_keys) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Map element is missing parameters: %1$s', implode(', ', $missing_keys)) ); } } if (array_key_exists('urls', $selement)) { $url_validate_options = ['allow_user_macro' => false]; if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST) { $url_validate_options['allow_inventory_macro'] = INVENTORY_URL_MACRO_HOST; } elseif ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { $url_validate_options['allow_inventory_macro'] = INVENTORY_URL_MACRO_TRIGGER; } else { $url_validate_options['allow_inventory_macro'] = INVENTORY_URL_MACRO_NONE; } foreach ($selement['urls'] as $url_data) { if (!CHtmlUrlValidator::validate($url_data['url'], $url_validate_options)) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Wrong value for url field.')); } } } if ($update && array_key_exists('selementid', $selement) && array_key_exists($selement['selementid'], $db_selements)) { $db_selement = $db_selements[$selement['selementid']]; } if (!$elementtype_validator->validate($selement['elementtype'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'elementtype', _s('value must be one of %1$s', implode(', ', $element_types)) )); } if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_IMAGE) { unset($selement['elements']); } else { if (!array_key_exists('elements', $selement)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Map element is missing parameters: %1$s', 'elements') ); } if (!is_array($selement['elements'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } if (!$selement['elements']) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'elements', _('cannot be empty')) ); } } if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { foreach ($selement['elements'] as $element) { if (!array_key_exists('triggerid', $element)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Map element is missing parameters: %1$s', 'triggerid') ); } if (is_array($element['triggerid'])) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } elseif ($element['triggerid'] === '' || $element['triggerid'] === null || $element['triggerid'] === false) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'triggerid', _('cannot be empty')) ); } } } else { if (array_key_exists('elements', $selement)) { switch ($selement['elementtype']) { case SYSMAP_ELEMENT_TYPE_HOST_GROUP: $field = 'groupid'; break; case SYSMAP_ELEMENT_TYPE_HOST: $field = 'hostid'; break; case SYSMAP_ELEMENT_TYPE_MAP: $field = 'sysmapid'; break; } $elements = reset($selement['elements']); if (!is_array($elements)) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } if (!array_key_exists($field, $elements)) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Map element is missing parameters: %1$s', $field) ); } if (is_array($elements[$field])) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } elseif ($elements[$field] === '' || $elements[$field] === null || $elements[$field] === false) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', $field, _('cannot be empty')) ); } if (count($selement['elements']) > 1) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.', 'elements', _('incorrect element count')) ); } } } if (isset($selement['iconid_off']) && $selement['iconid_off'] == 0) { self::exception(ZBX_API_ERROR_PARAMETERS, _s('No icon for map element ""%1$s".', array_key_exists('label', $selement) ? $selement['label'] : '' )); } if ($create) { $selement['urls'] = array_key_exists('urls', $selement) ? $selement['urls'] : []; } } unset($selement); // check permissions to used objects if (!CMapHelper::checkSelementPermissions($selements)) { self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!')); } return $update ? $db_selements : true; } /** * Checks that shape color attributes are valid. * * @throws APIException if input is invalid. * * @param array $shapes An array of shapes. */ protected function checkShapeInput($shapes) { $color_validator = new CColorValidator(); $fields = ['border_color', 'background_color', 'font_color']; foreach ($shapes as $shape) { foreach ($fields as $field) { if (array_key_exists($field, $shape) && $shape[$field] !== '' && !$color_validator->validate($shape[$field])) { self::exception(ZBX_API_ERROR_PARAMETERS, $color_validator->getError()); } } } } /** * Returns a hash of map elements with the given IDs. The result also includes URL assigned to the elements. * * @param array $selementIds * * @return array */ protected function fetchSelementsByIds(array $selementIds) { $selements = API::getApiService()->select('sysmaps_elements', [ 'output' => API_OUTPUT_EXTEND, 'filter' => ['selementid' => $selementIds], 'preservekeys' => true ]); if ($selements) { foreach ($selements as &$selement) { $selement['urls'] = []; $selement['elements'] = []; } unset($selement); $selementUrls = API::getApiService()->select('sysmap_element_url', [ 'output' => API_OUTPUT_EXTEND, 'filter' => ['selementid' => $selementIds] ]); foreach ($selementUrls as $selementUrl) { $selements[$selementUrl['selementid']]['urls'][] = $selementUrl; } $selement_triggers = API::getApiService()->select('sysmap_element_trigger', [ 'output' => ['selement_triggerid', 'selementid', 'triggerid'], 'filter' => ['selementid' => $selementIds] ]); foreach ($selement_triggers as $selement_trigger) { $selements[$selement_trigger['selementid']]['elements'][] = [ 'selement_triggerid' => $selement_trigger['selement_triggerid'], 'triggerid' => $selement_trigger['triggerid'] ]; } $single_element_types = [SYSMAP_ELEMENT_TYPE_HOST, SYSMAP_ELEMENT_TYPE_MAP, SYSMAP_ELEMENT_TYPE_HOST_GROUP]; foreach ($selements as &$selement) { if (in_array($selement['elementtype'], $single_element_types)) { switch ($selement['elementtype']) { case SYSMAP_ELEMENT_TYPE_HOST_GROUP: $field = 'groupid'; break; case SYSMAP_ELEMENT_TYPE_HOST: $field = 'hostid'; break; case SYSMAP_ELEMENT_TYPE_MAP: $field = 'sysmapid'; break; } $selement['elements'][] = [$field => $selement['elementid']]; } unset($selement['elementid']); } unset($selement); } return $selements; } protected function checkLinkInput($links, $method) { $update = ($method == 'updateLink'); $delete = ($method == 'deleteLink'); // permissions if ($update || $delete) { $linkDbFields = ['linkid' => null]; $dbLinks = API::getApiService()->select('sysmap_element_url', [ 'filter' => ['selementid' => zbx_objectValues($links, 'linkid')], 'output' => ['linkid'], 'preservekeys' => true ]); } else { $linkDbFields = [ 'sysmapid' => null, 'selementid1' => null, 'selementid2' => null ]; } $colorValidator = new CColorValidator(); foreach ($links as $link) { if (!check_db_fields($linkDbFields, $link)) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Wrong fields for map link.')); } if (isset($link['color']) && !$colorValidator->validate($link['color'])) { self::exception(ZBX_API_ERROR_PARAMETERS, $colorValidator->getError()); } if ($update || $delete) { if (!isset($dbLinks[$link['linkid']])) { self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!')); } } } return true; } /** * Add element to sysmap. * * @param array $elements[0,...]['sysmapid'] * @param array $elements[0,...]['elementid'] * @param array $elements[0,...]['elementtype'] * @param array $elements[0,...]['label'] * @param array $elements[0,...]['x'] * @param array $elements[0,...]['y'] * @param array $elements[0,...]['iconid_off'] * @param array $elements[0,...]['iconid_on'] * @param array $elements[0,...]['iconid_disabled'] * @param array $elements[0,...]['urls'][0,...] * @param array $elements[0,...]['label_location'] * * @return array */ protected function createSelements(array $selements) { $selements = zbx_toArray($selements); $this->checkSelementInput($selements, __FUNCTION__); $single_element_types = [SYSMAP_ELEMENT_TYPE_HOST, SYSMAP_ELEMENT_TYPE_MAP, SYSMAP_ELEMENT_TYPE_HOST_GROUP]; foreach ($selements as &$selement) { if (in_array($selement['elementtype'], $single_element_types)) { $selement['elementid'] = reset($selement['elements'][0]); } elseif ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { unset($selement['elementid']); } } unset($selement); $selementids = DB::insert('sysmaps_elements', $selements); $triggerids = []; foreach ($selementids as $key => $selementid) { if ($selements[$key]['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { foreach ($selements[$key]['elements'] as $element) { $triggerids[$element['triggerid']] = true; } } } $db_triggers = API::Trigger()->get([ 'output' => ['triggerid', 'priority'], 'triggerids' => array_keys($triggerids), 'preservekeys' => true ]); $triggers = []; foreach ($selementids as $key => $selementid) { if ($selements[$key]['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { foreach ($selements[$key]['elements'] as $element) { $priority = $db_triggers[$element['triggerid']]['priority']; $triggers[$selementid][$priority][] = [ 'selementid' => $selementid, 'triggerid' => $element['triggerid'] ]; } krsort($triggers[$selementid]); } } $triggers_to_add = []; foreach ($triggers as $selement_triggers) { foreach ($selement_triggers as $selement_trigger_priorities) { foreach ($selement_trigger_priorities as $selement_trigger_priority) { $triggers_to_add[] = $selement_trigger_priority; } } } DB::insert('sysmap_element_trigger', $triggers_to_add); $insertUrls = []; foreach ($selementids as $key => $selementid) { foreach ($selements[$key]['urls'] as $url) { $url['selementid'] = $selementid; $insertUrls[] = $url; } } DB::insert('sysmap_element_url', $insertUrls); return ['selementids' => $selementids]; } /** * Update element to sysmap. * * @param array $elements[0,...]['selementid'] * @param array $elements[0,...]['sysmapid'] * @param array $elements[0,...]['elementid'] * @param array $elements[0,...]['elementtype'] * @param array $elements[0,...]['label'] * @param array $elements[0,...]['x'] * @param array $elements[0,...]['y'] * @param array $elements[0,...]['iconid_off'] * @param array $elements[0,...]['iconid_on'] * @param array $elements[0,...]['iconid_disabled'] * @param array $elements[0,...]['url'] * @param array $elements[0,...]['label_location'] */ protected function updateSelements(array $selements) { $selements = zbx_toArray($selements); $selementIds = []; $db_selements = $this->checkSelementInput($selements, __FUNCTION__); $update = []; $urlsToDelete = []; $urlsToUpdate = []; $urlsToAdd = []; $triggers_to_add = []; $triggers_to_delete = []; $triggerids = []; foreach ($selements as &$selement) { $db_selement = $db_selements[$selement['selementid']]; // Change type from something to trigger. if ($selement['elementtype'] != $db_selement['elementtype'] && $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { $selement['elementid'] = 0; foreach ($selement['elements'] as $element) { $triggerids[$element['triggerid']] = true; } } // Change type from trigger to something. if ($selement['elementtype'] != $db_selement['elementtype'] && $db_selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { foreach ($db_selement['elements'] as $db_element) { $triggers_to_delete[] = $db_element['selement_triggerid']; } } if ($selement['elementtype'] != SYSMAP_ELEMENT_TYPE_IMAGE && $selement['elementtype'] != SYSMAP_ELEMENT_TYPE_TRIGGER) { $selement['elementid'] = reset($selement['elements'][0]); } $db_elements = $db_selement['elements']; foreach ($db_selement['elements'] as &$element) { unset($element['selement_triggerid']); } unset($element); if ($selement['elementtype'] == $db_selement['elementtype'] && $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { foreach ($db_elements as $element) { $triggers_to_delete[] = $element['selement_triggerid']; } foreach ($selement['elements'] as $element) { $triggerids[$element['triggerid']] = true; } } $update[] = [ 'values' => $selement, 'where' => ['selementid' => $selement['selementid']] ]; $selementIds[] = $selement['selementid']; if (!isset($selement['urls'])) { continue; } $diffUrls = zbx_array_diff($selement['urls'], $db_selement['urls'], 'name'); // add foreach ($diffUrls['first'] as $newUrl) { $newUrl['selementid'] = $selement['selementid']; $urlsToAdd[] = $newUrl; } // update url foreach ($diffUrls['both'] as $updUrl) { $urlsToUpdate[] = [ 'values' => $updUrl, 'where' => [ 'selementid' => $selement['selementid'], 'name' => $updUrl['name'] ] ]; } // delete url $urlsToDelete = array_merge($urlsToDelete, zbx_objectValues($diffUrls['second'], 'sysmapelementurlid')); } unset($selement); $db_triggers = API::Trigger()->get([ 'output' => ['triggerid', 'priority'], 'triggerids' => array_keys($triggerids), 'preservekeys' => true ]); $triggers = []; foreach ($selements as $key => $selement) { if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) { $selementid = $selement['selementid']; foreach ($selement['elements'] as $element) { $priority = $db_triggers[$element['triggerid']]['priority']; $triggers[$selementid][$priority][] = [ 'selementid' => $selementid, 'triggerid' => $element['triggerid'] ]; } krsort($triggers[$selementid]); } } $triggers_to_add = []; foreach ($triggers as $selement_triggers) { foreach ($selement_triggers as $selement_trigger_priorities) { foreach ($selement_trigger_priorities as $selement_trigger_priority) { $triggers_to_add[] = $selement_trigger_priority; } } } DB::update('sysmaps_elements', $update); if (!empty($urlsToDelete)) { DB::delete('sysmap_element_url', ['sysmapelementurlid' => $urlsToDelete]); } if (!empty($urlsToUpdate)) { DB::update('sysmap_element_url', $urlsToUpdate); } if (!empty($urlsToAdd)) { DB::insert('sysmap_element_url', $urlsToAdd); } if ($triggers_to_delete) { DB::delete('sysmap_element_trigger', ['selement_triggerid' => $triggers_to_delete]); } if ($triggers_to_add) { DB::insert('sysmap_element_trigger', $triggers_to_add); } return ['selementids' => $selementIds]; } /** * Delete element from map. * * @param array $selements multidimensional array with selement objects * @param array $selements[0, ...]['selementid'] selementid to delete */ protected function deleteSelements(array $selements) { $selements = zbx_toArray($selements); $selementIds = zbx_objectValues($selements, 'selementid'); DB::delete('sysmaps_elements', ['selementid' => $selementIds]); return $selementIds; } /** * Add shape to sysmap. * * @param array $shapes Multidimensional array with shape properties. */ protected function createShapes(array $shapes) { $shapes = zbx_toArray($shapes); $this->checkShapeInput($shapes); DB::insert('sysmap_shape', $shapes); } /** * Update shapes to sysmap. * * @param array $shapes Multidimensional array with shape properties. */ protected function updateShapes(array $shapes) { $shapes = zbx_toArray($shapes); $this->checkShapeInput($shapes); $update = []; foreach ($shapes as $shape) { $shapeid = $shape['sysmap_shapeid']; unset($shape['sysmap_shapeid']); if ($shape) { $update[] = [ 'values' => $shape, 'where' => ['sysmap_shapeid' => $shapeid] ]; } } DB::update('sysmap_shape', $update); } /** * Delete shapes from map. * * @param array $shapes Multidimensional array with shape properties. */ protected function deleteShapes(array $shapes) { $shapes = zbx_toArray($shapes); $shapeids = zbx_objectValues($shapes, 'sysmap_shapeid'); DB::delete('sysmap_shape', ['sysmap_shapeid' => $shapeids]); } /** * Create link. * * @param array $links * @param array $links[0,...]['sysmapid'] * @param array $links[0,...]['selementid1'] * @param array $links[0,...]['selementid2'] * @param array $links[0,...]['drawtype'] * @param array $links[0,...]['color'] * * @return array */ protected function createLinks(array $links) { $links = zbx_toArray($links); $this->checkLinkInput($links, __FUNCTION__); $linkIds = DB::insert('sysmaps_links', $links); return ['linkids' => $linkIds]; } protected function updateLinks($links) { $links = zbx_toArray($links); $this->checkLinkInput($links, __FUNCTION__); $udpateLinks = []; foreach ($links as $link) { $udpateLinks[] = ['values' => $link, 'where' => ['linkid' => $link['linkid']]]; } DB::update('sysmaps_links', $udpateLinks); return ['linkids' => zbx_objectValues($links, 'linkid')]; } /** * Delete Link from map. * * @param array $links multidimensional array with link objects * @param array $links[0, ...]['linkid'] link ID to delete * * @return array */ protected function deleteLinks($links) { zbx_value2array($links); $linkIds = zbx_objectValues($links, 'linkid'); $this->checkLinkInput($links, __FUNCTION__); DB::delete('sysmaps_links', ['linkid' => $linkIds]); return ['linkids' => $linkIds]; } /** * Add link trigger to link (sysmap). * * @param array $links[0,...]['linkid'] * @param array $links[0,...]['triggerid'] * @param array $links[0,...]['drawtype'] * @param array $links[0,...]['color'] */ protected function createLinkTriggers($linkTriggers) { $linkTriggers = zbx_toArray($linkTriggers); $this->validateCreateLinkTriggers($linkTriggers); $linkTriggerIds = DB::insert('sysmaps_link_triggers', $linkTriggers); return ['linktriggerids' => $linkTriggerIds]; } protected function validateCreateLinkTriggers(array $linkTriggers) { $linkTriggerDbFields = [ 'linkid' => null, 'triggerid' => null ]; $colorValidator = new CColorValidator(); foreach ($linkTriggers as $linkTrigger) { if (!check_db_fields($linkTriggerDbFields, $linkTrigger)) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } if (isset($linkTrigger['color']) && !$colorValidator->validate($linkTrigger['color'])) { self::exception(ZBX_API_ERROR_PARAMETERS, $colorValidator->getError()); } } } protected function updateLinkTriggers($linkTriggers) { $linkTriggers = zbx_toArray($linkTriggers); $this->validateUpdateLinkTriggers($linkTriggers); $linkTriggerIds = zbx_objectValues($linkTriggers, 'linktriggerid'); $updateLinkTriggers = []; foreach ($linkTriggers as $linkTrigger) { $updateLinkTriggers[] = [ 'values' => $linkTrigger, 'where' => ['linktriggerid' => $linkTrigger['linktriggerid']] ]; } DB::update('sysmaps_link_triggers', $updateLinkTriggers); return ['linktriggerids' => $linkTriggerIds]; } protected function validateUpdateLinkTriggers(array $linkTriggers) { $linkTriggerDbFields = ['linktriggerid' => null]; $colorValidator = new CColorValidator(); foreach ($linkTriggers as $linkTrigger) { if (!check_db_fields($linkTriggerDbFields, $linkTrigger)) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } if (isset($linkTrigger['color']) && !$colorValidator->validate($linkTrigger['color'])) { self::exception(ZBX_API_ERROR_PARAMETERS, $colorValidator->getError()); } } } protected function deleteLinkTriggers($linkTriggers) { $linkTriggers = zbx_toArray($linkTriggers); $this->validateDeleteLinkTriggers($linkTriggers); $linkTriggerIds = zbx_objectValues($linkTriggers, 'linktriggerid'); DB::delete('sysmaps_link_triggers', ['linktriggerid' => $linkTriggerIds]); return ['linktriggerids' => $linkTriggerIds]; } protected function validateDeleteLinkTriggers(array $linkTriggers) { $linktriggerDbFields = ['linktriggerid' => null]; foreach ($linkTriggers as $linkTrigger) { if (!check_db_fields($linktriggerDbFields, $linkTrigger)) { self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.')); } } } }