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 22class CScreenBuilder { 23 24 /** 25 * Switch on/off flicker-free screens auto refresh. 26 * 27 * @var boolean 28 */ 29 public $isFlickerfree; 30 31 /** 32 * Page file. 33 * 34 * @var string 35 */ 36 public $pageFile; 37 38 /** 39 * Screen data 40 * 41 * @var array 42 */ 43 public $screen; 44 45 /** 46 * Display mode 47 * 48 * @var int 49 */ 50 public $mode; 51 52 /** 53 * @see Request timestamp 54 */ 55 public $timestamp; 56 57 /** 58 * Host id 59 * 60 * @var string 61 */ 62 public $hostid; 63 64 /** 65 * Profile table entity name #1 66 * 67 * @var string 68 */ 69 public $profileIdx; 70 71 /** 72 * Profile table record id belongs to #1 73 * 74 * @var int 75 */ 76 public $profileIdx2; 77 78 /** 79 * Time control timeline 80 * 81 * @var array 82 */ 83 public $timeline; 84 85 /** 86 * Init screen data. 87 * 88 * @param array $options 89 * @param boolean $options['isFlickerfree'] 90 * @param string $options['pageFile'] 91 * @param int $options['mode'] 92 * @param int $options['timestamp'] 93 * @param int $options['hostid'] 94 * @param string $options['profileIdx'] Profile idx value. 95 * @param int $options['profileIdx2'] Profile idx2 value. 96 * @param string $options['from'] Start time of selected time period. 97 * @param string $options['to'] End time of selected time period. 98 * @param array $options['screen'] 99 */ 100 public function __construct(array $options = []) { 101 $this->isFlickerfree = isset($options['isFlickerfree']) ? $options['isFlickerfree'] : true; 102 $this->mode = isset($options['mode']) ? $options['mode'] : SCREEN_MODE_SLIDESHOW; 103 $this->timestamp = !empty($options['timestamp']) ? $options['timestamp'] : time(); 104 $this->hostid = !empty($options['hostid']) ? $options['hostid'] : null; 105 106 // get page file 107 if (!empty($options['pageFile'])) { 108 $this->pageFile = $options['pageFile']; 109 } 110 else { 111 global $page; 112 $this->pageFile = $page['file']; 113 } 114 115 // get screen 116 if (!empty($options['screen'])) { 117 $this->screen = $options['screen']; 118 } 119 elseif (array_key_exists('screenid', $options) && $options['screenid'] > 0) { 120 $this->screen = API::Screen()->get([ 121 'screenids' => $options['screenid'], 122 'output' => API_OUTPUT_EXTEND, 123 'selectScreenItems' => API_OUTPUT_EXTEND, 124 'editable' => ($this->mode == SCREEN_MODE_EDIT) 125 ]); 126 127 if (!empty($this->screen)) { 128 $this->screen = reset($this->screen); 129 } 130 else { 131 access_deny(); 132 } 133 } 134 135 // calculate time 136 $this->profileIdx = !empty($options['profileIdx']) ? $options['profileIdx'] : ''; 137 $this->profileIdx2 = !empty($options['profileIdx2']) ? $options['profileIdx2'] : null; 138 139 $this->timeline = getTimeSelectorPeriod([ 140 'profileIdx' => $this->profileIdx, 141 'profileIdx2' => $this->profileIdx2, 142 'from' => array_key_exists('from', $options) ? $options['from'] : null, 143 'to' => array_key_exists('to', $options) ? $options['to'] : null 144 ]); 145 } 146 147 /** 148 * Get particular screen object. 149 * 150 * @static 151 * 152 * @param array $options 153 * @param int $options['resourcetype'] 154 * @param int $options['screenitemid'] 155 * @param int $options['hostid'] 156 * @param array $options['screen'] 157 * @param int $options['screenid'] 158 * 159 * @return CScreenBase 160 */ 161 public static function getScreen(array $options = []) { 162 if (!array_key_exists('resourcetype', $options)) { 163 $options['resourcetype'] = null; 164 165 // get resourcetype from screenitem 166 if (!array_key_exists('screenitem', $options) && array_key_exists('screenitemid', $options)) { 167 if (array_key_exists('hostid', $options) && $options['hostid'] > 0) { 168 $options['screenitem'] = API::TemplateScreenItem()->get([ 169 'screenitemids' => $options['screenitemid'], 170 'hostids' => $options['hostid'], 171 'output' => API_OUTPUT_EXTEND 172 ]); 173 } 174 else { 175 $options['screenitem'] = API::ScreenItem()->get([ 176 'screenitemids' => $options['screenitemid'], 177 'output' => API_OUTPUT_EXTEND 178 ]); 179 } 180 $options['screenitem'] = reset($options['screenitem']); 181 } 182 183 if (is_array($options['screenitem']) && array_key_exists('screenitem', $options) 184 && array_key_exists('resourcetype', $options['screenitem'])) { 185 $options['resourcetype'] = $options['screenitem']['resourcetype']; 186 } 187 else { 188 return null; 189 } 190 } 191 192 if ($options['resourcetype'] === null) { 193 return null; 194 } 195 196 // get screen 197 switch ($options['resourcetype']) { 198 case SCREEN_RESOURCE_GRAPH: 199 return new CScreenGraph($options); 200 201 case SCREEN_RESOURCE_SIMPLE_GRAPH: 202 return new CScreenSimpleGraph($options); 203 204 case SCREEN_RESOURCE_MAP: 205 return new CScreenMap($options); 206 207 case SCREEN_RESOURCE_PLAIN_TEXT: 208 return new CScreenPlainText($options); 209 210 case SCREEN_RESOURCE_HOST_INFO: 211 return new CScreenHostsInfo($options); 212 213 case SCREEN_RESOURCE_TRIGGER_INFO: 214 return new CScreenTriggersInfo($options); 215 216 case SCREEN_RESOURCE_SERVER_INFO: 217 return new CScreenServerInfo($options); 218 219 case SCREEN_RESOURCE_CLOCK: 220 return new CScreenClock($options); 221 222 case SCREEN_RESOURCE_TRIGGER_OVERVIEW: 223 return new CScreenTriggersOverview($options); 224 225 case SCREEN_RESOURCE_DATA_OVERVIEW: 226 return new CScreenDataOverview($options); 227 228 case SCREEN_RESOURCE_URL: 229 $options = self::appendTemplatedScreenOption($options); 230 return new CScreenUrl($options); 231 232 case SCREEN_RESOURCE_ACTIONS: 233 return new CScreenActions($options); 234 235 case SCREEN_RESOURCE_EVENTS: 236 return new CScreenEvents($options); 237 238 case SCREEN_RESOURCE_HOSTGROUP_TRIGGERS: 239 return new CScreenHostgroupTriggers($options); 240 241 case SCREEN_RESOURCE_SYSTEM_STATUS: 242 return new CScreenSystemStatus($options); 243 244 case SCREEN_RESOURCE_HOST_TRIGGERS: 245 return new CScreenHostTriggers($options); 246 247 case SCREEN_RESOURCE_HISTORY: 248 return new CScreenHistory($options); 249 250 case SCREEN_RESOURCE_LLD_GRAPH: 251 $options = self::appendTemplatedScreenOption($options); 252 return new CScreenLldGraph($options); 253 254 case SCREEN_RESOURCE_LLD_SIMPLE_GRAPH: 255 $options = self::appendTemplatedScreenOption($options); 256 return new CScreenLldSimpleGraph($options); 257 258 case SCREEN_RESOURCE_HTTPTEST_DETAILS: 259 return new CScreenHttpTestDetails($options); 260 261 case SCREEN_RESOURCE_DISCOVERY: 262 return new CScreenDiscovery($options); 263 264 case SCREEN_RESOURCE_HTTPTEST: 265 return new CScreenHttpTest($options); 266 267 case SCREEN_RESOURCE_PROBLEM: 268 return new CScreenProblem($options); 269 270 default: 271 return null; 272 } 273 } 274 275 /** 276 * Appends boolean option 'isTemplatedScreen' to output options. 277 * 278 * @param array $options 279 * 280 * @return array 281 */ 282 protected static function appendTemplatedScreenOption(array $options) { 283 if (array_key_exists('screen', $options)) { 284 $options['isTemplatedScreen'] = (bool) array_key_exists('templateid', $options['screen']); 285 } 286 elseif (array_key_exists('screenid', $options) && $options['screenid'] > 0) { 287 $options['isTemplatedScreen'] = (bool) API::TemplateScreen()->get([ 288 'screenids' => [$options['screenid']], 289 'output' => [] 290 ]); 291 } 292 293 return $options; 294 } 295 296 /** 297 * Process screen with particular screen objects. 298 * 299 * @return CTable 300 */ 301 public function show() { 302 if (empty($this->screen)) { 303 return new CTableInfo(); 304 } 305 306 $skipedFields = []; 307 $screenitems = []; 308 $emptyScreenColumns = []; 309 310 // calculate table columns and rows 311 foreach ($this->screen['screenitems'] as $screenitem) { 312 $screenitems[] = $screenitem; 313 314 for ($i = 0; $i < $screenitem['rowspan'] || $i == 0; $i++) { 315 for ($j = 0; $j < $screenitem['colspan'] || $j == 0; $j++) { 316 if ($i != 0 || $j != 0) { 317 if (!isset($skipedFields[$screenitem['y'] + $i])) { 318 $skipedFields[$screenitem['y'] + $i] = []; 319 } 320 $skipedFields[$screenitem['y'] + $i][$screenitem['x'] + $j] = 1; 321 } 322 } 323 } 324 } 325 326 // create screen table 327 $screenTable = (new CTable()) 328 ->setId(self::makeScreenTableId($this->screen['screenid'])) 329 ->addClass(ZBX_STYLE_SCREEN_TABLE); 330 331 if ($this->mode == SCREEN_MODE_EDIT) { 332 $screenTable->addClass(ZBX_STYLE_DASHED_BORDER); 333 } 334 335 // action top row 336 if ($this->mode == SCREEN_MODE_EDIT) { 337 $newColumns = [(new CCol())->addClass(ZBX_STYLE_CELL_WIDTH)]; 338 339 for ($i = 0, $size = $this->screen['hsize']; $i < $size; $i++) { 340 if ($this->screen['hsize'] >= SCREEN_MAX_SIZE) { 341 $link = (new CDiv('+')) 342 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 343 ->addClass(ZBX_STYLE_DISABLED); 344 } 345 else { 346 $link = (new CLink('+', 'screenedit.php?screenid='.$this->screen['screenid']. 347 url_param('templateid').'&add_col='.$i 348 )) 349 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 350 ->addSID(); 351 } 352 353 $newColumns[] = (new CCol($link)) 354 ->addClass(ZBX_STYLE_CENTER) 355 ->addClass(ZBX_STYLE_MIDDLE); 356 } 357 358 if ($this->screen['hsize'] >= SCREEN_MAX_SIZE) { 359 $link = (new CDiv('+')) 360 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 361 ->addClass(ZBX_STYLE_DISABLED); 362 } 363 else { 364 $link = (new CLink('+', 'screenedit.php?screenid='.$this->screen['screenid'].url_param('templateid'). 365 '&add_col='.$this->screen['hsize'] 366 )) 367 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 368 ->addSID(); 369 } 370 371 $newColumns[] = (new CCol($link)) 372 ->addClass(ZBX_STYLE_CENTER) 373 ->addClass(ZBX_STYLE_MIDDLE) 374 ->addClass(ZBX_STYLE_CELL_WIDTH); 375 376 $screenTable->addRow($newColumns); 377 } 378 379 for ($r = 0; $r < $this->screen['vsize']; $r++) { 380 $newColumns = []; 381 $emptyScreenRow = true; 382 383 // action left cell 384 if ($this->mode == SCREEN_MODE_EDIT) { 385 if ($this->screen['vsize'] >= SCREEN_MAX_SIZE) { 386 $link = (new CDiv('+')) 387 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 388 ->addClass(ZBX_STYLE_DISABLED); 389 } 390 else { 391 $link = (new CLink('+', 'screenedit.php?screenid='.$this->screen['screenid']. 392 url_param('templateid').'&add_row='.$r 393 )) 394 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 395 ->addSID(); 396 } 397 398 $newColumns[] = (new CCol($link)) 399 ->addClass(ZBX_STYLE_CENTER) 400 ->addClass(ZBX_STYLE_MIDDLE); 401 } 402 403 for ($c = 0; $c < $this->screen['hsize']; $c++) { 404 if (isset($skipedFields[$r][$c])) { 405 continue; 406 } 407 408 // screen item 409 $isEditForm = false; 410 $screenitem = []; 411 412 foreach ($screenitems as $tmprow) { 413 if ($tmprow['x'] == $c && $tmprow['y'] == $r) { 414 $screenitem = $tmprow; 415 break; 416 } 417 } 418 419 if (empty($screenitem)) { 420 $screenitem = [ 421 'screenitemid' => 0, 422 'resourcetype' => 0, 423 'resourceid' => 0, 424 'width' => 0, 425 'height' => 0, 426 'colspan' => 1, 427 'rowspan' => 1, 428 'elements' => 0, 429 'valign' => VALIGN_DEFAULT, 430 'halign' => HALIGN_DEFAULT, 431 'style' => 0, 432 'url' => '', 433 'dynamic' => 0, 434 'sort_triggers' => SCREEN_SORT_TRIGGERS_DATE_DESC 435 ]; 436 } 437 438 if (!empty($screenitem['screenitemid'])) { 439 $emptyScreenRow = false; 440 $emptyScreenColumns[$c] = true; 441 } 442 443 // action 444 if ($this->mode == SCREEN_MODE_EDIT) { 445 if ($screenitem['screenitemid'] != 0) { 446 $action = 'screenedit.php?form=update'.url_params(['screenid', 'templateid']). 447 '&screenitemid='.$screenitem['screenitemid']; 448 } 449 else { 450 $action = 'screenedit.php?form=update'.url_params(['screenid', 'templateid']).'&x='.$c.'&y='.$r; 451 } 452 } 453 else { 454 $action = null; 455 } 456 457 // edit form cell 458 if ($this->mode == SCREEN_MODE_EDIT 459 && (isset($_REQUEST['form']) && $_REQUEST['form'] == 'update') 460 && ((isset($_REQUEST['x']) && $_REQUEST['x'] == $c && isset($_REQUEST['y']) && $_REQUEST['y'] == $r) 461 || (isset($_REQUEST['screenitemid']) && bccomp($_REQUEST['screenitemid'], $screenitem['screenitemid']) == 0))) { 462 $item = new CObject( 463 (new CView('monitoring.screen.constructor.edit', ['screen' => $this->screen]))->getOutput() 464 ); 465 $isEditForm = true; 466 } 467 // screen cell 468 elseif (!empty($screenitem['screenitemid']) && isset($screenitem['resourcetype'])) { 469 $screenBase = CScreenBuilder::getScreen([ 470 'screen' => $this->screen, 471 'screenid' => $this->screen['screenid'], 472 'isFlickerfree' => $this->isFlickerfree, 473 'pageFile' => $this->pageFile, 474 'mode' => $this->mode, 475 'timestamp' => $this->timestamp, 476 'hostid' => $this->hostid, 477 'profileIdx' => $this->profileIdx, 478 'profileIdx2' => $this->profileIdx2, 479 'timeline' => $this->timeline, 480 'resourcetype' => $screenitem['resourcetype'], 481 'screenitem' => $screenitem 482 ]); 483 484 if (!empty($screenBase)) { 485 $screenBase->action = $action; 486 487 $item = $screenBase->get(); 488 } 489 else { 490 $item = null; 491 } 492 } 493 // change/empty cell 494 elseif ($this->mode == SCREEN_MODE_EDIT) { 495 $item =[ 496 (new CDiv( 497 (new CLink(_x('Change', 'verb'), $action))->addClass('empty_change_link') 498 ))->addClass(ZBX_STYLE_CENTER) 499 ]; 500 } 501 else { 502 $item = null; 503 } 504 505 if ($this->mode == SCREEN_MODE_EDIT && !$isEditForm) { 506 $item = (new CDiv($item)) 507 ->addClass('draggable') 508 ->setId('position_'.$r.'_'.$c) 509 ->setAttribute('data-xcoord', $c) 510 ->setAttribute('data-ycoord', $r); 511 } 512 513 // colspan/rowspan 514 $newColumn = (new CCol($item))->addClass('screenitem'); 515 516 if ($screenitem['halign'] == HALIGN_CENTER || $isEditForm) { 517 $newColumn->addClass(ZBX_STYLE_CENTER); 518 } 519 elseif ($screenitem['halign'] == HALIGN_LEFT) { 520 $newColumn->addClass(ZBX_STYLE_LEFT); 521 } 522 elseif ($screenitem['halign'] == HALIGN_RIGHT) { 523 $newColumn->addClass(ZBX_STYLE_RIGHT); 524 } 525 526 if ($screenitem['valign'] == VALIGN_MIDDLE || $isEditForm) { 527 $newColumn->addClass(ZBX_STYLE_MIDDLE); 528 } 529 elseif ($screenitem['valign'] == VALIGN_TOP) { 530 $newColumn->addClass(ZBX_STYLE_TOP); 531 } 532 elseif ($screenitem['valign'] == VALIGN_BOTTOM) { 533 $newColumn->addClass(ZBX_STYLE_BOTTOM); 534 } 535 536 if ($screenitem['colspan'] > 1) { 537 $newColumn->setColSpan($screenitem['colspan']); 538 } 539 if ($screenitem['rowspan'] > 1) { 540 $newColumn->setRowSpan($screenitem['rowspan']); 541 } 542 $newColumns[] = $newColumn; 543 } 544 545 // action right cell 546 if ($this->mode == SCREEN_MODE_EDIT) { 547 if ($this->screen['vsize'] == SCREEN_MIN_SIZE) { 548 $link = (new CDiv('−')) 549 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 550 ->addClass(ZBX_STYLE_DISABLED); 551 } 552 else { 553 $link = (new CLink('−', 'screenedit.php?screenid='.$this->screen['screenid']. 554 url_param('templateid').'&rmv_row='.$r 555 )) 556 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 557 ->addSID(); 558 if (!$emptyScreenRow) { 559 $link->addConfirmation(_('This screen row is not empty. Delete it?')); 560 } 561 } 562 563 $newColumns[] = (new CCol($link)) 564 ->addClass(ZBX_STYLE_CENTER) 565 ->addClass(ZBX_STYLE_MIDDLE); 566 } 567 $screenTable->addRow(new CRow($newColumns)); 568 } 569 570 // action bottom row 571 if ($this->mode == SCREEN_MODE_EDIT) { 572 if ($this->screen['vsize'] >= SCREEN_MAX_SIZE) { 573 $link = (new CDiv('+')) 574 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 575 ->addClass(ZBX_STYLE_DISABLED); 576 } 577 else { 578 $link = (new CLink('+', 'screenedit.php?screenid='.$this->screen['screenid'].url_param('templateid'). 579 '&add_row='.$this->screen['vsize'] 580 )) 581 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 582 ->addSID(); 583 } 584 585 $newColumns = [ 586 (new CCol($link)) 587 ->addClass(ZBX_STYLE_CENTER) 588 ->addClass(ZBX_STYLE_MIDDLE) 589 ]; 590 591 for ($i = 0; $i < $this->screen['hsize']; $i++) { 592 if ($this->screen['hsize'] == SCREEN_MIN_SIZE) { 593 $link = (new CDiv('−')) 594 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 595 ->addClass(ZBX_STYLE_DISABLED); 596 } 597 else { 598 $link = (new CLink('−', 'screenedit.php?screenid='.$this->screen['screenid']. 599 url_param('templateid').'&rmv_col='.$i 600 )) 601 ->addClass(ZBX_STYLE_TREEVIEW_PLUS) 602 ->addSID(); 603 604 if (array_key_exists($i, $emptyScreenColumns)) { 605 $link->addConfirmation(_('This screen column is not empty. Delete it?')); 606 } 607 } 608 609 $newColumns[] = (new CCol($link)) 610 ->addClass(ZBX_STYLE_CENTER) 611 ->addClass(ZBX_STYLE_MIDDLE); 612 } 613 614 $newColumns[] = ''; 615 $screenTable->addRow($newColumns); 616 } 617 618 return $screenTable; 619 } 620 621 /** 622 * Insert javascript to create scroll in time control. 623 * 624 * @static 625 * 626 * @param array $timeline 627 */ 628 private static function insertScreenScrollJs(array $timeline) { 629 $obj_data = [ 630 'id' => 'scrollbar', 631 'mainObject' => 1 632 ]; 633 634 zbx_add_post_js('timeControl.addObject("scrollbar", '.zbx_jsvalue($timeline).', '.zbx_jsvalue($obj_data).');'); 635 } 636 637 /** 638 * Insert javascript to init screens. 639 * 640 * @static 641 * 642 * @param string $screenid 643 */ 644 public static function insertInitScreenJs($screenid) { 645 zbx_add_post_js('init_screen("'.$screenid.'", "'.self::makeScreenTableId($screenid).'", "'.$screenid.'");'); 646 } 647 648 /** 649 * Insert javascript to start time control rendering. 650 * 651 * @static 652 */ 653 public static function insertProcessObjectsJs() { 654 zbx_add_post_js('timeControl.processObjects();'); 655 } 656 657 /** 658 * Insert javascript to clean all screen items. 659 * 660 * @static 661 */ 662 public static function insertScreenCleanJs() { 663 zbx_add_post_js('window.flickerfreeScreen.cleanAll();'); 664 } 665 666 /** 667 * Insert javascript for standard screens. 668 * 669 * @param array $timeline 670 * 671 * @static 672 */ 673 public static function insertScreenStandardJs(array $timeline) { 674 CScreenBuilder::insertScreenScrollJs($timeline); 675 CScreenBuilder::insertProcessObjectsJs(); 676 } 677 678 /** 679 * Creates a string for screen table ID attribute. 680 * 681 * @param string $screenId 682 * 683 * @return string 684 */ 685 protected static function makeScreenTableId($screenId) { 686 return 'screentable_'.$screenId; 687 } 688} 689