<?php declare(strict_types = 0); /* ** Zabbix ** Copyright (C) 2001-2024 Zabbix SIA ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. **/ class CControllerWidgetItemView extends CControllerWidget { public const CHANGE_INDICATOR_UP = 1; public const CHANGE_INDICATOR_DOWN = 2; public const CHANGE_INDICATOR_UP_DOWN = 3; public function __construct() { parent::__construct(); $this->setType(WIDGET_ITEM); $this->setValidationRules([ 'name' => 'string', 'fields' => 'json', 'dynamic_hostid' => 'db hosts.hostid' ]); } protected function doAction() { $name = $this->getDefaultName(); $cells = []; $url = null; $error = ''; $fields = $this->getForm()->getFieldsData(); $history_period = timeUnitToSeconds(CSettingsHelper::get(CSettingsHelper::HISTORY_PERIOD)); $description = ''; $value = null; $change_indicator = null; $time = ''; $units = ''; $decimals = null; $is_dynamic = ($this->hasInput('dynamic_hostid') && ($this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD || $fields['dynamic'] == WIDGET_DYNAMIC_ITEM) ); if ($is_dynamic) { $tmp_items = API::Item()->get([ 'output' => ['key_'], 'itemids' => $fields['itemid'], 'webitems' => true ]); if ($tmp_items) { $options = [ 'output' => ['value_type'], 'selectValueMap' => ['mappings'], 'hostids' => [$this->getInput('dynamic_hostid')], 'webitems' => true, 'filter' => [ 'key_' => $tmp_items[0]['key_'] ], 'preservekeys' => true ]; } } else { $options = [ 'output' => ['value_type'], 'selectValueMap' => ['mappings'], 'itemids' => $fields['itemid'], 'webitems' => true, 'preservekeys' => true ]; } $show = array_flip($fields['show']); /* * Select original item name in several cases: if user is in normal dashboards or in template dashboards when * user is in view mode to display that item name in widget name. Item name should be select only if it is not * overwritten. Host name can be attached to item name with delimiter when user is in normal dashboards. */ if ($this->getInput('name', '') === '') { if ($this->getContext() === CWidgetConfig::CONTEXT_DASHBOARD || $this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD && $this->hasInput('dynamic_hostid') && $tmp_items) { $options['output'] = array_merge($options['output'], ['name']); } if ($this->getContext() === CWidgetConfig::CONTEXT_DASHBOARD) { $options['selectHosts'] = ['name']; } } // Add other fields in case current widget is set in dynamic mode, template dashboard or has a specified host. if ($is_dynamic && $tmp_items || !$is_dynamic) { // If description contains user macros, we need "itemid" and "hostid" to resolve them. if (array_key_exists(WIDGET_ITEM_SHOW_DESCRIPTION, $show)) { $options['output'] = array_merge($options['output'], ['itemid', 'hostid']); } if ($fields['units_show'] == 1 && $fields['units'] === '') { $options['output'][] = 'units'; } } if ($is_dynamic) { if ($tmp_items) { $items = API::Item()->get($options); $itemid = key($items); } else { $items = []; } } else { $items = API::Item()->get($options); if ($fields['itemid']) { $itemid = $fields['itemid'][0]; } } if ($items) { $item = $items[$itemid]; $history_limit = array_key_exists(WIDGET_ITEM_SHOW_CHANGE_INDICATOR, $show) ? 2 : 1; $history = Manager::History()->getLastValues($items, $history_limit, $history_period); $value_type = $item['value_type']; if ($history) { $last_value = $history[$itemid][0]['value']; $prev_value = array_key_exists(1, $history[$itemid]) ? $history[$itemid][1]['value'] : null; if (array_key_exists(WIDGET_ITEM_SHOW_TIME, $show)) { $time = date(ZBX_FULL_DATE_TIME, (int) $history[$itemid][0]['clock']); } switch ($value_type) { case ITEM_VALUE_TYPE_FLOAT: case ITEM_VALUE_TYPE_UINT64: if ($fields['units_show'] == 1) { if ($fields['units'] !== '') { $item['units'] = $fields['units']; } } else { $item['units'] = ''; } $formatted_value = formatHistoryValueRaw($last_value, $item, false, $fields['decimal_places']); $value = $formatted_value['value']; $units = $formatted_value['units']; if (!$formatted_value['is_mapped']) { $numeric_formatting = getNumericFormatting(); $decimal_pos = strrpos($value, $numeric_formatting['decimal_point']); if ($decimal_pos !== false) { $decimals = substr($value, $decimal_pos); $value = substr($value, 0, $decimal_pos); } } if (array_key_exists(WIDGET_ITEM_SHOW_CHANGE_INDICATOR, $show) && $prev_value !== null) { if ($formatted_value['is_mapped']) { if ($last_value != $prev_value) { $change_indicator = self::CHANGE_INDICATOR_UP_DOWN; } } elseif ($last_value > $prev_value) { $change_indicator = self::CHANGE_INDICATOR_UP; } elseif ($last_value < $prev_value) { $change_indicator = self::CHANGE_INDICATOR_DOWN; } } break; case ITEM_VALUE_TYPE_STR: case ITEM_VALUE_TYPE_TEXT: case ITEM_VALUE_TYPE_LOG: $value = formatHistoryValue($last_value, $items[$itemid], false); if (array_key_exists(WIDGET_ITEM_SHOW_CHANGE_INDICATOR, $show) && $prev_value !== null && $last_value !== $prev_value) { $change_indicator = self::CHANGE_INDICATOR_UP_DOWN; } break; } } else { $value_type = ITEM_VALUE_TYPE_TEXT; // Since there is no value, we can still show time. if (array_key_exists(WIDGET_ITEM_SHOW_TIME, $show)) { $time = date(ZBX_FULL_DATE_TIME); } } if ($this->getInput('name', '') === '') { if ($this->getContext() === CWidgetConfig::CONTEXT_DASHBOARD || $this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD && $this->hasInput('dynamic_hostid')) { // Resolve original item name when user is in normal dashboards or template dashboards view mode. $name = $items[$itemid]['name']; } if ($this->getContext() === CWidgetConfig::CONTEXT_DASHBOARD) { $name = $items[$itemid]['hosts'][0]['name'].NAME_DELIMITER.$name; } } /* * It doesn't matter if item has value or not, description can be resolved separately if needed. If item * will have value it will resolve, otherwise it will not. */ if (array_key_exists(WIDGET_ITEM_SHOW_DESCRIPTION, $show)) { // Overwrite item name with the custom description. $items[$itemid]['name'] = $fields['description']; // Do not resolve macros if using template dashboard. Template dashboards only have edit mode. if ($this->getContext() === CWidgetConfig::CONTEXT_DASHBOARD || $this->getContext() === CWidgetConfig::CONTEXT_TEMPLATE_DASHBOARD && $this->hasInput('dynamic_hostid')) { $items = CMacrosResolverHelper::resolveWidgetItemNames($items); } // All macros in item name are resolved here. $description = $items[$itemid]['name']; } $cells = self::arrangeByCells($fields, [ 'description' => $description, 'value_type' => $value_type, 'units' => $units, 'value' => $value, 'decimals' => $decimals, 'change_indicator' => $change_indicator, 'time' => $time, 'items' => $items, 'itemid' => $itemid ]); // Use the real item value type. $url = (new CUrl('history.php')) ->setArgument('action', ($items[$itemid]['value_type'] == ITEM_VALUE_TYPE_FLOAT || $items[$itemid]['value_type'] == ITEM_VALUE_TYPE_UINT64) ? HISTORY_GRAPH : HISTORY_VALUES ) ->setArgument('itemids[]', $itemid); } else { $error = _('No permissions to referred object or it does not exist!'); } $this->setResponse(new CControllerResponseData([ 'name' => $this->getInput('name', $name), 'cells' => $cells, 'url' => $url, 'bg_color' => $fields['bg_color'], 'error' => $error, 'user' => [ 'debug_mode' => $this->getDebugMode() ] ])); } /** * Arrange all widget parts by cells, apply all related configuration settings to each part. * * @static * * @param array $fields Input fields from the form. * @param array $fields['show'] Flags to show description, value, time and change indicator. * @param int $fields['desc_v_pos'] Vertical position of the description. * @param int $fields['desc_h_pos'] Horizontal position of the description. * @param int $fields['desc_bold'] Font weight of the description (0 - normal, 1 - bold). * @param int $fields['desc_size'] Font size of the description. * @param string $fields['desc_color'] Font color of the description. * @param int $fields['value_v_pos'] Vertical position of the value. * @param int $fields['value_h_pos'] Horizontal position of the value. * @param int $fields['value_bold'] Font weight of the value (0 - normal, 1 - bold). * @param int $fields['value_size'] Font size of the value. * @param string $fields['value_color'] Font color of the value. * @param int $fields['units_show'] Display units or not (0 - hide, 1 - show). * @param int $fields['units_pos'] Position of the units. * @param int $fields['units_bold'] Font weight of the units (0 - normal, 1 - bold). * @param int $fields['units_size'] Font size of the units. * @param string $fields['units_color'] Font color of the units. * @param int $fields['decimal_size'] Font size of the fraction. * @param int $fields['time_v_pos'] Vertical position of the time. * @param int $fields['time_h_pos'] Horizontal position of the time. * @param int $fields['time_bold'] Font weight of the time (0 - normal, 1 - bold). * @param int $fields['time_size'] Font size of the time. * @param string $fields['time_color'] Font color of the time. * @param array $values Array of pre-processed data that needs to be displayed. * @param string $values['description'] Item description with all macros resolved. * @param string $values['value_type'] Calculated value type. It can be integer or text. * @param string $values['units'] Units of the item. Can be empty string if nothing to show. * @param string|null $values['value'] Value of the item or NULL if there is no value. * @param string|null $values['decimals'] Decimal places or NULL if there is no decimals to show. * @param int|null $values['change_indicator'] Change indicator type or NULL if indicator should not be shown. * @param string $values['time'] Time when item received the value or current time if no data. * @param array $values['items'] The original array of items. * @param string $values['itemid'] Item ID from the host. * * @return array */ private static function arrangeByCells(array $fields, array $values): array { $cells = []; $show = array_flip($fields['show']); if (array_key_exists(WIDGET_ITEM_SHOW_DESCRIPTION, $show)) { $cells[$fields['desc_v_pos']][$fields['desc_h_pos']] = [ 'item_description' => [ 'text' => $values['description'], 'font_size' => $fields['desc_size'], 'bold' => ($fields['desc_bold'] == 1), 'color' => $fields['desc_color'] ] ]; } if (array_key_exists(WIDGET_ITEM_SHOW_VALUE, $show)) { $item_value_cell = [ 'value_type' => $values['value_type'] ]; if ($fields['units_show'] == 1 && $values['units'] !== '') { $item_value_cell['parts']['units'] = [ 'text' => $values['units'], 'font_size' => $fields['units_size'], 'bold' => ($fields['units_bold'] == 1), 'color' => $fields['units_color'] ]; $item_value_cell['units_pos'] = $fields['units_pos']; } $item_value_cell['parts']['value'] = [ 'text' => $values['value'], 'font_size' => $fields['value_size'], 'bold' => ($fields['value_bold'] == 1), 'color' => $fields['value_color'] ]; if ($values['decimals'] !== null) { $item_value_cell['parts']['decimals'] = [ 'text' => $values['decimals'], 'font_size' => $fields['decimal_size'], 'bold' => ($fields['value_bold'] == 1), 'color' => $fields['value_color'] ]; } $cells[$fields['value_v_pos']][$fields['value_h_pos']] = [ 'item_value' => $item_value_cell ]; } if (array_key_exists(WIDGET_ITEM_SHOW_CHANGE_INDICATOR, $show) && $values['change_indicator'] !== null) { $colors = [ self::CHANGE_INDICATOR_UP => $fields['up_color'], self::CHANGE_INDICATOR_DOWN => $fields['down_color'], self::CHANGE_INDICATOR_UP_DOWN => $fields['updown_color'] ]; // Change indicator can be displayed with or without value. $cells[$fields['value_v_pos']][$fields['value_h_pos']]['item_value']['parts']['change_indicator'] = [ 'type' => $values['change_indicator'], 'font_size' => ($values['decimals'] !== null) ? max($fields['value_size'], $fields['decimal_size']) : $fields['value_size'], 'color' => $colors[$values['change_indicator']] ]; } if (array_key_exists(WIDGET_ITEM_SHOW_TIME, $show)) { $cells[$fields['time_v_pos']][$fields['time_h_pos']] = [ 'item_time' => [ 'text' => $values['time'], 'font_size' => $fields['time_size'], 'bold' => ($fields['time_bold'] == 1), 'color' => $fields['time_color'] ] ]; } // Sort data column blocks in order - left, center, right. foreach ($cells as &$row) { ksort($row); } unset($row); return $cells; } }