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