'select', // Type * 'name' => 'Search By', // Displayed title for item * 'id' => 'searchby', // Item id and name * 'width' => '120px', // (Optional) Item width * 'size' => '15', // (Optional) Maximum number of items to show in the menu (default 15) * 'value' => $vars['searchby'], // (Optional) Current value(-s) for item * 'values' => array('mac' => 'MAC Address', * 'ip' => 'IP Address')); // Array with option items * - array for 'multiselect' item type (array keys same as above) * $search[] = array('type' => 'multiselect', * 'name' => 'Priorities', * 'id' => 'priority', * 'width' => '150px', * 'subtext' => TRUE, // (Optional) Display items value right of the item name * 'encode' => FALSE, // (Optional) Use var_encode for values, use when values contains commas or empty string * 'value' => $vars['priority'], * 'values' => $priorities); * - array for 'text' or 'input' item type * $search[] = array('type' => 'text', * 'name' => 'Address', * 'id' => 'address', * 'width' => '120px', * 'placeholder' => FALSE, // (Optional) Display item name as pleseholder or left relatively input * 'value' => $vars['address']); * - array for 'datetime' item type * $search[] = array('type' => 'datetime', * 'id' => 'timestamp', * 'presets' => TRUE, // (optional) Show select field with timerange presets * 'min' => dbFetchCell('SELECT MIN(`timestamp`) FROM `syslog`'), // (optional) Minimum allowed date/time * 'max' => dbFetchCell('SELECT MAX(`timestamp`) FROM `syslog`'), // (optional) Maximum allowed date/time * 'from' => $vars['timestamp_from'], // (optional) Current 'from' value * 'to' => $vars['timestamp_to']); // (optional) Current 'to' value * - array for 'sort' item pseudo type * $search[] = array('type' => 'sort', * 'value' => $vars['sort'], * 'values' => $sorts); * - array for 'newline' item pseudo type * $search[] = array('type' => 'newline', * 'hr' => FALSE); // (optional) show or not horizontal line * print_search($search, 'Title here', 'search', url); * * @param array $data * @param string|null $title * @param string $button * @param string|null $url * * @return void */ function print_search($data, $title = NULL, $button = 'search', $url = NULL) { // Cache permissions to session var permissions_cache_session(); //r($_SESSION['cache']); $submit_by_key = FALSE; $string_items = ''; foreach ($data as $item) { if ($url && isset($item['id'])) { // Remove old vars from url $url = preg_replace('/'.$item['id'].'=[^\/]+\/?/', '', $url); } if ($item['type'] == 'sort') { $sort = $item; continue; } else if (isset($item['submit_by_key']) && $item['submit_by_key']) { $submit_by_key = TRUE; } $string_items .= generate_form_element($item); } $form_id = 'search-'.strgen('4'); if ($submit_by_key) { $action = ''; if ($url) { $action .= 'this.form.prop(\'action\', form_to_path(\'' . $form_id . '\'));'; } register_html_resource('script', '$(function(){$(\'form#' . $form_id . '\').each(function(){$(this).find(\'input\').keypress(function(e){if(e.which==10||e.which==13){'.$action.'this.form.submit();}});});});'); } // Form header $string = PHP_EOL . '' . PHP_EOL; $string .= '
' . PHP_EOL; $string .= '
' . PHP_EOL; $string .= '' . PHP_EOL . PHP_EOL; // Print search form echo($string); } // Just callback functions to print_form with $return flag function generate_form($data) { return print_form($data, TRUE); } function generate_form_box($data) { return print_form_box($data, TRUE); } /** * Pretty form generator * * Form options: * id - form id, default is auto generated * type - rows (multiple elements with small amount of rows), horizontal (mostly single element per row), simple (raw form without any grid/divs) * brand - only for rows, adds "other" form title (I think not work and obsolete) * title - displayed form title (only for rows and horizontal) * icon - adds icon to title * class - adds div with class (default box box-solid) in horizontal * space - adds style for base div in rows type and horizontal with box box-solid class (padding: xx) and horizontal type with box class (padding-top: xx) * style - adds style for base form element, default (margin-bottom:0;) * url - form action url, if url set and submit element with id "search" used (or submit_by_key), than form send params as GET query * submit_by_key - send form query by press enter key in text/input forms * fieldset - horizontal specific, array with fieldset names and descriptions, in form element should be add fieldset option with same key name * * Elements options see in generate_form_element() description * * @param array $data Form options and form elements * @param bool $return If used and set to TRUE, print_form() will return form html instead of outputting it. * * @return NULL */ function print_form($data, $return = FALSE) { // Just return if safety requirements are not fulfilled if (isset($data['userlevel']) && $data['userlevel'] > $_SESSION['userlevel']) { return; } // Return if the user doesn't have write permissions to the relevant entity if (isset($data['entity_write_permit']) && !is_entity_write_permitted($data['entity_write_permit']['entity_id'], $data['entity_write_permit']['entity_type'])) { return; } /* // Use modal with form if (isset($data['modal_args']) && !empty($data['modal_args'])) { // Print modal form echo(generate_form_modal($data)); } */ // Time our form filling. $form_start = microtime(TRUE); $form_id = (isset($data['id']) ? $data['id'] : 'form-'.strgen()); $form_class = 'form form-inline'; // default for rows and simple if (isset($data['style'])) { $form_style = ' style="'.$data['style'].'"'; } else { $form_style = ' style="margin-bottom: 0px;"'; } $base_class = array_key_exists('class', $data) ? $data['class'] : OBS_CLASS_BOX; $base_space = $data['space'] ?: '5px'; $used_vars = []; // Cache permissions to session var permissions_cache_session(); //r($_SESSION['cache']); if ($data['submit_by_key']) { $action = ''; if ($data['url']) { $action .= 'this.form.prop(\'action\', form_to_path(\'' . $form_id . '\'));'; } register_html_resource('script', '$(function(){$(\'form#' . $form_id . '\').each(function(){$(this).find(\'input\').keypress(function(e){if(e.which==10||e.which==13){'.$action.'this.form.submit();}});});});'); } // Form elements if ($data['type'] === 'rows') { // Rows form, see example in html/pages/devices.inc.php //$div_padding = 'padding: 0px '.$base_space.' '.$base_space.' '.$base_space.' !important;'; // Top padding set as 0px, all other to base $div_padding = 'padding: '.$base_space.' !important;'; if (str_contains_array($base_class, 'box')) { $base_space = ($data['space'] ? $data['space'] : '10px'); // Box horizontal style $box_args = [ 'id' => 'box-' . $form_id, 'header-border' => TRUE, 'body-style' => $div_padding // Override top padding ]; if (isset($data['title'])) { $box_args['title'] = $data['title']; } $div_begin = generate_box_open($box_args); $div_end = generate_box_close(); unset($box_args); } else { $div_begin = '
' . PHP_EOL; $div_end = '
' . PHP_EOL; } $row_style = ''; $string_elements = ''; // Calculate grid sizes for rows foreach ($data['row'] as $k => $row) { $row_count = safe_count($row); // Default (auto) grid size for elements $row_grid = (int)(12 / $row_count); $grid_count = 0; // Count for custom passed grid sizes foreach ($row as $id => $element) { if (isset($element['div_class']) && preg_match('/col-(?:lg|md|sm)-(\d+)/', $element['div_class'], $matches)) { // Class with col size passed $grid_count += (int)$matches[1]; } else if (isset($element['grid'])) { // Grid size passed if ($element['grid'] > 0 && $element['grid'] <= 12) { $grid_count += (int)$element['grid']; } else { // Incorrect size unset($row[$k]['grid']); } } } $row_grid = 12 - $grid_count; // Free grid size after custom grid $row_grid = (int)($row_grid / $row_count); // Default (auto) grid size for elements if ($grid_count > 2 && $row_grid < 1) { $row_grid = 1; } // minimum 1 for auto if custom grid passed else if ($row_grid < 2) { $row_grid = 2; } // minimum 2 for auto $data['grid'][$k] = $row_grid; // Store default grid size for row } //r($data['grid']); foreach ($data['row'] as $k => $row) { $row_class = 'row'; if (isset($data['row_options'][$k])) // If row options exist for current row { if ($data['row_options'][$k]['class']) { $row_class .= ' ' . $data['row_options'][$k]['class']; } } $string_elements .= '
' . PHP_EOL; foreach ($row as $id => $element) { $used_vars[] = $id; $element['id'] = $id; // Default class with default row grid size or passed from options $grid = (isset($element['grid']) ? $element['grid'] : $data['grid'][$k]); $div_class = 'col-lg-' . $grid . ' col-md-' . $grid . ' col-sm-' . $grid; // By default xs grid always 12 if (isset($element['grid_xs']) && $element['grid_xs'] > 0 && $element['grid_xs'] <= 12) { $div_class .= ' col-xs-' . $element['grid_xs']; } if (empty($element['div_class'])) { $element['div_class'] = $div_class; } else if (isset($element['grid']) && !preg_match('/col-(?:lg|md|sm|xs)-(\d+)/', $element['div_class'])) { // Combine if passed both: grid size and div_class (and class not has col-* grid elements) $element['div_class'] = $div_class . ' ' . $element['div_class']; } if ($element['right']) { $element['div_class'] .= ' col-lg-push-0 col-md-push-0 col-sm-push-0'; } if ($id === 'search') { // Add form_id here, for generate onclick action in submit button if ($data['url']) { $element['form_id'] = $form_id; } } else { // all other cases add form_id $element['form_id'] = $form_id; } // Here added padding-block-start for space between rows (also if row elements moved to newline) //$string_elements .= '
' . PHP_EOL; $string_elements .= '
' . PHP_EOL; // Add space between rows $row_style = 'style="margin-top: '.$base_space.';"'; } // end rows type } elseif ($data['type'] === 'horizontal') { // Horizontal form, see example in html/pages/user_edit.inc.php if (str_contains_array($base_class, [ 'widget', 'box' ])) { $base_space = ($data['space'] ? $data['space'] : '10px'); // Box horizontal style $box_args = [ 'id' => 'box-' . $form_id, 'header-border' => TRUE, 'body-style' => 'padding-top: '.$base_space.' !important;' // Override top padding ]; if (isset($data['title'])) { $box_args['title'] = $data['title']; } $div_begin = generate_box_open($box_args); $div_end = generate_box_close(); unset($box_args); } elseif (empty($base_class)) { // Clean class // Example in html/pages.logon.inc.php $div_begin = PHP_EOL; $div_end = PHP_EOL; } else { // Old box box-solid style (or any custom style) $div_begin = '
' . PHP_EOL; if (isset($data['title'])) { $div_begin .= '
'; $div_begin .= get_icon($data['icon']); $div_begin .= ' '.escape_html($data['title']).'
' . PHP_EOL; } $div_end = '
' . PHP_EOL; } $form_class = 'form form-horizontal'; $row_style = ''; $fieldset = array(); foreach ($data['row'] as $k => $row) { $first_key = key($row); $row_group = $k; $row_elements = ''; $row_label = ''; $row_control_group = FALSE; $i = 0; foreach ($row as $id => $element) { $used_vars[] = $id; $element['id'] = $id; if ($element['fieldset']) { $row_group = $element['fieldset']; // Add this element to group } // Additional element options for horizontal specific form $div_style = ''; switch ($element['type']) { case 'hidden': break; case 'submit': $div_class = 'form-actions'; break; case 'text': case 'input': case 'password': case 'textarea': default: $row_control_group = TRUE; // In horizontal, first element name always placed at left if (!isset($element['placeholder'])) { $element['placeholder'] = TRUE; } // offset == FALSE disable label width and align class control-label if (!isset($element['offset'])) { if (isset($data['fieldset'][$element['fieldset']]['offset'])) { // Copy from fieldset $element['offset'] = $data['fieldset'][$element['fieldset']]['offset']; } else if (($element['type'] === 'raw' || $element['type'] === 'html') && !isset($element['name']) && $first_key === $id) { // When raw/html element first, disable offset $element['offset'] = FALSE; } else { // Default $element['offset'] = TRUE; } } if ($i < 1) { // Add label for first element in row if ($element['name']) { $row_label = ' '.$element['name'].'' . PHP_EOL; } $row_control_id = $element['id'] . '_div'; if ($element['type'] === 'datetime') { $element['name'] = ''; } } // nextrow class element to new line (after label) $div_class = ($element['offset']) ? 'controls' : 'nextrow'; break; } if (!isset($element['div_class'])) { $element['div_class'] = $div_class; } if ($element['div_class'] === 'form-actions') { // Remove margins only for form-actions elements $div_style = 'margin: 0px;'; } //if ($element['right']) //{ // $element['div_class'] .= ' pull-right'; //} if (isset($element['div_style'])) { $div_style .= ' ' . $element['div_style']; } if ($id === 'search') { // Add form_id here, for generate onclick action in submit button if ($data['url']) { $element['form_id'] = $form_id; } } else { // all other cases add form_id $element['form_id'] = $form_id; } $row_elements .= generate_form_element($element); $i++; } if ($element['div_class']) { // no additional divs if empty div class (hidden element for example) $row_begin = $row_label . PHP_EOL . '
' . PHP_EOL . $row_elements . '
' . PHP_EOL; } else { $row_elements = $row_label . PHP_EOL . $row_elements; } if ($row_control_group) { $fieldset[$row_group] .= '
' . PHP_EOL; $fieldset[$row_group] .= $row_elements; $fieldset[$row_group] .= '
' . PHP_EOL; } else { // Do not add control group for submit/hidden $fieldset[$row_group] .= $row_elements; } //$row_style = 'style="margin-top: '.$base_space.';"'; // Add space between rows } foreach ($data['fieldset'] as $row_group => $entry) { if (isset($fieldset[$row_group])) { if (!is_array($entry)) { $entry = array('title' => $entry); } $fieldset_begin = ''; $fieldset_end = ''; // Additional div class if set if (isset($entry['class'])) { $fieldset_begin = '
' . PHP_EOL . $fieldset_begin; $fieldset_end .= '
' . PHP_EOL; } $row_elements = $fieldset_begin . '
'; if (!empty($entry['title'])) { // fieldset title $row_elements .= '

