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/items.inc.php';
25require_once dirname(__FILE__).'/include/forms.inc.php';
26
27$page['title'] = _('Configuration of item prototypes');
28$page['file'] = 'disc_prototypes.php';
29$page['scripts'] = ['effects.js', 'class.cviewswitcher.js', 'multilineinput.js', 'multiselect.js', 'items.js',
30	'textareaflexible.js', 'class.tab-indicators.js'
31];
32
33require_once dirname(__FILE__).'/include/page_header.php';
34
35$paramsFieldName = getParamFieldNameByType(getRequest('type', 0));
36
37// VAR	TYPE	OPTIONAL	FLAGS	VALIDATION	EXCEPTION
38$fields = [
39	'parent_discoveryid' =>			[T_ZBX_INT, O_MAND, P_SYS,	DB_ID,		null],
40	'hostid' =>						[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		null],
41	'itemid' =>						[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		'isset({form}) && {form} == "update"'],
42	'interfaceid' =>				[T_ZBX_INT, O_OPT, P_SYS,	DB_ID,		null, _('Interface')],
43	'name' =>						[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,	'isset({add}) || isset({update})',
44										_('Name')
45									],
46	'description' =>				[T_ZBX_STR, O_OPT, null,	null,		'isset({add}) || isset({update})'],
47	'key' =>						[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,	'isset({add}) || isset({update})',
48										_('Key')
49									],
50	'master_itemid' =>				[T_ZBX_STR, O_OPT, null,	null,
51										'(isset({add}) || isset({update})) && isset({type})'.
52											' && {type} == '.ITEM_TYPE_DEPENDENT,
53										_('Master item')
54									],
55	'delay' =>						[T_ZBX_TU, O_OPT, P_ALLOW_USER_MACRO | P_ALLOW_LLD_MACRO, null,
56										'(isset({add}) || isset({update}))'.
57											' && isset({type}) && {type} != '.ITEM_TYPE_TRAPPER.
58												' && {type} != '.ITEM_TYPE_SNMPTRAP.
59												' && {type} != '.ITEM_TYPE_DEPENDENT.
60												' && !({type} == '.ITEM_TYPE_ZABBIX_ACTIVE.
61													' && isset({key}) && strncmp({key}, "mqtt.get", 8) === 0)',
62										_('Update interval')
63									],
64	'delay_flex' =>					[T_ZBX_STR, O_OPT, null,	null,			null],
65	'status' =>						[T_ZBX_INT, O_OPT, null,	IN([ITEM_STATUS_ACTIVE, ITEM_STATUS_DISABLED]), null],
66	'discover' =>					[T_ZBX_INT, O_OPT, null,	IN([ZBX_PROTOTYPE_DISCOVER, ZBX_PROTOTYPE_NO_DISCOVER]), null],
67	'type' =>						[T_ZBX_INT, O_OPT, null,
68										IN([-1, ITEM_TYPE_ZABBIX, ITEM_TYPE_TRAPPER, ITEM_TYPE_SIMPLE,
69											ITEM_TYPE_INTERNAL, ITEM_TYPE_ZABBIX_ACTIVE,
70											ITEM_TYPE_EXTERNAL, ITEM_TYPE_DB_MONITOR, ITEM_TYPE_IPMI, ITEM_TYPE_SSH,
71											ITEM_TYPE_TELNET, ITEM_TYPE_JMX, ITEM_TYPE_CALCULATED, ITEM_TYPE_SNMPTRAP,
72											ITEM_TYPE_DEPENDENT, ITEM_TYPE_HTTPAGENT, ITEM_TYPE_SNMP, ITEM_TYPE_SCRIPT
73										]),
74										'isset({add}) || isset({update})'
75									],
76	'value_type' =>					[T_ZBX_INT, O_OPT, null,	IN('0,1,2,3,4'), 'isset({add}) || isset({update})'],
77	'valuemapid' =>					[T_ZBX_INT, O_OPT, null,	DB_ID,		null],
78	'authtype' =>					[T_ZBX_INT, O_OPT, null,	IN(ITEM_AUTHTYPE_PASSWORD.','.ITEM_AUTHTYPE_PUBLICKEY),
79										'(isset({add}) || isset({update})) && isset({type}) && {type} == '.ITEM_TYPE_SSH
80									],
81	'username' =>					[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,
82										'(isset({add}) || isset({update})) && isset({type})'.
83											' && '.IN(ITEM_TYPE_SSH.','.ITEM_TYPE_TELNET, 'type'),
84										_('User name')
85									],
86	'password' =>					[T_ZBX_STR, O_OPT, null,	null,
87										'(isset({add}) || isset({update})) && isset({type})'.
88											' && '.IN(ITEM_TYPE_SSH.','.ITEM_TYPE_TELNET, 'type')
89									],
90	'publickey' =>					[T_ZBX_STR, O_OPT, null,	null,
91										'(isset({add}) || isset({update})) && isset({type})'.
92											' && {type} == '.ITEM_TYPE_SSH.' && {authtype} == '.ITEM_AUTHTYPE_PUBLICKEY
93									],
94	'privatekey' =>					[T_ZBX_STR, O_OPT, null,	null,
95										'(isset({add}) || isset({update})) && isset({type})'.
96											' && {type} == '.ITEM_TYPE_SSH.' && {authtype} == '.ITEM_AUTHTYPE_PUBLICKEY
97									],
98	$paramsFieldName =>				[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,
99										'(isset({add}) || isset({update})) && isset({type})'.
100											' && '.IN(ITEM_TYPE_SSH.','.ITEM_TYPE_DB_MONITOR.','.ITEM_TYPE_TELNET.','.
101												ITEM_TYPE_CALCULATED.','.ITEM_TYPE_SCRIPT, 'type'
102											),
103										getParamFieldLabelByType(getRequest('type', 0))
104									],
105	'snmp_oid' =>					[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,
106										'(isset({add}) || isset({update})) && isset({type})'.
107											' && {type} == '.ITEM_TYPE_SNMP,
108										_('SNMP OID')
109									],
110	'ipmi_sensor' =>				[T_ZBX_STR, O_OPT, P_NO_TRIM, null,
111										'(isset({add}) || isset({update})) && isset({type})'.
112											' && {type} == '.ITEM_TYPE_IPMI,
113										_('IPMI sensor')
114									],
115	'trapper_hosts' =>				[T_ZBX_STR, O_OPT, null,	null,
116										'(isset({add}) || isset({update})) && isset({type})'.
117											' && {type} == '.ITEM_TYPE_TRAPPER
118									],
119	'units' =>						[T_ZBX_STR, O_OPT, null,	null,
120										'(isset({add}) || isset({update})) && isset({value_type})'.
121											' && '.IN(ITEM_VALUE_TYPE_FLOAT.','.ITEM_VALUE_TYPE_UINT64, 'value_type')
122									],
123	'logtimefmt' =>					[T_ZBX_STR, O_OPT, null,	null,
124										'(isset({add}) || isset({update})) && isset({value_type})'.
125											' && {value_type} == '.ITEM_VALUE_TYPE_LOG
126									],
127	'preprocessing' =>				[T_ZBX_STR, O_OPT, P_NO_TRIM,	null,	null],
128	'group_itemid' =>				[T_ZBX_INT, O_OPT, null,	DB_ID,		null],
129	'history_mode' =>				[T_ZBX_INT, O_OPT, null,	IN([ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]), null],
130	'history' =>					[T_ZBX_STR, O_OPT, null,	null,
131										'(isset({add}) || isset({update}))'.
132											' && isset({history_mode}) && {history_mode}=='.ITEM_STORAGE_CUSTOM,
133										_('History storage period')
134									],
135	'trends_mode' =>				[T_ZBX_INT, O_OPT, null,	IN([ITEM_STORAGE_OFF, ITEM_STORAGE_CUSTOM]), null],
136	'trends' =>						[T_ZBX_STR, O_OPT, null,	null,
137										'(isset({add}) || isset({update}))'.
138											' && isset({trends_mode}) && {trends_mode}=='.ITEM_STORAGE_CUSTOM.
139											' && isset({value_type})'.
140											' && '.IN(ITEM_VALUE_TYPE_FLOAT.','.ITEM_VALUE_TYPE_UINT64, 'value_type'),
141										_('Trend storage period')
142									],
143	'jmx_endpoint' =>				[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,
144										'(isset({add}) || isset({update})) && isset({type}) && {type} == '.ITEM_TYPE_JMX
145									],
146	'timeout' =>	 				[T_ZBX_TU, O_OPT, P_ALLOW_USER_MACRO|P_ALLOW_LLD_MACRO,	null,
147										'(isset({add}) || isset({update})) && isset({type})'.
148											' && '.IN(ITEM_TYPE_HTTPAGENT.','.ITEM_TYPE_SCRIPT, 'type'),
149										_('Timeout')
150									],
151	'url' =>						[T_ZBX_STR, O_OPT, null,	NOT_EMPTY,
152										'(isset({add}) || isset({update})) && isset({type})'.
153											' && {type} == '.ITEM_TYPE_HTTPAGENT,
154										_('URL')
155									],
156	'query_fields' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
157	'parameters' =>					[T_ZBX_STR, O_OPT, null,	null,		null],
158	'posts' =>						[T_ZBX_STR, O_OPT, null,	null,		null],
159	'status_codes' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
160	'follow_redirects' =>			[T_ZBX_INT, O_OPT, null,
161										IN([HTTPTEST_STEP_FOLLOW_REDIRECTS_OFF, HTTPTEST_STEP_FOLLOW_REDIRECTS_ON]),
162										null
163									],
164	'post_type' =>					[T_ZBX_INT, O_OPT, null,
165										IN([ZBX_POSTTYPE_RAW, ZBX_POSTTYPE_JSON, ZBX_POSTTYPE_XML]),
166										null
167									],
168	'http_proxy' =>					[T_ZBX_STR, O_OPT, null,	null,		null],
169	'headers' =>					[T_ZBX_STR, O_OPT, null,	null,		null],
170	'retrieve_mode' =>				[T_ZBX_INT, O_OPT, null,
171										IN([HTTPTEST_STEP_RETRIEVE_MODE_CONTENT, HTTPTEST_STEP_RETRIEVE_MODE_HEADERS,
172											HTTPTEST_STEP_RETRIEVE_MODE_BOTH
173										]),
174										null
175									],
176	'request_method' =>				[T_ZBX_INT, O_OPT, null,
177										IN([HTTPCHECK_REQUEST_GET, HTTPCHECK_REQUEST_POST, HTTPCHECK_REQUEST_PUT,
178											HTTPCHECK_REQUEST_HEAD
179										]),
180										null
181									],
182	'output_format' =>				[T_ZBX_INT, O_OPT, null,	IN([HTTPCHECK_STORE_RAW, HTTPCHECK_STORE_JSON]), null],
183	'allow_traps' =>				[T_ZBX_INT, O_OPT, null,
184										IN([HTTPCHECK_ALLOW_TRAPS_OFF, HTTPCHECK_ALLOW_TRAPS_ON]),
185										null
186									],
187	'ssl_cert_file' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
188	'ssl_key_file' =>				[T_ZBX_STR, O_OPT, null,	null,		null],
189	'ssl_key_password' =>			[T_ZBX_STR, O_OPT, null,	null,		null],
190	'verify_peer' =>				[T_ZBX_INT, O_OPT, null,
191										IN([HTTPTEST_VERIFY_PEER_OFF, HTTPTEST_VERIFY_PEER_ON]),
192										null
193									],
194	'verify_host' =>				[T_ZBX_INT, O_OPT, null,
195										IN([HTTPTEST_VERIFY_HOST_OFF, HTTPTEST_VERIFY_HOST_ON]),
196										null
197									],
198	'http_authtype' =>				[T_ZBX_INT, O_OPT, null,
199										IN([HTTPTEST_AUTH_NONE, HTTPTEST_AUTH_BASIC, HTTPTEST_AUTH_NTLM,
200											HTTPTEST_AUTH_KERBEROS, HTTPTEST_AUTH_DIGEST
201										]),
202										null
203									],
204	'http_username' =>				[T_ZBX_STR, O_OPT, null,	null,
205										'(isset({add}) || isset({update})) && isset({http_authtype})'.
206											' && ({http_authtype} == '.HTTPTEST_AUTH_BASIC.
207												' || {http_authtype} == '.HTTPTEST_AUTH_NTLM.
208												' || {http_authtype} == '.HTTPTEST_AUTH_KERBEROS.
209												' || {http_authtype} == '.HTTPTEST_AUTH_DIGEST.
210											')',
211										_('Username')
212									],
213	'http_password' =>				[T_ZBX_STR, O_OPT, null,	null,
214										'(isset({add}) || isset({update})) && isset({http_authtype})'.
215											' && ({http_authtype} == '.HTTPTEST_AUTH_BASIC.
216												' || {http_authtype} == '.HTTPTEST_AUTH_NTLM.
217												' || {http_authtype} == '.HTTPTEST_AUTH_KERBEROS.
218												' || {http_authtype} == '.HTTPTEST_AUTH_DIGEST.
219											')',
220										_('Password')
221									],
222	'visible' =>					[T_ZBX_STR, O_OPT, null,	null,		null],
223	'context' =>					[T_ZBX_STR, O_MAND, P_SYS,	IN('"host", "template"'),	null],
224	'tags' =>						[T_ZBX_STR, O_OPT, null,	null,		null],
225	'show_inherited_tags' =>		[T_ZBX_INT, O_OPT, null,	IN([0,1]),	null],
226	// actions
227	'action' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT,
228										IN('"itemprototype.massdelete","itemprototype.massdisable",'.
229											'"itemprototype.massenable","itemprototype.massdiscover.enable",'.
230											'"itemprototype.massdiscover.disable"'
231										),
232										null
233									],
234	'add' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
235	'update' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
236	'clone' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
237	'delete' =>						[T_ZBX_STR, O_OPT, P_SYS|P_ACT, null,	null],
238	'cancel' =>						[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
239	'form' =>						[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
240	'form_refresh' =>				[T_ZBX_INT, O_OPT, null,	null,		null],
241	// filter
242	'filter_set' =>					[T_ZBX_STR, O_OPT, P_SYS,	null,		null],
243	// sort and sortorder
244	'sort' =>						[T_ZBX_STR, O_OPT, P_SYS,
245										IN('"delay","history","key_","name","status","trends","type","discover"'), null
246									],
247	'sortorder' =>					[T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'),	null]
248];
249$valid_input = check_fields($fields);
250
251$_REQUEST['params'] = getRequest($paramsFieldName, '');
252unset($_REQUEST[$paramsFieldName]);
253
254// permissions
255$discoveryRule = API::DiscoveryRule()->get([
256	'output' => ['hostid'],
257	'itemids' => getRequest('parent_discoveryid'),
258	'editable' => true
259]);
260$discoveryRule = reset($discoveryRule);
261if (!$discoveryRule) {
262	access_deny();
263}
264
265$itemPrototypeId = getRequest('itemid');
266if ($itemPrototypeId) {
267	$item_prorotypes = API::ItemPrototype()->get([
268		'output' => [],
269		'itemids' => $itemPrototypeId,
270		'editable' => true
271	]);
272
273	if (!$item_prorotypes) {
274		access_deny();
275	}
276}
277
278// Convert CR+LF to LF in preprocessing script.
279if (hasRequest('preprocessing')) {
280	foreach ($_REQUEST['preprocessing'] as &$step) {
281		if ($step['type'] == ZBX_PREPROC_SCRIPT) {
282			$step['params'][0] = CRLFtoLF($step['params'][0]);
283		}
284	}
285	unset($step);
286}
287
288$tags = getRequest('tags', []);
289foreach ($tags as $key => $tag) {
290	if ($tag['tag'] === '' && $tag['value'] === '') {
291		unset($tags[$key]);
292	}
293	elseif (array_key_exists('type', $tag) && !($tag['type'] & ZBX_PROPERTY_OWN)) {
294		unset($tags[$key]);
295	}
296	else {
297		unset($tags[$key]['type']);
298	}
299}
300
301/*
302 * Actions
303 */
304if (hasRequest('delete') && hasRequest('itemid')) {
305	DBstart();
306	$result = API::ItemPrototype()->delete([getRequest('itemid')]);
307	$result = DBend($result);
308
309	if ($result) {
310		uncheckTableRows(getRequest('parent_discoveryid'));
311	}
312	show_messages($result, _('Item prototype deleted'), _('Cannot delete item prototype'));
313
314	unset($_REQUEST['itemid'], $_REQUEST['form']);
315}
316elseif (hasRequest('add') || hasRequest('update')) {
317	$result = true;
318	DBstart();
319
320	$delay = getRequest('delay', DB::getDefault('items', 'delay'));
321	$type = getRequest('type', ITEM_TYPE_ZABBIX);
322
323	/*
324	 * "delay_flex" is a temporary field that collects flexible and scheduling intervals separated by a semicolon.
325	 * In the end, custom intervals together with "delay" are stored in the "delay" variable.
326	 */
327	if ($type != ITEM_TYPE_TRAPPER && $type != ITEM_TYPE_SNMPTRAP
328			&& ($type != ITEM_TYPE_ZABBIX_ACTIVE || strncmp(getRequest('key'), 'mqtt.get', 8) !== 0)
329			&& hasRequest('delay_flex')) {
330		$intervals = [];
331		$simple_interval_parser = new CSimpleIntervalParser([
332			'usermacros' => true,
333			'lldmacros' => true
334		]);
335		$time_period_parser = new CTimePeriodParser([
336			'usermacros' => true,
337			'lldmacros' => true
338		]);
339		$scheduling_interval_parser = new CSchedulingIntervalParser([
340			'usermacros' => true,
341			'lldmacros' => true
342		]);
343
344		foreach (getRequest('delay_flex') as $interval) {
345			if ($interval['type'] == ITEM_DELAY_FLEXIBLE) {
346				if ($interval['delay'] === '' && $interval['period'] === '') {
347					continue;
348				}
349
350				if ($simple_interval_parser->parse($interval['delay']) != CParser::PARSE_SUCCESS) {
351					$result = false;
352					info(_s('Invalid interval "%1$s".', $interval['delay']));
353					break;
354				}
355				elseif ($time_period_parser->parse($interval['period']) != CParser::PARSE_SUCCESS) {
356					$result = false;
357					info(_s('Invalid interval "%1$s".', $interval['period']));
358					break;
359				}
360
361				$intervals[] = $interval['delay'].'/'.$interval['period'];
362			}
363			else {
364				if ($interval['schedule'] === '') {
365					continue;
366				}
367
368				if ($scheduling_interval_parser->parse($interval['schedule']) != CParser::PARSE_SUCCESS) {
369					$result = false;
370					info(_s('Invalid interval "%1$s".', $interval['schedule']));
371					break;
372				}
373
374				$intervals[] = $interval['schedule'];
375			}
376		}
377
378		if ($intervals) {
379			$delay .= ';'.implode(';', $intervals);
380		}
381	}
382
383	if ($result) {
384		$preprocessing = getRequest('preprocessing', []);
385
386		foreach ($preprocessing as &$step) {
387			switch ($step['type']) {
388				case ZBX_PREPROC_MULTIPLIER:
389				case ZBX_PREPROC_PROMETHEUS_TO_JSON:
390					$step['params'] = trim($step['params'][0]);
391					break;
392
393				case ZBX_PREPROC_RTRIM:
394				case ZBX_PREPROC_LTRIM:
395				case ZBX_PREPROC_TRIM:
396				case ZBX_PREPROC_XPATH:
397				case ZBX_PREPROC_JSONPATH:
398				case ZBX_PREPROC_VALIDATE_REGEX:
399				case ZBX_PREPROC_VALIDATE_NOT_REGEX:
400				case ZBX_PREPROC_ERROR_FIELD_JSON:
401				case ZBX_PREPROC_ERROR_FIELD_XML:
402				case ZBX_PREPROC_THROTTLE_TIMED_VALUE:
403				case ZBX_PREPROC_SCRIPT:
404					$step['params'] = $step['params'][0];
405					break;
406
407				case ZBX_PREPROC_VALIDATE_RANGE:
408				case ZBX_PREPROC_PROMETHEUS_PATTERN:
409					foreach ($step['params'] as &$param) {
410						$param = trim($param);
411					}
412					unset($param);
413
414					$step['params'] = implode("\n", $step['params']);
415					break;
416
417				case ZBX_PREPROC_REGSUB:
418				case ZBX_PREPROC_ERROR_FIELD_REGEX:
419				case ZBX_PREPROC_STR_REPLACE:
420					$step['params'] = implode("\n", $step['params']);
421					break;
422
423				// ZBX-16642
424				case ZBX_PREPROC_CSV_TO_JSON:
425					if (!array_key_exists(2, $step['params'])) {
426						$step['params'][2] = ZBX_PREPROC_CSV_NO_HEADER;
427					}
428					$step['params'] = implode("\n", $step['params']);
429					break;
430
431				default:
432					$step['params'] = '';
433			}
434
435			$step += [
436				'error_handler' => ZBX_PREPROC_FAIL_DEFAULT,
437				'error_handler_params' => ''
438			];
439		}
440		unset($step);
441
442		$item = [
443			'name'			=> getRequest('name'),
444			'description'	=> getRequest('description'),
445			'key_'			=> getRequest('key'),
446			'hostid'		=> $discoveryRule['hostid'],
447			'interfaceid'	=> getRequest('interfaceid'),
448			'delay'			=> $delay,
449			'status'		=> getRequest('status', ITEM_STATUS_DISABLED),
450			'discover'		=> getRequest('discover', ZBX_PROTOTYPE_DISCOVER),
451			'type'			=> getRequest('type'),
452			'snmp_oid'		=> getRequest('snmp_oid'),
453			'value_type'	=> getRequest('value_type'),
454			'trapper_hosts'	=> getRequest('trapper_hosts'),
455			'history'		=> (getRequest('history_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
456				? ITEM_NO_STORAGE_VALUE
457				: getRequest('history'),
458			'units'			=> getRequest('units'),
459			'logtimefmt'	=> getRequest('logtimefmt'),
460			'valuemapid'	=> getRequest('valuemapid', 0),
461			'authtype'		=> getRequest('authtype'),
462			'username'		=> getRequest('username'),
463			'password'		=> getRequest('password'),
464			'publickey'		=> getRequest('publickey'),
465			'privatekey'	=> getRequest('privatekey'),
466			'params'		=> getRequest('params'),
467			'ipmi_sensor'	=> getRequest('ipmi_sensor'),
468			'ruleid'		=> getRequest('parent_discoveryid')
469		];
470
471		if ($item['type'] == ITEM_TYPE_SCRIPT) {
472			$script_item = [
473				'parameters' => getRequest('parameters', []),
474				'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout'))
475			];
476
477			$item = prepareScriptItemFormData($script_item) + $item;
478		}
479
480		if ($item['type'] == ITEM_TYPE_JMX) {
481			$item['jmx_endpoint'] = getRequest('jmx_endpoint', '');
482		}
483
484		if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) {
485			$item['trends'] = (getRequest('trends_mode', ITEM_STORAGE_CUSTOM) == ITEM_STORAGE_OFF)
486				? ITEM_NO_STORAGE_VALUE
487				: getRequest('trends');
488		}
489
490		if ($item['type'] == ITEM_TYPE_DEPENDENT) {
491			$item['master_itemid'] = getRequest('master_itemid');
492		}
493
494		if (hasRequest('update')) {
495			$itemId = getRequest('itemid');
496
497			$db_item = API::ItemPrototype()->get([
498				'output' => ['type', 'snmp_oid', 'hostid', 'name', 'key_', 'delay', 'history',
499					'trends', 'status', 'value_type', 'trapper_hosts', 'units',
500					'logtimefmt', 'templateid', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username',
501					'password', 'publickey', 'privatekey', 'interfaceid', 'description', 'jmx_endpoint',
502					'master_itemid', 'timeout', 'url', 'query_fields', 'posts', 'status_codes', 'follow_redirects',
503					'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method', 'output_format',
504					'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', 'allow_traps',
505					'discover', 'parameters'
506				],
507				'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
508				'selectTags' => ['tag', 'value'],
509				'itemids' => [$itemId]
510			]);
511
512			$db_item = $db_item[0];
513
514			if ($item['type'] == ITEM_TYPE_HTTPAGENT) {
515				$http_item = [
516					'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
517					'url' => getRequest('url'),
518					'query_fields' => getRequest('query_fields', []),
519					'posts' => getRequest('posts'),
520					'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
521					'follow_redirects' => (int) getRequest('follow_redirects'),
522					'post_type' => (int) getRequest('post_type'),
523					'http_proxy' => getRequest('http_proxy'),
524					'headers' => getRequest('headers', []),
525					'retrieve_mode' => (int) getRequest('retrieve_mode'),
526					'request_method' => (int) getRequest('request_method'),
527					'output_format' => (int) getRequest('output_format'),
528					'allow_traps' => (int) getRequest('allow_traps', HTTPCHECK_ALLOW_TRAPS_OFF),
529					'ssl_cert_file' => getRequest('ssl_cert_file'),
530					'ssl_key_file' => getRequest('ssl_key_file'),
531					'ssl_key_password' => getRequest('ssl_key_password'),
532					'verify_peer' => (int) getRequest('verify_peer'),
533					'verify_host' => (int) getRequest('verify_host'),
534					'authtype' => getRequest('http_authtype', HTTPTEST_AUTH_NONE),
535					'username' => getRequest('http_username', ''),
536					'password' => getRequest('http_password', '')
537				];
538				$item = prepareItemHttpAgentFormData($http_item) + $item;
539			}
540
541			if ($db_item['type'] == $item['type']) {
542				$item = CArrayHelper::unsetEqualValues($item, $db_item);
543			}
544
545			$item['itemid'] = $itemId;
546
547			if ($db_item['preprocessing'] !== $preprocessing) {
548				$item['preprocessing'] = $preprocessing;
549			}
550
551			$compare = function($arr, $arr2) {
552				return (array_combine(array_column($arr, 'name'), array_column($arr, 'value')) ==
553					array_combine(array_column($arr2, 'name'), array_column($arr2, 'value'))
554				);
555			};
556			if (getRequest('type') == ITEM_TYPE_SCRIPT && $db_item['type'] == getRequest('type')
557					&& $compare($db_item['parameters'], $item['parameters'])) {
558				unset($item['parameters']);
559			}
560
561			CArrayHelper::sort($db_item['tags'], ['tag', 'value']);
562			CArrayHelper::sort($tags, ['tag', 'value']);
563			if (array_values($db_item['tags']) !== array_values($tags)) {
564				$item['tags'] = $tags;
565			}
566
567			$result = API::ItemPrototype()->update($item);
568		}
569		else {
570			if (getRequest('type') == ITEM_TYPE_HTTPAGENT) {
571				$http_item = [
572					'timeout' => getRequest('timeout', DB::getDefault('items', 'timeout')),
573					'url' => getRequest('url'),
574					'query_fields' => getRequest('query_fields', []),
575					'posts' => getRequest('posts'),
576					'status_codes' => getRequest('status_codes', DB::getDefault('items', 'status_codes')),
577					'follow_redirects' => (int) getRequest('follow_redirects'),
578					'post_type' => (int) getRequest('post_type'),
579					'http_proxy' => getRequest('http_proxy'),
580					'headers' => getRequest('headers', []),
581					'retrieve_mode' => (int) getRequest('retrieve_mode'),
582					'request_method' => (int) getRequest('request_method'),
583					'output_format' => (int) getRequest('output_format'),
584					'allow_traps' => (int) getRequest('allow_traps', HTTPCHECK_ALLOW_TRAPS_OFF),
585					'ssl_cert_file' => getRequest('ssl_cert_file'),
586					'ssl_key_file' => getRequest('ssl_key_file'),
587					'ssl_key_password' => getRequest('ssl_key_password'),
588					'verify_peer' => (int) getRequest('verify_peer'),
589					'verify_host' => (int) getRequest('verify_host'),
590					'authtype' => getRequest('http_authtype', HTTPTEST_AUTH_NONE),
591					'username' => getRequest('http_username', ''),
592					'password' => getRequest('http_password', '')
593				];
594				$item = prepareItemHttpAgentFormData($http_item) + $item;
595			}
596
597			if ($preprocessing) {
598				$item['preprocessing'] = $preprocessing;
599			}
600
601			$item['tags'] = $tags;
602
603			$result = API::ItemPrototype()->create($item);
604		}
605	}
606
607	$result = DBend($result);
608
609	if (hasRequest('add')) {
610		show_messages($result, _('Item prototype added'), _('Cannot add item prototype'));
611	}
612	else {
613		show_messages($result, _('Item prototype updated'), _('Cannot update item prototype'));
614	}
615
616	if ($result) {
617		unset($_REQUEST['itemid'], $_REQUEST['form']);
618		uncheckTableRows(getRequest('parent_discoveryid'));
619	}
620}
621elseif (hasRequest('action') && hasRequest('group_itemid')
622		&& str_in_array(getRequest('action'), ['itemprototype.massenable', 'itemprototype.massdisable'])) {
623	$itemids = getRequest('group_itemid');
624	$status = (getRequest('action') == 'itemprototype.massenable') ? ITEM_STATUS_ACTIVE : ITEM_STATUS_DISABLED;
625
626	$item_prototypes = [];
627	foreach ($itemids as $itemid) {
628		$item_prototypes[] = ['itemid' => $itemid, 'status' => $status];
629	}
630
631	$result = (bool) API::ItemPrototype()->update($item_prototypes);
632
633	if ($result) {
634		uncheckTableRows(getRequest('parent_discoveryid'));
635	}
636
637	$updated = count($itemids);
638
639	$messageSuccess = _n('Item prototype updated', 'Item prototypes updated', $updated);
640	$messageFailed = _n('Cannot update item prototype', 'Cannot update item prototypes', $updated);
641
642	show_messages($result, $messageSuccess, $messageFailed);
643}
644elseif (hasRequest('action') && getRequest('action') === 'itemprototype.massdelete' && hasRequest('group_itemid')) {
645	DBstart();
646
647	$result = API::ItemPrototype()->delete(getRequest('group_itemid'));
648	$result = DBend($result);
649
650	if ($result) {
651		uncheckTableRows(getRequest('parent_discoveryid'));
652	}
653	show_messages($result, _('Item prototypes deleted'), _('Cannot delete item prototypes'));
654}
655elseif (hasRequest('action') && hasRequest('group_itemid')
656		&& in_array(getRequest('action'), ['itemprototype.massdiscover.enable', 'itemprototype.massdiscover.disable'])) {
657	$itemids = getRequest('group_itemid');
658	$discover = (getRequest('action') == 'itemprototype.massdiscover.enable') ? ITEM_DISCOVER : ITEM_NO_DISCOVER;
659
660	$item_prototypes = [];
661	foreach ($itemids as $itemid) {
662		$item_prototypes[] = ['itemid' => $itemid, 'discover' => $discover];
663	}
664
665	$result = (bool) API::ItemPrototype()->update($item_prototypes);
666
667	if ($result) {
668		uncheckTableRows(getRequest('parent_discoveryid'));
669	}
670
671	$updated = count($itemids);
672
673	$messageSuccess = _n('Item prototype updated', 'Item prototypes updated', $updated);
674	$messageFailed = _n('Cannot update item prototype', 'Cannot update item prototypes', $updated);
675
676	show_messages($result, $messageSuccess, $messageFailed);
677}
678
679/*
680 * Display
681 */
682if (hasRequest('form') || (hasRequest('clone') && getRequest('itemid') != 0)) {
683	$itemPrototype = [];
684	$has_errors = false;
685
686	if (hasRequest('itemid') && !hasRequest('clone')) {
687		$itemPrototype = API::ItemPrototype()->get([
688			'itemids' => getRequest('itemid'),
689			'output' => [
690				'itemid', 'type', 'snmp_oid', 'hostid', 'name', 'key_', 'delay', 'history', 'trends', 'status',
691				'value_type', 'trapper_hosts', 'units', 'logtimefmt', 'templateid', 'valuemapid', 'params',
692				'ipmi_sensor', 'authtype', 'username', 'password', 'publickey', 'privatekey', 'interfaceid',
693				'description', 'jmx_endpoint', 'master_itemid', 'timeout', 'url', 'query_fields', 'parameters', 'posts',
694				'status_codes', 'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode',
695				'request_method', 'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer',
696				'verify_host', 'allow_traps', 'discover'
697			],
698			'selectDiscoveryRule' => ['itemid', 'templateid'],
699			'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
700			'selectTags' => ['tag', 'value']
701		]);
702		$itemPrototype = reset($itemPrototype);
703
704		foreach ($itemPrototype['preprocessing'] as &$step) {
705			if ($step['type'] == ZBX_PREPROC_SCRIPT) {
706				$step['params'] = [$step['params'], ''];
707			}
708			else {
709				$step['params'] = explode("\n", $step['params']);
710			}
711		}
712		unset($step);
713
714		if ($itemPrototype['type'] != ITEM_TYPE_JMX) {
715			$itemPrototype['jmx_endpoint'] = ZBX_DEFAULT_JMX_ENDPOINT;
716		}
717
718		if (getRequest('type', $itemPrototype['type']) == ITEM_TYPE_DEPENDENT) {
719			$master_prototypes = API::Item()->get([
720				'output' => ['itemid', 'hostid', 'name', 'key_'],
721				'itemids' => [getRequest('master_itemid', $itemPrototype['master_itemid'])],
722				'hostids' => [$itemPrototype['hostid']],
723				'webitems' => true
724			])
725			+ API::ItemPrototype()->get([
726				'output' => ['itemid', 'hostid', 'name', 'key_'],
727				'itemids' => getRequest('master_itemid', $itemPrototype['master_itemid'])
728			]);
729
730			if ($master_prototypes) {
731				$itemPrototype['master_item'] = reset($master_prototypes);
732			}
733		}
734	}
735	elseif (getRequest('master_itemid')) {
736		$master_prototypes = API::Item()->get([
737			'output' => ['itemid', 'hostid', 'name', 'key_'],
738			'itemids' => getRequest('master_itemid'),
739			'webitems' => true
740		])
741		+ API::ItemPrototype()->get([
742			'output' => ['itemid', 'hostid', 'name', 'key_'],
743			'itemids' => getRequest('master_itemid')
744		]);
745
746		if ($master_prototypes) {
747			$itemPrototype['master_item'] = reset($master_prototypes);
748		}
749		else {
750			show_messages(false, '', _('No permissions to referred object or it does not exist!'));
751			$has_errors = true;
752		}
753	}
754
755	$form_action = (hasRequest('clone') && getRequest('itemid') != 0) ? 'clone' : getRequest('form');
756	$data = getItemFormData($itemPrototype, ['form' => $form_action]);
757	$data['preprocessing_test_type'] = CControllerPopupItemTestEdit::ZBX_TEST_TYPE_ITEM_PROTOTYPE;
758	$data['preprocessing_types'] = CItemPrototype::SUPPORTED_PREPROCESSING_TYPES;
759	$data['trends_default'] = DB::getDefault('items', 'trends');
760
761	$data['display_interfaces'] = $data['hostid']
762		? (bool) API::Host()->get([
763			'countOutput' => true,
764			'hostids' => $data['hostid'],
765			'filter' => [
766				'status' => [HOST_STATUS_MONITORED, HOST_STATUS_NOT_MONITORED]
767			]
768		])
769		: false;
770
771	$history_in_seconds = timeUnitToSeconds($data['history']);
772	if (!getRequest('form_refresh') && $history_in_seconds !== null && $history_in_seconds == ITEM_NO_STORAGE_VALUE) {
773		$data['history_mode'] = getRequest('history_mode', ITEM_STORAGE_OFF);
774		$data['history'] = DB::getDefault('items', 'history');
775	}
776	else {
777		$data['history_mode'] = getRequest('history_mode', ITEM_STORAGE_CUSTOM);
778	}
779
780	$trends_in_seconds = timeUnitToSeconds($data['trends']);
781	if (!getRequest('form_refresh') && $trends_in_seconds !== null && $trends_in_seconds == ITEM_NO_STORAGE_VALUE) {
782		$data['trends_mode'] = getRequest('trends_mode', ITEM_STORAGE_OFF);
783		$data['trends'] = $data['trends_default'];
784	}
785	else {
786		$data['trends_mode'] = getRequest('trends_mode', ITEM_STORAGE_CUSTOM);
787	}
788
789	// render view
790	if (!$has_errors) {
791		echo (new CView('configuration.item.prototype.edit', $data))->getOutput();
792	}
793}
794else {
795	$prefix = (getRequest('context') === 'host') ? 'web.hosts.' : 'web.templates.';
796
797	$sortField = getRequest('sort', CProfile::get($prefix.$page['file'].'.sort', 'name'));
798	$sortOrder = getRequest('sortorder', CProfile::get($prefix.$page['file'].'.sortorder', ZBX_SORT_UP));
799
800	CProfile::update($prefix.$page['file'].'.sort', $sortField, PROFILE_TYPE_STR);
801	CProfile::update($prefix.$page['file'].'.sortorder', $sortOrder, PROFILE_TYPE_STR);
802
803	$data = [
804		'form' => getRequest('form'),
805		'parent_discoveryid' => getRequest('parent_discoveryid'),
806		'hostid' => $discoveryRule['hostid'],
807		'sort' => $sortField,
808		'sortorder' => $sortOrder,
809		'context' => getRequest('context')
810	];
811
812	$limit = CSettingsHelper::get(CSettingsHelper::SEARCH_LIMIT) + 1;
813	$data['items'] = API::ItemPrototype()->get([
814		'discoveryids' => $data['parent_discoveryid'],
815		'output' => API_OUTPUT_EXTEND,
816		'editable' => true,
817		'selectTags' => ['tag', 'value'],
818		'sortfield' => $sortField,
819		'limit' => $limit
820	]);
821
822	$data['items'] = expandItemNamesWithMasterItems($data['items'], 'itemprototypes');
823
824	switch ($sortField) {
825		case 'delay':
826			orderItemsByDelay($data['items'], $sortOrder, ['usermacros' => true, 'lldmacros' => true]);
827			break;
828
829		case 'history':
830			orderItemsByHistory($data['items'], $sortOrder);
831			break;
832
833		case 'trends':
834			orderItemsByTrends($data['items'], $sortOrder);
835			break;
836
837		default:
838			order_result($data['items'], $sortField, $sortOrder);
839	}
840
841	// pager
842	if (hasRequest('page')) {
843		$page_num = getRequest('page');
844	}
845	elseif (isRequestMethod('get') && !hasRequest('cancel')) {
846		$page_num = 1;
847	}
848	else {
849		$page_num = CPagerHelper::loadPage($page['file']);
850	}
851
852	CPagerHelper::savePage($page['file'], $page_num);
853
854	$data['paging'] = CPagerHelper::paginate($page_num, $data['items'], $sortOrder,
855		(new CUrl('disc_prototypes.php'))
856			->setArgument('parent_discoveryid', $data['parent_discoveryid'])
857			->setArgument('context', $data['context'])
858	);
859
860	$data['parent_templates'] = getItemParentTemplates($data['items'], ZBX_FLAG_DISCOVERY_PROTOTYPE);
861	$data['allowed_ui_conf_templates'] = CWebUser::checkAccess(CRoleHelper::UI_CONFIGURATION_TEMPLATES);
862
863	$data['tags'] = makeTags($data['items'], true, 'itemid', ZBX_TAG_COUNT_DEFAULT);
864
865	// render view
866	echo (new CView('configuration.item.prototype.list', $data))->getOutput();
867}
868
869require_once dirname(__FILE__).'/include/page_footer.php';
870