From 6310521982e4cde0ef999e13234f86b9f5f8b27f Mon Sep 17 00:00:00 2001 From: iliajie Date: Mon, 8 May 2023 23:56:04 +0300 Subject: [PATCH] Add support for listing jail blocks with pagination 2/2 --- WebminCore.pm | 2 +- fail2ban/jail_blocks.cgi | 24 ++++-- fail2ban/lang/en | 1 + fail2ban/list_status.cgi | 5 +- lang/en | 1 + ui-lib.pl | 179 ++++++++++++++++++++++++++++++++++++++- 6 files changed, 200 insertions(+), 12 deletions(-) diff --git a/WebminCore.pm b/WebminCore.pm index b0ce9f76a..3b1e7ddcb 100644 --- a/WebminCore.pm +++ b/WebminCore.pm @@ -23,7 +23,7 @@ $main::export_to_caller = 1; # Add functions in web-lib-funcs.pl # Generated with : # grep -h "^sub " web-lib-funcs.pl ui-lib.pl | sed -e 's/sub //' | xargs echo -@EXPORT = qw(read_file read_file_cached read_file_cached_with_stat write_file html_escape html_strip quote_escape quote_javascript tempname_dir tempname transname transname_timestamped trunc indexof indexoflc sysprint check_ipaddress check_ip6address generate_icon urlize un_urlize include copydata ReadParseMime ReadParse read_fully read_parse_mime_callback read_parse_mime_javascript PrintHeader header get_html_title get_html_framed_title get_html_status_line popup_header footer popup_footer load_module_preferences load_theme_library redirect kill_byname kill_byname_logged find_byname error popup_error register_error_handler call_error_handlers error_setup wait_for fast_wait_for has_command make_date file_chooser_button popup_window_button popup_window_link read_acl acl_filename acl_check get_miniserv_config_file get_miniserv_config put_miniserv_config restart_miniserv reload_miniserv check_os_support http_download complete_http_download http_post ftp_download ftp_upload no_proxy open_socket download_timeout ftp_command to_ipaddress to_ip6address to_hostname icons_table replace_meta replace_file_line read_file_lines flush_file_lines unflush_file_lines unix_user_input unix_group_input hlink user_chooser_button group_chooser_button foreign_check foreign_exists foreign_available foreign_require foreign_call foreign_config foreign_installed foreign_defined get_system_hostname get_webmin_version get_webmin_version_release get_webmin_full_version get_module_acl get_group_module_acl save_module_acl save_group_module_acl init_config load_language_auto load_language text_subs text encode_base64 decode_base64 encode_base32 decode_base32 get_module_info get_all_module_infos list_themes get_theme_info list_locales list_languages safe_language read_env_file write_env_file lock_file unlock_file test_lock unlock_all_files can_lock_file webmin_log additional_log var_dump webmin_debug_log system_logged backquote_logged backquote_with_timeout backquote_command kill_logged rename_logged rename_file symlink_logged symlink_file link_file make_dir make_dir_recursive set_ownership_permissions unlink_logged unlink_file copy_source_dest move_source_dest remote_session_name remote_foreign_require remote_foreign_call remote_foreign_check remote_foreign_config remote_eval remote_write remote_read remote_finished remote_error_setup remote_rpc_call remote_multi_callback remote_multi_callback_error serialise_variable unserialise_variable other_groups date_chooser_button help_file read_help_file seed_random disk_usage_kb recursive_disk_usage help_search_link make_http_connection validate_ssl_connection read_http_connection write_http_connection close_http_connection clean_environment reset_environment clean_language progress_callback switch_to_remote_user switch_to_unix_user eval_as_unix_user create_user_config_dirs create_missing_homedir filter_javascript resolve_links simplify_path same_file flush_webmin_caches list_usermods available_usermods get_available_module_infos get_visible_module_infos get_visible_modules_categories is_under_directory parse_http_url check_clicks_function load_entities_map entities_to_ascii get_product_name get_charset get_display_hostname save_module_config save_user_module_config nice_size get_perl_path get_goto_module select_all_link select_invert_link select_rows_link check_pid_file get_mod_lib module_root_directory list_mime_types guess_mime_type open_tempfile close_tempfile print_tempfile is_selinux_enabled get_clear_file_attributes reset_file_attributes cleanup_tempnames open_lock_tempfile END month_to_number number_to_month get_rbac_module_acl supports_rbac supports_ipv6 use_rbac_module_acl execute_command open_readfile open_execute_command translate_filename translate_command register_filename_callback register_command_callback capture_function_output capture_function_output_tempfile modules_chooser_button substitute_template substitute_pattern running_in_zone running_in_vserver running_in_xen running_in_openvz list_categories is_readonly_mode command_as_user list_osdn_mirrors convert_osdn_url get_current_dir supports_users supports_symlinks quote_path get_windows_root read_file_contents write_file_contents read_file_contents_limit unix_crypt split_quoted_string write_to_http_cache check_in_http_cache supports_javascript get_module_name get_module_variable clear_time_locale reset_time_locale callers_package web_libs_package get_userdb_string connect_userdb disconnect_userdb split_userdb_string uniquelc list_combined_webmin_menu list_modules_webmin_menu module_to_menu_item list_combined_system_info shell_is_bash compare_version_numbers convert_to_json convert_from_json print_json get_referer_relative get_webmin_email_url get_webmin_browser_url trim ui_link ui_help ui_img ui_link_button ui_table_start ui_table_end ui_table_row ui_table_hr ui_table_span ui_columns_start ui_columns_row ui_columns_header ui_checked_columns_row ui_radio_columns_row ui_columns_end ui_columns_table ui_form_columns_table ui_form_start ui_form_end ui_textbox ui_filebox ui_bytesbox ui_upload ui_password ui_hidden ui_select ui_multi_select ui_multi_select_javascript ui_radio ui_yesno_radio ui_checkbox ui_oneradio ui_textarea ui_user_textbox ui_users_textbox ui_group_textbox ui_groups_textbox ui_opt_textbox ui_submit ui_reset ui_button ui_date_input ui_buttons_start ui_buttons_end ui_buttons_row ui_buttons_hr ui_post_header ui_pre_footer ui_print_header ui_print_unbuffered_header ui_print_footer ui_config_link ui_print_endpage ui_subheading ui_links_row ui_hidden_javascript ui_hidden_start ui_hidden_end ui_hidden_table_row_start ui_hidden_table_row_end ui_hidden_table_start ui_hidden_table_end ui_tabs_start ui_tabs_end ui_tabs_start_tab ui_tabs_start_tabletab ui_tabs_end_tab ui_tabs_end_tabletab ui_max_text_width ui_radio_selector ui_radio_selector_javascript ui_grid_table ui_radio_table ui_up_down_arrows ui_hr ui_nav_link ui_confirmation_form ui_text_color ui_alert_box js_disable_inputs ui_page_flipper js_checkbox_disable js_redirect ui_webmin_link ui_line_break_double ui_page_refresh ui_details ui_div_row ui_hide_outside_of_viewport ui_read_file_contents_limit get_python_cmd get_buffer_size get_buffer_size_binary get_webprefix get_sub_ref_name setvar getvar delvar print_call_stack webmin_user_can_rpc webmin_user_login_mode webmin_user_is_admin webmin_user_is get_current_theme_info_cached miniserv_using_default_cert is_int float is_float parse_accepted_language get_default_system_locale create_wrapper); +@EXPORT = qw(read_file read_file_cached read_file_cached_with_stat write_file html_escape html_strip quote_escape quote_javascript tempname_dir tempname transname transname_timestamped trunc indexof indexoflc sysprint check_ipaddress check_ip6address generate_icon urlize un_urlize include copydata ReadParseMime ReadParse read_fully read_parse_mime_callback read_parse_mime_javascript PrintHeader header get_html_title get_html_framed_title get_html_status_line popup_header footer popup_footer load_module_preferences load_theme_library redirect kill_byname kill_byname_logged find_byname error popup_error register_error_handler call_error_handlers error_setup wait_for fast_wait_for has_command make_date file_chooser_button popup_window_button popup_window_link read_acl acl_filename acl_check get_miniserv_config_file get_miniserv_config put_miniserv_config restart_miniserv reload_miniserv check_os_support http_download complete_http_download http_post ftp_download ftp_upload no_proxy open_socket download_timeout ftp_command to_ipaddress to_ip6address to_hostname icons_table replace_meta replace_file_line read_file_lines flush_file_lines unflush_file_lines unix_user_input unix_group_input hlink user_chooser_button group_chooser_button foreign_check foreign_exists foreign_available foreign_require foreign_call foreign_config foreign_installed foreign_defined get_system_hostname get_webmin_version get_webmin_version_release get_webmin_full_version get_module_acl get_group_module_acl save_module_acl save_group_module_acl init_config load_language_auto load_language text_subs text encode_base64 decode_base64 encode_base32 decode_base32 get_module_info get_all_module_infos list_themes get_theme_info list_locales list_languages safe_language read_env_file write_env_file lock_file unlock_file test_lock unlock_all_files can_lock_file webmin_log additional_log var_dump webmin_debug_log system_logged backquote_logged backquote_with_timeout backquote_command kill_logged rename_logged rename_file symlink_logged symlink_file link_file make_dir make_dir_recursive set_ownership_permissions unlink_logged unlink_file copy_source_dest move_source_dest remote_session_name remote_foreign_require remote_foreign_call remote_foreign_check remote_foreign_config remote_eval remote_write remote_read remote_finished remote_error_setup remote_rpc_call remote_multi_callback remote_multi_callback_error serialise_variable unserialise_variable other_groups date_chooser_button help_file read_help_file seed_random disk_usage_kb recursive_disk_usage help_search_link make_http_connection validate_ssl_connection read_http_connection write_http_connection close_http_connection clean_environment reset_environment clean_language progress_callback switch_to_remote_user switch_to_unix_user eval_as_unix_user create_user_config_dirs create_missing_homedir filter_javascript resolve_links simplify_path same_file flush_webmin_caches list_usermods available_usermods get_available_module_infos get_visible_module_infos get_visible_modules_categories is_under_directory parse_http_url check_clicks_function load_entities_map entities_to_ascii get_product_name get_charset get_display_hostname save_module_config save_user_module_config nice_size get_perl_path get_goto_module select_all_link select_invert_link select_rows_link check_pid_file get_mod_lib module_root_directory list_mime_types guess_mime_type open_tempfile close_tempfile print_tempfile is_selinux_enabled get_clear_file_attributes reset_file_attributes cleanup_tempnames open_lock_tempfile END month_to_number number_to_month get_rbac_module_acl supports_rbac supports_ipv6 use_rbac_module_acl execute_command open_readfile open_execute_command translate_filename translate_command register_filename_callback register_command_callback capture_function_output capture_function_output_tempfile modules_chooser_button substitute_template substitute_pattern running_in_zone running_in_vserver running_in_xen running_in_openvz list_categories is_readonly_mode command_as_user list_osdn_mirrors convert_osdn_url get_current_dir supports_users supports_symlinks quote_path get_windows_root read_file_contents write_file_contents read_file_contents_limit unix_crypt split_quoted_string write_to_http_cache check_in_http_cache supports_javascript get_module_name get_module_variable clear_time_locale reset_time_locale callers_package web_libs_package get_userdb_string connect_userdb disconnect_userdb split_userdb_string uniquelc list_combined_webmin_menu list_modules_webmin_menu module_to_menu_item list_combined_system_info shell_is_bash compare_version_numbers convert_to_json convert_from_json print_json get_referer_relative get_webmin_email_url get_webmin_browser_url trim ui_link ui_help ui_img ui_link_button ui_table_start ui_table_end ui_table_row ui_table_hr ui_table_span ui_columns_start ui_columns_row ui_columns_header ui_checked_columns_row ui_radio_columns_row ui_columns_end ui_columns_table ui_form_columns_table ui_form_elements_wrapper ui_form_start ui_form_end ui_textbox ui_filebox ui_bytesbox ui_upload ui_password ui_hidden ui_select ui_multi_select ui_multi_select_javascript ui_radio ui_yesno_radio ui_checkbox ui_oneradio ui_textarea ui_user_textbox ui_users_textbox ui_group_textbox ui_groups_textbox ui_opt_textbox ui_submit ui_reset ui_button ui_date_input ui_buttons_start ui_buttons_end ui_buttons_row ui_buttons_hr ui_post_header ui_pre_footer ui_print_header ui_print_unbuffered_header ui_print_footer ui_config_link ui_print_endpage ui_subheading ui_links_row ui_hidden_javascript ui_hidden_start ui_hidden_end ui_hidden_table_row_start ui_hidden_table_row_end ui_hidden_table_start ui_hidden_table_end ui_tabs_start ui_tabs_end ui_tabs_start_tab ui_tabs_start_tabletab ui_tabs_end_tab ui_tabs_end_tabletab ui_max_text_width ui_radio_selector ui_radio_selector_javascript ui_grid_table ui_radio_table ui_up_down_arrows ui_hr ui_nav_link ui_confirmation_form ui_text_color ui_alert_box js_disable_inputs ui_page_flipper js_checkbox_disable js_redirect ui_webmin_link ui_line_break_double ui_page_refresh ui_details ui_div_row ui_paginations ui_hide_outside_of_viewport ui_read_file_contents_limit get_python_cmd get_buffer_size get_buffer_size_binary get_webprefix get_sub_ref_name setvar getvar delvar print_call_stack webmin_user_can_rpc webmin_user_login_mode webmin_user_is_admin webmin_user_is get_current_theme_info_cached miniserv_using_default_cert is_int float is_float parse_accepted_language get_default_system_locale create_wrapper); # Add global variables in web-lib.pl push(@EXPORT, qw(&unique)); diff --git a/fail2ban/jail_blocks.cgi b/fail2ban/jail_blocks.cgi index 58cb5559f..e8d14df8a 100644 --- a/fail2ban/jail_blocks.cgi +++ b/fail2ban/jail_blocks.cgi @@ -26,30 +26,42 @@ while(<$fh>) { my $start = $+{start}; my $end = $+{end}; if ($ip && $start && $end) { - push(@jail_blocks, &ui_checked_columns_row([$ip, $start, $end], [ 'width=5' ], "ip", $ip)); + push(@jail_blocks, [$ip, $start, $end]); } } } close($fh); +my $pagination_opts = \%in; +$pagination_opts->{'_form-exports'} = { 'jail' => $jail }; if (@jail_blocks) { + my $pagination = &ui_paginations(\@jail_blocks, $pagination_opts); my @links = ( &select_all_link("ip"), &select_invert_link("ip")); - print &ui_links_row(\@links); + print $pagination->{'search-form-data'}; print &ui_form_start("unblock_jailed_ip.cgi", "post"); + print &ui_links_row(\@links); print &ui_columns_start([ "", $text{'status_head_blocks_ip'}, $text{'status_head_blocks_stime'}, $text{'status_head_blocks_etime'} ]); - foreach my $r (@jail_blocks) { - print $r; + if (@jail_blocks) { + foreach my $r (@jail_blocks) { + print &ui_checked_columns_row($r, [ 'width=5' ], "ip", $r->[0]); + } + } + else { + print &ui_columns_row([&text('status_jail_nosearchrs', $in{'search'})], ['colspan="4" align="center"']); } print &ui_columns_end(); - print &ui_links_row(\@links); + print $pagination->{'paginator-form-data'}; print &ui_hidden("jail", $jail); print &ui_hidden("return", 1); - + print &ui_links_row(\@links); print &ui_form_end([ [ undef, $text{'status_jail_unblock_ips'} ] ]); + print $pagination->{'search-form'}; + print $pagination->{'paginator-form'}; + print $pagination->{"client-height-script"}; } else { print &text('status_jail_noactiveips', $jail); diff --git a/fail2ban/lang/en b/fail2ban/lang/en index 27bb96337..700e4244e 100644 --- a/fail2ban/lang/en +++ b/fail2ban/lang/en @@ -189,6 +189,7 @@ status_jail_unblock=Unblock Selected Jails status_jail_unblock_ips=Unblock Selected IP status_jail_noactive=There are no active jails enabled yet. status_jail_noactiveips=There are no blocked entries in jail $1 found. +status_jail_nosearchrs=There are no results matching $1 query status_err_set=Failed set action status_err_unblock=Failed to unblock action status_err_nojail=No jails have been selected diff --git a/fail2ban/list_status.cgi b/fail2ban/list_status.cgi index 801315143..f7b50dfd2 100755 --- a/fail2ban/list_status.cgi +++ b/fail2ban/list_status.cgi @@ -21,12 +21,13 @@ if (@jails) { &select_invert_link("jail") ); my $head; my @jipsall; + my $screenHeightGetter = "onclick='this.href=this.href+\"&client_height=\"+document.documentElement.clientHeight'"; foreach my $jail (@jails) { my $fh = 'cmdjail'; my $cmd = "$config{'client_cmd'} status ".quotemeta($jail); my $jcmd = "$cmd 2>&1 "; + " ".&text('status_rules_plus_more', $ipscount-$limit), undef, $screenHeightGetter)).""; } return $ips; }; diff --git a/lang/en b/lang/en index f147868c4..134dd912e 100644 --- a/lang/en +++ b/lang/en @@ -306,6 +306,7 @@ ui_etime=Invalid time ui_paging=Showing rows $1 to $2 of $3 ui_rowlabel=$2 in row $1 : ui_filterbox=Type to filter.. +ui_of=of header_statusmsg=$1 logged into $2 $3 on $4 ($5) diff --git a/ui-lib.pl b/ui-lib.pl index 70cdc5c93..1f1507d36 100755 --- a/ui-lib.pl +++ b/ui-lib.pl @@ -683,6 +683,24 @@ return $rv; ####################### form generation functions +=head2 ui_form_elements_wrapper(formdata, formid, [class], [tags]) + +HTML5 allows to have form elements to be placed outside of an actual +form to provide support for nested forms. The requirement is to have +`id` attribute set on the form and `form` attribute to be set on each +element referencing the given form. This is the wrapper for such form +elements. + +=cut +sub ui_form_elements_wrapper +{ +return &theme_ui_form_elements_wrapper(@_) if (defined(&theme_ui_form_elements_wrapper)); +my ($formdata, $formid, $class, $tags) = @_; +return "
$formdata
" +} + =head2 ui_form_start(script, method, [target], [tags]) Returns HTML for the start of a a form that submits to some script. The @@ -923,7 +941,7 @@ return ""; } -=head2 ui_hidden(name, value) +=head2 ui_hidden(name, value, [formid]) Returns HTML for a hidden field with the given name and value. @@ -931,11 +949,12 @@ Returns HTML for a hidden field with the given name and value. sub ui_hidden { return &theme_ui_hidden(@_) if (defined(&theme_ui_hidden)); -my ($name, $value) = @_; +my ($name, $value, $formid) = @_; return "\n"; + "value=\""."e_escape($value)."\"". + ($formid ? " form=\"$formid\"" : "").">\n"; } =head2 ui_select(name, value|&values, &options, [size], [multiple], [add-if-missing], [disabled?], [tags]) @@ -2788,6 +2807,160 @@ my ($label, $content) = @_; return "
$label$content
"; } +=head2 ui_paginations(&array, &opts) + +Given array reference returns pagination buttons +and search form to be used on the page + +=cut +sub ui_paginations +{ +return &theme_ui_paginations(@_) + if (defined(&theme_ui_paginations)); + +my ($arr, $opts) = @_; +my %rv; +my @arr = @{$arr}; +my ($script_name) = $0 =~ /([^\/]*\.cgi)$/; +my $items_per_page = int($tconfig{'paginate'}) || int($opts->{'paginate'}) || 20; +my $curent_page = int($opts->{'page'}) || 1; +my $search_term = $opts->{'search'}; +my $pagination_target = $opts->{'pagination_target'} || $script_name; +my $pagination_action = $opts->{'pagination_action'} || "post"; +my $search_target = $opts->{'search_target'} || $script_name; +my $search_action = $opts->{'search_action'} || "post"; +my $search_placeholder = $opts->{'search_placeholder'} || $text{'ui_searchok'}; +my $paginator_wrap_class = $opts->{'paginator_wrap_class'} || "ui_form_elements_wrapper_paginator"; +my $paginator_wrap_style = $opts->{'paginator_wrap_style'} || "style='float:right;margin-top: 3px;'"; +my $search_wrap_class = $opts->{'search_wrap_class'} || "ui_form_elements_wrapper_search"; +my $search_wrap_style = $opts->{'search_wrap_style'} || "style='float:right;margin-bottom: 3px;'"; +my $link_page_cls = $opts->{'paginator_link_class'} || 'ui_link_pagination'; +my $link_search_cls = $opts->{'paginator_textbox_class'} || 'ui_textbox_pagination'; +my $exported_form = $opts->{'_form-exports'}; +my $exported_form_query = ""; +if ($exported_form) { + foreach (keys %{$exported_form}) { + $exported_form_query .= "&"."$_=$exported_form->{$_}"; + } + } + +# If we have a search string filter content +if (ref($arr) eq 'ARRAY' && $arr->[0]) { + if ($search_term) { + my @sarr; + @arr = + grep { + arr: for (my $i = 0; $i <= $#$_; $i++) { + push(@sarr, $_), last arr + if(index($_->[$i], $search_term) != -1); + } + } @arr; + @arr = @sarr; + } + my $items_per_page = + $tconfig{'paginate-noauto'} ? + $items_per_page : + int($opts->{'client_height'}) ? + int($ENV{'HTTP_X_CLIENT_HEIGHT'}) || + int($opts->{'client_height'} / 35) : $items_per_page; + my $totals_items_original = scalar(@arr); + my $total_pages = ceil(($totals_items_original) / $items_per_page); + my $total_pages_length = length($total_pages); + + # Return pagination jumper only + # if there us more than one page + if ($total_pages > 1) { + my $totals_items_spliced = $totals_items_original; + my $start_page_with = $curent_page * $items_per_page; + $curent_page = $total_pages + if ($curent_page > $total_pages); + $curent_page = 1 + if ($curent_page <= 0); + + my $curent_page_prev = $curent_page - 1; + my $page_prev_disabled = $curent_page_prev <= 0 ? " disabled" : ""; + my $curent_page_next = $curent_page + 1; + my $page_next_disabled = $curent_page_next > $total_pages ? " disabled" : ""; + my $splice_start = $items_per_page * $curent_page_prev; + my $splice_end = $items_per_page; + @arr = splice(@arr, $splice_start, $splice_end); + $totals_items_spliced = scalar(@arr); + + # Pagination jumper + my $paginator_id = 'paginator-form'; + my $screenHeightGetter = + "onclick='this.href=this.href+\"&client_height=\"+document.documentElement.clientHeight'"; + $rv{$paginator_id} = + &ui_form_start($pagination_target, $pagination_action, undef, "id='$paginator_id'"); + $rv{$paginator_id} .= &ui_form_end(); + $rv{"$paginator_id-data"} = &ui_hidden("search", $search_term, $paginator_id) + if ($search_term); + $rv{"$paginator_id-data"} .= &ui_hidden("paginate", $items_per_page, $paginator_id); + $rv{"$paginator_id-data"} .= + &ui_link("?page=$curent_page_prev&search=$search_term&paginate=$items_per_page$exported_form_query", + ' ⏴ ', "$link_page_cls ${link_page_cls}_left$page_prev_disabled", $screenHeightGetter); + $rv{"$paginator_id-data"} .= + &ui_textbox("page", $curent_page, $total_pages_length, undef, $total_pages_length, + "data-class='$link_search_cls' form='$paginator_id'"); + $rv{"$paginator_id-data"} .= " $text{'ui_of'} $total_pages"; + $rv{"$paginator_id-data"} .= + &ui_link("?page=$curent_page_next&search=$search_term&paginate=$items_per_page$exported_form_query", + ' ▸ ', "$link_page_cls ${link_page_cls}_right$page_next_disabled", $screenHeightGetter); + if ($exported_form) { + foreach (keys %{$exported_form}) { + $rv{"$paginator_id-data"} .= &ui_hidden($_, $exported_form->{$_}, $paginator_id); + } + } + # $rv{"$paginator_id-data"} .= &ui_submit(undef, undef, undef, "form='$paginator_id'"); + $rv{"$paginator_id-data"} = + &ui_form_elements_wrapper($rv{"$paginator_id-data"}, $paginator_id, + $paginator_wrap_class, $paginator_wrap_style) + } + + # Search form + if ($total_pages > 1 || $search_term) { + my $search_id = 'search-form'; + $rv{$search_id} = &ui_form_start($search_target, $search_action, undef, "id='$search_id'"); + $rv{$search_id} .= &ui_form_end(); + $rv{"$search_id-data"} .= &ui_hidden("paginate", $items_per_page, $search_id); + $rv{"$search_id-data"} .= &ui_hidden("page", 1, $search_id); + my $search_placeholder_length = length($search_placeholder); + $rv{"$search_id-data"} .= + &ui_textbox("search", $search_term, $search_placeholder_length < 12 ? 12 : $search_placeholder_length, + undef, undef, "data-class='${link_search_cls}_search' placeholder='$search_placeholder' form='$search_id'"); + # $rv{"$search_id-data"} .= &ui_submit($text{'ui_searchok'}, undef, undef, "form='$search_id'"); + $rv{"$search_id-data"} .= + &ui_reset($text{'reset'}, undef, + "onclick='document.getElementById(\"$search_id\").search.value = \"\";". + "document.getElementById(\"$search_id\").submit()'"); + if ($exported_form) { + foreach (keys %{$exported_form}) { + $rv{"$search_id-data"} .= &ui_hidden($_, $exported_form->{$_}, $search_id); + } + } + $rv{"$search_id-data"} = &ui_form_elements_wrapper($rv{"$search_id-data"}, $search_id, $search_wrap_class, $search_wrap_style) + } + + # After all forms were added run JavaScript script to inject + # client height information into each form, which will enable + # automatic calculation of items per page to fit properly + $rv{"client-height-script"} = + ""; + } +@$arr = @arr; +return \%rv; +} + =head2 ui_hide_outside_of_viewport(elem) Prints element if not in viewport. Useful