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 22require_once dirname(__FILE__).'/include/config.inc.php'; 23require_once dirname(__FILE__).'/include/hosts.inc.php'; 24require_once dirname(__FILE__).'/include/screens.inc.php'; 25require_once dirname(__FILE__).'/include/forms.inc.php'; 26require_once dirname(__FILE__).'/include/ident.inc.php'; 27 28$page['type'] = detect_page_type(PAGE_TYPE_HTML); 29$page['title'] = _('Configuration of templates'); 30$page['file'] = 'templates.php'; 31$page['scripts'] = ['multiselect.js', 'textareaflexible.js', 'inputsecret.js', 'macrovalue.js']; 32 33require_once dirname(__FILE__).'/include/page_header.php'; 34 35// VAR TYPE OPTIONAL FLAGS VALIDATION EXCEPTION 36$fields = [ 37 'groups' => [T_ZBX_STR, O_OPT, null, NOT_EMPTY, 'isset({add}) || isset({update})'], 38 'mass_update_groups' => [T_ZBX_INT, O_OPT, null, IN([ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]), 39 null 40 ], 41 'clear_templates' => [T_ZBX_INT, O_OPT, P_SYS, DB_ID, null], 42 'templates' => [T_ZBX_INT, O_OPT, null, DB_ID, null], 43 'linked_templates' => [T_ZBX_INT, O_OPT, null, DB_ID, null], 44 'add_templates' => [T_ZBX_INT, O_OPT, null, DB_ID, null], 45 'templateid' => [T_ZBX_INT, O_OPT, P_SYS, DB_ID, 'isset({form}) && {form} == "update"'], 46 'template_name' => [T_ZBX_STR, O_OPT, null, NOT_EMPTY, 'isset({add}) || isset({update})', _('Template name')], 47 'visiblename' => [T_ZBX_STR, O_OPT, null, null, 'isset({add}) || isset({update})'], 48 'groupids' => [T_ZBX_INT, O_OPT, null, DB_ID, null], 49 'tags' => [T_ZBX_STR, O_OPT, null, null, null], 50 'mass_update_tags' => [T_ZBX_INT, O_OPT, null, IN([ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]), 51 null 52 ], 53 'description' => [T_ZBX_STR, O_OPT, null, null, null], 54 'macros' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 55 'mass_update_macros' => [T_ZBX_INT, O_OPT, null, 56 IN([ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE, ZBX_ACTION_REMOVE_ALL]), 57 null 58 ], 59 'macros_add' => [T_ZBX_INT, O_OPT, null, IN([0,1]), null], 60 'macros_update' => [T_ZBX_INT, O_OPT, null, IN([0,1]), null], 61 'macros_remove' => [T_ZBX_INT, O_OPT, null, IN([0,1]), null], 62 'macros_remove_all' => [T_ZBX_INT, O_OPT, null, IN([0,1]), null], 63 'visible' => [T_ZBX_STR, O_OPT, null, null, null], 64 'mass_action_tpls' => [T_ZBX_INT, O_OPT, null, IN([ZBX_ACTION_ADD, ZBX_ACTION_REPLACE, ZBX_ACTION_REMOVE]), null ], 65 'mass_clear_tpls' => [T_ZBX_STR, O_OPT, null, null, null], 66 'show_inherited_macros' => [T_ZBX_INT, O_OPT, null, IN([0,1]), null], 67 // actions 68 'action' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, 69 IN('"template.export","template.massupdate","template.massupdateform",'. 70 '"template.massdelete","template.massdeleteclear"' 71 ), 72 null 73 ], 74 'unlink' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 75 'unlink_and_clear' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 76 'add' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 77 'update' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 78 'masssave' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 79 'clone' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 80 'full_clone' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 81 'delete' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 82 'delete_and_clear' => [T_ZBX_STR, O_OPT, P_SYS|P_ACT, null, null], 83 'cancel' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 84 'form' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 85 'form_refresh' => [T_ZBX_INT, O_OPT, null, null, null], 86 // filter 87 'filter_set' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 88 'filter_rst' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 89 'filter_name' => [T_ZBX_STR, O_OPT, null, null, null], 90 'filter_templates' => [T_ZBX_INT, O_OPT, null, DB_ID, null], 91 'filter_groups' => [T_ZBX_INT, O_OPT, null, DB_ID, null], 92 'filter_evaltype' => [T_ZBX_INT, O_OPT, null, 93 IN([TAG_EVAL_TYPE_AND_OR, TAG_EVAL_TYPE_OR]), 94 null 95 ], 96 'filter_tags' => [T_ZBX_STR, O_OPT, null, null, null], 97 // sort and sortorder 98 'sort' => [T_ZBX_STR, O_OPT, P_SYS, IN('"name"'), null], 99 'sortorder' => [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'), null] 100]; 101check_fields($fields); 102 103/* 104 * Permissions 105 */ 106if (getRequest('templateid')) { 107 $templates = API::Template()->get([ 108 'output' => [], 109 'templateids' => getRequest('templateid'), 110 'editable' => true 111 ]); 112 113 if (!$templates) { 114 access_deny(); 115 } 116} 117 118$tags = getRequest('tags', []); 119foreach ($tags as $key => $tag) { 120 // remove empty new tag lines 121 if ($tag['tag'] === '' && $tag['value'] === '') { 122 unset($tags[$key]); 123 continue; 124 } 125 126 // remove inherited tags 127 if (array_key_exists('type', $tag) && !($tag['type'] & ZBX_PROPERTY_OWN)) { 128 unset($tags[$key]); 129 } 130 else { 131 unset($tags[$key]['type']); 132 } 133} 134 135// Remove inherited macros data (actions: 'add', 'update' and 'form'). 136$macros = cleanInheritedMacros(getRequest('macros', [])); 137 138// Remove empty new macro lines. 139$macros = array_filter($macros, function($macro) { 140 $keys = array_flip(['hostmacroid', 'macro', 'value', 'description']); 141 142 return (bool) array_filter(array_intersect_key($macro, $keys)); 143}); 144 145/* 146 * Actions 147 */ 148if (hasRequest('unlink') || hasRequest('unlink_and_clear')) { 149 $unlinkTemplates = []; 150 151 if (hasRequest('unlink') && is_array(getRequest('unlink'))) { 152 $unlinkTemplates = array_keys(getRequest('unlink')); 153 } 154 elseif (hasRequest('unlink_and_clear') && is_array(getRequest('unlink_and_clear'))) { 155 $unlinkTemplates = array_keys(getRequest('unlink_and_clear')); 156 $_REQUEST['clear_templates'] = array_merge($unlinkTemplates, getRequest('clear_templates', [])); 157 } 158 159 foreach ($unlinkTemplates as $id) { 160 unset($_REQUEST['templates'][array_search($id, $_REQUEST['templates'])]); 161 } 162} 163elseif (hasRequest('templateid') && (hasRequest('clone') || hasRequest('full_clone'))) { 164 $_REQUEST['form'] = hasRequest('clone') ? 'clone' : 'full_clone'; 165 166 $groups = getRequest('groups', []); 167 $groupids = []; 168 169 // Remove inaccessible groups from request, but leave "new". 170 foreach ($groups as $group) { 171 if (!is_array($group)) { 172 $groupids[] = $group; 173 } 174 } 175 176 if ($groupids) { 177 $groups_allowed = API::HostGroup()->get([ 178 'output' => [], 179 'groupids' => $groupids, 180 'editable' => true, 181 'preservekeys' => true 182 ]); 183 184 foreach ($groups as $idx => $group) { 185 if (!is_array($group) && !array_key_exists($group, $groups_allowed)) { 186 unset($groups[$idx]); 187 } 188 } 189 190 $_REQUEST['groups'] = $groups; 191 } 192 193 if ($macros && in_array(ZBX_MACRO_TYPE_SECRET, array_column($macros, 'type'))) { 194 // Reset macro type and value. 195 $macros = array_map(function($value) { 196 return ($value['type'] == ZBX_MACRO_TYPE_SECRET) 197 ? ['value' => '', 'type' => ZBX_MACRO_TYPE_TEXT] + $value 198 : $value; 199 }, $macros); 200 201 warning(_('The cloned template contains user defined macros with type "Secret text". The value and type of these macros were reset.')); 202 } 203 204 if (hasRequest('clone')) { 205 unset($_REQUEST['templateid']); 206 } 207} 208elseif (hasRequest('action') && getRequest('action') === 'template.massupdate' && hasRequest('masssave')) { 209 $templateids = getRequest('templates', []); 210 $visible = getRequest('visible', []); 211 212 try { 213 DBstart(); 214 215 $options = [ 216 'output' => ['templateid'], 217 'templateids' => $templateids 218 ]; 219 220 if (array_key_exists('groups', $visible)) { 221 $options['selectGroups'] = ['groupid']; 222 } 223 224 if (array_key_exists('linked_templates', $visible) 225 && !(getRequest('mass_action_tpls') == ZBX_ACTION_REPLACE && !hasRequest('mass_clear_tpls'))) { 226 $options['selectParentTemplates'] = ['templateid']; 227 } 228 229 if (array_key_exists('tags', $visible)) { 230 $mass_update_tags = getRequest('mass_update_tags', ZBX_ACTION_ADD); 231 232 if ($mass_update_tags == ZBX_ACTION_ADD || $mass_update_tags == ZBX_ACTION_REMOVE) { 233 $options['selectTags'] = ['tag', 'value']; 234 } 235 236 $unique_tags = []; 237 238 foreach ($tags as $tag) { 239 $unique_tags[$tag['tag'].':'.$tag['value']] = $tag; 240 } 241 242 $tags = array_values($unique_tags); 243 } 244 245 if (array_key_exists('macros', $visible)) { 246 $mass_update_macros = getRequest('mass_update_macros', ZBX_ACTION_ADD); 247 248 if ($mass_update_macros == ZBX_ACTION_ADD || $mass_update_macros == ZBX_ACTION_REPLACE 249 || $mass_update_macros == ZBX_ACTION_REMOVE) { 250 $options['selectMacros'] = ['hostmacroid', 'macro']; 251 } 252 } 253 254 $templates = API::Template()->get($options); 255 256 if (array_key_exists('groups', $visible)) { 257 $new_groupids = []; 258 $remove_groupids = []; 259 $mass_update_groups = getRequest('mass_update_groups', ZBX_ACTION_ADD); 260 261 if ($mass_update_groups == ZBX_ACTION_ADD || $mass_update_groups == ZBX_ACTION_REPLACE) { 262 if (CWebUser::getType() == USER_TYPE_SUPER_ADMIN) { 263 $ins_groups = []; 264 265 foreach (getRequest('groups', []) as $new_group) { 266 if (is_array($new_group) && array_key_exists('new', $new_group)) { 267 $ins_groups[] = ['name' => $new_group['new']]; 268 } 269 else { 270 $new_groupids[] = $new_group; 271 } 272 } 273 274 if ($ins_groups) { 275 if (!$result = API::HostGroup()->create($ins_groups)) { 276 throw new Exception(); 277 } 278 279 $new_groupids = array_merge($new_groupids, $result['groupids']); 280 } 281 } 282 else { 283 $new_groupids = getRequest('groups', []); 284 } 285 } 286 elseif ($mass_update_groups == ZBX_ACTION_REMOVE) { 287 $remove_groupids = getRequest('groups', []); 288 } 289 } 290 291 $new_values = []; 292 293 if (array_key_exists('description', $visible)) { 294 $new_values['description'] = getRequest('description'); 295 } 296 297 $template_macros_add = []; 298 $template_macros_update = []; 299 $template_macros_remove = []; 300 foreach ($templates as &$template) { 301 if (array_key_exists('groups', $visible)) { 302 if ($new_groupids && $mass_update_groups == ZBX_ACTION_ADD) { 303 $current_groupids = zbx_objectValues($template['groups'], 'groupid'); 304 $template['groups'] = zbx_toObject(array_unique(array_merge($current_groupids, $new_groupids)), 305 'groupid' 306 ); 307 } 308 elseif ($new_groupids && $mass_update_groups == ZBX_ACTION_REPLACE) { 309 $template['groups'] = zbx_toObject($new_groupids, 'groupid'); 310 } 311 elseif ($remove_groupids) { 312 $current_groupids = zbx_objectValues($template['groups'], 'groupid'); 313 $template['groups'] = zbx_toObject(array_diff($current_groupids, $remove_groupids), 'groupid'); 314 } 315 } 316 317 if (array_key_exists('linked_templates', $visible)) { 318 $parent_templateids = array_key_exists('parentTemplates', $template) 319 ? zbx_objectValues($template['parentTemplates'], 'templateid') 320 : []; 321 322 switch (getRequest('mass_action_tpls')) { 323 case ZBX_ACTION_ADD: 324 $template['templates'] = array_unique( 325 array_merge($parent_templateids, getRequest('linked_templates', [])) 326 ); 327 break; 328 329 case ZBX_ACTION_REPLACE: 330 $template['templates'] = getRequest('linked_templates', []); 331 if (getRequest('mass_clear_tpls')) { 332 $template['templates_clear'] = array_unique( 333 array_diff($parent_templateids, getRequest('linked_templates', [])) 334 ); 335 } 336 break; 337 338 case ZBX_ACTION_REMOVE: 339 $template['templates'] = array_unique( 340 array_diff($parent_templateids, getRequest('linked_templates', [])) 341 ); 342 if (getRequest('mass_clear_tpls')) { 343 $template['templates_clear'] = array_unique(getRequest('linked_templates', [])); 344 } 345 break; 346 } 347 } 348 349 if (array_key_exists('tags', $visible)) { 350 if ($tags && $mass_update_tags == ZBX_ACTION_ADD) { 351 $unique_tags = []; 352 353 foreach (array_merge($template['tags'], $tags) as $tag) { 354 $unique_tags[$tag['tag'].':'.$tag['value']] = $tag; 355 } 356 357 $template['tags'] = array_values($unique_tags); 358 } 359 elseif ($mass_update_tags == ZBX_ACTION_REPLACE) { 360 $template['tags'] = $tags; 361 } 362 elseif ($tags && $mass_update_tags == ZBX_ACTION_REMOVE) { 363 $diff_tags = []; 364 365 foreach ($template['tags'] as $a) { 366 foreach ($tags as $b) { 367 if ($a['tag'] === $b['tag'] && $a['value'] === $b['value']) { 368 continue 2; 369 } 370 } 371 372 $diff_tags[] = $a; 373 } 374 375 $template['tags'] = $diff_tags; 376 } 377 } 378 379 if (array_key_exists('macros', $visible)) { 380 switch ($mass_update_macros) { 381 case ZBX_ACTION_ADD: 382 if ($macros) { 383 $update_existing = getRequest('macros_add', 0); 384 385 foreach ($macros as $macro) { 386 foreach ($template['macros'] as $template_macro) { 387 if ($macro['macro'] === $template_macro['macro']) { 388 if ($update_existing) { 389 $macro['hostmacroid'] = $template_macro['hostmacroid']; 390 $template_macros_update[] = $macro; 391 } 392 393 continue 2; 394 } 395 } 396 397 $macro['hostid'] = $template['templateid']; 398 $template_macros_add[] = $macro; 399 } 400 } 401 break; 402 403 case ZBX_ACTION_REPLACE: // In Macros its update. 404 if ($macros) { 405 $add_missing = getRequest('macros_update', 0); 406 407 foreach ($macros as $macro) { 408 foreach ($template['macros'] as $template_macro) { 409 if ($macro['macro'] === $template_macro['macro']) { 410 $macro['hostmacroid'] = $template_macro['hostmacroid']; 411 $template_macros_update[] = $macro; 412 413 continue 2; 414 } 415 } 416 417 if ($add_missing) { 418 $macro['hostid'] = $template['templateid']; 419 $template_macros_add[] = $macro; 420 } 421 } 422 } 423 break; 424 425 case ZBX_ACTION_REMOVE: 426 if ($macros) { 427 $except_selected = getRequest('macros_remove', 0); 428 429 $macro_names = array_column($macros, 'macro'); 430 431 foreach ($template['macros'] as $template_macro) { 432 if ((!$except_selected && in_array($template_macro['macro'], $macro_names)) 433 || ($except_selected && !in_array($template_macro['macro'], $macro_names))) { 434 $template_macros_remove[] = $template_macro['hostmacroid']; 435 } 436 } 437 } 438 break; 439 440 case ZBX_ACTION_REMOVE_ALL: 441 if (!getRequest('macros_remove_all', 0)) { 442 throw new Exception(); 443 } 444 445 $template['macros'] = []; 446 break; 447 } 448 449 if ($mass_update_macros != ZBX_ACTION_REMOVE_ALL) { 450 unset($template['macros']); 451 } 452 } 453 454 unset($template['parentTemplates']); 455 456 $template = $new_values + $template; 457 } 458 unset($template); 459 460 if (!API::Template()->update($templates)) { 461 throw new Exception(); 462 } 463 464 /** 465 * Macros must be updated separately, since calling API::UserMacro->replaceMacros() inside 466 * API::Template->update() results in loss of secret macro values. 467 */ 468 if ($template_macros_remove) { 469 if (!API::UserMacro()->delete($template_macros_remove)) { 470 throw new Exception(); 471 } 472 } 473 474 if ($template_macros_add) { 475 if (!API::UserMacro()->create($template_macros_add)) { 476 throw new Exception(); 477 } 478 } 479 480 if ($template_macros_update) { 481 if (!API::UserMacro()->update($template_macros_update)) { 482 throw new Exception(); 483 } 484 } 485 486 DBend(true); 487 488 uncheckTableRows(); 489 show_message(_('Templates updated')); 490 491 unset($_REQUEST['masssave'], $_REQUEST['form'], $_REQUEST['templates']); 492 } 493 catch (Exception $e) { 494 DBend(false); 495 show_error_message(_('Cannot update templates')); 496 } 497} 498elseif (hasRequest('add') || hasRequest('update')) { 499 try { 500 DBstart(); 501 502 $templateId = getRequest('templateid', 0); 503 $cloneTemplateId = 0; 504 505 if (getRequest('form') === 'full_clone') { 506 $cloneTemplateId = $templateId; 507 $templateId = 0; 508 } 509 510 if ($templateId == 0) { 511 $messageSuccess = _('Template added'); 512 $messageFailed = _('Cannot add template'); 513 } 514 else { 515 $messageSuccess = _('Template updated'); 516 $messageFailed = _('Cannot update template'); 517 } 518 519 // Add new group. 520 $groups = getRequest('groups', []); 521 $new_groups = []; 522 523 foreach ($groups as $idx => $group) { 524 if (is_array($group) && array_key_exists('new', $group)) { 525 $new_groups[] = ['name' => $group['new']]; 526 unset($groups[$idx]); 527 } 528 } 529 530 if ($new_groups) { 531 $new_groupid = API::HostGroup()->create($new_groups); 532 533 if (!$new_groupid) { 534 throw new Exception(); 535 } 536 537 $groups = array_merge($groups, $new_groupid['groupids']); 538 } 539 540 // Linked templates. 541 $templates = []; 542 543 foreach (array_merge(getRequest('templates', []), getRequest('add_templates', [])) as $templateid) { 544 $templates[] = ['templateid' => $templateid]; 545 } 546 547 $templatesClear = getRequest('clear_templates', []); 548 $templatesClear = zbx_toObject($templatesClear, 'templateid'); 549 $templateName = getRequest('template_name', ''); 550 551 // create / update template 552 $template = [ 553 'host' => $templateName, 554 'name' => getRequest('visiblename', ''), 555 'groups' => zbx_toObject($groups, 'groupid'), 556 'templates' => $templates, 557 'macros' => $macros, 558 'tags' => $tags, 559 'description' => getRequest('description', '') 560 ]; 561 562 if ($templateId == 0) { 563 $result = API::Template()->create($template); 564 565 if ($result) { 566 $templateId = reset($result['templateids']); 567 } 568 else { 569 throw new Exception(); 570 } 571 } 572 else { 573 $template['templateid'] = $templateId; 574 $template['templates_clear'] = $templatesClear; 575 576 $result = API::Template()->update($template); 577 578 if ($result) { 579 add_audit_ext(AUDIT_ACTION_UPDATE, AUDIT_RESOURCE_TEMPLATE, $templateId, $templateName, 'hosts', null, null); 580 } 581 else { 582 throw new Exception(); 583 } 584 } 585 586 // full clone 587 if ($cloneTemplateId != 0 && getRequest('form') === 'full_clone') { 588 if (!copyApplications($cloneTemplateId, $templateId)) { 589 throw new Exception(); 590 } 591 592 /* 593 * First copy web scenarios with web items, so that later regular items can use web item as their master 594 * item. 595 */ 596 if (!copyHttpTests($cloneTemplateId, $templateId)) { 597 throw new Exception(); 598 } 599 600 if (!copyItems($cloneTemplateId, $templateId)) { 601 throw new Exception(); 602 } 603 604 // copy triggers 605 $dbTriggers = API::Trigger()->get([ 606 'output' => ['triggerid'], 607 'hostids' => $cloneTemplateId, 608 'inherited' => false 609 ]); 610 611 if ($dbTriggers) { 612 $result &= copyTriggersToHosts(zbx_objectValues($dbTriggers, 'triggerid'), 613 $templateId, $cloneTemplateId); 614 615 if (!$result) { 616 throw new Exception(); 617 } 618 } 619 620 // copy graphs 621 $dbGraphs = API::Graph()->get([ 622 'output' => ['graphid'], 623 'hostids' => $cloneTemplateId, 624 'inherited' => false 625 ]); 626 627 foreach ($dbGraphs as $dbGraph) { 628 copyGraphToHost($dbGraph['graphid'], $templateId); 629 } 630 631 // copy discovery rules 632 $dbDiscoveryRules = API::DiscoveryRule()->get([ 633 'output' => ['itemid'], 634 'hostids' => $cloneTemplateId, 635 'inherited' => false 636 ]); 637 638 if ($dbDiscoveryRules) { 639 $result &= API::DiscoveryRule()->copy([ 640 'discoveryids' => zbx_objectValues($dbDiscoveryRules, 'itemid'), 641 'hostids' => [$templateId] 642 ]); 643 644 if (!$result) { 645 throw new Exception(); 646 } 647 } 648 649 // copy template screens 650 $dbTemplateScreens = API::TemplateScreen()->get([ 651 'noInheritance' => true, 652 'output' => ['screenid'], 653 'templateids' => $cloneTemplateId, 654 'preservekeys' => true 655 ]); 656 657 if ($dbTemplateScreens) { 658 $result &= API::TemplateScreen()->copy([ 659 'screenIds' => zbx_objectValues($dbTemplateScreens, 'screenid'), 660 'templateIds' => $templateId 661 ]); 662 663 if (!$result) { 664 throw new Exception(); 665 } 666 } 667 } 668 669 unset($_REQUEST['form'], $_REQUEST['templateid']); 670 $result = DBend($result); 671 672 if ($result) { 673 uncheckTableRows(); 674 } 675 show_messages($result, $messageSuccess, $messageFailed); 676 } 677 catch (Exception $e) { 678 DBend(false); 679 show_error_message($messageFailed); 680 } 681} 682elseif (hasRequest('templateid') && hasRequest('delete')) { 683 DBstart(); 684 685 $result = API::Template()->massUpdate([ 686 'templates' => zbx_toObject($_REQUEST['templateid'], 'templateid'), 687 'hosts' => [] 688 ]); 689 if ($result) { 690 $result = API::Template()->delete([getRequest('templateid')]); 691 } 692 693 $result = DBend($result); 694 695 if ($result) { 696 unset($_REQUEST['form'], $_REQUEST['templateid']); 697 uncheckTableRows(); 698 } 699 unset($_REQUEST['delete']); 700 show_messages($result, _('Template deleted'), _('Cannot delete template')); 701} 702elseif (hasRequest('templateid') && hasRequest('delete_and_clear')) { 703 DBstart(); 704 705 $result = API::Template()->delete([getRequest('templateid')]); 706 707 $result = DBend($result); 708 709 if ($result) { 710 unset($_REQUEST['form'], $_REQUEST['templateid']); 711 uncheckTableRows(); 712 } 713 unset($_REQUEST['delete']); 714 show_messages($result, _('Template deleted'), _('Cannot delete template')); 715} 716elseif (hasRequest('templates') && hasRequest('action') && str_in_array(getRequest('action'), ['template.massdelete', 'template.massdeleteclear'])) { 717 $templates = getRequest('templates'); 718 719 DBstart(); 720 721 $result = true; 722 723 if (getRequest('action') === 'template.massdelete') { 724 $result = API::Template()->massUpdate([ 725 'templates' => zbx_toObject($templates, 'templateid'), 726 'hosts' => [] 727 ]); 728 } 729 730 if ($result) { 731 $result = API::Template()->delete($templates); 732 } 733 734 $result = DBend($result); 735 736 if ($result) { 737 uncheckTableRows(); 738 } 739 else { 740 $templateids = API::Template()->get([ 741 'output' => [], 742 'templateids' => $templates, 743 'editable' => true 744 ]); 745 uncheckTableRows(null, zbx_objectValues($templateids, 'templateid')); 746 } 747 show_messages($result, _('Template deleted'), _('Cannot delete template')); 748} 749 750/* 751 * Display 752 */ 753if (hasRequest('templates') && (getRequest('action') === 'template.massupdateform' || hasRequest('masssave'))) { 754 $data = [ 755 'templates' => getRequest('templates', []), 756 'visible' => getRequest('visible', []), 757 'mass_action_tpls' => getRequest('mass_action_tpls'), 758 'mass_clear_tpls' => getRequest('mass_clear_tpls'), 759 'groups' => getRequest('groups', []), 760 'mass_update_groups' => getRequest('mass_update_groups', ZBX_ACTION_ADD), 761 'tags' => $tags, 762 'mass_update_tags' => getRequest('mass_update_tags', ZBX_ACTION_ADD), 763 'macros' => $macros, 764 'macros_checkbox' => [ 765 ZBX_ACTION_ADD => getRequest('macros_add', '0'), 766 ZBX_ACTION_REPLACE => getRequest('macros_update', '0'), 767 ZBX_ACTION_REMOVE => getRequest('macros_remove', '0'), 768 ZBX_ACTION_REMOVE_ALL => getRequest('macros_remove_all', '0') 769 ], 770 'macros_visible' => getRequest('mass_update_macros', ZBX_ACTION_ADD), 771 'description' => getRequest('description'), 772 'linked_templates' => getRequest('linked_templates', []) 773 ]; 774 775 // sort templates 776 natsort($data['linked_templates']); 777 778 if (!$data['tags']) { 779 $data['tags'][] = ['tag' => '', 'value' => '']; 780 } 781 782 // get templates data 783 $data['linked_templates'] = $data['linked_templates'] 784 ? CArrayHelper::renameObjectsKeys(API::Template()->get([ 785 'output' => ['templateid', 'name'], 786 'templateids' => $data['linked_templates'] 787 ]), ['templateid' => 'id']) 788 : []; 789 790 $data['groups'] = $data['groups'] 791 ? CArrayHelper::renameObjectsKeys(API::HostGroup()->get([ 792 'output' => ['groupid', 'name'], 793 'groupids' => $data['groups'], 794 'editable' => true 795 ]), ['groupid' => 'id']) 796 : []; 797 798 if (!$data['macros']) { 799 $data['macros'] = [['macro' => '', 'type' => ZBX_MACRO_TYPE_TEXT, 'value' => '', 'description' => '']]; 800 } 801 802 $view = new CView('configuration.template.massupdate', $data); 803} 804elseif (hasRequest('form')) { 805 $data = [ 806 'form' => getRequest('form'), 807 'templateid' => getRequest('templateid', 0), 808 'linked_templates' => [], 809 'add_templates' => [], 810 'original_templates' => [], 811 'tags' => $tags, 812 'show_inherited_macros' => getRequest('show_inherited_macros', 0), 813 'readonly' => false, 814 'macros' => $macros 815 ]; 816 817 if ($data['templateid'] != 0) { 818 $dbTemplates = API::Template()->get([ 819 'output' => API_OUTPUT_EXTEND, 820 'selectGroups' => API_OUTPUT_EXTEND, 821 'selectParentTemplates' => ['templateid', 'name'], 822 'selectMacros' => API_OUTPUT_EXTEND, 823 'selectTags' => ['tag', 'value'], 824 'templateids' => $data['templateid'] 825 ]); 826 $data['dbTemplate'] = reset($dbTemplates); 827 828 foreach ($data['dbTemplate']['parentTemplates'] as $parentTemplate) { 829 $data['original_templates'][$parentTemplate['templateid']] = $parentTemplate['templateid']; 830 } 831 832 if (!hasRequest('form_refresh')) { 833 $data['tags'] = $data['dbTemplate']['tags']; 834 $data['macros'] = $data['dbTemplate']['macros']; 835 } 836 } 837 838 // description 839 $data['description'] = ($data['templateid'] != 0 && !hasRequest('form_refresh')) 840 ? $data['dbTemplate']['description'] 841 : getRequest('description', ''); 842 843 // tags 844 if (!$data['tags']) { 845 $data['tags'][] = ['tag' => '', 'value' => '']; 846 } 847 else { 848 CArrayHelper::sort($data['tags'], ['tag', 'value']); 849 } 850 851 // Add already linked and new templates. 852 $templates = []; 853 $request_linked_templates = getRequest('templates', hasRequest('form_refresh') ? [] : $data['original_templates']); 854 $request_add_templates = getRequest('add_templates', []); 855 856 if ($request_linked_templates || $request_add_templates) { 857 $templates = API::Template()->get([ 858 'output' => ['templateid', 'name'], 859 'templateids' => array_merge($request_linked_templates, $request_add_templates), 860 'preservekeys' => true 861 ]); 862 863 $data['linked_templates'] = array_intersect_key($templates, array_flip($request_linked_templates)); 864 CArrayHelper::sort($data['linked_templates'], ['name']); 865 866 $data['add_templates'] = array_intersect_key($templates, array_flip($request_add_templates)); 867 868 foreach ($data['add_templates'] as &$template) { 869 $template = CArrayHelper::renameKeys($template, ['templateid' => 'id']); 870 } 871 unset($template); 872 } 873 874 $data['writable_templates'] = API::Template()->get([ 875 'output' => ['templateid'], 876 'templateids' => array_keys($data['linked_templates']), 877 'editable' => true, 878 'preservekeys' => true 879 ]); 880 881 // Add inherited macros to template macros. 882 if ($data['show_inherited_macros']) { 883 $data['macros'] = mergeInheritedMacros($data['macros'], getInheritedMacros(array_keys($templates))); 884 } 885 886 // Sort only after inherited macros are added. Otherwise the list will look chaotic. 887 $data['macros'] = array_values(order_macros($data['macros'], 'macro')); 888 889 // The empty inputs will not be shown if there are inherited macros, for example. 890 if (!$data['macros']) { 891 $macro = ['macro' => '', 'value' => '', 'description' => '', 'type' => ZBX_MACRO_TYPE_TEXT]; 892 893 if ($data['show_inherited_macros']) { 894 $macro['inherited_type'] = ZBX_PROPERTY_OWN; 895 } 896 897 $data['macros'][] = $macro; 898 } 899 900 if (!hasRequest('form_refresh')) { 901 if ($data['templateid'] != 0) { 902 $groups = zbx_objectValues($data['dbTemplate']['groups'], 'groupid'); 903 } 904 else { 905 $groups = getRequest('groupids', []); 906 } 907 } 908 else { 909 $groups = getRequest('groups', []); 910 } 911 912 $groupids = []; 913 914 foreach ($groups as $group) { 915 if (is_array($group) && array_key_exists('new', $group)) { 916 continue; 917 } 918 919 $groupids[] = $group; 920 } 921 922 // Groups with R and RW permissions. 923 $groups_all = $groupids 924 ? API::HostGroup()->get([ 925 'output' => ['name'], 926 'groupids' => $groupids, 927 'preservekeys' => true 928 ]) 929 : []; 930 931 // Groups with RW permissions. 932 $groups_rw = $groupids && (CWebUser::getType() != USER_TYPE_SUPER_ADMIN) 933 ? API::HostGroup()->get([ 934 'output' => [], 935 'groupids' => $groupids, 936 'editable' => true, 937 'preservekeys' => true 938 ]) 939 : []; 940 941 $data['groups_ms'] = []; 942 943 // Prepare data for multiselect. 944 foreach ($groups as $group) { 945 if (is_array($group) && array_key_exists('new', $group)) { 946 $data['groups_ms'][] = [ 947 'id' => $group['new'], 948 'name' => $group['new'].' ('._x('new', 'new element in multiselect').')', 949 'isNew' => true 950 ]; 951 } 952 elseif (array_key_exists($group, $groups_all)) { 953 $data['groups_ms'][] = [ 954 'id' => $group, 955 'name' => $groups_all[$group]['name'], 956 'disabled' => (CWebUser::getType() != USER_TYPE_SUPER_ADMIN) && !array_key_exists($group, $groups_rw) 957 ]; 958 } 959 } 960 CArrayHelper::sort($data['groups_ms'], ['name']); 961 962 // This data is used in common.template.edit.js.php. 963 $data['macros_tab'] = [ 964 'linked_templates' => array_map('strval', array_keys($data['linked_templates'])), 965 'add_templates' => array_map('strval', array_keys($data['add_templates'])) 966 ]; 967 968 $view = new CView('configuration.template.edit', $data); 969} 970else { 971 $sortField = getRequest('sort', CProfile::get('web.'.$page['file'].'.sort', 'name')); 972 $sortOrder = getRequest('sortorder', CProfile::get('web.'.$page['file'].'.sortorder', ZBX_SORT_UP)); 973 974 CProfile::update('web.'.$page['file'].'.sort', $sortField, PROFILE_TYPE_STR); 975 CProfile::update('web.'.$page['file'].'.sortorder', $sortOrder, PROFILE_TYPE_STR); 976 977 // filter 978 if (hasRequest('filter_set')) { 979 CProfile::update('web.templates.filter_name', getRequest('filter_name', ''), PROFILE_TYPE_STR); 980 CProfile::updateArray('web.templates.filter_templates', getRequest('filter_templates', []), PROFILE_TYPE_ID); 981 CProfile::updateArray('web.templates.filter_groups', getRequest('filter_groups', []), PROFILE_TYPE_ID); 982 CProfile::update('web.templates.filter.evaltype', getRequest('filter_evaltype', TAG_EVAL_TYPE_AND_OR), 983 PROFILE_TYPE_INT 984 ); 985 986 $filter_tags = ['tags' => [], 'values' => [], 'operators' => []]; 987 foreach (getRequest('filter_tags', []) as $filter_tag) { 988 if ($filter_tag['tag'] === '' && $filter_tag['value'] === '') { 989 continue; 990 } 991 992 $filter_tags['tags'][] = $filter_tag['tag']; 993 $filter_tags['values'][] = $filter_tag['value']; 994 $filter_tags['operators'][] = $filter_tag['operator']; 995 } 996 CProfile::updateArray('web.templates.filter.tags.tag', $filter_tags['tags'], PROFILE_TYPE_STR); 997 CProfile::updateArray('web.templates.filter.tags.value', $filter_tags['values'], PROFILE_TYPE_STR); 998 CProfile::updateArray('web.templates.filter.tags.operator', $filter_tags['operators'], PROFILE_TYPE_INT); 999 } 1000 elseif (hasRequest('filter_rst')) { 1001 CProfile::delete('web.templates.filter_name'); 1002 CProfile::deleteIdx('web.templates.filter_templates'); 1003 CProfile::deleteIdx('web.templates.filter_groups'); 1004 CProfile::delete('web.templates.filter.evaltype'); 1005 CProfile::deleteIdx('web.templates.filter.tags.tag'); 1006 CProfile::deleteIdx('web.templates.filter.tags.value'); 1007 CProfile::deleteIdx('web.templates.filter.tags.operator'); 1008 } 1009 1010 $filter = [ 1011 'name' => CProfile::get('web.templates.filter_name', ''), 1012 'templates' => CProfile::getArray('web.templates.filter_templates', null), 1013 'groups' => CProfile::getArray('web.templates.filter_groups', null), 1014 'evaltype' => CProfile::get('web.templates.filter.evaltype', TAG_EVAL_TYPE_AND_OR), 1015 'tags' => [] 1016 ]; 1017 1018 foreach (CProfile::getArray('web.templates.filter.tags.tag', []) as $i => $tag) { 1019 $filter['tags'][] = [ 1020 'tag' => $tag, 1021 'value' => CProfile::get('web.templates.filter.tags.value', null, $i), 1022 'operator' => CProfile::get('web.templates.filter.tags.operator', null, $i) 1023 ]; 1024 } 1025 1026 $config = select_config(); 1027 1028 $filter['templates'] = $filter['templates'] 1029 ? CArrayHelper::renameObjectsKeys(API::Template()->get([ 1030 'output' => ['templateid', 'name'], 1031 'templateids' => $filter['templates'], 1032 'preservekeys' => true 1033 ]), ['templateid' => 'id']) 1034 : []; 1035 1036 // Get host groups. 1037 $filter['groups'] = $filter['groups'] 1038 ? CArrayHelper::renameObjectsKeys(API::HostGroup()->get([ 1039 'output' => ['groupid', 'name'], 1040 'groupids' => $filter['groups'], 1041 'editable' => true, 1042 'preservekeys' => true 1043 ]), ['groupid' => 'id']) 1044 : []; 1045 1046 $filter_groupids = $filter['groups'] ? array_keys($filter['groups']) : null; 1047 if ($filter_groupids) { 1048 $filter_groupids = getSubGroups($filter_groupids); 1049 } 1050 1051 // Select templates. 1052 $templates = API::Template()->get([ 1053 'output' => ['templateid', $sortField], 1054 'evaltype' => $filter['evaltype'], 1055 'tags' => $filter['tags'], 1056 'search' => [ 1057 'name' => ($filter['name'] === '') ? null : $filter['name'] 1058 ], 1059 'parentTemplateids' => $filter['templates'] ? array_keys($filter['templates']) : null, 1060 'groupids' => $filter_groupids, 1061 'editable' => true, 1062 'sortfield' => $sortField, 1063 'limit' => $config['search_limit'] + 1 1064 ]); 1065 1066 order_result($templates, $sortField, $sortOrder); 1067 1068 // pager 1069 if (hasRequest('page')) { 1070 $page_num = getRequest('page'); 1071 } 1072 elseif (isRequestMethod('get') && !hasRequest('cancel')) { 1073 $page_num = 1; 1074 } 1075 else { 1076 $page_num = CPagerHelper::loadPage($page['file']); 1077 } 1078 1079 CPagerHelper::savePage($page['file'], $page_num); 1080 1081 $paging = CPagerHelper::paginate($page_num, $templates, $sortOrder, new CUrl('templates.php')); 1082 1083 $templates = API::Template()->get([ 1084 'output' => ['templateid', 'name'], 1085 'selectHosts' => ['hostid'], 1086 'selectTemplates' => ['templateid', 'name'], 1087 'selectParentTemplates' => ['templateid', 'name'], 1088 'selectItems' => API_OUTPUT_COUNT, 1089 'selectTriggers' => API_OUTPUT_COUNT, 1090 'selectGraphs' => API_OUTPUT_COUNT, 1091 'selectApplications' => API_OUTPUT_COUNT, 1092 'selectDiscoveries' => API_OUTPUT_COUNT, 1093 'selectScreens' => API_OUTPUT_COUNT, 1094 'selectHttpTests' => API_OUTPUT_COUNT, 1095 'selectTags' => ['tag', 'value'], 1096 'templateids' => zbx_objectValues($templates, 'templateid'), 1097 'editable' => true, 1098 'preservekeys' => true 1099 ]); 1100 1101 order_result($templates, $sortField, $sortOrder); 1102 1103 // Select editable templates: 1104 $linked_templateids = []; 1105 $editable_templates = []; 1106 $linked_hostids = []; 1107 $editable_hosts = []; 1108 foreach ($templates as &$template) { 1109 order_result($template['templates'], 'name'); 1110 order_result($template['parentTemplates'], 'name'); 1111 1112 $linked_templateids += array_flip(array_column($template['parentTemplates'], 'templateid')); 1113 $linked_templateids += array_flip(array_column($template['templates'], 'templateid')); 1114 1115 $template['hosts'] = array_flip(array_column($template['hosts'], 'hostid')); 1116 $linked_hostids += $template['hosts']; 1117 } 1118 unset($template); 1119 1120 if ($linked_templateids) { 1121 $editable_templates = API::Template()->get([ 1122 'output' => ['templateid'], 1123 'templateids' => array_keys($linked_templateids), 1124 'editable' => true, 1125 'preservekeys' => true 1126 ]); 1127 } 1128 if ($linked_hostids) { 1129 $editable_hosts = API::Host()->get([ 1130 'output' => ['hostid'], 1131 'hostids' => array_keys($linked_hostids), 1132 'editable' => true, 1133 'preservekeys' => true 1134 ]); 1135 } 1136 1137 $data = [ 1138 'templates' => $templates, 1139 'paging' => $paging, 1140 'page' => $page_num, 1141 'filter' => $filter, 1142 'sortField' => $sortField, 1143 'sortOrder' => $sortOrder, 1144 'config' => [ 1145 'max_in_table' => $config['max_in_table'] 1146 ], 1147 'editable_templates' => $editable_templates, 1148 'editable_hosts' => $editable_hosts, 1149 'profileIdx' => 'web.templates.filter', 1150 'active_tab' => CProfile::get('web.templates.filter.active', 1), 1151 'tags' => makeTags($templates, true, 'templateid', ZBX_TAG_COUNT_DEFAULT, $filter['tags']) 1152 ]; 1153 1154 $view = new CView('configuration.template.list', $data); 1155} 1156 1157echo $view->getOutput(); 1158 1159require_once dirname(__FILE__).'/include/page_footer.php'; 1160