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