'uniqueid', * 'href' => 'value', * 'title' => 'title', * 'callback' => true|false, * 'class' => 'fa fa-icon' * ), * ... * ); * * If the callback is true, the Cacti attribute will be added to the href * to present only the contents and not to include both the headers. If * the link must go off page, simply make sure $callback is false. There * is a requirement to use fontawesome icon sets for this class, but it * can include other classes. In addition, the href can be a hash '#' if * your page has a ready function that has it's own javascript. * * @param mixed $add_label Used with legacy behavior to add specific text to the link. * This parameter is only used in the legacy behavior. * @param bool $showcols Show the column selector icon * * @return void */ function html_start_box(string $title, string $width, bool $div, int $cell_padding, string $align, mixed $url_or_buttons, mixed $add_label = false, bool $showcols = false) : void { static $table_suffix = 1; static $help_count = 0; static $mode_count = 0; static $beta_count = 0; if ($add_label === false) { $add_label = __('Add'); } if (!is_cacti_release() && $title != '' && $beta_count == 0) { $title .= ' [ ' . CACTI_VERSION_BRIEF_FULL . ' ]'; $beta_count = 1; } if (POLLER_ID > 1 && $title != '' && $mode_count == 0) { // @phpstan-ignore-line $title .= ' [ ' . __('Remote Server') . ': '; if (CACTI_CONNECTION == 'offline') { $title .= '' . __('Offline') . ''; } elseif (CACTI_CONNECTION == 'recovery') { $title .= '' . __('Recovering') . ''; } else { $title .= __('Online'); } $title .= ' ]'; $mode_count = 1; } $table_prefix = basename(get_current_page(), '.php'); if (!ierv('action')) { $table_prefix .= '_' . clean_up_name(gnrv('action')); } elseif (!ierv('report')) { $table_prefix .= '_' . clean_up_name(gnrv('report')); } elseif (!ierv('tab')) { $table_prefix .= '_' . clean_up_name(gnrv('tab')); } $table_id = $table_prefix . $table_suffix; // localStorage key for persisted column widths. Keep a stable logical // discriminator (tab/report, or a non-transient action) so unrelated table // schemas never share widths, while normalizing the transient filter-refresh // actions so a table keeps its widths across a filter change / refresh. $colsizing_view = ''; if (!ierv('action')) { $colsizing_view = clean_up_name(gnrv('action')); if (in_array($colsizing_view, ['go', 'change', 'clear', 'reset'], true)) { $colsizing_view = ''; } } elseif (!ierv('report')) { $colsizing_view = clean_up_name(gnrv('report')); } elseif (!ierv('tab')) { $colsizing_view = clean_up_name(gnrv('tab')); } $colsizing_id = basename(get_current_page(), '.php') . ($colsizing_view != '' ? '_' . $colsizing_view : '') . $table_suffix . '_child'; if ($title != '') { print "
"; print '
'; print "
" . ($title != '' ? '' . $title . '' : '') . '
'; print "
"; $page = get_current_page(); $help_file = html_help_page($page); if ($help_file === false) { if (isrv('tab')) { $tpage = $page . ':' . gnrv('tab'); $help_file = html_help_page($tpage); } } if ($help_file === false) { if (isrv('action')) { $tpage = $page . ':' . gnrv('action'); $help_file = html_help_page($tpage); } } if ($help_file !== false && $help_count == 0 && is_realm_allowed(28)) { print ""; $help_count = 1; } if ($showcols) { print ""; } if ($url_or_buttons != '' && !is_array($url_or_buttons)) { print ""; } else { if (is_array($url_or_buttons)) { if (cacti_sizeof($url_or_buttons)) { foreach ($url_or_buttons as $icon) { if (isset($icon['callback']) && $icon['callback'] === true) { $classo = 'pic'; } else { $classo = ''; } if (isset($icon['class']) && $icon['class'] !== '') { $classi = $icon['class']; } else { $classi = 'ti ti-plus plusAdd'; } if (isset($icon['href'])) { $href = htmle($icon['href']); } else { $href = '#'; } if (isset($icon['data_url'])) { $href = '#'; $data_url = " data-url='" . html_escape_url($icon['data_url']) . "'"; $classo .= ' linkOverDark cactiPostAction'; } else { $data_url = ''; } if (isset($icon['title'])) { $title = $icon['title']; } else { $title = $add_label; } print ""; } } } else { print ''; } } print '
'; if ($div === true) { print "
"; } else { print ""; } } else { print "
"; if ($div === true) { print "
"; } else { print "
"; } } $table_suffix++; } /** * Wrapper function for the html_start_box to control filters which presently are displayed * as tables in Cacti. This function will show the three bar show/hide column setting * * @param string $title * @param mixed $url_or_buttons * @param bool $div * @param bool $showcols * @param mixed $add_label * @param string $width * * @return void */ function html_filter_start_box(string $title, mixed $url_or_buttons = '', bool $div = false, bool $showcols = true, mixed $add_label = false, string $width = '100%') : void { html_start_box($title, $width, $div, 3, 'center', $url_or_buttons, $add_label, $showcols); } /** * html_sub_tabs - Creates a memory persistent sub-tab interface * for a page or pages using a simple method to lay those tabs * out. * * @param array $tabs An associative array of tab variables and names * Alternatively an array of names that can be converted * using the cacti_strtoupper() function to titles. * @param string $uri A string of URL parameters like 'action=edit&id=x' * @param string $session_var An option session variable to use to store * the current tab status. Defaults to the page * name and the suffix of current_tab * * @return void Output is printed to standard output */ function html_sub_tabs(array $tabs, string $uri = '', string $session_var = '') : void { // determine the session variables if not set if ($session_var == '') { $session_var = basename(get_current_page(), '.php') . '_current_tab'; } $page_name = get_current_page() . '?' . $uri . ($uri != '' ? '&' : ''); // set the default settings category if (!isrv('tab')) { // there is no selected tab; select the first one if (isset($_SESSION[$session_var])) { $current_tab = $_SESSION[$session_var]; } else { $current_tab = array_keys($tabs)[0]; } } else { $current_tab = grv('tab'); } // Check to see if the tab exists, and if not, use the default if (!isset($tabs[$current_tab])) { $current_tab = array_keys($tabs)[0]; } $_SESSION[$session_var] = $current_tab; srv('tab', $current_tab); // draw the categories tabs on the top of the page print '
'; print "
'; print '
'; } /** * html_end_box - draws the end of an HTML box * * @param bool $trailing_br Whether to draw a trailing
tag after ending * @param bool $div Whether type of box is div or table * * @return void */ function html_end_box(bool $trailing_br = true, bool $div = false) : void { if ($div) { print ''; } else { print '
'; } if ($trailing_br == true) { print "
"; } } function html_filter_end_box() : void { html_end_box(true, true); } /** * html_graph_area - draws an area the contains full sized graphs * * @param array $graph_array The array to contains graph information. for each graph in the * array, the following two keys must exist * $arr[0]["local_graph_id"] // graph id * $arr[0]["title_cache"] // graph title * @param string $no_graphs_message Display this message if no graphs are found in $graph_array * @param string $extra_url_args Extra arguments to append to the url * @param string $header Html to use as a header * @param int $columns The number of columns to present * @param int $tree_id The tree id if this is a tree thumbnail * @param int $branch_id The branch id if this is a tree thumbnail * * @return void */ function html_graph_area(array &$graph_array, string $no_graphs_message = '', string $extra_url_args = '', string $header = '', int $columns = 0, int $tree_id = 0, int $branch_id = 0) : void { $i = 0; $k = 0; $j = 0; $num_graphs = cacti_sizeof($graph_array); if ($columns == 0) { $columns = read_user_setting('num_columns'); } ?> '; if ($num_graphs > 0) { if ($header != '') { print $header; } foreach ($graph_array as $graph) { if (!isset($graph['host_id'])) { [$graph['host_id'], $graph['disabled']] = db_fetch_row_prepared('SELECT host_id, disabled FROM graph_local AS gl LEFT JOIN host AS h ON gl.host_id = h.id WHERE gl.id = ?', [$graph['local_graph_id']]); } ?>
'>
' graph_width='' graph_height='' title_font_size=''>
' class='noprint graphDrillDown graphDrillDownToggle'>
' . htmle($graph['title_cache']) . '
' : ''); ?>
$no_graphs_message"; } } print ''; } /** * html_graph_thumbnail_area - draws an area the contains thumbnail sized graphs * * @param array $graph_array The array to contains graph information. for each graph in the * array, the following two keys must exist * $arr[0]["local_graph_id"] // graph id * $arr[0]["title_cache"] // graph title * @param string $no_graphs_message Display this message if no graphs are found in $graph_array * @param string $extra_url_args Extra arguments to append to the url * @param string $header Html to use as a header * @param int $columns The number of columns to present * @param int $tree_id The tree id if this is a tree thumbnail * @param int $branch_id The branch id if this is a tree thumbnail * * @return void */ function html_graph_thumbnail_area(array &$graph_array, string $no_graphs_message = '', string $extra_url_args = '', string $header = '', int $columns = 0, int $tree_id = 0, int $branch_id = 0) : void { $i = 0; $k = 0; $j = 0; $num_graphs = cacti_sizeof($graph_array); if ($columns == 0) { $columns = read_user_setting('num_columns'); } ?> '; if ($num_graphs > 0) { if ($header != '') { print $header; } $start = true; foreach ($graph_array as $graph) { if (!isset($graph['host_id'])) { $gdata = db_fetch_row_prepared('SELECT host_id, disabled FROM graph_local AS gl LEFT JOIN host AS h ON gl.host_id = h.id WHERE gl.id = ?', [$graph['local_graph_id']]); if (cacti_sizeof($gdata)) { $graph['host_id'] = $gdata['host_id']; $graph['disabled'] = $gdata['disabled']; } else { continue; } } if (isset($graph['graph_template_name'])) { if (isset($prev_graph_template_name)) { if ($prev_graph_template_name != $graph['graph_template_name']) { $prev_graph_template_name = $graph['graph_template_name']; } } else { $prev_graph_template_name = $graph['graph_template_name']; } } elseif (isset($graph['data_query_name'])) { if (isset($prev_data_query_name)) { if ($prev_data_query_name != $graph['data_query_name']) { $print = true; $prev_data_query_name = $graph['data_query_name']; } else { $print = false; } } else { $print = true; $prev_data_query_name = $graph['data_query_name']; } if ($print) { print "
" . __('Data Query:') . ' ' . htmle($graph['data_query_name']) . '
'; } } ?>
'>
' graph_width='' graph_height=''>
' class='noprint graphDrillDown graphDrillDownToggle'>
" . htmle($graph['title_cache']) . '
' : ''); ?> $no_graphs_message"; } } print ''; } /** * Generates and prints HTML for graph drilldown icons. * * This function creates a set of icons for various graph-related actions such as * viewing graph details, exporting CSV data, viewing time graphs, editing devices, * editing graph templates, viewing graphs in real-time, and killing spikes in graphs. * It also allows for plugin hooks to add additional icons. * * @param int $local_graph_id The ID of the local graph. * @param string $type The type of icons to generate, default is 'graph_buttons'. * @param int $tree_id The ID of the tree, default is 0. * @param int $branch_id The ID of the branch, default is 0. * * @return void */ function graph_drilldown_icons(int $local_graph_id, string $type = 'graph_buttons', int $tree_id = 0, int $branch_id = 0) : void { static $rand = 0; $aggregate_url = aggregate_build_children_url($local_graph_id); $graph_template_id = db_fetch_cell_prepared('SELECT graph_template_id FROM graph_local WHERE id = ?', [$local_graph_id]); print ""; print "
"; print "
"; print "
"; print "
"; if (is_realm_allowed(3)) { $host_id = db_fetch_cell_prepared('SELECT host_id FROM graph_local WHERE id = ?', [$local_graph_id]); if ($host_id > 0) { print ""; print '
'; $rand++; } } if (is_realm_allowed(10) && $graph_template_id > 0) { print ""; print '
'; } if (read_config_option('realtime_enabled') == 'on' && is_realm_allowed(25)) { if (read_user_setting('realtime_mode') == '' || read_user_setting('realtime_mode') == '1') { print "
"; } else { print "
"; } } if (is_realm_allowed(1043)) { print ""; print '
'; } if ($aggregate_url != '') { print $aggregate_url; } api_plugin_hook($type, [ 'hook' => $type, 'local_graph_id' => $local_graph_id, 'rra' => 0, 'view_type' => $tree_id > 0 ? 'tree' : 'preview', 'tree_id' => $tree_id, 'branch_id' => $branch_id] ); print '
'; } /** * Generates an HTML navigation bar for paginated content. * * @param string $base_url The base URL for the navigation links. * @param int $max_pages The maximum number of pages to display in the navigation. * @param int $current_page The current page number. * @param int $rows_per_page The number of rows to display per page. * @param int $total_rows The total number of rows available. * @param int $colspan The number of columns to span for the navigation bar (default is 30). * @param string $object The name of the object being paginated (default is 'Rows'). * @param string $page_var The query parameter name for the page number (default is 'page'). * @param string $return_to The ID of the HTML element to update with the new page content (default is ''). * @param bool $page_count Whether to display the page count (default is true). * * @return string The generated HTML for the navigation bar. */ function html_nav_bar(string $base_url, int $max_pages, int $current_page, int $rows_per_page, int $total_rows, int $colspan = 30, string $object = '', string $page_var = 'page', string $return_to = '', bool $page_count = true) : string { if (!preg_match('/^[A-Za-z_$][A-Za-z0-9_$]*$/', $page_var)) { $page_var = 'page'; } if ($object == '') { $object = __('Rows'); } $object = htmle($object); if ($total_rows >= $rows_per_page && $page_count) { if (substr_count($base_url, '?') == 0) { $base_url = trim($base_url) . '?'; } else { $base_url = trim($base_url) . '&'; } $url_page_select = get_page_list($current_page, $max_pages, $rows_per_page, $total_rows, $base_url, $page_var, $return_to); $nav = "'; } elseif ($total_rows > 0) { if ($page_count || ($total_rows < $rows_per_page && $current_page == 1)) { $nav = "\n"; } else { if (substr_count($base_url, '?') == 0) { $base_url = trim($base_url) . '?'; } else { $base_url = trim($base_url) . '&'; } $url_page_select = "'; $nav = "\n"; if ($return_to == '') { $return_to = 'main'; } $url = cacti_js_encode($base_url . $page_var); $return_to_json = cacti_js_encode($return_to); $nav .= ""; } } else { $nav = "'; } return $nav; } /** * Retrieve the sort history for this renderer instance, action and tab. */ function html_header_sort_order(int $page_count) : array { $page = $page_count . '_' . str_replace('.php', '', get_current_script_name()); if (isrv('action')) { $page .= '_' . grv('action'); } if (isrv('tab')) { $page .= '_' . grv('tab'); } if (isset($_SESSION['sort_data'][$page])) { $order_data = $_SESSION['sort_data'][$page]; } else { $order_data = [grv('sort_column') => grv('sort_direction')]; } return $order_data; } /** * Resolve one sortable header column without changing the public renderer contracts. * * @return array{mixed, string, mixed, mixed, mixed, string, string} */ function html_header_sort_column(array $display_array, string $db_column, string $sort_column, string $sort_direction, array $order_data, string $primarySort, string $default_icon) : array { $named = array_key_exists('display', $display_array); $display_text = $named ? $display_array['display'] : $display_array[0]; $align = $named ? ($display_array['align'] ?? 'left') : 'left'; $tip = $named ? ($display_array['tip'] ?? '') : ''; $nohide = isset($display_array['nohide']) ? 'nohide' : ''; $isSort = ''; $icon = ''; if ($sort_column == $db_column) { $icon = $sort_direction; $direction = $sort_direction == 'ASC' ? 'DESC' : 'ASC'; $isSort = $db_column == $primarySort ? 'primarySort' : 'secondarySort'; } elseif (isset($order_data[$db_column])) { $icon = $order_data[$db_column]; $direction = $order_data[$db_column] == 'DESC' ? 'ASC' : 'DESC'; $isSort = $db_column == $primarySort ? 'primarySort' : 'secondarySort'; } else { $direction = $named ? ($display_array['sort'] ?? 'ASC') : $display_array[1]; } if (cacti_strtolower($icon) == 'asc') { $icon = 'ti ti-caret-up-filled'; } elseif (cacti_strtolower($icon) == 'desc') { $icon = 'ti ti-caret-down-filled'; } else { $icon = $default_icon; } return [$display_text, $icon, $direction, $align, $tip, $nohide, $isSort]; } /** * html_header_sort - draws a header row suitable for display inside of a box element. When * a user selects a column header, the callback function "filename" will be called to handle * the sort the column and display the altered results. * * @param array $header_items An array containing a list of column items to display. The * format is similar to the html_header, with the exception that it has three * dimensions associated with each element (db_column => display_text, default_sort_order) * alternatively (db_column => array('display' = 'blah', 'align' = 'blah', 'sort' = 'blah')) * @param string $sort_column The value of current sort column. * @param string $sort_direction The value the current sort direction. The actual sort direction * will be opposite this direction if the user selects the same named column. * @param int $last_item_colspan The TD 'colspan' to apply to the last cell in the row * @param string $url A base url to redirect sort actions to * @param string $return_to The id of the object to inject output into as a result of the sort action * * @return void */ function html_header_sort(array $header_items, string $sort_column, string $sort_direction, int $last_item_colspan = 1, string $url = '', string $return_to = '') : void { static $page_count = 0; $table_id = form_get_table_id(); $header_items = form_process_visible_display_text($table_id, $header_items); $order_data = html_header_sort_order($page_count); $primarySort = ''; foreach ($order_data as $key => $direction) { $primarySort = $key; break; } $table_visibility = [ 'table_id' => $table_id, 'columns' => $header_items ]; print ""; $i = 1; foreach ($header_items as $db_column => $display_array) { // if the column is not visible, don't display it if (isset($display_array['visible']) && $display_array['visible'] === false) { continue; } [$display_text, $icon, $direction, $align, $tip, $nohide, $isSort] = html_header_sort_column( $display_array, $db_column, $sort_column, $sort_direction, $order_data, $primarySort, 'ti ti-caret-up-down-filled'); if (($db_column == '') || (substr_count($db_column, 'nosort'))) { print '' . $display_text . ''; } else { print '"; print "
" . $display_text . "
"; } $i++; } print ''; $page_count++; } /** * html_header_sort_checkbox - draws a header row with a 'select all' checkbox in the last cell * suitable for display inside of a box element. When a user selects a column header, * the callback function "filename" will be called to handle the sort the column and display * the altered results. * * @param array $header_items An array containing a list of column items to display. The * fonrmat is similar to the html_header, with the exception that it has three * dimensions associated with each element (db_column => display_text, default_sort_order) * alternatively (db_column => array('display' = 'blah', 'align' = 'blah', 'sort' = 'blah')) * @param string $sort_column The value of current sort column. * @param string $sort_direction The value the current sort direction. The actual sort direction * will be opposite this direction if the user selects the same named column. * @param bool $include_form Whether to include the 'select all' form * @param string $form_action The url to post the 'select all' form to * @param string $return_to The id of the object to inject output into as a result of the sort action * @param string $prefix The prefix to use for the checkbox names * * @return void */ function html_header_sort_checkbox(array $header_items, string $sort_column, string $sort_direction, bool $include_form = true, string $form_action = '', string $return_to = '', string $prefix = 'chk') : void { static $page_count = 0; $table_id = form_get_table_id(); $header_items = form_process_visible_display_text($table_id, $header_items); $order_data = html_header_sort_order($page_count); $primarySort = ''; foreach ($order_data as $key => $direction) { $primarySort = $key; break; } // default to the 'current' file if ($form_action == '') { $form_action = get_current_page(); } $table_visibility = [ 'table_id' => $table_id, 'columns' => $header_items ]; print ""; foreach ($header_items as $db_column => $display_array) { // if the column is not visible, don't display it if (isset($display_array['visible']) && $display_array['visible'] === false) { continue; } [$display_text, $icon, $direction, $align, $tip, $nohide, $isSort] = html_header_sort_column( $display_array, $db_column, $sort_column, $sort_direction, $order_data, $primarySort, 'ti ti-caret-down-filled'); if (($db_column == '') || (substr_count($db_column, 'nosort'))) { print '" . $display_text . ''; } else { print '"; print "
" . $display_text . "
"; } } print "" . ($include_form ? "
" : ''); print ''; $page_count++; } /** * html_header - draws a header row suitable for display inside a box element * * @param array $header_items An array containing a list of items to be included in the header * alternatively and array of header names and alignment * array('display' = 'blah', 'align' = 'blah') * @param int $last_item_colspan The TD 'colspan' to apply to the last cell in the row * @param bool $resizable Is the header resizable * * @return void */ function html_header(array $header_items, int $last_item_colspan = 1, bool $resizable = true) : void { $table_id = form_get_table_id(); $header_items = form_process_visible_display_text($table_id, $header_items); $table_visibility = [ 'table_id' => $table_id, 'columns' => $header_items ]; print ""; $i = 0; foreach ($header_items as $item) { if (is_array($item)) { // if the column is not visible, don't display it if (isset($header_items['visible']) && $header_items['visible'] === false) { continue; } if (isset($item['nohide'])) { $nohide = 'nohide'; } else { $nohide = ''; } if (isset($item['align'])) { $align = $item['align']; } else { $align = 'left'; } if (isset($item['tip'])) { $tip = $item['tip']; } else { $tip = ''; } print '' . htmle($item['display']) . ''; } else { print '' . htmle($item) . ''; } $i++; } print ''; } /** * html_section_header - draws a header row suitable for display inside a box element * but for display as a section title and not as a series of table header columns * * @param mixed $header_item An array of the display name of the header for the section and * optional alignment. * @param int $last_item_colspan The TD 'colspan' to apply to the last cell in the row * @param bool $resizable Will this header be resizable * * @return void */ function html_section_header(mixed $header_item, int $last_item_colspan = 1, bool $resizable = true) : void { print ""; if (is_array($header_item) && isset($header_item['display'])) { print '" . $header_item['display'] . ''; } else { print "" . $header_item . ''; } print ''; } /** * html_header_checkbox - draws a header row with a 'select all' checkbox in the last cell * suitable for display inside a box element * * @param array $header_items An array containing a list of items to be included in the header * alternatively and array of header names and alignment * array('display' = 'blah', 'align' = 'blah') * @param bool $include_form Whether to include the 'select all' form * @param string $form_action The url to post the 'select all' form to * @param bool $resizable Whether the table is resizable * @param string $prefix The prefix to use for the checkbox names * * @return void */ function html_header_checkbox(array $header_items, bool $include_form = true, string $form_action = '', bool $resizable = true, string $prefix = 'chk') : void { $table_id = form_get_table_id(); $header_items = form_process_visible_display_text($table_id, $header_items); // default to the 'current' file if ($form_action == '') { $form_action = get_current_page(); } $table_visibility = [ 'table_id' => $table_id, 'columns' => $header_items ]; print ""; foreach ($header_items as $item) { if (is_array($item)) { // if the column is not visible, don't display it if (isset($header_items['visible']) && $header_items['visible'] === false) { continue; } if (isset($item['nohide'])) { $nohide = 'nohide'; } else { $nohide = ''; } if (isset($item['align'])) { $align = $item['align']; } else { $align = 'left'; } if (isset($item['tip'])) { $tip = $item['tip']; } else { $tip = ''; } print '" . htmle($item['display']) . ''; } else { print "" . htmle($item) . ''; } } print "" . ($include_form ? "" : ''); print ''; } /** * html_create_list - draws the items for a html dropdown given an array of data * * @param array $form_data An array containing data for this dropdown. it can be * formatted in one of three ways: * * $dropdown_array = array( * 'id' => 'name1' * 'id2' => 'name2', * ... * ); * * -- or -- * * $dropdown_array = array( * array( * 'id' => 'id1', * 'name' => 'name1', * ), * array( * 'id' => 'id2', * 'name' => 'name2' * ), * ... * ); * * -- or for custom rendering of icons -- * * $dropdown_array = array( * 'server' => array( * 'display' => __('Some Value'), * 'class' => 'ti ti-server', * 'style' => 'width:30px;...' * ), * ... * ); * * @param string $column_display Used to identify the key to be used for display data. this * is only applicable if the array is formatted using the second method above * @param string $column_id Used to identify the key to be used for id data. this * is only applicable if the array is formatted using the second method above * @param string $prev_val The current value of this form element * * @return void */ function html_create_list(array $form_data, string $column_display, string $column_id, string $prev_val) : void { if (cacti_sizeof($form_data)) { foreach ($form_data as $key => $row) { if (is_array($row)) { if ($column_id != '') { print "'; } elseif (isset($row['host_id'])) { print '>' . htmle($row[$column_display]) . ''; } elseif (isset($row['display'])) { print '>' . htmle($row['display']) . ''; } elseif (isset($row[$column_display])) { print '>' . htmle(null_out_substitutions($row[$column_display])) . ''; } } } } /** * alias of html_escape_request_var() * * @param string $string String the request variable to escape * * @return string The escaped request variable to be returned. */ function htmlerv(string $string) : string { return html_escape(gnrv($string)); } /** * html_escape_request_var - sanitizes a request variable for display * * @param string $string String the request variable to escape * * @return string The escaped request variable to be returned. */ function html_escape_request_var(mixed $string) : string { return html_escape(gnrv($string)); } /** * alias of html_escape() * * @param string $string String the string to escape * * @return string The escaped string to be returned. */ function htmle(mixed $string) : string { return html_escape($string); } /** * html_escape_attr - sanitizes a string for display in an HTML attribute * * @param mixed $string String the attribute value to escape * * @return string The escaped attribute value to be returned. */ function html_escape_attr(mixed $string = '') : string { static $charset; if ($string === null || $string === '') { return ''; } if ($charset == '') { $charset = ini_get('default_charset'); } if ($charset == '') { $charset = 'UTF-8'; } // double_encode is enabled here (unlike html_escape) so that an // already-encoded entity in the input cannot survive into an attribute // value and be decoded back into a quote by the HTML parser. $string = htmlspecialchars((string) $string, ENT_QUOTES | ENT_HTML5, $charset, true); // Grave accent can lead to xss; htmlspecialchars leaves it untouched, so // replace it after escaping to avoid double-encoding the entity itself. return str_replace('`', '`', $string); } /** * html_escape_url - escapes a URL for display in an HTML attribute. This * function does not validate or restrict URL schemes; callers accepting * untrusted navigation targets must validate them separately. * * @param mixed $url URL the attribute value to escape * * @return string The escaped URL value to be returned. */ function html_escape_url(mixed $url = '') : string { return html_escape_attr($url); } /** * cacti_script_data - renders JSON data for scripts to read from the DOM * * @param string $id HTML id for the script element * @param mixed $data Data to encode as a JavaScript-safe JSON literal * * @return string The script tag containing JSON data. */ function cacti_script_data(string $id, mixed $data) : string { return "'; } /** * html_escape - sanitizes a string for display * * @param mixed $string String the string to escape * * @return string The escaped string to be returned. */ function html_escape(mixed $string = '') : string { static $charset; if ($charset == '') { $charset = ini_get('default_charset'); } if ($charset == '') { $charset = 'UTF-8'; } // Grave Accent character can lead to xss if ($string !== null && $string !== '') { $string = str_replace('`', '`', $string); return htmlspecialchars($string, ENT_QUOTES | ENT_HTML5, $charset, false); } else { return ''; } } /** * Renders HTML attributes with escaped values. * * @param array $attributes Attribute name/value pairs * * @return string Rendered attributes with a leading space when not empty */ function html_attributes(array $attributes) : string { $output = ''; foreach ($attributes as $name => $value) { if ($value === null || $value === false) { continue; } $name = (string) $name; if (preg_match('/^[A-Za-z][A-Za-z0-9_:-]*$/', $name) !== 1) { continue; } if ($value === true) { $output .= ' ' . $name; } else { $output .= ' ' . $name . "='" . html_escape_attr($value) . "'"; } } return $output; } /** * Renders a hidden input with escaped attributes. * * @param string $name Input name * @param mixed $value Input value * @param array $attributes Additional attributes * * @return string The rendered hidden input */ function html_hidden_input(string $name, mixed $value, array $attributes = []) : string { return ' 'hidden', 'name' => $name, 'value' => $value] + $attributes) . '>'; } /** * Renders a text input with escaped attributes. * * @param string $name Input name * @param mixed $value Input value * @param array $attributes Additional attributes * * @return string The rendered text input */ function html_text_input(string $name, mixed $value, array $attributes = []) : string { return ' 'text', 'name' => $name, 'value' => $value] + $attributes) . '>'; } /** * html_split_string - takes a string and breaks it into a number of
separated segments * * @param string $string - string to be modified and returned * @param int $length - the maximal string length to split to * @param int $forgiveness - the maximum number of characters to walk back from to determine * the correct break location. * * @return string $new_string - the modified string to be returned. */ function html_split_string(string $string, int $length = 90, int $forgiveness = 10) : string { $new_string = ''; $j = 0; $done = false; while (!$done) { if (mb_strlen($string, 'UTF-8') > $length) { for ($i = 0; $i < $forgiveness; $i++) { if (substr($string, $length - $i, 1) == ' ') { $new_string .= mb_substr($string, 0, $length - $i, 'UTF-8') . '
'; break; } } $string = mb_substr($string, $length - $i, null, 'UTF-8'); } else { $new_string .= $string; $done = true; } $j++; if ($j > 4) { break; } } return $new_string; } /** * draw_graph_items_list - draws a nicely formatted list of graph items for display * on an edit form * * @param array $item_list - an array representing the list of graph items. this array should * come directly from the output of db_fetch_assoc() * @param string $filename - the filename to use when referencing any external url * @param string $url_data - any extra GET url information to pass on when referencing any * external url * @param bool $disable_controls - whether to hide all edit/delete functionality on this form * * @return void */ function draw_graph_items_list(array $item_list, string $filename, string $url_data, bool $disable_controls) : void { global $consolidation_functions, $graph_item_types; include(CACTI_PATH_INCLUDE . '/global_arrays.php'); $display_text = [ [ 'display' => __('Data Source'), 'align' => 'left' ], [ 'display' => __('Seq#'), 'align' => 'center' ], [ 'display' => __('Type'), 'align' => 'left' ], [ 'display' => __('Consolidation'), 'align' => 'left' ], [ 'display' => __('Legend'), 'tip' => __('When exporting or showing the values while hovering over the Graph, what legend to you want displayed? If empty, it will default to data_source_name (consolidation function). This does not work for some Cacti items such as TOTAL_ALL_DATA_SOURCES for example.'), 'align' => 'left' ], [ 'display' => __('GPrint'), 'align' => 'left' ], [ 'display' => __('CDEF'), 'align' => 'left' ], [ 'display' => __('VDEF'), 'align' => 'left' ], [ 'display' => __('Primary Color'), 'align' => 'left' ], [ 'display' => __('Gradient Color'), 'align' => 'left' ], [ 'display' => __('Actions'), 'align' => 'right' ] ]; html_header($display_text); $group_counter = 0; $_graph_type_name = ''; $i = 0; $matrix_title = ''; $customClass = ''; if (cacti_sizeof($item_list)) { foreach ($item_list as $item) { $_graph_type_name = $graph_item_types[$item['graph_type_id']]; // graph grouping display logic $this_row_style = ''; $use_custom_class = false; $hard_return = ''; if (!preg_match('/(GPRINT|TEXTALIGN|HRULE|VRULE|TICK)/', $_graph_type_name)) { $this_row_style = 'font-weight: bold;'; $use_custom_class = true; $item['gprint_name'] = '-'; if ($group_counter % 2 == 0) { $customClass = 'graphItem'; } else { $customClass = 'graphItemAlternate'; } $group_counter++; } // alternating row color if ($use_custom_class == false) { print ""; } else { print ""; } if (empty($item['data_source_name'])) { $item['data_source_name'] = __('No Source'); } switch (true) { case preg_match('/(TEXTALIGN)/', $_graph_type_name): $matrix_title = 'TEXTALIGN: ' . ucfirst($item['textalign']); break; case preg_match('/(TICK)/', $_graph_type_name): $matrix_title = $item['data_source_name'] . ': ' . $item['text_format']; break; case preg_match('/(AREA|STACK|GPRINT|LINE[123])/', $_graph_type_name): $matrix_title = $item['data_source_name'] . ': ' . $item['text_format']; break; case preg_match('/(HRULE)/', $_graph_type_name): $matrix_title = 'HRULE: ' . $item['value']; break; case preg_match('/(VRULE)/', $_graph_type_name): $matrix_title = 'VRULE: ' . $item['value']; break; case preg_match('/(COMMENT)/', $_graph_type_name): $matrix_title = 'COMMENT: ' . $item['text_format']; break; } if (preg_match('/(TEXTALIGN)/', $_graph_type_name)) { $hard_return = ''; } elseif ($item['hard_return'] == 'on') { $hard_return = "<HR>"; } if ($disable_controls == false) { $display = "" . htmle($matrix_title) . ''; } else { $display = htmle($matrix_title); } // data source display print "" . $display . $hard_return . ''; // sequence number print "" . $item['sequence'] . ''; // graph item type display print "" . $graph_item_types[$item['graph_type_id']] . ''; // consolidation function display if (!preg_match('/(TICK|TEXTALIGN|HRULE|VRULE)/', $_graph_type_name)) { print "" . $consolidation_functions[$item['consolidation_function_id']] . ''; } else { print '-'; } // export/hover legend if ($item['legend'] != '') { print "" . htmle($item['legend']) . ''; } else { print '-'; } // grpint display print ""; if ($item['gprint_name'] != '') { print htmle($item['gprint_name']); } else { print '-'; } print ''; // cdef display print ""; if ($item['cdef_name'] != '') { print $item['cdef_name']; } else { print '-'; } print ''; // vdef display print ""; if ($item['vdef_name'] != '') { print $item['vdef_name']; } else { print '-'; } print ''; // color display $blank = '-'; if (preg_match('/(AREA|STACK|TICK|LINE[123])/', $_graph_type_name)) { if (preg_match('/(AREA|STACK)/', $_graph_type_name)) { if ($item['hex'] != '') { $color1 = $item['hex'] . $item['alpha']; if ($item['hex2'] != '') { $color2 = $item['hex2'] . ($item['hex2'] != '' ? $item['alpha2'] : ''); } else { $color2 = $blank; } } else { $color1 = $color2 = $blank; } } else { if ($item['hex'] != '') { $color1 = $item['hex'] . $item['alpha']; } else { $color1 = $blank; } $color2 = $blank; } } else { $color1 = $color2 = $blank; } if (!preg_match('/(TEXTALIGN)/', $_graph_type_name)) { if (preg_match('/(AREA|STACK)/', $_graph_type_name)) { // color1 print ""; print "
"; print "
{$color1}
"; print ''; // color2 print ""; if ($color2 != $blank) { print "
"; print "
{$color2}
"; } else { print $color2; } print ''; } else { // color 1 print ""; if ($color1 != $blank) { print "
"; print "
{$color1}
"; } else { print $color1; } print ''; // color2 print "{$color2}"; } } else { print ''; } if ($disable_controls == false) { print ""; if ($i != cacti_sizeof($item_list) - 1) { print ""; } else { print ""; } if ($i > 0) { print ""; } else { print ""; } print ""; print ''; } print ''; $i++; } } else { print "" . __('No Items') . ''; } } /** * is_menu_pick_active - determines if current selection is active * * @param string $menu_url - url of current page * * @return bool true if active, false if not */ function is_menu_pick_active(string $menu_url) : bool { static $url_array, $url_parts; $menu_parts = []; // special case for host.php?action=edit&create=true if (str_contains($_SERVER['REQUEST_URI'], 'host.php?action=edit&create=true')) { if (str_contains($menu_url, 'host.php?action=edit&create=true')) { return true; } else { return false; } } elseif (!is_array($url_array) || !cacti_sizeof($url_array)) { // break out the URL and variables $url_array = parse_url($_SERVER['REQUEST_URI']); if (isset($url_array['query'])) { parse_str($url_array['query'], $url_parts); } else { $url_parts = []; } } // Host requires another check if (str_contains($menu_url, 'host.php?action=edit&create=true')) { return false; } $menu_array = parse_url($menu_url); if ($menu_array === false) { return false; } if (!array_key_exists('path', $menu_array)) { return false; } $base_url_path = basename($url_array['path']); if (empty($base_url_path)) { cacti_log('INFO: Empty url path detceted - ' . json_encode($url_array), false, 'MENU', POLLER_VERBOSITY_DEBUG); return false; } $base_menu_path = basename($menu_array['path']); if (empty($base_menu_path)) { cacti_log('INFO: Empty menu path detceted - ' . json_encode($menu_array), false, 'MENU', POLLER_VERBOSITY_DEBUG); return false; } if ($base_url_path == $base_menu_path) { if (isset($menu_array['query'])) { parse_str($menu_array['query'], $menu_parts); } else { $menu_parts = []; } if (isset($menu_parts['id'])) { if (isset($url_parts['id'])) { if ($menu_parts['id'] == $url_parts['id']) { return true; } } } elseif (isset($menu_parts['action'])) { if (isset($url_parts['action'])) { if ($menu_parts['action'] == $url_parts['action']) { return true; } } } else { return true; } } return false; } /** * draw_menu - draws the cacti menu for display in the console * * @param mixed $user_menu - the user menu to display * * @return void */ function draw_menu(mixed $user_menu = '') : void { global $user_auth_realm_filenames, $menu, $menu_glyphs; if (!is_array($user_menu)) { $user_menu = $menu; } print "'; } /** * draw_actions_dropdown - draws a table the allows the user to select an action to perform * on one or more data elements * * @param array $actions_array - an array that contains a list of possible actions. this array should * be compatible with the form_dropdown() function * @param int $delete_action - if there is a delete action that should suppress removal of rows * specify it here. If you don't want any delete actions, set to 0. * * @return void */ function draw_actions_dropdown(array $actions_array, int $delete_action = 1) : void { global $form_id; if ($actions_array === null || cacti_sizeof($actions_array) == 0) { return; } if (!isset($actions_array[0])) { $my_actions[0] = __('Choose an action'); $my_actions += $actions_array; $actions_array = $my_actions; } ?>
`) with a single table data cell (``) * that contains the provided text. The text is escaped using the `htmle` function * to prevent XSS attacks. * * @param string $text The text to be displayed inside the table cell. * * @return void */ function form_area(string $text) : void { ?> $name) { if (basename($page) == $basename) { return true; } } } } } return false; } function html_show_tabs_left() : void { global $tabs_left; $realm_allowed = []; $realm_allowed[7] = is_realm_allowed(7); $realm_allowed[8] = is_realm_allowed(8); $realm_allowed[18] = is_realm_allowed(18); $realm_allowed[19] = is_realm_allowed(19); $realm_allowed[21] = is_realm_allowed(21); $realm_allowed[22] = is_realm_allowed(22); if ($realm_allowed[8]) { $show_console_tab = true; } else { $show_console_tab = false; } if ($show_console_tab) { $tabs_left[] = [ 'title' => __('Console'), 'id' => 'tab-console', 'url' => CACTI_PATH_URL . 'index.php', ]; } if ($realm_allowed[7]) { if (POLLER_ID > 1 && CACTI_CONNECTION != 'online') { // @phpstan-ignore-line // Don't show the graphs tab when offline } else { $tabs_left[] = [ 'title' => __('Graphs'), 'id' => 'tab-graphs', 'url' => CACTI_PATH_URL . 'graph_view.php', ]; } } if ($realm_allowed[21] || $realm_allowed[22]) { if (POLLER_ID > 1) { // Don't show the reports tab on other pollers } else { $tabs_left[] = [ 'title' => __('Reporting'), 'id' => 'tab-reports', 'url' => CACTI_PATH_URL . 'reports.php' ]; } } if ($realm_allowed[18] || $realm_allowed[19]) { $tabs_left[] = [ 'title' => __('Logs'), 'id' => 'tab-logs', 'url' => CACTI_PATH_URL . ($realm_allowed[18] ? 'clog.php' : 'clog_user.php'), ]; } // Get Plugin Text Out of Band ob_start(); api_plugin_hook('top_graph_header_tabs'); $tab_text = trim((string) ob_get_clean()); $tab_text = str_replace('', '|', $tab_text); $tab_text = str_replace('', '', $tab_text); $elements = explode('|', $tab_text); $count = 0; foreach ($elements as $p) { $p = trim($p); if ($p == '') { continue; } $altpos = strpos($p, 'alt='); $hrefpos = strpos($p, 'href='); $idpos = strpos($p, 'id='); if ($altpos !== false) { $alt = substr($p, $altpos + 4); $parts = explode("'", $alt); if ($parts[0] == '') { $alt = $parts[1]; } else { $alt = $parts[0]; } } else { $alt = __('Title'); } if ($hrefpos !== false) { $href = substr($p, $hrefpos + 5); $parts = explode("'", $href); if ($parts[0] == '') { $href = $parts[1]; } else { $href = $parts[0]; } } else { $href = 'unknown'; } if ($idpos !== false) { $id = substr($p, $idpos + 3); $parts = explode("'", $id); if ($parts[0] == '') { $id = $parts[1]; } else { $id = $parts[0]; } } else { $id = 'unknown' . $count; $count++; } $tabs_left[] = ['title' => ucwords($alt), 'id' => 'tab-' . $id, 'url' => $href]; } if (POLLER_ID > 1 && CACTI_CONNECTION != 'online') { // @phpstan-ignore-line // Only show external links when online } else { $external_links = db_fetch_assoc('SELECT id, title FROM external_links WHERE style="TAB" AND enabled="on" ORDER BY sortorder'); if (cacti_sizeof($external_links)) { foreach ($external_links as $tab) { if (is_realm_allowed($tab['id'] + 10000)) { $tabs_left[] = [ 'title' => $tab['title'], 'id' => 'tab-link' . $tab['id'], 'url' => CACTI_PATH_URL . 'link.php?id=' . $tab['id'] ]; } } } } $i = 0; $me_base = get_current_page(); foreach ($tabs_left as $tab) { $tab_base = basename($tab['url']); if ($tab_base == 'graph_view.php' && ($me_base == 'graph_view.php' || $me_base == 'graph.php')) { $tabs_left[$i]['selected'] = true; } elseif (isrv('id') && ($tab_base == 'link.php?id=' . gnrv('id')) && $me_base == 'link.php') { $tabs_left[$i]['selected'] = true; } elseif ($tab_base == 'index.php' && is_console_page($me_base)) { $tabs_left[$i]['selected'] = true; } elseif ($tab_base == $me_base) { $tabs_left[$i]['selected'] = true; } else { $tabs_left[$i]['selected'] = false; } $i++; } $i = 0; print "
'; } function html_graph_tabs_right() : void { global $tabs_right; $theme = get_selected_theme(); $tabs_right = []; if (is_view_allowed('show_tree')) { $tabs_right[] = [ 'title' => __('Tree View'), 'image' => get_theme_paths('%s', 'images/tab_tree.gif'), 'id' => 'tree', 'url' => 'graph_view.php?action=tree', ]; } if (is_view_allowed('show_list')) { $tabs_right[] = [ 'title' => __('List View'), 'image' => get_theme_paths('%s', 'images/tab_list.gif'), 'id' => 'list', 'url' => 'graph_view.php?action=list', ]; } if (is_view_allowed('show_preview')) { $tabs_right[] = [ 'title' => __('Preview'), 'image' => get_theme_paths('%s', 'images/tab_preview.gif'), 'id' => 'preview', 'url' => 'graph_view.php?action=preview', ]; } $i = 0; foreach ($tabs_right as $tab) { if ($tab['id'] == 'tree') { if (isrv('action') && gnrv('action') == 'tree') { $tabs_right[$i]['selected'] = true; } } elseif ($tab['id'] == 'list') { if (isrv('action') && gnrv('action') == 'list') { $tabs_right[$i]['selected'] = true; } } elseif ($tab['id'] == 'preview') { if (isrv('action') && gnrv('action') == 'preview') { $tabs_right[$i]['selected'] = true; } } elseif (strstr(get_current_page(false), $tab['url'])) { $tabs_right[$i]['selected'] = true; } if (!isset($tabs_right[$i]['selected'])) { $tabs_right[$i]['selected'] = false; } $i++; } print "
'; } function html_transform_graph_template_ids(mixed $ids) : string { $return_ids = []; if (str_contains($ids, ',')) { $ids = explode(',', $ids); } else { $ids = [$ids]; } foreach ($ids as $id) { if (is_numeric($id)) { $return_ids[] = intval($id); } elseif (str_contains($id, 'cg_')) { $cg_id = str_replace('cg_', '', $id); // non-numeric remainder would coerce to 0 (not templated); skip it instead if (is_numeric($cg_id)) { $return_ids[] = intval($cg_id); } } else { $id = str_replace('dq_', '', $id); $graph_template_id = db_fetch_cell_prepared('SELECT graph_template_id FROM snmp_query_graph WHERE id = ?', [$id]); // a missing row returns false; skip it so id 0 (not templated) is not injected if (is_numeric($graph_template_id)) { $return_ids[] = intval($graph_template_id); } } } return implode(',', $return_ids); } function html_make_device_where() : string { $sql_where = ''; if (isrv('site_id') && gfrv('site_id') > 0) { $sql_where .= ($sql_where != '' ? ' AND ' : ' (') . 'h.site_id = ' . grv('site_id'); } if (isrv('location') && gnrv('location') != '-1') { $sql_where .= ($sql_where != '' ? ' AND ' : ' (') . 'h.location = ' . db_qstr(gnrv('location')); } if (isrv('host_template_id') && gfrv('host_template_id') > 0) { $sql_where .= ($sql_where != '' ? ' AND ' : ' (') . 'h.host_template_id = ' . grv('host_template_id'); } if (isrv('external_id') && gnrv('external_id') != '-1') { $sql_where .= ($sql_where != '' ? ' AND ' : ' (') . 'h.external_id = ' . db_qstr(gnrv('external_id')); } // cross-filter the Device list by the selected Graph Template(s); -1 == 'All' applies no restriction if (isrv('graph_template_id') && gnrv('graph_template_id') != '-1') { // ajax_hosts reaches this before the graph filter is sanitized, so validate the raw // request value against the same allowlist the filter uses before transforming it $requested = gnrv('graph_template_id'); if (is_string($requested) && preg_match('/^(cg_[0-9]+|dq_[0-9]+|-?[0-9]+)(,(cg_[0-9]+|dq_[0-9]+|-?[0-9]+))*$/', $requested)) { $graph_templates = html_transform_graph_template_ids($requested); if ($graph_templates !== '' && $graph_templates !== '-1') { $sql_where .= ($sql_where != '' ? ' AND ' : ' (') . db_in_clause('gl.graph_template_id', $graph_templates); } } } if ($sql_where != '') { $sql_where .= ')'; } return $sql_where; } function html_graph_order_filter_array() : array { $return = []; if (read_config_option('dsstats_enable') == '') { $data_sources = ['-1' => __('Enable Data Source Statistics to Sort')]; $return['graph_source'] = [ 'method' => 'drop_array', 'friendly_name' => __('Source'), 'filter' => FILTER_CALLBACK, 'filter_options' => ['options' => 'sanitize_search_string'], 'array' => $data_sources, 'value' => '', 'default' => '' ]; } else { $mode = read_config_option('dsstats_mode'); // 0 - Peak/Average only, 1 - Kitchen Sink $peak = read_config_option('dsstats_peak'); // '' - Average CF Only, 'on' - Average and Max CF's if (isrv('graph_template_id')) { $graph_templates = html_transform_graph_template_ids(gnrv('graph_template_id')); if (str_contains($graph_templates, ',') || $graph_templates == '' || $graph_templates <= 0) { $show_sort = false; $data_sources = ['-1' => __('Select a Single Template')]; } else { $show_sort = true; $data_sources = array_rekey( db_fetch_assoc_prepared('SELECT DISTINCT data_source_name AS graph_source FROM graph_templates_item AS gti INNER JOIN data_template_rrd AS dtr ON dtr.id = gti.task_item_id WHERE graph_template_id = ? AND local_graph_id = 0 ORDER BY data_source_name', [$graph_templates]), 'graph_source', 'graph_source' ); if (gnrv('graph_source') == '' || gnrv('graph_source') == '-1' || !in_array(gnrv('graph_source'), $data_sources, true)) { if (cacti_sizeof($data_sources)) { srv('graph_source', array_keys($data_sources)[0]); srv('graph_order', 'desc'); } } } $return['graph_source'] = [ 'method' => 'drop_array', 'friendly_name' => __('Source'), 'filter' => FILTER_CALLBACK, 'filter_options' => ['options' => 'sanitize_search_string'], 'array' => $data_sources, 'value' => '' ]; if ($show_sort) { $options = [ 'asc' => __('Ascending'), 'desc' => __('Descending') ]; $return['graph_order'] = [ 'method' => 'drop_array', 'friendly_name' => __('Order'), 'filter' => FILTER_CALLBACK, 'filter_options' => ['options' => 'sanitize_search_string'], 'default' => 'desc', 'array' => $options, 'value' => 'desc' ]; if ($peak == 'on') { $options = [ '0' => __esc('Average'), '1' => __esc('Maximum') ]; $return['cf'] = [ 'method' => 'drop_array', 'friendly_name' => __('CF'), 'filter' => FILTER_VALIDATE_INT, 'default' => '0', 'array' => $options, 'value' => gnrv('cf') ]; } if ($mode == 0) { $options = [ 'average' => __esc('Average'), 'peak' => __esc('Maximum') ]; $return['measure'] = [ 'method' => 'drop_array', 'friendly_name' => __('Measure'), 'filter' => FILTER_CALLBACK, 'filter_options' => ['options' => 'sanitize_search_string'], 'default' => 'average', 'array' => $options, 'value' => gnrv('measure') ]; } else { $options = [ 'average' => __esc('Average'), 'peak' => __esc('Maximum'), 'p25n' => __esc('25th Percentile'), 'p50n' => __esc('50th Percentile (Median)'), 'p75n' => __esc('75th Percentile'), 'p90n' => __esc('90th Percentile'), 'p95n' => __esc('95th Percentile'), 'sum' => __esc('Total/Sum/Bandwidth') ]; $return['measure'] = [ 'method' => 'drop_array', 'friendly_name' => __('Measure'), 'filter' => FILTER_CALLBACK, 'filter_options' => ['options' => 'sanitize_search_string'], 'default' => 'average', 'array' => $options, 'value' => gnrv('measure') ]; } } } else { $data_sources = ['-1' => __('Select a Single Template')]; $return['graph_source'] = [ 'method' => 'drop_array', 'friendly_name' => __('Source'), 'filter' => FILTER_CALLBACK, 'filter_options' => ['options' => 'sanitize_search_string'], 'array' => $data_sources, 'value' => '' ]; } } foreach (array_keys($return) as $return_key) { $return[$return_key]['bar'] = true; } return $return; } function html_thumbnails_filter(string $callBack = 'applyGraphFilter') : string { $checked = (grv('thumbnails') == 'true' ? ' checked' : ''); $output = ""; $output .= ""; $output .= "'; $output .= ''; return $output; } function html_business_hours_filter(string $callBack = 'applyGraphFilter') : string { if (read_config_option('business_hours_enable') == 'on') { $checked = (grv('business_hours') == 'true' ? ' checked' : ''); $output = ""; $output .= ""; $output .= "'; $output .= ''; return $output; } return ''; } /** * Generates an HTML host filter dropdown or input field based on configuration. * * @param mixed $host_id - The ID of the host to be selected by default. Defaults to '-1'. * @param string $call_back - The JavaScript function to call when the selection changes. Defaults to 'applyFilter'. * @param string $sql_where - Not applied: the option list is populated via the shared * 'ajax_hosts' action, which is not safe to pass caller-supplied * raw SQL to over the client/server boundary. Callers that need to * constrain the device list must add their own SQL-safe filtering * (e.g. by request var) inside their page's own 'ajax_hosts' handler. * @param bool $noany - Whether to exclude the 'Any' option from the dropdown. Defaults to false. * @param bool $nonone - Whether to exclude the 'None' option from the dropdown. Defaults to false. * * @return void */ function html_host_filter(mixed $host_id = -1, string $call_back = 'applyFilter', string $sql_where = '', bool $noany = false, bool $nonone = false) : void { if (!str_contains($call_back, '()')) { $call_back .= '()'; } if ($host_id == '-1' && isrv('host_id')) { $host_id = gfrv('host_id'); } if ($host_id > 0) { $hostname = db_fetch_cell_prepared('SELECT description FROM host WHERE id = ?', [$host_id]); } elseif ($host_id == 0) { $hostname = __('None'); } else { $hostname = __('Any'); } ?> '; $output .= ''; if (!empty($icon)) { $output .= ""; } $output .= "$text"; if (!empty($subitem)) { $output .= "
    $subitem
"; } $output .= ''; return $output; } /** * Generates the HTML for the SpikeKill menu. * * This function creates a menu for configuring the SpikeKill settings in Cacti. * It includes options for various replacement methods, standard deviations, variance percentages, * variance outliers, kills per RRA, and absolute maximum values. * * @param int $local_graph_id The ID of the local graph. * * @return void */ function html_spikekill_menu(int $local_graph_id) : void { global $settings; $ravgnan1 = html_spikekill_menu_item(__('Average'), html_spikekill_setting('spikekill_avgnan') == 'avg' ? 'ti ti-check' : 'fa', 'skmethod', 'method_avg'); $ravgnan2 = html_spikekill_menu_item(__('NaN\'s'), html_spikekill_setting('spikekill_avgnan') == 'nan' ? 'ti ti-check' : 'fa', 'skmethod', 'method_nan'); $ravgnan3 = html_spikekill_menu_item(__('Last Known Good'), html_spikekill_setting('spikekill_avgnan') == 'last' ? 'ti ti-check' : 'fa', 'skmethod', 'method_last'); $ravgnan = html_spikekill_menu_item(__('Replacement Method'), '', '', '', '', $ravgnan1 . $ravgnan2 . $ravgnan3); $rstddev = ''; foreach ($settings['spikes']['spikekill_deviations']['array'] as $key => $value) { $rstddev .= html_spikekill_menu_item($value, html_spikekill_setting('spikekill_deviations') == $key ? 'ti ti-check' : 'fa', 'skstddev', 'stddev_' . $key); } $rstddev = html_spikekill_menu_item(__('Standard Deviations'), '', '', '', '', $rstddev); $rkills = ''; foreach ($settings['spikes']['spikekill_number']['array'] as $key => $value) { $rkills .= html_spikekill_menu_item($value,html_spikekill_setting('spikekill_number') == $key ? 'ti ti-check' : 'fa', 'skills', 'kills_' . $key); } $rkills = html_spikekill_menu_item(__('Kills Per RRA'), '', '', '', '', $rkills); $rabsmax = ''; foreach ($settings['spikes']['spikekill_absmax']['array'] as $key => $value) { $rabsmax .= html_spikekill_menu_item($value, html_spikekill_setting('spikekill_absmax') == $key ? 'ti ti-check' : 'fa', 'skabsmax', 'absmax_' . $key); } $rabsmax = html_spikekill_menu_item(__('Absolute Max Value'), '', '', '', '', $rabsmax); ?>