You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							511 lines
						
					
					
						
							19 KiB
						
					
					
				
			
		
		
	
	
							511 lines
						
					
					
						
							19 KiB
						
					
					
				| <?php
 | |
| /*
 | |
| ** Zabbix
 | |
| ** Copyright (C) 2001-2023 Zabbix SIA
 | |
| **
 | |
| ** This program is free software; you can redistribute it and/or modify
 | |
| ** it under the terms of the GNU General Public License as published by
 | |
| ** the Free Software Foundation; either version 2 of the License, or
 | |
| ** (at your option) any later version.
 | |
| **
 | |
| ** This program is distributed in the hope that it will be useful,
 | |
| ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
| ** GNU General Public License for more details.
 | |
| **
 | |
| ** You should have received a copy of the GNU General Public License
 | |
| ** along with this program; if not, write to the Free Software
 | |
| ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 | |
| **/
 | |
| 
 | |
| 
 | |
| /**
 | |
|  * Template dashboards API implementation.
 | |
|  */
 | |
| class CTemplateDashboard extends CDashboardGeneral {
 | |
| 
 | |
| 	public const ACCESS_RULES = [
 | |
| 		'get' => ['min_user_type' => USER_TYPE_ZABBIX_USER],
 | |
| 		'create' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN],
 | |
| 		'update' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN],
 | |
| 		'delete' => ['min_user_type' => USER_TYPE_ZABBIX_ADMIN]
 | |
| 	];
 | |
| 
 | |
| 	/**
 | |
| 	 * @param array $options
 | |
| 	 *
 | |
| 	 * @throws APIException if the input is invalid.
 | |
| 	 *
 | |
| 	 * @return array|int
 | |
| 	 */
 | |
