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
22require_once dirname(__FILE__).'/include/config.inc.php';
23require_once dirname(__FILE__).'/include/hosts.inc.php';
24require_once dirname(__FILE__).'/include/triggers.inc.php';
25require_once dirname(__FILE__).'/include/forms.inc.php';
26
27$page['title'] = _('Configuration of triggers');
28$page['file'] = 'triggers.php';
29$page['scripts'] = ['multiselect.js'];
30
31require_once dirname(__FILE__).'/include/page_header.php';
32
33// VAR											TYPE	OPTIONAL	FLAGS	VALIDATION		EXCEPTION
34$fields = [
35	'groupid' =>								[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,			null],
36	'hostid' =>									[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,			null],
37	'triggerid' =>								[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,			'(isset({form}) && ({form} == "update"))'],
38	'copy_type' =>								[T_ZBX_INT, O_OPT, P_SYS,
39													IN([COPY_TYPE_TO_HOST_GROUP, COPY_TYPE_TO_HOST,
40														COPY_TYPE_TO_TEMPLATE
41													]),
42													'isset({copy})'
43												],
44	'copy_mode' =>								[T_ZBX_INT, O_OPT, P_SYS,	IN('0'),		null],
45	'type' =>									[T_ZBX_INT, O_OPT, null,	IN('0,1'),		null],
46	'description' =>							[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,		'isset({add}) || isset({update})', _('Name')],
47	'expression' =>								[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,		'isset({add}) || isset({update})', _('Expression')],
48	'recovery_expression' =>					[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,		'(isset({add}) || isset({update})) && isset({recovery_mode}) && {recovery_mode} == '.ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION.'', _('Recovery expression')],
49	'recovery_mode' =>							[T_ZBX_INT, O_OPT, null,	IN(ZBX_RECOVERY_MODE_EXPRESSION.','.ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION.','.ZBX_RECOVERY_MODE_NONE),	null],
50	'priority' =>								[T_ZBX_INT, O_OPT, null,	IN('0,1,2,3,4,5'), 'isset({add}) || isset({update})'],
51	'comments' =>								[T_ZBX_STR, O_OPT, null,	null,			'isset({add}) || isset({update})'],
52	'url' =>									[T_ZBX_STR, O_OPT, null,	null,			'isset({add}) || isset({update})'],
53	'correlation_mode' =>						[T_ZBX_STR, O_OPT, null,	IN(ZBX_TRIGGER_CORRELATION_NONE.','.ZBX_TRIGGER_CORRELATION_TAG),	null],
54	'correlation_tag' =>						[T_ZBX_STR, O_OPT, null,	null,			'isset({add}) || isset({update})'],
55	'status' =>									[T_ZBX_STR, O_OPT, null,	null,			null],
56	'expression_constructor' =>					[T_ZBX_INT, O_OPT, null,	NOT_EMPTY,		'isset({toggle_expression_constructor})'],
57	'recovery_expression_constructor' =>		[T_ZBX_INT, O_OPT, null,	NOT_EMPTY,		'isset({toggle_recovery_expression_constructor})'],
58	'expr_temp' =>								[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,		'(isset({add_expression}) || isset({and_expression}) || isset({or_expression}) || isset({replace_expression}))', _('Expression')],
59	'expr_target_single' =>						[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,		'(isset({and_expression}) || isset({or_expression}) || isset({replace_expression}))', _('Target')],
60	'recovery_expr_temp' =>						[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,		'(isset({add_recovery_expression}) || isset({and_recovery_expression}) || isset({or_recovery_expression}) || isset({replace_recovery_expression}))', _('Recovery expression')],
61	'recovery_expr_target_single' =>			[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,		'(isset({and_recovery_expression}) || isset({or_recovery_expression}) || isset({replace_recovery_expression}))', _('Target')],
62	'dependencies' =>							[T_ZBX_INT, O_OPT, null,	DB_ID,			null],
63	'new_dependency' =>							[T_ZBX_INT, O_OPT, null,	DB_ID.'{}>0',	'isset({add_dependency})'],
64	'g_triggerid' =>							[T_ZBX_INT, O_OPT, null,	DB_ID,			null],
65	'copy_targetids' =>							[T_ZBX_INT, O_OPT, null,	DB_ID,			null],
66	'visible' =>								[T_ZBX_STR, O_OPT, null,	null,			null],
67	'tags' =>									[T_ZBX_STR, O_OPT, null,	null,			null],
68	'manual_close' =>							[T_ZBX_INT, O_OPT, null,
69													IN([ZBX_TRIGGER_MANUAL_CLOSE_NOT_ALLOWED,
70														ZBX_TRIGGER_MANUAL_CLOSE_ALLOWED
71													]),
72													null
73												],
74	// filter
75	'filter_set' =>								[T_ZBX_STR, O_OPT, P_SYS,	null,			null],
76	'filter_rst' =>								[T_ZBX_STR, O_OPT, P_SYS,	null,			null],
77	'filter_priority' =>						[T_ZBX_INT, O_OPT, null,
78													IN([
79														-1, TRIGGER_SEVERITY_NOT_CLASSIFIED,
80														TRIGGER_SEVERITY_INFORMATION, TRIGGER_SEVERITY_WARNING,
81														TRIGGER_SEVERITY_AVERAGE, TRIGGER_SEVERITY_HIGH,
82														TRIGGER_SEVERITY_DISASTER
83													]), null
84												],
85	'filter_state' =>							[T_ZBX_INT, O_OPT, null,
86													IN([-1, TRIGGER_STATE_NORMAL, TRIGGER_STATE_UNKNOWN]), null
87												],
88	'filter_status' =>							[T_ZBX_INT, O_OPT, null,
89													IN([-1, TRIGGER_STATUS_ENABLED, TRIGGER_STATUS_DISABLED]), null
90												],
91	'filter_value' =>							[T_ZBX_INT, O_OPT, null,
92													IN([-1, TRIGGER_VALUE_FALSE, TRIGGER_VALUE_TRUE]), null
93												],
94	'filter_evaltype' =>						[T_ZBX_INT, O_OPT, null,
95													IN([TAG_EVAL_TYPE_AND_OR, TAG_EVAL_TYPE_OR]), null
96												],
97	'filter_tags' =>							[T_ZBX_STR, O_OPT, null,	null,			null],
98	// actions
99	'action' =>									[T_ZBX_STR, O_OPT, P_SYS|P_ACT,
100													IN('"trigger.masscopyto","trigger.massdelete","trigger.massdisable",'.
101														'"trigger.massenable","trigger.massupdate","trigger.massupdateform"'
102													),
103													null
104												],
105	'toggle_expression_constructor' =>			[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
106	'toggle_recovery_expression_constructor' =>	[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
107	'add_expression' =>							[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
108	'add_recovery_expression' =>				[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
109	'and_expression' =>							[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
110	'and_recovery_expression' =>				[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
111	'or_expression' =>							[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
112	'or_recovery_expression' =>					[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
113	'replace_expression' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
114	'replace_recovery_expression' =>			[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
115	'remove_expression' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
116	'remove_recovery_expression' =>				[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
117	'test_expression' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
118	'add_dependency' =>							[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
119	'group_enable' =>							[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
120	'group_disable' =>							[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
121	'group_delete' =>							[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
122	'copy' =>									[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
123	'clone' =>									[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
124	'add' =>									[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
125	'update' =>									[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
126	'massupdate' =>								[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
127	'delete' =>									[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
128	'cancel' =>									[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
129	'form' =>									[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
130	'form_refresh' =>							[T_ZBX_INT, O_OPT, null,	null,		null],
131	// sort and sortorder
132	'sort' =>									[T_ZBX_STR, O_OPT, P_SYS, IN('"description","priority","status"'),		null],
133	'sortorder' =>								[T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'),	null]
134];
135
136check_fields($fields);
137
138$_REQUEST['status'] = isset($_REQUEST['status']) ? TRIGGER_STATUS_ENABLED : TRIGGER_STATUS_DISABLED;
139
140// Validate permissions to single trigger.
141$triggerId = getRequest('triggerid');
142
143if ($triggerId !== null) {
144	$trigger = API::Trigger()->get([
145		'output' => ['triggerid'],
146		'triggerids' => [$triggerId],
147		'editable' => true
148	]);
149
150	if (!$trigger) {
151		access_deny();
152	}
153}
154
155// Validate permissions to a group of triggers for mass enable/disable actions.
156$triggerIds = getRequest('g_triggerid', []);
157$triggerIds = zbx_toArray($triggerIds);
158
159if ($triggerIds) {
160	$triggerIds = array_unique($triggerIds);
161
162	$triggers = API::Trigger()->get([
163		'output' => [],
164		'triggerids' => $triggerIds,
165		'editable' => true
166	]);
167
168	if (count($triggers) != count($triggerIds)) {
169		uncheckTableRows(getRequest('hostid'), zbx_objectValues($triggers, 'triggerid'));
170	}
171}
172
173if (getRequest('groupid') && !isWritableHostGroups([getRequest('groupid')])) {
174	access_deny();
175}
176if (getRequest('hostid') && !isWritableHostTemplates([getRequest('hostid')])) {
177	access_deny();
178}
179
180/*
181 * Actions
182 */
183$expression_action = '';
184if (hasRequest('add_expression')) {
185	$_REQUEST['expression'] = getRequest('expr_temp');
186	$_REQUEST['expr_temp'] = '';
187}
188elseif (hasRequest('and_expression')) {
189	$expression_action = 'and';
190}
191elseif (hasRequest('or_expression')) {
192	$expression_action = 'or';
193}
194elseif (hasRequest('replace_expression')) {
195	$expression_action = 'r';
196}
197elseif (hasRequest('remove_expression')) {
198	$expression_action = 'R';
199	$_REQUEST['expr_target_single'] = getRequest('remove_expression');
200}
201
202$recovery_expression_action = '';
203if (hasRequest('add_recovery_expression')) {
204	$_REQUEST['recovery_expression'] = getRequest('recovery_expr_temp');
205	$_REQUEST['recovery_expr_temp'] = '';
206}
207elseif (hasRequest('and_recovery_expression')) {
208	$recovery_expression_action = 'and';
209}
210elseif (hasRequest('or_recovery_expression')) {
211	$recovery_expression_action = 'or';
212}
213elseif (hasRequest('replace_recovery_expression')) {
214	$recovery_expression_action = 'r';
215}
216elseif (hasRequest('remove_recovery_expression')) {
217	$recovery_expression_action = 'R';
218	$_REQUEST['recovery_expr_target_single'] = getRequest('remove_recovery_expression');
219}
220
221if (hasRequest('clone') && hasRequest('triggerid')) {
222	unset($_REQUEST['triggerid']);
223	$_REQUEST['form'] = 'clone';
224}
225elseif (hasRequest('add') || hasRequest('update')) {
226	$tags = getRequest('tags', []);
227	$dependencies = zbx_toObject(getRequest('dependencies', []), 'triggerid');
228
229	// Remove empty new tag lines.
230	foreach ($tags as $key => $tag) {
231		if ($tag['tag'] === '' && $tag['value'] === '') {
232			unset($tags[$key]);
233		}
234	}
235
236	$description = getRequest('description', '');
237	$expression = getRequest('expression', '');
238	$recovery_mode = getRequest('recovery_mode', ZBX_RECOVERY_MODE_EXPRESSION);
239	$recovery_expression = getRequest('recovery_expression', '');
240	$type = getRequest('type', 0);
241	$url = getRequest('url', '');
242	$priority = getRequest('priority', TRIGGER_SEVERITY_NOT_CLASSIFIED);
243	$comments = getRequest('comments', '');
244	$correlation_mode = getRequest('correlation_mode', ZBX_TRIGGER_CORRELATION_NONE);
245	$correlation_tag = getRequest('correlation_tag', '');
246	$manual_close = getRequest('manual_close', ZBX_TRIGGER_MANUAL_CLOSE_NOT_ALLOWED);
247	$status = getRequest('status', TRIGGER_STATUS_ENABLED);
248
249	if (hasRequest('add')) {
250		$trigger = [
251			'description' => $description,
252			'expression' => $expression,
253			'recovery_mode' => $recovery_mode,
254			'type' => $type,
255			'url' => $url,
256			'priority' => $priority,
257			'comments' => $comments,
258			'tags' => $tags,
259			'manual_close' => $manual_close,
260			'dependencies' => $dependencies,
261			'status' => $status
262		];
263		switch ($recovery_mode) {
264			case ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION:
265				$trigger['recovery_expression'] = $recovery_expression;
266				// break; is not missing here
267
268			case ZBX_RECOVERY_MODE_EXPRESSION:
269				$trigger['correlation_mode'] = $correlation_mode;
270				if ($correlation_mode == ZBX_TRIGGER_CORRELATION_TAG) {
271					$trigger['correlation_tag'] = $correlation_tag;
272				}
273				break;
274		}
275
276		$result = (bool) API::Trigger()->create($trigger);
277
278		show_messages($result, _('Trigger added'), _('Cannot add trigger'));
279	}
280	else {
281		$db_triggers = API::Trigger()->get([
282			'output' => ['expression', 'description', 'url', 'status', 'priority', 'comments', 'templateid', 'type',
283				'flags', 'recovery_mode', 'recovery_expression', 'correlation_mode', 'correlation_tag', 'manual_close'
284			],
285			'selectDependencies' => ['triggerid'],
286			'selectTags' => ['tag', 'value'],
287			'triggerids' => getRequest('triggerid')
288		]);
289
290		$db_triggers = CMacrosResolverHelper::resolveTriggerExpressions($db_triggers,
291			['sources' => ['expression', 'recovery_expression']]
292		);
293
294		$db_trigger = reset($db_triggers);
295
296		$trigger = [];
297
298		if ($db_trigger['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
299			if ($db_trigger['templateid'] == 0) {
300				if ($db_trigger['description'] !== $description) {
301					$trigger['description'] = $description;
302				}
303				if ($db_trigger['expression'] !== $expression) {
304					$trigger['expression'] = $expression;
305				}
306				if ($db_trigger['recovery_mode'] != $recovery_mode) {
307					$trigger['recovery_mode'] = $recovery_mode;
308				}
309				switch ($recovery_mode) {
310					case ZBX_RECOVERY_MODE_RECOVERY_EXPRESSION:
311						if ($db_trigger['recovery_expression'] !== $recovery_expression) {
312							$trigger['recovery_expression'] = $recovery_expression;
313						}
314						// break; is not missing here
315
316					case ZBX_RECOVERY_MODE_EXPRESSION:
317						if ($db_trigger['correlation_mode'] != $correlation_mode) {
318							$trigger['correlation_mode'] = $correlation_mode;
319						}
320						if ($correlation_mode == ZBX_TRIGGER_CORRELATION_TAG
321								&& $db_trigger['correlation_tag'] !== $correlation_tag) {
322							$trigger['correlation_tag'] = $correlation_tag;
323						}
324						break;
325				}
326			}
327
328			if ($db_trigger['type'] != $type) {
329				$trigger['type'] = $type;
330			}
331			if ($db_trigger['url'] !== $url) {
332				$trigger['url'] = $url;
333			}
334			if ($db_trigger['priority'] != $priority) {
335				$trigger['priority'] = $priority;
336			}
337			if ($db_trigger['comments'] !== $comments) {
338				$trigger['comments'] = $comments;
339			}
340
341			$db_tags = $db_trigger['tags'];
342			CArrayHelper::sort($db_tags, ['tag', 'value']);
343			CArrayHelper::sort($tags, ['tag', 'value']);
344			if (array_values($db_tags) !== array_values($tags)) {
345				$trigger['tags'] = $tags;
346			}
347
348			if ($db_trigger['manual_close'] != $manual_close) {
349				$trigger['manual_close'] = $manual_close;
350			}
351
352			$db_dependencies = $db_trigger['dependencies'];
353			CArrayHelper::sort($db_dependencies, ['triggerid']);
354			CArrayHelper::sort($dependencies, ['triggerid']);
355			if (array_values($db_dependencies) !== array_values($dependencies)) {
356				$trigger['dependencies'] = $dependencies;
357			}
358		}
359
360		if ($db_trigger['status'] != $status) {
361			$trigger['status'] = $status;
362		}
363
364		if ($trigger) {
365			$trigger['triggerid'] = getRequest('triggerid');
366
367			$result = (bool) API::Trigger()->update($trigger);
368		}
369		else {
370			$result = true;
371		}
372
373		show_messages($result, _('Trigger updated'), _('Cannot update trigger'));
374	}
375
376	if ($result) {
377		unset($_REQUEST['form']);
378		uncheckTableRows(getRequest('hostid'));
379	}
380}
381elseif (isset($_REQUEST['delete']) && isset($_REQUEST['triggerid'])) {
382	$result = API::Trigger()->delete([getRequest('triggerid')]);
383
384	if ($result) {
385		unset($_REQUEST['form'], $_REQUEST['triggerid']);
386		uncheckTableRows(getRequest('hostid'));
387	}
388	show_messages($result, _('Trigger deleted'), _('Cannot delete trigger'));
389}
390elseif (isset($_REQUEST['add_dependency']) && isset($_REQUEST['new_dependency'])) {
391	if (!isset($_REQUEST['dependencies'])) {
392		$_REQUEST['dependencies'] = [];
393	}
394	foreach ($_REQUEST['new_dependency'] as $triggerid) {
395		if (!uint_in_array($triggerid, $_REQUEST['dependencies'])) {
396			array_push($_REQUEST['dependencies'], $triggerid);
397		}
398	}
399}
400elseif (hasRequest('action') && getRequest('action') === 'trigger.massupdate'
401		&& hasRequest('massupdate') && hasRequest('g_triggerid')) {
402	$result = true;
403	$visible = getRequest('visible', []);
404
405	if ($visible) {
406		$triggerids = getRequest('g_triggerid');
407		$triggers_to_update = [];
408
409		$triggers = API::Trigger()->get([
410			'output' => ['triggerid', 'templateid'],
411			'triggerids' => $triggerids,
412			'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
413			'preservekeys' => true
414		]);
415
416		if ($triggers) {
417			$tags = getRequest('tags', []);
418
419			// Remove empty new tag lines.
420			foreach ($tags as $key => $tag) {
421				if ($tag['tag'] === '' && $tag['value'] === '') {
422					unset($tags[$key]);
423				}
424			}
425
426			foreach ($triggerids as $triggerid) {
427				if (array_key_exists($triggerid, $triggers)) {
428					$trigger = ['triggerid' => $triggerid];
429
430					if (array_key_exists('priority', $visible)) {
431						$trigger['priority'] = getRequest('priority');
432					}
433
434					if (array_key_exists('dependencies', $visible)) {
435						$trigger['dependencies'] = zbx_toObject(getRequest('dependencies', []), 'triggerid');
436					}
437
438					if (array_key_exists('tags', $visible)) {
439						$trigger['tags'] = $tags;
440					}
441
442					if ($triggers[$triggerid]['templateid'] == 0 && array_key_exists('manual_close', $visible)) {
443						$trigger['manual_close'] = getRequest('manual_close');
444					}
445
446					$triggers_to_update[] = $trigger;
447				}
448			}
449		}
450
451		if ($triggers_to_update) {
452			$result = (bool) API::Trigger()->update($triggers_to_update);
453		}
454	}
455
456	if ($result) {
457		unset($_REQUEST['form'], $_REQUEST['g_triggerid']);
458		uncheckTableRows(getRequest('hostid'));
459	}
460	show_messages($result, _('Trigger updated'), _('Cannot update trigger'));
461}
462elseif (hasRequest('action') && str_in_array(getRequest('action'), ['trigger.massenable', 'trigger.massdisable']) && hasRequest('g_triggerid')) {
463	$enable = (getRequest('action') == 'trigger.massenable');
464	$status = $enable ? TRIGGER_STATUS_ENABLED : TRIGGER_STATUS_DISABLED;
465	$update = [];
466
467	// get requested triggers with permission check
468	$dbTriggers = API::Trigger()->get([
469		'output' => ['triggerid', 'status'],
470		'triggerids' => getRequest('g_triggerid'),
471		'editable' => true
472	]);
473
474	if ($dbTriggers) {
475		foreach ($dbTriggers as $dbTrigger) {
476			$update[] = [
477				'triggerid' => $dbTrigger['triggerid'],
478				'status' => $status
479			];
480		}
481
482		$result = API::Trigger()->update($update);
483	}
484	else {
485		$result = true;
486	}
487
488	$updated = count($update);
489	$messageSuccess = $enable
490		? _n('Trigger enabled', 'Triggers enabled', $updated)
491		: _n('Trigger disabled', 'Triggers disabled', $updated);
492	$messageFailed = $enable
493		? _n('Cannot enable trigger', 'Cannot enable triggers', $updated)
494		: _n('Cannot disable trigger', 'Cannot disable triggers', $updated);
495
496	if ($result) {
497		uncheckTableRows(getRequest('hostid'));
498		unset($_REQUEST['g_triggerid']);
499	}
500	show_messages($result, $messageSuccess, $messageFailed);
501}
502elseif (hasRequest('action') && getRequest('action') === 'trigger.masscopyto' && hasRequest('copy')
503		&& hasRequest('g_triggerid')) {
504	if (getRequest('copy_targetids', []) && hasRequest('copy_type')) {
505		// hosts or templates
506		if (getRequest('copy_type') == COPY_TYPE_TO_HOST || getRequest('copy_type') == COPY_TYPE_TO_TEMPLATE) {
507			$hosts_ids = getRequest('copy_targetids');
508		}
509		// host groups
510		else {
511			$hosts_ids = [];
512			$group_ids = getRequest('copy_targetids');
513
514			$db_hosts = DBselect(
515				'SELECT DISTINCT h.hostid'.
516				' FROM hosts h,hosts_groups hg'.
517				' WHERE h.hostid=hg.hostid'.
518					' AND '.dbConditionInt('hg.groupid', $group_ids)
519			);
520			while ($db_host = DBfetch($db_hosts)) {
521				$hosts_ids[] = $db_host['hostid'];
522			}
523		}
524
525		DBstart();
526
527		$result = copyTriggersToHosts(getRequest('g_triggerid'), $hosts_ids, getRequest('hostid'));
528		$result = DBend($result);
529
530		$triggers_count = count(getRequest('g_triggerid'));
531
532		if ($result) {
533			uncheckTableRows(getRequest('hostid'));
534			unset($_REQUEST['g_triggerid']);
535		}
536		show_messages($result,
537			_n('Trigger copied', 'Triggers copied', $triggers_count),
538			_n('Cannot copy trigger', 'Cannot copy triggers', $triggers_count)
539		);
540	}
541	else {
542		show_error_message(_('No target selected'));
543	}
544}
545elseif (hasRequest('action') && getRequest('action') == 'trigger.massdelete' && hasRequest('g_triggerid')) {
546	$result = API::Trigger()->delete(getRequest('g_triggerid'));
547
548	if ($result) {
549		uncheckTableRows(getRequest('hostid'));
550	}
551	show_messages($result, _('Triggers deleted'), _('Cannot delete triggers'));
552}
553
554$config = select_config();
555
556/*
557 * Display
558 */
559if ((getRequest('action') === 'trigger.massupdateform' || hasRequest('massupdate')) && hasRequest('g_triggerid')) {
560	$data = getTriggerMassupdateFormData();
561	$data['action'] = 'trigger.massupdate';
562	$triggersView = new CView('configuration.triggers.massupdate', $data);
563	$triggersView->render();
564	$triggersView->show();
565}
566elseif (isset($_REQUEST['form'])) {
567	$data = [
568		'config' => $config,
569		'form' => getRequest('form'),
570		'form_refresh' => getRequest('form_refresh'),
571		'parent_discoveryid' => null,
572		'dependencies' => getRequest('dependencies', []),
573		'db_dependencies' => [],
574		'triggerid' => getRequest('triggerid'),
575		'expression' => getRequest('expression', ''),
576		'recovery_expression' => getRequest('recovery_expression', ''),
577		'expr_temp' => getRequest('expr_temp', ''),
578		'recovery_expr_temp' => getRequest('recovery_expr_temp', ''),
579		'recovery_mode' => getRequest('recovery_mode', 0),
580		'description' => getRequest('description', ''),
581		'type' => getRequest('type', 0),
582		'priority' => getRequest('priority', TRIGGER_SEVERITY_NOT_CLASSIFIED),
583		'status' => getRequest('status', TRIGGER_STATUS_ENABLED),
584		'comments' => getRequest('comments', ''),
585		'url' => getRequest('url', ''),
586		'expression_constructor' => getRequest('expression_constructor', IM_ESTABLISHED),
587		'recovery_expression_constructor' => getRequest('recovery_expression_constructor', IM_ESTABLISHED),
588		'limited' => false,
589		'templates' => [],
590		'hostid' => getRequest('hostid', 0),
591		'expression_action' => $expression_action,
592		'recovery_expression_action' => $recovery_expression_action,
593		'tags' => array_values(getRequest('tags', [])),
594		'correlation_mode' => getRequest('correlation_mode', ZBX_TRIGGER_CORRELATION_NONE),
595		'correlation_tag' => getRequest('correlation_tag', ''),
596		'manual_close' => getRequest('manual_close', ZBX_TRIGGER_MANUAL_CLOSE_NOT_ALLOWED),
597		'groupid' => getRequest('groupid', 0)
598	];
599
600	$triggersView = new CView('configuration.triggers.edit', getTriggerFormData($data));
601	$triggersView->render();
602	$triggersView->show();
603}
604elseif (hasRequest('action') && getRequest('action') === 'trigger.masscopyto' && hasRequest('g_triggerid')) {
605	$data = getCopyElementsFormData('g_triggerid', _('Triggers'));
606	$data['action'] = 'trigger.masscopyto';
607
608	// render view
609	$triggersView = new CView('configuration.copy.elements', $data);
610	$triggersView->render();
611	$triggersView->show();
612}
613else {
614	$data = [
615		'pageFilter' => new CPageFilter([
616			'groups' => [
617				'with_hosts_and_templates' => true,
618				'editable' => true
619			],
620			'hosts' => [
621				'templated_hosts' => true,
622				'editable' => true
623			],
624			'triggers' => ['editable' => true],
625			'groupid' => getRequest('groupid'),
626			'hostid' => getRequest('hostid')
627		])
628	];
629
630	$data += [
631		'config' => $config,
632		'hostid' => $data['pageFilter']->hostid,
633		'groupid' => $data['pageFilter']->groupid,
634		'triggers' => [],
635		'profileIdx' => 'web.triggers.filter',
636		'active_tab' => CProfile::get('web.triggers.filter.active', 1),
637		'sort' => getRequest('sort', CProfile::get('web.'.$page['file'].'.sort', 'description')),
638		'sortorder' => getRequest('sortorder', CProfile::get('web.'.$page['file'].'.sortorder', ZBX_SORT_UP)),
639		'show_value_column' => false
640	];
641
642	if ($data['hostid'] == 0) {
643		foreach ($data['pageFilter']->hosts as $host) {
644			if ($host['status'] == HOST_STATUS_MONITORED || $host['status'] == HOST_STATUS_NOT_MONITORED) {
645				$data['show_value_column'] = true;
646				break;
647			}
648		}
649	}
650	else {
651		$data['show_value_column'] = ($data['pageFilter']->hosts[$data['hostid']]['status'] == HOST_STATUS_MONITORED
652				|| $data['pageFilter']->hosts[$data['hostid']]['status'] == HOST_STATUS_NOT_MONITORED
653		);
654	}
655
656	CProfile::update('web.'.$page['file'].'.sort', $data['sort'], PROFILE_TYPE_STR);
657	CProfile::update('web.'.$page['file'].'.sortorder', $data['sortorder'], PROFILE_TYPE_STR);
658
659	if (hasRequest('filter_set')) {
660		CProfile::update('web.triggers.filter_priority', getRequest('filter_priority', -1), PROFILE_TYPE_INT);
661		CProfile::update('web.triggers.filter_state', getRequest('filter_state', -1), PROFILE_TYPE_INT);
662		CProfile::update('web.triggers.filter_status', getRequest('filter_status', -1), PROFILE_TYPE_INT);
663
664		if ($data['show_value_column']) {
665			CProfile::update('web.triggers.filter_value', getRequest('filter_value', -1), PROFILE_TYPE_INT);
666		}
667
668		CProfile::update('web.triggers.filter.evaltype', getRequest('filter_evaltype', TAG_EVAL_TYPE_AND_OR),
669			PROFILE_TYPE_INT
670		);
671
672		$filter_tags = ['tags' => [], 'values' => [], 'operators' => []];
673		foreach (getRequest('filter_tags', []) as $filter_tag) {
674			if ($filter_tag['tag'] === '' && $filter_tag['value'] === '') {
675				continue;
676			}
677
678			$filter_tags['tags'][] = $filter_tag['tag'];
679			$filter_tags['values'][] = $filter_tag['value'];
680			$filter_tags['operators'][] = $filter_tag['operator'];
681		}
682		CProfile::updateArray('web.triggers.filter.tags.tag', $filter_tags['tags'], PROFILE_TYPE_STR);
683		CProfile::updateArray('web.triggers.filter.tags.value', $filter_tags['values'], PROFILE_TYPE_STR);
684		CProfile::updateArray('web.triggers.filter.tags.operator', $filter_tags['operators'], PROFILE_TYPE_INT);
685	}
686	elseif (hasRequest('filter_rst')) {
687		CProfile::delete('web.triggers.filter_priority');
688		CProfile::delete('web.triggers.filter_state');
689		CProfile::delete('web.triggers.filter_status');
690
691		if ($data['show_value_column']) {
692			CProfile::delete('web.triggers.filter_value');
693		}
694
695		CProfile::delete('web.triggers.filter.evaltype');
696		CProfile::deleteIdx('web.triggers.filter.tags.tag');
697		CProfile::deleteIdx('web.triggers.filter.tags.value');
698		CProfile::deleteIdx('web.triggers.filter.tags.operator');
699	}
700
701	$data += [
702		'filter_priority' => CProfile::get('web.triggers.filter_priority', -1),
703		'filter_state' => CProfile::get('web.triggers.filter_state', -1),
704		'filter_status' => CProfile::get('web.triggers.filter_status', -1)
705	];
706
707	if ($data['show_value_column']) {
708		$data['filter_value'] = CProfile::get('web.triggers.filter_value', -1);
709	}
710
711	$data['filter_evaltype'] = CProfile::get('web.triggers.filter.evaltype', TAG_EVAL_TYPE_AND_OR);
712
713	$data['filter_tags'] = [];
714	foreach (CProfile::getArray('web.triggers.filter.tags.tag', []) as $i => $tag) {
715		$data['filter_tags'][] = [
716			'tag' => $tag,
717			'value' => CProfile::get('web.triggers.filter.tags.value', null, $i),
718			'operator' => CProfile::get('web.triggers.filter.tags.operator', null, $i)
719		];
720	}
721
722	// get triggers
723	if ($data['pageFilter']->hostsSelected) {
724		$options = [
725			'editable' => true,
726			'sortfield' => $data['sort'],
727			'limit' => $config['search_limit'] + 1
728		];
729
730		if ($data['sort'] === 'status') {
731			$options['output'] = ['triggerid', 'status', 'state'];
732		}
733		else {
734			$options['output'] = ['triggerid', $data['sort']];
735		}
736
737		if ($data['filter_priority'] != -1) {
738			$options['filter']['priority'] = $data['filter_priority'];
739		}
740
741		switch ($data['filter_state']) {
742			case TRIGGER_STATE_NORMAL:
743				$options['filter']['state'] = TRIGGER_STATE_NORMAL;
744				$options['filter']['status'] = TRIGGER_STATUS_ENABLED;
745				break;
746
747			case TRIGGER_STATE_UNKNOWN:
748				$options['filter']['state'] = TRIGGER_STATE_UNKNOWN;
749				$options['filter']['status'] = TRIGGER_STATUS_ENABLED;
750				break;
751
752			default:
753				if ($data['filter_status'] != -1) {
754					$options['filter']['status'] = $data['filter_status'];
755				}
756		}
757
758		if ($data['filter_tags']) {
759			$options['evaltype'] = $data['filter_evaltype'];
760			$options['tags'] = $data['filter_tags'];
761		}
762
763		if ($data['pageFilter']->hostid > 0) {
764			$options['hostids'] = $data['pageFilter']->hostid;
765		}
766		elseif ($data['pageFilter']->groupid > 0) {
767			$options['groupids'] = $data['pageFilter']->groupids;
768		}
769
770		if ($data['show_value_column'] && $data['filter_value'] != -1) {
771			$options['filter']['value'] = $data['filter_value'];
772
773			// Exclude templates when all hosts selected and filtered by specific values.
774			if ($data['hostid'] == 0) {
775				$hosts = API::Host()->get([
776					'output' => [],
777					'editable' => true,
778					'groupids' => ($data['pageFilter']->groupid > 0) ? $data['pageFilter']->groupid : null,
779					'preservekeys' => true
780				]);
781				$options['hostids'] = array_keys($hosts);
782			}
783		}
784
785		$data['triggers'] = API::Trigger()->get($options);
786	}
787
788	// sort for paging
789	if ($data['sort'] === 'status') {
790		orderTriggersByStatus($data['triggers'], $data['sortorder']);
791	}
792	else {
793		order_result($data['triggers'], $data['sort'], $data['sortorder']);
794	}
795
796	// paging
797	$url = (new CUrl('triggers.php'))
798		->setArgument('groupid', $data['groupid'])
799		->setArgument('hostid', $data['hostid']);
800
801	$data['paging'] = getPagingLine($data['triggers'], $data['sortorder'], $url);
802
803	$data['triggers'] = API::Trigger()->get([
804		'output' => ['triggerid', 'expression', 'description', 'status', 'priority', 'error', 'templateid', 'state',
805			'recovery_mode', 'recovery_expression', 'value'
806		],
807		'selectHosts' => ['hostid', 'host', 'name', 'status'],
808		'selectDependencies' => ['triggerid', 'description'],
809		'selectDiscoveryRule' => ['itemid', 'name'],
810		'selectTags' => ['tag', 'value'],
811		'triggerids' => zbx_objectValues($data['triggers'], 'triggerid')
812	]);
813
814	// sort for displaying full results
815	if ($data['sort'] === 'status') {
816		orderTriggersByStatus($data['triggers'], $data['sortorder']);
817	}
818	else {
819		order_result($data['triggers'], $data['sort'], $data['sortorder']);
820	}
821
822	$data['tags'] = makeTags($data['triggers'], true, 'triggerid', ZBX_TAG_COUNT_DEFAULT, $data['filter_tags']);
823
824	$depTriggerIds = [];
825	foreach ($data['triggers'] as $trigger) {
826		foreach ($trigger['dependencies'] as $depTrigger) {
827			$depTriggerIds[$depTrigger['triggerid']] = true;
828		}
829	}
830
831	$dependencyTriggers = [];
832	if ($depTriggerIds) {
833		$dependencyTriggers = API::Trigger()->get([
834			'output' => ['triggerid', 'description', 'status', 'flags'],
835			'selectHosts' => ['hostid', 'name'],
836			'triggerids' => array_keys($depTriggerIds),
837			'preservekeys' => true
838		]);
839
840		foreach ($data['triggers'] as &$trigger) {
841			order_result($trigger['dependencies'], 'description', ZBX_SORT_UP);
842		}
843		unset($trigger);
844
845		foreach ($dependencyTriggers as &$dependencyTrigger) {
846			order_result($dependencyTrigger['hosts'], 'name', ZBX_SORT_UP);
847		}
848		unset($dependencyTrigger);
849	}
850
851	$data['dependencyTriggers'] = $dependencyTriggers;
852
853	$data['parent_templates'] = getTriggerParentTemplates($data['triggers'], ZBX_FLAG_DISCOVERY_NORMAL);
854
855	// Do not show 'Info' column, if it is a template.
856	if ($data['hostid']) {
857		$data['showInfoColumn'] = (bool) API::Host()->get([
858			'output' => [],
859			'hostids' => $data['hostid']
860		]);
861	}
862	else {
863		$data['showInfoColumn'] = true;
864	}
865
866	// render view
867	$triggersView = new CView('configuration.triggers.list', $data);
868	$triggersView->render();
869	$triggersView->show();
870}
871
872require_once dirname(__FILE__).'/include/page_footer.php';
873