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
28if (hasRequest('action') && getRequest('action') == 'template.export' && hasRequest('templates')) {
29	$exportData = true;
30
31	$page['type'] = detect_page_type(PAGE_TYPE_XML);
32	$page['file'] = 'zbx_export_templates.xml';
33}
34else {
35	$exportData = false;
36
37	$page['type'] = detect_page_type(PAGE_TYPE_HTML);
38	$page['title'] = _('Configuration of templates');
39	$page['file'] = 'templates.php';
40	$page['scripts'] = ['multiselect.js'];
41}
42
43require_once dirname(__FILE__).'/include/page_header.php';
44
45//		VAR						TYPE		OPTIONAL FLAGS			VALIDATION	EXCEPTION
46$fields = [
47	'hosts'				=> [T_ZBX_INT, O_OPT, P_SYS,		DB_ID,	null],
48	'groups'			=> [T_ZBX_INT, O_OPT, P_SYS,		DB_ID,	null],
49	'clear_templates'	=> [T_ZBX_INT, O_OPT, P_SYS,		DB_ID,	null],
50	'templates'			=> [T_ZBX_INT, O_OPT, null,		DB_ID,	null],
51	'add_templates'		=> [T_ZBX_INT, O_OPT, null,		DB_ID,	null],
52	'add_template' 		=> [T_ZBX_STR, O_OPT, null,		null,	null],
53	'templateid'		=> [T_ZBX_INT, O_OPT, P_SYS,		DB_ID,	'isset({form}) && {form} == "update"'],
54	'template_name'		=> [T_ZBX_STR, O_OPT, null,		NOT_EMPTY, 'isset({add}) || isset({update})', _('Template name')],
55	'visiblename'		=> [T_ZBX_STR, O_OPT, null,		null,	'isset({add}) || isset({update})'],
56	'groupid'			=> [T_ZBX_INT, O_OPT, P_SYS,		DB_ID,	null],
57	'twb_groupid'		=> [T_ZBX_INT, O_OPT, P_SYS,		DB_ID,	null],
58	'newgroup'			=> [T_ZBX_STR, O_OPT, null,		null,	null],
59	'description'		=> [T_ZBX_STR, O_OPT, null,		null,	null],
60	'macros'			=> [T_ZBX_STR, O_OPT, P_SYS,		null,	null],
61	'show_inherited_macros' => [T_ZBX_INT, O_OPT, null,	IN([0,1]), null],
62	// actions
63	'action'			=> [T_ZBX_STR, O_OPT, P_SYS|P_ACT,
64								IN('"template.export","template.massdelete","template.massdeleteclear"'),
65								null
66							],
67	'unlink'			=> [T_ZBX_STR, O_OPT, P_SYS|P_ACT,	null,	null],
68	'unlink_and_clear'	=> [T_ZBX_STR, O_OPT, P_SYS|P_ACT,	null,	null],
69	'add'				=> [T_ZBX_STR, O_OPT, P_SYS|P_ACT,	null,	null],
70	'update'			=> [T_ZBX_STR, O_OPT, P_SYS|P_ACT,	null,	null],
71	'clone'				=> [T_ZBX_STR, O_OPT, P_SYS|P_ACT,	null,	null],
72	'full_clone'		=> [T_ZBX_STR, O_OPT, P_SYS|P_ACT,	null,	null],
73	'delete'			=> [T_ZBX_STR, O_OPT, P_SYS|P_ACT,	null,	null],
74	'delete_and_clear'	=> [T_ZBX_STR, O_OPT, P_SYS|P_ACT,	null,	null],
75	'cancel'			=> [T_ZBX_STR, O_OPT, P_SYS,		null,	null],
76	'form'				=> [T_ZBX_STR, O_OPT, P_SYS,		null,	null],
77	'form_refresh'		=> [T_ZBX_INT, O_OPT, null,		null,	null],
78	// sort and sortorder
79	'sort'				=> [T_ZBX_STR, O_OPT, P_SYS, IN('"name"'),									null],
80	'sortorder'			=> [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'),	null]
81];
82check_fields($fields);
83
84/*
85 * Permissions
86 */
87if (getRequest('groupid') && !API::HostGroup()->isWritable([$_REQUEST['groupid']])) {
88	access_deny();
89}
90if (getRequest('templateid') && !API::Template()->isWritable([$_REQUEST['templateid']])) {
91	access_deny();
92}
93
94$templateIds = getRequest('templates', []);
95
96if ($exportData) {
97	$export = new CConfigurationExport(['templates' => $templateIds]);
98	$export->setBuilder(new CConfigurationExportBuilder());
99	$export->setWriter(CExportWriterFactory::getWriter(CExportWriterFactory::XML));
100	$exportData = $export->export();
101
102	if (hasErrorMesssages()) {
103		show_messages();
104	}
105	else {
106		print($exportData);
107	}
108
109	exit;
110}
111
112// remove inherited macros data (actions: 'add', 'update' and 'form')
113if (hasRequest('macros')) {
114	$_REQUEST['macros'] = cleanInheritedMacros($_REQUEST['macros']);
115
116	// remove empty new macro lines
117	foreach ($_REQUEST['macros'] as $idx => $macro) {
118		if (!array_key_exists('hostmacroid', $macro) && $macro['macro'] === '' && $macro['value'] === '') {
119			unset($_REQUEST['macros'][$idx]);
120		}
121	}
122}
123
124/*
125 * Actions
126 */
127if (isset($_REQUEST['add_template']) && isset($_REQUEST['add_templates'])) {
128	$_REQUEST['templates'] = array_merge($templateIds, $_REQUEST['add_templates']);
129}
130if (hasRequest('unlink') || hasRequest('unlink_and_clear')) {
131	$unlinkTemplates = [];
132
133	if (hasRequest('unlink') && is_array(getRequest('unlink'))) {
134		$unlinkTemplates = array_keys(getRequest('unlink'));
135	}
136	elseif (hasRequest('unlink_and_clear') && is_array(getRequest('unlink_and_clear'))) {
137		$unlinkTemplates = array_keys(getRequest('unlink_and_clear'));
138		$_REQUEST['clear_templates'] = array_merge($unlinkTemplates, getRequest('clear_templates', []));
139	}
140
141	foreach ($unlinkTemplates as $id) {
142		unset($_REQUEST['templates'][array_search($id, $_REQUEST['templates'])]);
143	}
144}
145elseif (isset($_REQUEST['clone']) && isset($_REQUEST['templateid'])) {
146	$_REQUEST['form'] = 'clone';
147	unset($_REQUEST['templateid'], $_REQUEST['hosts']);
148}
149elseif (isset($_REQUEST['full_clone']) && isset($_REQUEST['templateid'])) {
150	$_REQUEST['form'] = 'full_clone';
151	$_REQUEST['hosts'] = [];
152}
153elseif (hasRequest('add') || hasRequest('update')) {
154	try {
155		DBstart();
156
157		$templateId = getRequest('templateid', 0);
158		$cloneTemplateId = 0;
159
160		if (getRequest('form') === 'full_clone') {
161			$cloneTemplateId = $templateId;
162			$templateId = 0;
163		}
164
165		if ($templateId == 0) {
166			$messageSuccess = _('Template added');
167			$messageFailed = _('Cannot add template');
168			$auditAction = AUDIT_ACTION_ADD;
169		}
170		else {
171			$messageSuccess = _('Template updated');
172			$messageFailed = _('Cannot update template');
173			$auditAction = AUDIT_ACTION_UPDATE;
174		}
175
176		// groups
177		$groups = getRequest('groups', []);
178		$groups = zbx_toObject($groups, 'groupid');
179
180		// create new group
181		$newGroup = getRequest('newgroup');
182
183		if (!zbx_empty($newGroup)) {
184			$result = API::HostGroup()->create([
185				'name' => $newGroup
186			]);
187
188			if (!$result) {
189				throw new Exception();
190			}
191
192			$newGroup = API::HostGroup()->get([
193				'groupids' => $result['groupids'],
194				'output' => API_OUTPUT_EXTEND
195			]);
196
197			if ($newGroup) {
198				$groups = array_merge($groups, $newGroup);
199			}
200			else {
201				throw new Exception();
202			}
203		}
204
205		// linked templates
206		$linkedTemplates = getRequest('templates', []);
207		$templates = [];
208		foreach ($linkedTemplates as $linkedTemplateId) {
209			$templates[] = ['templateid' => $linkedTemplateId];
210		}
211
212		$templatesClear = getRequest('clear_templates', []);
213		$templatesClear = zbx_toObject($templatesClear, 'templateid');
214
215		// discovered hosts
216		$dbHosts = API::Host()->get([
217			'output' => ['hostid'],
218			'hostids' => getRequest('hosts', []),
219			'templated_hosts' => true,
220			'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL]
221		]);
222
223		$templateName = getRequest('template_name', '');
224
225		// create / update template
226		$template = [
227			'host' => $templateName,
228			'name' => getRequest('visiblename', ''),
229			'groups' => $groups,
230			'templates' => $templates,
231			'hosts' => $dbHosts,
232			'macros' => getRequest('macros', []),
233			'description' => getRequest('description', '')
234		];
235
236		if ($templateId == 0) {
237			$result = API::Template()->create($template);
238
239			if ($result) {
240				$templateId = reset($result['templateids']);
241			}
242			else {
243				throw new Exception();
244			}
245		}
246		else {
247			$template['templateid'] = $templateId;
248			$template['templates_clear'] = $templatesClear;
249
250			$result = API::Template()->update($template);
251
252			if (!$result) {
253				throw new Exception();
254			}
255		}
256
257		// full clone
258		if ($cloneTemplateId != 0 && getRequest('form') === 'full_clone') {
259			if (!copyApplications($cloneTemplateId, $templateId)) {
260				throw new Exception();
261			}
262
263			if (!copyItems($cloneTemplateId, $templateId)) {
264				throw new Exception();
265			}
266
267			// copy web scenarios
268			if (!copyHttpTests($cloneTemplateId, $templateId)) {
269				throw new Exception();
270			}
271
272			// copy triggers
273			$dbTriggers = API::Trigger()->get([
274				'output' => ['triggerid'],
275				'hostids' => $cloneTemplateId,
276				'inherited' => false
277			]);
278
279			if ($dbTriggers) {
280				$result &= copyTriggersToHosts(zbx_objectValues($dbTriggers, 'triggerid'),
281						$templateId, $cloneTemplateId);
282
283				if (!$result) {
284					throw new Exception();
285				}
286			}
287
288			// copy graphs
289			$dbGraphs = API::Graph()->get([
290				'output' => ['graphid'],
291				'hostids' => $cloneTemplateId,
292				'inherited' => false
293			]);
294
295			foreach ($dbGraphs as $dbGraph) {
296				copyGraphToHost($dbGraph['graphid'], $templateId);
297			}
298
299			// copy discovery rules
300			$dbDiscoveryRules = API::DiscoveryRule()->get([
301				'output' => ['itemid'],
302				'hostids' => $cloneTemplateId,
303				'inherited' => false
304			]);
305
306			if ($dbDiscoveryRules) {
307				$result &= API::DiscoveryRule()->copy([
308					'discoveryids' => zbx_objectValues($dbDiscoveryRules, 'itemid'),
309					'hostids' => [$templateId]
310				]);
311
312				if (!$result) {
313					throw new Exception();
314				}
315			}
316
317			// copy template screens
318			$dbTemplateScreens = API::TemplateScreen()->get([
319				'output' => ['screenid'],
320				'templateids' => $cloneTemplateId,
321				'preservekeys' => true,
322				'inherited' => false
323			]);
324
325			if ($dbTemplateScreens) {
326				$result &= API::TemplateScreen()->copy([
327					'screenIds' => zbx_objectValues($dbTemplateScreens, 'screenid'),
328					'templateIds' => $templateId
329				]);
330
331				if (!$result) {
332					throw new Exception();
333				}
334			}
335		}
336
337		if ($result) {
338			add_audit_ext($auditAction, AUDIT_RESOURCE_TEMPLATE, $templateId, $templateName, 'hosts', null, null);
339		}
340
341		unset($_REQUEST['form'], $_REQUEST['templateid']);
342		$result = DBend($result);
343
344		if ($result) {
345			uncheckTableRows();
346		}
347		show_messages($result, $messageSuccess, $messageFailed);
348	}
349	catch (Exception $e) {
350		DBend(false);
351		show_error_message($messageFailed);
352	}
353}
354elseif (isset($_REQUEST['delete']) && isset($_REQUEST['templateid'])) {
355	DBstart();
356
357	$result = API::Template()->massUpdate([
358		'templates' => zbx_toObject($_REQUEST['templateid'], 'templateid'),
359		'hosts' => []
360	]);
361	if ($result) {
362		$result = API::Template()->delete([getRequest('templateid')]);
363	}
364
365	$result = DBend($result);
366
367	if ($result) {
368		unset($_REQUEST['form'], $_REQUEST['templateid']);
369		uncheckTableRows();
370	}
371	unset($_REQUEST['delete']);
372	show_messages($result, _('Template deleted'), _('Cannot delete template'));
373}
374elseif (isset($_REQUEST['delete_and_clear']) && isset($_REQUEST['templateid'])) {
375	DBstart();
376
377	$result = API::Template()->delete([getRequest('templateid')]);
378
379	$result = DBend($result);
380
381	if ($result) {
382		unset($_REQUEST['form'], $_REQUEST['templateid']);
383		uncheckTableRows();
384	}
385	unset($_REQUEST['delete']);
386	show_messages($result, _('Template deleted'), _('Cannot delete template'));
387}
388elseif (hasRequest('action') && str_in_array(getRequest('action'), ['template.massdelete', 'template.massdeleteclear']) && hasRequest('templates')) {
389	$templates = getRequest('templates');
390
391	DBstart();
392
393	$result = true;
394
395	if (getRequest('action') === 'template.massdelete') {
396		$result = API::Template()->massUpdate([
397			'templates' => zbx_toObject($templates, 'templateid'),
398			'hosts' => []
399		]);
400	}
401
402	if ($result) {
403		$result = API::Template()->delete($templates);
404	}
405
406	$result = DBend($result);
407
408	if ($result) {
409		uncheckTableRows();
410	}
411	show_messages($result, _('Template deleted'), _('Cannot delete template'));
412}
413
414/*
415 * Display
416 */
417$templateWidget = (new CWidget())->setTitle(_('Templates'));
418
419$pageFilter = new CPageFilter([
420	'config' => [
421		'individual' => 1
422	],
423	'groups' => [
424		'templated_hosts' => true,
425		'editable' => true
426	],
427	'groupid' => getRequest('groupid')
428]);
429$_REQUEST['groupid'] = $pageFilter->groupid;
430
431if (hasRequest('form')) {
432
433	if ($templateId = getRequest('templateid', 0)) {
434		$templateWidget->addItem(get_header_host_table('', $templateId));
435	}
436
437	$data = [
438		'form' => getRequest('form'),
439		'groupId' => getRequest('groupid', 0),
440		'groupIds' => getRequest('groups', []),
441		'show_inherited_macros' => getRequest('show_inherited_macros', 0)
442	];
443
444	if ($templateId) {
445		$dbTemplates = API::Template()->get([
446			'templateids' => $templateId,
447			'selectGroups' => API_OUTPUT_EXTEND,
448			'selectParentTemplates' => ['templateid', 'name'],
449			'selectMacros' => API_OUTPUT_EXTEND,
450			'output' => API_OUTPUT_EXTEND
451		]);
452		$data['dbTemplate'] = reset($dbTemplates);
453
454		$data['original_templates'] = [];
455		foreach ($data['dbTemplate']['parentTemplates'] as $parentTemplate) {
456			$data['original_templates'][$parentTemplate['templateid']] = $parentTemplate['templateid'];
457		}
458	}
459	else {
460		$data['original_templates'] = [];
461	}
462
463	// description
464	$data['description'] = ($templateId && !hasRequest('form_refresh'))
465		? $data['dbTemplate']['description']
466		: getRequest('description');
467
468	$templateids = getRequest('templates', hasRequest('form_refresh') ? [] : $data['original_templates']);
469
470	// Get linked templates.
471	$data['linkedTemplates'] = API::Template()->get([
472		'output' => ['templateid', 'name'],
473		'templateids' => $templateids,
474		'preservekeys' => true
475	]);
476
477	$data['writable_templates'] = API::Template()->get([
478		'output' => ['templateid'],
479		'templateids' => $templateids,
480		'editable' => true,
481		'preservekeys' => true
482	]);
483
484	CArrayHelper::sort($data['linkedTemplates'], ['name']);
485
486	// Get user allowed host groups and sort them by name.
487	$data['groupsAllowed'] = API::HostGroup()->get([
488		'output' => ['groupid', 'name'],
489		'editable' => true,
490		'preservekeys' => true
491	]);
492	CArrayHelper::sort($data['groupsAllowed'], ['name']);
493
494	// Get other host groups that user has also read permissions and sort by name.
495	$data['groupsAll'] = API::HostGroup()->get([
496		'output' => ['groupid', 'name'],
497		'preservekeys' => true
498	]);
499	CArrayHelper::sort($data['groupsAll'], ['name']);
500
501	// "Other | group" tweenbox selector for hosts and templates
502	$data['twb_groupid'] = getRequest('twb_groupid', 0);
503	if ($data['twb_groupid'] == 0) {
504		$group = reset($data['groupsAllowed']);
505		$data['twb_groupid'] = $group['groupid'];
506	}
507
508	// Get allowed hosts from selected twb_groupid combobox.
509	$data['hostsAllowedToAdd'] = API::Host()->get([
510		'output' => ['hostid', 'name'],
511		'groupids' => $data['twb_groupid'],
512		'templated_hosts' => true,
513		'editable' => true,
514		'preservekeys' => true,
515		'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL]
516	]);
517	CArrayHelper::sort($data['hostsAllowedToAdd'], ['name']);
518
519	if ($templateId != 0 && !hasRequest('form_refresh')) {
520		$data['groupIds'] = zbx_objectValues($data['dbTemplate']['groups'], 'groupid');
521
522		// Get template hosts from DB.
523		$hostIdsLinkedTo = API::Host()->get([
524			'output' => ['hostid'],
525			'templateids' => $templateId,
526			'templated_hosts' => true,
527			'preservekeys' => true
528		]);
529		$hostIdsLinkedTo = array_keys($hostIdsLinkedTo);
530	}
531	else {
532		if (!hasRequest('form_refresh') && $data['groupId'] != 0 && !$data['groupIds']) {
533			$data['groupIds'][] = $data['groupId'];
534		}
535		$hostIdsLinkedTo = getRequest('hosts', []);
536	}
537
538	if ($data['groupIds']) {
539		$data['groupIds'] = array_combine($data['groupIds'], $data['groupIds']);
540	}
541
542	if ($hostIdsLinkedTo) {
543		$hostIdsLinkedTo = array_combine($hostIdsLinkedTo, $hostIdsLinkedTo);
544	}
545
546	// Select allowed selected hosts.
547	$data['hostsAllowed'] = API::Host()->get([
548		'output' => ['hostid', 'name', 'flags'],
549		'hostids' => $hostIdsLinkedTo,
550		'templated_hosts' => true,
551		'editable' => true,
552		'preservekeys' => true,
553		'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL]
554	]);
555	CArrayHelper::sort($data['hostsAllowed'], ['name']);
556
557	// Select selected hosts including read only.
558	$data['hostsAll'] = API::Host()->get([
559		'output' => ['hostid', 'name', 'flags'],
560		'hostids' => $hostIdsLinkedTo,
561		'templated_hosts' => true
562	]);
563	CArrayHelper::sort($data['hostsAll'], ['name']);
564
565	$data['hostIdsLinkedTo'] = $hostIdsLinkedTo;
566	$data['templateId'] = $templateId;
567
568	$templateForm = new CView('configuration.template.edit', $data);
569	$templateWidget->addItem($templateForm->render());
570}
571else {
572	$sortField = getRequest('sort', CProfile::get('web.'.$page['file'].'.sort', 'name'));
573	$sortOrder = getRequest('sortorder', CProfile::get('web.'.$page['file'].'.sortorder', ZBX_SORT_UP));
574
575	CProfile::update('web.'.$page['file'].'.sort', $sortField, PROFILE_TYPE_STR);
576	CProfile::update('web.'.$page['file'].'.sortorder', $sortOrder, PROFILE_TYPE_STR);
577
578	$config = select_config();
579
580	$templateWidget->setControls((new CForm('get'))
581		->cleanItems()
582		->addItem((new CList())
583			->addItem([
584				new CLabel(_('Group'), 'groupid'),
585				(new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
586				$pageFilter->getGroupsCB()
587			])
588			->addItem(new CSubmit('form', _('Create template')))
589			->addItem(
590				(new CButton('form', _('Import')))
591					->onClick('redirect("conf.import.php?rules_preset=template")')
592			)
593		)
594	);
595
596	$form = (new CForm())->setName('templates');
597
598	$table = (new CTableInfo())
599		->setHeader([
600			(new CColHeader(
601				(new CCheckBox('all_templates'))->onClick("checkAll('".$form->getName()."', 'all_templates', 'templates');")
602			))->addClass(ZBX_STYLE_CELL_WIDTH),
603			make_sorting_header(_('Templates'), 'name', $sortField, $sortOrder),
604			_('Applications'),
605			_('Items'),
606			_('Triggers'),
607			_('Graphs'),
608			_('Screens'),
609			_('Discovery'),
610			_('Web'),
611			_('Linked templates'),
612			_('Linked to')
613		]);
614
615	// get templates
616	$templates = [];
617
618	if ($pageFilter->groupsSelected) {
619		$templates = API::Template()->get([
620			'output' => ['templateid', $sortField],
621			'groupids' => ($pageFilter->groupid > 0) ? $pageFilter->groupid : null,
622			'editable' => true,
623			'sortfield' => $sortField,
624			'limit' => $config['search_limit'] + 1
625		]);
626	}
627
628	// sorting && paging
629	order_result($templates, $sortField, $sortOrder);
630
631	$url = (new CUrl('templates.php'))
632		->setArgument('groupid', getRequest('groupid', 0));
633
634	$paging = getPagingLine($templates, $sortOrder, $url);
635
636	$templates = API::Template()->get([
637		'templateids' => zbx_objectValues($templates, 'templateid'),
638		'editable' => true,
639		'output' => ['name', 'proxy_hostid'],
640		'selectHosts' => ['hostid', 'name', 'status'],
641		'selectTemplates' => ['templateid', 'name', 'status'],
642		'selectParentTemplates' => ['templateid', 'name', 'status'],
643		'selectItems' => API_OUTPUT_COUNT,
644		'selectTriggers' => API_OUTPUT_COUNT,
645		'selectGraphs' => API_OUTPUT_COUNT,
646		'selectApplications' => API_OUTPUT_COUNT,
647		'selectDiscoveries' => API_OUTPUT_COUNT,
648		'selectScreens' => API_OUTPUT_COUNT,
649		'selectHttpTests' => API_OUTPUT_COUNT,
650		'nopermissions' => true
651	]);
652
653	order_result($templates, $sortField, $sortOrder);
654
655	// Select writable template IDs.
656	$linked_templateids = [];
657	$writable_templates = [];
658	$linked_hostids = [];
659	$writable_hosts = [];
660
661	foreach ($templates as $template) {
662		$linked_templateids = array_merge($linked_templateids,
663			zbx_objectValues($template['parentTemplates'], 'templateid'),
664			zbx_objectValues($template['templates'], 'templateid')
665		);
666
667		$linked_hostids = array_merge($linked_hostids, zbx_objectValues($template['hosts'], 'hostid'));
668	}
669
670	if ($linked_templateids) {
671		$writable_templates = API::Template()->get([
672			'output' => ['templateid'],
673			'templateids' => array_keys(array_flip($linked_templateids)),
674			'editable' => true,
675			'preservekeys' => true
676		]);
677	}
678
679	if ($linked_hostids) {
680		$writable_hosts = API::Host()->get([
681			'output' => ['hostid'],
682			'hostsids' => array_keys(array_flip($linked_hostids)),
683			'editable' => true,
684			'preservekeys' => true
685		]);
686	}
687
688	foreach ($templates as $template) {
689		$templatesOutput = [];
690
691		if ($template['proxy_hostid']) {
692			$proxy = get_host_by_hostid($template['proxy_hostid']);
693
694			$templatesOutput[] = $proxy['host'].NAME_DELIMITER;
695		}
696
697		$templatesOutput[] = new CLink($template['name'], 'templates.php?form=update&templateid='.$template['templateid'].url_param('groupid'));
698
699		$linkedTemplatesOutput = [];
700		$linkedToOutput = [];
701
702		$i = 0;
703
704		order_result($template['parentTemplates'], 'name');
705
706		foreach ($template['parentTemplates'] as $parentTemplate) {
707			$i++;
708
709			if ($i > $config['max_in_table']) {
710				$linkedTemplatesOutput[] = ' &hellip;';
711
712				break;
713			}
714
715			if ($linkedTemplatesOutput) {
716				$linkedTemplatesOutput[] = ', ';
717			}
718
719			if (array_key_exists($parentTemplate['templateid'], $writable_templates)) {
720				$url = 'templates.php?form=update&templateid='.$parentTemplate['templateid'].url_param('groupid');
721
722				$linkedTemplatesOutput[] = (new CLink($parentTemplate['name'], $url))
723					->addClass(ZBX_STYLE_LINK_ALT)
724					->addClass(ZBX_STYLE_GREY);
725			}
726			else {
727				$linkedTemplatesOutput[] = (new CSpan($parentTemplate['name']))->addClass(ZBX_STYLE_GREY);
728			}
729		}
730
731		$i = 0;
732
733		$linkedToObjects = array_merge($template['hosts'], $template['templates']);
734		order_result($linkedToObjects, 'name');
735
736		foreach ($linkedToObjects as $linkedToObject) {
737			$i++;
738
739			if ($i > $config['max_in_table']) {
740				$linkedToOutput[] = ' &hellip;';
741
742				break;
743			}
744
745			if ($linkedToOutput) {
746				$linkedToOutput[] = ', ';
747			}
748
749			if ($linkedToObject['status'] == HOST_STATUS_TEMPLATE) {
750				if (array_key_exists($linkedToObject['templateid'], $writable_templates)) {
751					$url = 'templates.php?form=update&templateid='.$linkedToObject['templateid'].url_param('groupid');
752					$link = (new CLink($linkedToObject['name'], $url))
753						->addClass(ZBX_STYLE_LINK_ALT)
754						->addClass(ZBX_STYLE_GREY);
755				}
756				else {
757					$link = (new CSpan($linkedToObject['name']))->addClass(ZBX_STYLE_GREY);
758				}
759			}
760			else {
761				if (array_key_exists($linkedToObject['hostid'], $writable_hosts)) {
762					$url = 'hosts.php?form=update&hostid='.$linkedToObject['hostid'].url_param('groupid');
763					$link = (new CLink($linkedToObject['name'], $url))->addClass(ZBX_STYLE_LINK_ALT);
764				}
765				else {
766					$link = new CSpan($linkedToObject['name']);
767				}
768
769				$link->addClass($linkedToObject['status'] == HOST_STATUS_MONITORED ? ZBX_STYLE_GREEN : ZBX_STYLE_RED);
770			}
771
772			$linkedToOutput[] = $link;
773		}
774
775		$table->addRow([
776			new CCheckBox('templates['.$template['templateid'].']', $template['templateid']),
777			(new CCol($templatesOutput))->addClass(ZBX_STYLE_NOWRAP),
778			[
779				new CLink(_('Applications'), 'applications.php?hostid='.$template['templateid'].url_param('groupid')),
780				CViewHelper::showNum($template['applications'])
781			],
782			[
783				new CLink(_('Items'), 'items.php?filter_set=1&hostid='.$template['templateid'].url_param('groupid')),
784				CViewHelper::showNum($template['items'])
785			],
786			[
787				new CLink(_('Triggers'), 'triggers.php?hostid='.$template['templateid'].url_param('groupid')),
788				CViewHelper::showNum($template['triggers'])
789			],
790			[
791				new CLink(_('Graphs'), 'graphs.php?hostid='.$template['templateid'].url_param('groupid')),
792				CViewHelper::showNum($template['graphs'])
793			],
794			[
795				new CLink(_('Screens'), 'screenconf.php?templateid='.$template['templateid']),
796				CViewHelper::showNum($template['screens'])
797			],
798			[
799				new CLink(_('Discovery'), 'host_discovery.php?hostid='.$template['templateid']),
800				CViewHelper::showNum($template['discoveries'])
801			],
802			[
803				new CLink(_('Web'), 'httpconf.php?hostid='.$template['templateid'].url_param('groupid')),
804				CViewHelper::showNum($template['httpTests'])
805			],
806			$linkedTemplatesOutput,
807			$linkedToOutput
808		]);
809	}
810
811	$footer = new CActionButtonList('action', 'templates', [
812		'template.export' => ['name' => _('Export')],
813		'template.massdelete' => ['name' => _('Delete'), 'confirm' => _('Delete selected templates?')],
814		'template.massdeleteclear' => ['name' => _('Delete and clear'),
815			'confirm' => _('Delete and clear selected templates? (Warning: all linked hosts will be cleared!)')
816		]
817	]);
818
819	$form->addItem([$table, $paging, $footer]);
820	$templateWidget->addItem($form);
821}
822
823$templateWidget->show();
824
825require_once dirname(__FILE__).'/include/page_footer.php';
826