Rebuild File Manager's non-Bootstrap interface on the core widgets

This commit is contained in:
Ilia Ross
2026-10-11 17:57:36 +02:00
parent 7b6c3b3857
commit bc9d7e67a8
44 changed files with 764 additions and 1716 deletions

View File

@@ -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 =
"<img src='images/icons/quick/edit.png'" .
" alt='$text{'edit'}' />";
$rename_icon =
"<img src='images/icons/quick/" .
"rename.png' " .
"alt='$text{'rename'}' />";
$extract_icon =
"<img src='images/icons/quick/" .
"extract.png' " .
"alt='$text{'extract_archive'}' />";
$goto_icon =
"<img src='images/icons/quick/" .
"go-next.png' " .
"alt='$text{'goto_folder'}'";
# Add static files
$head = "<link rel=\"stylesheet\" " .
"type=\"text/css\" " .
"href=\"unauthenticated/css/" .
"style.css\" />";
$head .= "<script type=\"text/javascript\"" .
" src=\"unauthenticated/jquery/" .
"jquery.min.js\"></script>";
$head .= "<script type=\"text/javascript\"" .
" src=\"unauthenticated/jquery/" .
"jquery-ui.min.js\"></script>";
$head .= "<script type=\"text/javascript\"" .
" src=\"unauthenticated/js/" .
"legacy.js\"></script>";
$head .= "<link rel=\"stylesheet\" " .
"type=\"text/css\" " .
"href=\"unauthenticated/jquery/" .
"jquery-ui.min.css\" />";
$head .= "<script type=\"text/javascript\"" .
" src=\"unauthenticated/js/" .
"chmod-calculator.js\"></script>";
$head .= "<link rel=\"stylesheet\" " .
"type=\"text/css\" " .
"href=\"unauthenticated/dropdown/" .
"fg.menu.css\" />";
$head .= "<script type=\"text/javascript\"" .
" src=\"unauthenticated/dropdown/" .
"fg.menu.js\"></script>";
print $head;
# Set "root" icon
if ($base eq '/') {
$root_icon =
"<img src=\"images/icons/quick/" .
"drive-harddisk.png\" " .
"class=\"hdd-icon\" />";
}
else {
$root_icon = "~";
}
# Legacy breadcrumbs
print "<div id='bread' style='float: left;" .
" padding-bottom: 2px;'>" .
"<a href='index.cgi?path='>" .
"$root_icon</a> / ";
my @breadcr = split('/', $path);
my $cp = '';
for (my $i = 1;
$i <= scalar(@breadcr)-1; $i++) {
chomp($breadcr[$i]);
$cp = $cp.'/'.$breadcr[$i];
print "<a href='index.cgi?path=" .
&urlize($cp) . "'>" .
&html_escape($breadcr[$i]) .
"</a> / ";
}
print "<br />";
# 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 "<a class='pages active' " .
"href='?path=" .
&urlize($path) .
"&page=$i" .
"&query=" .
&urlize($query) .
"'>" .
&html_escape($i) .
"</a>";
}
else {
print "<a class='pages' " .
"href='?path=" .
&urlize($path) .
"&page=$i" .
"&query=" .
&urlize($query) .
"'>" .
&html_escape($i) .
"</a>";
}
}
print "</div>";
# 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 "<li><a>$text{'no_bookmarks'}</a></li>";
}
my $bookmarks =
&read_file_lines($confdir.'/.bookmarks', 1);
$result = '';
foreach $bookmark (@$bookmarks) {
foreach $bookmark (&list_bookmarks()) {
$result .=
"<li><a href='index.cgi?path=" .
&urlize($bookmark) . "'>" .
@@ -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')."<br>".
&ui_checkbox('opt_extract', 1, $text{'extract_uploaded'}, 0,
"data-filemin-upload-option='extract_uploaded'").
"<br>".
&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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 944 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 734 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 956 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 788 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 738 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 529 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 864 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 702 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 660 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 537 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 838 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 736 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 754 B

View File

@@ -13,7 +13,6 @@ $query = $in{'query'};
$text{'search_results'} . " '" .
&html_escape($query) . "'", "");
print $head;
if ($in{'caseins'}) {
$criteria = '-iname';
}

View File

@@ -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;
}

View File

@@ -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; }

View File

