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/config.inc.php'; 23require_once dirname(__FILE__).'/include/hosts.inc.php'; 24require_once dirname(__FILE__).'/include/items.inc.php'; 25 26$page['title'] = _('Latest data'); 27$page['file'] = 'latest.php'; 28$page['type'] = detect_page_type(PAGE_TYPE_HTML); 29$page['scripts'] = ['multiselect.js']; 30 31if (PAGE_TYPE_HTML == $page['type']) { 32 define('ZBX_PAGE_DO_REFRESH', 1); 33} 34 35require_once dirname(__FILE__).'/include/page_header.php'; 36 37// VAR TYPE OPTIONAL FLAGS VALIDATION EXCEPTION 38$fields = [ 39 'groupids' => [T_ZBX_INT, O_OPT, P_SYS, DB_ID, null], 40 'hostids' => [T_ZBX_INT, O_OPT, P_SYS, DB_ID, null], 41 'fullscreen' => [T_ZBX_INT, O_OPT, P_SYS, IN('0,1'), null], 42 'select' => [T_ZBX_STR, O_OPT, null, null, null], 43 'show_without_data' => [T_ZBX_INT, O_OPT, null, IN('0,1'), null], 44 'show_details' => [T_ZBX_INT, O_OPT, null, IN('0,1'), null], 45 'application' => [T_ZBX_STR, O_OPT, null, null, null], 46 'filter_rst' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 47 'filter_set' => [T_ZBX_STR, O_OPT, P_SYS, null, null], 48 // sort and sortorder 49 'sort' => [T_ZBX_STR, O_OPT, P_SYS, IN('"host","lastclock","name"'), null], 50 'sortorder' => [T_ZBX_STR, O_OPT, P_SYS, IN('"'.ZBX_SORT_DOWN.'","'.ZBX_SORT_UP.'"'), null] 51]; 52check_fields($fields); 53 54/* 55 * Permissions 56 */ 57if (getRequest('groupids') && !API::HostGroup()->isReadable(getRequest('groupids'))) { 58 access_deny(); 59} 60if (getRequest('hostids') && !API::Host()->isReadable(getRequest('hostids'))) { 61 access_deny(); 62} 63 64if (PAGE_TYPE_JS == $page['type'] || PAGE_TYPE_HTML_BLOCK == $page['type']){ 65 require_once dirname(__FILE__).'/include/page_footer.php'; 66 exit; 67} 68 69require_once dirname(__FILE__).'/include/views/js/monitoring.latest.js.php'; 70 71/* 72 * Filter 73 */ 74if (hasRequest('filter_set')) { 75 CProfile::update('web.latest.filter.select', getRequest('select', ''), PROFILE_TYPE_STR); 76 CProfile::update('web.latest.filter.show_without_data', getRequest('show_without_data', 0), PROFILE_TYPE_INT); 77 CProfile::update('web.latest.filter.show_details', getRequest('show_details', 0), PROFILE_TYPE_INT); 78 CProfile::update('web.latest.filter.application', getRequest('application', ''), PROFILE_TYPE_STR); 79 CProfile::updateArray('web.latest.filter.groupids', getRequest('groupids', []), PROFILE_TYPE_STR); 80 CProfile::updateArray('web.latest.filter.hostids', getRequest('hostids', []), PROFILE_TYPE_STR); 81} 82elseif (hasRequest('filter_rst')) { 83 DBStart(); 84 CProfile::delete('web.latest.filter.select'); 85 CProfile::delete('web.latest.filter.show_without_data'); 86 CProfile::delete('web.latest.filter.show_details'); 87 CProfile::delete('web.latest.filter.application'); 88 CProfile::deleteIdx('web.latest.filter.groupids'); 89 CProfile::deleteIdx('web.latest.filter.hostids'); 90 DBend(); 91} 92 93$filter = [ 94 'select' => CProfile::get('web.latest.filter.select', ''), 95 'showWithoutData' => CProfile::get('web.latest.filter.show_without_data', 1), 96 'showDetails' => CProfile::get('web.latest.filter.show_details'), 97 'application' => CProfile::get('web.latest.filter.application', ''), 98 'groupids' => CProfile::getArray('web.latest.filter.groupids'), 99 'hostids' => CProfile::getArray('web.latest.filter.hostids') 100]; 101 102// we'll need to hide the host column if only one host is selected 103$singleHostSelected = (count($filter['hostids']) == 1); 104 105$sortField = getRequest('sort', CProfile::get('web.'.$page['file'].'.sort', 'name')); 106$sortOrder = getRequest('sortorder', CProfile::get('web.'.$page['file'].'.sortorder', ZBX_SORT_UP)); 107 108CProfile::update('web.'.$page['file'].'.sort', $sortField, PROFILE_TYPE_STR); 109CProfile::update('web.'.$page['file'].'.sortorder', $sortOrder, PROFILE_TYPE_STR); 110 111$applications = $items = $hostScripts = []; 112 113// we'll only display the values if the filter is set 114$filterSet = ($filter['select'] !== '' || $filter['application'] !== '' || $filter['groupids'] || $filter['hostids']); 115if ($filterSet) { 116 $hosts = API::Host()->get([ 117 'output' => ['name', 'hostid', 'status'], 118 'hostids' => $filter['hostids'], 119 'groupids' => $filter['groupids'], 120 'selectGraphs' => API_OUTPUT_COUNT, 121 'with_monitored_items' => true, 122 'preservekeys' => true 123 ]); 124} 125else { 126 $hosts = []; 127} 128 129if ($hosts) { 130 131 foreach ($hosts as &$host) { 132 $host['item_cnt'] = 0; 133 } 134 unset($host); 135 136 if (!$singleHostSelected) { 137 $sortFields = ($sortField === 'host') ? [['field' => 'name', 'order' => $sortOrder]] : ['name']; 138 CArrayHelper::sort($hosts, $sortFields); 139 } 140 141 $hostIds = array_keys($hosts); 142 143 $applications = null; 144 145 // if an application filter is set, fetch the applications and then use them to filter items 146 if ($filter['application'] !== '') { 147 $applications = API::Application()->get([ 148 'output' => API_OUTPUT_EXTEND, 149 'hostids' => $hostIds, 150 'search' => ['name' => $filter['application']], 151 'preservekeys' => true 152 ]); 153 } 154 155 $items = API::Item()->get([ 156 'hostids' => array_keys($hosts), 157 'output' => ['itemid', 'name', 'type', 'value_type', 'units', 'hostid', 'state', 'valuemapid', 'status', 158 'error', 'trends', 'history', 'delay', 'key_', 'flags'], 159 'selectApplications' => ['applicationid'], 160 'selectItemDiscovery' => ['ts_delete'], 161 'applicationids' => ($applications !== null) ? zbx_objectValues($applications, 'applicationid') : null, 162 'webitems' => true, 163 'filter' => [ 164 'status' => [ITEM_STATUS_ACTIVE] 165 ], 166 'preservekeys' => true 167 ]); 168 169 // if the applications haven't been loaded when filtering, load them based on the retrieved items to avoid 170 // fetching applications from hosts that may not be displayed 171 if ($applications === null) { 172 $applications = API::Application()->get([ 173 'output' => API_OUTPUT_EXTEND, 174 'hostids' => array_keys(array_flip(zbx_objectValues($items, 'hostid'))), 175 'search' => ['name' => $filter['application']], 176 'preservekeys' => true 177 ]); 178 } 179} 180 181if ($items) { 182 // macros 183 $items = CMacrosResolverHelper::resolveItemKeys($items); 184 $items = CMacrosResolverHelper::resolveItemNames($items); 185 186 // filter items by name 187 foreach ($items as $key => $item) { 188 if (($filter['select'] !== '')) { 189 $haystack = mb_strtolower($item['name_expanded']); 190 $needle = mb_strtolower($filter['select']); 191 192 if (mb_strpos($haystack, $needle) === false) { 193 unset($items[$key]); 194 } 195 } 196 } 197 198 if ($items) { 199 // get history 200 $history = Manager::History()->getLast($items, 2, ZBX_HISTORY_PERIOD); 201 202 // filter items without history 203 if (!$filter['showWithoutData']) { 204 foreach ($items as $key => $item) { 205 if (!isset($history[$item['itemid']])) { 206 unset($items[$key]); 207 } 208 } 209 } 210 } 211 212 if ($items) { 213 // add item last update date for sorting 214 foreach ($items as &$item) { 215 if (isset($history[$item['itemid']])) { 216 $item['lastclock'] = $history[$item['itemid']][0]['clock']; 217 } 218 } 219 unset($item); 220 221 // sort 222 if ($sortField === 'name') { 223 $sortFields = [['field' => 'name_expanded', 'order' => $sortOrder], 'itemid']; 224 } 225 elseif ($sortField === 'lastclock') { 226 $sortFields = [['field' => 'lastclock', 'order' => $sortOrder], 'name_expanded', 'itemid']; 227 } 228 else { 229 $sortFields = ['name_expanded', 'itemid']; 230 } 231 CArrayHelper::sort($items, $sortFields); 232 233 if ($applications) { 234 foreach ($applications as &$application) { 235 $application['hostname'] = $hosts[$application['hostid']]['name']; 236 $application['item_cnt'] = 0; 237 } 238 unset($application); 239 240 // by default order by application name and application id 241 $sortFields = ($sortField === 'host') ? [['field' => 'hostname', 'order' => $sortOrder]] : []; 242 array_push($sortFields, 'name', 'applicationid'); 243 CArrayHelper::sort($applications, $sortFields); 244 } 245 246 if (!$singleHostSelected) { 247 // get host scripts 248 $hostScripts = API::Script()->getScriptsByHosts($hostIds); 249 250 // get templates screen count 251 $screens = API::TemplateScreen()->get([ 252 'hostids' => $hostIds, 253 'countOutput' => true, 254 'groupCount' => true 255 ]); 256 $screens = zbx_toHash($screens, 'hostid'); 257 foreach ($hosts as &$host) { 258 $host['screens'] = isset($screens[$host['hostid']]); 259 } 260 unset($host); 261 } 262 } 263} 264 265// multiselect hosts 266$multiSelectHostData = []; 267if ($filter['hostids']) { 268 $filterHosts = API::Host()->get([ 269 'output' => ['hostid', 'name'], 270 'hostids' => $filter['hostids'] 271 ]); 272 273 foreach ($filterHosts as $host) { 274 $multiSelectHostData[] = [ 275 'id' => $host['hostid'], 276 'name' => $host['name'] 277 ]; 278 } 279} 280 281// multiselect host groups 282$multiSelectHostGroupData = []; 283if ($filter['groupids'] !== null) { 284 $filterGroups = API::HostGroup()->get([ 285 'output' => ['groupid', 'name'], 286 'groupids' => $filter['groupids'] 287 ]); 288 289 foreach ($filterGroups as $group) { 290 $multiSelectHostGroupData[] = [ 291 'id' => $group['groupid'], 292 'name' => $group['name'] 293 ]; 294 } 295} 296 297/* 298 * Display 299 */ 300$widget = (new CWidget()) 301 ->setTitle(_('Latest data')) 302 ->setControls((new CList()) 303 ->addItem(get_icon('fullscreen', ['fullscreen' => getRequest('fullscreen')])) 304 ); 305 306// Filter 307$filterForm = (new CFilter('web.latest.filter.state')) 308 ->addVar('fullscreen', getRequest('fullscreen')); 309 310$filterColumn1 = new CFormList(); 311$filterColumn1->addRow( 312 _('Host groups'), 313 (new CMultiSelect( 314 [ 315 'name' => 'groupids[]', 316 'objectName' => 'hostGroup', 317 'data' => $multiSelectHostGroupData, 318 'popup' => [ 319 'parameters' => 'srctbl=host_groups&dstfrm=zbx_filter&dstfld1=groupids_'. 320 '&srcfld1=groupid&multiselect=1' 321 ] 322 ]))->setWidth(ZBX_TEXTAREA_FILTER_STANDARD_WIDTH) 323); 324$filterColumn1->addRow( 325 _('Hosts'), 326 (new CMultiSelect( 327 [ 328 'name' => 'hostids[]', 329 'objectName' => 'hosts', 330 'data' => $multiSelectHostData, 331 'popup' => [ 332 'parameters' => 'srctbl=hosts&dstfrm=zbx_filter&dstfld1=hostids_&srcfld1=hostid'. 333 '&real_hosts=1&multiselect=1' 334 ] 335 ] 336 ))->setWidth(ZBX_TEXTAREA_FILTER_STANDARD_WIDTH) 337); 338$filterColumn1->addRow( 339 _('Application'), 340 [ 341 (new CTextBox('application', $filter['application']))->setWidth(ZBX_TEXTAREA_FILTER_STANDARD_WIDTH), 342 (new CDiv())->addClass(ZBX_STYLE_FORM_INPUT_MARGIN), 343 (new CButton('application_name', _('Select'))) 344 ->addClass(ZBX_STYLE_BTN_GREY) 345 ->onClick('return PopUp("popup.php?srctbl=applications&srcfld1=name&real_hosts=1&dstfld1=application'. 346 '&with_applications=1&dstfrm=zbx_filter");' 347 ) 348 ] 349); 350 351$filterColumn2 = new CFormList(); 352$filterColumn2->addRow( 353 _('Name'), 354 (new CTextBox('select', $filter['select']))->setWidth(ZBX_TEXTAREA_FILTER_STANDARD_WIDTH) 355); 356$filterColumn2->addRow( 357 _('Show items without data'), 358 (new CCheckBox('show_without_data'))->setChecked($filter['showWithoutData'] == 1) 359); 360$filterColumn2->addRow( 361 _('Show details'), 362 (new CCheckBox('show_details'))->setChecked($filter['showDetails'] == 1) 363); 364 365$filterForm->addColumn($filterColumn1); 366$filterForm->addColumn($filterColumn2); 367 368$widget->addItem($filterForm); 369// End of Filter 370 371$form = (new CForm('GET', 'history.php')) 372 ->setName('items') 373 ->addItem(new CVar('action', HISTORY_BATCH_GRAPH)); 374// table 375$table = (new CTableInfo())->addClass(ZBX_STYLE_OVERFLOW_ELLIPSIS); 376if (!$filterSet) { 377 $table->setNoDataMessage(_('Specify some filter condition to see the values.')); 378} 379 380$toggle_all = (new CColHeader( 381 (new CSimpleButton()) 382 ->addClass(ZBX_STYLE_TREEVIEW) 383 ->addClass('app-list-toggle-all') 384 ->addItem(new CSpan()) 385))->addStyle('width: 18px'); 386 387$check_all = (new CColHeader( 388 (new CCheckBox('all_items'))->onClick("checkAll('".$form->getName()."', 'all_items', 'itemids');") 389))->addStyle('width: 15px'); 390 391if ($filter['showDetails']) { 392 $table->setHeader([ 393 $toggle_all, 394 $check_all, 395 $singleHostSelected 396 ? null 397 : make_sorting_header(_('Host'), 'host', $sortField, $sortOrder)->addStyle('width: 13%'), 398 make_sorting_header(_('Name'), 'name', $sortField, $sortOrder) 399 ->addStyle('width: '.($singleHostSelected ? 34 : 21).'%'), 400 (new CColHeader(_('Interval')))->addStyle('width: 5%'), 401 (new CColHeader(_('History')))->addStyle('width: 5%'), 402 (new CColHeader(_('Trends')))->addStyle('width: 5%'), 403 (new CColHeader(_('Type')))->addStyle('width: 8%'), 404 make_sorting_header(_('Last check'), 'lastclock', $sortField, $sortOrder)->addStyle('width: 14%'), 405 (new CColHeader(_('Last value')))->addStyle('width: 14%'), 406 (new CColHeader(_x('Change', 'noun in latest data')))->addStyle('width: 10%'), 407 (new CColHeader())->addStyle('width: 5%'), 408 (new CColHeader(_('Info')))->addStyle('width: 35px') 409 ]); 410} 411else { 412 $table->setHeader([ 413 $toggle_all, 414 $check_all, 415 $singleHostSelected 416 ? null 417 : make_sorting_header(_('Host'), 'host', $sortField, $sortOrder)->addStyle('width: 17%'), 418 make_sorting_header(_('Name'), 'name', $sortField, $sortOrder) 419 ->addStyle('width: '.($singleHostSelected ? 57 : 40).'%'), 420 make_sorting_header(_('Last check'), 'lastclock', $sortField, $sortOrder)->addStyle('width: 14%'), 421 (new CColHeader(_('Last value')))->addStyle('width: 14%'), 422 (new CColHeader(_x('Change', 'noun in latest data')))->addStyle('width: 10%'), 423 (new CColHeader())->addStyle('width: 5%') 424 ]); 425} 426 427$hostColumn = $singleHostSelected ? null : ''; 428$tab_rows = []; 429 430$config = select_config(); 431 432foreach ($items as $key => $item){ 433 if (!$item['applications']) { 434 continue; 435 } 436 437 $lastHistory = isset($history[$item['itemid']][0]) ? $history[$item['itemid']][0] : null; 438 $prevHistory = isset($history[$item['itemid']][1]) ? $history[$item['itemid']][1] : null; 439 440 if (strpos($item['units'], ',') !== false) { 441 list($item['units'], $item['unitsLong']) = explode(',', $item['units']); 442 } 443 else { 444 $item['unitsLong'] = ''; 445 } 446 447 // last check time and last value 448 if ($lastHistory) { 449 $lastClock = zbx_date2str(DATE_TIME_FORMAT_SECONDS, $lastHistory['clock']); 450 $lastValue = formatHistoryValue($lastHistory['value'], $item, false); 451 } 452 else { 453 $lastClock = UNKNOWN_VALUE; 454 $lastValue = UNKNOWN_VALUE; 455 } 456 457 // change 458 $digits = ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT) ? 2 : 0; 459 if ($lastHistory && $prevHistory 460 && ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) 461 && (bcsub($lastHistory['value'], $prevHistory['value'], $digits) != 0)) { 462 463 $change = ''; 464 if (($lastHistory['value'] - $prevHistory['value']) > 0) { 465 $change = '+'; 466 } 467 468 // for 'unixtime' change should be calculated as uptime 469 $change .= convert_units([ 470 'value' => bcsub($lastHistory['value'], $prevHistory['value'], $digits), 471 'units' => $item['units'] == 'unixtime' ? 'uptime' : $item['units'] 472 ]); 473 $change = nbsp($change); 474 } 475 else { 476 $change = UNKNOWN_VALUE; 477 } 478 479 $showLink = ((($config['hk_history_global'] && $config['hk_history'] == 0) || $item['history'] == 0) 480 && (($config['hk_trends_global'] && $config['hk_trends'] == 0) || $item['trends'] == 0) 481 ); 482 483 $checkbox = (new CCheckBox('itemids['.$item['itemid'].']', $item['itemid'])) 484 ->removeAttribute('id'); 485 486 if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) { 487 $actions = $showLink 488 ? UNKNOWN_VALUE 489 : new CLink(_('Graph'), 'history.php?action='.HISTORY_GRAPH.'&itemids[]='.$item['itemid']); 490 } 491 else { 492 $actions = $showLink 493 ? UNKNOWN_VALUE 494 : new CLink(_('History'), 'history.php?action='.HISTORY_VALUES.'&itemids[]='.$item['itemid']); 495 $checkbox->setEnabled(false); 496 } 497 498 $state_css = ($item['state'] == ITEM_STATE_NOTSUPPORTED) ? ZBX_STYLE_GREY : null; 499 500 if ($filter['showDetails']) { 501 // item key 502 $itemKey = ($item['type'] == ITEM_TYPE_HTTPTEST || $item['flags'] == ZBX_FLAG_DISCOVERY_CREATED) 503 ? (new CSpan($item['key_expanded']))->addClass(ZBX_STYLE_GREEN) 504 : (new CLink($item['key_expanded'], 'items.php?form=update&itemid='.$item['itemid'])) 505 ->addClass(ZBX_STYLE_LINK_ALT) 506 ->addClass(ZBX_STYLE_GREEN); 507 508 // info 509 if ($item['status'] == ITEM_STATUS_ACTIVE && $item['error'] !== '') { 510 $info = makeErrorIcon($item['error']); 511 } 512 else { 513 $info = ''; 514 } 515 516 // trend value 517 if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) { 518 $trendValue = $config['hk_trends_global'] ? $config['hk_trends'] : $item['trends']; 519 } 520 else { 521 $trendValue = UNKNOWN_VALUE; 522 } 523 524 $row = new CRow([ 525 '', 526 $checkbox, 527 $hostColumn, 528 (new CCol([$item['name_expanded'], BR(), $itemKey]))->addClass($state_css), 529 (new CCol( 530 ($item['type'] == ITEM_TYPE_SNMPTRAP || $item['type'] == ITEM_TYPE_TRAPPER) 531 ? UNKNOWN_VALUE 532 : $item['delay'] 533 ))->addClass($state_css), 534 (new CCol($config['hk_history_global'] ? $config['hk_history'] : $item['history']))->addClass($state_css), 535 (new CCol($trendValue))->addClass($state_css), 536 (new CCol(item_type2str($item['type'])))->addClass($state_css), 537 (new CCol($lastClock))->addClass($state_css), 538 (new CCol($lastValue))->addClass($state_css), 539 (new CCol($change))->addClass($state_css), 540 $actions, 541 $info 542 ]); 543 } 544 else { 545 $row = new CRow([ 546 '', 547 $checkbox, 548 $hostColumn, 549 (new CCol($item['name_expanded']))->addClass($state_css), 550 (new CCol($lastClock))->addClass($state_css), 551 (new CCol($lastValue))->addClass($state_css), 552 (new CCol($change))->addClass($state_css), 553 $actions 554 ]); 555 } 556 557 // add the item row to each application tab 558 foreach ($item['applications'] as $itemApplication) { 559 $applicationId = $itemApplication['applicationid']; 560 561 if (isset($applications[$applicationId])) { 562 $applications[$applicationId]['item_cnt']++; 563 // objects may have different properties, so it's better to use a copy of it 564 $tab_rows[$applicationId][] = clone $row; 565 } 566 } 567 568 // remove items with applications from the collection 569 unset($items[$key]); 570} 571 572foreach ($applications as $appid => $dbApp) { 573 $host = $hosts[$dbApp['hostid']]; 574 575 if(!isset($tab_rows[$appid])) continue; 576 577 $appRows = $tab_rows[$appid]; 578 579 $open_state = CProfile::get('web.latest.toggle', null, $dbApp['applicationid']); 580 581 $hostName = null; 582 583 if (!$singleHostSelected) { 584 $hostName = (new CSpan($host['name'])) 585 ->addClass(ZBX_STYLE_LINK_ACTION) 586 ->setMenuPopup(CMenuPopupHelper::getHost($host, $hostScripts[$host['hostid']])); 587 if ($host['status'] == HOST_STATUS_NOT_MONITORED) { 588 $hostName->addClass(ZBX_STYLE_RED); 589 } 590 } 591 592 // add toggle row 593 $table->addRow([ 594 (new CSimpleButton()) 595 ->addClass(ZBX_STYLE_TREEVIEW) 596 ->addClass('app-list-toggle') 597 ->setAttribute('data-app-id', $dbApp['applicationid']) 598 ->setAttribute('data-open-state', $open_state) 599 ->addItem(new CSpan()), 600 '', 601 $hostName, 602 (new CCol([bold($dbApp['name']), ' ('._n('%1$s Item', '%1$s Items', $dbApp['item_cnt']).')'])) 603 ->setColSpan($filter['showDetails'] ? 10 : 5) 604 ]); 605 606 // add toggle sub rows 607 foreach ($appRows as $row) { 608 $row->setAttribute('parent_app_id', $dbApp['applicationid']); 609 $table->addRow($row); 610 } 611} 612 613/** 614 * Display OTHER ITEMS (which are not linked to application) 615 */ 616$tab_rows = []; 617foreach ($items as $item) { 618 $lastHistory = isset($history[$item['itemid']][0]) ? $history[$item['itemid']][0] : null; 619 $prevHistory = isset($history[$item['itemid']][1]) ? $history[$item['itemid']][1] : null; 620 621 if (strpos($item['units'], ',') !== false) 622 list($item['units'], $item['unitsLong']) = explode(',', $item['units']); 623 else 624 $item['unitsLong'] = ''; 625 626 // last check time and last value 627 if ($lastHistory) { 628 $lastClock = zbx_date2str(DATE_TIME_FORMAT_SECONDS, $lastHistory['clock']); 629 $lastValue = formatHistoryValue($lastHistory['value'], $item, false); 630 } 631 else { 632 $lastClock = UNKNOWN_VALUE; 633 $lastValue = UNKNOWN_VALUE; 634 } 635 636 // column "change" 637 $digits = ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT) ? 2 : 0; 638 if (isset($lastHistory['value']) && isset($prevHistory['value']) 639 && ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) 640 && (bcsub($lastHistory['value'], $prevHistory['value'], $digits) != 0)) { 641 642 $change = ''; 643 if (($lastHistory['value'] - $prevHistory['value']) > 0) { 644 $change = '+'; 645 } 646 647 // for 'unixtime' change should be calculated as uptime 648 $change .= convert_units([ 649 'value' => bcsub($lastHistory['value'], $prevHistory['value'], $digits), 650 'units' => $item['units'] == 'unixtime' ? 'uptime' : $item['units'] 651 ]); 652 $change = nbsp($change); 653 } 654 else { 655 $change = UNKNOWN_VALUE; 656 } 657 658 // column "action" 659 $showLink = ((($config['hk_history_global'] && $config['hk_history'] == 0) || $item['history'] == 0) 660 && (($config['hk_trends_global'] && $config['hk_trends'] == 0) || $item['trends'] == 0) 661 ); 662 663 $checkbox = (new CCheckBox('itemids['.$item['itemid'].']', $item['itemid'])) 664 ->removeAttribute('id'); 665 666 if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) { 667 $actions = $showLink 668 ? UNKNOWN_VALUE 669 : new CLink(_('Graph'), 'history.php?action='.HISTORY_GRAPH.'&itemids[]='.$item['itemid']); 670 } 671 else { 672 $actions = $showLink 673 ? UNKNOWN_VALUE 674 : new CLink(_('History'), 'history.php?action='.HISTORY_VALUES.'&itemids[]='.$item['itemid']); 675 $checkbox->setEnabled(false); 676 } 677 678 $state_css = ($item['state'] == ITEM_STATE_NOTSUPPORTED) ? ZBX_STYLE_GREY : null; 679 680 $host = $hosts[$item['hostid']]; 681 if ($filter['showDetails']) { 682 // item key 683 $itemKey = ($item['type'] == ITEM_TYPE_HTTPTEST || $item['flags'] == ZBX_FLAG_DISCOVERY_CREATED) 684 ? (new CSpan($item['key_expanded']))->addClass(ZBX_STYLE_GREEN) 685 : (new CLink($item['key_expanded'], 'items.php?form=update&itemid='.$item['itemid'])) 686 ->addClass(ZBX_STYLE_LINK_ALT) 687 ->addClass(ZBX_STYLE_GREEN); 688 689 // info 690 if ($item['status'] == ITEM_STATUS_ACTIVE && $item['error'] !== '') { 691 $info = makeErrorIcon($item['error']); 692 } 693 else { 694 $info = ''; 695 } 696 697 // trend value 698 if ($item['value_type'] == ITEM_VALUE_TYPE_FLOAT || $item['value_type'] == ITEM_VALUE_TYPE_UINT64) { 699 $trendValue = $config['hk_trends_global'] ? $config['hk_trends'] : $item['trends']; 700 } 701 else { 702 $trendValue = UNKNOWN_VALUE; 703 } 704 705 $row = new CRow([ 706 '', 707 $checkbox, 708 $hostColumn, 709 (new CCol([$item['name_expanded'], BR(), $itemKey]))->addClass($state_css), 710 (new CCol( 711 ($item['type'] == ITEM_TYPE_SNMPTRAP || $item['type'] == ITEM_TYPE_TRAPPER) 712 ? UNKNOWN_VALUE 713 : $item['delay'] 714 ))->addClass($state_css), 715 (new CCol($config['hk_history_global'] ? $config['hk_history'] : $item['history']))->addClass($state_css), 716 (new CCol($trendValue))->addClass($state_css), 717 (new CCol(item_type2str($item['type'])))->addClass($state_css), 718 (new CCol($lastClock))->addClass($state_css), 719 (new CCol($lastValue))->addClass($state_css), 720 (new CCol($change))->addClass($state_css), 721 $actions, 722 $info 723 ]); 724 } 725 else { 726 $row = new CRow([ 727 '', 728 $checkbox, 729 $hostColumn, 730 (new CCol($item['name_expanded']))->addClass($state_css), 731 (new CCol($lastClock))->addClass($state_css), 732 (new CCol($lastValue))->addClass($state_css), 733 (new CCol($change))->addClass($state_css), 734 $actions 735 ]); 736 } 737 738 $hosts[$item['hostid']]['item_cnt']++; 739 $tab_rows[$item['hostid']][] = $row; 740} 741 742foreach ($hosts as $hostId => $dbHost) { 743 $host = $hosts[$dbHost['hostid']]; 744 745 if(!isset($tab_rows[$hostId])) { 746 continue; 747 } 748 $appRows = $tab_rows[$hostId]; 749 750 $open_state = CProfile::get('web.latest.toggle_other', null, $host['hostid']); 751 752 $hostName = null; 753 754 if (!$singleHostSelected) { 755 $hostName = (new CSpan($host['name'])) 756 ->addClass(ZBX_STYLE_LINK_ACTION) 757 ->setMenuPopup(CMenuPopupHelper::getHost($host, $hostScripts[$host['hostid']])); 758 if ($host['status'] == HOST_STATUS_NOT_MONITORED) { 759 $hostName->addClass(ZBX_STYLE_RED); 760 } 761 } 762 763 // add toggle row 764 $table->addRow([ 765 (new CSimpleButton()) 766 ->addClass(ZBX_STYLE_TREEVIEW) 767 ->addClass('app-list-toggle') 768 ->setAttribute('data-host-id', $host['hostid']) 769 ->setAttribute('data-open-state', $open_state) 770 ->addItem(new CSpan()), 771 '', 772 $hostName, 773 (new CCol([bold('- '.('other').' -'), ' ('._n('%1$s Item', '%1$s Items', $dbHost['item_cnt']).')'])) 774 ->setColSpan($filter['showDetails'] ? 10 : 5) 775 ]); 776 777 // add toggle sub rows 778 foreach($appRows as $row) { 779 $row->setAttribute('parent_host_id', $host['hostid']); 780 $table->addRow($row); 781 } 782} 783 784$form->addItem([ 785 $table, 786 new CActionButtonList('graphtype', 'itemids', [ 787 GRAPH_TYPE_STACKED => ['name' => _('Display stacked graph')], 788 GRAPH_TYPE_NORMAL => ['name' => _('Display graph')] 789 ]) 790]); 791 792$widget->addItem($form)->show(); 793 794require_once dirname(__FILE__).'/include/page_footer.php'; 795