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_DASHBRD_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 'screenconf':
180			return (new CRedirectButton(SPACE, null))
181				->addClass(ZBX_STYLE_BTN_CONF)
182				->setTitle(_('Refresh interval'));
183
184		case 'overviewhelp':
185			return (new CRedirectButton(SPACE, null))
186				->addClass(ZBX_STYLE_BTN_INFO);
187	}
188}
189
190/**
191 * Create CDiv with host/template information and references to it's elements
192 *
193 * @param string $currentElement
194 * @param int $hostid
195 * @param int $lld_ruleid
196 *
197 * @return object
198 */
199function get_header_host_table($current_element, $hostid, $lld_ruleid = 0) {
200	$options = [
201		'output' => [
202			'hostid', 'status', 'name', 'maintenance_status', 'flags', 'available', 'snmp_available',
203			'jmx_available', 'ipmi_available', 'error', 'snmp_error', 'jmx_error', 'ipmi_error'
204		],
205		'selectHostDiscovery' => ['ts_delete'],
206		'hostids' => [$hostid],
207		'editable' => true
208	];
209	if ($lld_ruleid == 0) {
210		$options['selectApplications'] = API_OUTPUT_COUNT;
211		$options['selectItems'] = API_OUTPUT_COUNT;
212		$options['selectTriggers'] = API_OUTPUT_COUNT;
213		$options['selectGraphs'] = API_OUTPUT_COUNT;
214		$options['selectDiscoveries'] = API_OUTPUT_COUNT;
215		$options['selectHttpTests'] = API_OUTPUT_COUNT;
216	}
217
218	// get hosts
219	$db_host = API::Host()->get($options);
220
221	if (!$db_host) {
222		$options = [
223			'output' => ['templateid', 'name', 'flags'],
224			'templateids' => [$hostid],
225			'editable' => true
226		];
227		if ($lld_ruleid == 0) {
228			$options['selectApplications'] = API_OUTPUT_COUNT;
229			$options['selectItems'] = API_OUTPUT_COUNT;
230			$options['selectTriggers'] = API_OUTPUT_COUNT;
231			$options['selectGraphs'] = API_OUTPUT_COUNT;
232			$options['selectScreens'] = API_OUTPUT_COUNT;
233			$options['selectDiscoveries'] = API_OUTPUT_COUNT;
234			$options['selectHttpTests'] = API_OUTPUT_COUNT;
235		}
236
237		// get templates
238		$db_host = API::Template()->get($options);
239
240		$is_template = true;
241	}
242	else {
243		$is_template = false;
244	}
245
246	if (!$db_host) {
247		return null;
248	}
249
250	$db_host = reset($db_host);
251
252	// get lld-rules
253	if ($lld_ruleid != 0) {
254		$db_discovery_rule = API::DiscoveryRule()->get([
255			'output' => ['name'],
256			'selectItems' => API_OUTPUT_COUNT,
257			'selectTriggers' => API_OUTPUT_COUNT,
258			'selectGraphs' => API_OUTPUT_COUNT,
259			'selectHostPrototypes' => API_OUTPUT_COUNT,
260			'itemids' => [$lld_ruleid],
261			'editable' => true
262		]);
263		$db_discovery_rule = reset($db_discovery_rule);
264	}
265
266	/*
267	 * list and host (template) name
268	 */
269	$list = (new CList())
270		->addClass(ZBX_STYLE_OBJECT_GROUP)
271		->addClass(ZBX_STYLE_FILTER_BREADCRUMB);
272
273	$breadcrumbs = (new CListItem(null))
274		->setAttribute('role', 'navigation')
275		->setAttribute('aria-label', _x('Hierarchy', 'screen reader'));
276
277	if ($is_template) {
278		$template = new CSpan(
279			new CLink($db_host['name'], 'templates.php?form=update&templateid='.$db_host['templateid'])
280		);
281
282		if ($current_element === '') {
283			$template->addClass(ZBX_STYLE_SELECTED);
284		}
285
286		$breadcrumbs->addItem([
287			new CSpan(new CLink(_('All templates'), new CUrl('templates.php'))),
288			'/',
289			$template
290		]);
291
292		$db_host['hostid'] = $db_host['templateid'];
293		$list->addItem($breadcrumbs);
294	}
295	else {
296		switch ($db_host['status']) {
297			case HOST_STATUS_MONITORED:
298				if ($db_host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON) {
299					$status = (new CSpan(_('In maintenance')))->addClass(ZBX_STYLE_ORANGE);
300				}
301				else {
302					$status = (new CSpan(_('Enabled')))->addClass(ZBX_STYLE_GREEN);
303				}
304				break;
305			case HOST_STATUS_NOT_MONITORED:
306				$status = (new CSpan(_('Disabled')))->addClass(ZBX_STYLE_RED);
307				break;
308			default:
309				$status = _('Unknown');
310				break;
311		}
312
313		$host = new CSpan(new CLink(CHtml::encode($db_host['name']),
314			'hosts.php?form=update&hostid='.$db_host['hostid']
315		));
316
317		if ($current_element === '') {
318			$host->addClass(ZBX_STYLE_SELECTED);
319		}
320
321		$breadcrumbs->addItem([
322			new CSpan(new CLink(_('All hosts'), new CUrl('hosts.php'))),
323			'/',
324			$host
325		]);
326		$list->addItem($breadcrumbs);
327		$list->addItem($status);
328		$list->addItem(getHostAvailabilityTable($db_host));
329
330		if ($db_host['flags'] == ZBX_FLAG_DISCOVERY_CREATED && $db_host['hostDiscovery']['ts_delete'] != 0) {
331			$info_icons = [getHostLifetimeIndicator(time(), $db_host['hostDiscovery']['ts_delete'])];
332			$list->addItem(makeInformationList($info_icons));
333		}
334	}
335
336	$content_menu = (new CList())
337		->setAttribute('role', 'navigation')
338		->setAttribute('aria-label', _('Content menu'));
339
340	/*
341	 * the count of rows
342	 */
343	if ($lld_ruleid == 0) {
344		// applications
345		$applications = new CSpan([
346			new CLink(_('Applications'),
347				(new CUrl('applications.php'))
348					->setArgument('filter_set', '1')
349					->setArgument('filter_hostids', [$db_host['hostid']])
350			),
351			CViewHelper::showNum($db_host['applications'])
352		]);
353		if ($current_element == 'applications') {
354			$applications->addClass(ZBX_STYLE_SELECTED);
355		}
356		$content_menu->addItem($applications);
357
358		// items
359		$items = new CSpan([
360			new CLink(_('Items'),
361				(new CUrl('items.php'))
362					->setArgument('filter_set', '1')
363					->setArgument('filter_hostids', [$db_host['hostid']])
364			),
365			CViewHelper::showNum($db_host['items'])
366		]);
367		if ($current_element == 'items') {
368			$items->addClass(ZBX_STYLE_SELECTED);
369		}
370		$content_menu->addItem($items);
371
372		// triggers
373		$triggers = new CSpan([
374			new CLink(_('Triggers'),
375				(new CUrl('triggers.php'))
376					->setArgument('filter_set', '1')
377					->setArgument('filter_hostids', [$db_host['hostid']])
378			),
379			CViewHelper::showNum($db_host['triggers'])
380		]);
381		if ($current_element == 'triggers') {
382			$triggers->addClass(ZBX_STYLE_SELECTED);
383		}
384		$content_menu->addItem($triggers);
385
386		// graphs
387		$graphs = new CSpan([
388			new CLink(_('Graphs'), (new CUrl('graphs.php'))
389				->setArgument('filter_set', '1')
390				->setArgument('filter_hostids', [$db_host['hostid']])
391			),
392			CViewHelper::showNum($db_host['graphs'])
393		]);
394		if ($current_element == 'graphs') {
395			$graphs->addClass(ZBX_STYLE_SELECTED);
396		}
397		$content_menu->addItem($graphs);
398
399		// screens
400		if ($is_template) {
401			$screens = new CSpan([
402				new CLink(_('Screens'), 'screenconf.php?templateid='.$db_host['hostid']),
403				CViewHelper::showNum($db_host['screens'])
404			]);
405			if ($current_element == 'screens') {
406				$screens->addClass(ZBX_STYLE_SELECTED);
407			}
408			$content_menu->addItem($screens);
409		}
410
411		// discovery rules
412		$lld_rules = new CSpan([
413			new CLink(_('Discovery rules'), (new CUrl('host_discovery.php'))
414				->setArgument('filter_set', '1')
415				->setArgument('filter_hostids', [$db_host['hostid']])
416			),
417			CViewHelper::showNum($db_host['discoveries'])
418		]);
419		if ($current_element == 'discoveries') {
420			$lld_rules->addClass(ZBX_STYLE_SELECTED);
421		}
422		$content_menu->addItem($lld_rules);
423
424		// web scenarios
425		$http_tests = new CSpan([
426			new CLink(_('Web scenarios'),
427				(new CUrl('httpconf.php'))
428					->setArgument('filter_set', '1')
429					->setArgument('filter_hostids', [$db_host['hostid']])
430			),
431			CViewHelper::showNum($db_host['httpTests'])
432		]);
433		if ($current_element == 'web') {
434			$http_tests->addClass(ZBX_STYLE_SELECTED);
435		}
436		$content_menu->addItem($http_tests);
437	}
438	else {
439		$discovery_rule = (new CSpan())->addItem(
440			new CLink(
441				CHtml::encode($db_discovery_rule['name']),
442				'host_discovery.php?form=update&itemid='.$db_discovery_rule['itemid']
443			)
444		);
445
446		if ($current_element == 'discoveries') {
447			$discovery_rule->addClass(ZBX_STYLE_SELECTED);
448		}
449
450		$list->addItem([
451			(new CSpan())->addItem(
452				new CLink(_('Discovery list'), (new CUrl('host_discovery.php'))
453					->setArgument('filter_set', '1')
454					->setArgument('filter_hostids', [$db_host['hostid']])
455				)
456			),
457			'/',
458			$discovery_rule
459		]);
460
461		// item prototypes
462		$item_prototypes = new CSpan([
463			new CLink(_('Item prototypes'), 'disc_prototypes.php?parent_discoveryid='.$db_discovery_rule['itemid']),
464			CViewHelper::showNum($db_discovery_rule['items'])
465		]);
466		if ($current_element == 'items') {
467			$item_prototypes->addClass(ZBX_STYLE_SELECTED);
468		}
469		$content_menu->addItem($item_prototypes);
470
471		// trigger prototypes
472		$trigger_prototypes = new CSpan([
473			new CLink(_('Trigger prototypes'),
474				'trigger_prototypes.php?parent_discoveryid='.$db_discovery_rule['itemid']
475			),
476			CViewHelper::showNum($db_discovery_rule['triggers'])
477		]);
478		if ($current_element == 'triggers') {
479			$trigger_prototypes->addClass(ZBX_STYLE_SELECTED);
480		}
481		$content_menu->addItem($trigger_prototypes);
482
483		// graph prototypes
484		$graph_prototypes = new CSpan([
485			new CLink(_('Graph prototypes'), 'graphs.php?parent_discoveryid='.$db_discovery_rule['itemid']),
486			CViewHelper::showNum($db_discovery_rule['graphs'])
487		]);
488		if ($current_element == 'graphs') {
489			$graph_prototypes->addClass(ZBX_STYLE_SELECTED);
490		}
491		$content_menu->addItem($graph_prototypes);
492
493		// host prototypes
494		if ($db_host['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
495			$host_prototypes = new CSpan([
496				new CLink(_('Host prototypes'), 'host_prototypes.php?parent_discoveryid='.$db_discovery_rule['itemid']),
497				CViewHelper::showNum($db_discovery_rule['hostPrototypes'])
498			]);
499			if ($current_element == 'hosts') {
500				$host_prototypes->addClass(ZBX_STYLE_SELECTED);
501			}
502			$content_menu->addItem($host_prototypes);
503		}
504	}
505
506	$list->addItem($content_menu);
507
508	return $list;
509}
510
511/**
512 * Create breadcrumbs header object with sysmap parents information.
513 *
514 * @param int    $sysmapid      Used as value for sysmaid in map link generation.
515 * @param string $name          Used as label for map link generation.
516 * @param int    $severity_min  Used as value for severity_min in map link generation.
517 *
518 * @return object
519 */
520function get_header_sysmap_table($sysmapid, $name, $severity_min) {
521	$list = (new CList())
522		->setAttribute('role', 'navigation')
523		->setAttribute('aria-label', _x('Hierarchy', 'screen reader'))
524		->addClass(ZBX_STYLE_OBJECT_GROUP)
525		->addClass(ZBX_STYLE_FILTER_BREADCRUMB)
526		->addItem([
527			(new CSpan())->addItem(new CLink(_('All maps'), new CUrl('sysmaps.php'))),
528			'/',
529			(new CSpan())
530				->addClass(ZBX_STYLE_SELECTED)
531				->addItem(
532					new CLink($name, (new CUrl('zabbix.php'))
533						->setArgument('action', 'map.view')
534						->setArgument('sysmapid', $sysmapid)
535						->setArgument('severity_min', $severity_min)
536					)
537				)
538		]);
539
540	// get map parent maps
541	$parent_sysmaps = get_parent_sysmaps($sysmapid);
542	if ($parent_sysmaps) {
543		$parent_maps = (new CList())
544			->setAttribute('role', 'navigation')
545			->setAttribute('aria-label', _('Upper level maps'))
546			->addClass(ZBX_STYLE_FILTER_BREADCRUMB)
547			->addClass(ZBX_STYLE_OBJECT_GROUP)
548			->addItem((new CSpan())->addItem(_('Upper level maps').':'));
549
550		foreach ($parent_sysmaps as $parent_sysmap) {
551			$parent_maps->addItem((new CSpan())->addItem(
552				new CLink($parent_sysmap['name'], (new CUrl('zabbix.php'))
553					->setArgument('action', 'map.view')
554					->setArgument('sysmapid', $parent_sysmap['sysmapid'])
555					->setArgument('severity_min', $severity_min)
556				))
557			);
558		}
559
560		return new CHorList([$list, $parent_maps]);
561	}
562
563	return $list;
564}
565
566/**
567 * Renders a form footer with the given buttons.
568 *
569 * @param CButtonInterface 		$main_button	main button that will be displayed on the left
570 * @param CButtonInterface[] 	$other_buttons
571 *
572 * @return CDiv
573 *
574 * @throws InvalidArgumentException	if an element of $other_buttons contain something other than CButtonInterface
575 */
576function makeFormFooter(CButtonInterface $main_button = null, array $other_buttons = []) {
577	foreach ($other_buttons as $other_button) {
578		$other_button->addClass(ZBX_STYLE_BTN_ALT);
579	}
580
581	if ($main_button !== null) {
582		array_unshift($other_buttons, $main_button);
583	}
584
585	return (new CList())
586		->addClass(ZBX_STYLE_TABLE_FORMS)
587		->addItem([
588			(new CDiv())->addClass(ZBX_STYLE_TABLE_FORMS_TD_LEFT),
589			(new CDiv($other_buttons))
590				->addClass(ZBX_STYLE_TABLE_FORMS_TD_RIGHT)
591				->addClass('tfoot-buttons')
592		]);
593}
594
595/**
596 * Returns zbx, snmp, jmx, ipmi availability status icons and the discovered host lifetime indicator.
597 *
598 * @param array $host		an array of host data
599 *
600 * @return CDiv
601 */
602function getHostAvailabilityTable($host) {
603	$container = (new CDiv())->addClass(ZBX_STYLE_STATUS_CONTAINER);
604
605	foreach (['zbx' => '', 'snmp' => 'snmp_', 'jmx' => 'jmx_', 'ipmi' => 'ipmi_'] as $type => $prefix) {
606		switch ($host[$prefix.'available']) {
607			case HOST_AVAILABLE_TRUE:
608				$ai = (new CSpan($type))->addClass(ZBX_STYLE_STATUS_GREEN);
609				break;
610			case HOST_AVAILABLE_FALSE:
611				$ai = (new CSpan($type))->addClass(ZBX_STYLE_STATUS_RED);
612
613				if ($host[$prefix.'error'] !== '') {
614					$ai
615						->addClass(ZBX_STYLE_CURSOR_POINTER)
616						->setHint($host[$prefix.'error'], ZBX_STYLE_RED);
617				}
618
619				break;
620			case HOST_AVAILABLE_UNKNOWN:
621				$ai = (new CSpan($type))->addClass(ZBX_STYLE_STATUS_GREY);
622				break;
623		}
624		$container->addItem($ai);
625	}
626
627	return $container;
628}
629
630/**
631 * Returns the discovered host group lifetime indicator.
632 *
633 * @param string $current_time	current Unix timestamp
634 * @param array  $ts_delete		deletion timestamp of the host group
635 *
636 * @return CDiv
637 */
638function getHostGroupLifetimeIndicator($current_time, $ts_delete) {
639	// Check if the element should've been deleted in the past.
640	if ($current_time > $ts_delete) {
641		$warning = _(
642			'The host group is not discovered anymore and will be deleted the next time discovery rule is processed.'
643		);
644	}
645	else {
646		$warning = _s(
647			'The host group is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
648			zbx_date2age($current_time, $ts_delete),
649			zbx_date2str(DATE_FORMAT, $ts_delete),
650			zbx_date2str(TIME_FORMAT, $ts_delete)
651		);
652	}
653
654	return makeWarningIcon($warning);
655}
656
657/**
658 * Returns the discovered host lifetime indicator.
659 *
660 * @param string $current_time	current Unix timestamp
661 * @param array  $ts_delete		deletion timestamp of the host
662 *
663 * @return CDiv
664 */
665function getHostLifetimeIndicator($current_time, $ts_delete) {
666	// Check if the element should've been deleted in the past.
667	if ($current_time > $ts_delete) {
668		$warning = _(
669			'The host is not discovered anymore and will be deleted the next time discovery rule is processed.'
670		);
671	}
672	else {
673		$warning = _s(
674			'The host is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
675			zbx_date2age($current_time, $ts_delete),
676			zbx_date2str(DATE_FORMAT, $ts_delete),
677			zbx_date2str(TIME_FORMAT, $ts_delete)
678		);
679	}
680
681	return makeWarningIcon($warning);
682}
683
684/**
685 * Returns the discovered application lifetime indicator.
686 *
687 * @param string $current_time	current Unix timestamp
688 * @param array  $ts_delete		deletion timestamp of the application
689 *
690 * @return CDiv
691 */
692function getApplicationLifetimeIndicator($current_time, $ts_delete) {
693	// Check if the element should've been deleted in the past.
694	if ($current_time > $ts_delete) {
695		$warning = _(
696			'The application is not discovered anymore and will be deleted the next time discovery rule is processed.'
697		);
698	}
699	else {
700		$warning = _s(
701			'The application is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
702			zbx_date2age($current_time, $ts_delete),
703			zbx_date2str(DATE_FORMAT, $ts_delete),
704			zbx_date2str(TIME_FORMAT, $ts_delete)
705		);
706	}
707
708	return makeWarningIcon($warning);
709}
710
711/**
712 * Returns the discovered graph lifetime indicator.
713 *
714 * @param string $current_time	current Unix timestamp
715 * @param array  $ts_delete		deletion timestamp of the graph
716 *
717 * @return CDiv
718 */
719function getGraphLifetimeIndicator($current_time, $ts_delete) {
720	// Check if the element should've been deleted in the past.
721	if ($current_time > $ts_delete) {
722		$warning = _(
723			'The graph is not discovered anymore and will be deleted the next time discovery rule is processed.'
724		);
725	}
726	else {
727		$warning = _s(
728			'The graph is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
729			zbx_date2age($current_time, $ts_delete),
730			zbx_date2str(DATE_FORMAT, $ts_delete),
731			zbx_date2str(TIME_FORMAT, $ts_delete)
732		);
733	}
734
735	return makeWarningIcon($warning);
736}
737
738/**
739 * Returns the discovered trigger lifetime indicator.
740 *
741 * @param string $current_time	current Unix timestamp
742 * @param array  $ts_delete		deletion timestamp of the trigger
743 *
744 * @return CDiv
745 */
746function getTriggerLifetimeIndicator($current_time, $ts_delete) {
747	// Check if the element should've been deleted in the past.
748	if ($current_time > $ts_delete) {
749		$warning = _(
750			'The trigger is not discovered anymore and will be deleted the next time discovery rule is processed.'
751		);
752	}
753	else {
754		$warning = _s(
755			'The trigger is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
756			zbx_date2age($current_time, $ts_delete),
757			zbx_date2str(DATE_FORMAT, $ts_delete),
758			zbx_date2str(TIME_FORMAT, $ts_delete)
759		);
760	}
761
762	return makeWarningIcon($warning);
763}
764
765/**
766 * Returns the discovered item lifetime indicator.
767 *
768 * @param string $current_time	current Unix timestamp
769 * @param array  $ts_delete		deletion timestamp of the item
770 *
771 * @return CDiv
772 */
773function getItemLifetimeIndicator($current_time, $ts_delete) {
774	// Check if the element should've been deleted in the past.
775	if ($current_time > $ts_delete) {
776		$warning = _(
777			'The item is not discovered anymore and will be deleted the next time discovery rule is processed.'
778		);
779	}
780	else {
781		$warning = _s(
782			'The item is not discovered anymore and will be deleted in %1$s (on %2$s at %3$s).',
783			zbx_date2age($current_time, $ts_delete),
784			zbx_date2str(DATE_FORMAT, $ts_delete),
785			zbx_date2str(TIME_FORMAT, $ts_delete)
786		);
787	}
788
789	return makeWarningIcon($warning);
790}
791
792function makeServerStatusOutput() {
793	return (new CTag('output', true))
794		->setId('msg-global-footer')
795		->addClass(ZBX_STYLE_MSG_GLOBAL_FOOTER)
796		->addClass(ZBX_STYLE_MSG_WARNING);
797}
798
799/**
800* Make logo of the specified type.
801*
802* @param int $type  LOGO_TYPE_NORMAL | LOGO_TYPE_SIDEBAR | LOGO_TYPE_SIDEBAR_COMPACT.
803*
804* @return CTag
805*/
806function makeLogo(int $type): ?CTag {
807	static $zabbix_logo_classes = [
808		LOGO_TYPE_NORMAL => ZBX_STYLE_ZABBIX_LOGO,
809		LOGO_TYPE_SIDEBAR => ZBX_STYLE_ZABBIX_SIDEBAR_LOGO,
810		LOGO_TYPE_SIDEBAR_COMPACT => ZBX_STYLE_ZABBIX_SIDEBAR_LOGO_COMPACT
811	];
812
813	$brand_logo = CBrandHelper::getLogo($type);
814
815	if ($brand_logo !== null) {
816		return (new CImg($brand_logo));
817	}
818	else {
819		return (new CDiv())->addClass($zabbix_logo_classes[$type]);
820	}
821}
822
823/**
824 * Renders a page footer.
825 *
826 * @param bool $with_version
827 *
828 * @return CDiv
829 */
830function makePageFooter($with_version = true) {
831	return (new CTag('footer', true, CBrandHelper::getFooterContent($with_version)))
832		->setAttribute('role', 'contentinfo');
833}
834
835/**
836 * Get drop-down submenu item list for the Administration->General section.
837 *
838 * @return array  Menu definition for CWidget::setTitleSubmenu.
839 */
840function getAdministrationGeneralSubmenu() {
841	$gui_url = (new CUrl('zabbix.php'))
842		->setArgument('action', 'gui.edit')
843		->getUrl();
844
845	$autoreg_url = (new CUrl('zabbix.php'))
846		->setArgument('action', 'autoreg.edit')
847		->getUrl();
848
849	$housekeeping_url = (new CUrl('zabbix.php'))
850		->setArgument('action', 'housekeeping.edit')
851		->getUrl();
852
853	$image_url = (new CUrl('zabbix.php'))
854		->setArgument('action', 'image.list')
855		->getUrl();
856
857	$iconmap_url = (new CUrl('zabbix.php'))
858		->setArgument('action', 'iconmap.list')
859		->getUrl();
860
861	$regex_url = (new CUrl('zabbix.php'))
862		->setArgument('action', 'regex.list')
863		->getUrl();
864
865	$macros_url = (new CUrl('zabbix.php'))
866		->setArgument('action', 'macros.edit')
867		->getUrl();
868
869	$valuemap_url = (new CUrl('zabbix.php'))
870		->setArgument('action', 'valuemap.list')
871		->getUrl();
872
873	$workingtime_url = (new CUrl('zabbix.php'))
874		->setArgument('action', 'workingtime.edit')
875		->getUrl();
876
877	$trigseverity_url = (new CUrl('zabbix.php'))
878		->setArgument('action', 'trigseverity.edit')
879		->getUrl();
880
881	$trigdisplay_url = (new CUrl('zabbix.php'))
882		->setArgument('action', 'trigdisplay.edit')
883		->getUrl();
884
885	$modules_url = (new CUrl('zabbix.php'))
886		->setArgument('action', 'module.list')
887		->getUrl();
888
889	$miscconfig_url = (new CUrl('zabbix.php'))
890		->setArgument('action', 'miscconfig.edit')
891		->getUrl();
892
893	return [
894		'main_section' => [
895			'items' => [
896				$gui_url          => _('GUI'),
897				$autoreg_url      => _('Autoregistration'),
898				$housekeeping_url => _('Housekeeping'),
899				$image_url        => _('Images'),
900				$iconmap_url      => _('Icon mapping'),
901				$regex_url        => _('Regular expressions'),
902				$macros_url       => _('Macros'),
903				$valuemap_url     => _('Value mapping'),
904				$workingtime_url  => _('Working time'),
905				$trigseverity_url => _('Trigger severities'),
906				$trigdisplay_url  => _('Trigger displaying options'),
907				$modules_url      => _('Modules'),
908				$miscconfig_url   => _('Other')
909			]
910		]
911	];
912}
913
914/**
915 * Renders an icon list.
916 *
917 * @param array $info_icons  The list of information icons.
918 *
919 * @return CDiv|string
920 */
921function makeInformationList($info_icons) {
922	return $info_icons ? (new CDiv($info_icons))->addClass(ZBX_STYLE_REL_CONTAINER) : '';
923}
924
925/**
926 * Renders an information icon like green [i] with message.
927 *
928 * @param string $message
929 *
930 * @return CSpan
931 */
932function makeInformationIcon($message) {
933	return (new CSpan())
934		->addClass(ZBX_STYLE_ICON_INFO)
935		->addClass(ZBX_STYLE_STATUS_GREEN)
936		->setHint($message, ZBX_STYLE_HINTBOX_WRAP);
937}
938
939/**
940 * Renders an icon for host in maintenance.
941 *
942 * @param int    $type         Type of the maintenance.
943 * @param string $name         Name of the maintenance.
944 * @param string $description  Description of the maintenance.
945 *
946 * @return CSpan
947 */
948function makeMaintenanceIcon($type, $name, $description) {
949	$hint = $name.' ['.($type
950		? _('Maintenance without data collection')
951		: _('Maintenance with data collection')).']';
952
953	if ($description !== '') {
954		$hint .= "\n".$description;
955	}
956
957	return (new CSpan())
958		->addClass(ZBX_STYLE_ICON_MAINT)
959		->addClass(ZBX_STYLE_CURSOR_POINTER)
960		->setHint($hint);
961}
962
963/**
964 * Renders an icon for suppressed problem.
965 *
966 * @param array  $icon_data
967 * @param string $icon_data[]['suppress_until']    Time until the problem is suppressed.
968 * @param string $icon_data[]['maintenance_name']  Name of the maintenance.
969 *
970 * @return CSpan
971 */
972function makeSuppressedProblemIcon(array $icon_data) {
973	$suppress_until = max(zbx_objectValues($icon_data, 'suppress_until'));
974
975	CArrayHelper::sort($icon_data, ['maintenance_name']);
976	$maintenance_names = implode(', ', zbx_objectValues($icon_data, 'maintenance_name'));
977
978	return (new CSpan())
979		->addClass(ZBX_STYLE_ICON_INVISIBLE)
980		->addClass(ZBX_STYLE_CURSOR_POINTER)
981		->setHint(
982			_s('Suppressed till: %1$s', ($suppress_until < strtotime('tomorrow'))
983				? zbx_date2str(TIME_FORMAT, $suppress_until)
984				: zbx_date2str(DATE_TIME_FORMAT, $suppress_until)
985			).
986			"\n".
987			_s('Maintenance: %1$s', $maintenance_names)
988		);
989}
990
991/**
992 * Renders an action icon.
993 *
994 * @param array  $icon_data
995 * @param string $icon_data[icon]    Icon style.
996 * @param array  $icon_data[hint]    Hintbox content (optional).
997 * @param bool   $icon_data[button]  Use button element (optional).
998 * @param int    $icon_data[num]     Number displayed over the icon (optional).
999 *
1000 * @return CTag  Returns CSpan or CButton depending on boolean $icon_data['button'] parameter
1001 */
1002function makeActionIcon(array $icon_data): CTag {
1003
1004	if (array_key_exists('button', $icon_data) && $icon_data['button']) {
1005		$icon = (new CButton(null))->addClass($icon_data['icon']);
1006	}
1007	else {
1008		$icon = (new CSpan())->addClass($icon_data['icon']);
1009	}
1010
1011	if (array_key_exists('num', $icon_data)) {
1012		if ($icon_data['num'] > 99) {
1013			$icon_data['num'] = '99+';
1014		}
1015		$icon->setAttribute('data-count', $icon_data['num']);
1016	}
1017
1018	if (array_key_exists('hint', $icon_data)) {
1019		$icon
1020			->addClass(ZBX_STYLE_CURSOR_POINTER)
1021			->setHint($icon_data['hint'], '', true, 'max-width: '.ZBX_ACTIONS_POPUP_MAX_WIDTH.'px;');
1022	}
1023	elseif (array_key_exists('title', $icon_data)) {
1024		$icon->setTitle($icon_data['title']);
1025	}
1026
1027	if (array_key_exists('aria-label', $icon_data)) {
1028		$icon
1029			->addItem($icon_data['aria-label'])
1030			->addClass(ZBX_STYLE_INLINE_SR_ONLY);
1031	}
1032
1033	return $icon;
1034}
1035
1036/**
1037 * Renders an icon for a description.
1038 *
1039 * @param string $description
1040 *
1041 * @return CSpan
1042 */
1043function makeDescriptionIcon($description) {
1044	return (new CSpan())
1045		->addClass(ZBX_STYLE_ICON_DESCRIPTION)
1046		->addClass(ZBX_STYLE_CURSOR_POINTER)
1047		->setHint(zbx_str2links($description), ZBX_STYLE_HINTBOX_WRAP);
1048}
1049
1050/**
1051 * Renders an error icon like red [i] with error message
1052 *
1053 * @param string $error
1054 *
1055 * @return CSpan
1056 */
1057function makeErrorIcon($error) {
1058	return (new CSpan())
1059		->addClass(ZBX_STYLE_ICON_INFO)
1060		->addClass(ZBX_STYLE_STATUS_RED)
1061		->setHint($error, ZBX_STYLE_HINTBOX_WRAP." ".ZBX_STYLE_RED);
1062}
1063
1064/**
1065 * Renders an unknown icon like grey [i] with error message
1066 *
1067 * @param string $error
1068 *
1069 * @return CSpan
1070 */
1071function makeUnknownIcon($error) {
1072	return (new CSpan())
1073		->addClass(ZBX_STYLE_ICON_INFO)
1074		->addClass(ZBX_STYLE_STATUS_DARK_GREY)
1075		->setHint($error, ZBX_STYLE_HINTBOX_WRAP." ".ZBX_STYLE_RED);
1076}
1077
1078/**
1079 * Renders a warning icon like yellow [i] with error message
1080 *
1081 * @param string $error
1082 *
1083 * @return CSpan
1084 */
1085function makeWarningIcon($error) {
1086	return (new CSpan())
1087		->addClass(ZBX_STYLE_ICON_INFO)
1088		->addClass(ZBX_STYLE_STATUS_YELLOW)
1089		->setHint($error, ZBX_STYLE_HINTBOX_WRAP);
1090}
1091
1092/**
1093 * Returns css for trigger severity backgrounds.
1094 *
1095 * @param array $config
1096 * @param array $config[severity_color_0]
1097 * @param array $config[severity_color_1]
1098 * @param array $config[severity_color_2]
1099 * @param array $config[severity_color_3]
1100 * @param array $config[severity_color_4]
1101 * @param array $config[severity_color_5]
1102 *
1103 * @return string
1104 */
1105function getTriggerSeverityCss($config) {
1106	$css = '';
1107
1108	$severities = [
1109		ZBX_STYLE_NA_BG => $config['severity_color_0'],
1110		ZBX_STYLE_INFO_BG => $config['severity_color_1'],
1111		ZBX_STYLE_WARNING_BG => $config['severity_color_2'],
1112		ZBX_STYLE_AVERAGE_BG => $config['severity_color_3'],
1113		ZBX_STYLE_HIGH_BG => $config['severity_color_4'],
1114		ZBX_STYLE_DISASTER_BG => $config['severity_color_5']
1115	];
1116
1117	foreach ($severities as $class => $color) {
1118		$css .= '.'.$class.', .'.$class.' input[type="radio"]:checked + label, .'.$class.':before, .flh-'.$class.
1119			', .status-'.$class.', .status-'.$class.':before { background-color: #'.$color.' }'."\n";
1120	}
1121
1122	return $css;
1123}
1124
1125/**
1126 * Returns css for trigger status colors, if those are customized.
1127 *
1128 * @param array $config
1129 * @param array $config[custom_color]
1130 * @param array $config[problem_unack_color]
1131 * @param array $config[problem_ack_color]
1132 * @param array $config[ok_unack_color]
1133 * @param array $config[ok_ack_color]
1134 *
1135 * @return string
1136 */
1137function getTriggerStatusCss($config) {
1138	$css = '';
1139
1140	if ($config['custom_color'] == EVENT_CUSTOM_COLOR_ENABLED) {
1141		$event_statuses = [
1142			ZBX_STYLE_PROBLEM_UNACK_FG => $config['problem_unack_color'],
1143			ZBX_STYLE_PROBLEM_ACK_FG => $config['problem_ack_color'],
1144			ZBX_STYLE_OK_UNACK_FG => $config['ok_unack_color'],
1145			ZBX_STYLE_OK_ACK_FG => $config['ok_ack_color']
1146		];
1147
1148		foreach ($event_statuses as $class => $color) {
1149			$css .= '.' . $class . ' {color: #' . $color . ';}' . "\n";
1150		}
1151	}
1152
1153	return $css;
1154}
1155