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