@@ -1,648 +0,0 @@
/*--------------------------------------------------------------------
Scripts for creating and manipulating custom menus based on standard <ul> markup
Version: 3.0, 03.31.2009
By: Maggie Costello Wachs (maggie@filamentgroup.com) and Scott Jehl (scott@filamentgroup.com)
http://www.filamentgroup.com
* reference articles: http://www.filamentgroup.com/lab/jquery_ipod_style_drilldown_menu/
Copyright (c) 2009 Filament Group
Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
--------------------------------------------------------------------*/
var allUIMenus = [];
$.fn.menu = function(options){
var caller = this;
var options = options;
var m = new Menu(caller, options);
allUIMenus.push(m);
$(this)
.mousedown(function(){
if (!m.menuOpen) { m.showLoading(); };
})
.click(function(){
if (m.menuOpen == false) { m.showMenu(); }
else { m.kill(); };
return false;
});
};
function Menu(caller, options){
var menu = this;
var caller = $(caller);
var container = $('<div class="fg-menu-container ui-widget ui-widget-content ui-corner-all">'+options.content+'</div>');
this.menuOpen = false;
this.menuExists = false;
var options = jQuery.extend({
content: null,
width: 180, // width of menu container, must be set or passed in to calculate widths of child menus
maxHeight: 180, // max height of menu (if a drilldown: height does not include breadcrumb)
positionOpts: {
posX: 'left',
posY: 'bottom',
offsetX: 0,
offsetY: 0,
directionH: 'right',
directionV: 'down',
detectH: true, // do horizontal collision detection
detectV: true, // do vertical collision detection
linkToFront: false
},
showSpeed: 200, // show/hide speed in milliseconds
callerOnState: 'ui-state-active', // class to change the appearance of the link/button when the menu is showing
loadingState: 'ui-state-loading', // class added to the link/button while the menu is created
linkHover: 'ui-state-hover', // class for menu option hover state
linkHoverSecondary: 'li-hover', // alternate class, may be used for multi-level menus
// ----- multi-level menu defaults -----
crossSpeed: 200, // cross-fade speed for multi-level menus
crumbDefaultText: 'Choose an option:',
backLink: true, // in the ipod-style menu: instead of breadcrumbs, show only a 'back' link
backLinkText: 'Back',
flyOut: false, // multi-level menus are ipod-style by default; this parameter overrides to make a flyout instead
flyOutOnState: 'ui-state-default',
nextMenuLink: 'ui-icon-triangle-1-e', // class to style the link (specifically, a span within the link) used in the multi-level menu to show the next level
topLinkText: 'All',
nextCrumbLink: 'ui-icon-carat-1-e'
}, options);
var killAllMenus = function(){
$.each(allUIMenus, function(i){
if (allUIMenus[i].menuOpen) { allUIMenus[i].kill(); };
});
};
this.kill = function(){
caller
.removeClass(options.loadingState)
.removeClass('fg-menu-open')
.removeClass(options.callerOnState);
container.find('li').removeClass(options.linkHoverSecondary).find('a').removeClass(options.linkHover);
if (options.flyOutOnState) { container.find('li a').removeClass(options.flyOutOnState); };
if (options.callerOnState) { caller.removeClass(options.callerOnState); };
if (container.is('.fg-menu-ipod')) { menu.resetDrilldownMenu(); };
if (container.is('.fg-menu-flyout')) { menu.resetFlyoutMenu(); };
container.parent().hide();
menu.menuOpen = false;
$(document).unbind('click', killAllMenus);
$(document).unbind('keydown');
};
this.showLoading = function(){
caller.addClass(options.loadingState);
};
this.showMenu = function(){
killAllMenus();
if (!menu.menuExists) { menu.create() };
caller
.addClass('fg-menu-open')
.addClass(options.callerOnState);
container.parent().show().click(function(){ menu.kill(); return false; });
container.hide().slideDown(options.showSpeed).find('.fg-menu:eq(0)');
menu.menuOpen = true;
caller.removeClass(options.loadingState);
$(document).click(killAllMenus);
// assign key events
$(document).keydown(function(event){
var e;
if (event.which !="") { e = event.which; }
else if (event.charCode != "") { e = event.charCode; }
else if (event.keyCode != "") { e = event.keyCode; }
var menuType = ($(event.target).parents('div').is('.fg-menu-flyout')) ? 'flyout' : 'ipod' ;
switch(e) {
case 37: // left arrow
if (menuType == 'flyout') {
$(event.target).trigger('mouseout');
if ($('.'+options.flyOutOnState).length > 0) { $('.'+options.flyOutOnState).trigger('mouseover'); };
};
if (menuType == 'ipod') {
$(event.target).trigger('mouseout');
if ($('.fg-menu-footer').find('a').length > 0) { $('.fg-menu-footer').find('a').trigger('click'); };
if ($('.fg-menu-header').find('a').length > 0) { $('.fg-menu-current-crumb').prev().find('a').trigger('click'); };
if ($('.fg-menu-current').prev().is('.fg-menu-indicator')) {
$('.fg-menu-current').prev().trigger('mouseover');
};
};
return false;
break;
case 38: // up arrow
if ($(event.target).is('.' + options.linkHover)) {
var prevLink = $(event.target).parent().prev().find('a:eq(0)');
if (prevLink.length > 0) {
$(event.target).trigger('mouseout');
prevLink.trigger('mouseover');
};
}
else { container.find('a:eq(0)').trigger('mouseover'); }
return false;
break;
case 39: // right arrow
if ($(event.target).is('.fg-menu-indicator')) {
if (menuType == 'flyout') {
$(event.target).next().find('a:eq(0)').trigger('mouseover');
}
else if (menuType == 'ipod') {
$(event.target).trigger('click');
setTimeout(function(){
$(event.target).next().find('a:eq(0)').trigger('mouseover');
}, options.crossSpeed);
};
};
return false;
break;
case 40: // down arrow
if ($(event.target).is('.' + options.linkHover)) {
var nextLink = $(event.target).parent().next().find('a:eq(0)');
if (nextLink.length > 0) {
$(event.target).trigger('mouseout');
nextLink.trigger('mouseover');
};
}
else { container.find('a:eq(0)').trigger('mouseover'); }
return false;
break;
case 27: // escape
killAllMenus();
break;
case 13: // enter
if ($(event.target).is('.fg-menu-indicator') && menuType == 'ipod') {
$(event.target).trigger('click');
setTimeout(function(){
$(event.target).next().find('a:eq(0)').trigger('mouseover');
}, options.crossSpeed);
};
break;
};
});
};
this.create = function() {
container.css({ width: options.width }).appendTo('body').find('ul:first').not('.fg-menu-breadcrumb').addClass('fg-menu');
container.find('ul, li a').addClass('ui-corner-all');
// aria roles & attributes
container.find('ul').attr('role', 'menu').eq(0).attr('aria-activedescendant','active-menuitem').attr('aria-labelledby', caller.attr('id'));
container.find('li').attr('role', 'menuitem');
container.find('li:has(ul)').attr('aria-haspopup', 'true').find('ul').attr('aria-expanded', 'false');
container.find('a').attr('tabindex', '-1');
// when there are multiple levels of hierarchy, create flyout or drilldown menu
if (container.find('ul').length > 1) {
if (options.flyOut) { menu.flyout(container, options); }
else { menu.drilldown(container, options); }
}
else {
container.find('a').click(function(){
menu.chooseItem(this);
return false;
});
};
if (options.linkHover) {
var allLinks = container.find('.fg-menu li a');
allLinks.hover(
function(){
var menuitem = $(this);
$('.'+options.linkHover).removeClass(options.linkHover).blur().parent().removeAttr('id');
$(this).addClass(options.linkHover).focus().parent().attr('id','active-menuitem');
},
function(){
$(this).removeClass(options.linkHover).blur().parent().removeAttr('id');
}
);
};
if (options.linkHoverSecondary) {
container.find('.fg-menu li').hover(
function(){
$(this).siblings('li').removeClass(options.linkHoverSecondary);
if (options.flyOutOnState) { $(this).siblings('li').find('a').removeClass(options.flyOutOnState); }
$(this).addClass(options.linkHoverSecondary);
},
function(){ $(this).removeClass(options.linkHoverSecondary); }
);
};
menu.setPosition(container, caller, options);
menu.menuExists = true;
// Hide empty
container.find('a:empty').parent('li').hide()
};
this.chooseItem = function(item){
menu.kill();
// edit this for your own custom function/callback:
$('#menuSelection').text($(item).text());
location.href = $(item).attr('href');
};
};
Menu.prototype.flyout = function(container, options) {
var menu = this;
this.resetFlyoutMenu = function(){
var allLists = container.find('ul ul');
allLists.removeClass('ui-widget-content').hide();
};
container.addClass('fg-menu-flyout').find('li:has(ul)').each(function(){
var linkWidth = container.width();
var showTimer, hideTimer;
var allSubLists = $(this).find('ul');
allSubLists.css({ left: linkWidth, width: linkWidth }).hide();
$(this).find('a:eq(0)').addClass('fg-menu-indicator').html('<span>' + $(this).find('a:eq(0)').text() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>').hover(
function(){
clearTimeout(hideTimer);
var subList = $(this).next();
if (!fitVertical(subList, $(this).offset().top)) { subList.css({ top: 'auto', bottom: 0 }); };
if (!fitHorizontal(subList, $(this).offset().left + 100)) { subList.css({ left: 'auto', right: linkWidth, 'z-index': 999 }); };
showTimer = setTimeout(function(){
subList.addClass('ui-widget-content').show(options.showSpeed).attr('aria-expanded', 'true');
}, 300);
},
function(){
clearTimeout(showTimer);
var subList = $(this).next();
hideTimer = setTimeout(function(){
subList.removeClass('ui-widget-content').hide(options.showSpeed).attr('aria-expanded', 'false');
}, 400);
}
);
$(this).find('ul a').hover(
function(){
clearTimeout(hideTimer);
if ($(this).parents('ul').prev().is('a.fg-menu-indicator')) {
$(this).parents('ul').prev().addClass(options.flyOutOnState);
}
},
function(){
hideTimer = setTimeout(function(){
allSubLists.hide(options.showSpeed);
container.find(options.flyOutOnState).removeClass(options.flyOutOnState);
}, 500);
}
);
});
container.find('a').click(function(){
menu.chooseItem(this);
return false;
});
};
Menu.prototype.drilldown = function(container, options) {
var menu = this;
var topList = container.find('.fg-menu');
var breadcrumb = $('<ul class="fg-menu-breadcrumb ui-widget-header ui-corner-all ui-helper-clearfix"></ul>');
var crumbDefaultHeader = $('<li class="fg-menu-breadcrumb-text">'+options.crumbDefaultText+'</li>');
var firstCrumbText = (options.backLink) ? options.backLinkText : options.topLinkText;
var firstCrumbClass = (options.backLink) ? 'fg-menu-prev-list' : 'fg-menu-all-lists';
var firstCrumbLinkClass = (options.backLink) ? 'ui-state-default ui-corner-all' : '';
var firstCrumbIcon = (options.backLink) ? '<span class="ui-icon ui-icon-triangle-1-w"></span>' : '';
var firstCrumb = $('<li class="'+firstCrumbClass+'"><a href="#" class="'+firstCrumbLinkClass+'">'+firstCrumbIcon+firstCrumbText+'</a></li>');
container.addClass('fg-menu-ipod');
if (options.backLink) { breadcrumb.addClass('fg-menu-footer').appendTo(container).hide(); }
else { breadcrumb.addClass('fg-menu-header').prependTo(container); };
breadcrumb.append(crumbDefaultHeader);
var checkMenuHeight = function(el){
if (el.height() > options.maxHeight) { el.addClass('fg-menu-scroll') };
el.css({ height: options.maxHeight });
};
var resetChildMenu = function(el){ el.removeClass('fg-menu-scroll').removeClass('fg-menu-current').height('auto'); };
this.resetDrilldownMenu = function(){
$('.fg-menu-current').removeClass('fg-menu-current');
topList.animate({ left: 0 }, options.crossSpeed, function(){
$(this).find('ul').each(function(){
$(this).hide();
resetChildMenu($(this));
});
topList.addClass('fg-menu-current');
});
$('.fg-menu-all-lists').find('span').remove();
breadcrumb.empty().append(crumbDefaultHeader);
$('.fg-menu-footer').empty().hide();
checkMenuHeight(topList);
};
topList
.addClass('fg-menu-content fg-menu-current ui-widget-content ui-helper-clearfix')
.css({ width: container.width() })
.find('ul')
.css({ width: container.width(), left: container.width() })
.addClass('ui-widget-content')
.hide();
checkMenuHeight(topList);
topList.find('a').each(function(){
// if the link opens a child menu:
if ($(this).next().is('ul')) {
$(this)
.addClass('fg-menu-indicator')
.each(function(){ $(this).html('<span>' + $(this).text() + '</span><span class="ui-icon '+options.nextMenuLink+'"></span>'); })
.click(function(){ // ----- show the next menu
var nextList = $(this).next();
var parentUl = $(this).parents('ul:eq(0)');
var parentLeft = (parentUl.is('.fg-menu-content')) ? 0 : parseFloat(topList.css('left'));
var nextLeftVal = Math.round(parentLeft - parseFloat(container.width()));
var footer = $('.fg-menu-footer');
// show next menu
resetChildMenu(parentUl);
checkMenuHeight(nextList);
topList.animate({ left: nextLeftVal }, options.crossSpeed);
nextList.show().addClass('fg-menu-current').attr('aria-expanded', 'true');
var setPrevMenu = function(backlink){
var b = backlink;
var c = $('.fg-menu-current');
var prevList = c.parents('ul:eq(0)');
c.hide().attr('aria-expanded', 'false');
resetChildMenu(c);
checkMenuHeight(prevList);
prevList.addClass('fg-menu-current').attr('aria-expanded', 'true');
if (prevList.hasClass('fg-menu-content')) { b.remove(); footer.hide(); };
};
// initialize "back" link
if (options.backLink) {
if (footer.find('a').length == 0) {
footer.show();
$('<a href="#"><span class="ui-icon ui-icon-triangle-1-w"></span> <span>Back</span></a>')
.appendTo(footer)
.click(function(){ // ----- show the previous menu
var b = $(this);
var prevLeftVal = parseFloat(topList.css('left')) + container.width();
topList.animate({ left: prevLeftVal }, options.crossSpeed, function(){
setPrevMenu(b);
});
return false;
});
}
}
// or initialize top breadcrumb
else {
if (breadcrumb.find('li').length == 1){
breadcrumb.empty().append(firstCrumb);
firstCrumb.find('a').click(function(){
menu.resetDrilldownMenu();
return false;
});
}
$('.fg-menu-current-crumb').removeClass('fg-menu-current-crumb');
var crumbText = $(this).find('span:eq(0)').text();
var newCrumb = $('<li class="fg-menu-current-crumb"><a href="javascript://" class="fg-menu-crumb">'+crumbText+'</a></li>');
newCrumb
.appendTo(breadcrumb)
.find('a').click(function(){
if ($(this).parent().is('.fg-menu-current-crumb')){
menu.chooseItem(this);
}
else {
var newLeftVal = - ($('.fg-menu-current').parents('ul').length - 1) * 180;
topList.animate({ left: newLeftVal }, options.crossSpeed, function(){
setPrevMenu();
});
// make this the current crumb, delete all breadcrumbs after this one, and navigate to the relevant menu
$(this).parent().addClass('fg-menu-current-crumb').find('span').remove();
$(this).parent().nextAll().remove();
};
return false;
});
newCrumb.prev().append(' <span class="ui-icon '+options.nextCrumbLink+'"></span>');
};
return false;
});
}
// if the link is a leaf node (doesn't open a child menu)
else {
$(this).click(function(){
menu.chooseItem(this);
return false;
});
};
});
};
/* Menu.prototype.setPosition parameters (defaults noted with *):
referrer = the link (or other element) used to show the overlaid object
settings = can override the defaults:
- posX/Y: where the top left corner of the object should be positioned in relation to its referrer.
X: left*, center, right
Y: top, center, bottom*
- offsetX/Y: the number of pixels to be offset from the x or y position. Can be a positive or negative number.
- directionH/V: where the entire menu should appear in relation to its referrer.
Horizontal: left*, right
Vertical: up, down*
- detectH/V: detect the viewport horizontally / vertically
- linkToFront: copy the menu link and place it on top of the menu (visual effect to make it look like it overlaps the object) */
Menu.prototype.setPosition = function(widget, caller, options) {
var el = widget;
var referrer = caller;
var dims = {
refX: referrer.offset().left,
refY: referrer.offset().top,
refW: referrer.getTotalWidth(),
refH: referrer.getTotalHeight()
};
var options = options;
var xVal, yVal;
var helper = $('<div class="positionHelper"></div>');
helper.css({ position: 'absolute', left: dims.refX, top: dims.refY, width: dims.refW, height: dims.refH });
el.wrap(helper);
// get X pos
switch(options.positionOpts.posX) {
case 'left': xVal = 0;
break;
case 'center': xVal = dims.refW / 2;
break;
case 'right': xVal = dims.refW;
break;
};
// get Y pos
switch(options.positionOpts.posY) {
case 'top': yVal = 0;
break;
case 'center': yVal = dims.refH / 2;
break;
case 'bottom': yVal = dims.refH;
break;
};
// add the offsets (zero by default)
xVal += options.positionOpts.offsetX;
yVal += options.positionOpts.offsetY;
// position the object vertically
if (options.positionOpts.directionV == 'up') {
el.css({ top: 'auto', bottom: yVal });
if (options.positionOpts.detectV && !fitVertical(el)) {
el.css({ bottom: 'auto', top: yVal });
}
}
else {
el.css({ bottom: 'auto', top: yVal });
if (options.positionOpts.detectV && !fitVertical(el)) {
el.css({ top: 'auto', bottom: yVal });
}
};
// and horizontally
if (options.positionOpts.directionH == 'left') {
el.css({ left: 'auto', right: xVal });
if (options.positionOpts.detectH && !fitHorizontal(el)) {
el.css({ right: 'auto', left: xVal });
}
}
else {
el.css({ right: 'auto', left: xVal });
if (options.positionOpts.detectH && !fitHorizontal(el)) {
el.css({ left: 'auto', right: xVal });
}
};
// if specified, clone the referring element and position it so that it appears on top of the menu
if (options.positionOpts.linkToFront) {
referrer.clone().addClass('linkClone').css({
position: 'absolute',
top: 0,
right: 'auto',
bottom: 'auto',
left: 0,
width: referrer.width(),
height: referrer.height()
}).insertAfter(el);
};
};
/* Utilities to sort and find viewport dimensions */
function sortBigToSmall(a, b) { return b - a; };
jQuery.fn.getTotalWidth = function(){
return $(this).width() + parseInt($(this).css('paddingRight')) + parseInt($(this).css('paddingLeft')) + parseInt($(this).css('borderRightWidth')) + parseInt($(this).css('borderLeftWidth'));
};
jQuery.fn.getTotalHeight = function(){
return $(this).height() + parseInt($(this).css('paddingTop')) + parseInt($(this).css('paddingBottom')) + parseInt($(this).css('borderTopWidth')) + parseInt($(this).css('borderBottomWidth'));
};
function getScrollTop(){
return self.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
};
function getScrollLeft(){
return self.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
};
function getWindowHeight(){
var de = document.documentElement;
return self.innerHeight || (de && de.clientHeight) || document.body.clientHeight;
};
function getWindowWidth(){
var de = document.documentElement;
return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
};
/* Utilities to test whether an element will fit in the viewport
Parameters:
el = element to position, required
leftOffset / topOffset = optional parameter if the offset cannot be calculated (i.e., if the object is in the DOM but is set to display: 'none') */
function fitHorizontal(el, leftOffset){
var leftVal = parseInt(leftOffset) || $(el).offset().left;
return (leftVal + $(el).width() <= getWindowWidth() + getScrollLeft() && leftVal - getScrollLeft() >= 0);
};
function fitVertical(el, topOffset){
var topVal = parseInt(topOffset) || $(el).offset().top;
return (topVal + $(el).height() <= getWindowHeight() + getScrollTop() && topVal - getScrollTop() >= 0);
};
/*--------------------------------------------------------------------
* javascript method: "pxToEm"
* by:
Scott Jehl (scott@filamentgroup.com)
Maggie Wachs (maggie@filamentgroup.com)
http://www.filamentgroup.com
*
* Copyright (c) 2008 Filament Group
* Dual licensed under the MIT (filamentgroup.com/examples/mit-license.txt) and GPL (filamentgroup.com/examples/gpl-license.txt) licenses.
*
* Description: Extends the native Number and String objects with pxToEm method. pxToEm converts a pixel value to ems depending on inherited font size.
* Article: http://www.filamentgroup.com/lab/retaining_scalable_interfaces_with_pixel_to_em_conversion/
* Demo: http://www.filamentgroup.com/examples/pxToEm/
*
* Options:
scope: string or jQuery selector for font-size scoping
reverse: Boolean, true reverses the conversion to em-px
* Dependencies: jQuery library
* Usage Example: myPixelValue.pxToEm(); or myPixelValue.pxToEm({'scope':'#navigation', reverse: true});
*
* Version: 2.0, 08.01.2008
* Changelog:
* 08.02.2007 initial Version 1.0
* 08.01.2008 - fixed font-size calculation for IE
--------------------------------------------------------------------*/
Number.prototype.pxToEm = String.prototype.pxToEm = function(settings){
//set defaults
settings = jQuery.extend({
scope: 'body',
reverse: false
}, settings);
var pxVal = (this == '') ? 0 : parseFloat(this);
var scopeVal;
var getWindowWidth = function(){
var de = document.documentElement;
return self.innerWidth || (de && de.clientWidth) || document.body.clientWidth;
};
/* When a percentage-based font-size is set on the body, IE returns that percent of the window width as the font-size.
For example, if the body font-size is 62.5% and the window width is 1000px, IE will return 625px as the font-size.
When this happens, we calculate the correct body font-size (%) and multiply it by 16 (the standard browser font size)
to get an accurate em value. */
if (settings.scope == 'body' && $.browser.msie && (parseFloat($('body').css('font-size')) / getWindowWidth()).toFixed(1) > 0.0) {
var calcFontSize = function(){
return (parseFloat($('body').css('font-size'))/getWindowWidth()).toFixed(3) * 16;
};
scopeVal = calcFontSize();
}
else { scopeVal = parseFloat(jQuery(settings.scope).css("font-size")); };
var result = (settings.reverse == true) ? (pxVal * scopeVal).toFixed(2) + 'px' : (pxVal / scopeVal).toFixed(2) + 'em';
return result;
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,475 +0,0 @@
$( document ).ready(function() {
/*
$( "input[title]" ).tooltip({
position: {
my: "left top",
at: "right+5 top-5"
}
});
/*
$( "a[title]" ).tooltip({
position: {
my: "bottom center",
at: "bottom+30"
}
});*/
$('tr').removeAttr('onmouseover');
$('tr').removeAttr('onmouseout');
$('input').removeAttr('onclick');
$('._select-unselect_').change(function() { selectUnselect($(this)); });
// BUTTONS
$('.fg-button').hover(
function(){ $(this).removeClass('ui-state-default').addClass('ui-state-focus'); },
function(){ $(this).removeClass('ui-state-focus').addClass('ui-state-default'); }
);
// MENUS
$('#flat').menu({
content: $('#flat').next().html(), // grab content from this page
showSpeed: 100
});
});
window.onload = function() {
var checkboxes = document.getElementsByClassName('ui_checkbox');
for(var i = 0; i < checkboxes.length; i++) {
checkboxes[i].onclick = function() {
var row = this.parentNode.parentNode;
if (this.checked) {
row.className = row.className + ' checked';
}
else {
row.className = row.className.replace(' checked', '');
}
};
}
}
function countUploads(files) {
if(files.files.length = 0) return;
var info = '';
for (i = 0; i < files.files.length; i++) {
info += files.files[i].name + '<br>';
}
$('#readyForUploadList').html(info);
}
function selectAll() {
var rows = document.getElementsByClassName('ui_checked_columns');
for (i = 0; i < rows.length; i++) {
var input = rows[i].getElementsByTagName('input')[0];
if (!input.checked) {
rowClick(rows[i]);
}
}
}
function invertSelection() {
var rows = document.getElementsByClassName('ui_checked_columns');
for (i = 0; i < rows.length; i++)
rowClick(rows[i]);
}
function compressDialog() {
if(checkSelected()) {
$( "#compressDialog" ).dialog({
modal: true,
buttons: {
"Compress": function() {
compressSelected();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
}
function compressSelected() {
var filename = $('#compressSelectedForm input[name=filename]').val();
if (filename != null && filename != "") {
var method = $('#compressSelectedForm select[name=method] option:selected').val();
$('#list_form').attr('action', "compress.cgi?arch=" + filename + "&method=" + method);
$('#list_form').submit();
}
}
function removeSelected() {
if(checkSelected()) {
$('#items-to-remove').html('');
$(".ui_checked_checkbox input[type='checkbox']:checked").each(function() {
$('#items-to-remove').append($(this).val() + '<br>');
});
$( "#removeDialog" ).dialog({
modal: true,
buttons: {
"YES": function() {
document.forms['list_form'].action = "delete.cgi";
document.forms['list_form'].submit();
},
"NO": function() {
$( this ).dialog( "close" );
}
}
});
}
}
function chmodDialog() {
if(checkSelected()) {
$( "#chmodDialog" ).dialog({
modal: true,
minWidth: 550,
buttons: {
"Change": function() {
chmodSelected();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
}
function chmodSelected() {
var perms = $('#perms').val();
if (perms != null && perms != "") {
var applyto = $('#chmodForm select[name=applyto] option:selected').val();
$('#list_form').attr('action', "chmod.cgi?perms=" + perms + "&applyto=" + applyto);
$('#list_form').submit();
}
}
function chownDialog() {
if(checkSelected()) {
$( "#chownDialog" ).dialog({
modal: true,
buttons: {
"Change": function() {
chownSelected();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
}
function chownSelected() {
var owner = $('#chownForm input[name=owner]').val();
var group = $('#chownForm input[name=group]').val();
var recursive = $('#chown-recursive').prop('checked');
if (owner != null && owner != "" && group != null && group != "") {
$('#list_form').attr('action', "chown.cgi?owner=" + owner + "&group=" + group + "&recursive=" + recursive);
$('#list_form').submit();
}
}
function chattrDialog() {
if(checkSelected()) {
$( "#chattrDialog" ).dialog({
modal: true,
buttons: {
"Change": function() {
chattrSelected();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
}
function chattrSelected() {
var label = $('#chattrForm input[name=label]').val(),
recursive = $('#chattr-recursive').prop('checked');
if (label != null && label != "") {
$('#list_form').attr('action', "chattr.cgi?label=" + encodeURIComponent(label) + "&recursive=" + recursive);
$('#list_form').submit();
}
}
function chconDialog() {
if(checkSelected()) {
$( "#chconDialog" ).dialog({
modal: true,
buttons: {
"Change": function() {
chconSelected();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
}
function chconSelected() {
var label = $('#chconForm input[name=label]').val(),
recursive = $('#chcon-recursive').prop('checked');
if (label != null && label != "") {
$('#list_form').attr('action', "chcon.cgi?label=" + label + "&recursive=" + recursive);
$('#list_form').submit();
}
}
function aclsDialog() {
if(checkSelected()) {
$( "#aclsDialog" ).dialog({
modal: true,
buttons: {
"Change": function() {
aclsSelected();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
}
function aclsSelected() {
var aclsForm = $('#aclsForm').serialize();
$('#list_form').attr('action', "setfacl.cgi?" + aclsForm);
$('#list_form').submit();
}
function renameDialog(file) {
$("#renameForm input[name=name]").val(file);
$("#renameForm input[name=file]").val(file);
$( "#renameDialog" ).dialog({
modal: true,
buttons: {
"Rename": function() {
renameSelected();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
function renameSelected() {
var name = $('#renameForm input[name=name]').val();
var file = $('#renameForm input[name=file]').val();
if (name != null && name != "" && name != file) {
$('#renameForm').submit();
}
}
function copySelected() {
if(checkSelected()) {
document.forms['list_form'].action = "copy.cgi";
document.forms['list_form'].submit();
}
}
function cutSelected() {
if(checkSelected()) {
document.forms['list_form'].action = "cut.cgi";
document.forms['list_form'].submit();
}
}
function viewReadyForUpload() {
$( "#readyForUploadDialog" ).dialog({
modal: true,
buttons: {
"OK": function() {
uploadFiles();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
function browseForUpload() {
var files = document.getElementById('upfiles');
files.click();
return true;
}
function uploadFiles() {
var files = document.getElementById('upfiles');
if (files.files.length > 0)
$('#upload-form').submit();
else
files.click();
}
function createFolderDialog() {
$( "#createFolderDialog" ).dialog({
modal: true,
buttons: {
"Create": function() {
createFolder();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
function createFolder() {
var name = $('#createFolderForm input[name=name]').val();
if (name != null && name != "")
$("#createFolderForm").submit();
else {
/* var tooltip = $('#createFolderForm input[name=name]').tooltip({
position: {
my: "left top",
at: "right+5 top-5"
}
});
tooltip.tooltip('open');*/
$('#createFolderForm input[name=name]').tooltip('open');
}
}
function createFileDialog() {
$( "#createFileDialog" ).dialog({
modal: true,
buttons: {
"Create": function() {
createFile();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
function createFile() {
var name = $('#createFileForm input[name=name]').val();
if (name != null && name != "") {
$("#createFileForm").submit();
}
}
function downFromUrlDialog() {
$( "#downFromUrlDialog" ).dialog({
modal: true,
buttons: {
"Download": function() {
downFromUrl();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
function downFromUrl() {
var link = $('#downFromUrlForm input[name=link]').val();
if (link != null && link != "")
$('#downFromUrlForm').submit();
}
function selectUnselect(cb) {
var rows = $('.ui_checked_columns');
for (i = 0; i < rows.length; i++) {
switch(cb.is(":checked")) {
case true:
selectRow(rows[i]);
break;
case false:
unselectRow(rows[i]);
break;
}
}
}
function rowClick(row) {
var input = row.getElementsByTagName('input')[0];
input.checked = !input.checked;
if (input.checked) {
row.className = row.className + ' checked';
}
else {
row.className = row.className.replace(' checked', '');
}
}
function selectRow(row) {
var input = row.getElementsByTagName('input')[0];
if(!input.checked) {
input.checked = true;
row.className = row.className + ' checked';
}
}
function unselectRow(row) {
var input = row.getElementsByTagName('input')[0];
if(input.checked) {
input.checked = false;
row.className = row.className.replace(' checked', '');
}
}
function checkSelected() {
var checkboxes = $('.ui_checkbox');
for(var i = 0; i < checkboxes.length; i++) {
if(checkboxes[i].checked) return true;
}
$( "#nothingSelected" ).dialog({
modal: true,
buttons: {
"OK": function() {
$( this ).dialog( "close" );
}
}
});
return false;
}
function searchDialog() {
$( "#searchDialog" ).dialog({
modal: true,
buttons: {
"Search": function() {
search();
},
"Cancel": function() {
$( this ).dialog( "close" );
}
}
});
}
function search() {
var query = $('#searchForm input[name=query]').val();
if (query != null && query != "")
$("#searchForm").submit();
}
/*
function checkSelected() {
var checkboxes = document.getElementsByClassName('ui_checkbox');
for(var i = 0; i < checkboxes.length; i++) {
if(checkboxes[i].checked) return true;
}
alert('Nothing selected');
return false;
}
*/

View File

@@ -0,0 +1,183 @@
/*
* plain.js
* Behavior of the File Manager for themes without Bootstrap, on top of
* the core widget script : row selection, the selection check before an
* action on the selected entries, sending the selection form to an
* action script with a dialog's fields as parameters, the rename and
* delete dialogs, the permissions calculator and the upload options.
* Everything is delegated from the document, keyed off data-filemin-*
* attributes, so no handlers are generated in the markup.
*/
(function () {
'use strict';
var widgets = window.webminUiWidgets;
// The selection form and its row checkboxes
function listForm() {
return document.getElementById('list_form');
}
function rowBoxes() {
var form = listForm();
return form ? Array.prototype.slice.call(
form.querySelectorAll('input[type="checkbox"][name="name"]')) : [];
}
function selectedBoxes() {
return rowBoxes().filter(function (box) { return box.checked; });
}
function selectRows(mode) {
rowBoxes().forEach(function (box) {
box.checked = mode === 'all' ? true :
mode === 'none' ? false : !box.checked;
});
}
// Called by the checkbox in the table heading
window.selectUnselect = function (master) {
selectRows(master.checked ? 'all' : 'none');
};
// Called by the rename link of a row, with the entry's name
window.renameDialog = function (file) {
widgets.openDialog('filemin_rename', { name: file, file: file });
};
// Send the selection form to a script, with parameters in its URL
function submitSelection(script, params) {
var form = listForm();
if (!form) return;
var url = script;
var query = params ? params.toString() : '';
if (query) url += (url.indexOf('?') >= 0 ? '&' : '?') + query;
form.setAttribute('action', url);
form.submit();
}
// The named, enabled fields of a dialog form as URL parameters,
// leaving out the calculator's own fields
function formParams(form) {
var params = new URLSearchParams();
Array.prototype.forEach.call(form.elements, function (field) {
if (!field.name || field.disabled ||
field.hasAttribute('data-filemin-skip')) return;
if (/^(?:file|submit|button|reset)$/.test(field.type)) return;
if ((field.type === 'checkbox' || field.type === 'radio') &&
!field.checked) return;
if (field.tagName === 'SELECT' && field.multiple) {
Array.prototype.forEach.call(field.options, function (option) {
if (option.selected) params.append(field.name, option.value);
});
return;
}
params.append(field.name, field.value);
});
return params;
}
// Toolbar entries : selecting rows, actions that need a selection,
// and actions that send the selection straight to a script. Caught
// before the widget script, so a dialog is not opened for an empty
// selection.
document.addEventListener('click', function (e) {
var control = e.target.closest && e.target.closest(
'[data-filemin-select], [data-filemin-selection], [data-filemin-submit]');
if (!control) return;
if (control.hasAttribute('data-filemin-select')) {
e.preventDefault();
selectRows(control.getAttribute('data-filemin-select'));
return;
}
if (control.hasAttribute('data-filemin-selection') && !selectedBoxes().length) {
e.preventDefault();
e.stopImmediatePropagation();
widgets.openDialog('filemin_nothing');
return;
}
var script = control.getAttribute('data-filemin-submit');
if (script) {
e.preventDefault();
submitSelection(script);
}
}, true);
document.addEventListener('submit', function (e) {
var form = e.target;
if (!form.getAttribute) return;
// A dialog acting on the selection : its fields become the
// parameters of the action script
var script = form.getAttribute('data-filemin-apply');
if (script) {
e.preventDefault();
var dialog = form.closest('dialog');
if (dialog && dialog.open) dialog.close();
submitSelection(script, formParams(form));
return;
}
// An upload : the options go in the URL, which the server reads
// before the files arrive, and progress is shown in a popup
var upload = form.getAttribute('data-filemin-upload');
if (upload) {
var action = upload;
form.querySelectorAll('[data-filemin-upload-option]').forEach(function (box) {
if (box.checked) {
action += '&' + box.getAttribute('data-filemin-upload-option') + '=1';
}
});
form.setAttribute('action', action);
var tracker = form.getAttribute('data-filemin-tracker');
if (tracker) {
window.open(tracker, 'uptracker',
'toolbar=no,menubar=no,scrollbars=no,location=no,resizable=no,' +
'width=' + window.screen.width + ',height=' + window.screen.height);
}
}
});
// Directory upload : the file input picks directories instead
document.addEventListener('change', function (e) {
var box = e.target;
if (box.matches && box.matches('[data-filemin-upload-dirs]')) {
var input = box.form && box.form.querySelector('input[type="file"]');
if (input) input.toggleAttribute('webkitdirectory', box.checked);
}
});
// The delete dialog lists the selected entries as it opens
document.addEventListener('ui-dialog-open', function (e) {
if (e.target.id !== 'filemin_delete') return;
var list = e.target.querySelector('#filemin_delete_items');
if (list) {
list.textContent = selectedBoxes().map(function (box) {
return box.value;
}).join('\n');
}
});
// Permissions : the octal field, the checkbox matrix and the hidden
// perms parameter stay in step through the shared calculator
function syncPerms(fromMatrix) {
var form = document.forms['chmod'];
if (!form || typeof window.calc_chmod !== 'function') return;
if (fromMatrix) {
window.calc_chmod();
}
else if (/^[0-7]{4}$/.test(form.t_total.value) &&
typeof window.octalchange === 'function') {
window.octalchange();
}
var perms = form.querySelector('input[name="perms"]');
if (perms) perms.value = form.t_total.value;
}
document.addEventListener('input', function (e) {
if (e.target.matches && e.target.matches('[data-filemin-chmod-octal]')) {
syncPerms(false);
}
});
document.addEventListener('change', function (e) {
if (e.target.matches && e.target.matches('[data-filemin-chmod]')) {
syncPerms(true);
}
});
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () { syncPerms(false); });
} else {
syncPerms(false);
}
})();

View File

@@ -1,246 +0,0 @@
<div id="nothingSelected" class="modal">
<h3>$text{'nothing_selected'}</h3>
</div>
<div id="createFolderDialog" class="modal">
<form id="createFolderForm" action="create_folder.cgi" method="post">
<div class="form-group">
<label>$text{'new_folder_name'}</label>
<input id="name" name="name" type="text" title="$text{'provide_folder_name'}">
<input type='hidden' name='path' value='$quote_escaped_path'>
</div>
</form>
</div>
<div id="createFileDialog" class="modal">
<form id="createFileForm" action="create_file.cgi" method="post">
<div class="form-group">
<label>$text{'new_file_name'}</label>
<input name="name" type="text">
<input type='hidden' name='path' value='$quote_escaped_path'>
</div>
</form>
</div>
<div id="renameDialog" class="modal">
<form id="renameForm" action="rename.cgi" method="post">
<div class="form-group">
<label>$text{'new_name'}</label>
<input name="name" type="text">
<input type='hidden' name='file' value='' required>
<input type='hidden' name='path' value='$quote_escaped_path'>
</div>
</form>
</div>
<div id="compressDialog" class="modal">
<form id="compressSelectedForm" action="compress.cgi" method="post">
<div class="form-group">
<label>$text{'archive_name'}</label>
<input name="filename" type="text">
<select name="method">
<option value="plain-tar">.tar</option>
<option value="xz-tar">.tar.xz</option>
<option selected value="tar">.tar.gz</option>
<option value="zstd-tar">.zstd</option>
<option value="zip">.zip</option>
</select>
</div>
</form>
</div>
<div id="removeDialog" class="modal">
<p>$text{'about_to_delete'}</p>
<pre id="items-to-remove">
</pre>
$text{'are_you_sure'}
</div>
<div id="chownDialog" class="modal">
<form id="chownForm">
<div class="form-group">
<label>$text{'user_name'}</label><br>
<input name="owner" type="text"><br>
<label>$text{'group_name'}</label><br>
<input name="group" type="text"><br><br>
<label>$text{'dialog_recursive'}</label>
<input id="chown-recursive" type="checkbox" name="recursive">
</div>
</form>
</div>
<div id="chattrDialog" class="modal">
<form id="chattrForm">
<div class="form-group">
<label>$text{'attr_label'}</label><br>
<input name="label" type="text"><br><br>
<label>$text{'dialog_recursive'}</label>
<input id="chattr-recursive" type="checkbox" name="recursive">
</div>
</form>
</div>
<div id="chconDialog" class="modal">
<form id="chconForm">
<div class="form-group">
<label>$text{'context_label'}</label><br>
<input name="label" type="text"><br><br>
<label>$text{'dialog_recursive'}</label>
<input id="chcon-recursive" type="checkbox" name="recursive">
</div>
</form>
</div>
<div id="aclsDialog" class="modal">
<form id="aclsForm">
<div class="form-group">
<label>$text{'acls_action'}</label><br>
<select name="action">
<option value=""></option>
<option selected value="-m">Modify (-m)</option>
<option value="-x">Remove (-x)</option>
<option value="-b">Remove all extended (-b)</option>
<option value="-k">Remove default (-k)</option>
</select><br><br>
<label>$text{'acls_applyto'}</label><br>
<select multiple size=4 name="apply_to">
<option value="u">$text{'acls_user'}</option>
<option value="g">$text{'acls_group'}</option>
<option value="m">$text{'acls_mask'}</option>
<option value="o">$text{'acls_other'}</option>
</select><br><br>
<label>$text{'acls_perms'}</label><br>
<select name="perms">
<option value=""></option>
<option value="---">---</option>
<option value="r--">r--</option>
<option value="-w-">-w-</option>
<option value="--x">--x</option>
<option value="r-x">r-x</option>
<option value="rw-">rw-</option>
<option value="-wx">-wx</option>
<option value="rwx">rwx</option>
</select><br><br>
<label>$text{'acls_user'}</label><br>
$acl_user_select<br><br>
<label>$text{'acls_group'}</label><br>
$acl_group_select<br><br>
<label>$text{'dialog_recursive'}</label>
<input id="acls-recursive" type="checkbox" name="recursive">
<br><br>
$acl_manual
</div>
</form>
</div>
<div id="downFromUrlDialog" class="modal">
<form id="downFromUrlForm" action="http_download.cgi" method="post">
<div class="form-group">
<label style="display: block;">$text{'dialog_file_url'} <sup class="text-danger">&nbsp;*</sup><br>
<input name="link" type="url" data-placement="top" data-content="$text{'provide_url'}" data-trigger="manual" autocomplete="url" style="font-weight: normal;">
</label>
<label style="display: block;">$text{'dialog_user_name'}<br>
<input name="username" type="text" autocomplete="off" style="font-weight: normal;">
</label>
<label style="display: block;">$text{'dialog_user_pass'}<br>
<input name="password" type="password" autocomplete="new-password" style="font-weight: normal;">
</label>
<input type='hidden' name='path' value='$quote_escaped_path'>
</div>
</form>
</div>
<div id="readyForUploadDialog" class="modal">
<form id='upload-form' method='post' onsubmit="this.extract_upload.checked=false" action='upload.cgi?path=$urlized_path&id=$upid' enctype='multipart/form-data' onsubmit='window.open("@{[&get_webprefix()]}/uptracker.cgi?id=$upid&uid=$uid", "uptracker", "toolbar=no, menubar=no, scrollbars=no, location=no, resizable=no, width=screen.width, height=screen.height")'>
<input type='file' id='upfiles' name='upfiles' multiple onchange='countUploads(this)'>
<input type='hidden' name='path' value='$quote_escaped_path'>
<br>
<br>
<input type="checkbox" id="filetype" onchange="this.parentElement.querySelector('#upfiles').toggleAttribute('webkitdirectory')"><label for="filetype"> $text{'upload_dirs'}</label>
<br>
<input type="checkbox" id="extract_upload" onchange="t=document.querySelector('#upload-form'),v=t.getAttribute('action'),x='&extract_uploaded=1',this.checked?t.setAttribute('action', t.getAttribute('action') + x):t.setAttribute('action',t.getAttribute('action').replace(x,''))"><label for="extract_upload"> $text{'extract_uploaded'}</label>
<br>
<input type="checkbox" id="overwrite_existing" onchange="t=document.querySelector('#upload-form'),v=t.getAttribute('action'),x='&overwrite_existing=1',this.checked?t.setAttribute('action', t.getAttribute('action') + x):t.setAttribute('action',t.getAttribute('action').replace(x,''))"><label for="overwrite_existing"> $text{'overwrite_existing'}</label>
</form>
<div id="readyForUploadList" class="well" style="max-height: 180px;">
</div>
</div>
<div id="chmodDialog" class="modal fade">
<form name="chmod" id="chmodForm">
<div class="form-group">
<label for="t_total">$text{'permissions'}:&nbsp; </label><br>
<input id="perms" type="text" oninput="if(this.value.length>4)this.value=this.value.slice(0,4)" name="t_total" value="0644" size="4" onKeyUp="octalchange()" class="form-group-item">
<input type="text" name="sym_total" value="" size="12" readonly="true" class="form-group-item">
</div>
<table class="table">
<tr>
<th></th>
<th><b>$text{'chmod_owner'}</b></th>
<th><b>$text{'chmod_group'}</b></th>
<th><b>$text{'chmod_others'}<b></th>
</tr>
<tr>
<td>$text{'chmod_read'}</td>
<td><input type="checkbox" name="owner4" value="4" onchange="calc_chmod()"></td>
<td><input type="checkbox" name="group4" value="4" onchange="calc_chmod()"></td>
<td><input type="checkbox" name="other4" value="4" onchange="calc_chmod()"></td>
</tr>
<tr>
<td>$text{'chmod_write'}</td>
<td><input type="checkbox" name="owner2" value="2" onchange="calc_chmod()"></td>
<td><input type="checkbox" name="group2" value="2" onchange="calc_chmod()"></td>
<td><input type="checkbox" name="other2" value="2" onchange="calc_chmod()"></td>
</tr>
<tr>
<td>$text{'chmod_execute'}</td>
<td><input type="checkbox" name="owner1" value="1" onchange="calc_chmod()"></td>
<td><input type="checkbox" name="group1" value="1" onchange="calc_chmod()"></td>
<td><input type="checkbox" name="other1" value="1" onchange="calc_chmod()"></td>
</tr>
<tr>
<td>----------</td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>$text{'chmod_sticky'}</td>
<td><input type="checkbox" name="sticky" value="1" onchange="calc_chmod()"></td>
<td></td>
<td></td>
</tr>
<tr>
<td>$text{'chmod_setgid'}</td>
<td><input type="checkbox" name="setgid" value="2" onchange="calc_chmod()"></td>
<td></td>
<td></td>
</tr>
</table>
<label>$text{'chmod_apply_to'}:&nbsp; </label>
<select name="applyto">
<option value="1">$text{'chmod_dir_only'}</option>
<option value="2">$text{'chmod_dir_and_its_files'}</option>
<option value="3">$text{'chmod_all'}</option>
<option value="4">$text{'chmod_dir_files_and_subs_files'}</option>
<option value="5">$text{'chmod_dir_and_subs'}</option>
</select>
</form>
</div>
<div id="searchDialog" class="modal">
<form id="searchForm" action="search.cgi" method="post">
<div class="form-group">
<label>$text{'search_label'}</label>
<input id="query" name="query" type="text" title="$text{'provide_search_query'}"><br><br>
<input id="caseins" name="caseins" type="checkbox" checked="checked"> $text{'search_insensitive'}
<input type='hidden' name='path' value='$quote_escaped_path'>
</div>
</form>
</div>

View File

@@ -1,29 +0,0 @@
<div id="quicks" style="float:right">
<a href="javascript:void(0)" onclick='searchDialog()' title="$text{'search'}"><img src='images/icons/quick/edit-find.png' alt="$text{'search'}" /></a>
<a href="bookmark.cgi?path=$urlized_path" title="$text{'bookmark_folder'}"><img src='images/icons/quick/bookmark-new.png' alt="$text{'bookmark_folder'}" /></a>
<a tabindex="0" class="fg-button fg-button-icon-right ui-widget ui-state-default ui-corner-all" id="flat">
<img src="images/icons/quick/go-down.png" alt="$text{'config_bookmarks'}">
</a>
<div id="bookmarks" class="ui-helper-hidden">
<ul>
$bookmarks
</ul>
</div>
<a href="javascript:void(0)" onclick='selectAll()' title="$text{'select_all'}"><img src='images/icons/quick/select-all.png' alt="$text{'select_all'}" /></a>
<a href="javascript:void(0)" onclick='invertSelection()' title="$text{'invert_selection'}"><img src='images/icons/quick/invert.png' alt="$text{'invert_selection'}" /></a>
<a href="javascript:void(0)" onclick='copySelected()' title="$text{'copy_selected'}"><img src='images/icons/quick/edit-copy.png' alt="$text{'copy_selected'}" /></a>
<a href="javascript:void(0)" onclick='cutSelected()' title="$text{'cut_selected'}"><img src='images/icons/quick/edit-cut.png' alt="$text{'cut_selected'}" /></a>
<a href='paste.cgi?path=$urlized_path' title="$text{'paste'}"><img src='images/icons/quick/edit-paste.png' alt="$text{'paste'}" /></a>
<a href="javascript:void(0)" onclick='createFolderDialog()' title="$text{'create_folder'}"><img src='images/icons/quick/folder-new.png' alt="$text{'create_folder'}" /></a>
<a href="javascript:void(0)" onclick='createFileDialog()' title="$text{'create_file'}"><img src='images/icons/quick/document-new.png' alt="$text{'create_file'}" /></a>
<a href="javascript:void(0)" onclick='compressDialog()' title="$text{'compress_selected'}"><img src='images/icons/quick/compress.png' alt="$text{'compress_selected'}" /></a>
<a href="javascript:void(0)" onclick='chmodDialog()' title="$text{'chmod_selected'}"><img src='images/icons/quick/chmod.png' alt="$text{'chmod_selected'}" /></a>
<a href="javascript:void(0)" onclick='chownDialog()' title="$text{'chown_selected'}"><img src='images/icons/quick/chown.png' alt="$text{'chown_selected'}" /></a>
<a href="javascript:void(0)" onclick='chattrDialog()' title="$text{'chattr_selected'}"><img src='images/icons/quick/chattr.png' alt="$text{'chattr_selected'}" /></a>
<a href="javascript:void(0)" onclick='chconDialog()' title="$text{'chcon_selected'}"><img src='images/icons/quick/chcon.png' alt="$text{'chcon_selected'}" /></a>
<a href="javascript:void(0)" onclick='aclsDialog()' title="$text{'acls_selected'}"><img src='images/icons/quick/acls.png' alt="$text{'acls_selected'}" /></a>
<a href="javascript:void(0)" onclick='removeSelected()' title="$text{'remove_selected'}"><img src='images/icons/quick/remove.png' alt="$text{'remove_selected'}" /></a>
<a href="javascript:void(0)" onclick='viewReadyForUpload()' title="$text{'upload_files'}"><img src='images/icons/quick/upload.png' alt="$text{'upload_files'}" /></a>
<a href="javascript:void(0)" onclick='downFromUrlDialog()' title="$text{'get_from_url'}"><img src='images/icons/quick/from-url.png' alt="$text{'get_from_url'}" /></a>
</div>
<div id="info" style="float:right"></div>

View File

@@ -1,67 +0,0 @@
<div class="btn-group pull-right" role="group">
<button class="btn btn-inverse" onclick='searchDialog()' title="$text{'search'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-search" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick="location.href='bookmark.cgi?path=$urlized_path'" title="$text{'bookmark_folder'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-bookmark-o" aria-hidden="true"></i>
</button>
<div class="btn-group">
<button type="button" class="btn btn-inverse dropdown-toggle" data-toggle="dropdown" data-hover="dropdown" title="$text{'config_bookmarks'}">
<i class="fa fa-book" aria-hidden="true"></i>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
$bookmarks
</ul>
</div>
<button class="btn btn-inverse" onclick='window.location.href="index.cgi?path=$urlized_path"' title="$text{'refresh'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-refresh" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='selectAll()' title="$text{'select_all'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-check-square-o" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='invertSelection()' title="$text{'invert_selection'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-check-square" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='copySelected()' title="$text{'copy_selected'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-copy" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='cutSelected()' title="$text{'cut_selected'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-cut" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick="location.href='paste.cgi?path=$urlized_path'" title="$text{'paste'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-paste" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='createFolderDialog()' title="$text{'create_folder'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-folder-o" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='createFileDialog()' title="$text{'create_file'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-file-o" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='compressDialog()' title="$text{'compress_selected'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-file-archive-o" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='chmodDialog()' title="$text{'chmod_selected'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-gears" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='chownDialog()' title="$text{'chown_selected'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-users" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='chattrDialog()' title="$text{'chattr_selected'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-tags" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='chconDialog()' title="$text{'chcon_selected'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-shield" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='aclsDialog()' title="$text{'acls_selected'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-account-key" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='removeDialog()' title="$text{'remove_selected'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-trash-o" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='viewReadyForUpload()' title="$text{'upload_files'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-upload" aria-hidden="true"></i>
</button>
<button class="btn btn-inverse" onclick='downFromUrlDialog()' title="$text{'get_from_url'}" data-toggle="tooltip" data-placement="bottom" data-container="body">
<i class="fa fa-globe" aria-hidden="true"></i>
</button>
</div>