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
22function italic($str) {
23	if (is_array($str)) {
24		foreach ($str as $key => $val) {
25			if (is_string($val)) {
26				$em = new CTag('em', true);
27				$em->addItem($val);
28				$str[$key] = $em;
29			}
30		}
31	}
32	elseif (is_string($str)) {
33		$em = new CTag('em', true, '');
34		$em->addItem($str);
35		$str = $em;
36	}
37	return $str;
38}
39
40function bold($str) {
41	if (is_array($str)) {
42		foreach ($str as $key => $val) {
43			if (is_string($val)) {
44				$str[$key] = new CTag('b', true, $val);
45			}
46		}
47
48		return $str;
49	}
50
51	return new CTag('b', true, $str);
52}
53
54function make_decoration($haystack, $needle, $class = null) {
55	$result = $haystack;
56
57	$tmpHaystack = mb_strtolower($haystack);
58	$tmpNeedle = mb_strtolower($needle);
59	$pos = mb_strpos($tmpHaystack, $tmpNeedle);
60
61	if ($pos !== false) {
62		$start = CHtml::encode(mb_substr($haystack, 0, $pos));
63		$end = CHtml::encode(mb_substr($haystack, $pos + mb_strlen($needle)));
64		$found = CHtml::encode(mb_substr($haystack, $pos, mb_strlen($needle)));
65
66		if (is_null($class)) {
67			$result = [$start, bold($found), $end];
68		}
69		else {
70			$result = [$start, (new CSpan($found))->addClass($class), $end];
71		}
72	}
73
74	return $result;
75}
76
77function prepareUrlParam($value, $name = null) {
78	if (is_array($value)) {
79		$result = '';
80
81		foreach ($value as $key => $param) {
82			$result .= prepareUrlParam($param, isset($name) ? $name.'['.$key.']' : $key);
83		}
84	}
85	else {
86		$result = '&'.$name.'='.urlencode($value);
87	}
88
89	return $result;
90}
91
92/**
93 * Get ready for url params.
94 *
95 * @param mixed  $param				param name or array with data depend from $getFromRequest
96 * @param bool   $getFromRequest	detect data source - input array or $_REQUEST variable
97 * @param string $name				if $_REQUEST variable is used this variable not used
98 *
99 * @return string
100 */
101function url_param($param, $getFromRequest = true, $name = null) {
102	if (is_array($param)) {
103		if ($getFromRequest) {
104			fatal_error(_('URL parameter cannot be array.'));
105		}
106	}
107	else {
108		if (is_null($name)) {
109			if (!$getFromRequest) {
110				fatal_error(_('URL parameter name is empty.'));
111			}
112
113			$name = $param;
114		}
115	}
116
117	if ($getFromRequest) {
118		$value =& $_REQUEST[$param];
119	}
120	else {
121		$value =& $param;
122	}
123
124	return isset($value) ? prepareUrlParam($value, $name) : '';
125}
126
127function url_params(array $params) {
128	$result = '';
129
130	foreach ($params as $param) {
131		$result .= url_param($param);
132	}
133
134	return $result;
135}
136
137function BR() {
138	return new CTag('br');
139}
140
141function get_icon($type, $params = []) {
142	switch ($type) {
143		case 'favourite':
144			if (CFavorite::exists($params['fav'], $params['elid'], $params['elname'])) {
145				$icon = (new CRedirectButton(SPACE, null))
146					->addClass(ZBX_STYLE_BTN_REMOVE_FAV)
147					->setTitle(_('Remove from favourites'))
148					->onClick('rm4favorites("'.$params['elname'].'", "'.$params['elid'].'");');
149			}
150			else {
151				$icon = (new CRedirectButton(SPACE, null))
152					->addClass(ZBX_STYLE_BTN_ADD_FAV)
153					->setTitle(_('Add to favourites'))
154					->onClick('add2favorites("'.$params['elname'].'", "'.$params['elid'].'");');
155			}
156			$icon->setId('addrm_fav');
157
158			return $icon;
159
160		case 'kioskmode':
161			if ($params['mode'] == ZBX_LAYOUT_KIOSKMODE) {
162				$icon = (new CButton(null, '&nbsp;'))
163					->setTitle(_('Normal view'))
164					->setAttribute('data-layout-mode', ZBX_LAYOUT_NORMAL)
165					->addClass(ZBX_LAYOUT_MODE)
166					->addClass(ZBX_STYLE_BTN_DASHBOARD_NORMAL)
167					->addClass(ZBX_STYLE_BTN_MIN);
168			}
169			else {
170				$icon = (new CButton(null, '&nbsp;'))
171					->setTitle(_('Kiosk mode'))
172					->setAttribute('data-layout-mode', ZBX_LAYOUT_KIOSKMODE)
173					->addClass(ZBX_LAYOUT_MODE)
174					->addClass(ZBX_STYLE_BTN_KIOSK);
175			}
176
177			return $icon;
178
179		case 'overviewhelp':
180			return (new CRedirectButton(SPACE, null))
181				->addClass(ZBX_STYLE_BTN_INFO);
182	}
183}
184
185/**
186 * Get host/template configuration navigation.
187 *
188 * @param string  $current_element
189 * @param int     $hostid
190 * @param int     $lld_ruleid
191 *
192 * @return CList|null
193 */
194function getHostNavigation($current_element, $hostid, $lld_ruleid = 0) {
195	$options = [
196		'output' => [
197			'hostid', 'status', 'name', 'maintenance_status', 'flags'
198		],
199		'selectHostDiscovery' => ['ts_delete'],
200		'selectInterfaces' => ['type', 'useip', 'ip', 'dns', 'port', 'version', 'details', 'available', 'error'],
201		'hostids' => [$hostid],
202		'editable' => true
203	];
204	if ($lld_ruleid == 0) {
205		$options['selectItems'] = API_OUTPUT_COUNT;
206		$options['selectTriggers'] = API_OUTPUT_COUNT;
207		$options['selectGraphs'] = API_OUTPUT_COUNT;
208		$options['selectDiscoveries'] = API_OUTPUT_COUNT;
209		$options['selectHttpTests'] = API_OUTPUT_COUNT;
210	}
211
212	// get hosts
213	$db_host = API::Host()->get($options);
214
215	if (!$db_host) {
216		$options = [
217			'output' => ['templateid', 'name', 'flags'],
218			'templateids' => [$hostid],
219			'editable' => true
220		];
221		if ($lld_ruleid == 0) {
222			$options['selectItems'] = API_OUTPUT_COUNT;
223			$options['selectTriggers'] = API_OUTPUT_COUNT;
224			$options['selectGraphs'] = API_OUTPUT_COUNT;
225			$options['selectDashboards'] = API_OUTPUT_COUNT;
226			$options['selectDiscoveries'] = API_OUTPUT_COUNT;
227			$options['selectHttpTests'] = API_OUTPUT_COUNT;
228		}
229
230		// get templates
231		$db_host = API::Template()->get($options);
232
233		$is_template = true;
234	}
235	else {
236		$is_template = false;
237	}
238
239	if (!$db_host) {
240		return null;
241	}
242
243	$db_host = reset($db_host);
244
245	// get lld-rules
246	if ($lld_ruleid != 0) {
247		$db_discovery_rule = API::DiscoveryRule()->get([
248			'output' => ['name'],
249			'selectItems' => API_OUTPUT_COUNT,
250			'selectTriggers' => API_OUTPUT_COUNT,
251			'selectGraphs' => API_OUTPUT_COUNT,
252			'selectHostPrototypes' => API_OUTPUT_COUNT,
253			'itemids' => [$lld_ruleid],
254			'editable' => true
255		]);
256		$db_discovery_rule = reset($db_discovery_rule);
257	}
258
259	$list = new CList();
260
261	if ($is_template) {
262		$template = new CSpan(
263			new CLink($db_host['name'], 'templates.php?form=update&templateid='.$db_host['templateid'])
264		);
265
266		if ($current_element === '') {
267			$template->addClass(ZBX_STYLE_SELECTED);
268		}
269
270		$list->addItem(new CBreadcrumbs([
271			new CSpan(new CLink(_('All templates'), new CUrl('templates.php'))),
272			$template
273		]));
274
275		$db_host['hostid'] = $db_host['templateid'];
276	}
277	else {
278		switch ($db_host['status']) {
279			case HOST_STATUS_MONITORED:
280				if ($db_host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON) {
281					$status = (new CSpan(_('In maintenance')))->addClass(ZBX_STYLE_ORANGE);
282				}
283				else {
284					$status = (new CSpan(_('Enabled')))->addClass(ZBX_STYLE_GREEN);
285				}
286				break;
287			case HOST_STATUS_NOT_MONITORED:
288				$status = (new CSpan(_('Disabled')))->addClass(ZBX_STYLE_RED);
289				break;
290			default:
291				$status = (new CSpan(_('Unknown')))->addClass(ZBX_STYLE_GREY);
292				break;
293		}
294
295		$host = new CSpan(new CLink(CHtml::encode($db_host['name']),
296			'hosts.php?form=update&hostid='.$db_host['hostid']
297		));
298
299		if ($current_element === '') {
300			$host->addClass(ZBX_STYLE_SELECTED);
301		}
302
303		$list
304			->addItem(new CBreadcrumbs([new CSpan(new CLink(_('All hosts'), new CUrl('hosts.php'))), $host]))
305			->addItem($status)
306			->addItem(getHostAvailabilityTable($db_host['interfaces']));
307
308		if ($db_host['flags'] == ZBX_FLAG_DISCOVERY_CREATED && $db_host['hostDiscovery']['ts_delete'] != 0) {
309			$info_icons = [getHostLifetimeIndicator(time(), $db_host['hostDiscovery']['ts_delete'])];
310			$list->addItem(makeInformationList($info_icons));
311		}
312	}
313
314	$content_menu = (new CList())
315		->setAttribute('role', 'navigation')
316		->setAttribute('aria-label', _('Content menu'));
317
318	$context = $is_template ? 'template' : 'host';
319
320	/*
321	 * the count of rows
322	 */
323	if ($lld_ruleid == 0) {
324		// items
325		$items = new CSpan([
326			new CLink(_('Items'),
327				(new CUrl('items.php'))
328					->setArgument('filter_set', '1')
329					->setArgument('filter_hostids', [$db_host['hostid']])
330					->setArgument('context', $context)
331			),
332			CViewHelper::showNum($db_host['items'])
333		]);
334		if ($current_element == 'items') {
335			$items->addClass(ZBX_STYLE_SELECTED);
336		}
337		$content_menu->addItem($items);
338
339		// triggers
340		$triggers = new CSpan([
341			new CLink(_('Triggers'),
342				(new CUrl('triggers.php'))
343					->setArgument('filter_set', '1')
344					->setArgument('filter_hostids', [$db_host['hostid']])
345					->setArgument('context', $context)
346			),
347			CViewHelper::showNum($db_host['triggers'])
348		]);
349		if ($current_element == 'triggers') {
350			$triggers->addClass(ZBX_STYLE_SELECTED);
351		}
352		$content_menu->addItem($triggers);
353
354		// graphs
355		$graphs = new CSpan([
356			new CLink(_('Graphs'), (new CUrl('graphs.php'))
357				->setArgument('filter_set', '1')
358				->setArgument('filter_hostids', [$db_host['hostid']])
359				->setArgument('context', $context)
360			),
361			CViewHelper::showNum($db_host['graphs'])
362		]);
363		if ($current_element == 'graphs') {
364			$graphs->addClass(ZBX_STYLE_SELECTED);
365		}
366		$content_menu->addItem($graphs);
367
368		// Dashboards
369		if ($is_template) {
370			$dashboards = new CSpan([
371				new CLink(_('Dashboards'),
372					(new CUrl('zabbix.php'))
373						->setArgument('action', 'template.dashboard.list')
374						->setArgument('templateid', $db_host['hostid'])
375				),
376				CViewHelper::showNum($db_host['dashboards'])
377			]);
378			if ($current_element == 'dashboards') {
379				$dashboards->addClass(ZBX_STYLE_SELECTED);
380			}
381			$content_menu->addItem($dashboards);
382		}
383
384		// discovery rules
385		$lld_rules = new CSpan([
386			new CLink(_('Discovery rules'), (new CUrl('host_discovery.php'))
387				->setArgument('filter_set', '1')
388				->setArgument('filter_hostids', [$db_host['hostid']])
389				->setArgument('context', $context)
390			),
391			CViewHelper::showNum($db_host['discoveries'])
392		]);
393		if ($current_element == 'discoveries') {
394			$lld_rules->addClass(ZBX_STYLE_SELECTED);
395		}
396		$content_menu->addItem($lld_rules);
397
398		// web scenarios
399		$http_tests = new CSpan([
400			new CLink(_('Web scenarios'),
401				(new CUrl('httpconf.php'))
402					->setArgument('filter_set', '1')
403					->setArgument('filter_hostids', [$db_host['hostid']])
404					->setArgument('context', $context)
405			),
406			CViewHelper::showNum($db_host['httpTests'])
407		]);
408		if ($current_element == 'web') {
409			$http_tests->addClass(ZBX_STYLE_SELECTED);
410		}
411		$content_menu->addItem($http_tests);
412	}
413	else {
414		$discovery_rule = (new CSpan())->addItem(
415			new CLink(
416				CHtml::encode($db_discovery_rule['name']),
417					(new CUrl('host_discovery.php'))
418						->setArgument('form', 'update')
419						->setArgument('itemid', $db_discovery_rule['itemid'])
420						->setArgument('context', $context)
421			)
422		);
423
424		if ($current_element == 'discoveries') {
425			$discovery_rule->addClass(ZBX_STYLE_SELECTED);
426		}
427
428		$list->addItem(new CBreadcrumbs([
429			(new CSpan())->addItem(new CLink(_('Discovery list'),
430				(new CUrl('host_discovery.php'))
431					->setArgument('filter_set', '1')
432					->setArgument('filter_hostids', [$db_host['hostid']])
433					->setArgument('context', $context)
434			)),
435			$discovery_rule
436		]));
437
438		// item prototypes
439		$item_prototypes = new CSpan([
440			new CLink(_('Item prototypes'),
441				(new CUrl('disc_prototypes.php'))
442					->setArgument('parent_discoveryid', $db_discovery_rule['itemid'])
443					->setArgument('context', $context)
444			),
445			CViewHelper::showNum($db_discovery_rule['items'])
446		]);
447		if ($current_element == 'items') {
448			$item_prototypes->addClass(ZBX_STYLE_SELECTED);
449		}
450		$content_menu->addItem($item_prototypes);
451
452		// trigger prototypes
453		$trigger_prototypes = new CSpan([
454			new CLink(_('Trigger prototypes'),
455				(new CUrl('trigger_prototypes.php'))
456					->setArgument('parent_discoveryid', $db_discovery_rule['itemid'])
457					->setArgument('context', $context)
458			),
459			CViewHelper::showNum($db_discovery_rule['triggers'])
460		]);
461		if ($current_element == 'triggers') {
462			$trigger_prototypes->addClass(ZBX_STYLE_SELECTED);
463		}
464		$content_menu->addItem($trigger_prototypes);
465
466		// graph prototypes
467		$graph_prototypes = new CSpan([
468			new CLink(_('Graph prototypes'),
469				(new CUrl('graphs.php'))
470					->setArgument('parent_discoveryid', $db_discovery_rule['itemid'])
471					->setArgument('context', $context)
472			),
473			CViewHelper::showNum($db_discovery_rule['graphs'])
474		]);
475		if ($current_element === 'graphs') {
476			$graph_prototypes->addClass(ZBX_STYLE_SELECTED);
477		}
478		$content_menu->addItem($graph_prototypes);
479
480		// host prototypes
481		if ($db_host['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
482			$host_prototypes = new CSpan([
483				new CLink(_('Host prototypes'),
484					(new CUrl('host_prototypes.php'))
485						->setArgument('parent_discoveryid', $db_discovery_rule['itemid'])
486						->setArgument('context', $context)
487				),
488				CViewHelper::showNum($db_discovery_rule['hostPrototypes'])
489			]);
490			if ($current_element == 'hosts') {
491				$host_prototypes->addClass(ZBX_STYLE_SELECTED);
492			}
493			$content_menu->addItem($host_prototypes);
494		}
495	}
496
497	$list->addItem($content_menu);
498
499	return $list;
500}
501
502/**
503 * Get map navigation.
504 *
505 * @param int    $sysmapid      Used as value for sysmaid in map link generation.
506 * @param string $name          Used as label for map link generation.
507 * @param int    $severity_min  Used as value for severity_min in map link generation.
508 *
509 * @return CList
510 */
511function getSysmapNavigation($sysmapid, $name, $severity_min) {
512	$list = (new CList())->addItem(new CBreadcrumbs([
513		(new CSpan())->addItem(new CLink(_('All maps'), new CUrl('sysmaps.php'))),
514		(new CSpan())
515			->addClass(ZBX_STYLE_SELECTED)
516			->addItem(new CLink($name,
517				(new CUrl('zabbix.php'))
518					->setArgument('action', 'map.view')
519					->setArgument('sysmapid', $sysmapid)
520					->setArgument('severity_min', $severity_min)
521			))
522	]));
523
524	// get map parent maps
525	$parent_sysmaps = get_parent_sysmaps($sysmapid);
526	if ($parent_sysmaps) {
527		$parent_maps = (new CList())
528			->setAttribute('aria-label', _('Upper level maps'))
529			->addItem((new CSpan())->addItem(_('Upper level maps').':'));
530
531		foreach ($parent_sysmaps as $parent_sysmap) {
532			$parent_maps->addItem((new CSpan())->addItem(new CLink($parent_sysmap['name'],
533				(new CUrl('zabbix.php'))
534					->setArgument('action', 'map.view')
535					->setArgument('sysmapid', $parent_sysmap['sysmapid'])
536					->setArgument('severity_min', $severity_min)
537			)));
538		}
539
540		$list->addItem($parent_maps);
541	}
542
543	return $list;
544}
545
546/**
547 * Renders a form footer with the given buttons.
548 *
549 * @param CButtonInterface 		$main_button	main button that will be displayed on the left
550 * @param CButtonInterface[] 	$other_buttons
551 *
552 * @return CDiv
553 *
554 * @throws InvalidArgumentException	if an element of $other_buttons contain something other than CButtonInterface
555 */
556function makeFormFooter(CButtonInterface $main_button = null, array $other_buttons = []) {
557	foreach ($other_buttons as $other_button) {
558		$other_button->addClass(ZBX_STYLE_BTN_ALT);
559	}
560
561	if ($main_button !== null) {
562		array_unshift($other_buttons, $main_button);
563	}
564
565	return (new CList())
566		->addClass(ZBX_STYLE_TABLE_FORMS)
567		->addItem([
568			(new CDiv())->addClass(ZBX_STYLE_TABLE_FORMS_TD_LEFT),
569			(new CDiv($other_buttons))
570				->addClass(ZBX_STYLE_TABLE_FORMS_TD_RIGHT)
571				->addClass('tfoot-buttons')
572		]);
573}
574
575/**
576 * Create HTML helper element for host interfaces availability.
577 *
578 * @param array $host_interfaces                                Array of arrays of host interfaces.
579 * @param int   $host_interfaces[]['type']                      Interface type.
580 * @param int   $host_interfaces[]['available']                 Interface availability.
581 * @param int   $host_interfaces[]['useip']                     Interface use IP or DNS.
582 * @param int   $host_interfaces[]['ip']                        Interface IP address.
583 * @param int   $host_interfaces[]['dns']                       Interface domain name.
584 * @param int   $host_interfaces[]['port']                      Interface port.
585 * @param int   $host_interfaces[]['details']['version']        Interface SNMP version.
586 * @param int   $host_interfaces[]['details']['contextname']    Interface context name for SNMP version 3.
587 * @param int   $host_interfaces[]['details']['community']      Interface community for SNMP non version 3 interface.
588 * @param int   $host_interfaces[]['details']['securitylevel']  Security level for SNMP version 3 interface.
589 * @param int   $host_interfaces[]['details']['authprotocol']   Authentication protocol for SNMP version 3 interface.
590 * @param int   $host_interfaces[]['details']['privprotocol']   Privacy protocol for SNMP version 3 interface.
591 * @param int   $host_interfaces[]['error']                     Interface error message.
592 *
593 * @return CHostAvailability
594 */
595function getHostAvailabilityTable($host_interfaces): CHostAvailability {
596	$interfaces = [];
597
598	foreach ($host_interfaces as $interface) {
599		$description = null;
600
601		if ($interface['type'] == INTERFACE_TYPE_SNMP) {
602			$description = getSnmpInterfaceDescription($interface);
603		}
604
605		$interfaces[] = [
606			'type' => $interface['type'],
607			'available' => $interface['available'],
608			'interface' => getHostInterface($interface),
609			'description' => $description,
610			'error' => ($interface['available'] == INTERFACE_AVAILABLE_TRUE) ? '' : $interface['error']
611		];
612	}
613
614	return (new CHostAvailability())->setInterfaces($interfaces);
615}
616
617/**
618 * Returns the discovered host group lifetime indicator.
619 *
620 * @param string $current_time	current Unix timestamp
621 * @param array  $ts_delete		deletion timestamp of the host group
622 *
623 * @return CDiv
624 */
625function getHostGroupLifetimeIndicator($current_time, $ts_delete) {
626	// Check if the element should've been deleted in the past.
627	if ($current_time > $ts_delete) {
628		$warning = _(
629			'The host group is not discovered anymore and will be deleted the next time discovery rule is processed.'
630		);
631	}
632	else {
633		$warning = _s(
634			'The host group is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
635			zbx_date2age($current_time, $ts_delete),
636			zbx_date2str(DATE_FORMAT, $ts_delete),
637			zbx_date2str(TIME_FORMAT, $ts_delete)
638		);
639	}
640
641	return makeWarningIcon($warning);
642}
643
644/**
645 * Returns the discovered host lifetime indicator.
646 *
647 * @param string $current_time	current Unix timestamp
648 * @param array  $ts_delete		deletion timestamp of the host
649 *
650 * @return CDiv
651 */
652function getHostLifetimeIndicator($current_time, $ts_delete) {
653	// Check if the element should've been deleted in the past.
654	if ($current_time > $ts_delete) {
655		$warning = _(
656			'The host is not discovered anymore and will be deleted the next time discovery rule is processed.'
657		);
658	}
659	else {
660		$warning = _s(
661			'The host is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
662			zbx_date2age($current_time, $ts_delete),
663			zbx_date2str(DATE_FORMAT, $ts_delete),
664			zbx_date2str(TIME_FORMAT, $ts_delete)
665		);
666	}
667
668	return makeWarningIcon($warning);
669}
670
671/**
672 * Returns the discovered graph lifetime indicator.
673 *
674 * @param string $current_time	current Unix timestamp
675 * @param array  $ts_delete		deletion timestamp of the graph
676 *
677 * @return CDiv
678 */
679function getGraphLifetimeIndicator($current_time, $ts_delete) {
680	// Check if the element should've been deleted in the past.
681	if ($current_time > $ts_delete) {
682		$warning = _(
683			'The graph is not discovered anymore and will be deleted the next time discovery rule is processed.'
684		);
685	}
686	else {
687		$warning = _s(
688			'The graph is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
689			zbx_date2age($current_time, $ts_delete),
690			zbx_date2str(DATE_FORMAT, $ts_delete),
691			zbx_date2str(TIME_FORMAT, $ts_delete)
692		);
693	}
694
695	return makeWarningIcon($warning);
696}
697
698/**
699 * Returns the discovered trigger lifetime indicator.
700 *
701 * @param string $current_time	current Unix timestamp
702 * @param array  $ts_delete		deletion timestamp of the trigger
703 *
704 * @return CDiv
705 */
706function getTriggerLifetimeIndicator($current_time, $ts_delete) {
707	// Check if the element should've been deleted in the past.
708	if ($current_time > $ts_delete) {
709		$warning = _(
710			'The trigger is not discovered anymore and will be deleted the next time discovery rule is processed.'
711		);
712	}
713	else {
714		$warning = _s(
715			'The trigger is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
716			zbx_date2age($current_time, $ts_delete),
717			zbx_date2str(DATE_FORMAT, $ts_delete),
718			zbx_date2str(TIME_FORMAT, $ts_delete)
719		);
720	}
721
722	return makeWarningIcon($warning);
723}
724
725/**
726 * Returns the discovered item lifetime indicator.
727 *
728 * @param string $current_time	current Unix timestamp
729 * @param array  $ts_delete		deletion timestamp of the item
730 *
731 * @return CDiv
732 */
733function getItemLifetimeIndicator($current_time, $ts_delete) {
734	// Check if the element should've been deleted in the past.
735	if ($current_time > $ts_delete) {
736		$warning = _(
737			'The item is not discovered anymore and will be deleted the next time discovery rule is processed.'
738		);
739	}
740	else {
741		$warning = _s(
742			'The item is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
743			zbx_date2age($current_time, $ts_delete),
744			zbx_date2str(DATE_FORMAT, $ts_delete),
745			zbx_date2str(TIME_FORMAT, $ts_delete)
746		);
747	}
748
749	return makeWarningIcon($warning);
750}
751
752function makeServerStatusOutput() {
753	return (new CTag('output', true))
754		->setId('msg-global-footer')
755		->addClass(ZBX_STYLE_MSG_GLOBAL_FOOTER)
756		->addClass(ZBX_STYLE_MSG_WARNING);
757}
758
759/**
760* Make logo of the specified type.
761*
762* @param int $type  LOGO_TYPE_NORMAL | LOGO_TYPE_SIDEBAR | LOGO_TYPE_SIDEBAR_COMPACT.
763*
764* @return CTag
765*/
766function makeLogo(int $type): ?CTag {
767	static $zabbix_logo_classes = [
768		LOGO_TYPE_NORMAL => ZBX_STYLE_ZABBIX_LOGO,
769		LOGO_TYPE_SIDEBAR => ZBX_STYLE_ZABBIX_SIDEBAR_LOGO,
770		LOGO_TYPE_SIDEBAR_COMPACT => ZBX_STYLE_ZABBIX_SIDEBAR_LOGO_COMPACT
771	];
772
773	$brand_logo = CBrandHelper::getLogo($type);
774
775	if ($brand_logo !== null) {
776		return (new CImg($brand_logo));
777	}
778	else {
779		return (new CDiv())->addClass($zabbix_logo_classes[$type]);
780	}
781}
782
783/**
784 * Renders a page footer.
785 *
786 * @param bool $with_version
787 *
788 * @return CDiv
789 */
790function makePageFooter($with_version = true) {
791	return (new CTag('footer', true, CBrandHelper::getFooterContent($with_version)))
792		->setAttribute('role', 'contentinfo');
793}
794
795/**
796 * Get drop-down submenu item list for the User settings section.
797 *
798 * @return array|null  Menu definition for CWidget::setTitleSubmenu.
799 */
800function getUserSettingsSubmenu(): ?array {
801	if (!CWebUser::checkAccess(CRoleHelper::ACTIONS_MANAGE_API_TOKENS)) {
802		return null;
803	}
804
805	$profile_url = (new CUrl('zabbix.php'))
806		->setArgument('action', 'userprofile.edit')
807		->getUrl();
808
809	$tokens_url = (new CUrl('zabbix.php'))
810		->setArgument('action', 'user.token.list')
811		->getUrl();
812
813	return [
814		'main_section' => [
815			'items' => array_filter([
816				$profile_url => _('User profile'),
817				$tokens_url  => _('API tokens')
818			])
819		]
820	];
821}
822
823/**
824 * Get drop-down submenu item list for the Administration->General section.
825 *
826 * @return array  Menu definition for CWidget::setTitleSubmenu.
827 */
828function getAdministrationGeneralSubmenu() {
829	$gui_url = (new CUrl('zabbix.php'))
830		->setArgument('action', 'gui.edit')
831		->getUrl();
832
833	$autoreg_url = (new CUrl('zabbix.php'))
834		->setArgument('action', 'autoreg.edit')
835		->getUrl();
836
837	$housekeeping_url = (new CUrl('zabbix.php'))
838		->setArgument('action', 'housekeeping.edit')
839		->getUrl();
840
841	$image_url = (new CUrl('zabbix.php'))
842		->setArgument('action', 'image.list')
843		->getUrl();
844
845	$iconmap_url = (new CUrl('zabbix.php'))
846		->setArgument('action', 'iconmap.list')
847		->getUrl();
848
849	$regex_url = (new CUrl('zabbix.php'))
850		->setArgument('action', 'regex.list')
851		->getUrl();
852
853	$macros_url = (new CUrl('zabbix.php'))
854		->setArgument('action', 'macros.edit')
855		->getUrl();
856
857	$trigdisplay_url = (new CUrl('zabbix.php'))
858		->setArgument('action', 'trigdisplay.edit')
859		->getUrl();
860
861	$modules_url = (new CUrl('zabbix.php'))
862		->setArgument('action', 'module.list')
863		->getUrl();
864
865	$tokens_url = (new CUrl('zabbix.php'))
866		->setArgument('action', 'token.list')
867		->getUrl();
868
869	$miscconfig_url = (new CUrl('zabbix.php'))
870		->setArgument('action', 'miscconfig.edit')
871		->getUrl();
872
873	$can_access_tokens = (!CWebUser::isGuest() && CWebUser::checkAccess(CRoleHelper::ACTIONS_MANAGE_API_TOKENS));
874
875	return [
876		'main_section' => [
877			'items' => array_filter([
878				$gui_url          => _('GUI'),
879				$autoreg_url      => _('Autoregistration'),
880				$housekeeping_url => _('Housekeeping'),
881				$image_url        => _('Images'),
882				$iconmap_url      => _('Icon mapping'),
883				$regex_url        => _('Regular expressions'),
884				$macros_url       => _('Macros'),
885				$trigdisplay_url  => _('Trigger displaying options'),
886				$modules_url      => _('Modules'),
887				$tokens_url       => $can_access_tokens ? _('API tokens') : null,
888				$miscconfig_url   => _('Other')
889			])
890		]
891	];
892}
893
894/**
895 * Renders an icon list.
896 *
897 * @param array $info_icons  The list of information icons.
898 *
899 * @return CDiv|string
900 */
901function makeInformationList($info_icons) {
902	return $info_icons ? (new CDiv($info_icons))->addClass(ZBX_STYLE_REL_CONTAINER) : '';
903}
904
905/**
906 * Renders an information icon like green [i] with message.
907 *
908 * @param string $message
909 *
910 * @return CSpan
911 */
912function makeInformationIcon($message) {
913	return (new CSpan())
914		->addClass(ZBX_STYLE_ICON_INFO)
915		->addClass(ZBX_STYLE_STATUS_GREEN)
916		->setHint($message, ZBX_STYLE_HINTBOX_WRAP);
917}
918
919/**
920 * Renders an icon for host in maintenance.
921 *
922 * @param int    $type         Type of the maintenance.
923 * @param string $name         Name of the maintenance.
924 * @param string $description  Description of the maintenance.
925 *
926 * @return CSpan
927 */
928function makeMaintenanceIcon($type, $name, $description) {
929	$hint = $name.' ['.($type
930		? _('Maintenance without data collection')
931		: _('Maintenance with data collection')).']';
932
933	if ($description !== '') {
934		$hint .= "\n".$description;
935	}
936
937	return (new CSpan())
938		->addClass(ZBX_STYLE_ICON_MAINT)
939		->addClass(ZBX_STYLE_CURSOR_POINTER)
940		->setHint($hint);
941}
942
943/**
944 * Renders an icon for suppressed problem.
945 *
946 * @param array  $icon_data
947 * @param string $icon_data[]['suppress_until']    Time until the problem is suppressed.
948 * @param string $icon_data[]['maintenance_name']  Name of the maintenance.
949 *
950 * @return CSpan
951 */
952function makeSuppressedProblemIcon(array $icon_data) {
953	$suppress_until = max(zbx_objectValues($icon_data, 'suppress_until'));
954
955	CArrayHelper::sort($icon_data, ['maintenance_name']);
956	$maintenance_names = implode(', ', zbx_objectValues($icon_data, 'maintenance_name'));
957
958	return (new CSpan())
959		->addClass(ZBX_STYLE_ICON_INVISIBLE)
960		->addClass(ZBX_STYLE_CURSOR_POINTER)
961		->setHint(
962			_s('Suppressed till: %1$s', ($suppress_until < strtotime('tomorrow'))
963				? zbx_date2str(TIME_FORMAT, $suppress_until)
964				: zbx_date2str(DATE_TIME_FORMAT, $suppress_until)
965			).
966			"\n".
967			_s('Maintenance: %1$s', $maintenance_names)
968		);
969}
970
971/**
972 * Renders an action icon.
973 *
974 * @param array  $icon_data
975 * @param string $icon_data[icon]    Icon style.
976 * @param array  $icon_data[hint]    Hintbox content (optional).
977 * @param bool   $icon_data[button]  Use button element (optional).
978 * @param int    $icon_data[num]     Number displayed over the icon (optional).
979 *
980 * @return CTag  Returns CSpan or CButton depending on boolean $icon_data['button'] parameter
981 */
982function makeActionIcon(array $icon_data): CTag {
983
984	if (array_key_exists('button', $icon_data) && $icon_data['button']) {
985		$icon = (new CButton(null))->addClass($icon_data['icon']);
986	}
987	else {
988		$icon = (new CSpan())->addClass($icon_data['icon']);
989	}
990
991	if (array_key_exists('num', $icon_data)) {
992		if ($icon_data['num'] > 99) {
993			$icon_data['num'] = '99+';
994		}
995		$icon->setAttribute('data-count', $icon_data['num']);
996	}
997
998	if (array_key_exists('hint', $icon_data)) {
999		$icon
1000			->addClass(ZBX_STYLE_CURSOR_POINTER)
1001			->setHint($icon_data['hint'], '', true, 'max-width: '.ZBX_ACTIONS_POPUP_MAX_WIDTH.'px;');
1002	}
1003	elseif (array_key_exists('title', $icon_data)) {
1004		$icon->setTitle($icon_data['title']);
1005	}
1006
1007	if (array_key_exists('aria-label', $icon_data)) {
1008		$icon
1009			->addItem($icon_data['aria-label'])
1010			->addClass(ZBX_STYLE_INLINE_SR_ONLY);
1011	}
1012
1013	return $icon;
1014}
1015
1016/**
1017 * Renders an icon for a description.
1018 *
1019 * @param string $description
1020 *
1021 * @return CSpan
1022 */
1023function makeDescriptionIcon($description) {
1024	return (new CSpan())
1025		->addClass(ZBX_STYLE_ICON_DESCRIPTION)
1026		->addClass(ZBX_STYLE_CURSOR_POINTER)
1027		->setHint(zbx_str2links($description), ZBX_STYLE_HINTBOX_WRAP);
1028}
1029
1030/**
1031 * Renders an error icon like red [i] with error message
1032 *
1033 * @param string $error
1034 *
1035 * @return CSpan
1036 */
1037function makeErrorIcon($error) {
1038	return (new CSpan())
1039		->addClass(ZBX_STYLE_ICON_INFO)
1040		->addClass(ZBX_STYLE_STATUS_RED)
1041		->setHint($error, ZBX_STYLE_HINTBOX_WRAP." ".ZBX_STYLE_RED);
1042}
1043
1044/**
1045 * Renders an unknown icon like grey [i] with error message
1046 *
1047 * @param string $error
1048 *
1049 * @return CSpan
1050 */
1051function makeUnknownIcon($error) {
1052	return (new CSpan())
1053		->addClass(ZBX_STYLE_ICON_INFO)
1054		->addClass(ZBX_STYLE_STATUS_DARK_GREY)
1055		->setHint($error, ZBX_STYLE_HINTBOX_WRAP." ".ZBX_STYLE_RED);
1056}
1057
1058/**
1059 * Renders a warning icon like yellow [i] with error message
1060 *
1061 * @param string $error
1062 *
1063 * @return CSpan
1064 */
1065function makeWarningIcon($error) {
1066	return (new CSpan())
1067		->addClass(ZBX_STYLE_ICON_INFO)
1068		->addClass(ZBX_STYLE_STATUS_YELLOW)
1069		->setHint($error, ZBX_STYLE_HINTBOX_WRAP);
1070}
1071
1072/**
1073 * Returns css for trigger severity backgrounds.
1074 *
1075 * @return string
1076 */
1077function getTriggerSeverityCss() {
1078	$css = '';
1079
1080	$severities = [
1081		ZBX_STYLE_NA_BG => CSettingsHelper::getGlobal(CSettingsHelper::SEVERITY_COLOR_0),
1082		ZBX_STYLE_INFO_BG => CSettingsHelper::getGlobal(CSettingsHelper::SEVERITY_COLOR_1),
1083		ZBX_STYLE_WARNING_BG => CSettingsHelper::getGlobal(CSettingsHelper::SEVERITY_COLOR_2),
1084		ZBX_STYLE_AVERAGE_BG => CSettingsHelper::getGlobal(CSettingsHelper::SEVERITY_COLOR_3),
1085		ZBX_STYLE_HIGH_BG => CSettingsHelper::getGlobal(CSettingsHelper::SEVERITY_COLOR_4),
1086		ZBX_STYLE_DISASTER_BG => CSettingsHelper::getGlobal(CSettingsHelper::SEVERITY_COLOR_5)
1087	];
1088
1089	foreach ($severities as $class => $color) {
1090		$css .= '.'.$class.', .'.$class.' input[type="radio"]:checked + label, .'.$class.':before, .flh-'.$class.
1091			', .status-'.$class.', .status-'.$class.':before { background-color: #'.$color.' }'."\n";
1092	}
1093
1094	return $css;
1095}
1096
1097/**
1098 * Returns css for trigger status colors, if those are customized.
1099 *
1100 * @return string
1101 */
1102function getTriggerStatusCss() {
1103	$css = '';
1104
1105	if (CSettingsHelper::getGlobal(CSettingsHelper::CUSTOM_COLOR) == EVENT_CUSTOM_COLOR_ENABLED) {
1106		$event_statuses = [
1107			ZBX_STYLE_PROBLEM_UNACK_FG => CSettingsHelper::get(CSettingsHelper::PROBLEM_UNACK_COLOR),
1108			ZBX_STYLE_PROBLEM_ACK_FG => CSettingsHelper::get(CSettingsHelper::PROBLEM_ACK_COLOR),
1109			ZBX_STYLE_OK_UNACK_FG => CSettingsHelper::get(CSettingsHelper::OK_UNACK_COLOR),
1110			ZBX_STYLE_OK_ACK_FG => CSettingsHelper::get(CSettingsHelper::OK_ACK_COLOR)
1111		];
1112
1113		foreach ($event_statuses as $class => $color) {
1114			$css .= '.' . $class . ' {color: #' . $color . ';}' . "\n";
1115		}
1116	}
1117
1118	return $css;
1119}
1120