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 condition_operator2str($operator) {
23	$operators = [
24		CONDITION_OPERATOR_EQUAL  => _('equals'),
25		CONDITION_OPERATOR_NOT_EQUAL  => _('does not equal'),
26		CONDITION_OPERATOR_LIKE  => _('contains'),
27		CONDITION_OPERATOR_NOT_LIKE  => _('does not contain'),
28		CONDITION_OPERATOR_IN => _('in'),
29		CONDITION_OPERATOR_MORE_EQUAL => _('is greater than or equals'),
30		CONDITION_OPERATOR_LESS_EQUAL => _('is less than or equals'),
31		CONDITION_OPERATOR_NOT_IN => _('not in'),
32		CONDITION_OPERATOR_YES => _('Yes'),
33		CONDITION_OPERATOR_NO => _('No'),
34		CONDITION_OPERATOR_REGEXP => _('matches'),
35		CONDITION_OPERATOR_NOT_REGEXP => _('does not match')
36	];
37
38	return $operators[$operator];
39}
40
41function condition_type2str($type) {
42	$types = [
43		CONDITION_TYPE_SUPPRESSED => _('Problem is suppressed'),
44		CONDITION_TYPE_TRIGGER_NAME => _('Trigger name'),
45		CONDITION_TYPE_TRIGGER_SEVERITY => _('Trigger severity'),
46		CONDITION_TYPE_TRIGGER => _('Trigger'),
47		CONDITION_TYPE_HOST_NAME => _('Host name'),
48		CONDITION_TYPE_HOST_GROUP => _('Host group'),
49		CONDITION_TYPE_TEMPLATE => _('Template'),
50		CONDITION_TYPE_HOST => _('Host'),
51		CONDITION_TYPE_TIME_PERIOD => _('Time period'),
52		CONDITION_TYPE_DRULE => _('Discovery rule'),
53		CONDITION_TYPE_DCHECK => _('Discovery check'),
54		CONDITION_TYPE_DOBJECT => _('Discovery object'),
55		CONDITION_TYPE_DHOST_IP => _('Host IP'),
56		CONDITION_TYPE_DSERVICE_TYPE => _('Service type'),
57		CONDITION_TYPE_DSERVICE_PORT => _('Service port'),
58		CONDITION_TYPE_DSTATUS => _('Discovery status'),
59		CONDITION_TYPE_DUPTIME => _('Uptime/Downtime'),
60		CONDITION_TYPE_DVALUE => _('Received value'),
61		CONDITION_TYPE_EVENT_ACKNOWLEDGED => _('Event acknowledged'),
62		CONDITION_TYPE_APPLICATION => _('Application'),
63		CONDITION_TYPE_PROXY => _('Proxy'),
64		CONDITION_TYPE_EVENT_TYPE => _('Event type'),
65		CONDITION_TYPE_HOST_METADATA => _('Host metadata'),
66		CONDITION_TYPE_EVENT_TAG => _('Tag name'),
67		CONDITION_TYPE_EVENT_TAG_VALUE => _('Tag value')
68	];
69
70	return $types[$type];
71}
72
73function discovery_object2str($object = null) {
74	$objects = [
75		EVENT_OBJECT_DHOST => _('Device'),
76		EVENT_OBJECT_DSERVICE => _('Service')
77	];
78
79	if ($object === null) {
80		return $objects;
81	}
82
83	return $objects[$object];
84}
85
86/**
87 * Converts numerical action condition values to their corresponding string values according to action condition type.
88 *
89 * For action condition types such as: hosts, host groups, templates, proxies, triggers, discovery rules
90 * and discovery checks, action condition values contain IDs. All unique IDs are first collected and then queried.
91 * For other action condition types values are returned as they are or converted using simple string conversion
92 * functions according to action condition type.
93 *
94 * @param array $actions							array of actions
95 * @param array $action['filter']					array containing arrays of action conditions and other data
96 * @param array $action['filter']['conditions']		array of action conditions
97 * @param array $config								array containing configuration parameters for getting trigger
98 *													severity names
99 *
100 * @return array									returns an array of actions condition string values
101 */
102function actionConditionValueToString(array $actions, array $config) {
103	$result = [];
104
105	$groupIds = [];
106	$triggerIds = [];
107	$hostIds = [];
108	$templateIds = [];
109	$proxyIds = [];
110	$dRuleIds = [];
111	$dCheckIds = [];
112
113	foreach ($actions as $i => $action) {
114		$result[$i] = [];
115
116		foreach ($action['filter']['conditions'] as $j => $condition) {
117			// unknown types and all of the default values for other types are 'Unknown'
118			$result[$i][$j] = _('Unknown');
119
120			switch ($condition['conditiontype']) {
121				case CONDITION_TYPE_HOST_GROUP:
122					$groupIds[$condition['value']] = $condition['value'];
123					break;
124
125				case CONDITION_TYPE_TRIGGER:
126					$triggerIds[$condition['value']] = $condition['value'];
127					break;
128
129				case CONDITION_TYPE_HOST:
130					$hostIds[$condition['value']] = $condition['value'];
131					break;
132
133				case CONDITION_TYPE_TEMPLATE:
134					$templateIds[$condition['value']] = $condition['value'];
135					break;
136
137				case CONDITION_TYPE_PROXY:
138					$proxyIds[$condition['value']] = $condition['value'];
139					break;
140
141				// return values as is for following condition types
142				case CONDITION_TYPE_TRIGGER_NAME:
143				case CONDITION_TYPE_HOST_METADATA:
144				case CONDITION_TYPE_HOST_NAME:
145				case CONDITION_TYPE_TIME_PERIOD:
146				case CONDITION_TYPE_DHOST_IP:
147				case CONDITION_TYPE_DSERVICE_PORT:
148				case CONDITION_TYPE_DUPTIME:
149				case CONDITION_TYPE_DVALUE:
150				case CONDITION_TYPE_APPLICATION:
151				case CONDITION_TYPE_EVENT_TAG:
152				case CONDITION_TYPE_EVENT_TAG_VALUE:
153					$result[$i][$j] = $condition['value'];
154					break;
155
156				case CONDITION_TYPE_EVENT_ACKNOWLEDGED:
157					$result[$i][$j] = $condition['value'] ? _('Ack') : _('Not Ack');
158					break;
159
160				case CONDITION_TYPE_TRIGGER_SEVERITY:
161					$result[$i][$j] = getSeverityName($condition['value'], $config);
162					break;
163
164				case CONDITION_TYPE_DRULE:
165					$dRuleIds[$condition['value']] = $condition['value'];
166					break;
167
168				case CONDITION_TYPE_DCHECK:
169					$dCheckIds[$condition['value']] = $condition['value'];
170					break;
171
172				case CONDITION_TYPE_DOBJECT:
173					$result[$i][$j] = discovery_object2str($condition['value']);
174					break;
175
176				case CONDITION_TYPE_DSERVICE_TYPE:
177					$result[$i][$j] = discovery_check_type2str($condition['value']);
178					break;
179
180				case CONDITION_TYPE_DSTATUS:
181					$result[$i][$j] = discovery_object_status2str($condition['value']);
182					break;
183
184				case CONDITION_TYPE_EVENT_TYPE:
185					$result[$i][$j] = eventType($condition['value']);
186					break;
187			}
188		}
189	}
190
191	$groups = [];
192	$triggers = [];
193	$hosts = [];
194	$templates = [];
195	$proxies = [];
196	$dRules = [];
197	$dChecks = [];
198
199	if ($groupIds) {
200		$groups = API::HostGroup()->get([
201			'output' => ['name'],
202			'groupids' => $groupIds,
203			'preservekeys' => true
204		]);
205	}
206
207	if ($triggerIds) {
208		$triggers = API::Trigger()->get([
209			'output' => ['description'],
210			'triggerids' => $triggerIds,
211			'expandDescription' => true,
212			'selectHosts' => ['name'],
213			'preservekeys' => true
214		]);
215	}
216
217	if ($hostIds) {
218		$hosts = API::Host()->get([
219			'output' => ['name'],
220			'hostids' => $hostIds,
221			'preservekeys' => true
222		]);
223	}
224
225	if ($templateIds) {
226		$templates = API::Template()->get([
227			'output' => ['name'],
228			'templateids' => $templateIds,
229			'preservekeys' => true
230		]);
231	}
232
233	if ($proxyIds) {
234		$proxies = API::Proxy()->get([
235			'output' => ['host'],
236			'proxyids' => $proxyIds,
237			'preservekeys' => true
238		]);
239	}
240
241	if ($dRuleIds) {
242		$dRules = API::DRule()->get([
243			'output' => ['name'],
244			'druleids' => $dRuleIds,
245			'preservekeys' => true
246		]);
247	}
248
249	if ($dCheckIds) {
250		$dChecks = API::DCheck()->get([
251			'output' => ['type', 'key_', 'ports'],
252			'dcheckids' => $dCheckIds,
253			'selectDRules' => ['name'],
254			'preservekeys' => true
255		]);
256	}
257
258	if ($groups || $triggers || $hosts || $templates || $proxies || $dRules || $dChecks) {
259		foreach ($actions as $i => $action) {
260			foreach ($action['filter']['conditions'] as $j => $condition) {
261				$id = $condition['value'];
262
263				switch ($condition['conditiontype']) {
264					case CONDITION_TYPE_HOST_GROUP:
265						if (isset($groups[$id])) {
266							$result[$i][$j] = $groups[$id]['name'];
267						}
268						break;
269
270					case CONDITION_TYPE_TRIGGER:
271						if (isset($triggers[$id])) {
272							$host = reset($triggers[$id]['hosts']);
273							$result[$i][$j] = $host['name'].NAME_DELIMITER.$triggers[$id]['description'];
274						}
275						break;
276
277					case CONDITION_TYPE_HOST:
278						if (isset($hosts[$id])) {
279							$result[$i][$j] = $hosts[$id]['name'];
280						}
281						break;
282
283					case CONDITION_TYPE_TEMPLATE:
284						if (isset($templates[$id])) {
285							$result[$i][$j] = $templates[$id]['name'];
286						}
287						break;
288
289					case CONDITION_TYPE_PROXY:
290						if (isset($proxies[$id])) {
291							$result[$i][$j] = $proxies[$id]['host'];
292						}
293						break;
294
295					case CONDITION_TYPE_DRULE:
296						if (isset($dRules[$id])) {
297							$result[$i][$j] = $dRules[$id]['name'];
298						}
299						break;
300
301					case CONDITION_TYPE_DCHECK:
302						if (isset($dChecks[$id])) {
303							$drule = reset($dChecks[$id]['drules']);
304							$type = $dChecks[$id]['type'];
305							$key_ = $dChecks[$id]['key_'];
306							$ports = $dChecks[$id]['ports'];
307
308							$dCheck = discovery_check2str($type, $key_, $ports);
309
310							$result[$i][$j] = $drule['name'].NAME_DELIMITER.$dCheck;
311						}
312						break;
313				}
314			}
315		}
316	}
317
318	return $result;
319}
320
321/**
322 * Returns the HTML representation of an action condition and action operation condition.
323 *
324 * @param string $condition_type
325 * @param string $operator
326 * @param string $value
327 * @param string $value2
328 *
329 * @return array
330 */
331function getConditionDescription($condition_type, $operator, $value, $value2) {
332	if ($condition_type == CONDITION_TYPE_EVENT_TAG_VALUE) {
333		$description = [_('Value of tag')];
334		$description[] = ' ';
335		$description[] = italic(CHtml::encode($value2));
336		$description[] = ' ';
337	}
338	elseif ($condition_type == CONDITION_TYPE_SUPPRESSED) {
339		return ($operator == CONDITION_OPERATOR_YES)
340			? [_('Problem is suppressed')]
341			: [_('Problem is not suppressed')];
342	}
343	elseif ($condition_type == CONDITION_TYPE_EVENT_ACKNOWLEDGED) {
344		return $value ? _('Event is acknowledged') : _('Event is not acknowledged');
345	}
346	else {
347		$description = [condition_type2str($condition_type)];
348		$description[] = ' ';
349	}
350
351	$description[] = condition_operator2str($operator);
352	$description[] = ' ';
353	$description[] = italic(CHtml::encode($value));
354
355	return $description;
356}
357
358/**
359 * Gathers media types, user groups, users, host groups, hosts and templates for actions and their operations, and
360 * returns the HTML representation of action operation values according to action operation type.
361 *
362 * @param array $actions				Array of actions
363 * @param int $type						Operations recovery type (ACTION_OPERATION or ACTION_RECOVERY_OPERATION)
364 *
365 * @return array						Returns an array of actions operation descriptions.
366 */
367function getActionOperationDescriptions(array $actions, $type) {
368	$result = [];
369
370	$media_typeids = [];
371	$userids = [];
372	$usr_grpids = [];
373	$hostids = [];
374	$groupids = [];
375	$templateids = [];
376
377	foreach ($actions as $i => $action) {
378		$result[$i] = [];
379
380		if ($type == ACTION_OPERATION) {
381			foreach ($action['operations'] as $j => $operation) {
382				$result[$i][$j] = [];
383
384				switch ($operation['operationtype']) {
385					case OPERATION_TYPE_MESSAGE:
386						$media_typeid = $operation['opmessage']['mediatypeid'];
387
388						if ($media_typeid != 0) {
389							$media_typeids[$media_typeid] = $media_typeid;
390						}
391
392						if (array_key_exists('opmessage_usr', $operation) && $operation['opmessage_usr']) {
393							foreach ($operation['opmessage_usr'] as $users) {
394								$userids[$users['userid']] = $users['userid'];
395							}
396						}
397
398						if (array_key_exists('opmessage_grp', $operation) && $operation['opmessage_grp']) {
399							foreach ($operation['opmessage_grp'] as $user_groups) {
400								$usr_grpids[$user_groups['usrgrpid']] = $user_groups['usrgrpid'];
401							}
402						}
403						break;
404
405					case OPERATION_TYPE_COMMAND:
406						if (array_key_exists('opcommand_hst', $operation) && $operation['opcommand_hst']) {
407							foreach ($operation['opcommand_hst'] as $host) {
408								if ($host['hostid'] != 0) {
409									$hostids[$host['hostid']] = $host['hostid'];
410								}
411							}
412						}
413
414						if (array_key_exists('opcommand_grp', $operation) && $operation['opcommand_grp']) {
415							foreach ($operation['opcommand_grp'] as $host_group) {
416								$groupids[$host_group['groupid']] = true;
417							}
418						}
419						break;
420
421					case OPERATION_TYPE_GROUP_ADD:
422					case OPERATION_TYPE_GROUP_REMOVE:
423						foreach ($operation['opgroup'] as $groupid) {
424							$groupids[$groupid['groupid']] = true;
425						}
426						break;
427
428					case OPERATION_TYPE_TEMPLATE_ADD:
429					case OPERATION_TYPE_TEMPLATE_REMOVE:
430						foreach ($operation['optemplate'] as $templateid) {
431							$templateids[$templateid['templateid']] = true;
432						}
433						break;
434				}
435			}
436		}
437		else {
438			$operations_key = ($type == ACTION_RECOVERY_OPERATION)
439				? 'recovery_operations'
440				: 'ack_operations';
441
442			foreach ($action[$operations_key] as $j => $operation) {
443				$result[$i][$j] = [];
444
445				switch ($operation['operationtype']) {
446					case OPERATION_TYPE_MESSAGE:
447						$media_typeid = $operation['opmessage']['mediatypeid'];
448
449						if ($media_typeid != 0) {
450							$media_typeids[$media_typeid] = $media_typeid;
451						}
452
453						if (array_key_exists('opmessage_usr', $operation) && $operation['opmessage_usr']) {
454							foreach ($operation['opmessage_usr'] as $users) {
455								$userids[$users['userid']] = $users['userid'];
456							}
457						}
458
459						if (array_key_exists('opmessage_grp', $operation) && $operation['opmessage_grp']) {
460							foreach ($operation['opmessage_grp'] as $user_groups) {
461								$usr_grpids[$user_groups['usrgrpid']] = $user_groups['usrgrpid'];
462							}
463						}
464						break;
465
466					case OPERATION_TYPE_COMMAND:
467						if (array_key_exists('opcommand_hst', $operation) && $operation['opcommand_hst']) {
468							foreach ($operation['opcommand_hst'] as $host) {
469								if ($host['hostid'] != 0) {
470									$hostids[$host['hostid']] = $host['hostid'];
471								}
472							}
473						}
474
475						if (array_key_exists('opcommand_grp', $operation) && $operation['opcommand_grp']) {
476							foreach ($operation['opcommand_grp'] as $host_group) {
477								$groupids[$host_group['groupid']] = true;
478							}
479						}
480						break;
481				}
482			}
483		}
484	}
485
486	$media_types = [];
487	$users = [];
488	$user_groups = [];
489	$hosts = [];
490	$host_groups = [];
491	$templates = [];
492
493	if ($media_typeids) {
494		$media_types = API::Mediatype()->get([
495			'output' => ['name'],
496			'mediatypeids' => $media_typeids,
497			'preservekeys' => true
498		]);
499	}
500
501	if ($userids) {
502		$fullnames = [];
503
504		$users = API::User()->get([
505			'output' => ['userid', 'alias', 'name', 'surname'],
506			'userids' => $userids
507		]);
508
509		foreach ($users as $user) {
510			$fullnames[$user['userid']] = getUserFullname($user);
511		}
512	}
513
514	if ($usr_grpids) {
515		$user_groups = API::UserGroup()->get([
516			'output' => ['name'],
517			'usrgrpids' => $usr_grpids,
518			'preservekeys' => true
519		]);
520	}
521
522	if ($hostids) {
523		$hosts = API::Host()->get([
524			'output' => ['name'],
525			'hostids' => $hostids,
526			'preservekeys' => true
527		]);
528	}
529
530	if ($groupids) {
531		$host_groups = API::HostGroup()->get([
532			'output' => ['name'],
533			'groupids' => array_keys($groupids),
534			'preservekeys' => true
535		]);
536	}
537
538	if ($templateids) {
539		$templates = API::Template()->get([
540			'output' => ['name'],
541			'templateids' => array_keys($templateids),
542			'preservekeys' => true
543		]);
544	}
545
546	// Format the HTML output.
547	foreach ($actions as $i => $action) {
548		if ($type == ACTION_OPERATION) {
549			foreach ($action['operations'] as $j => $operation) {
550				switch ($operation['operationtype']) {
551					case OPERATION_TYPE_MESSAGE:
552						$media_type = _('all media');
553						$media_typeid = $operation['opmessage']['mediatypeid'];
554
555						if ($media_typeid != 0 && isset($media_types[$media_typeid])) {
556							$media_type = $media_types[$media_typeid]['name'];
557						}
558
559						if (array_key_exists('opmessage_usr', $operation) && $operation['opmessage_usr']) {
560							$user_names_list = [];
561
562							foreach ($operation['opmessage_usr'] as $user) {
563								if (isset($fullnames[$user['userid']])) {
564									$user_names_list[] = $fullnames[$user['userid']];
565								}
566							}
567
568							order_result($user_names_list);
569
570							$result[$i][$j][] = bold(_('Send message to users').': ');
571							$result[$i][$j][] = [implode(', ', $user_names_list), SPACE, _('via'), SPACE,
572								$media_type
573							];
574							$result[$i][$j][] = BR();
575						}
576
577						if (array_key_exists('opmessage_grp', $operation) && $operation['opmessage_grp']) {
578							$user_groups_list = [];
579
580							foreach ($operation['opmessage_grp'] as $userGroup) {
581								if (isset($user_groups[$userGroup['usrgrpid']])) {
582									$user_groups_list[] = $user_groups[$userGroup['usrgrpid']]['name'];
583								}
584							}
585
586							order_result($user_groups_list);
587
588							$result[$i][$j][] = bold(_('Send message to user groups').': ');
589							$result[$i][$j][] = [implode(', ', $user_groups_list), SPACE, _('via'), SPACE,
590								$media_type
591							];
592							$result[$i][$j][] = BR();
593						}
594						break;
595
596					case OPERATION_TYPE_COMMAND:
597						if (array_key_exists('opcommand_hst', $operation) && $operation['opcommand_hst']) {
598							$host_list = [];
599
600							foreach ($operation['opcommand_hst'] as $host) {
601								if ($host['hostid'] == 0) {
602									$result[$i][$j][] = [
603										bold(_('Run remote commands on current host')),
604										BR()
605									];
606								}
607								elseif (isset($hosts[$host['hostid']])) {
608									$host_list[] = $hosts[$host['hostid']]['name'];
609								}
610							}
611
612							if ($host_list) {
613								order_result($host_list);
614
615								$result[$i][$j][] = bold(_('Run remote commands on hosts').': ');
616								$result[$i][$j][] = [implode(', ', $host_list), BR()];
617							}
618						}
619
620						if (array_key_exists('opcommand_grp', $operation) && $operation['opcommand_grp']) {
621							$host_group_list = [];
622
623							foreach ($operation['opcommand_grp'] as $host_group) {
624								if (isset($host_groups[$host_group['groupid']])) {
625									$host_group_list[] = $host_groups[$host_group['groupid']]['name'];
626								}
627							}
628
629							order_result($host_group_list);
630
631							$result[$i][$j][] = bold(_('Run remote commands on host groups').': ');
632							$result[$i][$j][] = [implode(', ', $host_group_list), BR()];
633						}
634						break;
635
636					case OPERATION_TYPE_HOST_ADD:
637						$result[$i][$j][] = [bold(_('Add host')), BR()];
638						break;
639
640					case OPERATION_TYPE_HOST_REMOVE:
641						$result[$i][$j][] = [bold(_('Remove host')), BR()];
642						break;
643
644					case OPERATION_TYPE_HOST_ENABLE:
645						$result[$i][$j][] = [bold(_('Enable host')), BR()];
646						break;
647
648					case OPERATION_TYPE_HOST_DISABLE:
649						$result[$i][$j][] = [bold(_('Disable host')), BR()];
650						break;
651
652					case OPERATION_TYPE_GROUP_ADD:
653					case OPERATION_TYPE_GROUP_REMOVE:
654						$host_group_list = [];
655
656						foreach ($operation['opgroup'] as $groupid) {
657							if (array_key_exists($groupid['groupid'], $host_groups)) {
658								$host_group_list[] = $host_groups[$groupid['groupid']]['name'];
659							}
660						}
661
662						order_result($host_group_list);
663
664						if ($operation['operationtype'] == OPERATION_TYPE_GROUP_ADD) {
665							$result[$i][$j][] = bold(_('Add to host groups').': ');
666						}
667						else {
668							$result[$i][$j][] = bold(_('Remove from host groups').': ');
669						}
670
671						$result[$i][$j][] = [implode(', ', $host_group_list), BR()];
672						break;
673
674					case OPERATION_TYPE_TEMPLATE_ADD:
675					case OPERATION_TYPE_TEMPLATE_REMOVE:
676						$template_list = [];
677
678						foreach ($operation['optemplate'] as $templateid) {
679							if (array_key_exists($templateid['templateid'], $templates)) {
680								$template_list[] = $templates[$templateid['templateid']]['name'];
681							}
682						}
683
684						order_result($template_list);
685
686						if ($operation['operationtype'] == OPERATION_TYPE_TEMPLATE_ADD) {
687							$result[$i][$j][] = bold(_('Link to templates').': ');
688						}
689						else {
690							$result[$i][$j][] = bold(_('Unlink from templates').': ');
691						}
692
693						$result[$i][$j][] = [implode(', ', $template_list), BR()];
694						break;
695
696					case OPERATION_TYPE_HOST_INVENTORY:
697						$host_inventory_modes = getHostInventoryModes();
698						$result[$i][$j][] = bold(operation_type2str(OPERATION_TYPE_HOST_INVENTORY).': ');
699						$result[$i][$j][] = [
700							$host_inventory_modes[$operation['opinventory']['inventory_mode']],
701							BR()
702						];
703						break;
704				}
705			}
706		}
707		else {
708			$operations_key = ($type == ACTION_RECOVERY_OPERATION)
709				? 'recovery_operations'
710				: 'ack_operations';
711
712			foreach ($action[$operations_key] as $j => $operation) {
713				switch ($operation['operationtype']) {
714					case OPERATION_TYPE_MESSAGE:
715						$media_type = _('all media');
716						$media_typeid = $operation['opmessage']['mediatypeid'];
717
718						if ($media_typeid != 0 && isset($media_types[$media_typeid])) {
719							$media_type = $media_types[$media_typeid]['name'];
720						}
721
722						if (array_key_exists('opmessage_usr', $operation) && $operation['opmessage_usr']) {
723							$user_names_list = [];
724
725							foreach ($operation['opmessage_usr'] as $user) {
726								if (isset($fullnames[$user['userid']])) {
727									$user_names_list[] = $fullnames[$user['userid']];
728								}
729							}
730
731							order_result($user_names_list);
732
733							$result[$i][$j][] = bold(_('Send message to users').': ');
734							$result[$i][$j][] = [implode(', ', $user_names_list), SPACE, _('via'), SPACE,
735								$media_type
736							];
737							$result[$i][$j][] = BR();
738						}
739
740						if (array_key_exists('opmessage_grp', $operation) && $operation['opmessage_grp']) {
741							$user_groups_list = [];
742
743							foreach ($operation['opmessage_grp'] as $userGroup) {
744								if (isset($user_groups[$userGroup['usrgrpid']])) {
745									$user_groups_list[] = $user_groups[$userGroup['usrgrpid']]['name'];
746								}
747							}
748
749							order_result($user_groups_list);
750
751							$result[$i][$j][] = bold(_('Send message to user groups').': ');
752							$result[$i][$j][] = [implode(', ', $user_groups_list), SPACE, _('via'), SPACE,
753								$media_type
754							];
755							$result[$i][$j][] = BR();
756						}
757						break;
758
759					case OPERATION_TYPE_COMMAND:
760						if (array_key_exists('opcommand_hst', $operation) && $operation['opcommand_hst']) {
761							$host_list = [];
762
763							foreach ($operation['opcommand_hst'] as $host) {
764								if ($host['hostid'] == 0) {
765									$result[$i][$j][] = [
766										bold(_('Run remote commands on current host')),
767										BR()
768									];
769								}
770								elseif (isset($hosts[$host['hostid']])) {
771									$host_list[] = $hosts[$host['hostid']]['name'];
772								}
773							}
774
775							if ($host_list) {
776								order_result($host_list);
777
778								$result[$i][$j][] = bold(_('Run remote commands on hosts').': ');
779								$result[$i][$j][] = [implode(', ', $host_list), BR()];
780							}
781						}
782
783						if (array_key_exists('opcommand_grp', $operation) && $operation['opcommand_grp']) {
784							$host_group_list = [];
785
786							foreach ($operation['opcommand_grp'] as $host_group) {
787								if (isset($host_groups[$host_group['groupid']])) {
788									$host_group_list[] = $host_groups[$host_group['groupid']]['name'];
789								}
790							}
791
792							order_result($host_group_list);
793
794							$result[$i][$j][] = bold(_('Run remote commands on host groups').': ');
795							$result[$i][$j][] = [implode(', ', $host_group_list), BR()];
796						}
797						break;
798
799					case OPERATION_TYPE_RECOVERY_MESSAGE:
800					case OPERATION_TYPE_ACK_MESSAGE:
801						$result[$i][$j][] = bold(_('Notify all involved'));
802						break;
803				}
804			}
805		}
806	}
807
808	return $result;
809}
810
811/**
812 * Gathers action operation script details and returns the HTML items representing action operation with hint.
813 *
814 * @param array  $operations                      Array of action operations.
815 * @param string $operation['operationtype']      Action operation type.
816 *                                                Possible values: OPERATION_TYPE_MESSAGE, OPERATION_TYPE_COMMAND,
817 *                                                OPERATION_TYPE_ACK_MESSAGE and OPERATION_TYPE_RECOVERY_MESSAGE
818 * @param string $operation['opcommand']['type']  Action operation command type.
819 *                                                Possible values: ZBX_SCRIPT_TYPE_IPMI, ZBX_SCRIPT_TYPE_SSH,
820 *                                                ZBX_SCRIPT_TYPE_TELNET, ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT
821 *                                                and ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT
822 *
823 * @return array  Returns an array of action operation hints.
824 */
825function getActionOperationHints(array $operations) {
826	$result = [];
827	$scriptids = [];
828	$scripts = [];
829
830	foreach ($operations as $operation) {
831		if ($operation['operationtype'] == OPERATION_TYPE_COMMAND
832				&& $operation['opcommand']['type'] == ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT) {
833			$scriptids[$operation['opcommand']['scriptid']] = true;
834		}
835	}
836
837	if ($scriptids) {
838		$scripts = API::Script()->get([
839			'output' => ['name'],
840			'scriptids' => array_keys($scriptids),
841			'preservekeys' => true
842		]);
843	}
844
845	foreach ($operations as $key => $operation) {
846		$result[$key] = [];
847
848		if ($operation['operationtype'] == OPERATION_TYPE_COMMAND) {
849			switch ($operation['opcommand']['type']) {
850				case ZBX_SCRIPT_TYPE_IPMI:
851					$result[$key][] = [bold(_('Run IPMI command').': '), BR(),
852						italic(zbx_nl2br($operation['opcommand']['command']))
853					];
854					break;
855
856				case ZBX_SCRIPT_TYPE_SSH:
857					$result[$key][] = [bold(_('Run SSH commands').': '), BR(),
858						italic(zbx_nl2br($operation['opcommand']['command']))
859					];
860					break;
861
862				case ZBX_SCRIPT_TYPE_TELNET:
863					$result[$key][] = [bold(_('Run TELNET commands').': '), BR(),
864						italic(zbx_nl2br($operation['opcommand']['command']))
865					];
866					break;
867
868				case ZBX_SCRIPT_TYPE_CUSTOM_SCRIPT:
869					if ($operation['opcommand']['execute_on'] == ZBX_SCRIPT_EXECUTE_ON_AGENT) {
870						$result[$key][] = [bold(_s('Run custom commands on %1$s', _('Zabbix agent')).': '),
871							BR(), italic(zbx_nl2br($operation['opcommand']['command']))
872						];
873					}
874					elseif ($operation['opcommand']['execute_on'] == ZBX_SCRIPT_EXECUTE_ON_PROXY) {
875						$result[$key][] = [bold(_s('Run custom commands on %1$s', _('Zabbix server (proxy)')).': '),
876							BR(), italic(zbx_nl2br($operation['opcommand']['command']))
877						];
878					}
879					else {
880						$result[$key][] = [bold(_s('Run custom commands on %1$s', _('Zabbix server')).': '),
881							BR(), italic(zbx_nl2br($operation['opcommand']['command']))
882						];
883					}
884					break;
885
886				case ZBX_SCRIPT_TYPE_GLOBAL_SCRIPT:
887					$scriptId = $operation['opcommand']['scriptid'];
888
889					if (isset($scripts[$scriptId])) {
890						$result[$key][] = [bold(_('Run global script').': '),
891							italic($scripts[$scriptId]['name'])
892						];
893					}
894					break;
895
896				default:
897					$result[$key][] = [bold(_('Run commands').': '), BR(),
898						italic(zbx_nl2br($operation['opcommand']['command']))
899					];
900			}
901		}
902	}
903
904	return $result;
905}
906
907/**
908 * Return an array of action conditions supported by the given event source.
909 *
910 * @param int $eventsource
911 *
912 * @return mixed
913 */
914function get_conditions_by_eventsource($eventsource) {
915	$conditions[EVENT_SOURCE_TRIGGERS] = [
916		CONDITION_TYPE_TRIGGER_NAME,
917		CONDITION_TYPE_TRIGGER,
918		CONDITION_TYPE_TRIGGER_SEVERITY,
919		CONDITION_TYPE_APPLICATION,
920		CONDITION_TYPE_HOST,
921		CONDITION_TYPE_HOST_GROUP,
922		CONDITION_TYPE_SUPPRESSED,
923		CONDITION_TYPE_EVENT_TAG,
924		CONDITION_TYPE_EVENT_TAG_VALUE,
925		CONDITION_TYPE_TEMPLATE,
926		CONDITION_TYPE_TIME_PERIOD
927	];
928	$conditions[EVENT_SOURCE_DISCOVERY] = [
929		CONDITION_TYPE_DHOST_IP,
930		CONDITION_TYPE_DCHECK,
931		CONDITION_TYPE_DOBJECT,
932		CONDITION_TYPE_DRULE,
933		CONDITION_TYPE_DSTATUS,
934		CONDITION_TYPE_PROXY,
935		CONDITION_TYPE_DVALUE,
936		CONDITION_TYPE_DSERVICE_PORT,
937		CONDITION_TYPE_DSERVICE_TYPE,
938		CONDITION_TYPE_DUPTIME
939	];
940	$conditions[EVENT_SOURCE_AUTOREGISTRATION] = [
941		CONDITION_TYPE_HOST_NAME,
942		CONDITION_TYPE_HOST_METADATA,
943		CONDITION_TYPE_PROXY
944	];
945	$conditions[EVENT_SOURCE_INTERNAL] = [
946		CONDITION_TYPE_APPLICATION,
947		CONDITION_TYPE_EVENT_TYPE,
948		CONDITION_TYPE_HOST,
949		CONDITION_TYPE_HOST_GROUP,
950		CONDITION_TYPE_TEMPLATE
951	];
952
953	if (isset($conditions[$eventsource])) {
954		return $conditions[$eventsource];
955	}
956
957	return $conditions[EVENT_SOURCE_TRIGGERS];
958}
959
960function get_opconditions_by_eventsource($eventsource) {
961	$conditions = [
962		EVENT_SOURCE_TRIGGERS => [CONDITION_TYPE_EVENT_ACKNOWLEDGED],
963		EVENT_SOURCE_DISCOVERY => []
964	];
965
966	if (isset($conditions[$eventsource])) {
967		return $conditions[$eventsource];
968	}
969}
970
971/**
972 * Return allowed operations types.
973 *
974 * @param int $eventsource
975 *
976 * @return array
977 */
978function getAllowedOperations($eventsource) {
979	if ($eventsource == EVENT_SOURCE_TRIGGERS) {
980		$operations = [
981			ACTION_OPERATION => [
982				OPERATION_TYPE_MESSAGE,
983				OPERATION_TYPE_COMMAND
984			],
985			ACTION_RECOVERY_OPERATION => [
986				OPERATION_TYPE_MESSAGE,
987				OPERATION_TYPE_COMMAND,
988				OPERATION_TYPE_RECOVERY_MESSAGE
989			],
990			ACTION_ACKNOWLEDGE_OPERATION => [
991				OPERATION_TYPE_MESSAGE,
992				OPERATION_TYPE_COMMAND,
993				OPERATION_TYPE_ACK_MESSAGE
994			]
995		];
996	}
997
998	if ($eventsource == EVENT_SOURCE_DISCOVERY) {
999		$operations[ACTION_OPERATION] = [
1000			OPERATION_TYPE_MESSAGE,
1001			OPERATION_TYPE_COMMAND,
1002			OPERATION_TYPE_HOST_ADD,
1003			OPERATION_TYPE_HOST_REMOVE,
1004			OPERATION_TYPE_GROUP_ADD,
1005			OPERATION_TYPE_GROUP_REMOVE,
1006			OPERATION_TYPE_TEMPLATE_ADD,
1007			OPERATION_TYPE_TEMPLATE_REMOVE,
1008			OPERATION_TYPE_HOST_ENABLE,
1009			OPERATION_TYPE_HOST_DISABLE,
1010			OPERATION_TYPE_HOST_INVENTORY
1011		];
1012	}
1013
1014	if ($eventsource == EVENT_SOURCE_AUTOREGISTRATION) {
1015		$operations[ACTION_OPERATION] = [
1016			OPERATION_TYPE_MESSAGE,
1017			OPERATION_TYPE_COMMAND,
1018			OPERATION_TYPE_HOST_ADD,
1019			OPERATION_TYPE_HOST_REMOVE,
1020			OPERATION_TYPE_GROUP_ADD,
1021			OPERATION_TYPE_GROUP_REMOVE,
1022			OPERATION_TYPE_TEMPLATE_ADD,
1023			OPERATION_TYPE_TEMPLATE_REMOVE,
1024			OPERATION_TYPE_HOST_ENABLE,
1025			OPERATION_TYPE_HOST_DISABLE,
1026			OPERATION_TYPE_HOST_INVENTORY
1027		];
1028	}
1029
1030	if ($eventsource == EVENT_SOURCE_INTERNAL) {
1031		$operations = [
1032			ACTION_OPERATION => [OPERATION_TYPE_MESSAGE],
1033			ACTION_RECOVERY_OPERATION => [
1034				OPERATION_TYPE_MESSAGE,
1035				OPERATION_TYPE_RECOVERY_MESSAGE
1036			]
1037		];
1038	}
1039
1040	return $operations;
1041}
1042
1043/**
1044 * Get operation type text label according $type value. If $type is equal null array of all available operation types
1045 * will be returned.
1046 *
1047 * @param int|null $type  Operation type, one of OPERATION_TYPE_* constant or null.
1048 *
1049 * @return string|array
1050 */
1051function operation_type2str($type) {
1052	$types = [
1053		OPERATION_TYPE_MESSAGE => _('Send message'),
1054		OPERATION_TYPE_COMMAND => _('Remote command'),
1055		OPERATION_TYPE_HOST_ADD => _('Add host'),
1056		OPERATION_TYPE_HOST_REMOVE => _('Remove host'),
1057		OPERATION_TYPE_HOST_ENABLE => _('Enable host'),
1058		OPERATION_TYPE_HOST_DISABLE => _('Disable host'),
1059		OPERATION_TYPE_GROUP_ADD => _('Add to host group'),
1060		OPERATION_TYPE_GROUP_REMOVE => _('Remove from host group'),
1061		OPERATION_TYPE_TEMPLATE_ADD => _('Link to template'),
1062		OPERATION_TYPE_TEMPLATE_REMOVE => _('Unlink from template'),
1063		OPERATION_TYPE_HOST_INVENTORY => _('Set host inventory mode'),
1064		OPERATION_TYPE_RECOVERY_MESSAGE => _('Notify all involved'),
1065		OPERATION_TYPE_ACK_MESSAGE => _('Notify all involved')
1066	];
1067
1068	if (is_null($type)) {
1069		return order_result($types);
1070	}
1071	elseif (isset($types[$type])) {
1072		return $types[$type];
1073	}
1074	else {
1075		return _('Unknown');
1076	}
1077}
1078
1079function sortOperations($eventsource, &$operations) {
1080	if ($eventsource == EVENT_SOURCE_TRIGGERS || $eventsource == EVENT_SOURCE_INTERNAL) {
1081		$esc_step_from = [];
1082		$esc_step_to = [];
1083		$esc_period = [];
1084		$operationTypes = [];
1085
1086		$simple_interval_parser = new CSimpleIntervalParser();
1087
1088		foreach ($operations as $key => $operation) {
1089			$esc_step_from[$key] = $operation['esc_step_from'];
1090			$esc_step_to[$key] = $operation['esc_step_to'];
1091			// Try to sort by "esc_period" in seconds, otherwise sort as string in case it's a macro or something invalid.
1092			$esc_period[$key] = ($simple_interval_parser->parse($operation['esc_period']) == CParser::PARSE_SUCCESS)
1093				? timeUnitToSeconds($operation['esc_period'])
1094				: $operation['esc_period'];
1095
1096			$operationTypes[$key] = $operation['operationtype'];
1097		}
1098		array_multisort($esc_step_from, SORT_ASC, $esc_step_to, SORT_ASC, $esc_period, SORT_ASC, $operationTypes, SORT_ASC, $operations);
1099	}
1100	else {
1101		CArrayHelper::sort($operations, ['operationtype']);
1102	}
1103}
1104
1105/**
1106 * Return an array of operators supported by the given action condition.
1107 *
1108 * @param int $conditiontype
1109 *
1110 * @return array
1111 */
1112function get_operators_by_conditiontype($conditiontype) {
1113	$operators[CONDITION_TYPE_HOST_GROUP] = [
1114		CONDITION_OPERATOR_EQUAL,
1115		CONDITION_OPERATOR_NOT_EQUAL
1116	];
1117	$operators[CONDITION_TYPE_TEMPLATE] = [
1118		CONDITION_OPERATOR_EQUAL,
1119		CONDITION_OPERATOR_NOT_EQUAL
1120	];
1121	$operators[CONDITION_TYPE_HOST] = [
1122		CONDITION_OPERATOR_EQUAL,
1123		CONDITION_OPERATOR_NOT_EQUAL
1124	];
1125	$operators[CONDITION_TYPE_TRIGGER] = [
1126		CONDITION_OPERATOR_EQUAL,
1127		CONDITION_OPERATOR_NOT_EQUAL
1128	];
1129	$operators[CONDITION_TYPE_TRIGGER_NAME] = [
1130		CONDITION_OPERATOR_LIKE,
1131		CONDITION_OPERATOR_NOT_LIKE
1132	];
1133	$operators[CONDITION_TYPE_TRIGGER_SEVERITY] = [
1134		CONDITION_OPERATOR_EQUAL,
1135		CONDITION_OPERATOR_NOT_EQUAL,
1136		CONDITION_OPERATOR_MORE_EQUAL,
1137		CONDITION_OPERATOR_LESS_EQUAL
1138	];
1139	$operators[CONDITION_TYPE_TIME_PERIOD] = [
1140		CONDITION_OPERATOR_IN,
1141		CONDITION_OPERATOR_NOT_IN
1142	];
1143	$operators[CONDITION_TYPE_SUPPRESSED] = [
1144		CONDITION_OPERATOR_NO,
1145		CONDITION_OPERATOR_YES
1146	];
1147	$operators[CONDITION_TYPE_DRULE] = [
1148		CONDITION_OPERATOR_EQUAL,
1149		CONDITION_OPERATOR_NOT_EQUAL
1150	];
1151	$operators[CONDITION_TYPE_DCHECK] = [
1152		CONDITION_OPERATOR_EQUAL,
1153		CONDITION_OPERATOR_NOT_EQUAL
1154	];
1155	$operators[CONDITION_TYPE_DOBJECT] = [
1156		CONDITION_OPERATOR_EQUAL
1157	];
1158	$operators[CONDITION_TYPE_PROXY] = [
1159		CONDITION_OPERATOR_EQUAL,
1160		CONDITION_OPERATOR_NOT_EQUAL
1161	];
1162	$operators[CONDITION_TYPE_DHOST_IP] = [
1163		CONDITION_OPERATOR_EQUAL,
1164		CONDITION_OPERATOR_NOT_EQUAL
1165	];
1166	$operators[CONDITION_TYPE_DSERVICE_TYPE] = [
1167		CONDITION_OPERATOR_EQUAL,
1168		CONDITION_OPERATOR_NOT_EQUAL
1169	];
1170	$operators[CONDITION_TYPE_DSERVICE_PORT] = [
1171		CONDITION_OPERATOR_EQUAL,
1172		CONDITION_OPERATOR_NOT_EQUAL
1173	];
1174	$operators[CONDITION_TYPE_DSTATUS] = [
1175		CONDITION_OPERATOR_EQUAL
1176	];
1177	$operators[CONDITION_TYPE_DUPTIME] = [
1178		CONDITION_OPERATOR_MORE_EQUAL,
1179		CONDITION_OPERATOR_LESS_EQUAL
1180	];
1181	$operators[CONDITION_TYPE_DVALUE] = [
1182		CONDITION_OPERATOR_EQUAL,
1183		CONDITION_OPERATOR_NOT_EQUAL,
1184		CONDITION_OPERATOR_MORE_EQUAL,
1185		CONDITION_OPERATOR_LESS_EQUAL,
1186		CONDITION_OPERATOR_LIKE,
1187		CONDITION_OPERATOR_NOT_LIKE
1188	];
1189	$operators[CONDITION_TYPE_EVENT_ACKNOWLEDGED] = [
1190		CONDITION_OPERATOR_EQUAL
1191	];
1192	$operators[CONDITION_TYPE_APPLICATION] = [
1193		CONDITION_OPERATOR_EQUAL,
1194		CONDITION_OPERATOR_LIKE,
1195		CONDITION_OPERATOR_NOT_LIKE
1196	];
1197	$operators[CONDITION_TYPE_HOST_NAME] = [
1198		CONDITION_OPERATOR_LIKE,
1199		CONDITION_OPERATOR_NOT_LIKE,
1200		CONDITION_OPERATOR_REGEXP,
1201		CONDITION_OPERATOR_NOT_REGEXP
1202	];
1203	$operators[CONDITION_TYPE_EVENT_TYPE] = [
1204		CONDITION_OPERATOR_EQUAL
1205	];
1206	$operators[CONDITION_TYPE_HOST_METADATA] = [
1207		CONDITION_OPERATOR_LIKE,
1208		CONDITION_OPERATOR_NOT_LIKE,
1209		CONDITION_OPERATOR_REGEXP,
1210		CONDITION_OPERATOR_NOT_REGEXP
1211	];
1212	$operators[CONDITION_TYPE_EVENT_TAG] = [
1213		CONDITION_OPERATOR_EQUAL,
1214		CONDITION_OPERATOR_NOT_EQUAL,
1215		CONDITION_OPERATOR_LIKE,
1216		CONDITION_OPERATOR_NOT_LIKE
1217	];
1218	$operators[CONDITION_TYPE_EVENT_TAG_VALUE] = [
1219		CONDITION_OPERATOR_EQUAL,
1220		CONDITION_OPERATOR_NOT_EQUAL,
1221		CONDITION_OPERATOR_LIKE,
1222		CONDITION_OPERATOR_NOT_LIKE
1223	];
1224
1225	if (isset($operators[$conditiontype])) {
1226		return $operators[$conditiontype];
1227	}
1228
1229	return [];
1230}
1231
1232function count_operations_delay($operations, $def_period) {
1233	$delays = [1 => 0];
1234	$periods = [];
1235	$max_step = 0;
1236
1237	$simple_interval_parser = new CSimpleIntervalParser();
1238
1239	$def_period = CMacrosResolverHelper::resolveTimeUnitMacros(
1240		[['def_period' => $def_period]], ['def_period']
1241	)[0]['def_period'];
1242
1243	$def_period = ($simple_interval_parser->parse($def_period) == CParser::PARSE_SUCCESS)
1244		? timeUnitToSeconds($def_period)
1245		: null;
1246
1247	$operations = CMacrosResolverHelper::resolveTimeUnitMacros($operations, ['esc_period']);
1248
1249	foreach ($operations as $operation) {
1250		$esc_period = ($simple_interval_parser->parse($operation['esc_period']) == CParser::PARSE_SUCCESS)
1251			? timeUnitToSeconds($operation['esc_period'])
1252			: null;
1253
1254		$esc_period = ($esc_period === null || $esc_period != 0) ? $esc_period : $def_period;
1255		$step_to = ($operation['esc_step_to'] != 0) ? $operation['esc_step_to'] : 9999;
1256
1257		if ($max_step < $operation['esc_step_from']) {
1258			$max_step = $operation['esc_step_from'];
1259		}
1260
1261		for ($i = $operation['esc_step_from']; $i <= $step_to; $i++) {
1262			if (!array_key_exists($i, $periods) || $esc_period === null || $periods[$i] > $esc_period) {
1263				$periods[$i] = $esc_period;
1264			}
1265		}
1266	}
1267
1268	for ($i = 1; $i <= $max_step; $i++) {
1269		$esc_period = array_key_exists($i, $periods) ? $periods[$i] : $def_period;
1270		$delays[$i + 1] = ($esc_period !== null && $delays[$i] !== null) ? $delays[$i] + $esc_period : null;
1271	}
1272
1273	return $delays;
1274}
1275
1276/**
1277 * Returns the names of the "Event type" action condition values.
1278 *
1279 * If the $type parameter is passed, returns the name of the specific value, otherwise - returns an array of all
1280 * supported values.
1281 *
1282 * @param string $type
1283 *
1284 * @return array|string
1285 */
1286function eventType($type = null) {
1287	$types = [
1288		EVENT_TYPE_ITEM_NOTSUPPORTED => _('Item in "not supported" state'),
1289		EVENT_TYPE_LLDRULE_NOTSUPPORTED => _('Low-level discovery rule in "not supported" state'),
1290		EVENT_TYPE_TRIGGER_UNKNOWN => _('Trigger in "unknown" state')
1291	];
1292
1293	if (is_null($type)) {
1294		return $types;
1295	}
1296
1297	return $types[$type];
1298}
1299
1300/**
1301 * Get data required to create messages, severity changes, actions icon with popup with event actions.
1302 *
1303 * @param array $events    Array with event objects with acknowledges.
1304 * @param array $triggers  Array of triggers.
1305 *
1306 * @return array
1307 */
1308function getEventsActionsIconsData(array $events, array $triggers): array {
1309	$messages = getEventsMessages($events);
1310	$severities = getEventsSeverityChanges($events, $triggers);
1311	$actions = getEventsAlertsOverview($events);
1312
1313	return [
1314		'data' => [
1315			'messages' => $messages['data'],
1316			'severities' => $severities['data'],
1317			'actions' => $actions
1318		],
1319		'userids' => $messages['userids'] + $severities['userids']
1320	];
1321}
1322
1323/**
1324 * Get data, required to create messages icon with popup with event messages.
1325 *
1326 * @param array  $events                                 Array with event objects with acknowledges.
1327 * @param string $events[]['eventid']                    Problem event ID.
1328 * @param array  $events[]['acknowledges']               Array with manual updates to problem.
1329 * @param string $events[]['acknowledges'][]['action']   Action that was performed by problem update.
1330 * @param string $events[]['acknowledges'][]['message']  Message text.
1331 * @param string $events[]['acknowledges'][]['clock']    Time when message was added.
1332 * @param string $events[]['acknowledges'][]['userid']   Author's userid.
1333 *
1334 * @return array
1335 */
1336function getEventsMessages(array $events) {
1337	$messages = [];
1338	$userids = [];
1339
1340	// Create array of messages for each event
1341	foreach ($events as $event) {
1342		$event_messages = [];
1343
1344		foreach ($event['acknowledges'] as $ack) {
1345			if (($ack['action'] & ZBX_PROBLEM_UPDATE_MESSAGE) == ZBX_PROBLEM_UPDATE_MESSAGE) {
1346				// Alias is mandatory for each user, so if alias is not returned, we don't have rights for this user.
1347				$event_messages[] = [
1348					'message' => $ack['message'],
1349					'userid' => $ack['userid'],
1350					'clock' => $ack['clock']
1351				];
1352
1353				$userids[$ack['userid']] = true;
1354			}
1355		}
1356
1357		CArrayHelper::sort($event_messages, [['field' => 'clock', 'order' => ZBX_SORT_DOWN]]);
1358
1359		$messages[$event['eventid']] = [
1360			'messages' => array_values($event_messages),
1361			'count' => count($event_messages)
1362		];
1363	}
1364
1365	return [
1366		'data' => $messages,
1367		'userids' => $userids
1368	];
1369}
1370
1371/**
1372 * Get data, required to create severity changes icon with popup with event severity changes.
1373 *
1374 * @param array  $events                                      Array with event objects with acknowledges.
1375 * @param string $events[]['eventid']                         Problem event ID.
1376 * @param string $events[]['severity']                        Current event severity.
1377 * @param string $events[]['objectid']                        Related trigger ID.
1378 * @param array  $events[]['acknowledges']                    Array with manual updates to problem.
1379 * @param string $events[]['acknowledges'][]['action']        Action that was performed by problem update.
1380 * @param string $events[]['acknowledges'][]['clock']         Time when severity was changed.
1381 * @param string $events[]['acknowledges'][]['old_severity']  Severity before the change.
1382 * @param string $events[]['acknowledges'][]['new_severity']  Severity after the change.
1383 * @param string $events[]['acknowledges'][]['userid']        Responsible user's userid.
1384 * @param array  $triggers                                    Related trigger data.
1385 * @param string $triggers[]['priority']                      Severity of trigger.
1386 *
1387 * @return array
1388 */
1389function getEventsSeverityChanges(array $events, array $triggers) {
1390	$severities = [];
1391	$userids = [];
1392
1393	// Create array of messages for each event.
1394	foreach ($events as $event) {
1395		$event_severities = [];
1396
1397		foreach ($event['acknowledges'] as $ack) {
1398			if (($ack['action'] & ZBX_PROBLEM_UPDATE_SEVERITY) == ZBX_PROBLEM_UPDATE_SEVERITY) {
1399				$event_severities[] = [
1400					'old_severity' => $ack['old_severity'],
1401					'new_severity' => $ack['new_severity'],
1402					'userid' => $ack['userid'],
1403					'clock' => $ack['clock']
1404				];
1405
1406				$userids[$ack['userid']] = true;
1407			}
1408		}
1409
1410		CArrayHelper::sort($event_severities, [['field' => 'clock', 'order' => ZBX_SORT_DOWN]]);
1411
1412		$severities[$event['eventid']] = [
1413			'severities' => array_values($event_severities),
1414			'count' => count($event_severities),
1415			'original_severity' => $triggers[$event['objectid']]['priority'],
1416			'current_severity' => $event['severity']
1417		];
1418	}
1419
1420	return [
1421		'data' => $severities,
1422		'userids' => $userids
1423	];
1424}
1425
1426/**
1427 * Get data, required to create actions icon.
1428 *
1429 * @param array  $events                 Array with event objects with acknowledges.
1430 * @param string $events[]['eventid']    Problem event ID.
1431 * @param string $events[]['r_eventid']  OK event ID.
1432 *
1433 * @return array  List indexed by eventid containing overview on event alerts.
1434 */
1435function getEventsAlertsOverview(array $events): array {
1436	$alert_eventids = [];
1437	$actions = [];
1438	$event_alert_state = [];
1439
1440	foreach ($events as $event) {
1441		// Get alerts for event.
1442		$alert_eventids[$event['eventid']] = true;
1443
1444		// Get alerts for related recovery events.
1445		if ($event['r_eventid'] != 0) {
1446			$alert_eventids[$event['r_eventid']] = true;
1447		}
1448	}
1449
1450	if ($alert_eventids) {
1451		$event_alert_state = array_combine(array_keys($alert_eventids), array_fill(0, count($alert_eventids), [
1452			'failed_cnt' => 0,
1453			'in_progress_cnt' => 0,
1454			'total_cnt' => 0
1455		]));
1456
1457		$alerts = API::Alert()->get([
1458			'groupCount' => true,
1459			'countOutput' => true,
1460			'filter' => ['status' => ALERT_STATUS_FAILED],
1461			'eventids' => array_keys($alert_eventids)
1462		]);
1463
1464		foreach ($alerts as $alert) {
1465			$event_alert_state[$alert['eventid']]['failed_cnt'] = (int) $alert['rowscount'];
1466		}
1467
1468		$alerts = API::Alert()->get([
1469			'groupCount' => true,
1470			'countOutput' => true,
1471			'filter' => ['status' => [ALERT_STATUS_NEW, ALERT_STATUS_NOT_SENT]],
1472			'eventids' => array_keys($alert_eventids)
1473		]);
1474
1475		foreach ($alerts as $alert) {
1476			$event_alert_state[$alert['eventid']]['in_progress_cnt'] = (int) $alert['rowscount'];
1477		}
1478
1479		$alerts = API::Alert()->get([
1480			'groupCount' => true,
1481			'countOutput' => true,
1482			'eventids' => array_keys($alert_eventids)
1483		]);
1484
1485		foreach ($alerts as $alert) {
1486			$event_alert_state[$alert['eventid']]['total_cnt'] = (int) $alert['rowscount'];
1487		}
1488	}
1489
1490	// Create array of actions for each event.
1491	foreach ($events as $event) {
1492		$event_actions = $event_alert_state[$event['eventid']];
1493		if ($event['r_eventid']) {
1494			$r_event_actions = $event_alert_state[$event['r_eventid']];
1495			$event_actions['failed_cnt'] += $r_event_actions['failed_cnt'];
1496			$event_actions['total_cnt'] += $r_event_actions['total_cnt'];
1497			$event_actions['in_progress_cnt'] += $r_event_actions['in_progress_cnt'];
1498		}
1499
1500		$actions[$event['eventid']] = [
1501			'count' => $event_actions['total_cnt'] + count($event['acknowledges']),
1502			'has_uncomplete_action' => (bool) $event_actions['in_progress_cnt'],
1503			'has_failed_action' => (bool) $event_actions['failed_cnt']
1504		];
1505	}
1506
1507	return $actions;
1508}
1509
1510/**
1511 * Get data, required to create table with all (automatic and manual) actions for Event details page.
1512 *
1513 * @param array  $event               Event object with acknowledges.
1514 * @param string $event['eventid']    Problem event ID.
1515 * @param string $event['r_eventid']  OK event ID.
1516 *
1517 * @return array
1518 */
1519function getEventDetailsActions(array $event) {
1520	$r_events = [];
1521
1522	// Select eventids for alert retrieval.
1523	$alert_eventids = [$event['eventid']];
1524
1525	if ($event['r_eventid'] != 0) {
1526		$alert_eventids[] = $event['r_eventid'];
1527
1528		$r_events = API::Event()->get([
1529			'output' => ['clock'],
1530			'eventids' => $event['r_eventid'],
1531			'preservekeys' => true
1532		]);
1533	}
1534
1535	$config = select_config();
1536
1537	// Get automatic actions (alerts).
1538	$alerts = API::Alert()->get([
1539		'output' => ['alerttype', 'clock', 'error', 'eventid', 'esc_step', 'mediatypeid', 'message', 'retries',
1540			'sendto', 'status', 'subject', 'userid', 'p_eventid', 'acknowledgeid'
1541		],
1542		'eventids' => $alert_eventids,
1543		'config' => $config['search_limit']
1544	]);
1545
1546	$actions = getSingleEventActions($event, $r_events, $alerts);
1547
1548	return [
1549		'actions' => $actions['actions'],
1550		'mediatypeids' => $actions['mediatypeids'],
1551		'userids' => $actions['userids'],
1552		'count' => $actions['count']
1553	];
1554}
1555
1556/**
1557 * Get array with all actions for single event.
1558 *
1559 * @param array   $event                              Event objects with acknowledges.
1560 * @param string  $event['eventid']                   Problem event ID.
1561 * @param string  $event['r_eventid']                 OK event ID.
1562 * @param string  $event['clock']                     Time when event occurred.
1563 * @param array   $event['acknowledges']              Array with manual updates to problem.
1564 * @param string  $event['acknowledges'][]['userid']  User ID.
1565 * @param array   $r_events                           Recovery event data for all requested events.
1566 * @param string  $r_events[]['clock']                Recovery event creation time.
1567 * @param array   $alerts                             Alert data for all requested alerts.
1568 * @param string  $alerts[]['eventid']                If of problem event for which this alert was generated.
1569 * @param string  $alerts[]['mediatypeid']            ID for mediatype used for alert.
1570 * @param string  $alerts[]['alerttype']              Type of alert.
1571 * @param string  $alerts[]['status']                 Alert status.
1572 * @param string  $alerts[]['userid']                 ID of alert recipient.
1573 *
1574 * @return array
1575 */
1576function getSingleEventActions(array $event, array $r_events, array $alerts) {
1577	$action_count = 0;
1578	$has_uncomplete_action = false;
1579	$has_failed_action = false;
1580	$mediatypeids = [];
1581	$userids = [];
1582
1583	// Create array of automatically and manually performed actions combined.
1584	$actions = [];
1585
1586	// Add row for problem generation event.
1587	$actions[] = [
1588		'action_type' => ZBX_EVENT_HISTORY_PROBLEM_EVENT,
1589		'clock' => $event['clock']
1590	];
1591
1592	// Add row for problem recovery event.
1593	if (array_key_exists($event['r_eventid'], $r_events)) {
1594		$actions[] = [
1595			'action_type' => ZBX_EVENT_HISTORY_RECOVERY_EVENT,
1596			'clock' => $r_events[$event['r_eventid']]['clock']
1597		];
1598	}
1599
1600	// Add manual operations.
1601	foreach ($event['acknowledges'] as $ack) {
1602		$ack['action_type'] = ZBX_EVENT_HISTORY_MANUAL_UPDATE;
1603		$actions[] = $ack;
1604
1605		$action_count++;
1606		$userids[$ack['userid']] = true;
1607	}
1608
1609	// Add alerts.
1610	foreach ($alerts as $alert) {
1611		// Add only alerts, related to current event.
1612		if (bccomp($alert['eventid'], $event['eventid']) == 0
1613				|| bccomp($alert['eventid'], $event['r_eventid']) == 0) {
1614			$alert['action_type'] = ZBX_EVENT_HISTORY_ALERT;
1615			$actions[] = $alert;
1616
1617			$action_count++;
1618
1619			if ($alert['alerttype'] == ALERT_TYPE_MESSAGE) {
1620				if ($alert['mediatypeid'] != 0) {
1621					$mediatypeids[$alert['mediatypeid']] = true;
1622				}
1623
1624				if ($alert['userid'] != 0) {
1625					$userids[$alert['userid']] = true;
1626				}
1627			}
1628
1629			if ($alert['status'] == ALERT_STATUS_NEW || $alert['status'] == ALERT_STATUS_NOT_SENT) {
1630				$has_uncomplete_action = true;
1631			}
1632			elseif ($alert['status'] == ALERT_STATUS_FAILED) {
1633				$has_failed_action = true;
1634			}
1635		}
1636	}
1637
1638	// Sort by action_type is done to put Recovery event before actions, resulted from it. Same for other action_type.
1639	CArrayHelper::sort($actions, [
1640		['field' => 'clock', 'order' => ZBX_SORT_DOWN],
1641		['field' => 'action_type', 'order' => ZBX_SORT_DOWN]
1642	]);
1643
1644	return [
1645		'actions' => array_values($actions),
1646		'count' => $action_count,
1647		'has_uncomplete_action' => $has_uncomplete_action,
1648		'has_failed_action' => $has_failed_action,
1649		'mediatypeids' => $mediatypeids,
1650		'userids' => $userids
1651	];
1652}
1653
1654/**
1655 * Get data required to create history list in problem update page.
1656 *
1657 * @param array  $event                               Array with event objects with acknowledges.
1658 * @param array  $event['acknowledges']               Array with manual updates to problem.
1659 * @param string $event['acknowledges'][]['clock']    Time when severity was changed.
1660 * @param string $event['acknowledges'][]['userid']   Responsible user's userid.
1661 *
1662 * @return array
1663 */
1664function getEventUpdates(array $event) {
1665	$userids = [];
1666
1667	foreach ($event['acknowledges'] as $ack) {
1668		$userids[$ack['userid']] = true;
1669	}
1670
1671	CArrayHelper::sort($event['acknowledges'], [['field' => 'clock', 'order' => ZBX_SORT_DOWN]]);
1672
1673	return [
1674		'data' => array_values($event['acknowledges']),
1675		'userids' => $userids
1676	];
1677}
1678
1679/**
1680 * Make icons (messages, severity changes, actions) for actions column.
1681 *
1682 * @param string $eventid                Id for event, for which icons are created.
1683 * @param array  $actions                Array of actions data.
1684 * @param array  $actions['messages']    Messages icon data.
1685 * @param array  $actions['severities']  Severity change icon data.
1686 * @param array  $actions['actions']     Actions icon data.
1687 * @param array  $users                  User name, surname and alias.
1688 * @param array  $config                 Zabbix config.
1689 *
1690 * @return CCol|string
1691 */
1692function makeEventActionsIcons($eventid, $actions, $users, $config) {
1693	$messages_icon = makeEventMessagesIcon($actions['messages'][$eventid], $users);
1694	$severities_icon = makeEventSeverityChangesIcon($actions['severities'][$eventid], $users, $config);
1695	$actions_icon = makeEventActionsIcon($actions['actions'][$eventid], $eventid);
1696
1697	$action_icons = [];
1698	if ($messages_icon !== null) {
1699		$action_icons[] = $messages_icon;
1700	}
1701	if ($severities_icon !== null) {
1702		$action_icons[] = $severities_icon;
1703	}
1704	if ($actions_icon !== null) {
1705		$action_icons[] = $actions_icon;
1706	}
1707
1708	return $action_icons ? (new CCol($action_icons))->addClass(ZBX_STYLE_NOWRAP) : '';
1709}
1710
1711/**
1712 * Create icon with hintbox for event messages.
1713 *
1714 * @param array  $data
1715 * @param array  $data['messages']               Array of messages.
1716 * @param string $data['messages'][]['message']  Message text.
1717 * @param string $data['messages'][]['clock']    Message creation time.
1718 * @param array  $users                          User name, surname and alias.
1719 *
1720 * @return CSpan|null
1721 */
1722function makeEventMessagesIcon(array $data, array $users) {
1723	$total = $data['count'];
1724
1725	$table = (new CTableInfo())->setHeader([_('Time'), _('User'), _('Message')]);
1726
1727	for ($i = 0; $i < $total && $i < ZBX_WIDGET_ROWS; $i++) {
1728		$message = $data['messages'][$i];
1729
1730		// Added in order to reuse makeActionTableUser().
1731		$message['action_type'] = ZBX_EVENT_HISTORY_MANUAL_UPDATE;
1732
1733		$table->addRow([
1734			zbx_date2str(DATE_TIME_FORMAT_SECONDS, $message['clock']),
1735			makeActionTableUser($message, $users),
1736			zbx_nl2br($message['message'])
1737		]);
1738	}
1739
1740	return $total
1741		? makeActionIcon([
1742			'icon' => ZBX_STYLE_ACTION_ICON_MSGS,
1743			'button' => true,
1744			'hint' => [
1745				$table,
1746				($total > ZBX_WIDGET_ROWS)
1747					? (new CDiv(
1748						(new CDiv(
1749							(new CDiv(_s('Displaying %1$s of %2$s found', ZBX_WIDGET_ROWS, $total)))
1750								->addClass(ZBX_STYLE_TABLE_STATS)
1751						))->addClass(ZBX_STYLE_PAGING_BTN_CONTAINER)
1752					))->addClass(ZBX_STYLE_TABLE_PAGING)
1753					: null
1754			],
1755			'num' => $total,
1756			'aria-label' => _xn('%1$s message', '%1$s messages', $total, 'screen reader', $total)
1757		])
1758		: null;
1759}
1760
1761/**
1762 * Create icon with hintbox for event severity changes.
1763 *
1764 * @param array  $data
1765 * @param array  $data['severities']                    Array of severities.
1766 * @param string $data['severities'][]['old_severity']  Event severity before change.
1767 * @param string $data['severities'][]['new_severity']  Event severity after change.
1768 * @param string $data['severities'][]['clock']         Severity change time.
1769 * @param string $data['original_severity']             Severity before change.
1770 * @param string $data['current_severity']              Current severity.
1771 * @param int    $data['count']                         Total number of severity changes.
1772 * @param array  $users                                 User name, surname and alias.
1773 * @param array  $config                                Zabbix config.
1774 *
1775 * @return CSpan|null
1776 */
1777function makeEventSeverityChangesIcon(array $data, array $users, array $config) {
1778	$total = $data['count'];
1779
1780	$table = (new CTableInfo())->setHeader([_('Time'), _('User'), _('Severity changes')]);
1781
1782	for ($i = 0; $i < $total && $i < ZBX_WIDGET_ROWS; $i++) {
1783		$severity = $data['severities'][$i];
1784
1785		// Added in order to reuse makeActionTableUser().
1786		$severity['action_type'] = ZBX_EVENT_HISTORY_MANUAL_UPDATE;
1787
1788		// severity changes
1789		$old_severity_name = getSeverityName($severity['old_severity'], $config);
1790		$new_severity_name = getSeverityName($severity['new_severity'], $config);
1791
1792		$table->addRow([
1793			zbx_date2str(DATE_TIME_FORMAT_SECONDS, $severity['clock']),
1794			makeActionTableUser($severity, $users),
1795			$old_severity_name.'&nbsp;&rArr;&nbsp;'.$new_severity_name
1796		]);
1797	}
1798
1799	// select icon
1800	if ($data['original_severity'] > $data['current_severity']) {
1801		$icon_style = ZBX_STYLE_ACTION_ICON_SEV_DOWN;
1802		$aria_label = _x('Severity decreased', 'screen reader');
1803	}
1804	elseif ($data['original_severity'] < $data['current_severity']) {
1805		$icon_style = ZBX_STYLE_ACTION_ICON_SEV_UP;
1806		$aria_label = _x('Severity increased', 'screen reader');
1807	}
1808	else {
1809		$icon_style = ZBX_STYLE_ACTION_ICON_SEV_CHANGED;
1810		$aria_label = _x('Severity changed', 'screen reader');
1811	}
1812
1813	return $total
1814		? makeActionIcon([
1815			'button' => true,
1816			'icon' => $icon_style,
1817			'hint' => [
1818				$table,
1819				($total > ZBX_WIDGET_ROWS)
1820					? (new CDiv(
1821						(new CDiv(
1822							(new CDiv(_s('Displaying %1$s of %2$s found', ZBX_WIDGET_ROWS, $total)))
1823								->addClass(ZBX_STYLE_TABLE_STATS)
1824						))->addClass(ZBX_STYLE_PAGING_BTN_CONTAINER)
1825					))->addClass(ZBX_STYLE_TABLE_PAGING)
1826					: null
1827			],
1828			'aria-label' => $aria_label
1829		])
1830		: null;
1831}
1832
1833/**
1834 * @param array  $actions                      Array with all actions sorted by clock.
1835 * @param int    $actions[]['action_type']     Type of action table entry (ZBX_EVENT_HISTORY_*).
1836 * @param string $actions[]['clock']           Time, when action was performed.
1837 * @param string $actions[]['message']         Message sent by alert, or written by manual update, or remote command text.
1838 * @param string $actions[]['action']          Flag with problem update operation performed (only for ZBX_EVENT_HISTORY_MANUAL_UPDATE).
1839 * @param string $actions[]['alerttype']       Type of alert (only for ZBX_EVENT_HISTORY_ALERT).
1840 * @param string $actions[]['mediatypeid']     Id for mediatype, where alert message was sent (only for ZBX_EVENT_HISTORY_ALERT).
1841 * @param string $actions[]['error']           Error message in case of failed alert (only for ZBX_EVENT_HISTORY_ALERT).
1842 * @param array  $users                        User name, surname and alias.
1843 * @param array  $mediatypes                   Mediatypes with maxattempts value and description.
1844 * @param string $mediatypes[]['name']         Mediatype name.
1845 * @param string $mediatypes[]['maxattempts']  Maximum attempts for this mediatype.
1846 * @param array  $config                       Zabbix config.
1847 *
1848 * @return CTableInfo
1849 */
1850function makeEventActionsTable(array $actions, array $users, array $mediatypes, array $config): CTableInfo {
1851	$action_count = count($actions);
1852
1853	$table = (new CTableInfo())->setHeader([
1854		_('Time'), _('User/Recipient'), _('Action'), _('Message/Command'), _('Status'), _('Info')
1855	]);
1856
1857	for ($i = 0; $i < $action_count && $i < ZBX_WIDGET_ROWS; $i++) {
1858		$action = $actions[$i];
1859
1860		$message = '';
1861		if ($action['action_type'] == ZBX_EVENT_HISTORY_MANUAL_UPDATE
1862				&& ($action['action'] & ZBX_PROBLEM_UPDATE_MESSAGE) == ZBX_PROBLEM_UPDATE_MESSAGE) {
1863			$message = zbx_nl2br($action['message']);
1864		}
1865		elseif ($action['action_type'] == ZBX_EVENT_HISTORY_ALERT) {
1866			if ($action['alerttype'] == ALERT_TYPE_COMMAND) {
1867				$message = _('Remote command');
1868			}
1869			elseif ($action['alerttype'] == ALERT_TYPE_MESSAGE) {
1870				$message = array_key_exists($action['mediatypeid'], $mediatypes)
1871					? $mediatypes[$action['mediatypeid']]['name']
1872					: '';
1873			}
1874		}
1875
1876		$table->addRow([
1877			zbx_date2str(DATE_TIME_FORMAT_SECONDS, $action['clock']),
1878			makeActionTableUser($action, $users),
1879			makeActionTableIcon($action, $config),
1880			$message,
1881			makeActionTableStatus($action),
1882			makeActionTableInfo($action, $mediatypes)
1883		]);
1884	}
1885
1886	return $table;
1887}
1888
1889/**
1890 * Create icon with hintbox for event actions.
1891 *
1892 * @param array  $data
1893 * @param int    $data['count']                     Number of actions.
1894 * @param bool   $data['has_uncomplete_action']     Does the event have at least one uncompleted alert action.
1895 * @param bool   $data['has_failed_action']         Does the event have at least one failed alert action.
1896 * @param string $eventid
1897 *
1898 * @return CButton|null
1899 */
1900function makeEventActionsIcon(array $data, $eventid): ?CButton {
1901	// Number of meaningful actions.
1902	$total = $data['count'];
1903
1904	// select icon
1905	if ($data['has_failed_action']) {
1906		$icon_style = ZBX_STYLE_ACTIONS_NUM_RED;
1907	}
1908	elseif ($data['has_uncomplete_action']) {
1909		$icon_style = ZBX_STYLE_ACTIONS_NUM_YELLOW;
1910	}
1911	else {
1912		$icon_style = ZBX_STYLE_ACTIONS_NUM_GRAY;
1913	}
1914
1915	return $total
1916		? makeActionIcon([
1917			'icon' => $icon_style,
1918			'button' => true,
1919			'num' => $total,
1920			'aria-label' => _xn('%1$s action', '%1$s actions', $total, 'screen reader', $total)
1921		])->setAjaxHint([
1922			'type' => 'eventactions',
1923			'data' => ['eventid' => $eventid]
1924		])
1925		: null;
1926}
1927
1928/**
1929 * Get table with list of event actions for event details page.
1930 *
1931 * @param array  $data
1932 * @param array  $data['actions']                     Array with all actions sorted by clock.
1933 * @param int    $data['actions'][]['action_type']    Type of action table entry (ZBX_EVENT_HISTORY_*).
1934 * @param string $data['actions'][]['clock']          Time, when action was performed.
1935 * @param string $data['actions'][]['message']        Message sent by alert, or written by manual update, or remote command text.
1936 * @param string $data['actions'][]['alerttype']      Type of alert (only for ZBX_EVENT_HISTORY_ALERT).
1937 * @param string $data['actions'][]['esc_step']       Alert escalation step (only for ZBX_EVENT_HISTORY_ALERT).
1938 * @param string $data['actions'][]['subject']        Message alert subject (only for ZBX_EVENT_HISTORY_ALERT).
1939 * @param string $data['actions'][]['p_eventid']      Problem eventid that was reason for alert (only for ZBX_EVENT_HISTORY_ALERT).
1940 * @param string $data['actions'][]['acknowledgeid']  Problem update action that was reason for alert (only for ZBX_EVENT_HISTORY_ALERT).
1941 * @param array  $users                               User name, surname and alias.
1942 * @param array  $mediatypes                          Mediatypes with maxattempts value.
1943 * @param array  $config                              Zabbix config.
1944 *
1945 * @return CTableInfo
1946 */
1947function makeEventDetailsActionsTable(array $data, array $users, array $mediatypes, array $config) {
1948	$table = (new CTableInfo())->setHeader([
1949		_('Step'), _('Time'), _('User/Recipient'), _('Action'), _('Message/Command'), _('Status'), _('Info')
1950	]);
1951
1952	foreach ($data['actions'] as $action) {
1953		$esc_step = '';
1954
1955		if ($action['action_type'] == ZBX_EVENT_HISTORY_ALERT && $action['p_eventid'] == 0
1956				&& $action['acknowledgeid'] == 0) {
1957			/*
1958			 * Escalation step should be displayed, only if alert is caused by problem event.
1959			 * Escalation step should not be displayed, if alert is caused by resolve event, or by problem update.
1960			 */
1961			$esc_step = $action['esc_step'];
1962		}
1963
1964		$message = '';
1965		if ($action['action_type'] == ZBX_EVENT_HISTORY_ALERT && $action['alerttype'] == ALERT_TYPE_MESSAGE) {
1966			$message = [bold($action['subject']), BR(), BR(), zbx_nl2br($action['message'])];
1967		}
1968		elseif (($action['action_type'] == ZBX_EVENT_HISTORY_ALERT && $action['alerttype'] == ALERT_TYPE_COMMAND)
1969				|| $action['action_type'] == ZBX_EVENT_HISTORY_MANUAL_UPDATE) {
1970			$message = zbx_nl2br($action['message']);
1971		}
1972
1973		$table->addRow([
1974			$esc_step,
1975			zbx_date2str(DATE_TIME_FORMAT_SECONDS, $action['clock']),
1976			makeEventDetailsTableUser($action, $users),
1977			makeActionTableIcon($action, $config),
1978			$message,
1979			makeActionTableStatus($action),
1980			makeActionTableInfo($action, $mediatypes)
1981		]);
1982	}
1983
1984	return $table;
1985}
1986
1987/**
1988 * Get table with list of event updates for update event page.
1989 *
1990 * @param array  $actions                   Array with all actions sorted by clock.
1991 * @param string $actions[]['clock']        Time, when action was performed.
1992 * @param string $actions[]['message']      Message sent by alert, or written by manual update, or remote command text.
1993 * @param array  $users                     User name, surname and alias.
1994 * @param array  $config                    Zabbix config.
1995 *
1996 * @return CTable
1997 */
1998function makeEventHistoryTable(array $actions, array $users, array $config) {
1999	$table = (new CTable())
2000		->addStyle('width: 100%;')
2001		->setHeader([_('Time'), _('User'), _('User action'), _('Message')]);
2002
2003	foreach ($actions as $action) {
2004		// Added in order to reuse makeActionTableUser() and makeActionTableIcon()
2005		$action['action_type'] = ZBX_EVENT_HISTORY_MANUAL_UPDATE;
2006
2007		$table->addRow([
2008			zbx_date2str(DATE_TIME_FORMAT_SECONDS, $action['clock']),
2009			makeActionTableUser($action, $users),
2010			makeActionTableIcon($action, $config),
2011			(new CCol(zbx_nl2br($action['message'])))->addClass(ZBX_STYLE_TABLE_FORMS_OVERFLOW_BREAK)
2012		]);
2013	}
2014
2015	return $table;
2016}
2017
2018/**
2019 * Creates username for message author or alert receiver.
2020 *
2021 * @param array  $action                 Array of action data.
2022 * @param int    $action['action_type']  Type of event table action (ZBX_EVENT_HISTORY_*).
2023 * @param string $action['alerttype']    Type of alert.
2024 * @param string $action['userid']       ID of message author, or alert receiver.
2025 * @param array  $users                  Array with user data - alias, name, surname.
2026 *
2027 * @return string
2028 */
2029function makeActionTableUser(array $action, array $users) {
2030	if (($action['action_type'] == ZBX_EVENT_HISTORY_ALERT && $action['alerttype'] == ALERT_TYPE_MESSAGE)
2031			|| $action['action_type'] == ZBX_EVENT_HISTORY_MANUAL_UPDATE) {
2032		return array_key_exists($action['userid'], $users)
2033			? getUserFullname($users[$action['userid']])
2034			: _('Inaccessible user');
2035	}
2036	else {
2037		return '';
2038	}
2039}
2040
2041/**
2042 * Creates username for message author or alert receiver. Also contains 'sendto' for message actions.
2043 *
2044 * @param array  $action
2045 * @param int    $action['action_type']  Type of event table action (ZBX_EVENT_HISTORY_*).
2046 * @param string $action['alerttype']    Type of alert.
2047 * @param array  $action['userid']       ID of message author, or alert receiver.
2048 * @param array  $action['sendto']       Receiver media address for automatic action.
2049 * @param array  $users                  Array with user data - alias, name, surname.
2050 *
2051 * @return string
2052 */
2053function makeEventDetailsTableUser(array $action, array $users) {
2054	if ($action['action_type'] == ZBX_EVENT_HISTORY_ALERT && $action['alerttype'] == ALERT_TYPE_MESSAGE) {
2055		return array_key_exists($action['userid'], $users)
2056			? [getUserFullname($users[$action['userid']]), BR(), italic(zbx_nl2br($action['sendto']))]
2057			: _('Inaccessible user');
2058	}
2059	elseif ($action['action_type'] == ZBX_EVENT_HISTORY_MANUAL_UPDATE) {
2060		return array_key_exists($action['userid'], $users)
2061			? getUserFullname($users[$action['userid']])
2062			: _('Inaccessible user');
2063	}
2064	else {
2065		return '';
2066	}
2067}
2068
2069/**
2070 * Make appropriate icon for event action.
2071 *
2072 * @param array  $action                  Array with actions table data.
2073 * @param int    $action['action_type']   Type of action table entry (ZBX_EVENT_HISTORY_*).
2074 * @param int    $action['action']        Flag with problem update operation performed. (only for ZBX_EVENT_HISTORY_MANUAL_UPDATE)
2075 * @param int    $action['old_severity']  Severity before problem update. (only for ZBX_EVENT_HISTORY_MANUAL_UPDATE)
2076 * @param int    $action['new_severity']  Severity after problem update. (only for ZBX_EVENT_HISTORY_MANUAL_UPDATE)
2077 * @param int    $action['alerttype']     Type of alert. (only for ZBX_EVENT_HISTORY_ALERT)
2078 * @param array  $config                  Zabbix config.
2079 *
2080 * @return CSpan
2081 */
2082function makeActionTableIcon(array $action, array $config) {
2083	switch ($action['action_type']) {
2084		case ZBX_EVENT_HISTORY_PROBLEM_EVENT:
2085			return makeActionIcon(['icon' => ZBX_STYLE_PROBLEM_GENERATED, 'title' => _('Problem created')]);
2086
2087		case ZBX_EVENT_HISTORY_RECOVERY_EVENT:
2088			return makeActionIcon(['icon' => ZBX_STYLE_PROBLEM_RECOVERY, 'title' => _('Problem resolved')]);
2089
2090		case ZBX_EVENT_HISTORY_MANUAL_UPDATE:
2091			$action_icons = [];
2092
2093			if (($action['action'] & ZBX_PROBLEM_UPDATE_CLOSE) == ZBX_PROBLEM_UPDATE_CLOSE) {
2094				$action_icons[] = makeActionIcon([
2095					'icon' => ZBX_STYLE_ACTION_ICON_CLOSE,
2096					'title' => _('Manually closed')
2097				]);
2098			}
2099
2100			if (($action['action'] & ZBX_PROBLEM_UPDATE_ACKNOWLEDGE) == ZBX_PROBLEM_UPDATE_ACKNOWLEDGE) {
2101				$action_icons[] = makeActionIcon(['icon' => ZBX_STYLE_ACTION_ICON_ACK, 'title' => _('Acknowledged')]);
2102			}
2103
2104			if (($action['action'] & ZBX_PROBLEM_UPDATE_UNACKNOWLEDGE) == ZBX_PROBLEM_UPDATE_UNACKNOWLEDGE) {
2105				$action_icons[] = makeActionIcon(['icon' => ZBX_STYLE_ACTION_ICON_UNACK, 'title' => _('Unacknowledged')]);
2106			}
2107
2108			if (($action['action'] & ZBX_PROBLEM_UPDATE_MESSAGE) == ZBX_PROBLEM_UPDATE_MESSAGE) {
2109				$action_icons[] = makeActionIcon(['icon' => ZBX_STYLE_ACTION_ICON_MSG, 'title' => _('Message')]);
2110			}
2111
2112			if (($action['action'] & ZBX_PROBLEM_UPDATE_SEVERITY) == ZBX_PROBLEM_UPDATE_SEVERITY) {
2113				$action_type = ($action['new_severity'] > $action['old_severity'])
2114					? ZBX_STYLE_ACTION_ICON_SEV_UP
2115					: ZBX_STYLE_ACTION_ICON_SEV_DOWN;
2116
2117				$old_severity_name = getSeverityName($action['old_severity'], $config);
2118				$new_severity_name = getSeverityName($action['new_severity'], $config);
2119				$hint = $old_severity_name.'&nbsp;&rArr;&nbsp;'.$new_severity_name;
2120
2121				$action_icons[] = makeActionIcon(['button' => true, 'icon' => $action_type, 'hint' => $hint]);
2122			}
2123
2124			return (new CCol($action_icons))->addClass(ZBX_STYLE_NOWRAP);
2125
2126		case ZBX_EVENT_HISTORY_ALERT:
2127			$action_icon = ($action['alerttype'] == ALERT_TYPE_COMMAND)
2128					? ZBX_STYLE_ACTION_COMMAND
2129					: ZBX_STYLE_ACTION_MESSAGE;
2130			$title = ($action['alerttype'] == ALERT_TYPE_COMMAND)
2131				? _('Remote command')
2132				: _('Alert message');
2133
2134			return makeActionIcon(['icon' => $action_icon, 'title' => $title]);
2135	}
2136}
2137
2138/**
2139 * Creates span with alert status text.
2140 *
2141 * @param array  $action
2142 * @param int    $action['action_type']  Type of event table action (ZBX_EVENT_HISTORY_*).
2143 * @param string $action['status']       Alert status.
2144 * @param string $action['alerttype']    Type of alert.
2145 *
2146 * @return CSpan|string
2147 */
2148function makeActionTableStatus(array $action) {
2149	if ($action['action_type'] == ZBX_EVENT_HISTORY_ALERT) {
2150		switch ($action['status']) {
2151			case ALERT_STATUS_SENT:
2152				$status_label = ($action['alerttype'] == ALERT_TYPE_COMMAND)
2153					? _('Executed')
2154					: _('Sent');
2155				$status_color = ZBX_STYLE_GREEN;
2156				break;
2157
2158			case ALERT_STATUS_NEW:
2159			case ALERT_STATUS_NOT_SENT:
2160				$status_label = _('In progress');
2161				$status_color = ZBX_STYLE_YELLOW;
2162				break;
2163
2164			default:
2165				$status_label = _('Failed');
2166				$status_color = ZBX_STYLE_RED;
2167				break;
2168		}
2169
2170		return (new CSpan($status_label))->addClass($status_color);
2171	}
2172	else {
2173		return '';
2174	}
2175}
2176
2177/**
2178 * Creates div with alert info icons.
2179 *
2180 * @param array  $action
2181 * @param int    $action['action_type']        Type of event table action (ZBX_EVENT_HISTORY_*).
2182 * @param string $action['status']             Alert status.
2183 * @param string $action['alerttype']          Type of alert.
2184 * @param string $action['mediatypeid']        ID for mediatype, where alert message was sent.
2185 * @param string $action['retries']            How many retries was done for pending alert message.
2186 * @param array  $mediatypes                   Array of media type data.
2187 * @param array  $mediatypes[]['maxattempts']  Maximum attempts for this mediatype.
2188 *
2189 * @return CDiv|string
2190 */
2191function makeActionTableInfo(array $action, array $mediatypes) {
2192	if ($action['action_type'] == ZBX_EVENT_HISTORY_ALERT) {
2193		$info_icons = [];
2194
2195		if ($action['alerttype'] == ALERT_TYPE_MESSAGE
2196				&& ($action['status'] == ALERT_STATUS_NEW || $action['status'] == ALERT_STATUS_NOT_SENT)) {
2197			$info_icons[] = makeWarningIcon(array_key_exists($action['mediatypeid'], $mediatypes)
2198				? _n('%1$s retry left', '%1$s retries left',
2199						$mediatypes[$action['mediatypeid']]['maxattempts'] - $action['retries']
2200				)
2201				: ''
2202			);
2203		}
2204		elseif ($action['error'] !== '') {
2205			$info_icons[] = makeErrorIcon($action['error']);
2206		}
2207
2208		return makeInformationList($info_icons);
2209	}
2210	else {
2211		return '';
2212	}
2213}
2214