| 	public function get(array $options = []) {
 | |
| 		$api_input_rules = ['type' => API_OBJECT, 'fields' => [
 | |
| 			// filter
 | |
| 			'dashboardids' =>			['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE, 'default' => null],
 | |
| 			'templateids' =>			['type' => API_IDS, 'flags' => API_ALLOW_NULL | API_NORMALIZE, 'default' => null],
 | |
| 			'filter' =>					['type' => API_FILTER, 'flags' => API_ALLOW_NULL, 'default' => null, 'fields' => ['uuid', 'dashboardid', 'name', 'templateid', 'display_period', 'auto_start']],
 | |
| 			'search' =>					['type' => API_FILTER, 'flags' => API_ALLOW_NULL, 'default' => null, 'fields' => ['name']],
 | |
| 			'searchByAny' =>			['type' => API_BOOLEAN, 'default' => false],
 | |
| 			'startSearch' =>			['type' => API_FLAG, 'default' => false],
 | |
| 			'excludeSearch' =>			['type' => API_FLAG, 'default' => false],
 | |
| 			'searchWildcardsEnabled' =>	['type' => API_BOOLEAN, 'default' => false],
 | |
| 			// output
 | |
| 			'output' =>					['type' => API_OUTPUT, 'in' => implode(',', ['dashboardid', 'name', 'templateid', 'display_period', 'auto_start', 'uuid']), 'default' => API_OUTPUT_EXTEND],
 | |
| 			'selectPages' =>			['type' => API_OUTPUT, 'flags' => API_ALLOW_NULL, 'in' => implode(',', ['dashboard_pageid', 'name', 'display_period', 'widgets']), 'default' => null],
 | |
| 			'countOutput' =>			['type' => API_FLAG, 'default' => false],
 | |
| 			'groupCount' =>				['type' => API_FLAG, 'default' => false],
 | |
| 			// sort and limit
 | |
| 			'sortfield' =>				['type' => API_STRINGS_UTF8, 'flags' => API_NORMALIZE, 'in' => implode(',', $this->sortColumns), 'uniq' => true, 'default' => []],
 | |
| 			'sortorder' =>				['type' => API_SORTORDER, 'default' => []],
 | |
| 			'limit' =>					['type' => API_INT32, 'flags' => API_ALLOW_NULL, 'in' => '1:'.ZBX_MAX_INT32, 'default' => null],
 | |
| 			// flags
 | |
| 			'editable' =>				['type' => API_BOOLEAN, 'default' => false],
 | |
| 			'preservekeys' =>			['type' => API_BOOLEAN, 'default' => false]
 | |
| 		]];
 | |
| 
 | |
| 		if (!CApiInputValidator::validate($api_input_rules, $options, '/', $error)) {
 | |
| 			self::exception(ZBX_API_ERROR_PARAMETERS, $error);
 | |
| 		}
 | |
| 
 | |
| 		$sql_parts = [
 | |
| 			'select' => ['dashboard' => 'd.dashboardid'],
 | |
| 			'from' => ['dashboard' => 'dashboard d'],
 | |
| 			'where' => [],
 | |
| 			'order' => [],
 | |
| 			'group' => []
 | |
| 		];
 | |
| 
 | |
| 		if (!$options['countOutput'] && $options['output'] === API_OUTPUT_EXTEND) {
 | |
| 			$options['output'] = $this->getTableSchema()['fields'];
 | |
| 			unset($options['output']['userid'], $options['output']['private']);
 | |
| 			$options['output'] = array_keys($options['output']);
 | |
| 		}
 | |
| 
 | |
| 		$options['groupCount'] = ($options['groupCount'] && $options['countOutput']);
 | |
| 
 | |
| 		// permissions
 | |
| 		if (in_array(self::$userData['type'], [USER_TYPE_ZABBIX_USER, USER_TYPE_ZABBIX_ADMIN])) {
 | |
| 			if ($options['editable']) {
 | |
| 				if ($options['templateids'] !== null) {
 | |
| 					$options['templateids'] = array_keys(API::Template()->get([
 | |
| 						'output' => [],
 | |
| 						'templateids' => $options['templateids'],
 | |
| 						'editable' => true,
 | |
| 						'preservekeys' => true
 | |
| 					]));
 | |
| 				}
 | |
| 				else {
 | |
| 					$user_groups = getUserGroupsByUserId(self::$userData['userid']);
 | |
| 
 | |
| 					$sql_parts['where'][] = 'EXISTS ('.
 | |
| 						'SELECT NULL'.
 | |
| 						' FROM hosts_groups hgg'.
 | |
| 							' JOIN rights r'.
 | |
| 								' ON r.id=hgg.groupid'.
 | |
| 									' AND '.dbConditionInt('r.groupid', $user_groups).
 | |
| 						' WHERE d.templateid=hgg.hostid'.
 | |
| 						' GROUP BY hgg.hostid'.
 | |
| 						' HAVING MIN(r.permission)>'.PERM_DENY.
 | |
| 							' AND MAX(r.permission)>='.PERM_READ_WRITE.
 | |
| 						')';
 | |
| 				}
 | |
| 			}
 | |
| 			else {
 | |
| 				$user_groups = getUserGroupsByUserId(self::$userData['userid']);
 | |
| 
 | |
| 				// Select direct templates of all hosts accessible to the current user.
 | |
| 				$db_host_templates = DBselect(
 | |
| 					'SELECT DISTINCT ht.templateid FROM hosts_templates ht'.
 | |
| 					' WHERE ht.hostid IN ('.
 | |
| 						'SELECT h.hostid FROM hosts h'.
 | |
| 						' WHERE h.flags IN ('.ZBX_FLAG_DISCOVERY_NORMAL.','.ZBX_FLAG_DISCOVERY_CREATED.')'.
 | |
| 							' AND h.status IN ('.HOST_STATUS_MONITORED.','.HOST_STATUS_NOT_MONITORED.')'.
 | |
| 							' AND EXISTS ('.
 | |
| 								'SELECT NULL'.
 | |
| 								' FROM hosts_groups hgg'.
 | |
| 									' JOIN rights r'.
 | |
| 										' ON r.id=hgg.groupid'.
 | |
| 											' AND '.dbConditionId('r.groupid', $user_groups).
 | |
| 								' WHERE h.hostid=hgg.hostid'.
 | |
| 								' GROUP BY hgg.hostid'.
 | |
| 								' HAVING MIN(r.permission)>'.PERM_DENY.
 | |
| 									' AND MAX(r.permission)>='.PERM_READ.
 | |
| 							')'.
 | |
| 					')'
 | |
| 				);
 | |
| 
 | |
| 				$templateids = [];
 | |
| 
 | |
| 				while ($db_host_template = DBfetch($db_host_templates)) {
 | |
| 					$templateids[$db_host_template['templateid']] = true;
 | |
| 				}
 | |
| 
 | |
| 				$all_templateids = [];
 | |
| 
 | |
| 				while ($templateids) {
 | |
| 					$all_templateids += $templateids;
 | |
| 
 | |
| 					$db_parent_templates = DBselect(
 | |
| 						'SELECT ht.templateid'.
 | |
| 						' FROM hosts_templates ht'.
 | |
| 						' WHERE '.dbConditionId('ht.hostid', array_keys($templateids))
 | |
| 					);
 | |
| 
 | |
| 					$templateids = [];
 | |
| 
 | |
| 					while ($db_parent_template = DBfetch($db_parent_templates)) {
 | |
| 						$templateids[$db_parent_template['templateid']] = true;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				$options['templateids'] = $options['templateids'] !== null
 | |
| 					? array_intersect($options['templateids'], array_keys($all_templateids))
 | |
| 					: array_keys($all_templateids);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		// dashboardids
 | |
| 		if ($options['dashboardids'] !== null) {
 | |
| 			$sql_parts['where'][] = dbConditionInt('d.dashboardid', $options['dashboardids']);
 | |
| 		}
 | |
| 
 | |
| 		// dashboardids
 | |
| 		$sql_parts['where'][] = ($options['templateids'] !== null)
 | |
| 			? dbConditionInt('d.templateid', $options['templateids'])
 | |
| 			: 'd.templateid IS NOT NULL';
 | |
| 
 | |
| 		// filter
 | |
| 		if ($options['filter'] !== null) {
 | |
| 			$this->dbFilter('dashboard d', $options, $sql_parts);
 | |
| 		}
 | |
| 
 | |
| 		// search
 | |
| 		if ($options['search'] !== null) {
 | |
| 			zbx_db_search('dashboard d', $options, $sql_parts);
 | |
| 		}
 | |
| 
 | |
| 		if ($options['groupCount']) {
 | |
| 			$sql_parts['group']['templateid'] = 'd.templateid';
 | |
| 		}
 | |
| 
 | |
| 		$sql_parts = $this->applyQueryOutputOptions($this->tableName(), $this->tableAlias(), $options, $sql_parts);
 | |
| 		$sql_parts = $this->applyQuerySortOptions($this->tableName(), $this->tableAlias(), $options, $sql_parts);
 | |
| 
 | |
| 		$result = DBselect(self::createSelectQueryFromParts($sql_parts), $options['limit']);
 | |
| 
 | |
| 		$db_dashboards = [];
 | |
| 
 | |
| 		while ($row = DBfetch($result)) {
 | |
| 			if ($options['countOutput']) {
 | |
| 				if ($options['groupCount']) {
 | |
| 					$db_dashboards[] = $row;
 | |
| 				}
 | |
| 				else {
 | |
| 					return $row['rowscount'];
 | |
| 				}
 | |
| 			}
 | |
| 			else {
 | |
| 				$db_dashboards[$row['dashboardid']] = $row;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ($db_dashboards && !$options['groupCount']) {
 | |
| 			$db_dashboards = $this->addRelatedObjects($options, $db_dashboards);
 | |
| 			$db_dashboards = $this->unsetExtraFields($db_dashboards, ['dashboardid'], $options['output']);
 | |
| 
 | |
| 			if (!$options['preservekeys']) {
 | |
| 				$db_dashboards = array_values($db_dashboards);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		return $db_dashboards;
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param array $dashboards
 | |
| 	 *
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public function create(array $dashboards): array {
 | |
| 		$this->validateCreate($dashboards);
 | |
| 
 | |
| 		$ins_dashboards = [];
 | |
| 
 | |
| 		foreach ($dashboards as $dashboard) {
 | |
| 			unset($dashboard['pages']);
 | |
| 			$ins_dashboards[] = $dashboard;
 | |
| 		}
 | |
| 
 | |
| 		$dashboardids = DB::insert('dashboard', $ins_dashboards);
 | |
| 
 | |
| 		foreach ($dashboards as $index => &$dashboard) {
 | |
| 			$dashboard['dashboardid'] = $dashboardids[$index];
 | |
| 		}
 | |
| 		unset($dashboard);
 | |
| 
 | |
| 		$this->updatePages($dashboards);
 | |
| 
 | |
| 		self::addAuditLog(CAudit::ACTION_ADD, CAudit::RESOURCE_TEMPLATE_DASHBOARD, $dashboards);
 | |
| 
 | |
| 		return ['dashboardids' => $dashboardids];
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param array $dashboards
 | |
| 	 *
 | |
| 	 * @return array
 | |
| 	 */
 | |
| 	public function update(array $dashboards): array {
 | |
| 		$this->validateUpdate($dashboards, $db_dashboards);
 | |
| 
 | |
| 		$upd_dashboards = [];
 | |
| 
 | |
| 		foreach ($dashboards as $dashboard) {
 | |
| 			$upd_dashboard = DB::getUpdatedValues('dashboard', $dashboard, $db_dashboards[$dashboard['dashboardid']]);
 | |
| 
 | |
| 			if ($upd_dashboard) {
 | |
| 				$upd_dashboards[] = [
 | |
| 					'values' => $upd_dashboard,
 | |
| 					'where' => ['dashboardid' => $dashboard['dashboardid']]
 | |
| 				];
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if ($upd_dashboards) {
 | |
| 			DB::update('dashboard', $upd_dashboards);
 | |
| 		}
 | |
| 
 | |
| 		$this->updatePages($dashboards, $db_dashboards);
 | |
| 
 | |
| 		self::addAuditLog(CAudit::ACTION_UPDATE, CAudit::RESOURCE_TEMPLATE_DASHBOARD, $dashboards, $db_dashboards);
 | |
| 
 | |
| 		return ['dashboardids' => array_column($dashboards, 'dashboardid')];
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param array $dashboards
 | |
| 	 *
 | |
| 	 * @throws APIException if the input is invalid.
 | |
| 	 */
 | |
| 	protected function validateCreate(array &$dashboards): void {
 | |
| 		$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['uuid'], ['templateid', 'name']], 'fields' => [
 | |
| 			'uuid' =>			['type' => API_UUID],
 | |
| 			'name' =>			['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('dashboard', 'name')],
 | |
| 			'templateid' =>		['type' => API_ID, 'flags' => API_REQUIRED],
 | |
| 			'display_period' =>	['type' => API_INT32, 'in' => implode(',', DASHBOARD_DISPLAY_PERIODS)],
 | |
| 			'auto_start' =>		['type' => API_INT32, 'in' => '0,1'],
 | |
| 			'pages' =>			['type' => API_OBJECTS, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DASHBOARD_MAX_PAGES, 'fields' => [
 | |
| 				'name' =>			['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('dashboard_page', 'name')],
 | |
| 				'display_period' =>	['type' => API_INT32, 'in' => implode(',', array_merge([0], DASHBOARD_DISPLAY_PERIODS))],
 | |
| 				'widgets' =>		['type' => API_OBJECTS, 'fields' => [
 | |
| 					'type' =>			['type' => API_STRING_UTF8, 'flags' => API_REQUIRED | API_NOT_EMPTY, 'length' => DB::getFieldLength('widget', 'type')],
 | |
| 					'name' =>			['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('widget', 'name')],
 | |
| 					'view_mode' =>		['type' => API_INT32, 'in' => implode(',', [ZBX_WIDGET_VIEW_MODE_NORMAL, ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER])],
 | |
| 					'x' =>				['type' => API_INT32, 'in' => '0:'.(DASHBOARD_MAX_COLUMNS - 1)],
 | |
| 					'y' =>				['type' => API_INT32, 'in' => '0:'.(DASHBOARD_MAX_ROWS - 2)],
 | |
| 					'width' =>			['type' => API_INT32, 'in' => '1:'.DASHBOARD_MAX_COLUMNS],
 | |
| 					'height' =>			['type' => API_INT32, 'in' => '2:'.DASHBOARD_WIDGET_MAX_ROWS],
 | |
| 					'fields' =>			['type' => API_OBJECTS, 'fields' => [
 | |
| 						'type' =>			['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', array_keys(self::WIDGET_FIELD_TYPE_COLUMNS))],
 | |
| 						'name' =>			['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('widget_field', 'name'), 'default' => DB::getDefault('widget_field', 'name')],
 | |
| 						'value' =>			['type' => API_MULTIPLE, 'flags' => API_REQUIRED, 'rules' => [
 | |
| 												['if' => ['field' => 'type', 'in' => implode(',', [ZBX_WIDGET_FIELD_TYPE_INT32])], 'type' => API_INT32],
 | |
| 												['if' => ['field' => 'type', 'in' => implode(',', [ZBX_WIDGET_FIELD_TYPE_STR])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('widget_field', 'value_str')],
 | |
| 												['if' => ['field' => 'type', 'in' => implode(',', array_keys(self::WIDGET_FIELD_TYPE_COLUMNS_FK))], 'type' => API_ID]
 | |
| 						]]
 | |
| 					]]
 | |
| 				]]
 | |
| 			]]
 | |
| 		]];
 | |
| 
 | |
| 		if (!CApiInputValidator::validate($api_input_rules, $dashboards, '/', $error)) {
 | |
| 			self::exception(ZBX_API_ERROR_PARAMETERS, $error);
 | |
| 		}
 | |
| 
 | |
| 		$templateids = array_column($dashboards, 'templateid', 'templateid');
 | |
| 
 | |
| 		$db_templates_count = API::Template()->get([
 | |
| 			'countOutput' => true,
 | |
| 			'templateids' => $templateids
 | |
| 		]);
 | |
| 
 | |
| 		if ($db_templates_count != count($templateids)) {
 | |
| 			self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
 | |
| 		}
 | |
| 
 | |
| 		self::addUuid($dashboards);
 | |
| 
 | |
| 		self::checkUuidDuplicates($dashboards);
 | |
| 		$this->checkDuplicates($dashboards);
 | |
| 		$this->checkWidgets($dashboards);
 | |
| 		$this->checkWidgetFields($dashboards);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Add the UUID to those of the given template dashboards that don't have the 'uuid' parameter set.
 | |
| 	 *
 | |
| 	 * @param array $dashboards
 | |
| 	 */
 | |
| 	private static function addUuid(array &$dashboards): void {
 | |
| 		foreach ($dashboards as &$dashboard) {
 | |
| 			if (!array_key_exists('uuid', $dashboard)) {
 | |
| 				$dashboard['uuid'] = generateUuidV4();
 | |
| 			}
 | |
| 		}
 | |
| 		unset($dashboard);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Verify template dashboard UUIDs are not repeated.
 | |
| 	 *
 | |
| 	 * @param array      $dashboards
 | |
| 	 * @param array|null $db_dashboards
 | |
| 	 *
 | |
| 	 * @throws APIException
 | |
| 	 */
 | |
| 	private static function checkUuidDuplicates(array $dashboards, array $db_dashboards = null): void {
 | |
| 		$dashboard_indexes = [];
 | |
| 
 | |
| 		foreach ($dashboards as $i => $dashboard) {
 | |
| 			if (!array_key_exists('uuid', $dashboard)) {
 | |
| 				continue;
 | |
| 			}
 | |
| 
 | |
| 			if ($db_dashboards === null || $dashboard['uuid'] !== $db_dashboards[$dashboard['dashboardid']]['uuid']) {
 | |
| 				$dashboard_indexes[$dashboard['uuid']] = $i;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (!$dashboard_indexes) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$duplicates = DB::select('dashboard', [
 | |
| 			'output' => ['uuid'],
 | |
| 			'filter' => [
 | |
| 				'uuid' => array_keys($dashboard_indexes)
 | |
| 			],
 | |
| 			'limit' => 1
 | |
| 		]);
 | |
| 
 | |
| 		if ($duplicates) {
 | |
| 			self::exception(ZBX_API_ERROR_PARAMETERS,
 | |
| 				_s('Invalid parameter "%1$s": %2$s.', '/'.($dashboard_indexes[$duplicates[0]['uuid']] + 1),
 | |
| 					_('template dashboard with the same UUID already exists')
 | |
| 				)
 | |
| 			);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * @param array      $dashboards
 | |
| 	 * @param array|null $db_dashboards
 | |
| 	 *
 | |
| 	 * @throws APIException if the input is invalid.
 | |
| 	 */
 | |
| 	protected function validateUpdate(array &$dashboards, array &$db_dashboards = null): void {
 | |
| 		$api_input_rules = ['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY | API_NORMALIZE, 'uniq' => [['uuid'], ['dashboardid']], 'fields' => [
 | |
| 			'uuid' => 				['type' => API_UUID],
 | |
| 			'dashboardid' =>		['type' => API_ID, 'flags' => API_REQUIRED],
 | |
| 			'name' =>				['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('dashboard', 'name')],
 | |
| 			'display_period' =>		['type' => API_INT32, 'in' => implode(',', DASHBOARD_DISPLAY_PERIODS)],
 | |
| 			'auto_start' =>			['type' => API_INT32, 'in' => '0,1'],
 | |
| 			'pages' =>				['type' => API_OBJECTS, 'flags' => API_NOT_EMPTY, 'uniq' => [['dashboard_pageid']], 'length' => DASHBOARD_MAX_PAGES, 'fields' => [
 | |
| 				'dashboard_pageid' =>	['type' => API_ID],
 | |
| 				'name' =>				['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('dashboard_page', 'name')],
 | |
| 				'display_period' =>		['type' => API_INT32, 'in' => implode(',', array_merge([0], DASHBOARD_DISPLAY_PERIODS))],
 | |
| 				'widgets' =>			['type' => API_OBJECTS, 'uniq' => [['widgetid']], 'fields' => [
 | |
| 					'widgetid' =>			['type' => API_ID],
 | |
| 					'type' =>				['type' => API_STRING_UTF8, 'flags' => API_NOT_EMPTY, 'length' => DB::getFieldLength('widget', 'type')],
 | |
| 					'name' =>				['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('widget', 'name')],
 | |
| 					'view_mode' =>			['type' => API_INT32, 'in' => implode(',', [ZBX_WIDGET_VIEW_MODE_NORMAL, ZBX_WIDGET_VIEW_MODE_HIDDEN_HEADER])],
 | |
| 					'x' =>					['type' => API_INT32, 'in' => '0:'.(DASHBOARD_MAX_COLUMNS - 1)],
 | |
| 					'y' =>					['type' => API_INT32, 'in' => '0:'.(DASHBOARD_MAX_ROWS - 2)],
 | |
| 					'width' =>				['type' => API_INT32, 'in' => '1:'.DASHBOARD_MAX_COLUMNS],
 | |
| 					'height' =>				['type' => API_INT32, 'in' => '2:'.DASHBOARD_WIDGET_MAX_ROWS],
 | |
| 					'fields' =>				['type' => API_OBJECTS, 'fields' => [
 | |
| 						'type' =>				['type' => API_INT32, 'flags' => API_REQUIRED, 'in' => implode(',', array_keys(self::WIDGET_FIELD_TYPE_COLUMNS))],
 | |
| 						'name' =>				['type' => API_STRING_UTF8, 'length' => DB::getFieldLength('widget_field', 'name'), 'default' => DB::getDefault('widget_field', 'name')],
 | |
| 						'value' =>				['type' => API_MULTIPLE, 'flags' => API_REQUIRED, 'rules' => [
 | |
| 													['if' => ['field' => 'type', 'in' => implode(',', [ZBX_WIDGET_FIELD_TYPE_INT32])], 'type' => API_INT32],
 | |
| 													['if' => ['field' => 'type', 'in' => implode(',', [ZBX_WIDGET_FIELD_TYPE_STR])], 'type' => API_STRING_UTF8, 'length' => DB::getFieldLength('widget_field', 'value_str')],
 | |
| 													['if' => ['field' => 'type', 'in' => implode(',', array_keys(self::WIDGET_FIELD_TYPE_COLUMNS_FK))], 'type' => API_ID]
 | |
| 						]]
 | |
| 					]]
 | |
| 				]]
 | |
| 			]]
 | |
| 		]];
 | |
| 
 | |
| 		if (!CApiInputValidator::validate($api_input_rules, $dashboards, '/', $error)) {
 | |
| 			self::exception(ZBX_API_ERROR_PARAMETERS, $error);
 | |
| 		}
 | |
| 
 | |
| 		$db_dashboards = $this->get([
 | |
| 			'output' => ['uuid', 'dashboardid', 'name', 'templateid', 'display_period', 'auto_start'],
 | |
| 			'dashboardids' => array_column($dashboards, 'dashboardid'),
 | |
| 			'editable' => true,
 | |
| 			'preservekeys' => true
 | |
| 		]);
 | |
| 
 | |
| 		if (count($db_dashboards) != count($dashboards)) {
 | |
| 			self::exception(ZBX_API_ERROR_PERMISSIONS, _('No permissions to referred object or it does not exist!'));
 | |
| 		}
 | |
| 
 | |
| 		// Copy original dashboard names and templateids when not specified (for validation and error reporting).
 | |
| 		$dashboards = $this->extendObjectsByKey($dashboards, $db_dashboards, 'dashboardid', ['name', 'templateid']);
 | |
| 
 | |
| 		$api_input_rules = ['type' => API_OBJECTS, 'uniq' => [['name', 'templateid']], 'fields' => [
 | |
| 			'name' =>		['type' => API_STRING_UTF8],
 | |
| 			'templateid' =>	['type' => API_ID]
 | |
| 		]];
 | |
| 
 | |
| 		if (!CApiInputValidator::validateUniqueness($api_input_rules, $dashboards, '/', $error)) {
 | |
| 			self::exception(ZBX_API_ERROR_PARAMETERS, $error);
 | |
| 		}
 | |
| 
 | |
| 		// Add the existing pages, widgets and widget fields to $db_dashboards.
 | |
| 		$this->addAffectedObjects($dashboards, $db_dashboards);
 | |
| 
 | |
| 		// Check ownership of the referenced pages and widgets.
 | |
| 		$this->checkReferences($dashboards, $db_dashboards);
 | |
| 
 | |
| 		self::checkUuidDuplicates($dashboards, $db_dashboards);
 | |
| 		$this->checkDuplicates($dashboards, $db_dashboards);
 | |
| 		$this->checkWidgets($dashboards, $db_dashboards);
 | |
| 		$this->checkWidgetFields($dashboards, $db_dashboards);
 | |
| 	}
 | |
| 
 | |
| 	/**
 | |
| 	 * Check for unique dashboard names per template.
 | |
| 	 *
 | |
| 	 * @param array      $dashboards
 | |
| 	 * @param array|null $db_dashboards
 | |
| 	 *
 | |
| 	 * @throws APIException if dashboard names are not unique.
 | |
| 	 */
 | |
| 	protected function checkDuplicates(array $dashboards, array $db_dashboards = null): void {
 | |
| 		$names_by_templateid = [];
 | |
| 
 | |
| 		foreach ($dashboards as $dashboard) {
 | |
| 			if ($db_dashboards === null || $dashboard['name'] !== $db_dashboards[$dashboard['dashboardid']]['name']) {
 | |
| 				$names_by_templateid[$dashboard['templateid']][] = $dashboard['name'];
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (!$names_by_templateid) {
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		$where = [];
 | |
| 		foreach ($names_by_templateid as $templateid => $names) {
 | |
| 			$where[] = '('.dbConditionId('d.templateid', [$templateid]).' AND '.dbConditionString('d.name', $names).')';
 | |
| 		}
 | |
| 
 | |
| 		$duplicate = DBfetch(DBselect('SELECT d.name FROM dashboard d WHERE '.implode(' OR ', $where), 1));
 | |
| 
 | |
| 		if ($duplicate) {
 | |
| 			self::exception(ZBX_API_ERROR_PARAMETERS, _s('Dashboard "%1$s" already exists.', $duplicate['name']));
 | |
| 		}
 | |
| 	}
 | |
| }
 |