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 prepareSubfilterOutput($label, $data, $subfilter, $subfilterName) {
23	CArrayHelper::sort($data, ['value', 'name']);
24
25	$output = [new CTag('h3', true, $label)];
26
27	foreach ($data as $id => $element) {
28		$element['name'] = CHtml::encode($element['name']);
29
30		// is activated
31		if (str_in_array($id, $subfilter)) {
32			$output[] = (new CSpan([
33				(new CLinkAction($element['name']))
34					->onClick(CHtml::encode(
35						'javascript: create_var("zbx_filter", "subfilter_set", "1", false);'.
36						'create_var("zbx_filter", '.json_encode($subfilterName.'['.$id.']').', null, true);'
37					)),
38				' ',
39				new CSup($element['count'])
40			]))
41				->addClass(ZBX_STYLE_NOWRAP)
42				->addClass(ZBX_STYLE_SUBFILTER)
43				->addClass(ZBX_STYLE_SUBFILTER_ENABLED);
44		}
45		// isn't activated
46		else {
47			// subfilter has 0 items
48			if ($element['count'] == 0) {
49				$output[] = (new CSpan([
50					(new CSpan($element['name']))->addClass(ZBX_STYLE_GREY),
51					' ',
52					new CSup($element['count'])
53				]))->addClass(ZBX_STYLE_SUBFILTER);
54			}
55			else {
56				$link = (new CLinkAction($element['name']))
57					->onClick(CHtml::encode(
58						'javascript: create_var("zbx_filter", "subfilter_set", "1", false);'.
59						'create_var("zbx_filter", '.
60							json_encode($subfilterName.'['.$id.']').', '.
61							json_encode($id).', '.
62							'true'.
63						');'
64					));
65
66				$output[] = (new CSpan([
67					$link,
68					' ',
69					new CSup(($subfilter ? '+' : '').$element['count'])
70				]))
71					->addClass(ZBX_STYLE_NOWRAP)
72					->addClass(ZBX_STYLE_SUBFILTER);
73			}
74		}
75	}
76
77	return $output;
78}
79
80function getItemFilterForm(&$items) {
81	$filter_groupids			= $_REQUEST['filter_groupids'];
82	$filter_hostids				= $_REQUEST['filter_hostids'];
83	$filter_application			= $_REQUEST['filter_application'];
84	$filter_name				= $_REQUEST['filter_name'];
85	$filter_type				= $_REQUEST['filter_type'];
86	$filter_key					= $_REQUEST['filter_key'];
87	$filter_snmp_oid			= $_REQUEST['filter_snmp_oid'];
88	$filter_value_type			= $_REQUEST['filter_value_type'];
89	$filter_delay				= $_REQUEST['filter_delay'];
90	$filter_history				= $_REQUEST['filter_history'];
91	$filter_trends				= $_REQUEST['filter_trends'];
92	$filter_status				= $_REQUEST['filter_status'];
93	$filter_state				= $_REQUEST['filter_state'];
94	$filter_templated_items		= $_REQUEST['filter_templated_items'];
95	$filter_with_triggers		= $_REQUEST['filter_with_triggers'];
96	$filter_discovery           = $_REQUEST['filter_discovery'];
97	$subfilter_hosts			= $_REQUEST['subfilter_hosts'];
98	$subfilter_apps				= $_REQUEST['subfilter_apps'];
99	$subfilter_types			= $_REQUEST['subfilter_types'];
100	$subfilter_value_types		= $_REQUEST['subfilter_value_types'];
101	$subfilter_status			= $_REQUEST['subfilter_status'];
102	$subfilter_state			= $_REQUEST['subfilter_state'];
103	$subfilter_templated_items	= $_REQUEST['subfilter_templated_items'];
104	$subfilter_with_triggers	= $_REQUEST['subfilter_with_triggers'];
105	$subfilter_discovery        = $_REQUEST['subfilter_discovery'];
106	$subfilter_history			= $_REQUEST['subfilter_history'];
107	$subfilter_trends			= $_REQUEST['subfilter_trends'];
108	$subfilter_interval			= $_REQUEST['subfilter_interval'];
109
110	$filter = (new CFilter(new CUrl('items.php')))
111		->setProfile('web.items.filter')
112		->setActiveTab(CProfile::get('web.items.filter.active', 1))
113		->addVar('subfilter_hosts', $subfilter_hosts)
114		->addVar('subfilter_apps', $subfilter_apps)
115		->addVar('subfilter_types', $subfilter_types)
116		->addVar('subfilter_value_types', $subfilter_value_types)
117		->addVar('subfilter_status', $subfilter_status)
118		->addVar('subfilter_state', $subfilter_state)
119		->addVar('subfilter_templated_items', $subfilter_templated_items)
120		->addVar('subfilter_with_triggers', $subfilter_with_triggers)
121		->addVar('subfilter_discovery', $subfilter_discovery)
122		->addVar('subfilter_history', $subfilter_history)
123		->addVar('subfilter_trends', $subfilter_trends)
124		->addVar('subfilter_interval', $subfilter_interval);
125
126	$filterColumn1 = new CFormList();
127	$filterColumn2 = new CFormList();
128	$filterColumn3 = new CFormList();
129	$filterColumn4 = new CFormList();
130
131	// type select
132	$fTypeVisibility = [];
133	$type_select = (new CSelect('filter_type'))
134		->setId('filter_type')
135		->setValue($filter_type)
136		->setFocusableElementId('label-filter-type')
137		->addOption(new CSelectOption(-1, _('all')));
138
139	zbx_subarray_push($fTypeVisibility, -1, 'filter_delay_row');
140
141	$item_types = item_type2str();
142	unset($item_types[ITEM_TYPE_HTTPTEST]); // httptest items are only for internal zabbix logic
143
144	$type_select->addOptions(CSelect::createOptionsFromArray($item_types));
145
146	foreach ($item_types as $type => $name) {
147		if ($type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP) {
148			zbx_subarray_push($fTypeVisibility, $type, 'filter_delay_row');
149		}
150		if ($type == ITEM_TYPE_SNMP) {
151			zbx_subarray_push($fTypeVisibility, $type, 'filter_snmp_oid_row');
152		}
153	}
154
155	zbx_add_post_js("var filterTypeSwitcher = new CViewSwitcher('filter_type', 'change', ".zbx_jsvalue($fTypeVisibility, true).');');
156
157	// row 1
158	$group_filter = !empty($filter_groupids)
159		? CArrayHelper::renameObjectsKeys(API::HostGroup()->get([
160			'output' => ['groupid', 'name'],
161			'groupids' => $filter_groupids,
162			'editable' => true
163		]), ['groupid' => 'id'])
164		: [];
165
166	$filterColumn1->addRow((new CLabel(_('Host groups'), 'filter_groupid_ms')),
167		(new CMultiSelect([
168			'name' => 'filter_groupids[]',
169			'object_name' => 'hostGroup',
170			'data' => $group_filter,
171			'popup' => [
172				'parameters' => [
173					'srctbl' => 'host_groups',
174					'srcfld1' => 'groupid',
175					'dstfrm' => $filter->getName(),
176					'dstfld1' => 'filter_groupids_',
177					'editable' => true
178				]
179			]
180		]))->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
181	);
182
183	$filterColumn2->addRow(new CLabel(_('Type'), $type_select->getFocusableElementId()), $type_select);
184	$filterColumn3->addRow(new CLabel(_('Type of information'), 'label-filter-value-type'),
185		(new CSelect('filter_value_type'))
186			->setFocusableElementId('label-filter-value-type')
187			->setValue($filter_value_type)
188			->addOptions(CSelect::createOptionsFromArray([
189				-1 => _('all'),
190				ITEM_VALUE_TYPE_UINT64 => _('Numeric (unsigned)'),
191				ITEM_VALUE_TYPE_FLOAT => _('Numeric (float)'),
192				ITEM_VALUE_TYPE_STR => _('Character'),
193				ITEM_VALUE_TYPE_LOG => _('Log'),
194				ITEM_VALUE_TYPE_TEXT => _('Text')
195			]))
196	);
197	$filterColumn4->addRow(new CLabel(_('State'), 'label-filter-state'),
198		(new CSelect('filter_state'))
199			->setId('filter_state')
200			->setFocusableElementId('label-filter-state')
201			->setValue($filter_state)
202			->addOptions(CSelect::createOptionsFromArray([
203				-1 => _('all'),
204				ITEM_STATE_NORMAL => itemState(ITEM_STATE_NORMAL),
205				ITEM_STATE_NOTSUPPORTED => itemState(ITEM_STATE_NOTSUPPORTED)
206			]))
207	);
208
209	// row 2
210	$host_filter = !empty($filter_hostids)
211		? CArrayHelper::renameObjectsKeys(API::Host()->get([
212			'output' => ['hostid', 'name'],
213			'hostids' => $filter_hostids,
214			'templated_hosts' => true,
215			'editable' => true
216		]), ['hostid' => 'id'])
217		: [];
218
219	$filterColumn1->addRow((new CLabel(_('Hosts'), 'filter_hostid_ms')),
220		(new CMultiSelect([
221			'name' => 'filter_hostids[]',
222			'object_name' => 'host_templates',
223			'data' => $host_filter,
224			'popup' => [
225				'filter_preselect_fields' => [
226					'hostgroups' => 'filter_groupids_'
227				],
228				'parameters' => [
229					'srctbl' => 'host_templates',
230					'srcfld1' => 'hostid',
231					'dstfrm' => $filter->getName(),
232					'dstfld1' => 'filter_hostids_',
233					'editable' => true
234				]
235			]
236		]))->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
237	);
238
239	$filterColumn2->addRow(_('Update interval'),
240		(new CTextBox('filter_delay', $filter_delay))->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH),
241		'filter_delay_row'
242	);
243	$filterColumn4->addRow(new CLabel(_('Status'), 'label-filter-status'),
244		(new CSelect('filter_status'))
245			->setId('filter_status')
246			->setFocusableElementId('label-filter-status')
247			->setValue($filter_status)
248			->addOptions(CSelect::createOptionsFromArray([
249				-1 => _('all'),
250				ITEM_STATUS_ACTIVE => item_status2str(ITEM_STATUS_ACTIVE),
251				ITEM_STATUS_DISABLED => item_status2str(ITEM_STATUS_DISABLED)
252			]))
253	);
254
255	// row 3
256	$filterColumn1->addRow(_('Application'),
257		[
258			(new CTextBox('filter_application', $filter_application))->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH),
259			(new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN),
260			(new CButton(null, _('Select')))
261				->addClass(ZBX_STYLE_BTN_GREY)
262				->onClick('return PopUp("popup.generic",jQuery.extend('.
263					json_encode([
264						'srctbl' => 'applications',
265						'srcfld1' => 'name',
266						'dstfrm' => $filter->getName(),
267						'dstfld1' => 'filter_application',
268						'with_applications' => '1'
269					]).
270					', getFirstMultiselectValue("filter_hostids_")), null, this);'
271				)
272		]
273	);
274
275	$filterColumn3->addRow(_('History'),
276		(new CTextBox('filter_history', $filter_history))->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
277	);
278	$filterColumn4->addRow(new CLabel(_('Triggers'), 'label-filter-with-triggers'),
279		(new CSelect('filter_with_triggers'))
280			->setFocusableElementId('label-filter-with-triggers')
281			->setValue($filter_with_triggers)
282			->addOptions(CSelect::createOptionsFromArray([
283				-1 => _('all'),
284				1 => _('With triggers'),
285				0 => _('Without triggers')
286			]))
287	);
288
289	// row 4
290	$filterColumn1->addRow(_('Name'),
291		(new CTextBox('filter_name', $filter_name))->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
292	);
293	$filterColumn2->addRow(_('SNMP OID'),
294		(new CTextBox('filter_snmp_oid', $filter_snmp_oid, '', 255))->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH),
295		'filter_snmp_oid_row'
296	);
297	$filterColumn3->addRow(_('Trends'),
298		(new CTextBox('filter_trends', $filter_trends))->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
299	);
300	$filterColumn4->addRow(new CLabel(_('Template'), 'label-filter-templated-items'),
301		(new CSelect('filter_templated_items'))
302			->setFocusableElementId('label-filter-templated-items')
303			->setValue($filter_templated_items)
304			->addOptions(CSelect::createOptionsFromArray([
305				-1 => _('all'),
306				1 => _('Inherited items'),
307				0 => _('Not inherited items')
308			]))
309	);
310
311	// row 5
312	$filterColumn1->addRow(_('Key'),
313		(new CTextBox('filter_key', $filter_key))->setWidth(ZBX_TEXTAREA_FILTER_SMALL_WIDTH)
314	);
315	$filterColumn4->addRow(new CLabel(_('Discovery'), 'label-filter-discovery'),
316		(new CSelect('filter_discovery'))
317			->setFocusableElementId('label-filter-discovery')
318			->setValue($filter_discovery)
319			->addOptions(CSelect::createOptionsFromArray([
320				-1 => _('all'),
321				ZBX_FLAG_DISCOVERY_CREATED => _('Discovered items'),
322				ZBX_FLAG_DISCOVERY_NORMAL => _('Regular items')
323			]))
324	);
325
326	// subfilters
327	$table_subfilter = (new CTableInfo())
328		->addRow([
329			new CTag('h4', true, [
330				_('Subfilter'), SPACE, (new CSpan(_('affects only filtered data')))->addClass(ZBX_STYLE_GREY)
331			])
332		]);
333
334	// array contains subfilters and number of items in each
335	$item_params = [
336		'hosts' => [],
337		'applications' => [],
338		'types' => [],
339		'value_types' => [],
340		'status' => [],
341		'state' => [],
342		'templated_items' => [],
343		'with_triggers' => [],
344		'discovery' => [],
345		'history' => [],
346		'trends' => [],
347		'interval' => []
348	];
349
350	$update_interval_parser = new CUpdateIntervalParser(['usermacros' => true]);
351	$simple_interval_parser = new CSimpleIntervalParser();
352
353	// generate array with values for subfilters of selected items
354	foreach ($items as $item) {
355		// hosts
356		if ($filter_hostids) {
357			$host = reset($item['hosts']);
358
359			if (!isset($item_params['hosts'][$host['hostid']])) {
360				$item_params['hosts'][$host['hostid']] = ['name' => $host['name'], 'count' => 0];
361			}
362			$show_item = true;
363			foreach ($item['subfilters'] as $name => $value) {
364				if ($name == 'subfilter_hosts') {
365					continue;
366				}
367				$show_item &= $value;
368			}
369			if ($show_item) {
370				$host = reset($item['hosts']);
371				$item_params['hosts'][$host['hostid']]['count']++;
372			}
373		}
374
375		// applications
376		if (!empty($item['applications'])) {
377			foreach ($item['applications'] as $application) {
378				if (!isset($item_params['applications'][$application['name']])) {
379					$item_params['applications'][$application['name']] = ['name' => $application['name'], 'count' => 0];
380				}
381			}
382		}
383		$show_item = true;
384		foreach ($item['subfilters'] as $name => $value) {
385			if ($name == 'subfilter_apps') {
386				continue;
387			}
388			$show_item &= $value;
389		}
390		$sel_app = false;
391		if ($show_item) {
392			// if any of item applications are selected
393			foreach ($item['applications'] as $app) {
394				if (str_in_array($app['name'], $subfilter_apps)) {
395					$sel_app = true;
396					break;
397				}
398			}
399			foreach ($item['applications'] as $app) {
400				if (str_in_array($app['name'], $subfilter_apps) || !$sel_app) {
401					$item_params['applications'][$app['name']]['count']++;
402				}
403			}
404		}
405
406		// types
407		if ($filter_type == -1) {
408			if (!isset($item_params['types'][$item['type']])) {
409				$item_params['types'][$item['type']] = ['name' => item_type2str($item['type']), 'count' => 0];
410			}
411			$show_item = true;
412			foreach ($item['subfilters'] as $name => $value) {
413				if ($name == 'subfilter_types') {
414					continue;
415				}
416				$show_item &= $value;
417			}
418			if ($show_item) {
419				$item_params['types'][$item['type']]['count']++;
420			}
421		}
422
423		// value types
424		if ($filter_value_type == -1) {
425			if (!isset($item_params['value_types'][$item['value_type']])) {
426				$item_params['value_types'][$item['value_type']] = [
427					'name' => itemValueTypeString($item['value_type']),
428					'count' => 0
429				];
430			}
431
432			$show_item = true;
433			foreach ($item['subfilters'] as $name => $value) {
434				if ($name == 'subfilter_value_types') {
435					continue;
436				}
437				$show_item &= $value;
438			}
439			if ($show_item) {
440				$item_params['value_types'][$item['value_type']]['count']++;
441			}
442		}
443
444		// status
445		if ($filter_status == -1) {
446			if (!isset($item_params['status'][$item['status']])) {
447				$item_params['status'][$item['status']] = [
448					'name' => item_status2str($item['status']),
449					'count' => 0
450				];
451			}
452			$show_item = true;
453			foreach ($item['subfilters'] as $name => $value) {
454				if ($name == 'subfilter_status') {
455					continue;
456				}
457				$show_item &= $value;
458			}
459			if ($show_item) {
460				$item_params['status'][$item['status']]['count']++;
461			}
462		}
463
464		// state
465		if ($filter_state == -1) {
466			if (!isset($item_params['state'][$item['state']])) {
467				$item_params['state'][$item['state']] = [
468					'name' => itemState($item['state']),
469					'count' => 0
470				];
471			}
472			$show_item = true;
473			foreach ($item['subfilters'] as $name => $value) {
474				if ($name == 'subfilter_state') {
475					continue;
476				}
477				$show_item &= $value;
478			}
479			if ($show_item) {
480				$item_params['state'][$item['state']]['count']++;
481			}
482		}
483
484		// template
485		if ($filter_templated_items == -1) {
486			if ($item['templateid'] == 0 && !isset($item_params['templated_items'][0])) {
487				$item_params['templated_items'][0] = ['name' => _('Not inherited items'), 'count' => 0];
488			}
489			elseif ($item['templateid'] > 0 && !isset($item_params['templated_items'][1])) {
490				$item_params['templated_items'][1] = ['name' => _('Inherited items'), 'count' => 0];
491			}
492			$show_item = true;
493			foreach ($item['subfilters'] as $name => $value) {
494				if ($name == 'subfilter_templated_items') {
495					continue;
496				}
497				$show_item &= $value;
498			}
499			if ($show_item) {
500				if ($item['templateid'] == 0) {
501					$item_params['templated_items'][0]['count']++;
502				}
503				else {
504					$item_params['templated_items'][1]['count']++;
505				}
506			}
507		}
508
509		// with triggers
510		if ($filter_with_triggers == -1) {
511			if (count($item['triggers']) == 0 && !isset($item_params['with_triggers'][0])) {
512				$item_params['with_triggers'][0] = ['name' => _('Without triggers'), 'count' => 0];
513			}
514			elseif (count($item['triggers']) > 0 && !isset($item_params['with_triggers'][1])) {
515				$item_params['with_triggers'][1] = ['name' => _('With triggers'), 'count' => 0];
516			}
517			$show_item = true;
518			foreach ($item['subfilters'] as $name => $value) {
519				if ($name == 'subfilter_with_triggers') {
520					continue;
521				}
522				$show_item &= $value;
523			}
524			if ($show_item) {
525				if (count($item['triggers']) == 0) {
526					$item_params['with_triggers'][0]['count']++;
527				}
528				else {
529					$item_params['with_triggers'][1]['count']++;
530				}
531			}
532		}
533
534		// discovery
535		if ($filter_discovery == -1) {
536			if ($item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL && !isset($item_params['discovery'][0])) {
537				$item_params['discovery'][0] = ['name' => _('Regular'), 'count' => 0];
538			}
539			elseif ($item['flags'] == ZBX_FLAG_DISCOVERY_CREATED && !isset($item_params['discovery'][1])) {
540				$item_params['discovery'][1] = ['name' => _('Discovered'), 'count' => 0];
541			}
542			$show_item = true;
543			foreach ($item['subfilters'] as $name => $value) {
544				if ($name == 'subfilter_discovery') {
545					continue;
546				}
547				$show_item &= $value;
548			}
549			if ($show_item) {
550				if ($item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
551					$item_params['discovery'][0]['count']++;
552				}
553				else {
554					$item_params['discovery'][1]['count']++;
555				}
556			}
557		}
558
559		// trends
560		if ($filter_trends === ''
561				&& !in_array($item['value_type'], [ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT])) {
562			$trends = $item['trends'];
563			$value = $trends;
564
565			if ($simple_interval_parser->parse($trends) == CParser::PARSE_SUCCESS) {
566				$value = timeUnitToSeconds($trends);
567				$trends = convertUnitsS($value);
568			}
569
570			if (!array_key_exists($trends, $item_params['trends'])) {
571				$item_params['trends'][$trends] = [
572					'name' => $trends,
573					'count' => 0,
574					'value' => $value
575				];
576			}
577
578			$show_item = true;
579
580			foreach ($item['subfilters'] as $name => $value) {
581				if ($name === 'subfilter_trends') {
582					continue;
583				}
584				$show_item &= $value;
585			}
586
587			if ($show_item) {
588				$item_params['trends'][$trends]['count']++;
589			}
590		}
591
592		// history
593		if ($filter_history === '') {
594			$history = $item['history'];
595			$value = $history;
596
597			if ($simple_interval_parser->parse($history) == CParser::PARSE_SUCCESS) {
598				$value = timeUnitToSeconds($history);
599				$history = convertUnitsS($value);
600			}
601
602			if (!array_key_exists($history, $item_params['history'])) {
603				$item_params['history'][$history] = [
604					'name' => $history,
605					'count' => 0,
606					'value' => $value
607				];
608			}
609
610			$show_item = true;
611
612			foreach ($item['subfilters'] as $name => $value) {
613				if ($name === 'subfilter_history') {
614					continue;
615				}
616				$show_item &= $value;
617			}
618
619			if ($show_item) {
620				$item_params['history'][$history]['count']++;
621			}
622		}
623
624		// interval
625		if ($filter_delay === '' && $filter_type != ITEM_TYPE_TRAPPER && $item['type'] != ITEM_TYPE_TRAPPER
626				&& $item['type'] != ITEM_TYPE_SNMPTRAP && $item['type'] != ITEM_TYPE_DEPENDENT) {
627			// Use temporary variable for delay, because the original will be used for sorting later.
628			$delay = $item['delay'];
629			$value = $delay;
630
631			if ($update_interval_parser->parse($delay) == CParser::PARSE_SUCCESS) {
632				$delay = $update_interval_parser->getDelay();
633
634				// "value" is delay represented in seconds and it is used for sorting the subfilter.
635				if ($delay[0] !== '{') {
636					$value = timeUnitToSeconds($delay);
637					$delay = convertUnitsS($value);
638				}
639				else {
640					$value = $delay;
641				}
642			}
643
644			if (!array_key_exists($delay, $item_params['interval'])) {
645				$item_params['interval'][$delay] = [
646					'name' => $delay,
647					'count' => 0,
648					'value' => $value
649				];
650			}
651
652			$show_item = true;
653
654			foreach ($item['subfilters'] as $name => $value) {
655				if ($name === 'subfilter_interval') {
656					continue;
657				}
658				$show_item &= $value;
659			}
660
661			if ($show_item) {
662				$item_params['interval'][$delay]['count']++;
663			}
664		}
665	}
666
667	// output
668	if ($filter_hostids && count($item_params['hosts']) > 1) {
669		$hosts_output = prepareSubfilterOutput(_('Hosts'), $item_params['hosts'], $subfilter_hosts, 'subfilter_hosts');
670		$table_subfilter->addRow([$hosts_output]);
671	}
672
673	if (!empty($item_params['applications']) && count($item_params['applications']) > 1) {
674		$application_output = prepareSubfilterOutput(_('Applications'), $item_params['applications'], $subfilter_apps, 'subfilter_apps');
675		$table_subfilter->addRow([$application_output]);
676	}
677
678	if ($filter_type == -1 && count($item_params['types']) > 1) {
679		$type_output = prepareSubfilterOutput(_('Types'), $item_params['types'], $subfilter_types, 'subfilter_types');
680		$table_subfilter->addRow([$type_output]);
681	}
682
683	if ($filter_value_type == -1 && count($item_params['value_types']) > 1) {
684		$value_types_output = prepareSubfilterOutput(_('Type of information'), $item_params['value_types'], $subfilter_value_types, 'subfilter_value_types');
685		$table_subfilter->addRow([$value_types_output]);
686	}
687
688	if ($filter_status == -1 && count($item_params['status']) > 1) {
689		$status_output = prepareSubfilterOutput(_('Status'), $item_params['status'], $subfilter_status, 'subfilter_status');
690		$table_subfilter->addRow([$status_output]);
691	}
692
693	if ($filter_state == -1 && count($item_params['state']) > 1) {
694		$state_output = prepareSubfilterOutput(_('State'), $item_params['state'], $subfilter_state, 'subfilter_state');
695		$table_subfilter->addRow([$state_output]);
696	}
697
698	if ($filter_templated_items == -1 && count($item_params['templated_items']) > 1) {
699		$templated_items_output = prepareSubfilterOutput(_('Template'), $item_params['templated_items'], $subfilter_templated_items, 'subfilter_templated_items');
700		$table_subfilter->addRow([$templated_items_output]);
701	}
702
703	if ($filter_with_triggers == -1 && count($item_params['with_triggers']) > 1) {
704		$with_triggers_output = prepareSubfilterOutput(_('With triggers'), $item_params['with_triggers'], $subfilter_with_triggers, 'subfilter_with_triggers');
705		$table_subfilter->addRow([$with_triggers_output]);
706	}
707
708	if ($filter_discovery == -1 && count($item_params['discovery']) > 1) {
709		$discovery_output = prepareSubfilterOutput(_('Discovery'), $item_params['discovery'], $subfilter_discovery, 'subfilter_discovery');
710		$table_subfilter->addRow([$discovery_output]);
711	}
712
713	if (zbx_empty($filter_history) && count($item_params['history']) > 1) {
714		$history_output = prepareSubfilterOutput(_('History'), $item_params['history'], $subfilter_history, 'subfilter_history');
715		$table_subfilter->addRow([$history_output]);
716	}
717
718	if (zbx_empty($filter_trends) && (count($item_params['trends']) > 1)) {
719		$trends_output = prepareSubfilterOutput(_('Trends'), $item_params['trends'], $subfilter_trends, 'subfilter_trends');
720		$table_subfilter->addRow([$trends_output]);
721	}
722
723	if (zbx_empty($filter_delay) && $filter_type != ITEM_TYPE_TRAPPER && count($item_params['interval']) > 1) {
724		$interval_output = prepareSubfilterOutput(_('Interval'), $item_params['interval'], $subfilter_interval, 'subfilter_interval');
725		$table_subfilter->addRow([$interval_output]);
726	}
727
728	$filter->addFilterTab(_('Filter'), [$filterColumn1, $filterColumn2, $filterColumn3, $filterColumn4],
729		$table_subfilter
730	);
731
732	return $filter;
733}
734
735/**
736 * Prepare ITEM_TYPE_HTTPAGENT type item data for create or update API calls.
737 * - Converts 'query_fields' from array of keys and array of values to array of hash maps for every field.
738 * - Converts 'headers' from array of keys and array of values to hash map.
739 * - For request method HEAD set retrieve mode to retrieve only headers.
740 *
741 * @param array $item                       Array of form fields data for ITEM_TYPE_HTTPAGENT item.
742 * @param int   $item['request_method']     Request method type.
743 * @param array $item['query_fields']       Array of 'name' and 'value' arrays for URL query fields.
744 * @param array $item['headers']            Array of 'name' and 'value' arrays for headers.
745 *
746 * @return array
747 */
748function prepareItemHttpAgentFormData(array $item) {
749	if ($item['request_method'] == HTTPCHECK_REQUEST_HEAD) {
750		$item['retrieve_mode'] = HTTPTEST_STEP_RETRIEVE_MODE_HEADERS;
751	}
752
753	if ($item['query_fields']) {
754		$query_fields = [];
755
756		foreach ($item['query_fields']['name'] as $index => $key) {
757			$value = $item['query_fields']['value'][$index];
758
759			if ($key !== '' || $value !== '') {
760				$query_fields[] = [$key => $value];
761			}
762		}
763		$item['query_fields'] = $query_fields;
764	}
765
766	if ($item['headers']) {
767		$headers = [];
768
769		foreach ($item['headers']['name'] as $index => $key) {
770			$value = $item['headers']['value'][$index];
771
772			if ($key !== '' || $value !== '') {
773				$headers[$key] = $value;
774			}
775		}
776
777		$item['headers'] = $headers;
778	}
779
780	return $item;
781}
782
783/**
784 * Get data for item edit page.
785 *
786 * @param array $item                          Item, item prototype, LLD rule or LLD item to take the data from.
787 * @param array $options
788 * @param bool  $options['is_discovery_rule']
789 *
790 * @return array
791 */
792function getItemFormData(array $item = [], array $options = []) {
793	$data = [
794		'form' => getRequest('form'),
795		'form_refresh' => getRequest('form_refresh'),
796		'is_discovery_rule' => !empty($options['is_discovery_rule']),
797		'parent_discoveryid' => getRequest('parent_discoveryid', 0),
798		'itemid' => getRequest('itemid'),
799		'limited' => false,
800		'interfaceid' => getRequest('interfaceid', 0),
801		'name' => getRequest('name', ''),
802		'description' => getRequest('description', ''),
803		'key' => getRequest('key', ''),
804		'master_itemid' => getRequest('master_itemid', 0),
805		'hostname' => getRequest('hostname'),
806		'delay' => getRequest('delay', ZBX_ITEM_DELAY_DEFAULT),
807		'history' => getRequest('history', DB::getDefault('items', 'history')),
808		'status' => getRequest('status', isset($_REQUEST['form_refresh']) ? 1 : 0),
809		'type' => getRequest('type', 0),
810		'snmp_oid' => getRequest('snmp_oid', ''),
811		'value_type' => getRequest('value_type', ITEM_VALUE_TYPE_UINT64),
812		'trapper_hosts' => getRequest('trapper_hosts', ''),
813		'units' => getRequest('units', ''),
814		'valuemapid' => getRequest('valuemapid', 0),
815		'params' => getRequest('params', ''),
816		'trends' => getRequest('trends', DB::getDefault('items', 'trends')),
817		'new_application' => getRequest('new_application', ''),
818		'applications' => getRequest('applications', []),
819		'delay_flex' => array_values(getRequest('delay_flex', [])),
820		'ipmi_sensor' => getRequest('ipmi_sensor', ''),
821		'authtype' => getRequest('authtype', 0),
822		'username' => getRequest('username', ''),
823		'password' => getRequest('password', ''),
824		'publickey' => getRequest('publickey', ''),
825		'privatekey' => getRequest('privatekey', ''),
826		'logtimefmt' => getRequest('logtimefmt', ''),
827		'valuemaps' => null,
828		'possibleHostInventories' => null,
829		'alreadyPopulated' => null,
830		'initial_item_type' => null,
831		'templates' => [],
832		'jmx_endpoint' => getRequest('jmx_endpoint', ZBX_DEFAULT_JMX_ENDPOINT),
833		'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
834		'url' => getRequest('url'),
835		'query_fields' => getRequest('query_fields', []),
836		'posts' => getRequest('posts'),
837		'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
838		'follow_redirects' => hasRequest('form_refresh')
839			? (int) getRequest('follow_redirects')
840			: getRequest('follow_redirects', DB::getDefault('items', 'follow_redirects')),
841		'post_type' => getRequest('post_type', DB::getDefault('items', 'post_type')),
842		'http_proxy' => getRequest('http_proxy'),
843		'headers' => getRequest('headers', []),
844		'retrieve_mode' => getRequest('retrieve_mode', DB::getDefault('items', 'retrieve_mode')),
845		'request_method' => getRequest('request_method', DB::getDefault('items', 'request_method')),
846		'output_format' => getRequest('output_format', DB::getDefault('items', 'output_format')),
847		'allow_traps' => getRequest('allow_traps', DB::getDefault('items', 'allow_traps')),
848		'ssl_cert_file' => getRequest('ssl_cert_file'),
849		'ssl_key_file' => getRequest('ssl_key_file'),
850		'ssl_key_password' => getRequest('ssl_key_password'),
851		'verify_peer' => getRequest('verify_peer', DB::getDefault('items', 'verify_peer')),
852		'verify_host' => getRequest('verify_host', DB::getDefault('items', 'verify_host')),
853		'http_authtype' => getRequest('http_authtype', HTTPTEST_AUTH_NONE),
854		'http_username' => getRequest('http_username', ''),
855		'http_password' => getRequest('http_password', ''),
856		'preprocessing' => getRequest('preprocessing', []),
857		'preprocessing_script_maxlength' => DB::getFieldLength('item_preproc', 'params')
858	];
859
860	if ($data['parent_discoveryid'] != 0) {
861		$data['discover'] = hasRequest('form_refresh')
862			? getRequest('discover', DB::getDefault('items', 'discover'))
863			: (($item && array_key_exists('discover', $item))
864				? $item['discover']
865				: DB::getDefault('items', 'discover')
866			);
867	}
868
869	if ($data['type'] == ITEM_TYPE_HTTPAGENT) {
870		foreach (['query_fields', 'headers'] as $property) {
871			$values = [];
872
873			if (is_array($data[$property]) && array_key_exists('name', $data[$property])
874					&& array_key_exists('value', $data[$property])) {
875				foreach ($data[$property]['name'] as $index => $key) {
876					if (array_key_exists($index, $data[$property]['value'])) {
877						$values[] = [$key => $data[$property]['value'][$index]];
878					}
879				}
880			}
881			$data[$property] = $values;
882		}
883	}
884	else {
885		$data['headers'] = [];
886		$data['query_fields'] = [];
887	}
888
889	// Dependent item initialization by master_itemid.
890	if (array_key_exists('master_item', $item)) {
891		$expanded = CMacrosResolverHelper::resolveItemNames([$item['master_item']]);
892		$master_item = reset($expanded);
893		$data['master_itemid'] = $master_item['itemid'];
894		$data['master_itemname'] = $master_item['name_expanded'];
895		// Do not initialize item data if only master_item array was passed.
896		unset($item['master_item']);
897	}
898
899	// hostid
900	if ($data['parent_discoveryid'] != 0) {
901		$discoveryRule = API::DiscoveryRule()->get([
902			'output' => ['hostid'],
903			'itemids' => $data['parent_discoveryid'],
904			'editable' => true
905		]);
906		$discoveryRule = reset($discoveryRule);
907		$data['hostid'] = $discoveryRule['hostid'];
908
909		$data['new_application_prototype'] = getRequest('new_application_prototype', '');
910		$data['application_prototypes'] = getRequest('application_prototypes', []);
911	}
912	else {
913		$data['hostid'] = getRequest('hostid', 0);
914	}
915
916	foreach ($data['preprocessing'] as &$step) {
917		$step += [
918			'error_handler' => ZBX_PREPROC_FAIL_DEFAULT,
919			'error_handler_params' => ''
920		];
921	}
922	unset($step);
923
924	// types, http items only for internal processes
925	$data['types'] = item_type2str();
926	unset($data['types'][ITEM_TYPE_HTTPTEST]);
927	if ($data['is_discovery_rule']) {
928		unset($data['types'][ITEM_TYPE_AGGREGATE],
929			$data['types'][ITEM_TYPE_CALCULATED],
930			$data['types'][ITEM_TYPE_SNMPTRAP]
931		);
932	}
933
934	// item
935	if (array_key_exists('itemid', $item)) {
936		$data['item'] = $item;
937		$data['hostid'] = !empty($data['hostid']) ? $data['hostid'] : $data['item']['hostid'];
938		$data['limited'] = ($data['item']['templateid'] != 0);
939		$data['interfaceid'] = $item['interfaceid'];
940
941		// discovery rule
942		if ($data['is_discovery_rule']) {
943			$flag = ZBX_FLAG_DISCOVERY_RULE;
944		}
945		// item prototype
946		elseif ($data['parent_discoveryid'] != 0) {
947			$flag = ZBX_FLAG_DISCOVERY_PROTOTYPE;
948		}
949		// plain item
950		else {
951			$flag = ZBX_FLAG_DISCOVERY_NORMAL;
952		}
953
954		$data['templates'] = makeItemTemplatesHtml($item['itemid'], getItemParentTemplates([$item], $flag), $flag);
955	}
956
957	// caption
958	if ($data['is_discovery_rule']) {
959		$data['caption'] = _('Discovery rule');
960	}
961	else {
962		$data['caption'] = ($data['parent_discoveryid'] != 0) ? _('Item prototype') : _('Item');
963	}
964
965	// hostname
966	if (empty($data['is_discovery_rule']) && empty($data['hostname'])) {
967		if (!empty($data['hostid'])) {
968			$hostInfo = API::Host()->get([
969				'hostids' => $data['hostid'],
970				'output' => ['name'],
971				'templated_hosts' => true
972			]);
973			$hostInfo = reset($hostInfo);
974			$data['hostname'] = $hostInfo['name'];
975		}
976		else {
977			$data['hostname'] = _('not selected');
978		}
979	}
980
981	// fill data from item
982	if (!hasRequest('form_refresh') && ($item || $data['limited'])) {
983		$data['name'] = $data['item']['name'];
984		$data['description'] = $data['item']['description'];
985		$data['key'] = $data['item']['key_'];
986		$data['interfaceid'] = $data['item']['interfaceid'];
987		$data['type'] = $data['item']['type'];
988		$data['snmp_oid'] = $data['item']['snmp_oid'];
989		$data['value_type'] = $data['item']['value_type'];
990		$data['trapper_hosts'] = $data['item']['trapper_hosts'];
991		$data['units'] = $data['item']['units'];
992		$data['valuemapid'] = $data['item']['valuemapid'];
993		$data['hostid'] = $data['item']['hostid'];
994		$data['params'] = $data['item']['params'];
995		$data['ipmi_sensor'] = $data['item']['ipmi_sensor'];
996		$data['authtype'] = $data['item']['authtype'];
997		$data['username'] = $data['item']['username'];
998		$data['password'] = $data['item']['password'];
999		$data['publickey'] = $data['item']['publickey'];
1000		$data['privatekey'] = $data['item']['privatekey'];
1001		$data['logtimefmt'] = $data['item']['logtimefmt'];
1002		$data['jmx_endpoint'] = $data['item']['jmx_endpoint'];
1003		$data['new_application'] = getRequest('new_application', '');
1004		// ITEM_TYPE_HTTPAGENT
1005		$data['timeout'] = $data['item']['timeout'];
1006		$data['url'] = $data['item']['url'];
1007		$data['query_fields'] = $data['item']['query_fields'];
1008		$data['posts'] = $data['item']['posts'];
1009		$data['status_codes'] = $data['item']['status_codes'];
1010		$data['follow_redirects'] = $data['item']['follow_redirects'];
1011		$data['post_type'] = $data['item']['post_type'];
1012		$data['http_proxy'] = $data['item']['http_proxy'];
1013		$data['headers'] = $data['item']['headers'];
1014		$data['retrieve_mode'] = $data['item']['retrieve_mode'];
1015		$data['request_method'] = $data['item']['request_method'];
1016		$data['allow_traps'] = $data['item']['allow_traps'];
1017		$data['ssl_cert_file'] = $data['item']['ssl_cert_file'];
1018		$data['ssl_key_file'] = $data['item']['ssl_key_file'];
1019		$data['ssl_key_password'] = $data['item']['ssl_key_password'];
1020		$data['verify_peer'] = $data['item']['verify_peer'];
1021		$data['verify_host'] = $data['item']['verify_host'];
1022		$data['http_authtype'] = $data['item']['authtype'];
1023		$data['http_username'] = $data['item']['username'];
1024		$data['http_password'] = $data['item']['password'];
1025
1026		if ($data['type'] == ITEM_TYPE_HTTPAGENT) {
1027			// Convert hash to array where every item is hash for single key value pair as it is used by view.
1028			$headers = [];
1029
1030			foreach ($data['headers'] as $key => $value) {
1031				$headers[] = [$key => $value];
1032			}
1033
1034			$data['headers'] = $headers;
1035		}
1036
1037		$data['preprocessing'] = $data['item']['preprocessing'];
1038
1039		if (!$data['is_discovery_rule']) {
1040			$data['output_format'] = $data['item']['output_format'];
1041		}
1042
1043		if ($data['parent_discoveryid'] != 0) {
1044			$data['new_application_prototype'] = getRequest('new_application_prototype', '');
1045		}
1046
1047		if (!$data['limited'] || !isset($_REQUEST['form_refresh'])) {
1048			$data['delay'] = $data['item']['delay'];
1049
1050			$update_interval_parser = new CUpdateIntervalParser([
1051				'usermacros' => true,
1052				'lldmacros' => ($data['parent_discoveryid'] != 0)
1053			]);
1054
1055			if ($update_interval_parser->parse($data['delay']) == CParser::PARSE_SUCCESS) {
1056				$data['delay'] = $update_interval_parser->getDelay();
1057
1058				if ($data['delay'][0] !== '{') {
1059					$delay = timeUnitToSeconds($data['delay']);
1060
1061					if ($delay == 0 && ($data['type'] == ITEM_TYPE_TRAPPER || $data['type'] == ITEM_TYPE_SNMPTRAP
1062							|| $data['type'] == ITEM_TYPE_DEPENDENT)) {
1063						$data['delay'] = ZBX_ITEM_DELAY_DEFAULT;
1064					}
1065				}
1066
1067				foreach ($update_interval_parser->getIntervals() as $interval) {
1068					if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
1069						$data['delay_flex'][] = [
1070							'delay' => $interval['update_interval'],
1071							'period' => $interval['time_period'],
1072							'type' => ITEM_DELAY_FLEXIBLE
1073						];
1074					}
1075					else {
1076						$data['delay_flex'][] = [
1077							'schedule' => $interval['interval'],
1078							'type' => ITEM_DELAY_SCHEDULING
1079						];
1080					}
1081				}
1082			}
1083			else {
1084				$data['delay'] = ZBX_ITEM_DELAY_DEFAULT;
1085			}
1086
1087			$data['history'] = $data['item']['history'];
1088			$data['status'] = $data['item']['status'];
1089			$data['trends'] = $data['item']['trends'];
1090
1091			$data['applications'] = array_unique(zbx_array_merge($data['applications'], get_applications_by_itemid($data['itemid'])));
1092
1093			if ($data['parent_discoveryid'] != 0) {
1094				/*
1095				 * Get a list of application prototypes assigned to item prototype. Don't select distinct names,
1096				 * since database can be accidentally created case insensitive.
1097				 */
1098				$application_prototypes = DBfetchArray(DBselect(
1099					'SELECT ap.name'.
1100					' FROM application_prototype ap,item_application_prototype iap'.
1101					' WHERE ap.application_prototypeid=iap.application_prototypeid'.
1102						' AND ap.itemid='.zbx_dbstr($data['parent_discoveryid']).
1103						' AND iap.itemid='.zbx_dbstr($data['itemid'])
1104				));
1105
1106				// Merge form submitted data with data existing in DB to find diff and correctly display ListBox.
1107				$data['application_prototypes'] = array_unique(
1108					zbx_array_merge($data['application_prototypes'], zbx_objectValues($application_prototypes, 'name'))
1109				);
1110			}
1111		}
1112	}
1113
1114	if (!$data['delay_flex']) {
1115		$data['delay_flex'][] = ['delay' => '', 'period' => '', 'type' => ITEM_DELAY_FLEXIBLE];
1116	}
1117
1118	// applications
1119	if (count($data['applications']) == 0) {
1120		array_push($data['applications'], 0);
1121	}
1122	$data['db_applications'] = DBfetchArray(DBselect(
1123		'SELECT DISTINCT a.applicationid,a.name'.
1124		' FROM applications a'.
1125		' WHERE a.hostid='.zbx_dbstr($data['hostid']).
1126			(($data['parent_discoveryid'] != 0) ? ' AND a.flags='.ZBX_FLAG_DISCOVERY_NORMAL : '')
1127	));
1128	order_result($data['db_applications'], 'name');
1129
1130	if ($data['parent_discoveryid'] != 0) {
1131		// Make the application prototype list no appearing empty, but filling it with "-None-" as first element.
1132		if (count($data['application_prototypes']) == 0) {
1133			$data['application_prototypes'][] = 0;
1134		}
1135
1136		// Get a list of application prototypes by discovery rule.
1137		$data['db_application_prototypes'] = DBfetchArray(DBselect(
1138			'SELECT ap.application_prototypeid,ap.name'.
1139			' FROM application_prototype ap'.
1140			' WHERE ap.itemid='.zbx_dbstr($data['parent_discoveryid'])
1141		));
1142		order_result($data['db_application_prototypes'], 'name');
1143	}
1144
1145	// interfaces
1146	$data['interfaces'] = API::HostInterface()->get([
1147		'hostids' => $data['hostid'],
1148		'output' => API_OUTPUT_EXTEND
1149	]);
1150	// Sort interfaces to be listed starting with one selected as 'main'.
1151	CArrayHelper::sort($data['interfaces'], [
1152		['field' => 'main', 'order' => ZBX_SORT_DOWN],
1153		['field' => 'interfaceid','order' => ZBX_SORT_UP]
1154	]);
1155
1156	if ($data['limited'] || (array_key_exists('item', $data) && $data['parent_discoveryid'] == 0
1157			&& $data['item']['flags'] == ZBX_FLAG_DISCOVERY_CREATED)) {
1158		if ($data['valuemapid'] != 0) {
1159			$valuemaps = API::ValueMap()->get([
1160				'output' => ['name'],
1161				'valuemapids' => [$data['valuemapid']]
1162			]);
1163
1164			if ($valuemaps) {
1165				$data['valuemaps'] = $valuemaps[0]['name'];
1166			}
1167		}
1168	}
1169	else {
1170		$data['valuemaps'] = API::ValueMap()->get([
1171			'output' => ['valuemapid', 'name']
1172		]);
1173
1174		CArrayHelper::sort($data['valuemaps'], ['name']);
1175	}
1176
1177	// possible host inventories
1178	if ($data['parent_discoveryid'] == 0) {
1179		$data['possibleHostInventories'] = getHostInventories();
1180
1181		// get already populated fields by other items
1182		$data['alreadyPopulated'] = API::item()->get([
1183			'output' => ['inventory_link'],
1184			'filter' => ['hostid' => $data['hostid']],
1185			'nopermissions' => true
1186		]);
1187		$data['alreadyPopulated'] = zbx_toHash($data['alreadyPopulated'], 'inventory_link');
1188	}
1189
1190	// unset ssh auth fields
1191	if ($data['type'] != ITEM_TYPE_SSH) {
1192		$data['authtype'] = ITEM_AUTHTYPE_PASSWORD;
1193		$data['publickey'] = '';
1194		$data['privatekey'] = '';
1195	}
1196
1197	if ($data['type'] != ITEM_TYPE_DEPENDENT) {
1198		$data['master_itemid'] = 0;
1199	}
1200
1201	return $data;
1202}
1203
1204/**
1205 * Get list of item pre-processing data and return a prepared HTML object.
1206 *
1207 * @param CForm  $form                                     Form object to where add pre-processing list.
1208 * @param array  $preprocessing                            Array of item pre-processing steps.
1209 * @param string $preprocessing[]['type']                  Pre-processing step type.
1210 * @param array  $preprocessing[]['params']                Additional parameters used by pre-processing.
1211 * @param string $preprocessing[]['error_handler']         Action type used in case of pre-processing step failure.
1212 * @param string $preprocessing[]['error_handler_params']  Error handler parameters.
1213 * @param bool   $readonly                                 True if fields should be read only.
1214 * @param array  $types                                    Supported pre-processing types.
1215 *
1216 * @return CList
1217 */
1218function getItemPreprocessing(CForm $form, array $preprocessing, $readonly, array $types) {
1219	$script_maxlength = DB::getFieldLength('item_preproc', 'params');
1220	$preprocessing_list = (new CList())
1221		->setId('preprocessing')
1222		->addClass('preprocessing-list')
1223		->addClass('list-numbered')
1224		->addItem(
1225			(new CListItem([
1226				(new CDiv(_('Name')))->addClass('step-name'),
1227				(new CDiv(_('Parameters')))->addClass('step-parameters'),
1228				(new CDiv(_('Custom on fail')))->addClass('step-on-fail'),
1229				(new CDiv(_('Actions')))->addClass('step-action')
1230			]))
1231				->addClass('preprocessing-list-head')
1232				->addStyle(!$preprocessing ? 'display: none;' : null)
1233		);
1234
1235	$sortable = (count($preprocessing) > 1 && !$readonly);
1236
1237	$i = 0;
1238
1239	foreach ($preprocessing as $step) {
1240		// Create a select with preprocessing types.
1241		$preproc_types_select = (new CSelect('preprocessing['.$i.'][type]'))
1242			->setId('preprocessing_'.$i.'_type')
1243			->setValue($step['type'])
1244			->setReadonly($readonly)
1245			->setWidthAuto();
1246
1247		foreach (get_preprocessing_types(null, true, $types) as $group) {
1248			$opt_group = new CSelectOptionGroup($group['label']);
1249
1250			foreach ($group['types'] as $type => $label) {
1251				$opt_group->addOption(new CSelectOption($type, $label));
1252			}
1253
1254			$preproc_types_select->addOptionGroup($opt_group);
1255		}
1256
1257		// Depending on preprocessing type, display corresponding params field and placeholders.
1258		$params = '';
1259
1260		// Create a primary param text box, so it can be hidden if necessary.
1261		$step_param_0_value = array_key_exists('params', $step) ? $step['params'][0] : '';
1262		$step_param_0 = (new CTextBox('preprocessing['.$i.'][params][0]', $step_param_0_value))
1263			->setTitle($step_param_0_value)
1264			->setReadonly($readonly);
1265
1266		// Create a secondary param text box, so it can be hidden if necessary.
1267		$step_param_1_value = (array_key_exists('params', $step) && array_key_exists(1, $step['params']))
1268			? $step['params'][1]
1269			: '';
1270		$step_param_1 = (new CTextBox('preprocessing['.$i.'][params][1]', $step_param_1_value))
1271			->setTitle($step_param_1_value)
1272			->setReadonly($readonly);
1273
1274		// Add corresponding placeholders and show or hide text boxes.
1275		switch ($step['type']) {
1276			case ZBX_PREPROC_MULTIPLIER:
1277				$params = $step_param_0
1278					->setAttribute('placeholder', _('number'))
1279					->setWidth(ZBX_TEXTAREA_NUMERIC_BIG_WIDTH);
1280				break;
1281
1282			case ZBX_PREPROC_RTRIM:
1283			case ZBX_PREPROC_LTRIM:
1284			case ZBX_PREPROC_TRIM:
1285				$params = $step_param_0
1286					->setAttribute('placeholder', _('list of characters'))
1287					->setWidth(ZBX_TEXTAREA_SMALL_WIDTH);
1288				break;
1289
1290			case ZBX_PREPROC_XPATH:
1291			case ZBX_PREPROC_ERROR_FIELD_XML:
1292				$params = $step_param_0->setAttribute('placeholder', _('XPath'));
1293				break;
1294
1295			case ZBX_PREPROC_JSONPATH:
1296			case ZBX_PREPROC_ERROR_FIELD_JSON:
1297				$params = $step_param_0->setAttribute('placeholder', _('$.path.to.node'));
1298				break;
1299
1300			case ZBX_PREPROC_REGSUB:
1301			case ZBX_PREPROC_ERROR_FIELD_REGEX:
1302				$params = [
1303					$step_param_0->setAttribute('placeholder', _('pattern')),
1304					$step_param_1->setAttribute('placeholder', _('output'))
1305				];
1306				break;
1307
1308			case ZBX_PREPROC_VALIDATE_RANGE:
1309				$params = [
1310					$step_param_0->setAttribute('placeholder', _('min')),
1311					$step_param_1->setAttribute('placeholder', _('max'))
1312				];
1313				break;
1314
1315			case ZBX_PREPROC_VALIDATE_REGEX:
1316			case ZBX_PREPROC_VALIDATE_NOT_REGEX:
1317				$params = $step_param_0->setAttribute('placeholder', _('pattern'));
1318				break;
1319
1320			case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
1321				$params = $step_param_0
1322					->setAttribute('placeholder', _('seconds'))
1323					->setWidth(ZBX_TEXTAREA_NUMERIC_BIG_WIDTH);
1324				break;
1325
1326			case ZBX_PREPROC_SCRIPT:
1327				$params = new CMultilineInput($step_param_0->getName(), $step_param_0_value, [
1328					'title' => _('JavaScript'),
1329					'placeholder' => _('script'),
1330					'placeholder_textarea' => 'return value',
1331					'label_before' => 'function (value) {',
1332					'label_after' => '}',
1333					'grow' => 'auto',
1334					'rows' => 0,
1335					'maxlength' => $script_maxlength,
1336					'readonly' => $readonly
1337				]);
1338				break;
1339
1340			case ZBX_PREPROC_PROMETHEUS_PATTERN:
1341				$params = [
1342					$step_param_0->setAttribute('placeholder',
1343						_('<metric name>{<label name>="<label value>", ...} == <value>')
1344					),
1345					$step_param_1->setAttribute('placeholder', _('<label name>'))
1346				];
1347				break;
1348
1349			case ZBX_PREPROC_PROMETHEUS_TO_JSON:
1350				$params = $step_param_0->setAttribute('placeholder',
1351					_('<metric name>{<label name>="<label value>", ...} == <value>')
1352				);
1353				break;
1354
1355			// ZBX-16642
1356			case ZBX_PREPROC_CSV_TO_JSON:
1357				$step_param_2_value = (array_key_exists('params', $step) && array_key_exists(2, $step['params']))
1358					? $step['params'][2]
1359					: ZBX_PREPROC_CSV_NO_HEADER;
1360
1361				$params = [
1362					$step_param_0
1363						->setAttribute('placeholder', ',')
1364						->setWidth(ZBX_TEXTAREA_NUMERIC_STANDARD_WIDTH)
1365						->setAttribute('maxlength', 1),
1366					$step_param_1
1367						->setAttribute('placeholder', '"')
1368						->setWidth(ZBX_TEXTAREA_NUMERIC_STANDARD_WIDTH)
1369						->setAttribute('maxlength', 1),
1370					(new CCheckBox('preprocessing['.$i.'][params][2]', ZBX_PREPROC_CSV_HEADER))
1371						->setLabel(_('With header row'))
1372						->setChecked($step_param_2_value == ZBX_PREPROC_CSV_HEADER)
1373						->setReadonly($readonly)
1374				];
1375				break;
1376
1377			case ZBX_PREPROC_STR_REPLACE:
1378				$params = [
1379					$step_param_0->setAttribute('placeholder', _('search string')),
1380					$step_param_1->setAttribute('placeholder', _('replacement'))
1381				];
1382				break;
1383		}
1384
1385		// Create checkbox "Custom on fail" and enable or disable depending on preprocessing type.
1386		$on_fail = new CCheckBox('preprocessing['.$i.'][on_fail]');
1387
1388		switch ($step['type']) {
1389			case ZBX_PREPROC_RTRIM:
1390			case ZBX_PREPROC_LTRIM:
1391			case ZBX_PREPROC_TRIM:
1392			case ZBX_PREPROC_THROTTLE_VALUE:
1393			case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
1394			case ZBX_PREPROC_SCRIPT:
1395			case ZBX_PREPROC_STR_REPLACE:
1396				$on_fail->setEnabled(false);
1397				break;
1398
1399			default:
1400				$on_fail->setEnabled(!$readonly);
1401
1402				if ($step['error_handler'] != ZBX_PREPROC_FAIL_DEFAULT) {
1403					$on_fail->setChecked(true);
1404				}
1405				break;
1406		}
1407
1408		$error_handler = (new CRadioButtonList('preprocessing['.$i.'][error_handler]',
1409			($step['error_handler'] == ZBX_PREPROC_FAIL_DEFAULT)
1410				? ZBX_PREPROC_FAIL_DISCARD_VALUE
1411				: (int) $step['error_handler']
1412		))
1413			->addValue(_('Discard value'), ZBX_PREPROC_FAIL_DISCARD_VALUE)
1414			->addValue(_('Set value to'), ZBX_PREPROC_FAIL_SET_VALUE)
1415			->addValue(_('Set error to'), ZBX_PREPROC_FAIL_SET_ERROR)
1416			->setModern(true);
1417
1418		$error_handler_params = (new CTextBox('preprocessing['.$i.'][error_handler_params]',
1419			$step['error_handler_params'])
1420		)->setTitle($step['error_handler_params']);
1421
1422		if ($step['error_handler'] == ZBX_PREPROC_FAIL_DEFAULT) {
1423			$error_handler->setEnabled(false);
1424		}
1425
1426		if ($step['error_handler'] == ZBX_PREPROC_FAIL_DEFAULT
1427				|| $step['error_handler'] == ZBX_PREPROC_FAIL_DISCARD_VALUE) {
1428			$error_handler_params
1429				->setEnabled(false)
1430				->addStyle('display: none;');
1431		}
1432
1433		$on_fail_options = (new CDiv([
1434			new CLabel(_('Custom on fail')),
1435			$error_handler->setReadonly($readonly),
1436			$error_handler_params->setReadonly($readonly)
1437		]))->addClass('on-fail-options');
1438
1439		if ($step['error_handler'] == ZBX_PREPROC_FAIL_DEFAULT) {
1440			$on_fail_options->addStyle('display: none;');
1441		}
1442
1443		$preprocessing_list->addItem(
1444			(new CListItem([
1445				(new CDiv([
1446					(new CDiv())
1447						->addClass(ZBX_STYLE_DRAG_ICON)
1448						->addClass(!$sortable ? ZBX_STYLE_DISABLED : null),
1449					(new CDiv($preproc_types_select))
1450						->addClass('list-numbered-item')
1451						->addClass('step-name'),
1452					(new CDiv($params))->addClass('step-parameters'),
1453					(new CDiv($on_fail))->addClass('step-on-fail'),
1454					(new CDiv([
1455						(new CButton('preprocessing['.$i.'][test]', _('Test')))
1456							->addClass(ZBX_STYLE_BTN_LINK)
1457							->addClass('preprocessing-step-test')
1458							->removeId(),
1459						(new CButton('preprocessing['.$i.'][remove]', _('Remove')))
1460							->addClass(ZBX_STYLE_BTN_LINK)
1461							->addClass('element-table-remove')
1462							->setEnabled(!$readonly)
1463							->removeId()
1464					]))->addClass('step-action')
1465				]))->addClass('preprocessing-step'),
1466				$on_fail_options
1467			]))
1468				->addClass('preprocessing-list-item')
1469				->addClass('sortable')
1470				->setAttribute('data-step', $i)
1471		);
1472
1473		$i++;
1474	}
1475
1476	$preprocessing_list->addItem(
1477		(new CListItem([
1478			(new CDiv(
1479				(new CButton('param_add', _('Add')))
1480					->addClass(ZBX_STYLE_BTN_LINK)
1481					->addClass('element-table-add')
1482					->setEnabled(!$readonly)
1483			))->addClass('step-action'),
1484			(new CDiv(
1485				(new CButton('preproc_test_all', _('Test all steps')))
1486					->addClass(ZBX_STYLE_BTN_LINK)
1487					->addStyle(($i > 0) ? null : 'display: none')
1488			))->addClass('step-action')
1489		]))->addClass('preprocessing-list-foot')
1490	);
1491
1492	return $preprocessing_list;
1493}
1494
1495/**
1496 * Prepares data to copy items/triggers/graphs.
1497 *
1498 * @param string      $elements_field
1499 * @param null|string $title
1500 *
1501 * @return array
1502 */
1503function getCopyElementsFormData($elements_field, $title = null) {
1504	$data = [
1505		'title' => $title,
1506		'elements_field' => $elements_field,
1507		'elements' => getRequest($elements_field, []),
1508		'copy_type' => getRequest('copy_type', COPY_TYPE_TO_HOST_GROUP),
1509		'copy_targetids' => getRequest('copy_targetids', []),
1510		'hostid' => getRequest('hostid', 0)
1511	];
1512
1513	if (!$data['elements'] || !is_array($data['elements'])) {
1514		show_error_message(_('Incorrect list of items.'));
1515
1516		return $data;
1517	}
1518
1519	if ($data['copy_targetids']) {
1520		switch ($data['copy_type']) {
1521			case COPY_TYPE_TO_HOST_GROUP:
1522				$data['copy_targetids'] = CArrayHelper::renameObjectsKeys(API::HostGroup()->get([
1523					'output' => ['groupid', 'name'],
1524					'groupids' => $data['copy_targetids'],
1525					'editable' => true
1526				]), ['groupid' => 'id']);
1527				break;
1528
1529			case COPY_TYPE_TO_HOST:
1530				$data['copy_targetids'] = CArrayHelper::renameObjectsKeys(API::Host()->get([
1531					'output' => ['hostid', 'name'],
1532					'hostids' => $data['copy_targetids'],
1533					'editable' => true
1534				]), ['hostid' => 'id']);
1535				break;
1536
1537			case COPY_TYPE_TO_TEMPLATE:
1538				$data['copy_targetids'] = CArrayHelper::renameObjectsKeys(API::Template()->get([
1539					'output' => ['templateid', 'name'],
1540					'templateids' => $data['copy_targetids'],
1541					'editable' => true
1542				]), ['templateid' => 'id']);
1543		}
1544	}
1545
1546	return $data;
1547}
1548
1549function getTriggerMassupdateFormData() {
1550	$data = [
1551		'visible' => getRequest('visible', []),
1552		'dependencies' => getRequest('dependencies', []),
1553		'tags' => getRequest('tags', []),
1554		'mass_update_tags' => getRequest('mass_update_tags', ZBX_ACTION_ADD),
1555		'manual_close' => getRequest('manual_close', ZBX_TRIGGER_MANUAL_CLOSE_NOT_ALLOWED),
1556		'massupdate' => getRequest('massupdate', 1),
1557		'parent_discoveryid' => getRequest('parent_discoveryid'),
1558		'g_triggerid' => getRequest('g_triggerid', []),
1559		'priority' => getRequest('priority', 0),
1560		'config' => select_config(),
1561		'hostid' => getRequest('hostid', 0)
1562	];
1563
1564	if ($data['dependencies']) {
1565		$dependencyTriggers = API::Trigger()->get([
1566			'output' => ['triggerid', 'description', 'flags'],
1567			'selectHosts' => ['hostid', 'name'],
1568			'triggerids' => $data['dependencies'],
1569			'preservekeys' => true
1570		]);
1571
1572		if ($data['parent_discoveryid']) {
1573			$dependencyTriggerPrototypes = API::TriggerPrototype()->get([
1574				'output' => ['triggerid', 'description', 'flags'],
1575				'selectHosts' => ['hostid', 'name'],
1576				'triggerids' => $data['dependencies'],
1577				'preservekeys' => true
1578			]);
1579			$data['dependencies'] = $dependencyTriggers + $dependencyTriggerPrototypes;
1580		}
1581		else {
1582			$data['dependencies'] = $dependencyTriggers;
1583		}
1584	}
1585
1586	foreach ($data['dependencies'] as &$dependency) {
1587		order_result($dependency['hosts'], 'name', ZBX_SORT_UP);
1588	}
1589	unset($dependency);
1590
1591	order_result($data['dependencies'], 'description', ZBX_SORT_UP);
1592
1593	if (!$data['tags']) {
1594		$data['tags'][] = ['tag' => '', 'value' => ''];
1595	}
1596
1597	return $data;
1598}
1599
1600/**
1601 * Generate data for the trigger configuration form.
1602 *
1603 * @param array       $data                                     Trigger data array.
1604 * @param string      $data['form']                             Form action.
1605 * @param string      $data['form_refresh']                     Form refresh.
1606 * @param null|string $data['parent_discoveryid']               Parent discovery ID.
1607 * @param array       $data['dependencies']                     Trigger dependencies.
1608 * @param array       $data['db_dependencies']                  DB trigger dependencies.
1609 * @param string      $data['triggerid']                        Trigger ID.
1610 * @param string      $data['expression']                       Trigger expression.
1611 * @param string      $data['recovery_expression']              Trigger recovery expression.
1612 * @param string      $data['expr_temp']                        Trigger temporary expression.
1613 * @param string      $data['recovery_expr_temp']               Trigger temporary recovery expression.
1614 * @param string      $data['recovery_mode']                    Trigger recovery mode.
1615 * @param string      $data['description']                      Trigger description.
1616 * @param int         $data['type']                             Trigger problem event generation mode.
1617 * @param string      $data['priority']                         Trigger severity.
1618 * @param int         $data['status']                           Trigger status.
1619 * @param string      $data['comments']                         Trigger description.
1620 * @param string      $data['url']                              Trigger URL.
1621 * @param string      $data['expression_constructor']           Trigger expression constructor mode.
1622 * @param string      $data['recovery_expression_constructor']  Trigger recovery expression constructor mode.
1623 * @param bool        $data['limited']                          Templated trigger.
1624 * @param array       $data['templates']                        Trigger templates.
1625 * @param string      $data['hostid']                           Host ID.
1626 * @param string      $data['expression_action']                Trigger expression action.
1627 * @param string      $data['recovery_expression_action']       Trigger recovery expression action.
1628 *
1629 * @return array
1630 */
1631function getTriggerFormData(array $data) {
1632	if ($data['triggerid'] !== null) {
1633		// Get trigger.
1634		$options = [
1635			'output' => API_OUTPUT_EXTEND,
1636			'selectHosts' => ['hostid'],
1637			'triggerids' => $data['triggerid']
1638		];
1639
1640		if (!hasRequest('form_refresh')) {
1641			$options['selectTags'] = ['tag', 'value'];
1642		}
1643
1644		if ($data['show_inherited_tags']) {
1645			$options['selectItems'] = ['itemid', 'templateid', 'flags'];
1646		}
1647
1648		if ($data['parent_discoveryid'] === null) {
1649			$options['selectDiscoveryRule'] = ['itemid', 'name', 'templateid'];
1650			$options['selectTriggerDiscovery'] = ['parent_triggerid'];
1651			$triggers = API::Trigger()->get($options);
1652			$flag = ZBX_FLAG_DISCOVERY_NORMAL;
1653		}
1654		else {
1655			$triggers = API::TriggerPrototype()->get($options);
1656			$flag = ZBX_FLAG_DISCOVERY_PROTOTYPE;
1657		}
1658
1659		$triggers = CMacrosResolverHelper::resolveTriggerExpressions($triggers,
1660			['sources' => ['expression', 'recovery_expression']]
1661		);
1662
1663		$trigger = reset($triggers);
1664
1665		if (!hasRequest('form_refresh')) {
1666			$data['tags'] = $trigger['tags'];
1667		}
1668
1669		// Get templates.
1670		$data['templates'] = makeTriggerTemplatesHtml($trigger['triggerid'],
1671			getTriggerParentTemplates([$trigger], $flag), $flag
1672		);
1673
1674		if ($data['show_inherited_tags']) {
1675			if ($data['parent_discoveryid'] === null) {
1676				if ($trigger['discoveryRule']) {
1677					$item_parent_templates = getItemParentTemplates([$trigger['discoveryRule']],
1678						ZBX_FLAG_DISCOVERY_RULE
1679					)['templates'];
1680				}
1681				else {
1682					$item_parent_templates = getItemParentTemplates($trigger['items'],
1683						ZBX_FLAG_DISCOVERY_NORMAL
1684					)['templates'];
1685				}
1686			}
1687			else {
1688				$items = [];
1689				$item_prototypes = [];
1690
1691				foreach ($trigger['items'] as $item) {
1692					if ($item['flags'] == ZBX_FLAG_DISCOVERY_NORMAL) {
1693						$items[] = $item;
1694					}
1695					else {
1696						$item_prototypes[] = $item;
1697					}
1698				}
1699
1700				$item_parent_templates = getItemParentTemplates($items, ZBX_FLAG_DISCOVERY_NORMAL)['templates']
1701					+ getItemParentTemplates($item_prototypes, ZBX_FLAG_DISCOVERY_PROTOTYPE)['templates'];
1702			}
1703			unset($item_parent_templates[0]);
1704
1705			$db_templates = $item_parent_templates
1706				? API::Template()->get([
1707					'output' => ['templateid'],
1708					'selectTags' => ['tag', 'value'],
1709					'templateids' => array_keys($item_parent_templates),
1710					'preservekeys' => true
1711				])
1712				: [];
1713
1714			$inherited_tags = [];
1715
1716			foreach ($item_parent_templates as $templateid => $template) {
1717				if (array_key_exists($templateid, $db_templates)) {
1718					foreach ($db_templates[$templateid]['tags'] as $tag) {
1719						if (array_key_exists($tag['tag'], $inherited_tags)
1720								&& array_key_exists($tag['value'], $inherited_tags[$tag['tag']])) {
1721							$inherited_tags[$tag['tag']][$tag['value']]['parent_templates'] += [
1722								$templateid => $template
1723							];
1724						}
1725						else {
1726							$inherited_tags[$tag['tag']][$tag['value']] = $tag + [
1727								'parent_templates' => [$templateid => $template],
1728								'type' => ZBX_PROPERTY_INHERITED
1729							];
1730						}
1731					}
1732				}
1733			}
1734
1735			$db_hosts = API::Host()->get([
1736				'output' => [],
1737				'selectTags' => ['tag', 'value'],
1738				'hostids' => $data['hostid']
1739			]);
1740
1741			if ($db_hosts) {
1742				foreach ($db_hosts[0]['tags'] as $tag) {
1743					$inherited_tags[$tag['tag']][$tag['value']] = $tag;
1744					$inherited_tags[$tag['tag']][$tag['value']]['type'] = ZBX_PROPERTY_INHERITED;
1745				}
1746			}
1747
1748			foreach ($data['tags'] as $tag) {
1749				if (array_key_exists($tag['tag'], $inherited_tags)
1750						&& array_key_exists($tag['value'], $inherited_tags[$tag['tag']])) {
1751					$inherited_tags[$tag['tag']][$tag['value']]['type'] = ZBX_PROPERTY_BOTH;
1752				}
1753				else {
1754					$inherited_tags[$tag['tag']][$tag['value']] = $tag + ['type' => ZBX_PROPERTY_OWN];
1755				}
1756			}
1757
1758			$data['tags'] = [];
1759
1760			foreach ($inherited_tags as $tag) {
1761				foreach ($tag as $value) {
1762					$data['tags'][] = $value;
1763				}
1764			}
1765		}
1766
1767		$data['limited'] = ($trigger['templateid'] != 0);
1768
1769		// Select first host from triggers if no matching value is given.
1770		$hosts = $trigger['hosts'];
1771		if (count($hosts) > 0 && !in_array(['hostid' => $data['hostid']], $hosts)) {
1772			$host = reset($hosts);
1773			$data['hostid'] = $host['hostid'];
1774		}
1775	}
1776
1777	// tags
1778	if (!$data['tags']) {
1779		$data['tags'][] = ['tag' => '', 'value' => ''];
1780	}
1781	else {
1782		CArrayHelper::sort($data['tags'], ['tag', 'value']);
1783	}
1784
1785	if ((!empty($data['triggerid']) && !isset($_REQUEST['form_refresh'])) || $data['limited']) {
1786		$data['expression'] = $trigger['expression'];
1787		$data['recovery_expression'] = $trigger['recovery_expression'];
1788
1789		if (!$data['limited'] || !isset($_REQUEST['form_refresh'])) {
1790			$data['description'] = $trigger['description'];
1791			$data['opdata'] = $trigger['opdata'];
1792			$data['type'] = $trigger['type'];
1793			$data['recovery_mode'] = $trigger['recovery_mode'];
1794			$data['correlation_mode'] = $trigger['correlation_mode'];
1795			$data['correlation_tag'] = $trigger['correlation_tag'];
1796			$data['manual_close'] = $trigger['manual_close'];
1797			$data['priority'] = $trigger['priority'];
1798			$data['status'] = $trigger['status'];
1799			$data['comments'] = $trigger['comments'];
1800			$data['url'] = $trigger['url'];
1801
1802			if ($data['parent_discoveryid'] !== null) {
1803				$data['discover'] = $trigger['discover'];
1804			}
1805
1806			$db_triggers = DBselect(
1807				'SELECT t.triggerid,t.description'.
1808				' FROM triggers t,trigger_depends d'.
1809				' WHERE t.triggerid=d.triggerid_up'.
1810					' AND d.triggerid_down='.zbx_dbstr($data['triggerid'])
1811			);
1812			while ($db_trigger = DBfetch($db_triggers)) {
1813				if (uint_in_array($db_trigger['triggerid'], $data['dependencies'])) {
1814					continue;
1815				}
1816				array_push($data['dependencies'], $db_trigger['triggerid']);
1817			}
1818		}
1819	}
1820
1821	$readonly = false;
1822	if ($data['triggerid'] !== null) {
1823		$data['flags'] = $trigger['flags'];
1824
1825		if ($data['parent_discoveryid'] === null) {
1826			$data['discoveryRule'] = $trigger['discoveryRule'];
1827			$data['triggerDiscovery'] = $trigger['triggerDiscovery'];
1828		}
1829
1830		if ($trigger['flags'] == ZBX_FLAG_DISCOVERY_CREATED || $data['limited']) {
1831			$readonly = true;
1832		}
1833	}
1834
1835	// Trigger expression constructor.
1836	if ($data['expression_constructor'] == IM_TREE) {
1837		$analyze = analyzeExpression($data['expression'], TRIGGER_EXPRESSION);
1838
1839		if ($analyze !== false) {
1840			list($data['expression_formula'], $data['expression_tree']) = $analyze;
1841
1842			if ($data['expression_action'] !== '' && $data['expression_tree'] !== null) {
1843				$new_expr = remakeExpression($data['expression'], $_REQUEST['expr_target_single'],
1844					$data['expression_action'], $data['expr_temp']
1845				);
1846
1847				if ($new_expr !== false) {
1848					$data['expression'] = $new_expr;
1849					$analyze = analyzeExpression($data['expression'], TRIGGER_EXPRESSION);
1850
1851					if ($analyze !== false) {
1852						list($data['expression_formula'], $data['expression_tree']) = $analyze;
1853					}
1854					else {
1855						show_messages(false, '', _('Expression syntax error.'));
1856					}
1857
1858					$data['expr_temp'] = '';
1859				}
1860				else {
1861					show_messages(false, '', _('Expression syntax error.'));
1862				}
1863			}
1864
1865			$data['expression_field_name'] = 'expr_temp';
1866			$data['expression_field_value'] = $data['expr_temp'];
1867			$data['expression_field_readonly'] = true;
1868		}
1869		else {
1870			show_messages(false, '', _('Expression syntax error.'));
1871			$data['expression_field_name'] = 'expression';
1872			$data['expression_field_value'] = $data['expression'];
1873			$data['expression_field_readonly'] = $readonly;
1874			$data['expression_constructor'] = IM_ESTABLISHED;
1875		}
1876	}
1877	elseif ($data['expression_constructor'] != IM_TREE) {
1878		$data['expression_field_name'] = 'expression';
1879		$data['expression_field_value'] = $data['expression'];
1880		$data['expression_field_readonly'] = $readonly;
1881	}
1882
1883	// Trigger recovery expression constructor.
1884	if ($data['recovery_expression_constructor'] == IM_TREE) {
1885		$analyze = analyzeExpression($data['recovery_expression'], TRIGGER_RECOVERY_EXPRESSION);
1886
1887		if ($analyze !== false) {
1888			list($data['recovery_expression_formula'], $data['recovery_expression_tree']) = $analyze;
1889
1890			if ($data['recovery_expression_action'] !== '' && $data['recovery_expression_tree'] !== null) {
1891				$new_expr = remakeExpression($data['recovery_expression'], $_REQUEST['recovery_expr_target_single'],
1892					$data['recovery_expression_action'], $data['recovery_expr_temp']
1893				);
1894
1895				if ($new_expr !== false) {
1896					$data['recovery_expression'] = $new_expr;
1897					$analyze = analyzeExpression($data['recovery_expression'], TRIGGER_RECOVERY_EXPRESSION);
1898
1899					if ($analyze !== false) {
1900						list($data['recovery_expression_formula'], $data['recovery_expression_tree']) = $analyze;
1901					}
1902					else {
1903						show_messages(false, '', _('Recovery expression syntax error.'));
1904					}
1905
1906					$data['recovery_expr_temp'] = '';
1907				}
1908				else {
1909					show_messages(false, '', _('Recovery expression syntax error.'));
1910				}
1911			}
1912
1913			$data['recovery_expression_field_name'] = 'recovery_expr_temp';
1914			$data['recovery_expression_field_value'] = $data['recovery_expr_temp'];
1915			$data['recovery_expression_field_readonly'] = true;
1916		}
1917		else {
1918			show_messages(false, '', _('Recovery expression syntax error.'));
1919			$data['recovery_expression_field_name'] = 'recovery_expression';
1920			$data['recovery_expression_field_value'] = $data['recovery_expression'];
1921			$data['recovery_expression_field_readonly'] = $readonly;
1922			$data['recovery_expression_constructor'] = IM_ESTABLISHED;
1923		}
1924	}
1925	elseif ($data['recovery_expression_constructor'] != IM_TREE) {
1926		$data['recovery_expression_field_name'] = 'recovery_expression';
1927		$data['recovery_expression_field_value'] = $data['recovery_expression'];
1928		$data['recovery_expression_field_readonly'] = $readonly;
1929	}
1930
1931	if ($data['dependencies']) {
1932		$dependencyTriggers = API::Trigger()->get([
1933			'output' => ['triggerid', 'description', 'flags'],
1934			'selectHosts' => ['hostid', 'name'],
1935			'triggerids' => $data['dependencies'],
1936			'preservekeys' => true
1937		]);
1938
1939		if ($data['parent_discoveryid']) {
1940			$dependencyTriggerPrototypes = API::TriggerPrototype()->get([
1941				'output' => ['triggerid', 'description', 'flags'],
1942				'selectHosts' => ['hostid', 'name'],
1943				'triggerids' => $data['dependencies'],
1944				'preservekeys' => true
1945			]);
1946
1947			$data['db_dependencies'] = $dependencyTriggers + $dependencyTriggerPrototypes;
1948		}
1949		else {
1950			$data['db_dependencies'] = $dependencyTriggers;
1951		}
1952	}
1953
1954	foreach ($data['db_dependencies'] as &$dependency) {
1955		order_result($dependency['hosts'], 'name', ZBX_SORT_UP);
1956	}
1957	unset($dependency);
1958
1959	order_result($data['db_dependencies'], 'description');
1960
1961	return $data;
1962}
1963
1964/**
1965 * Renders tag table row.
1966 *
1967 * @param int|string $index
1968 * @param string     $tag      (optional)
1969 * @param string     $value    (optional)
1970 * @param array      $options  (optional)
1971 *
1972 * @return CRow
1973 */
1974function renderTagTableRow($index, $tag = '', $value = '', array $options = []) {
1975	$options = array_merge([
1976		'readonly' => false,
1977		'field_name' => 'tags'
1978	], $options);
1979
1980	return (new CRow([
1981		(new CCol(
1982			(new CTextAreaFlexible($options['field_name'].'['.$index.'][tag]', $tag, $options))
1983				->setWidth(ZBX_TEXTAREA_TAG_WIDTH)
1984				->setAttribute('placeholder', _('tag'))
1985		))->addClass(ZBX_STYLE_TEXTAREA_FLEXIBLE_PARENT),
1986		(new CCol(
1987			(new CTextAreaFlexible($options['field_name'].'['.$index.'][value]', $value, $options))
1988				->setWidth(ZBX_TEXTAREA_TAG_VALUE_WIDTH)
1989				->setAttribute('placeholder', _('value'))
1990		))->addClass(ZBX_STYLE_TEXTAREA_FLEXIBLE_PARENT),
1991		(new CButton($options['field_name'].'['.$index.'][remove]', _('Remove')))
1992			->addClass(ZBX_STYLE_BTN_LINK)
1993			->addClass('element-table-remove')
1994			->setEnabled(!$options['readonly'])
1995	]))->addClass('form_row');
1996}
1997
1998/**
1999 * Renders tag table.
2000 *
2001 * @param array  $tags
2002 * @param array  $tags[]['tag']
2003 * @param array  $tags[]['value']
2004 * @param bool   $readonly         (optional)
2005 *
2006 * @return CTable
2007 */
2008function renderTagTable(array $tags, $readonly = false, array $options = []) {
2009	$table = (new CTable())->addClass(ZBX_STYLE_TEXTAREA_FLEXIBLE_CONTAINER);
2010
2011	$row_options = ['readonly' => $readonly];
2012
2013	if (array_key_exists('field_name', $options)) {
2014		$row_options['field_name'] = $options['field_name'];
2015	}
2016
2017	foreach ($tags as $index => $tag) {
2018		$table->addRow(renderTagTableRow($index, $tag['tag'], $tag['value'], $row_options));
2019	}
2020
2021	return $table->setFooter(new CCol(
2022		(new CButton('tag_add', _('Add')))
2023			->addClass(ZBX_STYLE_BTN_LINK)
2024			->addClass('element-table-add')
2025			->setEnabled(!$readonly)
2026	));
2027}
2028