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 * Class containing methods for operations with map elements.
24 *
25 * @return mixed
26 */
27abstract class CMapElement extends CApiService {
28
29	protected function checkSelementInput(&$selements, $method) {
30		$create = ($method === 'createSelements');
31		$update = ($method === 'updateSelements');
32
33		$element_types = [SYSMAP_ELEMENT_TYPE_HOST, SYSMAP_ELEMENT_TYPE_MAP, SYSMAP_ELEMENT_TYPE_TRIGGER,
34			SYSMAP_ELEMENT_TYPE_HOST_GROUP, SYSMAP_ELEMENT_TYPE_IMAGE
35		];
36
37		$elementtype_validator = new CLimitedSetValidator(['values' => $element_types]);
38
39		if ($update) {
40			$db_selements = $this->fetchSelementsByIds(zbx_objectValues($selements, 'selementid'));
41			$selements = $this->extendFromObjects(zbx_toHash($selements, 'selementid'), $db_selements, ['elementtype', 'elements']);
42		}
43
44		foreach ($selements as &$selement) {
45			if (!is_array($selement)) {
46				self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
47			}
48
49			if ($create) {
50				// Check required parameters.
51				$missing_keys = array_diff(['sysmapid', 'elementtype', 'iconid_off'], array_keys($selement));
52
53				if ($missing_keys) {
54					self::exception(ZBX_API_ERROR_PARAMETERS,
55						_s('Map element is missing parameters: %1$s', implode(', ', $missing_keys))
56					);
57				}
58			}
59
60			if (array_key_exists('urls', $selement)) {
61				foreach ($selement['urls'] as $url_data) {
62					if (!CHtmlUrlValidator::validate($url_data['url'], false)) {
63						self::exception(ZBX_API_ERROR_PARAMETERS, _('Wrong value for url field.'));
64					}
65				}
66			}
67
68			/*
69			 * @deprecated  As of version 3.4, use elements data array.
70			 */
71			if (array_key_exists('elementid', $selement)) {
72				$this->deprecated('elementid is deprecated parameter.');
73
74				switch ($selement['elementtype']) {
75					case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
76						$selement['elements'][0]['groupid'] = $selement['elementid'];
77						break;
78
79					case SYSMAP_ELEMENT_TYPE_HOST:
80						$selement['elements'][0]['hostid'] = $selement['elementid'];
81						break;
82
83					case SYSMAP_ELEMENT_TYPE_TRIGGER:
84						$selement['elements'][0]['triggerid'] = $selement['elementid'];
85						break;
86
87					case SYSMAP_ELEMENT_TYPE_MAP:
88						$selement['elements'][0]['sysmapid'] = $selement['elementid'];
89						break;
90				}
91
92				unset($selement['elementid']);
93			}
94
95			if ($update && array_key_exists('selementid', $selement)
96						&& array_key_exists($selement['selementid'], $db_selements)) {
97				$db_selement = $db_selements[$selement['selementid']];
98			}
99
100			if (!$elementtype_validator->validate($selement['elementtype'])) {
101				self::exception(ZBX_API_ERROR_PARAMETERS, _s('Incorrect value for field "%1$s": %2$s.',
102					'elementtype', _s('value must be one of %1$s', implode(', ', $element_types))
103				));
104			}
105
106			if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_IMAGE) {
107				unset($selement['elements']);
108			}
109			else {
110				if (!array_key_exists('elements', $selement)) {
111					self::exception(ZBX_API_ERROR_PARAMETERS,
112						_s('Map element is missing parameters: %1$s', 'elements')
113					);
114				}
115
116				if (!is_array($selement['elements'])) {
117					self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
118				}
119
120				if (!$selement['elements']) {
121					self::exception(ZBX_API_ERROR_PARAMETERS,
122						_s('Incorrect value for field "%1$s": %2$s.', 'elements', _('cannot be empty'))
123					);
124				}
125			}
126
127			if (array_key_exists('elements', $selement)) {
128				if (!is_array($selement['elements'])) {
129					self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
130				}
131			}
132
133			if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) {
134				foreach ($selement['elements'] as $element) {
135					if (!array_key_exists('triggerid', $element)) {
136						self::exception(ZBX_API_ERROR_PARAMETERS,
137							_s('Map element is missing parameters: %1$s', 'triggerid')
138						);
139					}
140
141					if (is_array($element['triggerid'])) {
142						self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
143					}
144					elseif ($element['triggerid'] === '' || $element['triggerid'] === null
145							|| $element['triggerid'] === false) {
146						self::exception(ZBX_API_ERROR_PARAMETERS,
147							_s('Incorrect value for field "%1$s": %2$s.', 'triggerid', _('cannot be empty'))
148						);
149					}
150				}
151			}
152			else {
153				if (array_key_exists('elements', $selement)) {
154					switch ($selement['elementtype']) {
155						case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
156							$field = 'groupid';
157							break;
158
159						case SYSMAP_ELEMENT_TYPE_HOST:
160							$field = 'hostid';
161							break;
162
163						case SYSMAP_ELEMENT_TYPE_MAP:
164							$field = 'sysmapid';
165							break;
166					}
167
168					$elements = reset($selement['elements']);
169
170					if (!is_array($elements)) {
171						self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
172					}
173
174					if (!array_key_exists($field, $elements)) {
175						self::exception(ZBX_API_ERROR_PARAMETERS,
176							_s('Map element is missing parameters: %1$s', $field)
177						);
178					}
179
180					if (is_array($elements[$field])) {
181						self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
182					}
183					elseif ($elements[$field] === '' || $elements[$field] === null || $elements[$field] === false) {
184						self::exception(ZBX_API_ERROR_PARAMETERS,
185							_s('Incorrect value for field "%1$s": %2$s.', $field, _('cannot be empty'))
186						);
187					}
188
189					if (count($selement['elements']) > 1) {
190						self::exception(ZBX_API_ERROR_PARAMETERS,
191							_s('Incorrect value for field "%1$s": %2$s.', 'elements', _('incorrect element count'))
192						);
193					}
194				}
195			}
196
197			if (isset($selement['iconid_off']) && $selement['iconid_off'] == 0) {
198				self::exception(ZBX_API_ERROR_PARAMETERS,
199					_s('No icon for map element "%s".', array_key_exists('label', $selement) ? $selement['label'] : '')
200				);
201			}
202
203			if ($create) {
204				$selement['urls'] = array_key_exists('urls', $selement) ? $selement['urls'] : [];
205			}
206		}
207		unset($selement);
208
209		// check permissions to used objects
210		if (!CMapHelper::checkSelementPermissions($selements)) {
211			self::exception(ZBX_API_ERROR_PERMISSIONS,
212					_('No permissions to referred object or it does not exist!')
213			);
214		}
215
216		return $update ? $db_selements : true;
217	}
218
219	/**
220	 * Checks that shape color attributes are valid.
221	 *
222	 * @throws APIException if input is invalid.
223	 *
224	 * @param array $shapes			An array of shapes.
225	 */
226	protected function checkShapeInput($shapes) {
227		$color_validator = new CColorValidator();
228		$fields = ['border_color', 'background_color', 'font_color'];
229
230		foreach ($shapes as $shape) {
231			foreach ($fields as $field) {
232				if (array_key_exists($field, $shape) && $shape[$field] !== ''
233						&& !$color_validator->validate($shape[$field])) {
234					self::exception(ZBX_API_ERROR_PARAMETERS, $color_validator->getError());
235				}
236			}
237		}
238	}
239
240	/**
241	 * Returns a hash of map elements with the given IDs. The result also includes URL assigned to the elements.
242	 *
243	 * @param array $selementIds
244	 *
245	 * @return array
246	 */
247	protected function fetchSelementsByIds(array $selementIds) {
248		$selements = API::getApiService()->select('sysmaps_elements', [
249			'output' => API_OUTPUT_EXTEND,
250			'filter' => ['selementid' => $selementIds],
251			'preservekeys' => true
252		]);
253
254		if ($selements) {
255			foreach ($selements as &$selement) {
256				$selement['urls'] = [];
257				$selement['elements'] = [];
258			}
259			unset($selement);
260
261			$selementUrls = API::getApiService()->select('sysmap_element_url', [
262				'output' => API_OUTPUT_EXTEND,
263				'filter' => ['selementid' => $selementIds]
264			]);
265			foreach ($selementUrls as $selementUrl) {
266				$selements[$selementUrl['selementid']]['urls'][] = $selementUrl;
267			}
268
269			$selement_triggers = API::getApiService()->select('sysmap_element_trigger', [
270				'output' => ['selement_triggerid', 'selementid', 'triggerid'],
271				'filter' => ['selementid' => $selementIds]
272			]);
273
274			foreach ($selement_triggers as $selement_trigger) {
275				$selements[$selement_trigger['selementid']]['elements'][] = [
276					'selement_triggerid' => $selement_trigger['selement_triggerid'],
277					'triggerid' => $selement_trigger['triggerid']
278				];
279			}
280
281			$single_element_types = [SYSMAP_ELEMENT_TYPE_HOST, SYSMAP_ELEMENT_TYPE_MAP, SYSMAP_ELEMENT_TYPE_HOST_GROUP];
282			foreach ($selements as &$selement) {
283				if (in_array($selement['elementtype'], $single_element_types)) {
284					switch ($selement['elementtype']) {
285						case SYSMAP_ELEMENT_TYPE_HOST_GROUP:
286							$field = 'groupid';
287							break;
288
289						case SYSMAP_ELEMENT_TYPE_HOST:
290							$field = 'hostid';
291							break;
292
293						case SYSMAP_ELEMENT_TYPE_MAP:
294							$field = 'sysmapid';
295							break;
296					}
297					$selement['elements'][] = [$field => $selement['elementid']];
298				}
299
300				unset($selement['elementid']);
301			}
302			unset($selement);
303		}
304
305		return $selements;
306	}
307
308	protected function checkLinkInput($links, $method) {
309		$update = ($method == 'updateLink');
310		$delete = ($method == 'deleteLink');
311
312		// permissions
313		if ($update || $delete) {
314			$linkDbFields = ['linkid' => null];
315
316			$dbLinks = API::getApiService()->select('sysmap_element_url', [
317				'filter' => ['selementid' => zbx_objectValues($links, 'linkid')],
318				'output' => ['linkid'],
319				'preservekeys' => true
320			]);
321		}
322		else {
323			$linkDbFields = [
324				'sysmapid' => null,
325				'selementid1' => null,
326				'selementid2' => null
327			];
328		}
329
330		$colorValidator = new CColorValidator();
331
332		foreach ($links as $link) {
333			if (!check_db_fields($linkDbFields, $link)) {
334				self::exception(ZBX_API_ERROR_PARAMETERS, _('Wrong fields for map link.'));
335			}
336
337			if (isset($link['color']) && !$colorValidator->validate($link['color'])) {
338				self::exception(ZBX_API_ERROR_PARAMETERS, $colorValidator->getError());
339			}
340
341			if ($update || $delete) {
342				if (!isset($dbLinks[$link['linkid']])) {
343					self::exception(ZBX_API_ERROR_PARAMETERS, _('No permissions to referred object or it does not exist!'));
344				}
345			}
346		}
347
348		return true;
349	}
350
351	/**
352	 * Add element to sysmap.
353	 *
354	 * @param array $elements[0,...]['sysmapid']
355	 * @param array $elements[0,...]['elementid']
356	 * @param array $elements[0,...]['elementtype']
357	 * @param array $elements[0,...]['label']
358	 * @param array $elements[0,...]['x']
359	 * @param array $elements[0,...]['y']
360	 * @param array $elements[0,...]['iconid_off']
361	 * @param array $elements[0,...]['iconid_on']
362	 * @param array $elements[0,...]['iconid_disabled']
363	 * @param array $elements[0,...]['urls'][0,...]
364	 * @param array $elements[0,...]['label_location']
365	 *
366	 * @return array
367	 */
368	protected function createSelements(array $selements) {
369		$selements = zbx_toArray($selements);
370
371		$this->checkSelementInput($selements, __FUNCTION__);
372
373		$single_element_types = [SYSMAP_ELEMENT_TYPE_HOST, SYSMAP_ELEMENT_TYPE_MAP, SYSMAP_ELEMENT_TYPE_HOST_GROUP];
374		foreach ($selements as &$selement) {
375			if (in_array($selement['elementtype'], $single_element_types)) {
376				$selement['elementid'] = reset($selement['elements'][0]);
377			}
378			elseif ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) {
379				unset($selement['elementid']);
380			}
381		}
382		unset($selement);
383
384		$selementids = DB::insert('sysmaps_elements', $selements);
385
386		$triggerids = [];
387
388		foreach ($selementids as $key => $selementid) {
389			if ($selements[$key]['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) {
390				foreach ($selements[$key]['elements'] as $element) {
391					$triggerids[$element['triggerid']] = true;
392				}
393			}
394		}
395
396		$db_triggers = API::Trigger()->get([
397			'output' => ['triggerid', 'priority'],
398			'triggerids' => array_keys($triggerids),
399			'preservekeys' => true
400		]);
401
402		$triggers = [];
403
404		foreach ($selementids as $key => $selementid) {
405			if ($selements[$key]['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) {
406				foreach ($selements[$key]['elements'] as $element) {
407					$priority = $db_triggers[$element['triggerid']]['priority'];
408					$triggers[$selementid][$priority][] = [
409						'selementid' => $selementid,
410						'triggerid' => $element['triggerid']
411					];
412				}
413				krsort($triggers[$selementid]);
414			}
415		}
416
417		$triggers_to_add = [];
418
419		foreach ($triggers as $selement_triggers) {
420			foreach ($selement_triggers as $selement_trigger_priorities) {
421				foreach ($selement_trigger_priorities as $selement_trigger_priority) {
422					$triggers_to_add[] = $selement_trigger_priority;
423				}
424			}
425		}
426
427		DB::insert('sysmap_element_trigger', $triggers_to_add);
428
429		$insertUrls = [];
430
431		foreach ($selementids as $key => $selementid) {
432			foreach ($selements[$key]['urls'] as $url) {
433				$url['selementid'] = $selementid;
434
435				$insertUrls[] = $url;
436			}
437		}
438
439		DB::insert('sysmap_element_url', $insertUrls);
440
441		return ['selementids' => $selementids];
442	}
443
444	/**
445	 * Update element to sysmap.
446	 *
447	 * @param array $elements[0,...]['selementid']
448	 * @param array $elements[0,...]['sysmapid']
449	 * @param array $elements[0,...]['elementid']
450	 * @param array $elements[0,...]['elementtype']
451	 * @param array $elements[0,...]['label']
452	 * @param array $elements[0,...]['x']
453	 * @param array $elements[0,...]['y']
454	 * @param array $elements[0,...]['iconid_off']
455	 * @param array $elements[0,...]['iconid_on']
456	 * @param array $elements[0,...]['iconid_disabled']
457	 * @param array $elements[0,...]['url']
458	 * @param array $elements[0,...]['label_location']
459	 */
460	protected function updateSelements(array $selements) {
461		$selements = zbx_toArray($selements);
462		$selementIds = [];
463
464		$db_selements = $this->checkSelementInput($selements, __FUNCTION__);
465
466		$update = [];
467		$urlsToDelete = [];
468		$urlsToUpdate = [];
469		$urlsToAdd = [];
470		$triggers_to_add = [];
471		$triggers_to_delete = [];
472		$triggerids = [];
473
474		foreach ($selements as &$selement) {
475			$db_selement = $db_selements[$selement['selementid']];
476
477			// Change type from something to trigger.
478			if ($selement['elementtype'] != $db_selement['elementtype']
479					&& $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) {
480				$selement['elementid'] = 0;
481
482				foreach ($selement['elements'] as $element) {
483					$triggerids[$element['triggerid']] = true;
484				}
485			}
486
487			// Change type from trigger to something.
488			if ($selement['elementtype'] != $db_selement['elementtype']
489					&& $db_selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) {
490				foreach ($db_selement['elements'] as $db_element) {
491					$triggers_to_delete[] = $db_element['selement_triggerid'];
492				}
493			}
494
495			if ($selement['elementtype'] != SYSMAP_ELEMENT_TYPE_IMAGE
496					&& $selement['elementtype'] != SYSMAP_ELEMENT_TYPE_TRIGGER) {
497				$selement['elementid'] = reset($selement['elements'][0]);
498			}
499
500			$db_elements = $db_selement['elements'];
501
502			foreach ($db_selement['elements'] as &$element) {
503				unset($element['selement_triggerid']);
504			}
505			unset($element);
506
507			if ($selement['elementtype'] == $db_selement['elementtype']
508					&& $selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) {
509				foreach ($db_elements as $element) {
510					$triggers_to_delete[] = $element['selement_triggerid'];
511				}
512
513				foreach ($selement['elements'] as $element) {
514					$triggerids[$element['triggerid']] = true;
515				}
516			}
517
518			$update[] = [
519				'values' => $selement,
520				'where' => ['selementid' => $selement['selementid']]
521			];
522			$selementIds[] = $selement['selementid'];
523
524			if (!isset($selement['urls'])) {
525				continue;
526			}
527
528			$diffUrls = zbx_array_diff($selement['urls'], $db_selement['urls'], 'name');
529
530			// add
531			foreach ($diffUrls['first'] as $newUrl) {
532				$newUrl['selementid'] = $selement['selementid'];
533				$urlsToAdd[] = $newUrl;
534			}
535
536			// update url
537			foreach ($diffUrls['both'] as $updUrl) {
538				$urlsToUpdate[] = [
539					'values' => $updUrl,
540					'where' => [
541						'selementid' => $selement['selementid'],
542						'name' => $updUrl['name']
543					]
544				];
545			}
546
547			// delete url
548			$urlsToDelete = array_merge($urlsToDelete, zbx_objectValues($diffUrls['second'], 'sysmapelementurlid'));
549		}
550		unset($selement);
551
552		$db_triggers = API::Trigger()->get([
553			'output' => ['triggerid', 'priority'],
554			'triggerids' => array_keys($triggerids),
555			'preservekeys' => true
556		]);
557
558		$triggers = [];
559
560		foreach ($selements as $key => $selement) {
561			if ($selement['elementtype'] == SYSMAP_ELEMENT_TYPE_TRIGGER) {
562				$selementid = $selement['selementid'];
563
564				foreach ($selement['elements'] as $element) {
565					$priority = $db_triggers[$element['triggerid']]['priority'];
566					$triggers[$selementid][$priority][] = [
567						'selementid' => $selementid,
568						'triggerid' => $element['triggerid']
569					];
570				}
571				krsort($triggers[$selementid]);
572			}
573		}
574
575		$triggers_to_add = [];
576
577		foreach ($triggers as $selement_triggers) {
578			foreach ($selement_triggers as $selement_trigger_priorities) {
579				foreach ($selement_trigger_priorities as $selement_trigger_priority) {
580					$triggers_to_add[] = $selement_trigger_priority;
581				}
582			}
583		}
584
585		DB::update('sysmaps_elements', $update);
586
587		if (!empty($urlsToDelete)) {
588			DB::delete('sysmap_element_url', ['sysmapelementurlid' => $urlsToDelete]);
589		}
590
591		if (!empty($urlsToUpdate)) {
592			DB::update('sysmap_element_url', $urlsToUpdate);
593		}
594
595		if (!empty($urlsToAdd)) {
596			DB::insert('sysmap_element_url', $urlsToAdd);
597		}
598
599		if ($triggers_to_delete) {
600			DB::delete('sysmap_element_trigger', ['selement_triggerid' => $triggers_to_delete]);
601		}
602
603		if ($triggers_to_add) {
604			DB::insert('sysmap_element_trigger', $triggers_to_add);
605		}
606
607		return ['selementids' => $selementIds];
608	}
609
610	/**
611	 * Delete element from map.
612	 *
613	 * @param array $selements							multidimensional array with selement objects
614	 * @param array $selements[0, ...]['selementid']	selementid to delete
615	 */
616	protected function deleteSelements(array $selements) {
617		$selements = zbx_toArray($selements);
618		$selementIds = zbx_objectValues($selements, 'selementid');
619
620		DB::delete('sysmaps_elements', ['selementid' => $selementIds]);
621
622		return $selementIds;
623	}
624
625	/**
626	 * Add shape to sysmap.
627	 *
628	 * @param array $shapes							Multidimensional array with shape properties.
629	 */
630	protected function createShapes(array $shapes) {
631		$shapes = zbx_toArray($shapes);
632
633		$this->checkShapeInput($shapes);
634
635		DB::insert('sysmap_shape', $shapes);
636	}
637
638	/**
639	 * Update shapes to sysmap.
640	 *
641	 * @param array $shapes							Multidimensional array with shape properties.
642	 */
643	protected function updateShapes(array $shapes) {
644		$shapes = zbx_toArray($shapes);
645
646		$this->checkShapeInput($shapes);
647
648		$update = [];
649		foreach ($shapes as $shape) {
650			$shapeid = $shape['sysmap_shapeid'];
651			unset($shape['sysmap_shapeid']);
652
653			if ($shape) {
654				$update[] = [
655					'values' => $shape,
656					'where' => ['sysmap_shapeid' => $shapeid]
657				];
658			}
659		}
660
661		DB::update('sysmap_shape', $update);
662	}
663
664	/**
665	 * Delete shapes from map.
666	 *
667	 * @param array $shapes							Multidimensional array with shape properties.
668	 */
669	protected function deleteShapes(array $shapes) {
670		$shapes = zbx_toArray($shapes);
671		$shapeids = zbx_objectValues($shapes, 'sysmap_shapeid');
672
673		DB::delete('sysmap_shape', ['sysmap_shapeid' => $shapeids]);
674	}
675
676	/**
677	 * Create link.
678	 *
679	 * @param array $links
680	 * @param array $links[0,...]['sysmapid']
681	 * @param array $links[0,...]['selementid1']
682	 * @param array $links[0,...]['selementid2']
683	 * @param array $links[0,...]['drawtype']
684	 * @param array $links[0,...]['color']
685	 *
686	 * @return array
687	 */
688	protected function createLinks(array $links) {
689		$links = zbx_toArray($links);
690
691		$this->checkLinkInput($links, __FUNCTION__);
692
693		$linkIds = DB::insert('sysmaps_links', $links);
694
695		return ['linkids' => $linkIds];
696	}
697
698	protected function updateLinks($links) {
699		$links = zbx_toArray($links);
700
701		$this->checkLinkInput($links, __FUNCTION__);
702
703		$udpateLinks = [];
704		foreach ($links as $link) {
705			$udpateLinks[] = ['values' => $link, 'where' => ['linkid' => $link['linkid']]];
706		}
707
708		DB::update('sysmaps_links', $udpateLinks);
709
710		return ['linkids' => zbx_objectValues($links, 'linkid')];
711	}
712
713	/**
714	 * Delete Link from map.
715	 *
716	 * @param array $links						multidimensional array with link objects
717	 * @param array $links[0, ...]['linkid']	link ID to delete
718	 *
719	 * @return array
720	 */
721	protected function deleteLinks($links) {
722		zbx_value2array($links);
723		$linkIds = zbx_objectValues($links, 'linkid');
724
725		$this->checkLinkInput($links, __FUNCTION__);
726
727		DB::delete('sysmaps_links', ['linkid' => $linkIds]);
728
729		return ['linkids' => $linkIds];
730	}
731
732	/**
733	 * Add link trigger to link (sysmap).
734	 *
735	 * @param array $links[0,...]['linkid']
736	 * @param array $links[0,...]['triggerid']
737	 * @param array $links[0,...]['drawtype']
738	 * @param array $links[0,...]['color']
739	 */
740	protected function createLinkTriggers($linkTriggers) {
741		$linkTriggers = zbx_toArray($linkTriggers);
742
743		$this->validateCreateLinkTriggers($linkTriggers);
744
745		$linkTriggerIds = DB::insert('sysmaps_link_triggers', $linkTriggers);
746
747		return ['linktriggerids' => $linkTriggerIds];
748	}
749
750	protected function validateCreateLinkTriggers(array $linkTriggers) {
751		$linkTriggerDbFields = [
752			'linkid' => null,
753			'triggerid' => null
754		];
755
756		$colorValidator = new CColorValidator();
757
758		foreach ($linkTriggers as $linkTrigger) {
759			if (!check_db_fields($linkTriggerDbFields, $linkTrigger)) {
760				self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
761			}
762
763			if (isset($linkTrigger['color']) && !$colorValidator->validate($linkTrigger['color'])) {
764				self::exception(ZBX_API_ERROR_PARAMETERS, $colorValidator->getError());
765			}
766		}
767	}
768
769	protected function updateLinkTriggers($linkTriggers) {
770		$linkTriggers = zbx_toArray($linkTriggers);
771		$this->validateUpdateLinkTriggers($linkTriggers);
772
773		$linkTriggerIds = zbx_objectValues($linkTriggers, 'linktriggerid');
774
775		$updateLinkTriggers = [];
776		foreach ($linkTriggers as $linkTrigger) {
777			$updateLinkTriggers[] = [
778				'values' => $linkTrigger,
779				'where' => ['linktriggerid' => $linkTrigger['linktriggerid']]
780			];
781		}
782
783		DB::update('sysmaps_link_triggers', $updateLinkTriggers);
784
785		return ['linktriggerids' => $linkTriggerIds];
786	}
787
788	protected function validateUpdateLinkTriggers(array $linkTriggers) {
789		$linkTriggerDbFields = ['linktriggerid' => null];
790
791		$colorValidator = new CColorValidator();
792
793		foreach ($linkTriggers as $linkTrigger) {
794			if (!check_db_fields($linkTriggerDbFields, $linkTrigger)) {
795				self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
796			}
797
798			if (isset($linkTrigger['color']) && !$colorValidator->validate($linkTrigger['color'])) {
799				self::exception(ZBX_API_ERROR_PARAMETERS, $colorValidator->getError());
800			}
801		}
802	}
803
804	protected function deleteLinkTriggers($linkTriggers) {
805		$linkTriggers = zbx_toArray($linkTriggers);
806		$this->validateDeleteLinkTriggers($linkTriggers);
807
808		$linkTriggerIds = zbx_objectValues($linkTriggers, 'linktriggerid');
809
810		DB::delete('sysmaps_link_triggers', ['linktriggerid' => $linkTriggerIds]);
811
812		return ['linktriggerids' => $linkTriggerIds];
813	}
814
815	protected function validateDeleteLinkTriggers(array $linkTriggers) {
816		$linktriggerDbFields = ['linktriggerid' => null];
817
818		foreach ($linkTriggers as $linkTrigger) {
819			if (!check_db_fields($linktriggerDbFields, $linkTrigger)) {
820				self::exception(ZBX_API_ERROR_PARAMETERS, _('Incorrect arguments passed to function.'));
821			}
822		}
823	}
824}
825