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
22/**
23 * Convert windows events type constant in to the string representation
24 *
25 * @param int $logtype
26 * @return string
27 */
28function get_item_logtype_description($logtype) {
29	switch ($logtype) {
30		case ITEM_LOGTYPE_INFORMATION:
31			return _('Information');
32		case ITEM_LOGTYPE_WARNING:
33			return _('Warning');
34		case ITEM_LOGTYPE_ERROR:
35			return _('Error');
36		case ITEM_LOGTYPE_FAILURE_AUDIT:
37			return _('Failure Audit');
38		case ITEM_LOGTYPE_SUCCESS_AUDIT:
39			return _('Success Audit');
40		case ITEM_LOGTYPE_CRITICAL:
41			return _('Critical');
42		case ITEM_LOGTYPE_VERBOSE:
43			return _('Verbose');
44		default:
45			return _('Unknown');
46	}
47}
48
49/**
50 * Convert windows events type constant in to the CSS style name
51 *
52 * @param int $logtype
53 * @return string
54 */
55function get_item_logtype_style($logtype) {
56	switch ($logtype) {
57		case ITEM_LOGTYPE_INFORMATION:
58		case ITEM_LOGTYPE_SUCCESS_AUDIT:
59		case ITEM_LOGTYPE_VERBOSE:
60			return ZBX_STYLE_LOG_INFO_BG;
61
62		case ITEM_LOGTYPE_WARNING:
63			return ZBX_STYLE_LOG_WARNING_BG;
64
65		case ITEM_LOGTYPE_ERROR:
66		case ITEM_LOGTYPE_FAILURE_AUDIT:
67			return ZBX_STYLE_LOG_HIGH_BG;
68
69		case ITEM_LOGTYPE_CRITICAL:
70			return ZBX_STYLE_LOG_DISASTER_BG;
71
72		default:
73			return ZBX_STYLE_LOG_NA_BG;
74	}
75}
76
77/**
78 * Get item type string name by item type number, or array of all item types if null passed.
79 *
80 * @param int|null $type
81 *
82 * @return array|string
83 */
84function item_type2str($type = null) {
85	$types = [
86		ITEM_TYPE_ZABBIX => _('Zabbix agent'),
87		ITEM_TYPE_ZABBIX_ACTIVE => _('Zabbix agent (active)'),
88		ITEM_TYPE_SIMPLE => _('Simple check'),
89		ITEM_TYPE_SNMP => _('SNMP agent'),
90		ITEM_TYPE_SNMPTRAP => _('SNMP trap'),
91		ITEM_TYPE_INTERNAL => _('Zabbix internal'),
92		ITEM_TYPE_TRAPPER => _('Zabbix trapper'),
93		ITEM_TYPE_AGGREGATE => _('Zabbix aggregate'),
94		ITEM_TYPE_EXTERNAL => _('External check'),
95		ITEM_TYPE_DB_MONITOR => _('Database monitor'),
96		ITEM_TYPE_HTTPAGENT => _('HTTP agent'),
97		ITEM_TYPE_IPMI => _('IPMI agent'),
98		ITEM_TYPE_SSH => _('SSH agent'),
99		ITEM_TYPE_TELNET => _('TELNET agent'),
100		ITEM_TYPE_JMX => _('JMX agent'),
101		ITEM_TYPE_CALCULATED => _('Calculated'),
102		ITEM_TYPE_HTTPTEST => _('Web monitoring'),
103		ITEM_TYPE_DEPENDENT => _('Dependent item')
104	];
105
106	if ($type === null) {
107		return $types;
108	}
109
110	return array_key_exists($type, $types) ? $types[$type] : _('Unknown');
111}
112
113/**
114 * Returns human readable an item value type
115 *
116 * @param int $valueType
117 *
118 * @return string
119 */
120function itemValueTypeString($valueType) {
121	switch ($valueType) {
122		case ITEM_VALUE_TYPE_UINT64:
123			return _('Numeric (unsigned)');
124		case ITEM_VALUE_TYPE_FLOAT:
125			return _('Numeric (float)');
126		case ITEM_VALUE_TYPE_STR:
127			return _('Character');
128		case ITEM_VALUE_TYPE_LOG:
129			return _('Log');
130		case ITEM_VALUE_TYPE_TEXT:
131			return _('Text');
132	}
133	return _('Unknown');
134}
135
136function item_status2str($type = null) {
137	if (is_null($type)) {
138		return [ITEM_STATUS_ACTIVE => _('Enabled'), ITEM_STATUS_DISABLED => _('Disabled')];
139	}
140
141	return ($type == ITEM_STATUS_ACTIVE) ? _('Enabled') : _('Disabled');
142}
143
144/**
145 * Returns the names of supported item states.
146 *
147 * If the $state parameter is passed, returns the name of the specific state, otherwise - returns an array of all
148 * supported states.
149 *
150 * @param string $state
151 *
152 * @return array|string
153 */
154function itemState($state = null) {
155	$states = [
156		ITEM_STATE_NORMAL => _('Normal'),
157		ITEM_STATE_NOTSUPPORTED => _('Not supported')
158	];
159
160	if ($state === null) {
161		return $states;
162	}
163	elseif (isset($states[$state])) {
164		return $states[$state];
165	}
166	else {
167		return _('Unknown');
168	}
169}
170
171/**
172 * Returns the text indicating the items status and state. If the $state parameter is not given, only the status of
173 * the item will be taken into account.
174 *
175 * @param int $status
176 * @param int $state
177 *
178 * @return string
179 */
180function itemIndicator($status, $state = null) {
181	if ($status == ITEM_STATUS_ACTIVE) {
182		return ($state == ITEM_STATE_NOTSUPPORTED) ? _('Not supported') : _('Enabled');
183	}
184
185	return _('Disabled');
186}
187
188/**
189 * Returns the CSS class for the items status and state indicator. If the $state parameter is not given, only the status of
190 * the item will be taken into account.
191 *
192 * @param int $status
193 * @param int $state
194 *
195 * @return string
196 */
197function itemIndicatorStyle($status, $state = null) {
198	if ($status == ITEM_STATUS_ACTIVE) {
199		return ($state == ITEM_STATE_NOTSUPPORTED) ?
200			ZBX_STYLE_GREY :
201			ZBX_STYLE_GREEN;
202	}
203
204	return ZBX_STYLE_RED;
205}
206
207/**
208 * Order items by keep history.
209 *
210 * @param array  $items
211 * @param string $items['history']
212 * @param string $sortorder
213 */
214function orderItemsByHistory(array &$items, $sortorder){
215	$simple_interval_parser = new CSimpleIntervalParser();
216
217	foreach ($items as &$item) {
218		$item['history_sort'] = ($simple_interval_parser->parse($item['history']) == CParser::PARSE_SUCCESS)
219			? timeUnitToSeconds($item['history'])
220			: $item['history'];
221	}
222	unset($item);
223
224	order_result($items, 'history_sort', $sortorder);
225
226	foreach ($items as &$item) {
227		unset($item['history_sort']);
228	}
229	unset($item);
230}
231
232/**
233 * Order items by keep trends.
234 *
235 * @param array  $items
236 * @param int    $items['value_type']
237 * @param string $items['trends']
238 * @param string $sortorder
239 */
240function orderItemsByTrends(array &$items, $sortorder){
241	$simple_interval_parser = new CSimpleIntervalParser();
242
243	foreach ($items as &$item) {
244		if (in_array($item['value_type'], [ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_LOG, ITEM_VALUE_TYPE_TEXT])) {
245			$item['trends_sort'] = '';
246		}
247		else {
248			$item['trends_sort'] = ($simple_interval_parser->parse($item['trends']) == CParser::PARSE_SUCCESS)
249				? timeUnitToSeconds($item['trends'])
250				: $item['trends'];
251		}
252	}
253	unset($item);
254
255	order_result($items, 'trends_sort', $sortorder);
256
257	foreach ($items as &$item) {
258		unset($item['trends_sort']);
259	}
260	unset($item);
261}
262
263/**
264 * Order items by update interval.
265 *
266 * @param array  $items
267 * @param int    $items['type']
268 * @param string $items['delay']
269 * @param string $sortorder
270 * @param array  $options
271 * @param bool   $options['usermacros']
272 * @param bool   $options['lldmacros']
273 */
274function orderItemsByDelay(array &$items, $sortorder, array $options){
275	$update_interval_parser = new CUpdateIntervalParser($options);
276
277	foreach ($items as &$item) {
278		if (in_array($item['type'], [ITEM_TYPE_TRAPPER, ITEM_TYPE_SNMPTRAP, ITEM_TYPE_DEPENDENT])) {
279			$item['delay_sort'] = '';
280		}
281		elseif ($update_interval_parser->parse($item['delay']) == CParser::PARSE_SUCCESS) {
282			$item['delay_sort'] = $update_interval_parser->getDelay();
283
284			if ($item['delay_sort'][0] !== '{') {
285				$item['delay_sort'] = timeUnitToSeconds($item['delay_sort']);
286			}
287		}
288		else {
289			$item['delay_sort'] = $item['delay'];
290		}
291	}
292	unset($item);
293
294	order_result($items, 'delay_sort', $sortorder);
295
296	foreach ($items as &$item) {
297		unset($item['delay_sort']);
298	}
299	unset($item);
300}
301
302/**
303 * Orders items by both status and state. Items are sorted in the following order: enabled, disabled, not supported.
304 *
305 * Keep in sync with orderTriggersByStatus().
306 *
307 * @param array  $items
308 * @param string $sortorder
309 */
310function orderItemsByStatus(array &$items, $sortorder = ZBX_SORT_UP) {
311	$sort = [];
312
313	foreach ($items as $key => $item) {
314		if ($item['status'] == ITEM_STATUS_ACTIVE) {
315			$sort[$key] = ($item['state'] == ITEM_STATE_NOTSUPPORTED) ? 2 : 0;
316		}
317		else {
318			$sort[$key] = 1;
319		}
320	}
321
322	if ($sortorder == ZBX_SORT_UP) {
323		asort($sort);
324	}
325	else {
326		arsort($sort);
327	}
328
329	$sortedItems = [];
330	foreach ($sort as $key => $val) {
331		$sortedItems[$key] = $items[$key];
332	}
333	$items = $sortedItems;
334}
335
336/**
337 * Returns the name of the given interface type. Items "status" and "state" properties must be defined.
338 *
339 * @param int $type
340 *
341 * @return null
342 */
343function interfaceType2str($type) {
344	$interfaceGroupLabels = [
345		INTERFACE_TYPE_AGENT => _('Agent'),
346		INTERFACE_TYPE_SNMP => _('SNMP'),
347		INTERFACE_TYPE_JMX => _('JMX'),
348		INTERFACE_TYPE_IPMI => _('IPMI')
349	];
350
351	return isset($interfaceGroupLabels[$type]) ? $interfaceGroupLabels[$type] : null;
352}
353
354function itemTypeInterface($type = null) {
355	$types = [
356		ITEM_TYPE_SNMP =>  INTERFACE_TYPE_SNMP,
357		ITEM_TYPE_SNMPTRAP => INTERFACE_TYPE_SNMP,
358		ITEM_TYPE_IPMI => INTERFACE_TYPE_IPMI,
359		ITEM_TYPE_ZABBIX => INTERFACE_TYPE_AGENT,
360		ITEM_TYPE_SIMPLE => INTERFACE_TYPE_ANY,
361		ITEM_TYPE_EXTERNAL => INTERFACE_TYPE_ANY,
362		ITEM_TYPE_SSH => INTERFACE_TYPE_ANY,
363		ITEM_TYPE_TELNET => INTERFACE_TYPE_ANY,
364		ITEM_TYPE_JMX => INTERFACE_TYPE_JMX,
365		ITEM_TYPE_HTTPAGENT => INTERFACE_TYPE_ANY
366	];
367	if (is_null($type)) {
368		return $types;
369	}
370	elseif (isset($types[$type])) {
371		return $types[$type];
372	}
373	else {
374		return false;
375	}
376}
377
378/**
379 * Copies the given items to the given hosts or templates.
380 *
381 * @param array $src_itemids  Items which will be copied to $dst_hostids.
382 * @param array $dst_hostids  Hosts and templates to whom add items.
383 *
384 * @return bool
385 */
386function copyItemsToHosts($src_itemids, $dst_hostids) {
387	$items = API::Item()->get([
388		'output' => ['type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 'value_type',
389			'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username',
390			'password', 'publickey', 'privatekey', 'flags', 'description', 'inventory_link', 'jmx_endpoint',
391			'master_itemid', 'timeout', 'url', 'query_fields', 'posts', 'status_codes', 'follow_redirects',
392			'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method', 'output_format', 'ssl_cert_file',
393			'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host', 'allow_traps'
394		],
395		'selectApplications' => ['applicationid'],
396		'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
397		'itemids' => $src_itemids,
398		'preservekeys' => true
399	]);
400
401	// Check if dependent items have master items in same selection. If not, those could be web items.
402	$master_itemids = [];
403
404	foreach ($items as $itemid => $item) {
405		if ($item['type'] == ITEM_TYPE_DEPENDENT && !array_key_exists($item['master_itemid'], $items)) {
406			$master_itemids[$item['master_itemid']] = true;
407		}
408	}
409
410	// Find same master items (that includes web items) on destination host.
411	$dst_master_items = [];
412
413	foreach (array_keys($master_itemids) as $master_itemid) {
414		$same_master_item = get_same_item_for_host(['itemid' => $master_itemid], $dst_hostids);
415
416		if ($same_master_item) {
417			$dst_master_items[$master_itemid] = $same_master_item;
418		}
419	}
420
421	$create_order = [];
422	$src_itemid_to_key = [];
423
424	// Calculate dependency level between items so that master items are created before dependent items.
425	foreach ($items as $itemid => $item) {
426		$dependency_level = 0;
427		$master_item = $item;
428		$src_itemid_to_key[$itemid] = $item['key_'];
429
430		while ($master_item['type'] == ITEM_TYPE_DEPENDENT) {
431			if (!array_key_exists($master_item['master_itemid'], $items)) {
432				break;
433			}
434
435			$master_item = $items[$master_item['master_itemid']];
436			++$dependency_level;
437		}
438
439		$create_order[$itemid] = $dependency_level;
440	}
441
442	asort($create_order);
443
444	$dstHosts = API::Host()->get([
445		'output' => ['hostid', 'host', 'status'],
446		'selectInterfaces' => ['interfaceid', 'type', 'main'],
447		'hostids' => $dst_hostids,
448		'preservekeys' => true,
449		'nopermissions' => true,
450		'templated_hosts' => true
451	]);
452
453	foreach ($dstHosts as $dstHost) {
454		$interfaceids = [];
455
456		foreach ($dstHost['interfaces'] as $interface) {
457			if ($interface['main'] == 1) {
458				$interfaceids[$interface['type']] = $interface['interfaceid'];
459			}
460		}
461
462		$itemkey_to_id = [];
463		$create_items = [];
464		$current_dependency = reset($create_order);
465
466		foreach ($create_order as $itemid => $dependency_level) {
467			if ($current_dependency != $dependency_level) {
468				$current_dependency = $dependency_level;
469				$created_itemids = API::Item()->create($create_items);
470
471				if (!$created_itemids) {
472					return false;
473				}
474				$created_itemids = $created_itemids['itemids'];
475
476				foreach ($create_items as $index => $created_item) {
477					$itemkey_to_id[$created_item['key_']] = $created_itemids[$index];
478				}
479
480				$create_items = [];
481			}
482
483			$item = $items[$itemid];
484
485			if ($dstHost['status'] != HOST_STATUS_TEMPLATE) {
486				$type = itemTypeInterface($item['type']);
487
488				if ($type == INTERFACE_TYPE_ANY) {
489					foreach ([INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI] as $itype) {
490						if (isset($interfaceids[$itype])) {
491							$item['interfaceid'] = $interfaceids[$itype];
492							break;
493						}
494					}
495				}
496				elseif ($type !== false) {
497					if (!isset($interfaceids[$type])) {
498						error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'],
499							$item['key_']
500						));
501						return false;
502					}
503					$item['interfaceid'] = $interfaceids[$type];
504				}
505			}
506			unset($item['itemid']);
507			$item['hostid'] = $dstHost['hostid'];
508			$item['applications'] = get_same_applications_for_host(
509				zbx_objectValues($item['applications'], 'applicationid'),
510				$dstHost['hostid']
511			);
512
513			if ($item['type'] == ITEM_TYPE_DEPENDENT) {
514				if (array_key_exists($item['master_itemid'], $items)) {
515					$src_item_key = $src_itemid_to_key[$item['master_itemid']];
516					$item['master_itemid'] = $itemkey_to_id[$src_item_key];
517				}
518				else {
519					$item_found = false;
520
521					if (array_key_exists($item['master_itemid'], $dst_master_items)) {
522						foreach ($dst_master_items[$item['master_itemid']] as $dst_master_item) {
523							if ($dst_master_item['hostid'] == $dstHost['hostid']) {
524								// A matching item on destination host has been found.
525
526								$item['master_itemid'] = $dst_master_item['itemid'];
527								$item_found = true;
528							}
529						}
530					}
531
532					// Master item does not exist on destination host or has not been selected for copying.
533					if (!$item_found) {
534						error(_s('Item "%1$s" cannot be copied without its master item.', $item['name']));
535
536						return false;
537					}
538				}
539			}
540			else {
541				unset($item['master_itemid']);
542			}
543
544			$create_items[] = $item;
545		}
546
547		if ($create_items && !API::Item()->create($create_items)) {
548			return false;
549		}
550	}
551
552	return true;
553}
554
555function copyItems($srcHostId, $dstHostId) {
556	$srcItems = API::Item()->get([
557		'output' => ['type', 'snmp_oid', 'name', 'key_', 'delay', 'history', 'trends', 'status', 'value_type',
558			'trapper_hosts', 'units', 'logtimefmt', 'valuemapid', 'params', 'ipmi_sensor', 'authtype', 'username',
559			'password', 'publickey', 'privatekey', 'flags', 'description', 'inventory_link', 'jmx_endpoint',
560			'master_itemid', 'templateid', 'url', 'query_fields', 'timeout', 'posts', 'status_codes',
561			'follow_redirects', 'post_type', 'http_proxy', 'headers', 'retrieve_mode', 'request_method',
562			'output_format', 'ssl_cert_file', 'ssl_key_file', 'ssl_key_password', 'verify_peer', 'verify_host',
563			'allow_traps'
564		],
565		'selectApplications' => ['applicationid'],
566		'selectPreprocessing' => ['type', 'params', 'error_handler', 'error_handler_params'],
567		'hostids' => $srcHostId,
568		'webitems' => true,
569		'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL],
570		'preservekeys' => true
571	]);
572	$dstHosts = API::Host()->get([
573		'output' => ['hostid', 'host', 'status'],
574		'selectInterfaces' => ['interfaceid', 'type', 'main'],
575		'hostids' => $dstHostId,
576		'preservekeys' => true,
577		'nopermissions' => true,
578		'templated_hosts' => true
579	]);
580	$dstHost = reset($dstHosts);
581
582	$create_order = [];
583	$src_itemid_to_key = [];
584	foreach ($srcItems as $itemid => $item) {
585		$dependency_level = 0;
586		$master_item = $item;
587		$src_itemid_to_key[$itemid] = $item['key_'];
588
589		while ($master_item['type'] == ITEM_TYPE_DEPENDENT) {
590			$master_item = $srcItems[$master_item['master_itemid']];
591			++$dependency_level;
592		}
593
594		$create_order[$itemid] = $dependency_level;
595	}
596	asort($create_order);
597
598	$itemkey_to_id = [];
599	$create_items = [];
600	$current_dependency = reset($create_order);
601
602	foreach ($create_order as $itemid => $dependency_level) {
603		$srcItem = $srcItems[$itemid];
604
605		// Skip creating web items. Those were created before.
606		if ($srcItem['type'] == ITEM_TYPE_HTTPTEST) {
607			continue;
608		}
609
610		if ($current_dependency != $dependency_level && $create_items) {
611			$current_dependency = $dependency_level;
612			$created_itemids = API::Item()->create($create_items);
613
614			if (!$created_itemids) {
615				return false;
616			}
617			$created_itemids = $created_itemids['itemids'];
618
619			foreach ($create_items as $index => $created_item) {
620				$itemkey_to_id[$created_item['key_']] = $created_itemids[$index];
621			}
622
623			$create_items = [];
624		}
625
626		if ($srcItem['templateid']) {
627			$srcItem = get_same_item_for_host($srcItem, $dstHost['hostid']);
628
629			if (!$srcItem) {
630				return false;
631			}
632			$itemkey_to_id[$srcItem['key_']] = $srcItem['itemid'];
633			continue;
634		}
635
636		if ($dstHost['status'] != HOST_STATUS_TEMPLATE) {
637			// find a matching interface
638			$interface = CItem::findInterfaceForItem($srcItem['type'], $dstHost['interfaces']);
639			if ($interface) {
640				$srcItem['interfaceid'] = $interface['interfaceid'];
641			}
642			// no matching interface found, throw an error
643			elseif ($interface !== false) {
644				error(_s('Cannot find host interface on "%1$s" for item key "%2$s".', $dstHost['host'], $srcItem['key_']));
645			}
646		}
647		unset($srcItem['itemid']);
648		unset($srcItem['templateid']);
649		$srcItem['hostid'] = $dstHostId;
650		$srcItem['applications'] = get_same_applications_for_host(zbx_objectValues($srcItem['applications'], 'applicationid'), $dstHostId);
651
652		if (!$srcItem['preprocessing']) {
653			unset($srcItem['preprocessing']);
654		}
655
656		if ($srcItem['type'] == ITEM_TYPE_DEPENDENT) {
657			if ($srcItems[$srcItem['master_itemid']]['type'] == ITEM_TYPE_HTTPTEST) {
658				// Web items are outside the scope and are created before regular items.
659				$web_item = get_same_item_for_host($srcItems[$srcItem['master_itemid']], $dstHost['hostid']);
660				$srcItem['master_itemid'] = $web_item['itemid'];
661			}
662			else {
663				$src_item_key = $src_itemid_to_key[$srcItem['master_itemid']];
664				$srcItem['master_itemid'] = $itemkey_to_id[$src_item_key];
665			}
666		}
667		else {
668			unset($srcItem['master_itemid']);
669		}
670
671		$create_items[] = $srcItem;
672	}
673
674	if ($create_items && !API::Item()->create($create_items)) {
675		return false;
676	}
677
678	return true;
679}
680
681/**
682 * Copy applications to a different host.
683 *
684 * @param string $source_hostid
685 * @param string $destination_hostid
686 *
687 * @return bool
688 */
689function copyApplications($source_hostid, $destination_hostid) {
690	$applications_to_create = API::Application()->get([
691		'output' => ['name'],
692		'hostids' => [$source_hostid],
693		'inherited' => false,
694		'filter' => ['flags' => ZBX_FLAG_DISCOVERY_NORMAL]
695	]);
696
697	if (!$applications_to_create) {
698		return true;
699	}
700
701	foreach ($applications_to_create as &$application) {
702		$application['hostid'] = $destination_hostid;
703		unset($application['applicationid'], $application['templateid']);
704	}
705	unset($application);
706
707	return (bool) API::Application()->create($applications_to_create);
708}
709
710function get_item_by_itemid($itemid) {
711	$db_items = DBfetch(DBselect('SELECT i.* FROM items i WHERE i.itemid='.zbx_dbstr($itemid)));
712	if ($db_items) {
713		return $db_items;
714	}
715	error(_s('No item with itemid="%1$s".', $itemid));
716	return false;
717}
718
719/**
720 * Description:
721 * Replace items for specified host
722 *
723 * Comments:
724 * $error= true : rise Error if item doesn't exist (error generated), false: special processing (NO error generated)
725 */
726function get_same_item_for_host($item, $dest_hostids) {
727	$return_array = is_array($dest_hostids);
728	zbx_value2array($dest_hostids);
729
730	if (!is_array($item)) {
731		$itemid = $item;
732	}
733	elseif (isset($item['itemid'])) {
734		$itemid = $item['itemid'];
735	}
736
737	$same_item = null;
738	$same_items = [];
739
740	if (isset($itemid)) {
741		$db_items = DBselect(
742			'SELECT src.*'.
743			' FROM items src,items dest'.
744			' WHERE dest.itemid='.zbx_dbstr($itemid).
745				' AND src.key_=dest.key_'.
746				' AND '.dbConditionInt('src.hostid', $dest_hostids)
747		);
748		while ($db_item = DBfetch($db_items)) {
749			if (is_array($item)) {
750				$same_item = $db_item;
751				$same_items[$db_item['itemid']] = $db_item;
752			}
753			else {
754				$same_item = $db_item['itemid'];
755				$same_items[$db_item['itemid']] = $db_item['itemid'];
756			}
757		}
758		if ($return_array) {
759			return $same_items;
760		}
761		else {
762			return $same_item;
763		}
764	}
765	return false;
766}
767
768/**
769 * Get parent templates for each given item.
770 *
771 * @param array  $items                  An array of items.
772 * @param string $items[]['itemid']      ID of an item.
773 * @param string $items[]['templateid']  ID of parent template item.
774 * @param int    $flag                   Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE,
775 *                                       ZBX_FLAG_DISCOVERY_PROTOTYPE).
776 *
777 * @return array
778 */
779function getItemParentTemplates(array $items, $flag) {
780	$parent_itemids = [];
781	$data = [
782		'links' => [],
783		'templates' => []
784	];
785
786	foreach ($items as $item) {
787		if ($item['templateid'] != 0) {
788			$parent_itemids[$item['templateid']] = true;
789			$data['links'][$item['itemid']] = ['itemid' => $item['templateid']];
790		}
791	}
792
793	if (!$parent_itemids) {
794		return $data;
795	}
796
797	$all_parent_itemids = [];
798	$hostids = [];
799	if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
800		$lld_ruleids = [];
801	}
802
803	do {
804		if ($flag == ZBX_FLAG_DISCOVERY_RULE) {
805			$db_items = API::DiscoveryRule()->get([
806				'output' => ['itemid', 'hostid', 'templateid'],
807				'itemids' => array_keys($parent_itemids)
808			]);
809		}
810		elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
811			$db_items = API::ItemPrototype()->get([
812				'output' => ['itemid', 'hostid', 'templateid'],
813				'itemids' => array_keys($parent_itemids),
814				'selectDiscoveryRule' => ['itemid']
815			]);
816		}
817		// ZBX_FLAG_DISCOVERY_NORMAL
818		else {
819			$db_items = API::Item()->get([
820				'output' => ['itemid', 'hostid', 'templateid'],
821				'itemids' => array_keys($parent_itemids),
822				'webitems' => true
823			]);
824		}
825
826		$all_parent_itemids += $parent_itemids;
827		$parent_itemids = [];
828
829		foreach ($db_items as $db_item) {
830			$data['templates'][$db_item['hostid']] = [];
831			$hostids[$db_item['itemid']] = $db_item['hostid'];
832
833			if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
834				$lld_ruleids[$db_item['itemid']] = $db_item['discoveryRule']['itemid'];
835			}
836
837			if ($db_item['templateid'] != 0) {
838				if (!array_key_exists($db_item['templateid'], $all_parent_itemids)) {
839					$parent_itemids[$db_item['templateid']] = true;
840				}
841
842				$data['links'][$db_item['itemid']] = ['itemid' => $db_item['templateid']];
843			}
844		}
845	}
846	while ($parent_itemids);
847
848	foreach ($data['links'] as &$parent_item) {
849		$parent_item['hostid'] = array_key_exists($parent_item['itemid'], $hostids)
850			? $hostids[$parent_item['itemid']]
851			: 0;
852
853		if ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
854			$parent_item['lld_ruleid'] = array_key_exists($parent_item['itemid'], $lld_ruleids)
855				? $lld_ruleids[$parent_item['itemid']]
856				: 0;
857		}
858	}
859	unset($parent_item);
860
861	$db_templates = $data['templates']
862		? API::Template()->get([
863			'output' => ['name'],
864			'templateids' => array_keys($data['templates']),
865			'preservekeys' => true
866		])
867		: [];
868
869	$rw_templates = $db_templates
870		? API::Template()->get([
871			'output' => [],
872			'templateids' => array_keys($db_templates),
873			'editable' => true,
874			'preservekeys' => true
875		])
876		: [];
877
878	$data['templates'][0] = [];
879
880	foreach ($data['templates'] as $hostid => &$template) {
881		$template = array_key_exists($hostid, $db_templates)
882			? [
883				'hostid' => $hostid,
884				'name' => $db_templates[$hostid]['name'],
885				'permission' => array_key_exists($hostid, $rw_templates) ? PERM_READ_WRITE : PERM_READ
886			]
887			: [
888				'hostid' => $hostid,
889				'name' => _('Inaccessible template'),
890				'permission' => PERM_DENY
891			];
892	}
893	unset($template);
894
895	return $data;
896}
897
898/**
899 * Returns a template prefix for selected item.
900 *
901 * @param string $itemid
902 * @param array  $parent_templates  The list of the templates, prepared by getItemParentTemplates() function.
903 * @param int    $flag              Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE,
904 *                                  ZBX_FLAG_DISCOVERY_PROTOTYPE).
905 *
906 * @return array|null
907 */
908function makeItemTemplatePrefix($itemid, array $parent_templates, $flag) {
909	if (!array_key_exists($itemid, $parent_templates['links'])) {
910		return null;
911	}
912
913	while (array_key_exists($parent_templates['links'][$itemid]['itemid'], $parent_templates['links'])) {
914		$itemid = $parent_templates['links'][$itemid]['itemid'];
915	}
916
917	$template = $parent_templates['templates'][$parent_templates['links'][$itemid]['hostid']];
918
919	if ($template['permission'] == PERM_READ_WRITE) {
920		if ($flag == ZBX_FLAG_DISCOVERY_RULE) {
921			$url = (new CUrl('host_discovery.php'))
922				->setArgument('filter_set', '1')
923				->setArgument('filter_hostids', [$template['hostid']]);
924		}
925		elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
926			$url = (new CUrl('disc_prototypes.php'))
927				->setArgument('parent_discoveryid', $parent_templates['links'][$itemid]['lld_ruleid']);
928		}
929		// ZBX_FLAG_DISCOVERY_NORMAL
930		else {
931			$url = (new CUrl('items.php'))
932				->setArgument('filter_set', '1')
933				->setArgument('filter_hostids', [$template['hostid']]);
934		}
935
936		$name = (new CLink(CHtml::encode($template['name']), $url))->addClass(ZBX_STYLE_LINK_ALT);
937	}
938	else {
939		$name = new CSpan(CHtml::encode($template['name']));
940	}
941
942	return [$name->addClass(ZBX_STYLE_GREY), NAME_DELIMITER];
943}
944
945/**
946 * Returns a list of item templates.
947 *
948 * @param string $itemid
949 * @param array  $parent_templates  The list of the templates, prepared by getItemParentTemplates() function.
950 * @param int    $flag              Origin of the item (ZBX_FLAG_DISCOVERY_NORMAL, ZBX_FLAG_DISCOVERY_RULE,
951 *                                  ZBX_FLAG_DISCOVERY_PROTOTYPE).
952 *
953 * @return array
954 */
955function makeItemTemplatesHtml($itemid, array $parent_templates, $flag) {
956	$list = [];
957
958	while (array_key_exists($itemid, $parent_templates['links'])) {
959		$template = $parent_templates['templates'][$parent_templates['links'][$itemid]['hostid']];
960
961		if ($template['permission'] == PERM_READ_WRITE) {
962			if ($flag == ZBX_FLAG_DISCOVERY_RULE) {
963				$url = (new CUrl('host_discovery.php'))
964					->setArgument('form', 'update')
965					->setArgument('itemid', $parent_templates['links'][$itemid]['itemid']);
966			}
967			elseif ($flag == ZBX_FLAG_DISCOVERY_PROTOTYPE) {
968				$url = (new CUrl('disc_prototypes.php'))
969					->setArgument('form', 'update')
970					->setArgument('itemid', $parent_templates['links'][$itemid]['itemid'])
971					->setArgument('parent_discoveryid', $parent_templates['links'][$itemid]['lld_ruleid']);
972			}
973			// ZBX_FLAG_DISCOVERY_NORMAL
974			else {
975				$url = (new CUrl('items.php'))
976					->setArgument('form', 'update')
977					->setArgument('itemid', $parent_templates['links'][$itemid]['itemid']);
978			}
979
980			$name = new CLink(CHtml::encode($template['name']), $url);
981		}
982		else {
983			$name = (new CSpan(CHtml::encode($template['name'])))->addClass(ZBX_STYLE_GREY);
984		}
985
986		array_unshift($list, $name, '&nbsp;&rArr;&nbsp;');
987
988		$itemid = $parent_templates['links'][$itemid]['itemid'];
989	}
990
991	if ($list) {
992		array_pop($list);
993	}
994
995	return $list;
996}
997
998/**
999 * Collect latest value and actual severity value for each item of Data overview table.
1000 *
1001 * @param array $db_items
1002 * @param array $data
1003 * @param int   $show_suppressed
1004 *
1005 * @return array
1006 */
1007function getDataOverviewCellData(array $db_items, array $data, int $show_suppressed): array {
1008	$history = Manager::History()->getLastValues($db_items, 1, ZBX_HISTORY_PERIOD);
1009
1010	$db_triggers = getTriggersWithActualSeverity([
1011		'output' => ['triggerid', 'priority', 'value'],
1012		'selectItems' => ['itemid'],
1013		'itemids' => array_keys($db_items),
1014		'monitored' => true,
1015		'preservekeys' => true
1016	], ['show_suppressed' => $show_suppressed]);
1017
1018	$itemid_to_triggerids = [];
1019	foreach ($db_triggers as $triggerid => $db_trigger) {
1020		foreach ($db_trigger['items'] as $item) {
1021			if (!array_key_exists($item['itemid'], $itemid_to_triggerids)) {
1022				$itemid_to_triggerids[$item['itemid']] = [];
1023			}
1024			$itemid_to_triggerids[$item['itemid']][] = $triggerid;
1025		}
1026	}
1027
1028	// Apply values and trigger severity to each $data cell.
1029	foreach ($data as &$data_clusters) {
1030		foreach ($data_clusters as &$data_cluster) {
1031			foreach ($data_cluster as &$item) {
1032				$itemid = $item['itemid'];
1033
1034				if (array_key_exists($itemid, $itemid_to_triggerids)) {
1035					$max_priority = -1;
1036					$max_priority_triggerid = -1;
1037					foreach ($itemid_to_triggerids[$itemid] as $triggerid) {
1038						$trigger = $db_triggers[$triggerid];
1039
1040						// Bump lower priority triggers of value "true" ahead of triggers with value "false".
1041						$multiplier = ($trigger['value'] == TRIGGER_VALUE_TRUE) ? TRIGGER_SEVERITY_COUNT : 0;
1042						if ($trigger['priority'] + $multiplier > $max_priority) {
1043							$max_priority_triggerid = $triggerid;
1044							$max_priority = $trigger['priority'] + $multiplier;
1045						}
1046					}
1047					$trigger = $db_triggers[$max_priority_triggerid];
1048				}
1049				else {
1050					$trigger = null;
1051				}
1052
1053				$item += [
1054					'value' => array_key_exists($itemid, $history) ? $history[$itemid][0]['value'] : null,
1055					'trigger' => $trigger
1056				];
1057			}
1058		}
1059	}
1060	unset($data_clusters, $data_cluster, $item);
1061
1062	return $data;
1063}
1064
1065/**
1066 * @param array  $groupids
1067 * @param array  $hostids
1068 * @param string $application
1069 *
1070 * @return array
1071 */
1072function getDataOverviewItems(?array $groupids = null, ?array $hostids = null, ?string $application = ''): array {
1073	if ($application !== '') {
1074		$db_applications = API::Application()->get([
1075			'output' => ['hostid'],
1076			'hostids' => $hostids,
1077			'groupids' => $groupids,
1078			'search' => ['name' => $application],
1079			'preservekeys' => true
1080		]);
1081
1082		$applicationids = array_keys($db_applications);
1083		$hostids = array_keys(array_flip(array_column($db_applications, 'hostid')));
1084	}
1085	else {
1086		$applicationids = null;
1087	}
1088
1089	if ($hostids === null) {
1090		$db_hosts = API::Host()->get([
1091			'output' => [],
1092			'groupids' => $groupids,
1093			'applicationids' => $applicationids,
1094			'monitored_hosts' => true,
1095			'with_monitored_items' => true,
1096			'preservekeys' => true,
1097			'limit' => ZBX_MAX_TABLE_COLUMNS + 1
1098		]);
1099		$hostids = array_keys($db_hosts);
1100	}
1101
1102	if ($application !== '') {
1103		$db_items = API::Item()->get([
1104			'output' => ['itemid', 'hostid', 'name', 'key_', 'value_type', 'units', 'valuemapid'],
1105			'selectHosts' => ['name'],
1106			'applicationids' => $applicationids,
1107			'monitored' => true,
1108			'webitems' => true,
1109			'preservekeys' => true
1110		]);
1111	}
1112	else {
1113		$db_items = API::Item()->get([
1114			'output' => ['itemid', 'hostid', 'name', 'key_', 'value_type', 'units', 'valuemapid'],
1115			'selectHosts' => ['name'],
1116			'hostids' => $hostids,
1117			'groupids' => $groupids,
1118			'monitored' => true,
1119			'webitems' => true,
1120			'preservekeys' => true
1121		]);
1122	}
1123
1124	$db_items = CMacrosResolverHelper::resolveItemNames($db_items);
1125
1126	CArrayHelper::sort($db_items, [
1127		['field' => 'name_expanded', 'order' => ZBX_SORT_UP],
1128		['field' => 'itemid', 'order' => ZBX_SORT_UP]
1129	]);
1130
1131	return [$db_items, $hostids];
1132}
1133
1134/**
1135 * @param array   $groupids
1136 * @param array   $hostids
1137 * @param array   $filter
1138 * @param string  $filter['application']
1139 * @param int     $filter['show_suppressed']
1140 *
1141 * @return array
1142 */
1143function getDataOverview(?array $groupids, ?array $hostids, array $filter): array {
1144	[$db_items, $hostids] = getDataOverviewItems($groupids, $hostids, $filter['application']);
1145
1146	$data = [];
1147	$item_counter = [];
1148	$db_hosts = [];
1149
1150	foreach ($db_items as $db_item) {
1151		$item_name = $db_item['name_expanded'];
1152		$host_name = $db_item['hosts'][0]['name'];
1153		$db_hosts[$db_item['hostid']] = $db_item['hosts'][0];
1154
1155		if (!array_key_exists($host_name, $item_counter)) {
1156			$item_counter[$host_name] = [];
1157		}
1158
1159		if (!array_key_exists($item_name, $item_counter[$host_name])) {
1160			$item_counter[$host_name][$item_name] = 0;
1161		}
1162
1163		$item_place = $item_counter[$host_name][$item_name];
1164		$item_counter[$host_name][$item_name]++;
1165
1166		$item = [
1167			'itemid' => $db_item['itemid'],
1168			'value_type' => $db_item['value_type'],
1169			'units' => $db_item['units'],
1170			'valuemapid' => $db_item['valuemapid'],
1171			'acknowledged' => array_key_exists('acknowledged', $db_item) ? $db_item['acknowledged'] : 0
1172		];
1173
1174		if (array_key_exists('triggerid', $db_item)) {
1175			$item += [
1176				'triggerid' => $db_item['triggerid'],
1177				'severity' => $db_item['priority'],
1178				'tr_value' => $db_item['value']
1179			];
1180		}
1181		else {
1182			$item += [
1183				'triggerid' => null,
1184				'severity' => null,
1185				'tr_value' => null
1186			];
1187		}
1188
1189		$data[$item_name][$item_place][$host_name] = $item;
1190	}
1191
1192	CArrayHelper::sort($db_hosts, [
1193		['field' => 'name', 'order' => ZBX_SORT_UP]
1194	]);
1195
1196	$has_hidden_hosts = (count($db_hosts) > ZBX_MAX_TABLE_COLUMNS);
1197	$db_hosts = array_slice($db_hosts, 0, ZBX_MAX_TABLE_COLUMNS, true);
1198
1199	$data = array_slice($data, 0, ZBX_MAX_TABLE_COLUMNS, true);
1200	$items_left = ZBX_MAX_TABLE_COLUMNS;
1201	$itemids = [];
1202	array_walk($data, function (array &$item_columns) use (&$itemids, &$items_left) {
1203		if ($items_left != 0) {
1204			$item_columns = array_slice($item_columns, 0, min(ZBX_MAX_TABLE_COLUMNS, $items_left));
1205			$items_left -= count($item_columns);
1206		}
1207		else {
1208			$item_columns = null;
1209			return;
1210		}
1211
1212		array_walk($item_columns, function (array &$item_column) use (&$itemids) {
1213			$item_column = array_slice($item_column, 0, ZBX_MAX_TABLE_COLUMNS, true);
1214			$itemids += array_column($item_column, 'itemid', 'itemid');
1215		});
1216	});
1217	$data = array_filter($data);
1218
1219	$has_hidden_items = (count($db_items) != count($itemids));
1220
1221	$db_items = array_intersect_key($db_items, $itemids);
1222	$data = getDataOverviewCellData($db_items, $data, $filter['show_suppressed']);
1223
1224	return [$data, $db_hosts, ($has_hidden_items || $has_hidden_hosts)];
1225}
1226
1227/**
1228 * Prepares interfaces select element with options.
1229 *
1230 * @param array $interfaces
1231 *
1232 * @return CSelect
1233 */
1234function getInterfaceSelect(array $interfaces): CSelect {
1235	$interface_select = new CSelect('interfaceid');
1236
1237	/** @var CSelectOption[] $options_by_type */
1238	$options_by_type = [];
1239
1240	foreach ($interfaces as $interface) {
1241		$label = $interface['useip']
1242			? $interface['ip'].' : '.$interface['port']
1243			: $interface['dns'].' : '.$interface['port'];
1244
1245		$option = new CSelectOption($interface['interfaceid'], $label);
1246
1247		if ($interface['type'] == INTERFACE_TYPE_SNMP) {
1248			$version = $interface['details']['version'];
1249			if ($version == SNMP_V3) {
1250				$option->setExtra('description', sprintf('%s: %d, %s: %s', _('Version'), $version,
1251					_('Context name'), $interface['details']['contextname']
1252				));
1253			} else {
1254				$option->setExtra('description', sprintf('%s: %d, %s: %s', _('Version'), $version,
1255					_x('Community', 'SNMP Community'), $interface['details']['community']
1256				));
1257			}
1258		}
1259
1260		$options_by_type[$interface['type']][] = $option;
1261	}
1262
1263	foreach ([INTERFACE_TYPE_AGENT, INTERFACE_TYPE_SNMP, INTERFACE_TYPE_JMX, INTERFACE_TYPE_IPMI] as $interface_type) {
1264		if (array_key_exists($interface_type, $options_by_type)) {
1265			$interface_group = new CSelectOptionGroup((string) interfaceType2str($interface_type));
1266
1267			if ($interface_type == INTERFACE_TYPE_SNMP) {
1268				$interface_group->setOptionTemplate('#{label}'.(new CDiv('#{description}'))->addClass('description'));
1269			}
1270
1271			$interface_group->addOptions($options_by_type[$interface_type]);
1272
1273			$interface_select->addOptionGroup($interface_group);
1274		}
1275	}
1276
1277	return $interface_select;
1278}
1279
1280/**
1281 * @param array $item
1282 * @param array $trigger
1283 *
1284 * @return CCol
1285 */
1286function getItemDataOverviewCell(array $item, ?array $trigger = null): CCol {
1287	$ack = null;
1288	$css = '';
1289	$value = UNKNOWN_VALUE;
1290
1291	if ($trigger && $trigger['value'] == TRIGGER_VALUE_TRUE) {
1292		$css = getSeverityStyle($trigger['priority']);
1293
1294		if ($trigger['problem']['acknowledged'] == 1) {
1295			$ack = [' ', (new CSpan())->addClass(ZBX_STYLE_ICON_ACKN)];
1296		}
1297	}
1298
1299	if ($item['value'] !== null) {
1300		$value = formatHistoryValue($item['value'], $item);
1301	}
1302
1303	return (new CCol([$value, $ack]))
1304		->addClass($css)
1305		->setMenuPopup(CMenuPopupHelper::getHistory($item['itemid']))
1306		->addClass(ZBX_STYLE_CURSOR_POINTER)
1307		->addClass(ZBX_STYLE_NOWRAP);
1308}
1309
1310/**
1311 * Get same application IDs on destination host and return array with keys as source application IDs
1312 * and values as destination application IDs.
1313 *
1314 * Comments: !!! Don't forget sync code with C !!!
1315 *
1316 * @param array  $applicationIds
1317 * @param string $hostId
1318 *
1319 * @return array
1320 */
1321function get_same_applications_for_host(array $applicationIds, $hostId) {
1322	$applications = [];
1323
1324	$dbApplications = DBselect(
1325		'SELECT a1.applicationid AS dstappid,a2.applicationid AS srcappid'.
1326		' FROM applications a1,applications a2'.
1327		' WHERE a1.name=a2.name'.
1328			' AND a1.hostid='.zbx_dbstr($hostId).
1329			' AND '.dbConditionInt('a2.applicationid', $applicationIds)
1330	);
1331
1332	while ($dbApplication = DBfetch($dbApplications)) {
1333		$applications[$dbApplication['srcappid']] = $dbApplication['dstappid'];
1334	}
1335
1336	return $applications;
1337}
1338
1339/******************************************************************************
1340 *                                                                            *
1341 * Comments: !!! Don't forget sync code with C !!!                            *
1342 *                                                                            *
1343 ******************************************************************************/
1344function get_applications_by_itemid($itemids, $field = 'applicationid') {
1345	zbx_value2array($itemids);
1346	$result = [];
1347	$db_applications = DBselect(
1348		'SELECT DISTINCT app.'.$field.' AS result'.
1349		' FROM applications app,items_applications ia'.
1350		' WHERE app.applicationid=ia.applicationid'.
1351			' AND '.dbConditionInt('ia.itemid', $itemids)
1352	);
1353	while ($db_application = DBfetch($db_applications)) {
1354		array_push($result, $db_application['result']);
1355	}
1356
1357	return $result;
1358}
1359
1360/**
1361 * Format history value.
1362 * First format the value according to the configuration of the item. Then apply the value mapping to the formatted (!)
1363 * value.
1364 *
1365 * @param mixed     $value
1366 * @param array     $item
1367 * @param int       $item['value_type']     type of the value: ITEM_VALUE_TYPE_FLOAT, ITEM_VALUE_TYPE_UINT64, ...
1368 * @param string    $item['units']          units of item
1369 * @param int       $item['valuemapid']     id of mapping set of values
1370 * @param bool      $trim
1371 *
1372 * @return string
1373 */
1374function formatHistoryValue($value, array $item, $trim = true) {
1375	$mapping = false;
1376
1377	// format value
1378	if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) {
1379		$value = convertUnits([
1380			'value' => $value,
1381			'units' => $item['units']
1382		]);
1383	}
1384	elseif (!in_array($item['value_type'], [ITEM_VALUE_TYPE_STR, ITEM_VALUE_TYPE_TEXT, ITEM_VALUE_TYPE_LOG])) {
1385		$value = _('Unknown value type');
1386	}
1387
1388	// apply value mapping
1389	switch ($item['value_type']) {
1390		case ITEM_VALUE_TYPE_STR:
1391			$mapping = getMappedValue($value, $item['valuemapid']);
1392			// break; is not missing here
1393
1394		case ITEM_VALUE_TYPE_TEXT:
1395		case ITEM_VALUE_TYPE_LOG:
1396			if ($trim && mb_strlen($value) > 20) {
1397				$value = mb_substr($value, 0, 20).'...';
1398			}
1399
1400			if ($mapping !== false) {
1401				$value = $mapping.' ('.$value.')';
1402			}
1403
1404			break;
1405
1406		default:
1407			$value = applyValueMap($value, $item['valuemapid']);
1408	}
1409
1410	return $value;
1411}
1412
1413/**
1414 * Retrieves from DB historical data for items and applies functional calculations.
1415 * If fails for some reason, returns UNRESOLVED_MACRO_STRING.
1416 *
1417 * @param array		$item
1418 * @param string	$item['value_type']	type of item, allowed: ITEM_VALUE_TYPE_FLOAT and ITEM_VALUE_TYPE_UINT64
1419 * @param string	$item['itemid']		ID of item
1420 * @param string	$item['units']		units of item
1421 * @param string	$function			function to apply to time period from param, allowed: min, max and avg
1422 * @param string	$parameter			formatted parameter for function, example: "2w" meaning 2 weeks
1423 *
1424 * @return string item functional value from history
1425 */
1426function getItemFunctionalValue($item, $function, $parameter) {
1427	// Check whether function is allowed and parameter is specified.
1428	if (!in_array($function, ['min', 'max', 'avg']) || $parameter === '') {
1429		return UNRESOLVED_MACRO_STRING;
1430	}
1431
1432	// Check whether item type is allowed for min, max and avg functions.
1433	if ($item['value_type'] != ITEM_VALUE_TYPE_FLOAT && $item['value_type'] != ITEM_VALUE_TYPE_UINT64) {
1434		return UNRESOLVED_MACRO_STRING;
1435	}
1436
1437	$number_parser = new CNumberParser(['with_suffix' => true]);
1438
1439	if ($number_parser->parse($parameter) != CParser::PARSE_SUCCESS) {
1440		return UNRESOLVED_MACRO_STRING;
1441	}
1442
1443	$parameter = $number_parser->calcValue();
1444
1445	$time_from = time() - $parameter;
1446
1447	if ($time_from < 0 || $time_from > ZBX_MAX_DATE) {
1448		return UNRESOLVED_MACRO_STRING;
1449	}
1450
1451	$result = Manager::History()->getAggregatedValue($item, $function, $time_from);
1452
1453	if ($result !== null) {
1454		return convertUnits(['value' => $result, 'units' => $item['units']]);
1455	}
1456	else {
1457		return UNRESOLVED_MACRO_STRING;
1458	}
1459}
1460
1461/**
1462 * Check if current time is within the given period.
1463 *
1464 * @param string $period	time period format: "wd[-wd2],hh:mm-hh:mm"
1465 * @param int $now			current timestamp
1466 *
1467 * @return bool		true - within period, false - out of period
1468 */
1469function checkTimePeriod($period, $now) {
1470	if (sscanf($period, '%d-%d,%d:%d-%d:%d', $d1, $d2, $h1, $m1, $h2, $m2) != 6) {
1471		if (sscanf($period, '%d,%d:%d-%d:%d', $d1, $h1, $m1, $h2, $m2) != 5) {
1472			// delay period format is wrong - skip
1473			return false;
1474		}
1475		$d2 = $d1;
1476	}
1477
1478	$tm = localtime($now, true);
1479	$day = ($tm['tm_wday'] == 0) ? 7 : $tm['tm_wday'];
1480	$sec = SEC_PER_HOUR * $tm['tm_hour'] + SEC_PER_MIN * $tm['tm_min'] + $tm['tm_sec'];
1481
1482	$sec1 = SEC_PER_HOUR * $h1 + SEC_PER_MIN * $m1;
1483	$sec2 = SEC_PER_HOUR * $h2 + SEC_PER_MIN * $m2;
1484
1485	return $d1 <= $day && $day <= $d2 && $sec1 <= $sec && $sec < $sec2;
1486}
1487
1488/**
1489 * Get item minimum delay.
1490 *
1491 * @param string $delay
1492 * @param array $flexible_intervals
1493 *
1494 * @return string
1495 */
1496function getItemDelay($delay, array $flexible_intervals) {
1497	$delay = timeUnitToSeconds($delay);
1498
1499	if ($delay != 0 || !$flexible_intervals) {
1500		return $delay;
1501	}
1502
1503	$min_delay = SEC_PER_YEAR;
1504
1505	foreach ($flexible_intervals as $flexible_interval) {
1506		$flexible_interval_parts = explode('/', $flexible_interval);
1507		$flexible_delay = timeUnitToSeconds($flexible_interval_parts[0]);
1508
1509		$min_delay = min($min_delay, $flexible_delay);
1510	}
1511
1512	return $min_delay;
1513}
1514
1515/**
1516 * Return delay value that is currently applicable
1517 *
1518 * @param int $delay					default delay
1519 * @param array $flexible_intervals		array of intervals in format: "d/wd[-wd2],hh:mm-hh:mm"
1520 * @param int $now						current timestamp
1521 *
1522 * @return int							delay for a current timestamp
1523 */
1524function getCurrentDelay($delay, array $flexible_intervals, $now) {
1525	if (!$flexible_intervals) {
1526		return $delay;
1527	}
1528
1529	$current_delay = -1;
1530
1531	foreach ($flexible_intervals as $flexible_interval) {
1532		list($flexible_delay, $flexible_period) = explode('/', $flexible_interval);
1533		$flexible_delay = (int) $flexible_delay;
1534
1535		if (($current_delay == -1 || $flexible_delay < $current_delay) && checkTimePeriod($flexible_period, $now)) {
1536			$current_delay = $flexible_delay;
1537		}
1538	}
1539
1540	if ($current_delay == -1) {
1541		return $delay;
1542	}
1543
1544	return $current_delay;
1545}
1546
1547/**
1548 * Return time of next flexible interval
1549 *
1550 * @param array $flexible_intervals  array of intervals in format: "d/wd[-wd2],hh:mm-hh:mm"
1551 * @param int $now                   current timestamp
1552 * @param int $next_interval          timestamp of a next interval
1553 *
1554 * @return bool                      false if no flexible intervals defined
1555 */
1556function getNextDelayInterval(array $flexible_intervals, $now, &$next_interval) {
1557	if (!$flexible_intervals) {
1558		return false;
1559	}
1560
1561	$next = 0;
1562	$tm = localtime($now, true);
1563	$day = ($tm['tm_wday'] == 0) ? 7 : $tm['tm_wday'];
1564	$sec = SEC_PER_HOUR * $tm['tm_hour'] + SEC_PER_MIN * $tm['tm_min'] + $tm['tm_sec'];
1565
1566	foreach ($flexible_intervals as $flexible_interval) {
1567		$flexible_interval_parts = explode('/', $flexible_interval);
1568
1569		if (sscanf($flexible_interval_parts[1], '%d-%d,%d:%d-%d:%d', $d1, $d2, $h1, $m1, $h2, $m2) != 6) {
1570			if (sscanf($flexible_interval_parts[1], '%d,%d:%d-%d:%d', $d1, $h1, $m1, $h2, $m2) != 5) {
1571				continue;
1572			}
1573			$d2 = $d1;
1574		}
1575
1576		$sec1 = SEC_PER_HOUR * $h1 + SEC_PER_MIN * $m1;
1577		$sec2 = SEC_PER_HOUR * $h2 + SEC_PER_MIN * $m2;
1578
1579		// current period
1580		if ($d1 <= $day && $day <= $d2 && $sec1 <= $sec && $sec < $sec2) {
1581			if ($next == 0 || $next > $now - $sec + $sec2) {
1582				// the next second after the current interval's upper bound
1583				$next = $now - $sec + $sec2;
1584			}
1585		}
1586		// will be active today
1587		elseif ($d1 <= $day && $d2 >= $day && $sec < $sec1) {
1588			if ($next == 0 || $next > $now - $sec + $sec1) {
1589				$next = $now - $sec + $sec1;
1590			}
1591		}
1592		else {
1593			$nextDay = ($day + 1 <= 7) ? $day + 1 : 1;
1594
1595			// will be active tomorrow
1596			if ($d1 <= $nextDay && $nextDay <= $d2) {
1597				if ($next == 0 || $next > $now - $sec + SEC_PER_DAY + $sec1) {
1598					$next = $now - $sec + SEC_PER_DAY + $sec1;
1599				}
1600			}
1601			// later in the future
1602			else {
1603				$dayDiff = -1;
1604
1605				if ($day < $d1) {
1606					$dayDiff = $d1 - $day;
1607				}
1608				if ($day >= $d2) {
1609					$dayDiff = ($d1 + 7) - $day;
1610				}
1611				if ($d1 <= $day && $day < $d2) {
1612					// should never happen, could not deduce day difference
1613					$dayDiff = -1;
1614				}
1615				if ($dayDiff != -1 && ($next == 0 || $next > $now - $sec + SEC_PER_DAY * $dayDiff + $sec1)) {
1616					$next = $now - $sec + SEC_PER_DAY * $dayDiff + $sec1;
1617				}
1618			}
1619		}
1620	}
1621
1622	if ($next != 0) {
1623		$next_interval = $next;
1624	}
1625
1626	return ($next != 0);
1627}
1628
1629/**
1630 * Calculate nextcheck timestamp for an item using flexible intervals.
1631 *
1632 * the parameter $flexible_intervals is an array if strings that are in the following format:
1633 *
1634 *           +------------[;]<----------+
1635 *           |                          |
1636 *         ->+-[d/wd[-wd2],hh:mm-hh:mm]-+
1637 *
1638 *         d       - delay (0-n)
1639 *         wd, wd2 - day of week (1-7)
1640 *         hh      - hours (0-24)
1641 *         mm      - minutes (0-59)
1642 *
1643 * @param int $seed						seed value applied to delay to spread item checks over the delay period
1644 * @param string $delay					default delay, can be overridden
1645 * @param array $flexible_intervals		array of flexible intervals
1646 * @param int $now						current timestamp
1647 *
1648 * @return int
1649 */
1650function calculateItemNextCheck($seed, $delay, $flexible_intervals, $now) {
1651	/*
1652	 * Try to find the nearest 'nextcheck' value with condition 'now' < 'nextcheck' < 'now' + SEC_PER_YEAR
1653	 * If it is not possible to check the item within a year, fail.
1654	 */
1655
1656	$t = $now;
1657	$tMax = $now + SEC_PER_YEAR;
1658	$try = 0;
1659
1660	while ($t < $tMax) {
1661		// Calculate 'nextcheck' value for the current interval.
1662		$currentDelay = getCurrentDelay($delay, $flexible_intervals, $t);
1663
1664		if ($currentDelay != 0) {
1665			$nextCheck = $currentDelay * floor($t / $currentDelay) + ($seed % $currentDelay);
1666
1667			if ($try == 0) {
1668				while ($nextCheck <= $t) {
1669					$nextCheck += $currentDelay;
1670				}
1671			}
1672			else {
1673				while ($nextCheck < $t) {
1674					$nextCheck += $currentDelay;
1675				}
1676			}
1677		}
1678		else {
1679			$nextCheck = ZBX_JAN_2038;
1680		}
1681
1682		/*
1683		 * Is 'nextcheck' < end of the current interval and the end of the current interval
1684		 * is the beginning of the next interval - 1.
1685		 */
1686		if (getNextDelayInterval($flexible_intervals, $t, $nextInterval) && $nextCheck >= $nextInterval) {
1687			// 'nextcheck' is beyond the current interval.
1688			$t = $nextInterval;
1689			$try++;
1690		}
1691		else {
1692			break;
1693		}
1694	}
1695
1696	return $nextCheck;
1697}
1698
1699/*
1700 * Description:
1701 *	Function returns true if http items exists in the $items array.
1702 *	The array should contain a field 'type'
1703 */
1704function httpItemExists($items) {
1705	foreach ($items as $item) {
1706		if ($item['type'] == ITEM_TYPE_HTTPTEST) {
1707			return true;
1708		}
1709	}
1710	return false;
1711}
1712
1713function getParamFieldNameByType($itemType) {
1714	switch ($itemType) {
1715		case ITEM_TYPE_SSH:
1716		case ITEM_TYPE_TELNET:
1717		case ITEM_TYPE_JMX:
1718			return 'params_es';
1719		case ITEM_TYPE_DB_MONITOR:
1720			return 'params_ap';
1721		case ITEM_TYPE_CALCULATED:
1722			return 'params_f';
1723		default:
1724			return 'params';
1725	}
1726}
1727
1728function getParamFieldLabelByType($itemType) {
1729	switch ($itemType) {
1730		case ITEM_TYPE_SSH:
1731		case ITEM_TYPE_TELNET:
1732		case ITEM_TYPE_JMX:
1733			return _('Executed script');
1734		case ITEM_TYPE_DB_MONITOR:
1735			return _('SQL query');
1736		case ITEM_TYPE_CALCULATED:
1737			return _('Formula');
1738		default:
1739			return 'params';
1740	}
1741}
1742
1743/**
1744 * Get either one or all item preprocessing types.
1745 * If $grouped set to true, returns group labels. Returns empty string if no specific type is found.
1746 *
1747 * Usage examples:
1748 *    - get_preprocessing_types(null, true, [5, 4, 2])             Returns array as defined.
1749 *    - get_preprocessing_types(4, true, [5, 4, 2])                Returns string: 'Trim'.
1750 *    - get_preprocessing_types(<wrong type>, true, [5, 4, 2])     Returns an empty string: ''.
1751 *    - get_preprocessing_types(null, false, [5, 12, 15, 16, 20])  Returns subarrays in one array maintaining index:
1752 *                                                                     [5] => Regular expression
1753 *                                                                     [12] => JSONPath
1754 *                                                                     [15] => Does not match regular expression
1755 *                                                                     [16] => Check for error in JSON
1756 *                                                                     [20] => Discard unchanged with heartbeat
1757 *
1758 * @param int   $type             Item preprocessing type.
1759 * @param bool  $grouped          Group label flag. If specific type is given, this parameter does not matter.
1760 * @param array $supported_types  Array of supported pre-processing types. If none are given, empty array is returned.
1761 *
1762 * @return array|string
1763 */
1764function get_preprocessing_types($type = null, $grouped = true, array $supported_types = []) {
1765	$types = [
1766		ZBX_PREPROC_REGSUB => [
1767			'group' => _('Text'),
1768			'name' => _('Regular expression')
1769		],
1770		ZBX_PREPROC_STR_REPLACE => [
1771			'group' => _('Text'),
1772			'name' => _('Replace')
1773		],
1774		ZBX_PREPROC_TRIM => [
1775			'group' => _('Text'),
1776			'name' => _('Trim')
1777		],
1778		ZBX_PREPROC_RTRIM => [
1779			'group' => _('Text'),
1780			'name' => _('Right trim')
1781		],
1782		ZBX_PREPROC_LTRIM => [
1783			'group' => _('Text'),
1784			'name' => _('Left trim')
1785		],
1786		ZBX_PREPROC_XPATH => [
1787			'group' => _('Structured data'),
1788			'name' => _('XML XPath')
1789		],
1790		ZBX_PREPROC_JSONPATH => [
1791			'group' => _('Structured data'),
1792			'name' => _('JSONPath')
1793		],
1794		ZBX_PREPROC_CSV_TO_JSON => [
1795			'group' => _('Structured data'),
1796			'name' => _('CSV to JSON')
1797		],
1798		ZBX_PREPROC_MULTIPLIER => [
1799			'group' => _('Arithmetic'),
1800			'name' => _('Custom multiplier')
1801		],
1802		ZBX_PREPROC_DELTA_VALUE => [
1803			'group' => _x('Change', 'noun'),
1804			'name' => _('Simple change')
1805		],
1806		ZBX_PREPROC_DELTA_SPEED => [
1807			'group' => _x('Change', 'noun'),
1808			'name' => _('Change per second')
1809		],
1810		ZBX_PREPROC_BOOL2DEC => [
1811			'group' => _('Numeral systems'),
1812			'name' => _('Boolean to decimal')
1813		],
1814		ZBX_PREPROC_OCT2DEC => [
1815			'group' => _('Numeral systems'),
1816			'name' => _('Octal to decimal')
1817		],
1818		ZBX_PREPROC_HEX2DEC => [
1819			'group' => _('Numeral systems'),
1820			'name' => _('Hexadecimal to decimal')
1821		],
1822		ZBX_PREPROC_SCRIPT => [
1823			'group' => _('Custom scripts'),
1824			'name' => _('JavaScript')
1825		],
1826		ZBX_PREPROC_VALIDATE_RANGE => [
1827			'group' => _('Validation'),
1828			'name' => _('In range')
1829		],
1830		ZBX_PREPROC_VALIDATE_REGEX => [
1831			'group' => _('Validation'),
1832			'name' => _('Matches regular expression')
1833		],
1834		ZBX_PREPROC_VALIDATE_NOT_REGEX => [
1835			'group' => _('Validation'),
1836			'name' => _('Does not match regular expression')
1837		],
1838		ZBX_PREPROC_ERROR_FIELD_JSON => [
1839			'group' => _('Validation'),
1840			'name' => _('Check for error in JSON')
1841		],
1842		ZBX_PREPROC_ERROR_FIELD_XML => [
1843			'group' => _('Validation'),
1844			'name' => _('Check for error in XML')
1845		],
1846		ZBX_PREPROC_ERROR_FIELD_REGEX => [
1847			'group' => _('Validation'),
1848			'name' => _('Check for error using regular expression')
1849		],
1850		ZBX_PREPROC_THROTTLE_VALUE => [
1851			'group' => _('Throttling'),
1852			'name' => _('Discard unchanged')
1853		],
1854		ZBX_PREPROC_THROTTLE_TIMED_VALUE => [
1855			'group' => _('Throttling'),
1856			'name' => _('Discard unchanged with heartbeat')
1857		],
1858		ZBX_PREPROC_PROMETHEUS_PATTERN => [
1859			'group' => _('Prometheus'),
1860			'name' => _('Prometheus pattern')
1861		],
1862		ZBX_PREPROC_PROMETHEUS_TO_JSON => [
1863			'group' => _('Prometheus'),
1864			'name' => _('Prometheus to JSON')
1865		]
1866	];
1867
1868	$filtered_types = [];
1869
1870	foreach ($types as $_type => $data) {
1871		if (in_array($_type, $supported_types)) {
1872			$filtered_types[$data['group']][$_type] = $data['name'];
1873		}
1874	}
1875
1876	$groups = [];
1877
1878	foreach ($filtered_types as $label => $types) {
1879		$groups[] = [
1880			'label' => $label,
1881			'types' => $types
1882		];
1883	}
1884
1885	if ($type !== null) {
1886		foreach ($groups as $group) {
1887			if (array_key_exists($type, $group['types'])) {
1888				return $group['types'][$type];
1889			}
1890		}
1891
1892		return '';
1893	}
1894	elseif ($grouped) {
1895		return $groups;
1896	}
1897	else {
1898		$types = [];
1899
1900		foreach ($groups as $group) {
1901			$types += $group['types'];
1902		}
1903
1904		return $types;
1905	}
1906}
1907
1908/*
1909 * Quoting $param if it contain special characters.
1910 *
1911 * @param string $param
1912 * @param bool   $forced
1913 *
1914 * @return string
1915 */
1916function quoteItemKeyParam($param, $forced = false) {
1917	if (!$forced) {
1918		if (!isset($param[0]) || ($param[0] != '"' && false === strpbrk($param, ',]'))) {
1919			return $param;
1920		}
1921	}
1922
1923	return '"'.str_replace('"', '\\"', $param).'"';
1924}
1925
1926/**
1927 * Expands item name and for dependent item master item name.
1928 *
1929 * @param array  $items        Array of items.
1930 * @param string $data_source  'items' or 'itemprototypes'.
1931 *
1932 * @return array
1933 */
1934function expandItemNamesWithMasterItems($items, $data_source) {
1935	$items = CMacrosResolverHelper::resolveItemNames($items);
1936	$itemids = [];
1937	$master_itemids = [];
1938
1939	foreach ($items as $item_index => &$item) {
1940		if ($item['type'] == ITEM_TYPE_DEPENDENT) {
1941			$master_itemids[$item['master_itemid']] = true;
1942		}
1943
1944		// The "source" is required to tell the frontend where the link should point at - item or item prototype.
1945		$item['source'] = $data_source;
1946		$itemids[$item_index] = $item['itemid'];
1947	}
1948	unset($item);
1949
1950	$master_itemids = array_diff(array_keys($master_itemids), $itemids);
1951
1952	if ($master_itemids) {
1953		$options = [
1954			'output' => ['itemid', 'type', 'hostid', 'name', 'key_'],
1955			'itemids' => $master_itemids,
1956			'editable' => true,
1957			'preservekeys' => true
1958		];
1959		$master_items = API::Item()->get($options + ['webitems' => true]);
1960
1961		foreach ($master_items as &$master_item) {
1962			$master_item['source'] = 'items';
1963		}
1964		unset($master_item);
1965
1966		$master_item_prototypes = API::ItemPrototype()->get($options);
1967
1968		foreach ($master_item_prototypes as &$master_item_prototype) {
1969			$master_item_prototype['source'] = 'itemprototypes';
1970		}
1971		unset($master_item_prototype);
1972
1973		$master_items = CMacrosResolverHelper::resolveItemNames($master_items + $master_item_prototypes);
1974	}
1975
1976	foreach ($items as &$item) {
1977		if ($item['type'] == ITEM_TYPE_DEPENDENT) {
1978			$master_itemid = $item['master_itemid'];
1979			$items_index = array_search($master_itemid, $itemids);
1980
1981			$item['master_item'] = [
1982				'itemid' => $master_itemid,
1983				'name_expanded' => ($items_index === false)
1984					? $master_items[$master_itemid]['name_expanded']
1985					: $items[$items_index]['name_expanded'],
1986				'type' => ($items_index === false)
1987					? $master_items[$master_itemid]['type']
1988					: $items[$items_index]['type'],
1989				'source' => ($items_index === false)
1990					? $master_items[$master_itemid]['source']
1991					: $items[$items_index]['source']
1992			];
1993		}
1994	}
1995	unset($item);
1996
1997	return $items;
1998}
1999
2000/**
2001 * Returns an array of allowed item types for "Check now" functionality.
2002 *
2003 * @return array
2004 */
2005function checkNowAllowedTypes() {
2006	return [
2007		ITEM_TYPE_ZABBIX,
2008		ITEM_TYPE_SIMPLE,
2009		ITEM_TYPE_INTERNAL,
2010		ITEM_TYPE_AGGREGATE,
2011		ITEM_TYPE_EXTERNAL,
2012		ITEM_TYPE_DB_MONITOR,
2013		ITEM_TYPE_IPMI,
2014		ITEM_TYPE_SSH,
2015		ITEM_TYPE_TELNET,
2016		ITEM_TYPE_CALCULATED,
2017		ITEM_TYPE_JMX,
2018		ITEM_TYPE_HTTPAGENT,
2019		ITEM_TYPE_SNMP
2020	];
2021}
2022
2023/**
2024 * Validates update interval for items, item prototypes and low-level discovery rules and their overrides.
2025 *
2026 * @param CUpdateIntervalParser $parser [IN]      Parser used for delay validation.
2027 * @param string                $value  [IN]      Update interval to parse and validate.
2028 * @param string                $field_name [IN]  Frontend or API field name in the error
2029 * @param string                $error  [OUT]     Returned error string if delay validation fails.
2030 *
2031 * @return bool
2032 */
2033function validateDelay(CUpdateIntervalParser $parser, $field_name, $value, &$error) {
2034	if ($parser->parse($value) != CParser::PARSE_SUCCESS) {
2035		$error = _s('Incorrect value for field "%1$s": %2$s.', $field_name, _('invalid delay'));
2036
2037		return false;
2038	}
2039
2040	$delay = $parser->getDelay();
2041
2042	if ($delay[0] !== '{') {
2043		$delay_sec = timeUnitToSeconds($delay);
2044		$intervals = $parser->getIntervals();
2045		$flexible_intervals = $parser->getIntervals(ITEM_DELAY_FLEXIBLE);
2046		$has_scheduling_intervals = (bool) $parser->getIntervals(ITEM_DELAY_SCHEDULING);
2047		$has_macros = false;
2048
2049		foreach ($intervals as $interval) {
2050			if (strpos($interval['interval'], '{') !== false) {
2051				$has_macros = true;
2052				break;
2053			}
2054		}
2055
2056		// If delay is 0, there must be at least one either flexible or scheduling interval.
2057		if ($delay_sec == 0 && !$intervals) {
2058			$error = _('Item will not be refreshed. Specified update interval requires having at least one either flexible or scheduling interval.');
2059
2060			return false;
2061		}
2062		elseif ($delay_sec < 0 || $delay_sec > SEC_PER_DAY) {
2063			$error = _('Item will not be refreshed. Update interval should be between 1s and 1d. Also Scheduled/Flexible intervals can be used.');
2064
2065			return false;
2066		}
2067
2068		// If there are scheduling intervals or intervals with macros, skip the next check calculation.
2069		if (!$has_macros && !$has_scheduling_intervals && $flexible_intervals
2070				&& calculateItemNextCheck(0, $delay_sec, $flexible_intervals, time()) == ZBX_JAN_2038) {
2071			$error = _('Item will not be refreshed. Please enter a correct update interval.');
2072
2073			return false;
2074		}
2075	}
2076
2077	return true;
2078}
2079