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 * Class containing methods for operations with value maps. 24 */ 25class CValueMap extends CApiService { 26 27 public const ACCESS_RULES = [ 28 'get' => ['min_user_type' => USER_TYPE_ZABBIX_USER], 29 'create' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN], 30 'update' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN], 31 'delete' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN] 32 ]; 33 34 protected $tableName = 'valuemap'; 35 protected $tableAlias = 'vm'; 36 protected $sortColumns = ['valuemapid', 'name']; 37 38 /** 39 * Get value maps. 40 * 41 * @param array $options 42 * 43 * @return array 44 */ 45 public function get($options = []) { 46 $api_input_rules = ['type' => API_OBJECT, 'fields' => [ 47 // filter 48 'valuemapids' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE, 'default' => null], 49 'hostids' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE, 'default' => null], 50 'filter' => ['type' => API_OBJECT, 'flags' => API_ALLOW_NULL, 'default' => null, 'fields' => [ 51 'valuemapid' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 52 'hostid' => ['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE], 53 'name' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE] 54 ]], 55 'search' => ['type' => API_OBJECT, 'flags' => API_ALLOW_NULL, 'default' => null, 'fields' => [ 56 'name' => ['type' => API_STRINGS_UTF8, 'flags' => API_ALLOW_NULL | API_NORMALIZE] 57 ]], 58 'searchByAny' => ['type' => API_BOOLEAN, 'default' => false], 59 'startSearch' => ['type' => API_FLAG, 'default' => false], 60 'excludeSearch' => ['type' => API_FLAG, 'default' => false], 61 'searchWildcardsEnabled' => ['type' => API_BOOLEAN, 'default' => false], 62 // output 63 'output' => ['type' => API_OUTPUT, 'in' => implode(',', ['valuemapid', 'uuid', 'name', 'hostid']), 'default' => API_OUTPUT_EXTEND], 64 'selectMappings' => ['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL | API_ALLOW_COUNT, 'in' => implode(',', ['type', 'value', 'newvalue']), 'default' => null], 65 'countOutput' => ['type' => API_FLAG, 'default' => false], 66 // sort and limit 67 'sortfield' => ['type' => API_STRINGS_UTF8, 'flags' => API_NORMALIZE, 'in' => implode(',', $this->sortColumns), 'uniq' => true, 'default' => []], 68 'sortorder' => ['type' => API_SORTORDER, 'default' => []], 69 'limit' => ['type' => API_INT32, 'flags' => API_ALLOW_NULL, 'in' => '1:'.ZBX_MAX_INT32, 'default' => null], 70 // flags 71 'editable' => ['type' => API_BOOLEAN, 'default' => false], 72 'preservekeys' => ['type' => API_BOOLEAN, 'default' => false] 73 ]]; 74 if (!CApiInputValidator::validate($api_input_rules, $options, '/', $error)) { 75 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 76 } 77 78 $db_valuemaps = []; 79 $sql_parts = $this->createSelectQueryParts($this->tableName(), $this->tableAlias(), $options); 80 81 // Permission check. 82 if (self::$userData['type'] != USER_TYPE_SUPER_ADMIN) { 83 $permission = $options['editable'] ? PERM_READ_WRITE : PERM_READ; 84 $userGroups = getUserGroupsByUserId(self::$userData['userid']); 85 86 $sql_parts['where'][] = 'EXISTS ('. 87 'SELECT NULL'. 88 ' FROM hosts_groups hgg'. 89 ' JOIN rights r'. 90 ' ON r.id=hgg.groupid'. 91 ' AND '.dbConditionInt('r.groupid', $userGroups). 92 ' WHERE vm.hostid=hgg.hostid'. 93 ' GROUP BY hgg.hostid'. 94 ' HAVING MIN(r.permission)>'.PERM_DENY. 95 ' AND MAX(r.permission)>='.zbx_dbstr($permission). 96 ')'; 97 } 98 99 // hostids 100 if ($options['hostids'] !== null) { 101 $sql_parts['where']['hostid'] = dbConditionInt('vm.hostid', $options['hostids']); 102 } 103 104 $result = DBselect(self::createSelectQueryFromParts($sql_parts), $options['limit']); 105 106 while ($row = DBfetch($result)) { 107 if ($options['countOutput']) { 108 return $row['rowscount']; 109 } 110 111 $db_valuemaps[$row['valuemapid']] = $row; 112 } 113 114 if ($db_valuemaps) { 115 $db_valuemaps = $this->addRelatedObjects($options, $db_valuemaps); 116 $db_valuemaps = $this->unsetExtraFields($db_valuemaps, ['valuemapid'], $options['output']); 117 118 if (!$options['preservekeys']) { 119 $db_valuemaps = zbx_cleanHashes($db_valuemaps); 120 } 121 } 122 123 return $db_valuemaps; 124 } 125 126 /** 127 * @param array $valuemaps 128 * 129 * @return array 130 * 131 * @throws APIException 132 */ 133 public function create(array $valuemaps) { 134 $this->validateCreate($valuemaps); 135 136 $valuemapids = DB::insertBatch('valuemap', $valuemaps); 137 138 $mappings = []; 139 140 foreach ($valuemaps as $index => &$valuemap) { 141 $valuemap['valuemapid'] = $valuemapids[$index]; 142 $sortorder = 0; 143 144 foreach ($valuemap['mappings'] as $mapping) { 145 $mappings[] = [ 146 'type' => array_key_exists('type', $mapping) ? $mapping['type'] : VALUEMAP_MAPPING_TYPE_EQUAL, 147 'valuemapid' => $valuemap['valuemapid'], 148 'value' => array_key_exists('value', $mapping) ? $mapping['value'] : '', 149 'newvalue' => $mapping['newvalue'], 150 'sortorder' => $sortorder++ 151 ]; 152 } 153 } 154 unset($valuemap); 155 156 DB::insertBatch('valuemap_mapping', $mappings); 157 158 $this->addAuditBulk(AUDIT_ACTION_ADD, AUDIT_RESOURCE_VALUE_MAP, $valuemaps); 159 160 return ['valuemapids' => $valuemapids]; 161 } 162 163 /** 164 * @param array $valuemap 165 * 166 * @return array 167 */ 168 public function update(array $valuemaps) { 169 $this->validateUpdate($valuemaps, $db_valuemaps); 170 171 $upd_valuemaps = []; 172 $valuemaps_mappings = []; 173 174 foreach ($valuemaps as $valuemap) { 175 $valuemapid = $valuemap['valuemapid']; 176 177 $db_valuemap = $db_valuemaps[$valuemapid]; 178 179 if (array_key_exists('name', $valuemap) && $valuemap['name'] !== $db_valuemap['name']) { 180 $upd_valuemaps[] = [ 181 'values' => ['name' => $valuemap['name']], 182 'where' => ['valuemapid' => $valuemap['valuemapid']] 183 ]; 184 } 185 186 if (array_key_exists('mappings', $valuemap)) { 187 $valuemaps_mappings[$valuemapid] = []; 188 $sortorder = 0; 189 190 foreach ($valuemap['mappings'] as $mapping) { 191 $mapping += ['type' => VALUEMAP_MAPPING_TYPE_EQUAL, 'value' => '']; 192 $valuemaps_mappings[$valuemapid][] = [ 193 'type' => $mapping['type'], 194 'value' => $mapping['value'], 195 'newvalue' => $mapping['newvalue'], 196 'sortorder' => $sortorder++ 197 ]; 198 } 199 } 200 } 201 202 if ($upd_valuemaps) { 203 DB::update('valuemap', $upd_valuemaps); 204 } 205 206 if ($valuemaps_mappings) { 207 $db_mappings = DB::select('valuemap_mapping', [ 208 'output' => ['valuemap_mappingid', 'valuemapid', 'type', 'value', 'newvalue', 'sortorder'], 209 'filter' => ['valuemapid' => array_keys($valuemaps_mappings)] 210 ]); 211 CArrayHelper::sort($db_mappings, [['field' => 'sortorder', 'order' => ZBX_SORT_UP]]); 212 213 $ins_mapings = []; 214 $upd_mapings = []; 215 $del_mapingids = []; 216 $valuemapid_db_mappings = array_fill_keys(array_keys($valuemaps_mappings), []); 217 218 foreach ($db_mappings as $db_mapping) { 219 $valuemapid_db_mappings[$db_mapping['valuemapid']][] = $db_mapping; 220 } 221 222 foreach ($valuemaps_mappings as $valuemapid => $mappings) { 223 $db_mappings = &$valuemapid_db_mappings[$valuemapid]; 224 225 foreach ($mappings as $mapping) { 226 $exists = false; 227 228 foreach ($db_mappings as $i => $db_mapping) { 229 if ($db_mapping['type'] == $mapping['type'] && $db_mapping['value'] == $mapping['value']) { 230 $exists = true; 231 break; 232 } 233 } 234 235 if (!$exists) { 236 $ins_mapings[] = ['valuemapid' => $valuemapid] + $mapping; 237 continue; 238 } 239 240 $update_fields = array_diff_assoc($mapping, $db_mapping); 241 242 if ($update_fields) { 243 $upd_mapings[] = [ 244 'values' => $update_fields, 245 'where' => ['valuemap_mappingid' => $db_mapping['valuemap_mappingid']] 246 ]; 247 } 248 249 unset($db_mappings[$i]); 250 } 251 } 252 unset($db_mappings); 253 254 foreach ($valuemapid_db_mappings as $db_mappings) { 255 if ($db_mappings) { 256 $del_mapingids = array_merge($del_mapingids, array_column($db_mappings, 'valuemap_mappingid')); 257 } 258 } 259 260 if ($del_mapingids) { 261 DB::delete('valuemap_mapping', ['valuemap_mappingid' => $del_mapingids]); 262 } 263 264 if ($upd_mapings) { 265 DB::update('valuemap_mapping', $upd_mapings); 266 } 267 268 if ($ins_mapings) { 269 DB::insertBatch('valuemap_mapping', $ins_mapings); 270 } 271 } 272 273 $this->addAuditBulk(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_VALUE_MAP, $valuemaps, $db_valuemaps); 274 275 return ['valuemapids' => array_column($valuemaps, 'valuemapid')]; 276 } 277 278 /** 279 * @param array $valuemapids 280 * 281 * @return array 282 */ 283 public function delete(array $valuemapids) { 284 $api_input_rules = ['type' => API_IDS, 'flags' => API_NOT_EMPTY, 'uniq' => true]; 285 if (!CApiInputValidator::validate($api_input_rules, $valuemapids, '/', $error)) { 286 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 287 } 288 289 $db_valuemaps = $this->get([ 290 'output' => ['valuemapid', 'name'], 291 'valuemapids' => $valuemapids, 292 'editable' => true, 293 'preservekeys' => true 294 ]); 295 296 foreach ($valuemapids as $valuemapid) { 297 if (!array_key_exists($valuemapid, $db_valuemaps)) { 298 self::exception(ZBX_API_ERROR_PERMISSIONS, 299 _('No permissions to referred object or it does not exist!') 300 ); 301 } 302 } 303 304 DB::update('items', [[ 305 'values' => ['valuemapid' => 0], 306 'where' => ['valuemapid' => $valuemapids] 307 ]]); 308 309 $this->deleteByIds($valuemapids); 310 311 $this->addAuditBulk(AUDIT_ACTION_DELETE, AUDIT_RESOURCE_VALUE_MAP, $db_valuemaps); 312 313 return ['valuemapids' => $valuemapids]; 314 } 315 316 /** 317 * Check for duplicated value maps. 318 * 319 * @param array $names_by_hostid 320 * 321 * @throws APIException if value map already exists. 322 */ 323 private function checkDuplicates(array $names_by_hostid) { 324 $sql_where = []; 325 foreach ($names_by_hostid as $hostid => $names) { 326 $sql_where[] = '(vm.hostid='.$hostid.' AND '.dbConditionString('vm.name', $names).')'; 327 } 328 329 $db_valuemaps = DBfetchArray( 330 DBselect('SELECT vm.name FROM valuemap vm WHERE '.implode(' OR ', $sql_where), 1) 331 ); 332 333 if ($db_valuemaps) { 334 self::exception(ZBX_API_ERROR_PARAMETERS, 335 _s('Value map "%1$s" already exists.', $db_valuemaps[0]['name']) 336 ); 337 } 338 } 339 340 /** 341 * @param array $valuemaps 342 * 343 * @throws APIException if the input is invalid. 344 */ 345 private function validateCreate(array &$valuemaps) { 346 $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['uuid'], ['hostid', 'name']], 'fields' => [ 347 'hostid' => ['type' => API_ID, 'flags' => API_REQUIRED | API_NOT_EMPTY], 348 'uuid' => ['type' => API_UUID], 349 'name' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('valuemap', 'name')], 350 'mappings' => ['type' => API_OBJECTS, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'fields' => [ 351 'type' => ['type' => API_INT32, 'default' => VALUEMAP_MAPPING_TYPE_EQUAL, 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_EQUAL, VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, VALUEMAP_MAPPING_TYPE_LESS_EQUAL, VALUEMAP_MAPPING_TYPE_IN_RANGE, VALUEMAP_MAPPING_TYPE_REGEXP, VALUEMAP_MAPPING_TYPE_DEFAULT])], 352 'value' => ['type' => API_MULTIPLE, 'rules' => [ 353 [ 354 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_EQUAL])], 355 'type' => API_STRING_UTF8, 356 'length' => DB::getFieldLength('valuemap_mapping', 'value') 357 ], 358 [ 359 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, VALUEMAP_MAPPING_TYPE_LESS_EQUAL])], 360 'type' => API_FLOAT, 361 'length' => DB::getFieldLength('valuemap_mapping', 'value') 362 ], 363 [ 364 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_IN_RANGE])], 365 'type' => API_NUMERIC_RANGES, 366 'flags' => API_NOT_EMPTY, 367 'length' => DB::getFieldLength('valuemap_mapping', 'value') 368 ], 369 [ 370 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_REGEXP])], 371 'type' => API_REGEX, 372 'flags' => API_NOT_EMPTY, 373 'length' => DB::getFieldLength('valuemap_mapping', 'value') 374 ], 375 [ 376 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_DEFAULT])], 377 'type' => API_STRING_UTF8 378 ] 379 ]], 380 'newvalue' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('valuemap_mapping', 'newvalue')] 381 ]] 382 ]]; 383 if (!CApiInputValidator::validate($api_input_rules, $valuemaps, '/', $error)) { 384 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 385 } 386 387 $this->validateValuemapMappings($valuemaps); 388 $hostids = []; 389 390 foreach ($valuemaps as $valuemap) { 391 $hostids[$valuemap['hostid']] = true; 392 } 393 394 $db_hosts = API::Host()->get([ 395 'output' => ['status'], 396 'hostids' => array_keys($hostids), 397 'templated_hosts' => true, 398 'editable' => true, 399 'preservekeys' => true 400 ]); 401 402 $names_by_hostid = []; 403 404 foreach ($valuemaps as $valuemap) { 405 // check permissions by hostid 406 if (!array_key_exists($valuemap['hostid'], $db_hosts)) { 407 self::exception(ZBX_API_ERROR_PERMISSIONS, 408 _('No permissions to referred object or it does not exist!') 409 ); 410 } 411 412 $names_by_hostid[$valuemap['hostid']][] = $valuemap['name']; 413 } 414 415 $this->checkAndAddUuid($valuemaps, $db_hosts); 416 $this->checkDuplicates($names_by_hostid); 417 } 418 419 /** 420 * Check that only value maps on templates have UUID. Add UUID to all value maps on templates, if it doesn't exist. 421 * 422 * @param array $valuemaps_to_create 423 * @param array $db_hosts 424 * 425 * @throws APIException 426 */ 427 protected function checkAndAddUuid(array &$valuemaps_to_create, array $db_hosts): void { 428 foreach ($valuemaps_to_create as $index => &$valuemap) { 429 if ($db_hosts[$valuemap['hostid']]['status'] != HOST_STATUS_TEMPLATE 430 && array_key_exists('uuid', $valuemap)) { 431 self::exception(ZBX_API_ERROR_PARAMETERS, 432 _s('Invalid parameter "%1$s": %2$s.', '/'.($index + 1), _s('unexpected parameter "%1$s"', 'uuid')) 433 ); 434 } 435 436 if ($db_hosts[$valuemap['hostid']]['status'] == HOST_STATUS_TEMPLATE 437 && !array_key_exists('uuid', $valuemap)) { 438 $valuemap['uuid'] = generateUuidV4(); 439 } 440 } 441 unset($valuemap); 442 443 $db_uuid = DB::select('valuemap', [ 444 'output' => ['uuid'], 445 'filter' => ['uuid' => array_column($valuemaps_to_create, 'uuid')], 446 'limit' => 1 447 ]); 448 449 if ($db_uuid) { 450 self::exception(ZBX_API_ERROR_PARAMETERS, 451 _s('Entry with UUID "%1$s" already exists.', $db_uuid[0]['uuid']) 452 ); 453 } 454 } 455 456 /** 457 * @param array $valuemaps 458 * @param array $db_valuemaps 459 * 460 * @throws APIException if the input is invalid. 461 */ 462 private function validateUpdate(array &$valuemaps, array &$db_valuemaps = null) { 463 $api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['valuemapid']], 'fields' => [ 464 'valuemapid' => ['type' => API_ID, 'flags' => API_REQUIRED], 465 'name' => ['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('valuemap', 'name')], 466 'mappings' => ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY, 'fields' => [ 467 'type' => ['type' => API_INT32, 'default' => VALUEMAP_MAPPING_TYPE_EQUAL, 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_EQUAL, VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, VALUEMAP_MAPPING_TYPE_LESS_EQUAL, VALUEMAP_MAPPING_TYPE_IN_RANGE, VALUEMAP_MAPPING_TYPE_REGEXP, VALUEMAP_MAPPING_TYPE_DEFAULT])], 468 'value' => ['type' => API_MULTIPLE, 'rules' => [ 469 [ 470 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_EQUAL])], 471 'type' => API_STRING_UTF8, 472 'length' => DB::getFieldLength('valuemap_mapping', 'value') 473 ], 474 [ 475 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, VALUEMAP_MAPPING_TYPE_LESS_EQUAL])], 476 'type' => API_FLOAT, 477 'length' => DB::getFieldLength('valuemap_mapping', 'value') 478 ], 479 [ 480 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_IN_RANGE])], 481 'type' => API_NUMERIC_RANGES, 482 'flags' => API_NOT_EMPTY, 483 'length' => DB::getFieldLength('valuemap_mapping', 'value') 484 ], 485 [ 486 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_REGEXP])], 487 'type' => API_REGEX, 488 'flags' => API_NOT_EMPTY, 489 'length' => DB::getFieldLength('valuemap_mapping', 'value') 490 ], 491 [ 492 'if' => ['field' => 'type', 'in' => implode(',', [VALUEMAP_MAPPING_TYPE_DEFAULT])], 493 'type' => API_STRING_UTF8 494 ] 495 ]], 496 'newvalue' => ['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('valuemap_mapping', 'newvalue')] 497 ]] 498 ]]; 499 if (!CApiInputValidator::validate($api_input_rules, $valuemaps, '/', $error)) { 500 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 501 } 502 503 $this->validateValuemapMappings($valuemaps); 504 $db_valuemaps = $this->get([ 505 'output' => ['valuemapid', 'hostid', 'name'], 506 'valuemapids' => array_column($valuemaps, 'valuemapid'), 507 'editable' => true, 508 'preservekeys' => true 509 ]); 510 511 foreach ($valuemaps as $valuemap) { 512 if (!array_key_exists($valuemap['valuemapid'], $db_valuemaps)) { 513 self::exception(ZBX_API_ERROR_PERMISSIONS, 514 _('No permissions to referred object or it does not exist!') 515 ); 516 } 517 } 518 519 $valuemaps = $this->extendObjectsByKey($valuemaps, $db_valuemaps, 'valuemapid', ['hostid']); 520 521 $api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['hostid', 'name']], 'fields' => [ 522 'hostid' => ['type' => API_ID], 523 'name' => ['type' => API_STRING_UTF8] 524 ]]; 525 526 if (!CApiInputValidator::validateUniqueness($api_input_rules, $valuemaps, '/', $error)) { 527 self::exception(ZBX_API_ERROR_PARAMETERS, $error); 528 } 529 530 $names_by_hostid = []; 531 532 foreach ($valuemaps as $valuemap) { 533 $db_valuemap = $db_valuemaps[$valuemap['valuemapid']]; 534 535 if (array_key_exists('name', $valuemap) && $valuemap['name'] !== $db_valuemap['name']) { 536 $names_by_hostid[$valuemap['hostid']][] = $valuemap['name']; 537 } 538 } 539 540 if ($names_by_hostid) { 541 $this->checkDuplicates($names_by_hostid); 542 } 543 } 544 545 /** 546 * Validate uniqueness of mapping value in value maps, type VALUEMAP_MAPPING_TYPE_DEFAULT can be defined only once 547 * per value map mappings. 548 * 549 * @param array $valuemaps Array of valuemaps 550 * 551 * @throws Exception when non uniquw 552 */ 553 protected function validateValuemapMappings(array $valuemaps) { 554 $i = 0; 555 $error = ''; 556 557 foreach ($valuemaps as $valuemap) { 558 $i++; 559 560 if (!array_key_exists('mappings', $valuemap)) { 561 continue; 562 } 563 564 $type_uniq = array_fill_keys([VALUEMAP_MAPPING_TYPE_EQUAL, VALUEMAP_MAPPING_TYPE_GREATER_EQUAL, 565 VALUEMAP_MAPPING_TYPE_LESS_EQUAL, VALUEMAP_MAPPING_TYPE_IN_RANGE, VALUEMAP_MAPPING_TYPE_REGEXP 566 ], [] 567 ); 568 $has_default = false; 569 570 foreach (array_values($valuemap['mappings']) as $j => $mapping) { 571 $type = array_key_exists('type', $mapping) ? $mapping['type'] : VALUEMAP_MAPPING_TYPE_EQUAL; 572 $value = array_key_exists('value', $mapping) ? (string) $mapping['value'] : ''; 573 574 if ($has_default && $type == VALUEMAP_MAPPING_TYPE_DEFAULT) { 575 $error = _s('value %1$s already exists', '(type)=('.VALUEMAP_MAPPING_TYPE_DEFAULT.')'); 576 } 577 elseif (!array_key_exists('value', $mapping) && $type != VALUEMAP_MAPPING_TYPE_DEFAULT) { 578 $error = _s('the parameter "%1$s" is missing', 'value'); 579 } 580 elseif ($value !== '' && $type == VALUEMAP_MAPPING_TYPE_DEFAULT) { 581 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', 582 sprintf('/%1$s/mappings/%2$s/value', $i, $j + 1), 583 _('should be empty') 584 )); 585 } 586 elseif ($type != VALUEMAP_MAPPING_TYPE_DEFAULT && array_key_exists($value, $type_uniq[$type])) { 587 $error = _s('value %1$s already exists', '(value)=('.$value.')'); 588 } 589 590 if ($error !== '') { 591 self::exception(ZBX_API_ERROR_PARAMETERS, _s('Invalid parameter "%1$s": %2$s.', 592 sprintf('/%1$s/mappings/%2$s', $i, $j + 1), 593 $error 594 )); 595 } 596 597 $has_default = ($has_default || $type == VALUEMAP_MAPPING_TYPE_DEFAULT); 598 $type_uniq[$type][$value] = true; 599 } 600 } 601 } 602 603 protected function addRelatedObjects(array $options, array $db_valuemaps) { 604 $db_valuemaps = parent::addRelatedObjects($options, $db_valuemaps); 605 606 // Select mappings for value map. 607 if ($options['selectMappings'] !== null) { 608 $def_mappings = ($options['selectMappings'] == API_OUTPUT_COUNT) ? '0' : []; 609 610 foreach ($db_valuemaps as $valuemapid => $db_valuemap) { 611 $db_valuemaps[$valuemapid]['mappings'] = $def_mappings; 612 } 613 614 if ($options['selectMappings'] == API_OUTPUT_COUNT) { 615 $db_mappings = DBselect( 616 'SELECT m.valuemapid,COUNT(*) AS cnt'. 617 ' FROM valuemap_mapping m'. 618 ' WHERE '.dbConditionInt('m.valuemapid', array_keys($db_valuemaps)). 619 ' GROUP BY m.valuemapid' 620 ); 621 622 while ($db_mapping = DBfetch($db_mappings)) { 623 $db_valuemaps[$db_mapping['valuemapid']]['mappings'] = $db_mapping['cnt']; 624 } 625 } 626 else { 627 $db_mappings = API::getApiService()->select('valuemap_mapping', [ 628 'output' => $this->outputExtend($options['selectMappings'], ['valuemapid', 629 'valuemap_mappingid', 'sortorder' 630 ]), 631 'filter' => ['valuemapid' => array_keys($db_valuemaps)] 632 ]); 633 CArrayHelper::sort($db_mappings, [['field' => 'sortorder', 'order' => ZBX_SORT_UP]]); 634 635 foreach ($db_mappings as $db_mapping) { 636 $valuemapid = $db_mapping['valuemapid']; 637 unset($db_mapping['valuemap_mappingid'], $db_mapping['valuemapid'], $db_mapping['sortorder']); 638 639 $db_valuemaps[$valuemapid]['mappings'][] = $db_mapping; 640 } 641 } 642 } 643 644 return $db_valuemaps; 645 } 646} 647