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 sysmap_element_types($type = null) {
23	$types = [
24		SYSMAP_ELEMENT_TYPE_HOST => _('Host'),
25		SYSMAP_ELEMENT_TYPE_HOST_GROUP => _('Host group'),
26		SYSMAP_ELEMENT_TYPE_TRIGGER => _('Trigger'),
27		SYSMAP_ELEMENT_TYPE_MAP => _('Map'),
28		SYSMAP_ELEMENT_TYPE_IMAGE => _('Image')
29	];
30
31	if (is_null($type)) {
32		natsort($types);
33		return $types;
34	}
35	elseif (isset($types[$type])) {
36		return $types[$type];
37	}
38	else {
39		return _('Unknown');
40	}
41}
42
43function sysmapElementLabel($label = null) {
44	$labels = [
45		MAP_LABEL_TYPE_LABEL => _('Label'),
46		MAP_LABEL_TYPE_IP => _('IP address'),
47		MAP_LABEL_TYPE_NAME => _('Element name'),
48		MAP_LABEL_TYPE_STATUS => _('Status only'),
49		MAP_LABEL_TYPE_NOTHING => _('Nothing'),
50		MAP_LABEL_TYPE_CUSTOM => _('Custom label')
51	];
52
53	if (is_null($label)) {
54		return $labels;
55	}
56	elseif (isset($labels[$label])) {
57		return $labels[$label];
58	}
59	else {
60		return false;
61	}
62}
63
64/**
65 * Get actions (data for popup menu) for map elements.
66 *
67 * @param array $sysmap
68 * @param array $sysmap['selements']
69 * @param array $options                    Options used to retrieve actions.
70 * @param int   $options['severity_min']    Minimal severity used.
71 *
72 * @return array
73 */
74function getActionsBySysmap(array $sysmap, array $options = []) {
75	$actions = [];
76	$severity_min = array_key_exists('severity_min', $options)
77		? $options['severity_min']
78		: TRIGGER_SEVERITY_NOT_CLASSIFIED;
79
80	foreach ($sysmap['selements'] as $selementid => $elem) {
81		if ($elem['permission'] < PERM_READ) {
82			continue;
83		}
84
85		$hostid = ($elem['elementtype_orig'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP
86				&& $elem['elementsubtype_orig'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS)
87			? $elem['elements'][0]['hostid']
88			: 0;
89
90		$map = CMenuPopupHelper::getMapElement($sysmap['sysmapid'], $elem, $severity_min, $hostid);
91
92		$actions[$selementid] = CJs::encodeJson($map);
93	}
94
95	return $actions;
96}
97
98function get_png_by_selement($info) {
99	$image = get_image_by_imageid($info['iconid']);
100
101	return $image['image'] ? imagecreatefromstring($image['image']) : get_default_image();
102}
103
104function get_map_elements($db_element, &$elements) {
105	switch ($db_element['elementtype']) {
106		case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
107			$elements['hosts_groups'][] = $db_element['elements'][0]['groupid'];
108			break;
109		case SYSMAP_ELEMENT_TYPE_HOST:
110			$elements['hosts'][] = $db_element['elements'][0]['hostid'];
111			break;
112		case SYSMAP_ELEMENT_TYPE_TRIGGER:
113			foreach ($db_element['elements'] as $db_element) {
114				$elements['triggers'][] = $db_element['triggerid'];
115			}
116			break;
117		case SYSMAP_ELEMENT_TYPE_MAP:
118			$map = API::Map()->get([
119				'output' => [],
120				'selectSelements' => ['selementid', 'elements', 'elementtype'],
121				'sysmapids' => $db_element['elements'][0]['sysmapid'],
122				'nopermissions' => true
123			]);
124
125			if ($map) {
126				$map = reset($map);
127
128				foreach ($map['selements'] as $db_mapelement) {
129					get_map_elements($db_mapelement, $elements);
130				}
131			}
132			break;
133	}
134}
135
136/**
137 * Adds names to elements. Adds expression for SYSMAP_ELEMENT_TYPE_TRIGGER elements.
138 *
139 * @param array $selements
140 * @param array $selements[]['elements']
141 * @param int   $selements[]['elementtype']
142 * @param int   $selements[]['iconid_off']
143 * @param int   $selements[]['permission']
144 */
145function addElementNames(array &$selements) {
146	$hostids = [];
147	$triggerids = [];
148	$sysmapids = [];
149	$groupids = [];
150	$imageids = [];
151
152	foreach ($selements as $selement) {
153		if ($selement['permission'] < PERM_READ) {
154			continue;
155		}
156
157		switch ($selement['elementtype']) {
158			case SYSMAP_ELEMENT_TYPE_HOST:
159				$hostids[$selement['elements'][0]['hostid']] = $selement['elements'][0]['hostid'];
160				break;
161
162			case SYSMAP_ELEMENT_TYPE_MAP:
163				$sysmapids[$selement['elements'][0]['sysmapid']] = $selement['elements'][0]['sysmapid'];
164				break;
165
166			case SYSMAP_ELEMENT_TYPE_TRIGGER:
167				foreach ($selement['elements'] as $element) {
168					$triggerids[$element['triggerid']] = $element['triggerid'];
169				}
170				break;
171
172			case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
173				$groupids[$selement['elements'][0]['groupid']] = $selement['elements'][0]['groupid'];
174				break;
175
176			case SYSMAP_ELEMENT_TYPE_IMAGE:
177				$imageids[$selement['iconid_off']] = $selement['iconid_off'];
178				break;
179		}
180	}
181
182	$hosts = $hostids
183		? API::Host()->get([
184			'output' => ['name'],
185			'hostids' => $hostids,
186			'preservekeys' => true
187		])
188		: [];
189
190	$maps = $sysmapids
191		? API::Map()->get([
192			'output' => ['name'],
193			'sysmapids' => $sysmapids,
194			'preservekeys' => true
195		])
196		: [];
197
198	$triggers = $triggerids
199		? API::Trigger()->get([
200			'output' => ['description', 'expression', 'priority'],
201			'selectHosts' => ['name'],
202			'triggerids' => $triggerids,
203			'preservekeys' => true
204		])
205		: [];
206	$triggers = CMacrosResolverHelper::resolveTriggerNames($triggers);
207
208	$groups = $groupids
209		? API::HostGroup()->get([
210			'output' => ['name'],
211			'hostgroupids' => $groupids,
212			'preservekeys' => true
213		])
214		: [];
215
216	$images = $imageids
217		? API::Image()->get([
218			'output' => ['name'],
219			'imageids' => $imageids,
220			'preservekeys' => true
221		])
222		: [];
223
224	foreach ($selements as $snum => &$selement) {
225		if ($selement['permission'] < PERM_READ) {
226			continue;
227		}
228
229		switch ($selement['elementtype']) {
230			case SYSMAP_ELEMENT_TYPE_HOST:
231				$selements[$snum]['elements'][0]['elementName'] = $hosts[$selement['elements'][0]['hostid']]['name'];
232				break;
233
234			case SYSMAP_ELEMENT_TYPE_MAP:
235				$selements[$snum]['elements'][0]['elementName'] = $maps[$selement['elements'][0]['sysmapid']]['name'];
236				break;
237
238			case SYSMAP_ELEMENT_TYPE_TRIGGER:
239				foreach ($selement['elements'] as $enum => &$element) {
240					if (array_key_exists($element['triggerid'], $triggers)) {
241						$trigger = $triggers[$element['triggerid']];
242						$element['elementName'] = $trigger['hosts'][0]['name'].NAME_DELIMITER.$trigger['description'];
243						$element['elementExpressionTrigger'] = $trigger['expression'];
244						$element['priority'] = $trigger['priority'];
245					}
246					else {
247						unset($selement['elements'][$enum]);
248					}
249				}
250				unset($element);
251				$selement['elements'] = array_values($selement['elements']);
252				break;
253
254			case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
255				$selements[$snum]['elements'][0]['elementName'] = $groups[$selement['elements'][0]['groupid']]['name'];
256				break;
257
258			case SYSMAP_ELEMENT_TYPE_IMAGE:
259				if (array_key_exists($selement['iconid_off'], $images)) {
260					$selements[$snum]['elements'][0]['elementName'] = $images[$selement['iconid_off']]['name'];
261				}
262				break;
263		}
264	}
265	unset($selement);
266}
267
268/**
269 * Returns trigger element icon rendering parameters.
270 *
271 * @param $selement
272 * @param $i
273 * @param $showUnack    map "problem display" parameter
274 *
275 * @return array
276 */
277function getTriggersInfo($selement, $i, $showUnack) {
278	$info = [
279		'latelyChanged' => $i['latelyChanged'],
280		'ack' => $i['ack'],
281		'priority' => $i['priority'],
282		'info' => [],
283		'iconid' => $selement['iconid_off']
284	];
285
286	if ($i['problem'] && ($i['problem_unack'] && $showUnack == EXTACK_OPTION_UNACK
287			|| in_array($showUnack, [EXTACK_OPTION_ALL, EXTACK_OPTION_BOTH]))) {
288		// Number of problems.
289		if ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER) {
290			if ($i['problem'] == 1) {
291				$msg = _('PROBLEM');
292			}
293			else {
294				$msg = $i['problem'].' '._('Problems');
295			}
296		}
297		// Expand single problem.
298		elseif ($i['expandproblem'] == SYSMAP_SINGLE_PROBLEM) {
299			$msg = $i['problem_title'];
300		}
301		// Number of problems and expand most critical one.
302		elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER_CRITICAL) {
303			$msg = $i['problem_title'];
304
305			if ($i['problem'] > 1) {
306				$msg .= "\n".$i['problem'].' '._('Problems');
307			}
308		}
309
310		$info['info']['unack'] = [
311			'msg' => $msg,
312			'color' => getSelementLabelColor(true, !$i['problem_unack'])
313		];
314
315		if (!array_key_exists('maintenance_title', $i)) {
316			$info['iconid'] = $selement['iconid_on'];
317			$info['icon_type'] = SYSMAP_ELEMENT_ICON_ON;
318
319			return $info;
320		}
321	}
322
323	if (array_key_exists('maintenance_title', $i)) {
324		$info['iconid'] = $selement['iconid_maintenance'];
325		$info['icon_type'] = SYSMAP_ELEMENT_ICON_MAINTENANCE;
326		$info['info']['maintenance'] = [
327			'msg' => _('MAINTENANCE').' ('.$i['maintenance_title'].')',
328			'color' => 'EE9600'
329		];
330	}
331	elseif ($i['trigger_disabled']) {
332		$info['iconid'] = $selement['iconid_disabled'];
333		$info['icon_type'] = SYSMAP_ELEMENT_ICON_DISABLED;
334		$info['info']['status'] = [
335			'msg' => _('DISABLED'),
336			'color' => '960000'
337		];
338	}
339	else {
340		$info['iconid'] = $selement['iconid_off'];
341		$info['icon_type'] = SYSMAP_ELEMENT_ICON_OFF;
342		$info['info']['ok'] = [
343			'msg' => _('OK'),
344			'color' => getSelementLabelColor(false, $i['ack'])
345		];
346	}
347
348	return $info;
349}
350
351/**
352 * Returns host element icon rendering parameters.
353 *
354 * @param $selement
355 * @param $i
356 * @param $show_unack    map "problem display" parameter
357 *
358 * @return array
359 */
360function getHostsInfo($selement, $i, $show_unack) {
361	$info = [
362		'latelyChanged' => $i['latelyChanged'],
363		'ack' => $i['ack'],
364		'priority' => $i['priority'],
365		'info' => [],
366		'iconid' => $selement['iconid_off']
367	];
368	$hasProblem = false;
369
370	if ($i['problem']) {
371		if (in_array($show_unack, [EXTACK_OPTION_ALL, EXTACK_OPTION_BOTH])) {
372			// Number of problems.
373			if ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER) {
374				if ($i['problem'] == 1) {
375					$msg = _('PROBLEM');
376				}
377				else {
378					$msg = $i['problem'].' '._('Problems');
379				}
380			}
381			// Expand single problem.
382			elseif ($i['expandproblem'] == SYSMAP_SINGLE_PROBLEM) {
383				$msg = $i['problem_title'];
384			}
385			// Number of problems and expand most critical one.
386			elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER_CRITICAL) {
387				$msg = $i['problem_title'];
388
389				if ($i['problem'] > 1) {
390					$msg .= "\n".$i['problem'].' '._('Problems');
391				}
392			}
393
394			$info['info']['problem'] = [
395				'msg' => $msg,
396				'color' => getSelementLabelColor(true, !$i['problem_unack'])
397			];
398		}
399
400		if (in_array($show_unack, [EXTACK_OPTION_UNACK, EXTACK_OPTION_BOTH]) && $i['problem_unack']) {
401			$info['info']['unack'] = [
402				'msg' => $i['problem_unack'].' '._('Unacknowledged'),
403				'color' => getSelementLabelColor(true, false)
404			];
405		}
406
407		// set element to problem state if it has problem events
408		if ($info['info']) {
409			$info['iconid'] = $selement['iconid_on'];
410			$info['icon_type'] = SYSMAP_ELEMENT_ICON_ON;
411			$hasProblem = true;
412		}
413	}
414
415	if (array_key_exists('maintenance_title', $i)) {
416		$info['iconid'] = $selement['iconid_maintenance'];
417		$info['icon_type'] = SYSMAP_ELEMENT_ICON_MAINTENANCE;
418		$info['info']['maintenance'] = [
419			'msg' => _('MAINTENANCE').' ('.$i['maintenance_title'].')',
420			'color' => 'EE9600'
421		];
422	}
423	elseif ($i['disabled']) {
424		$info['iconid'] = $selement['iconid_disabled'];
425		$info['icon_type'] = SYSMAP_ELEMENT_ICON_DISABLED;
426		$info['info']['status'] = [
427			'msg' => _('DISABLED'),
428			'color' => '960000'
429		];
430	}
431	elseif (!$hasProblem) {
432		$info['iconid'] = $selement['iconid_off'];
433		$info['icon_type'] = SYSMAP_ELEMENT_ICON_OFF;
434		$info['info']['ok'] = [
435			'msg' => _('OK'),
436			'color' => getSelementLabelColor(false, $i['ack'])
437		];
438	}
439
440	return $info;
441}
442
443/**
444 * Returns host groups element icon rendering parameters.
445 *
446 * @param $selement
447 * @param $i
448 * @param $show_unack    map "problem display" parameter
449 *
450 * @return array
451 */
452function getHostGroupsInfo($selement, $i, $show_unack) {
453	$info = [
454		'latelyChanged' => $i['latelyChanged'],
455		'ack' => $i['ack'],
456		'priority' => $i['priority'],
457		'info' => [],
458		'iconid' => $selement['iconid_off']
459	];
460	$hasProblem = false;
461	$hasStatus = false;
462
463	if ($i['problem']) {
464		if (in_array($show_unack, [EXTACK_OPTION_ALL, EXTACK_OPTION_BOTH])) {
465			// Number of problems.
466			if ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER) {
467				if ($i['problem'] == 1) {
468					$msg = _('PROBLEM');
469				}
470				else {
471					$msg = $i['problem'].' '._('Problems');
472				}
473			}
474			// Expand single problem.
475			elseif ($i['expandproblem'] == SYSMAP_SINGLE_PROBLEM) {
476				$msg = $i['problem_title'];
477			}
478			// Number of problems and expand most critical one.
479			elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER_CRITICAL) {
480				$msg = $i['problem_title'];
481
482				if ($i['problem'] > 1) {
483					$msg .= "\n".$i['problem'].' '._('Problems');
484				}
485			}
486
487			$info['info']['problem'] = [
488				'msg' => $msg,
489				'color' => getSelementLabelColor(true, !$i['problem_unack'])
490			];
491		}
492
493		if (in_array($show_unack, [EXTACK_OPTION_UNACK, EXTACK_OPTION_BOTH]) && $i['problem_unack']) {
494			$info['info']['unack'] = [
495				'msg' => $i['problem_unack'].' '._('Unacknowledged'),
496				'color' => getSelementLabelColor(true, false)
497			];
498		}
499
500		// set element to problem state if it has problem events
501		if ($info['info']) {
502			$info['iconid'] = $selement['iconid_on'];
503			$info['icon_type'] = SYSMAP_ELEMENT_ICON_ON;
504			$hasProblem = true;
505		}
506	}
507
508	if ($i['maintenance']) {
509		$info['iconid'] = $selement['iconid_maintenance'];
510		$info['icon_type'] = SYSMAP_ELEMENT_ICON_MAINTENANCE;
511		$info['info']['maintenance'] = [
512			'msg' => $i['maintenance'].' '._('Maintenance'),
513			'color' => 'EE9600'
514		];
515		$hasStatus = true;
516	}
517
518	if (!$hasStatus && !$hasProblem) {
519		$info['icon_type'] = SYSMAP_ELEMENT_ICON_OFF;
520		$info['iconid'] = $selement['iconid_off'];
521		$info['info']['ok'] = [
522			'msg' => _('OK'),
523			'color' => getSelementLabelColor(false, $i['ack'])
524		];
525	}
526
527	return $info;
528}
529
530/**
531 * Returns maps groups element icon rendering parameters.
532 *
533 * @param $selement
534 * @param $i
535 * @param $show_unack    map "problem display" parameter
536 *
537 * @return array
538 */
539function getMapsInfo($selement, $i, $show_unack) {
540	$info = [
541		'latelyChanged' => $i['latelyChanged'],
542		'ack' => $i['ack'],
543		'priority' => $i['priority'],
544		'info' => [],
545		'iconid' => $selement['iconid_off']
546	];
547
548	$hasProblem = false;
549	$hasStatus = false;
550
551	if ($i['problem']) {
552		if (in_array($show_unack, [EXTACK_OPTION_ALL, EXTACK_OPTION_BOTH])) {
553			// Number of problems.
554			if ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER) {
555				if ($i['problem'] == 1) {
556					$msg = _('PROBLEM');
557				}
558				else {
559					$msg = $i['problem'].' '._('Problems');
560				}
561			}
562			// Expand single problem.
563			elseif ($i['expandproblem'] == SYSMAP_SINGLE_PROBLEM) {
564				$msg = $i['problem_title'];
565			}
566			// Number of problems and expand most critical one.
567			elseif ($i['expandproblem'] == SYSMAP_PROBLEMS_NUMBER_CRITICAL) {
568				$msg = $i['problem_title'];
569
570				if ($i['problem'] > 1) {
571					$msg .= "\n".$i['problem'].' '._('Problems');
572				}
573			}
574
575			$info['info']['problem'] = [
576				'msg' => $msg,
577				'color' => getSelementLabelColor(true, !$i['problem_unack'])
578			];
579		}
580
581		if (in_array($show_unack, [EXTACK_OPTION_UNACK, EXTACK_OPTION_BOTH]) && $i['problem_unack']) {
582			$info['info']['unack'] = [
583				'msg' => $i['problem_unack'].' '._('Unacknowledged'),
584				'color' => getSelementLabelColor(true, false)
585			];
586		}
587
588		if ($info['info']) {
589			$info['iconid'] = $selement['iconid_on'];
590			$info['icon_type'] = SYSMAP_ELEMENT_ICON_ON;
591			$hasProblem = true;
592		}
593	}
594
595	if ($i['maintenance']) {
596		if (!$hasProblem) {
597			$info['iconid'] = $selement['iconid_maintenance'];
598			$info['icon_type'] = SYSMAP_ELEMENT_ICON_MAINTENANCE;
599		}
600		$info['info']['maintenance'] = [
601			'msg' => $i['maintenance'].' '._('Maintenance'),
602			'color' => 'EE9600'
603		];
604		$hasStatus = true;
605	}
606
607	if (!$hasStatus && !$hasProblem) {
608		$info['icon_type'] = SYSMAP_ELEMENT_ICON_OFF;
609		$info['iconid'] = $selement['iconid_off'];
610		$info['info']['ok'] = [
611			'msg' => _('OK'),
612			'color' => getSelementLabelColor(false, $i['ack'])
613		];
614	}
615
616	return $info;
617}
618
619function getImagesInfo($selement) {
620	return [
621		'iconid' => $selement['iconid_off'],
622		'icon_type' => SYSMAP_ELEMENT_ICON_OFF,
623		'name' => _('Image'),
624		'latelyChanged' => false
625	];
626}
627
628/**
629 * Prepare map elements data.
630 * Calculate problem triggers and priorities. Populate map elements with automatic icon mapping, acknowledging and
631 * recent change markers.
632 *
633 * @param array $sysmap
634 * @param array $options
635 * @param int   $options['severity_min']  Minimum severity, default value is maximal (Disaster)
636 *
637 * @return array
638 */
639function getSelementsInfo(array $sysmap, array $options = []) {
640	if (!isset($options['severity_min'])) {
641		$options['severity_min'] = TRIGGER_SEVERITY_NOT_CLASSIFIED;
642	}
643
644	$triggerIdToSelementIds = [];
645	$subSysmapTriggerIdToSelementIds = [];
646	$hostGroupIdToSelementIds = [];
647	$hostIdToSelementIds = [];
648
649	if ($sysmap['sysmapid']) {
650		$iconMap = API::IconMap()->get([
651			'output' => API_OUTPUT_EXTEND,
652			'selectMappings' => API_OUTPUT_EXTEND,
653			'sysmapids' => $sysmap['sysmapid']
654		]);
655		$iconMap = reset($iconMap);
656	}
657	$hostsToGetInventories = [];
658
659	$selements = $sysmap['selements'];
660	$selementIdToSubSysmaps = [];
661	foreach ($selements as $selementId => &$selement) {
662		$selement['hosts'] = [];
663		$selement['triggers'] = [];
664
665		if ($selement['permission'] < PERM_READ) {
666			continue;
667		}
668
669		switch ($selement['elementtype']) {
670			case SYSMAP_ELEMENT_TYPE_MAP:
671				$sysmapIds = [$selement['elements'][0]['sysmapid']];
672
673				while (!empty($sysmapIds)) {
674					$subSysmaps = API::Map()->get([
675						'output' => ['sysmapid'],
676						'selectSelements' => ['elementtype', 'elements', 'application'],
677						'sysmapids' => $sysmapIds,
678						'preservekeys' => true
679					]);
680
681					if(!isset($selementIdToSubSysmaps[$selementId])) {
682						$selementIdToSubSysmaps[$selementId] = [];
683					}
684					$selementIdToSubSysmaps[$selementId] += $subSysmaps;
685
686					$sysmapIds = [];
687					foreach ($subSysmaps as $subSysmap) {
688						foreach ($subSysmap['selements'] as $subSysmapSelement) {
689							switch ($subSysmapSelement['elementtype']) {
690								case SYSMAP_ELEMENT_TYPE_MAP:
691									$sysmapIds[] = $subSysmapSelement['elements'][0]['sysmapid'];
692									break;
693								case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
694									$hostGroupIdToSelementIds[$subSysmapSelement['elements'][0]['groupid']][$selementId]
695										= $selementId;
696									break;
697								case SYSMAP_ELEMENT_TYPE_HOST:
698									$hostIdToSelementIds[$subSysmapSelement['elements'][0]['hostid']][$selementId]
699										= $selementId;
700									break;
701								case SYSMAP_ELEMENT_TYPE_TRIGGER:
702									foreach ($subSysmapSelement['elements'] as $element) {
703										$subSysmapTriggerIdToSelementIds[$element['triggerid']][$selementId]
704											= $selementId;
705									}
706									break;
707							}
708						}
709					}
710				}
711				break;
712			case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
713				$hostGroupId = $selement['elements'][0]['groupid'];
714				$hostGroupIdToSelementIds[$hostGroupId][$selementId] = $selementId;
715				break;
716			case SYSMAP_ELEMENT_TYPE_HOST:
717				$hostId = $selement['elements'][0]['hostid'];
718				$hostIdToSelementIds[$hostId][$selementId] = $selementId;
719
720				/**
721				 * If we have icon map applied, we need to get inventories for all hosts, where automatic icon
722				 * selection is enabled.
723				 */
724				if ($sysmap['iconmapid'] && $selement['use_iconmap']) {
725					$hostsToGetInventories[] = $hostId;
726				}
727				break;
728			case SYSMAP_ELEMENT_TYPE_TRIGGER:
729				foreach ($selement['elements'] as $element) {
730					$triggerIdToSelementIds[$element['triggerid']][$selementId] = $selementId;
731				}
732				break;
733		}
734	}
735	unset($selement);
736
737	// get host inventories
738	if ($sysmap['iconmapid']) {
739		$hostInventories = API::Host()->get([
740			'output' => ['hostid'],
741			'selectInventory' => API_OUTPUT_EXTEND,
742			'hostids' => $hostsToGetInventories,
743			'preservekeys' => true
744		]);
745	}
746
747	$allHosts = [];
748	if ($hostIdToSelementIds) {
749		$hosts = API::Host()->get([
750			'output' => ['name', 'status', 'maintenance_status', 'maintenanceid'],
751			'hostids' => array_keys($hostIdToSelementIds),
752			'preservekeys' => true
753		]);
754		$allHosts = array_merge($allHosts, $hosts);
755		foreach ($hosts as $hostId => $host) {
756			foreach ($hostIdToSelementIds[$hostId] as $selementId) {
757				$selements[$selementId]['hosts'][$hostId] = $hostId;
758			}
759		}
760	}
761
762	$hostsFromHostGroups = [];
763	if ($hostGroupIdToSelementIds) {
764		$hostsFromHostGroups = API::Host()->get([
765			'output' => ['name', 'status', 'maintenance_status', 'maintenanceid'],
766			'selectGroups' => ['groupid'],
767			'groupids' => array_keys($hostGroupIdToSelementIds),
768			'preservekeys' => true
769		]);
770
771		foreach ($hostsFromHostGroups as $hostId => $host) {
772			foreach ($host['groups'] as $group) {
773				$groupId = $group['groupid'];
774
775				if (isset($hostGroupIdToSelementIds[$groupId])) {
776					foreach ($hostGroupIdToSelementIds[$groupId] as $selementId) {
777						$selement =& $selements[$selementId];
778
779						$selement['hosts'][$hostId] = $hostId;
780
781						// Add hosts to hosts_map for trigger selection.
782						if (!isset($hostIdToSelementIds[$hostId])) {
783							$hostIdToSelementIds[$hostId] = [];
784						}
785						$hostIdToSelementIds[$hostId][$selementId] = $selementId;
786
787						unset($selement);
788					}
789				}
790			}
791		}
792
793		$allHosts = array_merge($allHosts, $hostsFromHostGroups);
794	}
795
796	$allHosts = zbx_toHash($allHosts, 'hostid');
797
798	// Get triggers data, triggers from current map and from submaps, select all.
799	if ($triggerIdToSelementIds || $subSysmapTriggerIdToSelementIds) {
800		$all_triggerid_to_selementids = [];
801
802		foreach ([$triggerIdToSelementIds, $subSysmapTriggerIdToSelementIds] as $triggerid_to_selementids) {
803			foreach ($triggerid_to_selementids as $triggerid => $selementids) {
804				if (!array_key_exists($triggerid, $all_triggerid_to_selementids)) {
805					$all_triggerid_to_selementids[$triggerid] = $selementids;
806				}
807				else {
808					$all_triggerid_to_selementids[$triggerid] += $selementids;
809				}
810			}
811		}
812
813		$triggers = API::Trigger()->get([
814			'output' => ['triggerid', 'status', 'value', 'priority', 'description', 'expression'],
815			'selectHosts' => ['maintenance_status', 'maintenanceid'],
816			'triggerids' => array_keys($all_triggerid_to_selementids),
817			'filter' => ['state' => null],
818			'preservekeys' => true
819		]);
820
821		$monitored_triggers = API::Trigger()->get([
822			'output' => [],
823			'triggerids' => array_keys($triggers),
824			'monitored' => true,
825			'skipDependent' => true,
826			'preservekeys' => true
827		]);
828
829		foreach ($triggers as $trigger) {
830			if (!array_key_exists($trigger['triggerid'], $monitored_triggers)) {
831				$trigger['status'] = TRIGGER_STATUS_DISABLED;
832			}
833
834			foreach ($all_triggerid_to_selementids[$trigger['triggerid']] as $belongs_to_sel) {
835				$selements[$belongs_to_sel]['triggers'][$trigger['triggerid']] = $trigger;
836			}
837		}
838		unset($triggers, $monitored_triggers);
839	}
840
841	$monitored_hostids = [];
842	foreach ($allHosts as $hostid => $host) {
843		if ($host['status'] == HOST_STATUS_MONITORED) {
844			$monitored_hostids[$hostid] = true;
845		}
846	}
847
848	// triggers from all hosts/hostgroups, skip dependent
849	if ($monitored_hostids) {
850		$triggers = API::Trigger()->get([
851			'output' => ['triggerid', 'status', 'value', 'priority', 'description', 'expression'],
852			'selectHosts' => ['hostid'],
853			'selectItems' => ['itemid'],
854			'hostids' => array_keys($monitored_hostids),
855			'filter' => ['state' => null],
856			'monitored' => true,
857			'skipDependent' => true,
858			'only_true' => true,
859			'preservekeys' => true
860		]);
861
862		foreach ($triggers as $trigger) {
863			foreach ($trigger['hosts'] as $host) {
864				if (array_key_exists($host['hostid'], $hostIdToSelementIds)) {
865					foreach ($hostIdToSelementIds[$host['hostid']] as $belongs_to_sel) {
866						$selements[$belongs_to_sel]['triggers'][$trigger['triggerid']] = $trigger;
867					}
868				}
869			}
870		}
871
872		$subSysmapHostApplicationFilters = getSelementHostApplicationFilters($selements, $selementIdToSubSysmaps,
873			$hostsFromHostGroups
874		);
875		$selements = filterSysmapTriggers($selements, $subSysmapHostApplicationFilters, $triggers,
876			$subSysmapTriggerIdToSelementIds
877		);
878	}
879
880	// Get problems by triggerids.
881	$triggerids = [];
882	foreach ($selements as $selement) {
883		foreach ($selement['triggers'] as $trigger) {
884			if ($trigger['status'] == TRIGGER_STATUS_ENABLED) {
885				$triggerids[$trigger['triggerid']] = true;
886			}
887		}
888	}
889
890	$problems = API::Problem()->get([
891		'output' => ['eventid', 'objectid', 'name', 'acknowledged', 'clock', 'r_clock', 'severity'],
892		'objectids' => array_keys($triggerids),
893		'recent' => true,
894		'suppressed' => ($sysmap['show_suppressed'] == ZBX_PROBLEM_SUPPRESSED_FALSE) ? false : null
895	]);
896
897	foreach ($selements as $snum => $selement) {
898		foreach ($problems as $problem) {
899			if (array_key_exists($problem['objectid'], $selement['triggers'])
900					&& $options['severity_min'] <= $problem['severity']) {
901				$selements[$snum]['triggers'][$problem['objectid']]['problems'][] = $problem;
902			}
903		}
904	}
905
906	$config = select_config();
907
908	$info = [];
909	foreach ($selements as $selementId => $selement) {
910		$i = [
911			'disabled' => 0,
912			'maintenance' => 0,
913			'problem' => 0,
914			'problem_unack' => 0,
915			'priority' => 0,
916			'trigger_disabled' => 0,
917			'latelyChanged' => false,
918			'ack' => true
919		];
920		$info[$selementId]['aria_label'] = '';
921
922		/*
923		 * If user has no rights to see the details of particular selement, add only info that is needed to render map
924		 * icons.
925		 */
926		if (PERM_READ > $selement['permission']) {
927			switch ($selement['elementtype']) {
928				case SYSMAP_ELEMENT_TYPE_MAP:
929					$info[$selementId] = getMapsInfo(['iconid_off' => $selement['iconid_off']], $i, null);
930					break;
931
932				case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
933					$info[$selementId] = getHostGroupsInfo(['iconid_off' => $selement['iconid_off']], $i, null);
934					break;
935
936				case SYSMAP_ELEMENT_TYPE_HOST:
937					$info[$selementId] = getHostsInfo(['iconid_off' => $selement['iconid_off']], $i, null);
938					break;
939
940				case SYSMAP_ELEMENT_TYPE_TRIGGER:
941					$info[$selementId] = getTriggersInfo(['iconid_off' => $selement['iconid_off']], $i, null);
942					break;
943
944				case SYSMAP_ELEMENT_TYPE_IMAGE:
945					$info[$selementId] = getImagesInfo(['iconid_off' => $selement['iconid_off']]);
946					break;
947			}
948
949			continue;
950		}
951
952		foreach ($selement['hosts'] as $hostId) {
953			$host = $allHosts[$hostId];
954			$last_hostid = $hostId;
955
956			if ($host['status'] == HOST_STATUS_NOT_MONITORED) {
957				$i['disabled']++;
958			}
959			elseif ($host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON) {
960				$i['maintenance']++;
961			}
962		}
963
964		$critical_problem = [];
965		$trigger_order = ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER)
966			? zbx_objectValues($selement['elements'], 'triggerid')
967			: [];
968		$lately_changed = 0;
969
970		foreach ($selement['triggers'] as $trigger) {
971			if ($trigger['status'] == TRIGGER_STATUS_DISABLED && $options['severity_min'] <= $trigger['priority']) {
972				$i['trigger_disabled']++;
973				continue;
974			}
975
976			if (array_key_exists('problems', $trigger)) {
977				foreach ($trigger['problems'] as $problem) {
978					if ($problem['r_clock'] == 0) {
979						$i['problem']++;
980
981						if ($problem['acknowledged'] == EVENT_NOT_ACKNOWLEDGED) {
982							$i['problem_unack']++;
983						}
984
985						if (!$critical_problem || $critical_problem['severity'] < $problem['severity']) {
986							$critical_problem = $problem;
987						}
988						elseif ($critical_problem['severity'] === $problem['severity']) {
989							if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) {
990								if ($problem['objectid'] === $critical_problem['objectid']
991										&& $critical_problem['eventid'] < $problem['eventid']) {
992									$critical_problem = $problem;
993								}
994								elseif (array_search($critical_problem['objectid'], $trigger_order)
995										> array_search($problem['objectid'], $trigger_order)) {
996									$critical_problem = $problem;
997								}
998							}
999							elseif ($critical_problem['eventid'] < $problem['eventid']) {
1000								$critical_problem = $problem;
1001							}
1002						}
1003					}
1004
1005					if ($problem['r_clock'] > $lately_changed) {
1006						$lately_changed = $problem['r_clock'];
1007					}
1008					elseif ($problem['clock'] > $lately_changed) {
1009						$lately_changed = $problem['clock'];
1010					}
1011				}
1012			}
1013
1014			$i['latelyChanged'] |= ((time() - $lately_changed) < timeUnitToSeconds($config['blink_period']));
1015		}
1016
1017		if ($critical_problem) {
1018			$i['priority'] = $critical_problem['severity'];
1019		}
1020
1021		$i['ack'] = !$i['problem_unack'];
1022
1023		// Number of problems.
1024		if ($sysmap['expandproblem'] == SYSMAP_PROBLEMS_NUMBER) {
1025			$i['expandproblem'] = SYSMAP_PROBLEMS_NUMBER;
1026		}
1027		// Expand single problem.
1028		elseif ($sysmap['expandproblem'] == SYSMAP_SINGLE_PROBLEM && $i['problem']) {
1029			$i['problem_title'] = $critical_problem['name'];
1030			$i['expandproblem'] = SYSMAP_SINGLE_PROBLEM;
1031		}
1032		// Number of problems and expand most critical one.
1033		elseif ($sysmap['expandproblem'] == SYSMAP_PROBLEMS_NUMBER_CRITICAL && $i['problem']) {
1034			$i['problem_title'] = $critical_problem['name'];
1035			$i['expandproblem'] = SYSMAP_PROBLEMS_NUMBER_CRITICAL;
1036		}
1037
1038		// replace default icons
1039		if (!$selement['iconid_on']) {
1040			$selement['iconid_on'] = $selement['iconid_off'];
1041		}
1042		if (!$selement['iconid_maintenance']) {
1043			$selement['iconid_maintenance'] = $selement['iconid_off'];
1044		}
1045		if (!$selement['iconid_disabled']) {
1046			$selement['iconid_disabled'] = $selement['iconid_off'];
1047		}
1048
1049		switch ($selement['elementtype']) {
1050			case SYSMAP_ELEMENT_TYPE_MAP:
1051				$info[$selementId] = getMapsInfo($selement, $i, $sysmap['show_unack']);
1052				break;
1053
1054			case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
1055				$info[$selementId] = getHostGroupsInfo($selement, $i, $sysmap['show_unack']);
1056				break;
1057
1058			case SYSMAP_ELEMENT_TYPE_HOST:
1059				if ($i['maintenance'] == 1) {
1060					$mnt = get_maintenance_by_maintenanceid($allHosts[$last_hostid]['maintenanceid']);
1061					$i['maintenance_title'] = $mnt['name'];
1062				}
1063
1064				$info[$selementId] = getHostsInfo($selement, $i, $sysmap['show_unack']);
1065				if ($sysmap['iconmapid'] && $selement['use_iconmap']) {
1066					$info[$selementId]['iconid'] = getIconByMapping($iconMap,
1067						$hostInventories[$selement['elements'][0]['hostid']]
1068					);
1069				}
1070				break;
1071
1072			case SYSMAP_ELEMENT_TYPE_TRIGGER:
1073				foreach ($selement['triggers'] as $trigger) {
1074					foreach ($trigger['hosts'] as $host) {
1075						if (array_key_exists('maintenance_status', $host)
1076								&& $host['maintenance_status'] == HOST_MAINTENANCE_STATUS_ON) {
1077							$maintenance = get_maintenance_by_maintenanceid($host['maintenanceid']);
1078							$i['maintenance_title'] = $maintenance['name'];
1079
1080							break;
1081						}
1082					}
1083				}
1084
1085				$info[$selementId] = getTriggersInfo($selement, $i, $sysmap['show_unack']);
1086				break;
1087
1088			case SYSMAP_ELEMENT_TYPE_IMAGE:
1089				$info[$selementId] = getImagesInfo($selement);
1090				break;
1091		}
1092
1093		if ($i['problem'] > 0) {
1094			$info[$selementId]['aria_label'] = ($i['problem'] > 1)
1095				? _n('%1$s problem', '%1$s problems', $i['problem'])
1096				: $critical_problem['name'];
1097		}
1098
1099		$info[$selementId]['problems_total'] = $i['problem'];
1100	}
1101
1102	if ($sysmap['label_format'] == SYSMAP_LABEL_ADVANCED_OFF) {
1103		$hlabel = $hglabel = $tlabel = $mlabel = ($sysmap['label_type'] == MAP_LABEL_TYPE_NAME);
1104	}
1105	else {
1106		$hlabel = ($sysmap['label_type_host'] == MAP_LABEL_TYPE_NAME);
1107		$hglabel = ($sysmap['label_type_hostgroup'] == MAP_LABEL_TYPE_NAME);
1108		$tlabel = ($sysmap['label_type_trigger'] == MAP_LABEL_TYPE_NAME);
1109		$mlabel = ($sysmap['label_type_map'] == MAP_LABEL_TYPE_NAME);
1110	}
1111
1112	// get names if needed
1113	$elems = separateMapElements($sysmap);
1114
1115	if ($elems['sysmaps'] && $mlabel) {
1116		$sysmapids = [];
1117
1118		foreach ($elems['sysmaps'] as $selement) {
1119			if ($selement['permission'] >= PERM_READ) {
1120				$sysmapids[$selement['elements'][0]['sysmapid']] = true;
1121			}
1122		}
1123
1124		$db_sysmaps = API::Map()->get([
1125			'output' => ['name'],
1126			'sysmapids' => array_keys($sysmapids),
1127			'preservekeys' => true
1128		]);
1129
1130		foreach ($elems['sysmaps'] as $selement) {
1131			if ($selement['permission'] >= PERM_READ) {
1132				$info[$selement['selementid']]['name'] =
1133					array_key_exists($selement['elements'][0]['sysmapid'], $db_sysmaps)
1134						? $db_sysmaps[$selement['elements'][0]['sysmapid']]['name']
1135						: '';
1136			}
1137		}
1138	}
1139
1140	if ($elems['hostgroups'] && $hglabel) {
1141		$groupids = [];
1142
1143		foreach ($elems['hostgroups'] as $selement) {
1144			if ($selement['permission'] >= PERM_READ) {
1145				$groupids[$selement['elements'][0]['groupid']] = true;
1146			}
1147		}
1148
1149		$db_groups = $groupids
1150			? API::HostGroup()->get([
1151				'output' => ['name'],
1152				'groupids' => array_keys($groupids),
1153				'preservekeys' => true
1154			])
1155			: [];
1156
1157		foreach ($elems['hostgroups'] as $selement) {
1158			if ($selement['permission'] >= PERM_READ) {
1159				$info[$selement['selementid']]['name'] =
1160					array_key_exists($selement['elements'][0]['groupid'], $db_groups)
1161						? $db_groups[$selement['elements'][0]['groupid']]['name']
1162						: '';
1163			}
1164		}
1165	}
1166
1167	if ($elems['triggers'] && $tlabel) {
1168		$selements = zbx_toHash($selements, 'selementid');
1169		foreach ($elems['triggers'] as $selementid => $selement) {
1170			foreach ($selement['elements'] as $element) {
1171				if ($selement['permission'] >= PERM_READ) {
1172					$trigger = array_key_exists($element['triggerid'], $selements[$selementid]['triggers'])
1173						? $selements[$selementid]['triggers'][$element['triggerid']]
1174						: null;
1175					$info[$selement['selementid']]['name'] = ($trigger != null)
1176						? CMacrosResolverHelper::resolveTriggerName($trigger)
1177						: '';
1178				}
1179			}
1180		}
1181	}
1182
1183	if ($elems['hosts'] && $hlabel) {
1184		foreach ($elems['hosts'] as $selement) {
1185			if ($selement['permission'] >= PERM_READ) {
1186				$info[$selement['selementid']]['name'] = array_key_exists($selement['elements'][0]['hostid'], $allHosts)
1187					? $allHosts[$selement['elements'][0]['hostid']]['name']
1188					: [];
1189			}
1190		}
1191	}
1192
1193	return $info;
1194}
1195
1196/**
1197 * Takes sysmap selements array, applies filtering by application to triggers and returns sysmap selements array.
1198 *
1199 * @param array $selements                          selements of current sysmap
1200 * @param array $selementHostApplicationFilters     a list of application filters applied to each host under each element
1201 *                                                  @see getSelementHostApplicationFilters()
1202 * @param array $triggersFromMonitoredHosts         triggers that are relevant to filtering
1203 * @param array $subSysmapTriggerIdToSelementIds    a map of triggers in sysmaps to selement IDs
1204 *
1205 * @return array
1206 */
1207function filterSysmapTriggers(array $selements, array $selementHostApplicationFilters,
1208		array $triggersFromMonitoredHosts, array $subSysmapTriggerIdToSelementIds) {
1209	// pick only host, host group or map selements
1210	$filterableSelements = [];
1211	foreach ($selements as $selementId => $selement) {
1212		if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST
1213				|| $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP
1214				|| $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP) {
1215			$filterableSelements[$selementId] = $selement;
1216		}
1217	}
1218	// calculate list of triggers that might get removed from $selement['triggers']
1219	$triggersToFilter = [];
1220	foreach ($filterableSelements as $selementId => $selement) {
1221		foreach ($selement['triggers'] as $trigger) {
1222			if (!isset($triggersFromMonitoredHosts[$trigger['triggerid']])) {
1223				continue;
1224			}
1225			$trigger = $triggersFromMonitoredHosts[$trigger['triggerid']];
1226			foreach ($trigger['hosts'] as $host) {
1227				$hostId = $host['hostid'];
1228				if (isset($selementHostApplicationFilters[$selementId][$hostId])) {
1229					$triggersToFilter[$trigger['triggerid']] = $trigger;
1230				}
1231			}
1232		}
1233	}
1234
1235	// if there are no triggers to filter
1236	if (!$triggersToFilter) {
1237		return $selements;
1238	}
1239
1240	// produce mapping of trigger to application names it is related to and produce mapping of host to triggers
1241	$itemIds = [];
1242	foreach ($triggersToFilter as $trigger) {
1243		foreach ($trigger['items'] as $item) {
1244			$itemIds[$item['itemid']] = $item['itemid'];
1245		}
1246	}
1247	$items = API::Item()->get([
1248		'output' => ['itemid'],
1249		'selectApplications' => ['name'],
1250		'itemids' => $itemIds,
1251		'webitems' => true,
1252		'preservekeys' => true
1253	]);
1254
1255	$triggerApplications = [];
1256	$hostIdToTriggers = [];
1257	foreach ($triggersToFilter as $trigger) {
1258		$triggerId = $trigger['triggerid'];
1259
1260		foreach ($trigger['items'] as $item) {
1261			foreach ($items[$item['itemid']]['applications'] as $application) {
1262				$triggerApplications[$triggerId][$application['name']] = true;
1263			}
1264		}
1265
1266		foreach ($trigger['hosts'] as $host) {
1267			$hostIdToTriggers[$host['hostid']][$triggerId] = $trigger;
1268		}
1269	}
1270
1271	foreach ($filterableSelements as $selementId => &$selement) {
1272		// walk through each host of a submap and apply its filters to all its triggers
1273		foreach ($selement['hosts'] as $hostId) {
1274			// skip hosts that don't have any filters or triggers to filter
1275			if (!isset($hostIdToTriggers[$hostId]) || !isset($selementHostApplicationFilters[$selementId][$hostId])) {
1276				continue;
1277			}
1278
1279			// remove the triggers that don't have applications or don't match the filter
1280			$filteredApplicationNames = $selementHostApplicationFilters[$selementId][$hostId];
1281			foreach ($hostIdToTriggers[$hostId] as $trigger) {
1282				$triggerId = $trigger['triggerid'];
1283
1284				// skip if this trigger is standalone trigger and those are not filtered
1285				if (isset($subSysmapTriggerIdToSelementIds[$triggerId])
1286						&& isset($subSysmapTriggerIdToSelementIds[$triggerId][$selementId])) {
1287					continue;
1288				}
1289
1290				$applicationNamesForTrigger = isset($triggerApplications[$triggerId])
1291					? array_keys($triggerApplications[$triggerId])
1292					: [];
1293
1294				if (!array_intersect($applicationNamesForTrigger, $filteredApplicationNames)) {
1295					unset($selement['triggers'][$triggerId]);
1296				}
1297			}
1298		}
1299	}
1300	unset($selement);
1301
1302	// put back updated selements
1303	foreach ($filterableSelements as $selementId => $selement) {
1304		$selements[$selementId] = $selement;
1305	}
1306
1307	return $selements;
1308}
1309
1310/**
1311 * Returns a list of application filters applied to each host under each element.
1312 *
1313 * @param array $selements                  selements of current sysmap
1314 * @param array $selementIdToSubSysmaps     all sub-sysmaps used in current sysmap, indexed by selementId
1315 * @param array $hostsFromHostGroups        collection of hosts that get included via host groups
1316 *
1317 * @return array    a two-dimensional array with selement IDs as the primary key, host IDs as the secondary key
1318 *                  application names as values
1319 */
1320function getSelementHostApplicationFilters(array $selements, array $selementIdToSubSysmaps,
1321		array $hostsFromHostGroups) {
1322	$hostIdsForHostGroupId = [];
1323	foreach ($hostsFromHostGroups as $host) {
1324		$hostId = $host['hostid'];
1325		foreach ($host['groups'] as $group) {
1326			$hostIdsForHostGroupId[$group['groupid']][$hostId] = $hostId;
1327		}
1328	}
1329
1330	$selementHostApplicationFilters = [];
1331	foreach ($selements as $selementId => $selement) {
1332		switch ($selement['elementtype']) {
1333			case SYSMAP_ELEMENT_TYPE_HOST:
1334			case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
1335				// skip host and host group elements with an empty filter
1336				if ($selement['application'] === '') {
1337					continue 2;
1338				}
1339
1340				foreach ($selement['hosts'] as $hostId) {
1341					$selementHostApplicationFilters[$selementId][$hostId][] = $selement['application'];
1342				}
1343
1344				break;
1345
1346			case SYSMAP_ELEMENT_TYPE_MAP:
1347				if (array_key_exists($selementId, $selementIdToSubSysmaps)) {
1348					foreach ($selementIdToSubSysmaps[$selementId] as $subSysmap) {
1349						// add all filters set for host elements
1350						foreach ($subSysmap['selements'] as $subSysmapSelement) {
1351							if ($subSysmapSelement['elementtype'] != SYSMAP_ELEMENT_TYPE_HOST
1352									|| $subSysmapSelement['application'] === '') {
1353
1354								continue;
1355							}
1356
1357							$hostId = $subSysmapSelement['elements'][0]['hostid'];
1358							$selementHostApplicationFilters[$selementId][$hostId][] = $subSysmapSelement['application'];
1359						}
1360
1361						// Find all selements with host groups and sort them into two arrays:
1362						// - with application filter
1363						// - without application filter
1364						$hostGroupSelementsWithApplication = [];
1365						$hostGroupSelementsWithoutApplication = [];
1366						foreach ($subSysmap['selements'] as $subSysmapSelement) {
1367							if ($subSysmapSelement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP) {
1368								if ($subSysmapSelement['application'] !== '') {
1369									$hostGroupSelementsWithApplication[] = $subSysmapSelement;
1370								}
1371								else {
1372									$hostGroupSelementsWithoutApplication[] = $subSysmapSelement;
1373								}
1374							}
1375						}
1376
1377						// Combine application filters for hosts from host group selements with
1378						// application filters set.
1379						foreach ($hostGroupSelementsWithApplication as $hostGroupSelement) {
1380							$hostGroupId = $hostGroupSelement['elements'][0]['groupid'];
1381
1382							if (isset($hostIdsForHostGroupId[$hostGroupId])) {
1383								foreach ($hostIdsForHostGroupId[$hostGroupId] as $hostId) {
1384									$selementHostApplicationFilters[$selementId][$hostId][]
1385										= $hostGroupSelement['application'];
1386								}
1387							}
1388						}
1389
1390						// Unset all application filters for hosts in host group selements without any filters.
1391						// This might reset application filters set by previous foreach.
1392						foreach ($hostGroupSelementsWithoutApplication AS $hostGroupSelement) {
1393							$hostGroupId = $hostGroupSelement['elements'][0]['groupid'];
1394
1395							if (isset($hostIdsForHostGroupId[$hostGroupId])) {
1396								foreach ($hostIdsForHostGroupId[$hostGroupId] as $hostId) {
1397									unset($selementHostApplicationFilters[$selementId][$hostId]);
1398								}
1399							}
1400						}
1401					}
1402				}
1403				break;
1404		}
1405	}
1406
1407	return $selementHostApplicationFilters;
1408}
1409
1410function separateMapElements($sysmap) {
1411	$elements = [
1412		'sysmaps' => [],
1413		'hostgroups' => [],
1414		'hosts' => [],
1415		'triggers' => [],
1416		'images' => []
1417	];
1418
1419	foreach ($sysmap['selements'] as $selement) {
1420		switch ($selement['elementtype']) {
1421			case SYSMAP_ELEMENT_TYPE_MAP:
1422				$elements['sysmaps'][$selement['selementid']] = $selement;
1423				break;
1424			case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
1425				$elements['hostgroups'][$selement['selementid']] = $selement;
1426				break;
1427			case SYSMAP_ELEMENT_TYPE_HOST:
1428				$elements['hosts'][$selement['selementid']] = $selement;
1429				break;
1430			case SYSMAP_ELEMENT_TYPE_TRIGGER:
1431				$elements['triggers'][$selement['selementid']] = $selement;
1432				break;
1433			case SYSMAP_ELEMENT_TYPE_IMAGE:
1434			default:
1435				$elements['images'][$selement['selementid']] = $selement;
1436		}
1437	}
1438	return $elements;
1439}
1440
1441/**
1442 * For each host group which is area for hosts virtual elements as hosts from that host group are created.
1443 *
1444 * @param array $map    Map data.
1445 * @param array $theme  Theme used to create missing elements (like hostgroup frame).
1446 *
1447 * @return array        Areas with area coordinates and selementids.
1448 */
1449function populateFromMapAreas(array &$map, array $theme) {
1450	$areas = [];
1451	$new_selementid = ((count($map['selements']) > 0) ? (int) max(array_keys($map['selements'])) : 0) + 1;
1452	$new_linkid = ((count($map['links']) > 0) ? (int) max(array_keys($map['links'])) : 0) + 1;
1453
1454	foreach ($map['selements'] as $selement) {
1455		if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP
1456				&& $selement['elementsubtype'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS) {
1457			$area = ['selementids' => []];
1458			$origSelement = $selement;
1459
1460			$hosts = API::Host()->get([
1461				'output' => ['hostid'],
1462				'groupids' => $selement['elements'][0]['groupid'],
1463				'sortfield' => 'name',
1464				'preservekeys' => true
1465			]);
1466
1467			if (!$hosts) {
1468				continue;
1469			}
1470
1471			if ($selement['areatype'] == SYSMAP_ELEMENT_AREA_TYPE_CUSTOM) {
1472				$area['width'] = $selement['width'];
1473				$area['height'] = $selement['height'];
1474				$area['x'] = $selement['x'];
1475				$area['y'] = $selement['y'];
1476
1477				$map['shapes'][] = [
1478					'sysmap_shapeid' => 'e-' . $selement['selementid'],
1479					'type' => SYSMAP_SHAPE_TYPE_RECTANGLE,
1480					'x' => $selement['x'],
1481					'y' => $selement['y'],
1482					'width' => $selement['width'],
1483					'height' => $selement['height'],
1484					'border_type' => SYSMAP_SHAPE_BORDER_TYPE_SOLID,
1485					'border_width' => 3,
1486					'border_color' => $theme['maingridcolor'],
1487					'background_color' => '',
1488					'zindex' => -1
1489				];
1490			}
1491			else {
1492				$area['width'] = $map['width'];
1493				$area['height'] = $map['height'];
1494				$area['x'] = 0;
1495				$area['y'] = 0;
1496			}
1497
1498			foreach ($hosts as $host) {
1499				$selement['elementtype'] = SYSMAP_ELEMENT_TYPE_HOST;
1500				$selement['elementsubtype'] = SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP;
1501				$selement['elements'][0]['hostid'] = $host['hostid'];
1502
1503				while (array_key_exists($new_selementid, $map['selements'])) {
1504					$new_selementid += 1;
1505				}
1506				$selement['selementid'] = -$new_selementid;
1507
1508				$area['selementids'][$new_selementid] = $new_selementid;
1509				$map['selements'][$new_selementid] = $selement;
1510			}
1511
1512			$areas[] = $area;
1513
1514			$selements = zbx_toHash($map['selements'], 'selementid');
1515
1516			foreach ($map['links'] as $link) {
1517				// Do not multiply links between two areas.
1518				$id1 = $link['selementid1'];
1519				$id2 = $link['selementid2'];
1520
1521				if ($selements[$id1]['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP
1522						&& $selements[$id1]['elementsubtype'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS
1523						&& $selements[$id2]['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP
1524						&& $selements[$id2]['elementsubtype'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS) {
1525					continue;
1526				}
1527
1528				$id_number = null;
1529
1530				if ($id1 == $origSelement['selementid']) {
1531					$id_number = 'selementid1';
1532				}
1533				elseif ($id2 == $origSelement['selementid']) {
1534					$id_number = 'selementid2';
1535				}
1536
1537				if ($id_number) {
1538					foreach ($area['selementids'] as $selement_id) {
1539						while (array_key_exists($new_linkid, $map['links'])) {
1540							$new_linkid += 1;
1541						}
1542
1543						$link['linkid'] = -$new_linkid;
1544						$link[$id_number] = -$selement_id;
1545						$map['links'][$new_linkid] = $link;
1546					}
1547				}
1548			}
1549		}
1550	}
1551
1552	$selements = [];
1553
1554	foreach ($map['selements'] as $key => $element) {
1555		if ($element['elementtype'] != SYSMAP_ELEMENT_TYPE_HOST_GROUP
1556				|| $element['elementsubtype'] != SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS) {
1557			$selements[$key] = $element;
1558		}
1559	}
1560
1561	$map['selements'] = $selements;
1562
1563	return $areas;
1564}
1565
1566/**
1567 * Calculates coordinates from elements inside areas
1568 *
1569 * @param array $map
1570 * @param array $areas
1571 * @param array $mapInfo
1572 */
1573function processAreasCoordinates(array &$map, array $areas, array $mapInfo) {
1574	foreach ($areas as $area) {
1575		$rowPlaceCount = ceil(sqrt(count($area['selementids'])));
1576
1577		// offset from area borders
1578		$area['x'] += 5;
1579		$area['y'] += 5;
1580		$area['width'] -= 5;
1581		$area['height'] -= 5;
1582
1583		$xOffset = floor($area['width'] / $rowPlaceCount);
1584		$yOffset = floor($area['height'] / $rowPlaceCount);
1585
1586		$colNum = 0;
1587		$rowNum = 0;
1588		// some offset is required so that icon highlights are not drawn outside area
1589		$borderOffset = 20;
1590		foreach ($area['selementids'] as $selementId) {
1591			$selement = $map['selements'][$selementId];
1592
1593			$image = get_png_by_selement($mapInfo[$selementId]);
1594			$iconX = imagesx($image);
1595			$iconY = imagesy($image);
1596
1597			$labelLocation = (is_null($selement['label_location']) || ($selement['label_location'] < 0))
1598				? $map['label_location'] : $selement['label_location'];
1599			switch ($labelLocation) {
1600				case MAP_LABEL_LOC_TOP:
1601					$newX = $area['x'] + ($xOffset / 2) - ($iconX / 2);
1602					$newY = $area['y'] + $yOffset - $iconY - ($iconY >= $iconX ? 0 : abs($iconX - $iconY) / 2) - $borderOffset;
1603					break;
1604				case MAP_LABEL_LOC_LEFT:
1605					$newX = $area['x'] + $xOffset - $iconX - $borderOffset;
1606					$newY = $area['y'] + ($yOffset / 2) - ($iconY / 2);
1607					break;
1608				case MAP_LABEL_LOC_RIGHT:
1609					$newX = $area['x'] + $borderOffset;
1610					$newY = $area['y'] + ($yOffset / 2) - ($iconY / 2);
1611					break;
1612				case MAP_LABEL_LOC_BOTTOM:
1613					$newX = $area['x'] + ($xOffset / 2) - ($iconX / 2);
1614					$newY = $area['y'] + abs($iconX - $iconY) / 2 + $borderOffset;
1615					break;
1616			}
1617
1618			$map['selements'][$selementId]['x'] = $newX + ($colNum * $xOffset);
1619			$map['selements'][$selementId]['y'] = $newY + ($rowNum * $yOffset);
1620
1621			$colNum++;
1622			if ($colNum == $rowPlaceCount) {
1623				$colNum = 0;
1624				$rowNum++;
1625			}
1626		}
1627	}
1628}
1629
1630/**
1631 * Calculates area connector point on area perimeter
1632 *
1633 * @param int $ax      x area coordinate
1634 * @param int $ay      y area coordinate
1635 * @param int $aWidth  area width
1636 * @param int $aHeight area height
1637 * @param int $x2      x coordinate of connector second element
1638 * @param int $y2      y coordinate of connector second element
1639 *
1640 * @return array contains two values, x and y coordinates of new area connector point
1641 */
1642function calculateMapAreaLinkCoord($ax, $ay, $aWidth, $aHeight, $x2, $y2) {
1643	$dY = abs($y2 - $ay);
1644	$dX = abs($x2 - $ax);
1645
1646	$halfHeight = $aHeight / 2;
1647	$halfWidth = $aWidth / 2;
1648
1649	if ($dY == 0) {
1650		$ay = $y2;
1651		$ax = ($x2 < $ax) ? $ax - $halfWidth : $ax + $halfWidth;
1652	}
1653	elseif ($dX == 0) {
1654		$ay = ($y2 > $ay) ? $ay + $halfHeight : $ay - $halfHeight;
1655		$ax = $x2;
1656	}
1657	else {
1658		$koef = $halfHeight / $dY;
1659
1660		$c = $dX * $koef;
1661
1662		// If point is further than area diagonal, we should use calculations with width instead of height.
1663		if (($halfHeight / $c) > ($halfHeight / $halfWidth)) {
1664			$ay = ($y2 > $ay) ? $ay + $halfHeight : $ay - $halfHeight;
1665			$ax = ($x2 < $ax) ? $ax - $c : $ax + $c;
1666		}
1667		else {
1668			$koef = $halfWidth / $dX;
1669
1670			$c = $dY * $koef;
1671
1672			$ay = ($y2 > $ay) ? $ay + $c : $ay - $c;
1673			$ax = ($x2 < $ax) ? $ax - $halfWidth : $ax + $halfWidth;
1674		}
1675	}
1676
1677	return [$ax, $ay];
1678}
1679
1680/**
1681 * Get icon id by mapping.
1682 *
1683 * @param array $iconMap
1684 * @param array $inventory
1685 *
1686 * @return int
1687 */
1688function getIconByMapping($iconMap, $inventory) {
1689	if ($inventory['inventory']['inventory_mode'] != HOST_INVENTORY_DISABLED) {
1690		$inventories = getHostInventories();
1691
1692		foreach ($iconMap['mappings'] as $mapping) {
1693			try {
1694				$expr = new CGlobalRegexp($mapping['expression']);
1695				if ($expr->match($inventory['inventory'][$inventories[$mapping['inventory_link']]['db_field']])) {
1696					return $mapping['iconid'];
1697				}
1698			}
1699			catch(Exception $e) {
1700				continue;
1701			}
1702		}
1703	}
1704
1705	return $iconMap['default_iconid'];
1706}
1707
1708/**
1709 * Get parent maps for current map.
1710 *
1711 * @param int $sysmapid
1712 *
1713 * @return array
1714 */
1715function get_parent_sysmaps($sysmapid) {
1716	$db_sysmaps_elements = DBselect(
1717		'SELECT DISTINCT se.sysmapid'.
1718		' FROM sysmaps_elements se'.
1719		' WHERE '.dbConditionInt('se.elementtype', [SYSMAP_ELEMENT_TYPE_MAP]).
1720			' AND '.dbConditionInt('se.elementid', [$sysmapid])
1721	);
1722
1723	$sysmapids = [];
1724
1725	while ($db_sysmaps_element = DBfetch($db_sysmaps_elements)) {
1726		$sysmapids[] = $db_sysmaps_element['sysmapid'];
1727	}
1728
1729	if ($sysmapids) {
1730		$sysmaps = API::Map()->get([
1731			'output' => ['sysmapid', 'name'],
1732			'sysmapids' => $sysmapids
1733		]);
1734
1735		CArrayHelper::sort($sysmaps, ['name']);
1736
1737		return $sysmaps;
1738	}
1739
1740	return [];
1741}
1742
1743/**
1744 * Get labels for map elements.
1745 *
1746 * @param array $map       Sysmap data array.
1747 * @param array $map_info  Array of selements (@see getSelementsInfo).
1748 *
1749 * @return array
1750 */
1751function getMapLabels($map, $map_info) {
1752	if ($map['label_type'] == MAP_LABEL_TYPE_NOTHING && $map['label_format'] == SYSMAP_LABEL_ADVANCED_OFF) {
1753		return;
1754	}
1755
1756	$selements = $map['selements'];
1757
1758	// set label type and custom label text for all selements
1759	foreach ($selements as $selementId => $selement) {
1760		if ($selement['permission'] < PERM_READ) {
1761			continue;
1762		}
1763
1764		$selements[$selementId]['label_type'] = $map['label_type'];
1765
1766		if ($map['label_format'] == SYSMAP_LABEL_ADVANCED_OFF) {
1767			continue;
1768		}
1769
1770		switch ($selement['elementtype']) {
1771			case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
1772				$selements[$selementId]['label_type'] = $map['label_type_hostgroup'];
1773				if ($map['label_type_hostgroup'] == MAP_LABEL_TYPE_CUSTOM) {
1774					$selements[$selementId]['label'] = $map['label_string_hostgroup'];
1775				}
1776				break;
1777
1778			case SYSMAP_ELEMENT_TYPE_HOST:
1779				$selements[$selementId]['label_type'] = $map['label_type_host'];
1780				if ($map['label_type_host'] == MAP_LABEL_TYPE_CUSTOM) {
1781					$selements[$selementId]['label'] = $map['label_string_host'];
1782				}
1783				break;
1784
1785			case SYSMAP_ELEMENT_TYPE_TRIGGER:
1786				$selements[$selementId]['label_type'] = $map['label_type_trigger'];
1787				if ($map['label_type_trigger'] == MAP_LABEL_TYPE_CUSTOM) {
1788					$selements[$selementId]['label'] = $map['label_string_trigger'];
1789				}
1790				break;
1791
1792			case SYSMAP_ELEMENT_TYPE_MAP:
1793				$selements[$selementId]['label_type'] = $map['label_type_map'];
1794				if ($map['label_type_map'] == MAP_LABEL_TYPE_CUSTOM) {
1795					$selements[$selementId]['label'] = $map['label_string_map'];
1796				}
1797				break;
1798
1799			case SYSMAP_ELEMENT_TYPE_IMAGE:
1800				$selements[$selementId]['label_type'] = $map['label_type_image'];
1801				if ($map['label_type_image'] == MAP_LABEL_TYPE_CUSTOM) {
1802					$selements[$selementId]['label'] = $map['label_string_image'];
1803				}
1804				break;
1805		}
1806	}
1807
1808	$labelLines = [];
1809	$statusLines = [];
1810
1811	foreach ($selements as $selementId => $selement) {
1812		if ($selement['permission'] < PERM_READ) {
1813			continue;
1814		}
1815
1816		$labelLines[$selementId] = [];
1817		$statusLines[$selementId] = [];
1818
1819		$msgs = explode("\n", CMacrosResolverHelper::resolveMapLabelMacrosAll($selement));
1820		foreach ($msgs as $msg) {
1821			$labelLines[$selementId][] = ['content' => $msg];
1822		}
1823
1824		$elementInfo = $map_info[$selementId];
1825
1826		foreach (['problem', 'unack', 'maintenance', 'ok', 'status'] as $caption) {
1827			if (!isset($elementInfo['info'][$caption]) || zbx_empty($elementInfo['info'][$caption]['msg'])) {
1828				continue;
1829			}
1830
1831			$msgs = explode("\n", $elementInfo['info'][$caption]['msg']);
1832
1833			foreach ($msgs as $msg) {
1834				$statusLines[$selementId][] = [
1835					'content' => $msg,
1836					'attributes' => [
1837						'fill' => '#' . $elementInfo['info'][$caption]['color']
1838					]
1839				];
1840			}
1841		}
1842	}
1843
1844	$elementsHostIds = [];
1845	foreach ($selements as $selement) {
1846		if ($selement['permission'] < PERM_READ) {
1847			continue;
1848		}
1849
1850		if ($selement['label_type'] != MAP_LABEL_TYPE_IP) {
1851			continue;
1852		}
1853		if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST) {
1854			$elementsHostIds[] = $selement['elements'][0]['hostid'];
1855		}
1856	}
1857
1858	if (!empty($elementsHostIds)) {
1859		$mapHosts = API::Host()->get([
1860			'output' => ['hostid'],
1861			'selectInterfaces' => API_OUTPUT_EXTEND,
1862			'hostids' => $elementsHostIds
1863		]);
1864		$mapHosts = zbx_toHash($mapHosts, 'hostid');
1865	}
1866
1867	$labels = [];
1868	foreach ($selements as $selementId => $selement) {
1869		if ($selement['permission'] < PERM_READ) {
1870			continue;
1871		}
1872
1873		if (empty($selement) || $selement['label_type'] == MAP_LABEL_TYPE_NOTHING) {
1874			$labels[$selementId] = [];
1875			continue;
1876		}
1877
1878		$elementInfo = $map_info[$selementId];
1879
1880		$hl_color = null;
1881		$st_color = null;
1882
1883		if (!isset($_REQUEST['noselements']) && ($map['highlight'] % 2) == SYSMAP_HIGHLIGHT_ON) {
1884			if ($elementInfo['icon_type'] == SYSMAP_ELEMENT_ICON_ON) {
1885				$hl_color = true;
1886			}
1887			if ($elementInfo['icon_type'] == SYSMAP_ELEMENT_ICON_MAINTENANCE) {
1888				$st_color = true;
1889			}
1890			if ($elementInfo['icon_type'] == SYSMAP_ELEMENT_ICON_DISABLED) {
1891				$st_color = true;
1892			}
1893		}
1894
1895		if (in_array($selement['elementtype'], [SYSMAP_ELEMENT_TYPE_HOST_GROUP, SYSMAP_ELEMENT_TYPE_MAP])
1896				&& !is_null($hl_color)) {
1897			$st_color = null;
1898		}
1899		elseif (!is_null($st_color)) {
1900			$hl_color = null;
1901		}
1902
1903		$label = [];
1904
1905		if ($selement['label_type'] == MAP_LABEL_TYPE_IP && $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST) {
1906			$interface = reset($mapHosts[$selement['elements'][0]['hostid']]['interfaces']);
1907
1908			$label[] = ['content' => $interface['ip']];
1909			$label = array_merge($label, $statusLines[$selementId]);
1910		}
1911		elseif ($selement['label_type'] == MAP_LABEL_TYPE_STATUS) {
1912			$label = $statusLines[$selementId];
1913		}
1914		elseif ($selement['label_type'] == MAP_LABEL_TYPE_NAME) {
1915			$label[] = ['content' => $elementInfo['name']];
1916			$label = array_merge($label, $statusLines[$selementId]);
1917		}
1918		else {
1919			$label = array_merge($labelLines[$selementId], $statusLines[$selementId]);
1920		}
1921
1922		$labels[$selementId] = $label;
1923	}
1924
1925	return $labels;
1926}
1927
1928/**
1929 * Get map element highlights (information about elements with marks or background).
1930 *
1931 * @param array $map       Sysmap data array.
1932 * @param array $map_info  Array of selements (@see getSelementsInfo).
1933 *
1934 * @return array
1935 */
1936function getMapHighligts(array $map, array $map_info) {
1937	$highlights = [];
1938
1939	foreach ($map['selements'] as $id => $selement) {
1940		if ((($map['highlight'] % 2) != SYSMAP_HIGHLIGHT_ON) || (array_key_exists('elementtype', $selement)
1941				&& $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP
1942				&& array_key_exists('elementsubtype', $selement)
1943				&& $selement['elementsubtype'] == SYSMAP_ELEMENT_SUBTYPE_HOST_GROUP_ELEMENTS)) {
1944			$highlights[$id] = null;
1945			continue;
1946		}
1947
1948		$hl_color = null;
1949		$st_color = null;
1950		$element_info = $map_info[$id];
1951
1952		switch ($element_info['icon_type']) {
1953			case SYSMAP_ELEMENT_ICON_ON:
1954				$hl_color = getSeverityColor($element_info['priority']);
1955				break;
1956
1957			case SYSMAP_ELEMENT_ICON_MAINTENANCE:
1958				$st_color = 'FF9933';
1959				break;
1960
1961			case SYSMAP_ELEMENT_ICON_DISABLED:
1962				$st_color = '999999';
1963				break;
1964		}
1965
1966		if (array_key_exists('elementtype', $selement)
1967				&& ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_HOST_GROUP
1968				|| $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_MAP) && $hl_color !== null) {
1969			$st_color = null;
1970		}
1971		elseif ($st_color !== null) {
1972			$hl_color = null;
1973		}
1974
1975		$highlights[$id] = [
1976			'st' =>  $st_color,
1977			'hl' => $hl_color,
1978			'ack' => ($hl_color !== null && array_key_exists('ack', $element_info) && $element_info['ack'])
1979		];
1980	}
1981
1982	return $highlights;
1983}
1984
1985/**
1986 * Get trigger data for all linktriggers.
1987 *
1988 * @param array $sysmap
1989 * @param array $sysmap['show_suppressed']  Whether to show suppressed problems.
1990 * @param array $sysmap['show_unack']       Property specified in sysmap's 'Problem display' field. Used to determine
1991 *                                          whether to show unacknowledged problems only.
1992 * @param array $options                    Options used to retrieve actions.
1993 * @param int   $options['severity_min']    Minimal severity used.
1994 *
1995 * @return array
1996 */
1997function getMapLinkTriggerInfo($sysmap, $options) {
1998	if (!array_key_exists('severity_min', $options)) {
1999		$options['severity_min'] = TRIGGER_SEVERITY_NOT_CLASSIFIED;
2000	}
2001
2002	$triggerids = [];
2003
2004	foreach ($sysmap['links'] as $link) {
2005		foreach ($link['linktriggers'] as $linktrigger) {
2006			$triggerids[$linktrigger['triggerid']] = true;
2007		}
2008	}
2009
2010	$trigger_options = [
2011		'output' => ['status', 'value', 'priority'],
2012		'triggerids' => array_keys($triggerids),
2013		'monitored' => true,
2014		'preservekeys' => true
2015	];
2016
2017	$problem_options = [
2018		'show_suppressed' => $sysmap['show_suppressed'],
2019		'acknowledged' => ($sysmap['show_unack'] == EXTACK_OPTION_UNACK) ? false : null
2020	];
2021
2022	return getTriggersWithActualSeverity($trigger_options, $problem_options);
2023}
2024
2025/**
2026 * Get map selement label color based on problem and acknowledgement state
2027 * as well as taking custom event status color settings into account.
2028 *
2029 * @throws APIException if the given table does not exist
2030 *
2031 * @param bool $is_problem
2032 * @param bool $is_ack
2033 *
2034 * @return string
2035 */
2036function getSelementLabelColor($is_problem, $is_ack) {
2037	static $config = null;
2038	static $schema = null;
2039
2040	if ($config === null) {
2041		$config = select_config();
2042		$schema = DB::getSchema('config');
2043	}
2044
2045	$ack_unack = $is_ack ? 'ack' : 'unack';
2046	$ok_problem = $is_problem ? 'problem' : 'ok';
2047
2048	if ($config['custom_color'] === '1') {
2049		return $config[$ok_problem . '_' . $ack_unack . '_color'];
2050	}
2051
2052	return $schema['fields'][$ok_problem . '_' . $ack_unack . '_color']['default'];
2053}
2054