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