diff --git a/filemin/filemin-lib.pl b/filemin/filemin-lib.pl index 3a3b1e6c5..8848d0860 100644 --- a/filemin/filemin-lib.pl +++ b/filemin/filemin-lib.pl @@ -11,7 +11,7 @@ eval "use File::MimeInfo"; our ($path, $quote_escaped_path, $urlized_path, $base, $cwd, $upload_max, $confdir, $template_name, $bookmarks, $page, $pagelimit, $edit_icon, - $rename_icon, $extract_icon, $goto_icon, $root_icon, $head); + $rename_icon, $extract_icon, $goto_icon, $root_icon); our (%access, %userconfig, %allowed_for_edit); our (@allowed_paths, @allowed_for_edit, @remote_user_info); @@ -413,124 +413,9 @@ if ($tinfo{'bootstrap'}) { "unauthenticated/templates/dialogs.html"); } -# Interface for legacy themes +# Interface for other themes, built from the core widgets else { - - # Set icons variables - $edit_icon = - "$text{"; - $rename_icon = - ""; - $extract_icon = - ""; - $goto_icon = - ""; - $head .= ""; - $head .= ""; - $head .= ""; - $head .= ""; - $head .= ""; - $head .= ""; - $head .= ""; - print $head; - - # Set "root" icon - if ($base eq '/') { - $root_icon = - ""; - } - else { - $root_icon = "~"; - } - # Legacy breadcrumbs - print "
" . - "" . - "$root_icon / "; - my @breadcr = split('/', $path); - my $cp = ''; - for (my $i = 1; - $i <= scalar(@breadcr)-1; $i++) { - chomp($breadcr[$i]); - $cp = $cp.'/'.$breadcr[$i]; - print "" . - &html_escape($breadcr[$i]) . - " / "; - } - print "
"; - # And pagination - $page = $in{'page'}; - $pagelimit = $userconfig{'per_page'}; - $pages = ceil((scalar(@list))/$pagelimit); - if (not defined $page or $page > $pages) { - $page = 1; - } - print "Pages: "; - for (my $i = 1; $i <= $pages; $i++) { - if ($page eq $i) { - print "" . - &html_escape($i) . - ""; - } - else { - print "" . - &html_escape($i) . - ""; - } - } - print "
"; - - # And toolbar - print_template( - "unauthenticated/templates/" . - "legacy_quicks.html"); - print_template( - "unauthenticated/templates/" . - "legacy_dialogs.html"); + print &plain_interface_head(); } my $info_total; my $info_files = scalar @files; @@ -801,6 +686,18 @@ for (my $count = 1 + $pagelimit * ($page - 1); print ui_columns_end(); print &ui_hidden("path", $path),"\n"; print &ui_form_end(); +# Close the widget page area opened by the non-Bootstrap interface +print &ui_page_end() if (!$tinfo{'bootstrap'}); +} + +# list_bookmarks() +# Returns the directories the user has bookmarked +sub list_bookmarks +{ +$confdir = "$remote_user_info[7]/.filemin"; +return () if (!-e "$confdir/.bookmarks"); +my $bookmarks = &read_file_lines($confdir.'/.bookmarks', 1); +return grep { $_ ne '' } @$bookmarks; } # get_bookmarks() @@ -811,10 +708,8 @@ $confdir = "$remote_user_info[7]/.filemin"; if (!-e "$confdir/.bookmarks") { return "
  • $text{'no_bookmarks'}
  • "; } -my $bookmarks = - &read_file_lines($confdir.'/.bookmarks', 1); $result = ''; -foreach $bookmark (@$bookmarks) { +foreach $bookmark (&list_bookmarks()) { $result .= "
  • " . @@ -824,6 +719,486 @@ foreach $bookmark (@$bookmarks) { return $result; } +# plain_interface_head() +# Returns the top of the file list for themes without Bootstrap : the +# module assets, the breadcrumbs, the toolbar of menus and buttons, the +# page links and the dialogs behind the toolbar, all built from the core +# widgets. Also sets the row action icons and the pagination that the +# shared listing code uses, and opens the widget page area that +# print_interface closes after the listing. +sub plain_interface_head +{ +my $rv = ""; + +# Row action icons from the built-in icon set +$edit_icon = &ui_svg_icon('edit', { 'title' => $text{'edit'} }); +$rename_icon = &ui_svg_icon('cursor-text', { 'title' => $text{'rename'} }); +$extract_icon = &ui_svg_icon('unarchive', + { 'title' => $text{'extract_archive'} }); +$goto_icon = &ui_svg_icon('arrow-right', { 'title' => $text{'goto_folder'} }); + +# Server-side pagination, with as many entries per page as the user chose +$page = int($in{'page'} || 0) || 1; +$pagelimit = int($userconfig{'per_page'} || 0) || 50; +$pages = ceil(scalar(@list) / $pagelimit) || 1; +$page = 1 if ($page > $pages); + +# The widget page area, with the module's own stylesheet and script +$rv .= &ui_page_start({ 'class' => 'filemin_page' }); +$rv .= &plain_asset_tags(); + +# Breadcrumbs at the left and the toolbar at the right +$rv .= &ui_tag('div', &plain_breadcrumb().&plain_toolbar(), + { 'class' => 'filemin_head' }); + +# Page links, when the listing does not fit on one page. The URL is +# relative, so a search keeps its query from page to page +my $pager = &ui_pager({ + 'page' => $page, + 'pages' => $pages, + 'href' => "?path=".&urlize($path). + (defined($query) && $query ne '' ? + "&query=".&urlize($query) : ""). + ($in{'caseins'} ? "&caseins=1" : ""). + "&page=" }); +$rv .= &ui_tag('div', $pager, { 'class' => 'filemin_pager' }) if ($pager); + +# The dialogs opened from the toolbar and the rows +$rv .= &plain_dialogs(); +return $rv; +} + +# plain_asset_tags() +# Returns the tags loading the module's stylesheet and script for the +# widget interface, each keyed by its modification time so that a changed +# file is fetched at once, and the permissions calculator shared with the +# Bootstrap interface +sub plain_asset_tags +{ +my $rv = ""; +foreach my $file ('css/plain.css', 'js/plain.js') { + my @st = stat("$module_root_directory/unauthenticated/$file"); + my $url = "unauthenticated/$file?".(@st ? $st[9] : &get_webmin_version()); + if ($file =~ /\.css$/) { + $rv .= &ui_tag('link', undef, + { 'rel' => 'stylesheet', 'href' => $url })."\n"; + } + else { + $rv .= &ui_tag('script', undef, + { 'src' => $url, 'defer' => undef })."\n"; + } + } +$rv .= &ui_tag('script', undef, + { 'src' => 'unauthenticated/js/chmod-calculator.js' })."\n"; +return $rv; +} + +# plain_breadcrumb() +# Returns the path to the current directory as breadcrumb links, starting +# with the allowed base directory : the disk when it is the root, else +# the home +sub plain_breadcrumb +{ +my @items = ( { 'icon' => $base eq '/' ? 'hard-drive' : 'home', + 'href' => 'index.cgi?path=', + 'title' => $base } ); +my $cp = ''; +foreach my $part (grep { $_ ne '' } split(/\//, $path)) { + $cp .= '/'.$part; + push(@items, { 'label' => $part, + 'href' => 'index.cgi?path='.&urlize($cp) }); + } +return &ui_breadcrumb(\@items, { 'class' => 'filemin_crumbs' }); +} + +# plain_toolbar() +# Returns the toolbar : the File, Edit, Tools and Bookmarks menus, and the +# refresh and delete buttons. Entries acting on the selected entries are +# marked, so that plain.js asks for a selection first. Tools whose +# command is missing are left out. +sub plain_toolbar +{ +my $urlpath = &urlize($path); +my $sel = { 'data-filemin-selection' => '1' }; +my @file = ( + { 'label' => $text{'create_file'}, 'icon' => 'file-plus', + 'dialog' => 'filemin_create_file' }, + { 'label' => $text{'create_folder'}, 'icon' => 'folder-plus', + 'dialog' => 'filemin_create_folder' }, + '-', + { 'label' => $text{'upload_files'}, 'icon' => 'upload', + 'dialog' => 'filemin_upload' }, + { 'label' => $text{'get_from_url'}, 'icon' => 'globe', + 'dialog' => 'filemin_download' }, + ); +my @edit = ( + { 'label' => $text{'copy_selected'}, 'icon' => 'copy', + 'attrs' => { %$sel, 'data-filemin-submit' => 'copy.cgi' } }, + { 'label' => $text{'cut_selected'}, 'icon' => 'scissors', + 'attrs' => { %$sel, 'data-filemin-submit' => 'cut.cgi' } }, + { 'label' => $text{'paste'}, 'icon' => 'clipboard', + 'href' => 'paste.cgi?path='.$urlpath }, + '-', + { 'label' => $text{'select_all'}, 'icon' => 'check-square', + 'attrs' => { 'data-filemin-select' => 'all' } }, + { 'label' => $text{'invert_selection'}, 'icon' => 'square-half', + 'attrs' => { 'data-filemin-select' => 'invert' } }, + ); +my @tools = ( + { 'label' => $text{'search'}, 'icon' => 'search', + 'dialog' => 'filemin_search' }, + '-', + { 'label' => $text{'compress_selected'}, 'icon' => 'archive', + 'dialog' => 'filemin_compress', 'attrs' => $sel }, + { 'label' => $text{'chmod_selected'}, 'icon' => 'lock', + 'dialog' => 'filemin_chmod', 'attrs' => $sel }, + { 'label' => $text{'chown_selected'}, 'icon' => 'user', + 'dialog' => 'filemin_chown', 'attrs' => $sel }, + ); +push(@tools, { 'label' => $text{'chattr_selected'}, 'icon' => 'tag', + 'dialog' => 'filemin_chattr', 'attrs' => $sel }) + if (&has_command('chattr')); +push(@tools, { 'label' => $text{'chcon_selected'}, 'icon' => 'shield', + 'dialog' => 'filemin_chcon', 'attrs' => $sel }) + if (&get_selinux_status()); +push(@tools, { 'label' => $text{'acls_selected'}, 'icon' => 'key', + 'dialog' => 'filemin_acls', 'attrs' => $sel }) + if (&has_command('setfacl')); +my @bookmarks = ( + { 'label' => $text{'bookmark_folder'}, 'icon' => 'bookmark', + 'href' => 'bookmark.cgi?path='.$urlpath }, + '-', + ); +my @marks = &list_bookmarks(); +if (@marks) { + push(@bookmarks, map { { 'label' => $_, 'icon' => 'folder', + 'href' => 'index.cgi?path='.&urlize($_) } } + @marks); + } +else { + push(@bookmarks, { 'label' => $text{'no_bookmarks'}, 'disabled' => 1 }); + } +my @buttons = ( + &ui_dropdown($text{'menu_file'}, \@file, { 'icon' => 'file' }), + &ui_dropdown($text{'menu_edit'}, \@edit, { 'icon' => 'clipboard' }), + &ui_dropdown($text{'menu_tools'}, \@tools, { 'icon' => 'gear' }), + &ui_dropdown($text{'menu_bookmarks'}, \@bookmarks, + { 'icon' => 'book', 'align' => 'end' }), + &ui_action_button($text{'refresh'}, 'neutral', + { 'icon' => 'refresh', 'href' => 'index.cgi?path='.$urlpath }), + &ui_dialog_button($text{'delete'}, 'filemin_delete', 'danger', + { 'icon' => 'trash', 'attrs' => $sel }), + ); +return &ui_tag('div', join("\n", @buttons), + { 'class' => 'filemin_toolbar', 'role' => 'toolbar' }); +} + +# plain_dialog_table(&rows) +# Returns a two-column table of label and input rows for a dialog body. +# A row with an empty label spans the table. +sub plain_dialog_table +{ +my ($rows) = @_; +my $rv = &ui_table_start(undef, "width=100%", 2); +foreach my $row (@$rows) { + $rv .= defined($row->[0]) && $row->[0] ne '' ? + &ui_table_row($row->[0], $row->[1]) : + &ui_table_row(undef, $row->[1], 2); + } +$rv .= &ui_table_end(); +return $rv; +} + +# plain_dialogs() +# Returns the dialogs behind the toolbar and the row actions. A dialog +# acting on the selected entries carries the script to run in a +# data-filemin-apply attribute : plain.js sends the selection form to that +# script with the dialog's fields as URL parameters, as the action +# scripts expect them. The other dialogs are ordinary forms posting to +# their script. +sub plain_dialogs +{ +my $rv = ""; +my $hidden_path = &ui_hidden('path', $path); +my $cancel = $text{'dialog_cancel'}; +my $recursive = &ui_checkbox('recursive', 'true', $text{'dialog_recursive'}, 0); +my $apply = sub { + # A form sent to the selection, closed natively if the script fails + my ($script, $name) = @_; + return { 'method' => 'dialog', + 'name' => $name, + 'attrs' => { 'data-filemin-apply' => $script } }; + }; +my $change = [ { 'label' => $text{'dialog_change'}, 'state' => 'success', + 'icon' => 'check' } ]; + +# Shown when an action needs a selection and nothing is checked +$rv .= &ui_dialog({ + 'id' => 'filemin_nothing', + 'title' => $text{'warning_title'}, + 'state' => 'warning', + 'size' => 'small', + 'text' => $text{'nothing_selected'}, + 'close' => $text{'dialog_ok'} }); + +# New file and directory +foreach my $kind ([ 'file', 'create_file', 'new_file_name', 'file-plus' ], + [ 'folder', 'create_folder', 'new_folder_name', 'folder-plus' ]) { + my ($what, $title, $label, $icon) = @$kind; + $rv .= &ui_dialog({ + 'id' => 'filemin_create_'.$what, + 'title' => $text{$title}, + 'icon' => $icon, + 'size' => 'small', + 'form' => 'create_'.$what.'.cgi', + 'body' => &plain_dialog_table([ + [ $text{$label}, + &ui_textbox('name', '', 30, 0, undef, 'required') ], + ]).$hidden_path, + 'buttons' => [ { 'label' => $text{'create'}, 'state' => 'success', + 'icon' => 'check' } ], + 'close' => $cancel }); + } + +# Rename, filled in by the row link +$rv .= &ui_dialog({ + 'id' => 'filemin_rename', + 'title' => $text{'rename'}, + 'icon' => 'cursor-text', + 'size' => 'small', + 'form' => 'rename.cgi', + 'body' => &plain_dialog_table([ + [ $text{'new_name'}, + &ui_textbox('name', '', 30, 0, undef, 'required') ], + ]).&ui_hidden('file', '').$hidden_path, + 'buttons' => [ { 'label' => $text{'rename'}, 'state' => 'success', + 'icon' => 'check' } ], + 'close' => $cancel }); + +# Upload. The options travel in the URL, which upload.cgi reads before +# the files arrive, so plain.js adds them to the action on submit +my $upload_url = "upload.cgi?path=".&urlize($path)."&id=".$upid; +$rv .= &ui_dialog({ + 'id' => 'filemin_upload', + 'title' => $text{'upload_files'}, + 'icon' => 'upload', + 'form' => { 'action' => $upload_url, + 'method' => 'form-data', + 'attrs' => { + 'data-filemin-upload' => $upload_url, + 'data-filemin-tracker' => + &get_webprefix()."/uptracker.cgi?id=".$upid } }, + 'body' => &plain_dialog_table([ + [ $text{'upload_files'}, + &ui_upload('upfiles', 40, 0, 'multiple required') ], + [ undef, + &ui_checkbox('opt_dirs', 1, $text{'upload_dirs'}, 0, + 'data-filemin-upload-dirs')."
    ". + &ui_checkbox('opt_extract', 1, $text{'extract_uploaded'}, 0, + "data-filemin-upload-option='extract_uploaded'"). + "
    ". + &ui_checkbox('opt_overwrite', 1, $text{'overwrite_existing'}, 0, + "data-filemin-upload-option='overwrite_existing'") ], + ]), + 'buttons' => [ { 'label' => $text{'upload_files'}, 'state' => 'success', + 'icon' => 'upload' } ], + 'close' => $cancel }); + +# Download from a URL +$rv .= &ui_dialog({ + 'id' => 'filemin_download', + 'title' => $text{'get_from_url'}, + 'icon' => 'globe', + 'form' => 'http_download.cgi', + 'body' => &plain_dialog_table([ + [ $text{'dialog_file_url'}, + &ui_tag('input', undef, { 'type' => 'url', 'name' => 'link', + 'class' => 'ui_textbox', 'size' => 40, + 'autocomplete' => 'url', + 'required' => undef }) ], + [ $text{'dialog_user_name'}, + &ui_textbox('username', '', 25, 0, undef, "autocomplete='off'") ], + [ $text{'dialog_user_pass'}, + &ui_password('password', '', 25, 0, undef, + "autocomplete='new-password' data-password-meter-skip") ], + ]).$hidden_path, + 'buttons' => [ { 'label' => $text{'dialog_download'}, 'state' => 'success', + 'icon' => 'download' } ], + 'close' => $cancel }); + +# Search under the current directory +$rv .= &ui_dialog({ + 'id' => 'filemin_search', + 'title' => $text{'search'}, + 'icon' => 'search', + 'size' => 'small', + 'form' => 'search.cgi', + 'body' => &plain_dialog_table([ + [ $text{'search_label'}, + &ui_textbox('query', '', 30, 0, undef, 'required') ], + [ undef, &ui_checkbox('caseins', 1, $text{'search_insensitive'}, 1) ], + ]).$hidden_path, + 'buttons' => [ { 'label' => $text{'search_go'}, 'state' => 'success', + 'icon' => 'search' } ], + 'close' => $cancel }); + +# Compress the selection into an archive +$rv .= &ui_dialog({ + 'id' => 'filemin_compress', + 'title' => $text{'compress_selected'}, + 'icon' => 'archive', + 'size' => 'small', + 'form' => &$apply('compress.cgi'), + 'body' => &plain_dialog_table([ + [ $text{'archive_name'}, + &ui_textbox('arch', '', 30, 0, undef, 'required') ], + [ $text{'type'}, + &ui_select('method', 'tar', [ [ 'plain-tar', '.tar' ], + [ 'xz-tar', '.tar.xz' ], + [ 'tar', '.tar.gz' ], + [ 'zstd-tar', '.zstd' ], + [ 'zip', '.zip' ] ]) ], + ]), + 'buttons' => [ { 'label' => $text{'dialog_compress'}, 'state' => 'success', + 'icon' => 'archive' } ], + 'close' => $cancel }); + +# Permissions. The form is named for the calculator, whose fields are +# kept out of the parameters; the perms parameter the script reads is a +# hidden field that plain.js keeps equal to the octal field +my $matrix = &ui_tag_start('table', { 'class' => 'filemin_chmod_matrix' }). + &ui_tag('tr', &ui_tag('th', ''). + join('', map { &ui_tag('th', &html_escape($text{$_})) } + ('chmod_owner', 'chmod_group', 'chmod_others'))); +foreach my $bit ([ 'chmod_read', 4 ], [ 'chmod_write', 2 ], + [ 'chmod_execute', 1 ]) { + $matrix .= &ui_tag('tr', &ui_tag('td', &html_escape($text{$bit->[0]})). + join('', map { &ui_tag('td', &ui_checkbox($_.$bit->[1], $bit->[1], + undef, 0, 'data-filemin-chmod data-filemin-skip')) } + ('owner', 'group', 'other'))); + } +foreach my $extra ([ 'chmod_sticky', 'sticky', 1 ], [ 'chmod_setgid', 'setgid', 2 ]) { + $matrix .= &ui_tag('tr', &ui_tag('td', &html_escape($text{$extra->[0]})). + &ui_tag('td', &ui_checkbox($extra->[1], $extra->[2], undef, 0, + 'data-filemin-chmod data-filemin-skip')). + &ui_tag('td', '').&ui_tag('td', '')); + } +$matrix .= &ui_tag_end('table'); +$rv .= &ui_dialog({ + 'id' => 'filemin_chmod', + 'title' => $text{'chmod_selected'}, + 'icon' => 'lock', + 'form' => &$apply('chmod.cgi', 'chmod'), + 'body' => &plain_dialog_table([ + [ $text{'permissions'}, + &ui_textbox('t_total', '0644', 4, 0, 4, + "data-filemin-chmod-octal data-filemin-skip ". + "pattern='[0-7]{3,4}' required")." ". + &ui_textbox('sym_total', '', 12, 0, undef, + 'readonly data-filemin-skip'). + &ui_hidden('perms', '0644') ], + [ undef, $matrix ], + [ $text{'chmod_apply_to'}, + &ui_select('applyto', 1, + [ map { [ $_->[0], $text{$_->[1]} ] } + ( [ 1, 'chmod_dir_only' ], + [ 2, 'chmod_dir_and_its_files' ], + [ 3, 'chmod_all' ], + [ 4, 'chmod_dir_files_and_subs_files' ], + [ 5, 'chmod_dir_and_subs' ] ) ]) ], + ]), + 'buttons' => $change, + 'close' => $cancel }); + +# Ownership +$rv .= &ui_dialog({ + 'id' => 'filemin_chown', + 'title' => $text{'chown_selected'}, + 'icon' => 'user', + 'size' => 'small', + 'form' => &$apply('chown.cgi'), + 'body' => &plain_dialog_table([ + [ $text{'user_name'}, + &ui_textbox('owner', '', 20, 0, undef, 'required') ], + [ $text{'group_name'}, + &ui_textbox('group', '', 20, 0, undef, 'required') ], + [ undef, $recursive ], + ]), + 'buttons' => $change, + 'close' => $cancel }); + +# File attributes and SELinux context, which take one label each +foreach my $tool ([ 'chattr', 'tag', 'attr_label' ], + [ 'chcon', 'shield', 'context_label' ]) { + my ($what, $icon, $label) = @$tool; + $rv .= &ui_dialog({ + 'id' => 'filemin_'.$what, + 'title' => $text{$what.'_selected'}, + 'icon' => $icon, + 'size' => 'small', + 'form' => &$apply($what.'.cgi'), + 'body' => &plain_dialog_table([ + [ $text{$label}, + &ui_textbox('label', '', 25, 0, undef, 'required') ], + [ undef, $recursive ], + ]), + 'buttons' => $change, + 'close' => $cancel }); + } + +# Access control lists, when setfacl exists +if (&has_command('setfacl')) { + $rv .= &ui_dialog({ + 'id' => 'filemin_acls', + 'title' => $text{'acls_selected'}, + 'icon' => 'key', + 'form' => &$apply('setfacl.cgi'), + 'body' => &plain_dialog_table([ + [ $text{'acls_action'}, + &ui_select('action', '-m', + [ [ '', '' ], [ '-m', 'Modify (-m)' ], + [ '-x', 'Remove (-x)' ], + [ '-b', 'Remove all extended (-b)' ], + [ '-k', 'Remove default (-k)' ] ]) ], + [ $text{'acls_applyto'}, + &ui_select('apply_to', [ ], + [ map { [ $_->[0], $text{$_->[1]} ] } + ( [ 'u', 'acls_user' ], [ 'g', 'acls_group' ], + [ 'm', 'acls_mask' ], [ 'o', 'acls_other' ] ) ], + 4, 1) ], + [ $text{'acls_perms'}, + &ui_select('perms', '', + [ map { [ $_, $_ ] } + ( '', '---', 'r--', '-w-', '--x', + 'r-x', 'rw-', '-wx', 'rwx' ) ]) ], + [ $text{'acls_user'}, $acl_user_select ], + [ $text{'acls_group'}, $acl_group_select ], + [ undef, &ui_checkbox('recursive', 1, $text{'dialog_recursive'}, 0) ], + [ undef, $acl_manual ], + ]), + 'buttons' => $change, + 'close' => $cancel }); + } + +# Delete, after listing what is selected. Cancel takes the focus, so +# Enter does not delete by accident +$rv .= &ui_dialog({ + 'id' => 'filemin_delete', + 'title' => $text{'remove_selected'}, + 'state' => 'danger', + 'icon' => 'trash', + 'form' => &$apply('delete.cgi'), + 'body' => &ui_tag('p', &html_escape($text{'about_to_delete'})). + &ui_tag('pre', '', { 'id' => 'filemin_delete_items', + 'class' => 'filemin_delete_items' }). + &ui_tag('p', &html_escape($text{'are_you_sure'})), + 'buttons' => [ { 'label' => $text{'delete'}, 'state' => 'danger', + 'icon' => 'trash' } ], + 'close' => $cancel, + 'focus' => 'close' }); +return $rv; +} + # get_paste_buffer_file() # Returns the location of the file for temporary copy/paste state sub get_paste_buffer_file diff --git a/filemin/images/icons/quick/acls.png b/filemin/images/icons/quick/acls.png deleted file mode 100644 index 83b12af51..000000000 Binary files a/filemin/images/icons/quick/acls.png and /dev/null differ diff --git a/filemin/images/icons/quick/bookmark-new.png b/filemin/images/icons/quick/bookmark-new.png deleted file mode 100644 index 832cc8a57..000000000 Binary files a/filemin/images/icons/quick/bookmark-new.png and /dev/null differ diff --git a/filemin/images/icons/quick/browse.png b/filemin/images/icons/quick/browse.png deleted file mode 100644 index f9fd372e3..000000000 Binary files a/filemin/images/icons/quick/browse.png and /dev/null differ diff --git a/filemin/images/icons/quick/chattr.png b/filemin/images/icons/quick/chattr.png deleted file mode 100644 index 3c6c6fa6e..000000000 Binary files a/filemin/images/icons/quick/chattr.png and /dev/null differ diff --git a/filemin/images/icons/quick/chcon.png b/filemin/images/icons/quick/chcon.png deleted file mode 100644 index 494249195..000000000 Binary files a/filemin/images/icons/quick/chcon.png and /dev/null differ diff --git a/filemin/images/icons/quick/chmod.png b/filemin/images/icons/quick/chmod.png deleted file mode 100644 index 8363b33ff..000000000 Binary files a/filemin/images/icons/quick/chmod.png and /dev/null differ diff --git a/filemin/images/icons/quick/chown.png b/filemin/images/icons/quick/chown.png deleted file mode 100644 index ad1ab7550..000000000 Binary files a/filemin/images/icons/quick/chown.png and /dev/null differ diff --git a/filemin/images/icons/quick/compress.png b/filemin/images/icons/quick/compress.png deleted file mode 100644 index 5bd7d52d3..000000000 Binary files a/filemin/images/icons/quick/compress.png and /dev/null differ diff --git a/filemin/images/icons/quick/document-new.png b/filemin/images/icons/quick/document-new.png deleted file mode 100644 index c13709b11..000000000 Binary files a/filemin/images/icons/quick/document-new.png and /dev/null differ diff --git a/filemin/images/icons/quick/drive-harddisk.png b/filemin/images/icons/quick/drive-harddisk.png deleted file mode 100644 index 0e6a85e5f..000000000 Binary files a/filemin/images/icons/quick/drive-harddisk.png and /dev/null differ diff --git a/filemin/images/icons/quick/edit-copy.png b/filemin/images/icons/quick/edit-copy.png deleted file mode 100644 index 9834bfb25..000000000 Binary files a/filemin/images/icons/quick/edit-copy.png and /dev/null differ diff --git a/filemin/images/icons/quick/edit-cut.png b/filemin/images/icons/quick/edit-cut.png deleted file mode 100644 index bd1558871..000000000 Binary files a/filemin/images/icons/quick/edit-cut.png and /dev/null differ diff --git a/filemin/images/icons/quick/edit-find.png b/filemin/images/icons/quick/edit-find.png deleted file mode 100644 index cab3b274b..000000000 Binary files a/filemin/images/icons/quick/edit-find.png and /dev/null differ diff --git a/filemin/images/icons/quick/edit-paste.png b/filemin/images/icons/quick/edit-paste.png deleted file mode 100644 index 3562d6206..000000000 Binary files a/filemin/images/icons/quick/edit-paste.png and /dev/null differ diff --git a/filemin/images/icons/quick/edit.png b/filemin/images/icons/quick/edit.png deleted file mode 100644 index ab52446d2..000000000 Binary files a/filemin/images/icons/quick/edit.png and /dev/null differ diff --git a/filemin/images/icons/quick/extract.png b/filemin/images/icons/quick/extract.png deleted file mode 100644 index b07e2e682..000000000 Binary files a/filemin/images/icons/quick/extract.png and /dev/null differ diff --git a/filemin/images/icons/quick/folder-new.png b/filemin/images/icons/quick/folder-new.png deleted file mode 100644 index 54463fa49..000000000 Binary files a/filemin/images/icons/quick/folder-new.png and /dev/null differ diff --git a/filemin/images/icons/quick/from-url.png b/filemin/images/icons/quick/from-url.png deleted file mode 100644 index a15d57836..000000000 Binary files a/filemin/images/icons/quick/from-url.png and /dev/null differ diff --git a/filemin/images/icons/quick/go-down.png b/filemin/images/icons/quick/go-down.png deleted file mode 100644 index 335378947..000000000 Binary files a/filemin/images/icons/quick/go-down.png and /dev/null differ diff --git a/filemin/images/icons/quick/go-next.png b/filemin/images/icons/quick/go-next.png deleted file mode 100644 index 299d5a973..000000000 Binary files a/filemin/images/icons/quick/go-next.png and /dev/null differ diff --git a/filemin/images/icons/quick/invert.png b/filemin/images/icons/quick/invert.png deleted file mode 100644 index 7543d1bfb..000000000 Binary files a/filemin/images/icons/quick/invert.png and /dev/null differ diff --git a/filemin/images/icons/quick/remove.png b/filemin/images/icons/quick/remove.png deleted file mode 100644 index 884d47336..000000000 Binary files a/filemin/images/icons/quick/remove.png and /dev/null differ diff --git a/filemin/images/icons/quick/rename.png b/filemin/images/icons/quick/rename.png deleted file mode 100644 index 190400a90..000000000 Binary files a/filemin/images/icons/quick/rename.png and /dev/null differ diff --git a/filemin/images/icons/quick/select-all.png b/filemin/images/icons/quick/select-all.png deleted file mode 100644 index d0562c466..000000000 Binary files a/filemin/images/icons/quick/select-all.png and /dev/null differ diff --git a/filemin/images/icons/quick/upload.png b/filemin/images/icons/quick/upload.png deleted file mode 100644 index 210345890..000000000 Binary files a/filemin/images/icons/quick/upload.png and /dev/null differ diff --git a/filemin/search.cgi b/filemin/search.cgi index 53bbf06ed..d83f67fd6 100755 --- a/filemin/search.cgi +++ b/filemin/search.cgi @@ -13,7 +13,6 @@ $query = $in{'query'}; $text{'search_results'} . " '" . &html_escape($query) . "'", ""); -print $head; if ($in{'caseins'}) { $criteria = '-iname'; } diff --git a/filemin/unauthenticated/css/plain.css b/filemin/unauthenticated/css/plain.css new file mode 100644 index 000000000..02a3b30e3 --- /dev/null +++ b/filemin/unauthenticated/css/plain.css @@ -0,0 +1,85 @@ +/* + * plain.css + * Layout of the File Manager for themes without Bootstrap. The toolbar, + * its menus, the dialogs and the page links are core widgets styled by + * ui-lib.css; this file only arranges them around the listing and + * styles the row action links and the permissions matrix. + */ + +/* Breadcrumbs at the left, toolbar at the right, wrapping on a narrow + * frame */ +.filemin_head { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + gap: 8px 16px; +} +.filemin_head .ui_breadcrumb { + min-width: 0; + font-size: 1.05em; +} +.filemin_toolbar { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 6px; + margin-left: auto; +} +.filemin_pager { display: flex; } +.filemin_page .total { color: var(--ui-fg-muted); } + +/* Row action links hold icons and sit side by side */ +.filemin_page .action-link { + display: inline-flex; + align-items: center; + margin-right: 4px; + color: var(--ui-fg-muted); + vertical-align: middle; +} +.filemin_page .action-link:hover { color: var(--ui-accent); } +.filemin_page td > a > img { vertical-align: middle; } + +/* Dialog forms sit flush inside the dialog body, and stay within it : + * both the theme's wrapper table and the form table take the dialog + * width with fixed columns, the label column has a set width, and the + * inputs fit the value column instead of widening the dialog */ +.filemin_page .ui_dialog_body > table.wrapper { margin: 0; } +.filemin_page .ui_dialog_body table.wrapper, +.filemin_page .ui_dialog_body table.ui_table { + width: 100%; + table-layout: fixed; +} +.filemin_page .ui_dialog_body td.ui_form_label { + width: 8.5em; + white-space: nowrap; +} +.filemin_page .ui_dialog_body td.ui_form_value select { width: 100%; } +.filemin_page .ui_dialog_body td.ui_form_value + input:not([type="checkbox"]):not([type="radio"]) { + max-width: 100%; +} + +/* The permissions matrix */ +.filemin_chmod_matrix { border-collapse: collapse; } +.filemin_chmod_matrix th, .filemin_chmod_matrix td { + padding: 2px 12px 2px 0; + text-align: left; + font-weight: normal; +} +.filemin_chmod_matrix th { font-weight: 600; } +.filemin_chmod_matrix input[type="checkbox"] { margin: 0; } + +/* The entries about to be deleted */ +.filemin_delete_items { + max-height: 180px; + margin: 8px 0; + padding: 8px 10px; + overflow: auto; + border: 1px solid var(--ui-border-soft); + background: var(--ui-surface-2); + font-family: var(--ui-font-mono); + font-size: 0.92em; + white-space: pre-wrap; + overflow-wrap: anywhere; +} diff --git a/filemin/unauthenticated/dropdown/fg.menu.css b/filemin/unauthenticated/dropdown/fg.menu.css deleted file mode 100644 index 820db38d8..000000000 --- a/filemin/unauthenticated/dropdown/fg.menu.css +++ /dev/null @@ -1,114 +0,0 @@ -/* Styles for jQuery menu widget -Author: Maggie Wachs, maggie@filamentgroup.com -Date: September 2008 -*/ - - -/* REQUIRED STYLES - the menus will only render correctly with these rules */ - -.fg-menu-container { position: absolute; top:0; left:-999px; padding: .4em; overflow: hidden; } -.fg-menu-container.fg-menu-flyout { overflow: visible; } - -.fg-menu, .fg-menu ul { list-style-type:none; padding: 0; margin:0; } - -.fg-menu { position:relative; } -.fg-menu-flyout .fg-menu { position:static; } - -.fg-menu ul { position:absolute; top:0; } -.fg-menu ul ul { top:-1px; } - -.fg-menu-container.fg-menu-ipod .fg-menu-content, -.fg-menu-container.fg-menu-ipod .fg-menu-content ul { background: none !important; } - -.fg-menu.fg-menu-scroll, -.fg-menu ul.fg-menu-scroll { overflow: scroll; overflow-x: hidden; } - -.fg-menu li { clear:both; float:left; width:100%; margin: 0; padding:0; border: 0; } -.fg-menu li li { font-size:1em; } /* inner li font size must be reset so that they don't blow up */ - -.fg-menu-flyout ul ul { padding: .4em; } -.fg-menu-flyout li { position:relative; } - -.fg-menu-scroll { overflow: scroll; overflow-x: hidden; } - -.fg-menu-breadcrumb { margin: 0; padding: 0; } - -.fg-menu-footer { margin-top: .4em; padding: .4em; } -.fg-menu-header { margin-bottom: .4em; padding: .4em; } - -.fg-menu-breadcrumb li { float: left; list-style: none; margin: 0; padding: 0 .2em; font-size: .9em; opacity: .7; } -.fg-menu-breadcrumb li.fg-menu-prev-list, -.fg-menu-breadcrumb li.fg-menu-current-crumb { clear: left; float: none; opacity: 1; } -.fg-menu-breadcrumb li.fg-menu-current-crumb { padding-top: .2em; } - -.fg-menu-breadcrumb a, -.fg-menu-breadcrumb span { float: left; } - -.fg-menu-footer a:link, -.fg-menu-footer a:visited { float:left; width:100%; text-decoration: none; } -.fg-menu-footer a:hover, -.fg-menu-footer a:active { } - -.fg-menu-footer a span { float:left; cursor: pointer; } - -.fg-menu-breadcrumb .fg-menu-prev-list a:link, -.fg-menu-breadcrumb .fg-menu-prev-list a:visited, -.fg-menu-breadcrumb .fg-menu-prev-list a:hover, -.fg-menu-breadcrumb .fg-menu-prev-list a:active { background-image: none; text-decoration:none; } - -.fg-menu-breadcrumb .fg-menu-prev-list a { float: left; padding-right: .4em; } -.fg-menu-breadcrumb .fg-menu-prev-list a .ui-icon { float: left; } - -.fg-menu-breadcrumb .fg-menu-current-crumb a:link, -.fg-menu-breadcrumb .fg-menu-current-crumb a:visited, -.fg-menu-breadcrumb .fg-menu-current-crumb a:hover, -.fg-menu-breadcrumb .fg-menu-current-crumb a:active { display:block; background-image:none; font-size:1.3em; text-decoration:none; } - - - -/* REQUIRED LINK STYLES: links are "display:block" by default; if the menu options are split into - selectable node links and 'next' links, the script floats the node links left and floats the 'next' links to the right */ - -.fg-menu a:link, -.fg-menu a:visited, -.fg-menu a:hover, -.fg-menu a:active { float:left; width:92%; padding:.3em 3%; text-decoration:none; outline: 0 !important; } - -.fg-menu a { border: 1px dashed transparent; } - -.fg-menu a.ui-state-default:link, -.fg-menu a.ui-state-default:visited, -.fg-menu a.ui-state-default:hover, -.fg-menu a.ui-state-default:active, -.fg-menu a.ui-state-hover:link, -.fg-menu a.ui-state-hover:visited, -.fg-menu a.ui-state-hover:hover, -.fg-menu a.ui-state-hover:active, - .fg-menu a.ui-state-active:link, - .fg-menu a.ui-state-active:visited, - .fg-menu a.ui-state-active:hover, -.fg-menu a.ui-state-active:active { border-style: solid; font-weight: normal; } - -.fg-menu a span { display:block; cursor:pointer; } - - - /* SUGGESTED STYLES - for use with jQuery UI Themeroller CSS */ - -.fg-menu-indicator span { float:left; } -.fg-menu-indicator span.ui-icon { float:right; } - -.fg-menu-content.ui-widget-content, -.fg-menu-content ul.ui-widget-content { border:0; } - - -/* ICONS AND DIVIDERS */ - -.fg-menu.fg-menu-has-icons a:link, -.fg-menu.fg-menu-has-icons a:visited, -.fg-menu.fg-menu-has-icons a:hover, -.fg-menu.fg-menu-has-icons a:active { padding-left:20px; } - -.fg-menu .horizontal-divider hr, .fg-menu .horizontal-divider span { padding:0; margin:5px .6em; } -.fg-menu .horizontal-divider hr { border:0; height:1px; } -.fg-menu .horizontal-divider span { font-size:.9em; text-transform: uppercase; padding-left:.2em; } - diff --git a/filemin/unauthenticated/dropdown/fg.menu.js b/filemin/unauthenticated/dropdown/fg.menu.js deleted file mode 100644 index 1edcce0d5..000000000 --- a/filemin/unauthenticated/dropdown/fg.menu.js +++ /dev/null @@ -1,648 +0,0 @@ -/*-------------------------------------------------------------------- -Scripts for creating and manipulating custom menus based on standard