'.escape_html($entry['title']).'

'; } $row_elements .= PHP_EOL . $fieldset[$row_group] . '
' . PHP_EOL; $fieldset[$row_group] = $row_elements . $fieldset_end; } } // Final combining elements $string_elements = implode('', $fieldset); } else { // Simple form, without any divs, see example in html/pages/user_edit.inc.php $div_begin = ''; $div_end = ''; $string_elements = ''; foreach ($data['row'] as $k => $row) { foreach ($row as $id => $element) { $used_vars[] = $id; $element['id'] = $id; if ($id === 'search') { // Add form_id here, for generate onclick action in submit button if ($data['url']) { $element['form_id'] = $form_id; } } else { // all other cases add form_id $element['form_id'] = $form_id; } $string_elements .= generate_form_element($element); } $string_elements .= PHP_EOL; } } // Add CSRF Token if (!in_array('requesttoken', $used_vars) && isset($_SESSION['requesttoken'])) { $string_elements .= generate_form_element([ 'type' => 'hidden', 'id' => 'requesttoken', 'value' => $_SESSION['requesttoken'] ]) . PHP_EOL; $used_vars[] = 'requesttoken'; } // Always clean pagination from form action url $used_vars[] = 'pageno'; $used_vars[] = 'pagination'; $used_vars[] = 'pagesize'; // Remove old vars from url if ($data['url']) { foreach ($used_vars as $var) { $data['url'] = preg_replace('/'.$var.'=[^\/]+\/?/', '', $data['url']); } } // Form header if (isset($data['right']) && $data['right']) { $form_class .= ' pull-right'; } // auto add some common html attribs $form_attribs = [ 'class' => $form_class ]; foreach ([ 'onchange', 'oninput', 'onclick', 'ondblclick', 'onfocus', 'onsubmit' ] as $attrib) { if (isset($data[$attrib])) { $form_attribs[$attrib] = $data[$attrib]; } } $string = PHP_EOL . "" . PHP_EOL; $string .= $div_begin; $string .= '
' . PHP_EOL; if ($data['brand']) { $string .= ' ' . $data['brand'] . '' . PHP_EOL; } if ($data['help']) { $string .= ' ' . $data['help'] . '' . PHP_EOL; } // Form elements $string .= $string_elements; // Form footer $string .= '
' . PHP_EOL; $string .= $div_end; $string .= "" . PHP_EOL; if ($return) { // Save generation time for profiling $GLOBALS['form_time'] += utime() - $form_start; // Return form as string return $string; } // Print form echo($string); // Save generation time for profiling (after echo) $GLOBALS['form_time'] += utime() - $form_start; } // Box specific form (mostly same as in print_form, but support only box style and fieldset options) // FIXME should likely not be in this file? As it's used throughout the software now... function print_form_box($data, $return = FALSE) { // Just return if safety requirements are not fulfilled if (isset($data['userlevel']) && $data['userlevel'] > $_SESSION['userlevel']) { return; } // Return if the user doesn't have write permissions to the relevant entity if (isset($data['entity_write_permit']) && !is_entity_write_permitted($data['entity_write_permit']['entity_id'], $data['entity_write_permit']['entity_type'])) { return; } // Time our form filling. $form_start = microtime(TRUE); $form_id = (isset($data['id']) ? $data['id'] : 'form-'.strgen()); $form_class = 'form form-horizontal'; if (isset($data['style'])) { $form_style = ' style="'.$data['style'].'"'; } else { $form_style = ' style="margin-bottom:0px;"'; } $base_class = (array_key_exists('class', $data) ? $data['class'] : OBS_CLASS_BOX); $base_space = ($data['space'] ? $data['space'] : '15px'); $used_vars = array(); // Cache permissions to session var permissions_cache_session(); //r($_SESSION['cache']); if ($data['submit_by_key']) { $action = ''; if ($data['url']) { $action .= 'this.form.prop(\'action\', form_to_path(\'' . $form_id . '\'));'; } register_html_resource('script', '$(function(){$(\'form#' . $form_id . '\').each(function(){$(this).find(\'input\').keypress(function(e){if(e.which==10||e.which==13){'.$action.'this.form.submit();}});});});'); } $header = ''; if (isset($data['title'])) { $header .= '

' . escape_html($data['title']) . '

' . PHP_EOL; } // Form elements $div_begin = '
' . PHP_EOL; $div_end = '
' . PHP_EOL; if ($data['type'] === 'horizontal') { $row_style = ''; $fieldset = array(); foreach ($data['row'] as $k => $row) { $first_key = key($row); $row_group = $k; $row_elements = ''; $row_label = ''; $row_control_group = FALSE; $i = 0; foreach ($row as $id => $element) { $used_vars[] = $id; $element['id'] = $id; if ($element['fieldset']) { $row_group = $element['fieldset']; // Add this element to group } // Additional element options for horizontal specific form $div_style = ''; switch ($element['type']) { case 'hidden': break; case 'submit': $div_class = 'form-actions'; break; case 'text': case 'input': case 'password': case 'textarea': default: $row_control_group = TRUE; // In horizontal, first element name always placed at left if (!isset($element['placeholder'])) { $element['placeholder'] = TRUE; } // offset == FALSE disable label width and align class control-label if (!isset($element['offset'])) { if (isset($data['fieldset'][$element['fieldset']]['offset'])) { // Copy from fieldset $element['offset'] = $data['fieldset'][$element['fieldset']]['offset']; } elseif (($element['type'] === 'raw' || $element['type'] === 'html') && $first_key === $id) { // When raw/html element first, disable offset $element['offset'] = FALSE; } else { // Default $element['offset'] = TRUE; } } if ($i < 1) { // Add label for first element in row if ($element['name']) { $row_label = ' '.$element['name'].'' . PHP_EOL; } $row_control_id = $element['id'] . '_div'; if ($element['type'] === 'datetime') { $element['name'] = ''; } } // nextrow class element to new line (after label) $div_class = ($element['offset']) ? 'controls' : 'nextrow'; break; } if (!isset($element['div_class'])) { $element['div_class'] = $div_class; } if ($element['div_class'] === 'form-actions') { // Remove margins only for form-actions elements $div_style = 'margin: 0px;'; } //if ($element['right']) //{ // $element['div_class'] .= ' pull-right'; //} if (isset($element['div_style'])) { $div_style .= ' ' . $element['div_style']; } if ($id === 'search') { // Add form_id here, for generate onclick action in submit button if ($data['url']) { $element['form_id'] = $form_id; } } else { // all other cases add form_id $element['form_id'] = $form_id; } $row_elements .= generate_form_element($element); $i++; } if ($element['div_class']) { // no additional divs if empty div class (hidden element for example) $row_begin = $row_label . PHP_EOL . '
' . PHP_EOL . $row_elements . '
' . PHP_EOL; } else { $row_label = str_replace(' class="control-label"', '', $row_label); $row_elements = $row_label . PHP_EOL . $row_elements; } if ($row_control_group) { $fieldset[$row_group] .= '
' . PHP_EOL; $fieldset[$row_group] .= $row_elements; $fieldset[$row_group] .= '
' . PHP_EOL; } else { // Do not add control group for submit/hidden $fieldset[$row_group] .= $row_elements; } //$row_style = 'style="margin-top: '.$base_space.';"'; // Add space between rows } $divs = array(); $fieldset_tooltip = ''; foreach ($data['fieldset'] as $group => $entry) { if (isset($fieldset[$group])) { if (!is_array($entry)) { $entry = array('title' => $entry); } // Custom style if (!isset($entry['style'])) { $entry['style'] = 'padding-bottom: 0px !important;'; // Remove last additional padding space } // Combine fieldsets into common rows if ($entry['div']) { $divs[$entry['div']][] = $group; } else { $divs['row'][] = $group; } $box_args = array('header-border' => TRUE, 'padding' => TRUE, 'id' => $group, ); if (isset($entry['style'])) { $box_args['body-style'] = $entry['style']; } if (isset($entry['title'])) { $box_args['title'] = $entry['title']; if ($entry['icon']) { // $box_args['icon'] => $entry['icon']; } } if (isset($entry['tooltip'])) { $box_args['header-controls'] = array('controls' => array('tooltip' => array('icon' => 'icon-info text-primary', 'anchor' => TRUE, //'url' => '#', 'class' => 'tooltip-from-element', 'data' => 'data-tooltip-id="tooltip-'.$group.'"'))); $fieldset_tooltip .= '' . PHP_EOL; } if (isset($entry['tooltip'])) { $box_args['style'] = $entry['style']; } $fieldset_begin = generate_box_open($box_args); $fieldset_end = generate_box_close(); // Additional div class if set if (isset($entry['class'])) { $fieldset_begin = '
' . PHP_EOL . $fieldset_begin; $fieldset_end .= '
' . PHP_EOL; } $row_elements = $fieldset_begin . '
'; $row_elements .= PHP_EOL . $fieldset[$group] . '
' . PHP_EOL; $fieldset[$group] = $row_elements . $fieldset_end; } } // Combine fieldsets into common rows foreach ($divs as $entry) { $row_elements = $div_begin; foreach ($entry as $i => $group) { $row_elements .= $fieldset[$group]; if ($i > 0) { // unset all fieldsets except first one for replace later unset($fieldset[$group]); } } $row_elements .= $div_end; // now replace first fieldset in group $fieldset[array_shift($entry)] = $row_elements; } // Final combining elements $string_elements = implode('', $fieldset); } // Add CSRF Token if (!in_array('requesttoken', $used_vars) && isset($_SESSION['requesttoken'])) { $string_elements .= generate_form_element(array('type' => 'hidden', 'id' => 'requesttoken', 'value' => $_SESSION['requesttoken'])) . PHP_EOL; $used_vars[] = 'requesttoken'; } // Remove old vars from url if ($data['url']) { foreach ($used_vars as $var) { $data['url'] = preg_replace('/'.$var.'=[^\/]+\/?/', '', $data['url']); } } // Form header $string = PHP_EOL . "" . PHP_EOL; $string .= $header; $string .= '
' . PHP_EOL; // Form elements $string .= $string_elements; // Form footer $string .= '
' . PHP_EOL; $string .= $fieldset_tooltip; $string .= "" . PHP_EOL; if ($return) { // Save generation time for profiling $GLOBALS['form_time'] += utime() - $form_start; // Return form as string return $string; } // Print form echo($string); // Save generation time for profiling (after echo) $GLOBALS['form_time'] += utime() - $form_start; } /** * Generates form elements. The main use for print_search() and print_form(), see examples of this functions. * * Common options (can be in any(mostly) element type): * (string) id - element identificator * (array) attribs - any custom element attrib (where key is attrib name, value - attrib value) * (bool) offset - for horizontal forms enable (default) or disable element offset (shift to the right on 180px) * Options tree: * textarea -\ * (string)id, (string)name, (bool)readonly, (bool)disabled, (string)width, (string)class, * (int)rows, (int)cols, * (string)value, (bool,string)placeholder, (bool)ajax, (array)ajax_vars * text, input, password -\ * (string)id, (string)name, (bool)readonly, (bool)disabled, (string)width, (string)class, * (string)value, (bool,string)placeholder, (bool)ajax, (array)ajax_vars, * (bool)show_password * hidden -\ * (string)id, (string)value * select, multiselect -\ * (string)id, (string)name, (bool)readonly, (bool)disabled, (string)onchange, (string)width, * (string)title, (int)size, (bool)right, (bool)live-search, (bool)encode, (bool)subtext * (string)value, (array)values, (string)icon, * values items can be arrays, ie: * value => array('name' => string, 'group' => string, 'icon' => string, 'class' => string, 'style' => string) * datetime -\ * (string)id, (string)name, (bool)readonly, (bool)disabled, * (string|FALSE)from, (string|FALSE)to, (bool)presets, (string)min, (string)max * (string)value (use it for single input) * checkbox, switch, toggle -\ * (string)id, (string)name, (bool)readonly, (bool)disabled, (string)onchange, * [switch only]: (bool)revert, (int)width, (string)size, (string)off-color, (string)on-color, (string)off-text, (string)on-text * [toggle only]: (string)view, (string)size, (string)palette, (string)group, (string)label, * (string)icon-check, (string)label-check, (string)icon-uncheck, (string)label-uncheck * (string)value, (string)placeholder, (string)title * submit -\ * (string)id, (string)name, (bool)readonly, (bool)disabled, * (string)class, (bool)right, (string)tooltip, * (string)value, (string)form_id, (string)icon * html, raw -\ * (string)id, (bool)offset, * (string)html, (string)value * newline -\ * (string)id, * (bool)hr * * @param array $item Options for current form element * @param string $type Type of form element, also can passed as $item['type'] * @return string Generated form element */ function generate_form_element($item, $type = '') { // Check community edition if (isset($item['community']) && !$item['community'] && OBSERVIUM_EDITION === 'community') { return ''; } $value_isset = isset($item['value']); if (!$value_isset) { $item['value'] = ''; } if (is_array($item['value'])) { // Passed from URI comma values always converted to array, re-implode it $item['value_escaped'] = escape_html(implode(',', $item['value'])); } else { $item['value_escaped'] = escape_html($item['value']); } if (!isset($item['type'])) { $item['type'] = $type; } $string = ''; $element_tooltip = ''; $element_data = ''; // auto add some common html attribs foreach ([ 'onchange', 'oninput', 'onclick', 'ondblclick', 'onfocus', 'onsubmit' ] as $attrib) { if (isset($item[$attrib])) { $item['attribs'][$attrib] = $item[$attrib]; } } if (isset($item['attribs']) && is_array($item['attribs'])) { // Custom html attributes if (isset($item['attribs']['data-toggle'])) { // Enable item specific JS/CSS/Script switch ($item['attribs']['data-toggle']) { case 'confirm': case 'confirmation': if ($item['attribs']['data-toggle'] === 'confirmation') { $item['attribs']['data-toggle'] = 'confirm'; } // popConfirm //register_html_resource('js', 'jquery.popconfirm.js'); //register_html_resource('script', '$("[data-toggle=\'' . $item['attribs']['data-toggle'] . '\']").popConfirm();'); // Bootstrap-Confirmation register_html_resource('js', 'bootstrap-confirmation.js'); //register_html_resource('script', '$("[data-toggle=\'' . $item['attribs']['data-toggle'] . // '\']").confirmation({rootSelector: \'[data-toggle=' . $item['attribs']['data-toggle'] . ']\',});'); //$script_options = [ 'rootSelector: \'[data-toggle=' . $item['attribs']['data-toggle'] . ']\'' ]; if (!isset($item['attribs']['data-btn-ok-label'])) { // default "Yes" //$item['attribs']['data-btn-ok-label'] = 'Yes'; } if (!isset($item['attribs']['data-btn-ok-class'])) { // default "btn btn-xs btn-primary" } if (!isset($item['attribs']['data-btn-ok-icon'])) { // default "glyphicon glyphicon-ok" //$item['attribs']['data-btn-ok-icon'] = 'Yes'; } if (!isset($item['attribs']['data-btn-cancel-label'])) { // default "No" //$item['attribs']['data-btn-cancel-label'] = 'Cheese'; } if (!isset($item['attribs']['data-btn-cancel-class'])) { // default "btn btn-xs btn-default" //$item['attribs']['data-btn-cancel-class'] = 'btn-small btn-warning'; } if (!isset($item['attribs']['data-btn-cancel-icon'])) { // default "glyphicon glyphicon-remove" //$item['attribs']['data-btn-cancel-icon'] = 'icon-sort'; } // migrate from popConfirm if (!isset($item['attribs']['data-title'])) { $item['attribs']['data-title'] = 'Confirmation'; } if (!isset($item['attribs']['data-singleton'])) { $item['attribs']['data-singleton'] = 'true'; } if (!isset($item['attribs']['data-popout'])) { $item['attribs']['data-popout'] = 'true'; } if (isset($item['attribs']['data-confirm-placement'])) { $item['attribs']['data-placement'] = $item['attribs']['data-confirm-placement']; unset($item['attribs']['data-confirm-placement']); } if (isset($item['attribs']['data-confirm-content'])) { $item['attribs']['data-content'] = $item['attribs']['data-confirm-content']; unset($item['attribs']['data-confirm-content']); } //register_html_resource('script', '$("[data-toggle=\'' . $item['attribs']['data-toggle'] . '\']").confirmation({' . implode(', ', $script_options) . '});'); break; case 'switch': // Bootstrap Toggle //$element_data .= ' data-selector="bootstrap-toggle"'; $item['attribs']['data-selector'] = "bootstrap-toggle"; register_html_resource('js', 'bootstrap-toggle.min.js'); //register_html_resource('js', 'bootstrap-toggle.js'); /// DEVEL register_html_resource('css', 'bootstrap-toggle.min.css'); register_html_resource('script', '$("input[data-selector=\'bootstrap-toggle\']").bootstrapToggle();'); /* bootstrapSwitch (deprecated) register_html_resource('js', 'bootstrap-switch.min.js'); //register_html_resource('css', 'bootstrap-switch.css'); register_html_resource('script', '$("[data-toggle=\'' . $item['attribs']['data-toggle'] . '\']").bootstrapSwitch();'); */ break; case 'toggle': if (str_contains($item['class'], 'tiny-toggle')) { // TinyToggle $script = ''; if ($item['onchange']) { // Here toggle specific onchange behavior $script .= 'onChange: function(obj, value) { ' . $item['onchange'] . ' },'; // Set custom element selector $selector = 'tiny-toggle-' . md5($item['onchange']); //$element_data .= ' data-selector="'.$selector.'"'; $item['attribs']['data-selector'] = $selector; register_html_resource('script', '$("input[data-selector=\''.$selector.'\'].tiny-toggle").tinyToggle({' . $script . '});'); unset($item['onchange']); } else { //$element_data .= ' data-selector="tiny-toggle"'; $item['attribs']['data-selector'] = "tiny-toggle"; //register_html_resource('script', '$("[data-toggle=\'' . $item['attribs']['data-toggle'] . '\']").tinyToggle({'.$script.'});'); // this selector intersects with bootstrap toggle register_html_resource('script', '$("input[data-selector=\'tiny-toggle\'].tiny-toggle").tinyToggle();'); } register_html_resource('js', 'tiny-toggle.min.js'); register_html_resource('css', 'tiny-toggle.min.css'); } else { // Bootstrap Toggle //$element_data .= ' data-selector="bootstrap-toggle"'; $item['attribs']['data-selector'] = "bootstrap-toggle"; register_html_resource('js', 'bootstrap-toggle.min.js'); //register_html_resource('js', 'bootstrap-toggle.js'); /// DEVEL register_html_resource('css', 'bootstrap-toggle.min.css'); register_html_resource('script', '$("input[data-selector=\'bootstrap-toggle\']").bootstrapToggle();'); } //r($item); break; } } $element_data .= ' ' . generate_html_attribs($item['attribs']); } switch ($item['type']) { case 'hidden': if (!$item['readonly'] && !$item['disabled']) // If item readonly or disabled, just skip item { $string .= ' ' . PHP_EOL; } break; case 'password': case 'textarea': case 'text': case 'input': $item_class = ''; $value_hidden = FALSE; if ($item['type'] !== 'textarea') { $item_begin = ' 7)) { $item['value_escaped'] = '••••••••'; $value_hidden = TRUE; } // add icon for show/hide password if ($item['show_password']) { $item_begin .= ' data-toggle="password" '; register_html_resource('js', 'bootstrap-show-password.min.js'); $GLOBALS['cache_html']['javascript'][] = "$('[data-toggle=\"password\"]').password();"; } //elseif (!$value_hidden && $autocomplete_off) {} } // Disable Autocomplete if required if ($autocomplete_off) { $browser = detect_browser(); //r($browser); // Autofill off is not simple! //https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion#The_autocomplete_attribute_and_login_fields //https://www.chromium.org/developers/design-documents/form-styles-that-chromium-understands //https://stackoverflow.com/questions/41945535/html-disable-password-manager if ($item['type'] === 'password' && !$value_hidden) { $autocomplete_value = 'new-password'; $item_begin = '' . '' . '' . $item_begin; } else { $autocomplete_value = 'off'; } $item_begin .= ' autocomplete="'.$autocomplete_value.'" '; if ($browser['browser'] === 'Safari') { // Safari issue: https://stackoverflow.com/questions/22661977/disabling-safari-autofill-on-usernames-and-passwords //$item_begin .= ' autocomplete="off" readonly onfocus="if (this.hasAttribute(\'readonly\')) {this.removeAttribute(\'readonly\'); this.blur(); this.focus();}" '; //$item_begin .= ' autocomplete="false" '; // This disable safari autocomplete button register_html_resource('style', <<