1<?php
2/*
3** Zabbix
4** Copyright (C) 2001-2021 Zabbix SIA
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19**/
20
21
22require_once dirname(__FILE__).'/../../include/blocks.inc.php';
23
24class CControllerDashboardView extends CControllerDashboardAbstract {
25
26	const DYNAMIC_ITEM_HOST_PROFILE_KEY = 'web.dashbrd.hostid';
27
28	private $dashboard;
29
30	protected function init() {
31		$this->disableSIDValidation();
32	}
33
34	protected function checkInput() {
35		$fields = [
36			'dashboardid' =>		'db dashboard.dashboardid',
37			'source_dashboardid' =>	'db dashboard.dashboardid',
38			'hostid' =>				'db hosts.hostid',
39			'new' =>				'in 1',
40			'cancel' =>				'in 1',
41			'from' =>				'range_time',
42			'to' =>					'range_time'
43		];
44
45		$ret = $this->validateInput($fields) && $this->validateTimeSelectorPeriod();
46
47		if (!$ret) {
48			$this->setResponse(new CControllerResponseFatal());
49		}
50
51		return $ret;
52	}
53
54	protected function checkPermissions() {
55		if ($this->getUserType() < USER_TYPE_ZABBIX_USER) {
56			return false;
57		}
58
59		if ($this->hasInput('hostid') && $this->getInput('hostid') != 0) {
60			$hosts = API::Host()->get([
61				'output' => [],
62				'hostids' => [$this->getInput('hostid')]
63			]);
64
65			if (!$hosts) {
66				return false;
67			}
68		}
69
70		return true;
71	}
72
73	protected function doAction() {
74		list($this->dashboard, $error) = $this->getDashboard();
75
76		if ($error !== null) {
77			$this->setResponse(new CControllerResponseData(['error' => $error]));
78
79			return;
80		}
81		elseif ($this->dashboard === null) {
82			$this->setResponse(new CControllerResponseRedirect((new CUrl('zabbix.php'))
83				->setArgument('action', 'dashboard.list')
84				->setArgument('page', $this->hasInput('cancel') ? CPagerHelper::loadPage('dashboard.list', null) : null)
85			));
86
87			return;
88		}
89		else {
90			$dashboard = $this->dashboard;
91			unset($dashboard['widgets']);
92
93			$timeselector_options = [
94				'profileIdx' => 'web.dashbrd.filter',
95				'profileIdx2' => $this->dashboard['dashboardid'],
96				'from' => $this->hasInput('from') ? $this->getInput('from') : null,
97				'to' => $this->hasInput('to') ? $this->getInput('to') : null
98			];
99			updateTimeSelectorPeriod($timeselector_options);
100
101			$widgets = self::getWidgets($this->dashboard['widgets']);
102
103			$data = [
104				'dashboard_edit_mode' => ($dashboard['dashboardid'] == 0),
105				'dashboard' => $dashboard,
106				'grid_widgets' => $widgets,
107				'widget_defaults' => CWidgetConfig::getDefaults(),
108				'show_timeselector' => self::showTimeSelector($widgets),
109				'active_tab' => CProfile::get('web.dashbrd.filter.active', 1),
110				'timeline' => getTimeSelectorPeriod($timeselector_options)
111			];
112
113			$data['timeControlData'] = [
114				'mainObject' => 1,
115				'onDashboard' => 1
116			];
117
118			if (self::hasDynamicWidgets($data['grid_widgets'])) {
119				$hostid = $this->getInput('hostid', CProfile::get(self::DYNAMIC_ITEM_HOST_PROFILE_KEY, 0));
120
121				$hosts = ($hostid > 0)
122					? CArrayHelper::renameObjectsKeys(API::Host()->get([
123						'output' => ['hostid', 'name'],
124						'hostids' => [$hostid]
125					]), ['hostid' => 'id'])
126					: [];
127
128				$data['dynamic'] = [
129					'has_dynamic_widgets' => true,
130					'host' => $hosts ? $hosts[0] : null
131				];
132			}
133			else {
134				$data['dynamic'] = [
135					'has_dynamic_widgets' => false,
136					'host' => null
137				];
138			}
139
140			$response = new CControllerResponseData($data);
141			$response->setTitle(_('Dashboard'));
142			$this->setResponse($response);
143		}
144	}
145
146	/**
147	 * Get dashboard data from API.
148	 *
149	 * @return array|null
150	 */
151	private function getDashboard() {
152		$dashboard = null;
153		$error = null;
154
155		if ($this->hasInput('new')) {
156			$dashboard = self::getNewDashboard();
157		}
158		elseif ($this->hasInput('source_dashboardid')) {
159			// Clone dashboard and show as new.
160			$dashboards = API::Dashboard()->get([
161				'output' => ['name', 'private'],
162				'selectWidgets' => ['widgetid', 'type', 'name', 'view_mode', 'x', 'y', 'width', 'height', 'fields'],
163				'selectUsers' => ['userid', 'permission'],
164				'selectUserGroups' => ['usrgrpid', 'permission'],
165				'dashboardids' => $this->getInput('source_dashboardid')
166			]);
167
168			if ($dashboards) {
169				$dashboard = self::getNewDashboard();
170				$dashboard['name'] = $dashboards[0]['name'];
171				$dashboard['widgets'] = $this->unsetInaccessibleFields($dashboards[0]['widgets']);
172				$dashboard['sharing'] = [
173					'private' => $dashboards[0]['private'],
174					'users' => $dashboards[0]['users'],
175					'userGroups' => $dashboards[0]['userGroups']
176				];
177			}
178			else {
179				$error = _('No permissions to referred object or it does not exist!');
180			}
181		}
182		else {
183			// Getting existing dashboard.
184			$dashboardid = $this->getInput('dashboardid', CProfile::get('web.dashbrd.dashboardid', 0));
185
186			if ($dashboardid == 0 && CProfile::get('web.dashbrd.list_was_opened') != 1) {
187				// Get first available dashboard that user has read permissions.
188				$dashboards = API::Dashboard()->get([
189					'output' => ['dashboardid'],
190					'sortfield' => 'name',
191					'limit' => 1
192				]);
193
194				if ($dashboards) {
195					$dashboardid = $dashboards[0]['dashboardid'];
196				}
197			}
198
199			if ($dashboardid != 0) {
200				$dashboards = API::Dashboard()->get([
201					'output' => ['dashboardid', 'name', 'userid'],
202					'selectWidgets' => ['widgetid', 'type', 'name', 'view_mode', 'x', 'y', 'width', 'height', 'fields'],
203					'dashboardids' => $dashboardid,
204					'preservekeys' => true
205				]);
206
207				if ($dashboards) {
208					$this->prepareEditableFlag($dashboards);
209					$dashboard = array_shift($dashboards);
210					$dashboard['owner'] = self::getOwnerData($dashboard['userid']);
211
212					CProfile::update('web.dashbrd.dashboardid', $dashboardid, PROFILE_TYPE_ID);
213				}
214				elseif ($this->hasInput('dashboardid')) {
215					$error = _('No permissions to referred object or it does not exist!');
216				}
217				else {
218					// In case if previous dashboard is deleted, show dashboard list.
219				}
220			}
221		}
222
223		return [$dashboard, $error];
224	}
225
226	/**
227	 * Get new dashboard.
228	 *
229	 * @return array
230	 */
231	public static function getNewDashboard() {
232		return [
233			'dashboardid' => 0,
234			'name' => _('New dashboard'),
235			'editable' => true,
236			'widgets' => [],
237			'owner' => self::getOwnerData(CWebUser::$data['userid'])
238		];
239	}
240
241	/**
242	 * Get owner details.
243	 *
244	 * @param string $userid
245	 *
246	 * @return array
247	 */
248	public static function getOwnerData($userid) {
249		$owner = ['id' => $userid, 'name' => _('Inaccessible user')];
250
251		$users = API::User()->get([
252			'output' => ['name', 'surname', 'alias'],
253			'userids' => $userid
254		]);
255		if ($users) {
256			$owner['name'] = getUserFullname($users[0]);
257		}
258
259		return $owner;
260	}
261
262	/**
263	 * Get widgets for dashboard.
264	 *
265	 * @static
266	 *
267	 * @return array
268	 */
269	private static function getWidgets($widgets) {
270		$grid_widgets = [];
271
272		if ($widgets) {
273			CArrayHelper::sort($widgets, ['y', 'x']);
274
275			foreach ($widgets as $widget) {
276				if (!in_array($widget['type'], array_keys(CWidgetConfig::getKnownWidgetTypes()))) {
277					continue;
278				}
279
280				$widgetid = $widget['widgetid'];
281				$fields_orig = self::convertWidgetFields($widget['fields']);
282
283				// Transforms corrupted data to default values.
284				$widget_form = CWidgetConfig::getForm($widget['type'], json_encode($fields_orig));
285				$widget_form->validate();
286				$fields = $widget_form->getFieldsData();
287
288				$rf_rate = ($fields['rf_rate'] == -1)
289					? CWidgetConfig::getDefaultRfRate($widget['type'])
290					: $fields['rf_rate'];
291
292				$grid_widgets[] = [
293					'widgetid' => $widgetid,
294					'type' => $widget['type'],
295					'header' => $widget['name'],
296					'view_mode' => $widget['view_mode'],
297					'pos' => [
298						'x' => (int) $widget['x'],
299						'y' => (int) $widget['y'],
300						'width' => (int) $widget['width'],
301						'height' => (int) $widget['height']
302					],
303					'rf_rate' => (int) CProfile::get('web.dashbrd.widget.rf_rate', $rf_rate, $widgetid),
304					'fields' => $fields_orig,
305					'configuration' => CWidgetConfig::getConfiguration($widget['type'], $fields, $widget['view_mode'])
306				];
307			}
308		}
309
310		return $grid_widgets;
311	}
312
313	/**
314	 * Converts fields, received from API to key/value format.
315	 *
316	 * @param array $fields  fields as received from API
317	 *
318	 * @static
319	 *
320	 * @return array
321	 */
322	private static function convertWidgetFields($fields) {
323		$ret = [];
324		foreach ($fields as $field) {
325			if (array_key_exists($field['name'], $ret)) {
326				$ret[$field['name']] = (array) $ret[$field['name']];
327				$ret[$field['name']][] = $field['value'];
328			}
329			else {
330				$ret[$field['name']] = $field['value'];
331			}
332		}
333
334		return $ret;
335	}
336
337	/**
338	 * Checks, if any of widgets has checked dynamic field.
339	 *
340	 * @param array $grid_widgets
341	 *
342	 * @static
343	 *
344	 * @return bool
345	 */
346	private static function hasDynamicWidgets($grid_widgets) {
347		foreach ($grid_widgets as $widget) {
348			if (array_key_exists('dynamic', $widget['fields']) && $widget['fields']['dynamic'] == 1) {
349				return true;
350			}
351		}
352
353		return false;
354	}
355
356	/**
357	 * Checks, if any of widgets needs time selector.
358	 *
359	 * @param array $widgets
360	 *
361	 * @static
362	 *
363	 * @return bool
364	 */
365	private static function showTimeSelector(array $widgets) {
366		foreach ($widgets as $widget) {
367			if (CWidgetConfig::usesTimeSelector($widget)) {
368				return true;
369			}
370		}
371		return false;
372	}
373}
374