diff --git a/htdocs/themes/math4/math4.scss b/htdocs/themes/math4/math4.scss
index f0fee9b40f..87494e47b8 100644
--- a/htdocs/themes/math4/math4.scss
+++ b/htdocs/themes/math4/math4.scss
@@ -456,7 +456,7 @@ h2.page-title {
gap: 0.25rem;
margin: 0 0 0.5rem;
- p {
+ div {
margin: 0;
}
}
diff --git a/lib/WeBWorK/ContentGenerator.pm b/lib/WeBWorK/ContentGenerator.pm
index 9c0d7dad01..992597c747 100644
--- a/lib/WeBWorK/ContentGenerator.pm
+++ b/lib/WeBWorK/ContentGenerator.pm
@@ -265,8 +265,9 @@ message() template escape handler.
sub addgoodmessage ($c, $message) {
$c->addmessage($c->tag(
- 'p',
+ 'div',
class => 'alert alert-success alert-dismissible fade show ps-1 py-1',
+ role => 'alert',
$c->c(
$message,
$c->tag(
@@ -290,8 +291,9 @@ message() template escape handler.
sub addbadmessage ($c, $message) {
$c->addmessage($c->tag(
- 'p',
+ 'div',
class => 'alert alert-danger alert-dismissible fade show ps-1 py-1',
+ role => 'alert',
$c->c(
$message,
$c->tag(
diff --git a/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm b/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm
index 47e93ca25f..83b5ccaa1c 100644
--- a/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm
+++ b/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm
@@ -31,7 +31,7 @@ use Archive::Tar;
use Archive::Zip qw(:ERROR_CODES);
use Archive::Zip::SimpleZip qw($SimpleZipError);
-use WeBWorK::Utils qw(readDirectory readFile sortByName listFilesRecursive);
+use WeBWorK::Utils qw(readDirectory readFile sortByName listFilesRecursive min);
use WeBWorK::Upload;
use WeBWorK::Utils::CourseManagement qw(archiveCourse);
@@ -449,8 +449,7 @@ sub UnpackArchive ($c) {
sub unpack_archive ($c, $archive) {
my $dir = Mojo::File->new($c->{courseRoot}, $c->{pwd});
- # Used for determining non-existing and existing files.
- my (@members, @existing_files);
+ my (@members, @existing_files, @outside_files);
my $num_extracted = 0;
if ($archive =~ m/\.zip$/) {
@@ -465,8 +464,7 @@ sub unpack_archive ($c, $archive) {
$c->addbadmessage($error);
});
- my @members = $zip->members;
- my @outside_files;
+ @members = $zip->members;
for (@members) {
my $out_file = $dir->child($_->fileName)->realpath;
if ($out_file !~ /^$dir/) {
@@ -481,26 +479,7 @@ sub unpack_archive ($c, $archive) {
++$num_extracted if $zip->extractMember($_ => $out_file->to_string) == AZ_OK;
}
- if (@outside_files) {
- if (scalar(@outside_files) == 1) {
- $c->addbadmessage($c->maketext(
- 'The file "[_1]" can not be safely unpacked as it is outside the current working directory.',
- $outside_files[0]
- ));
- } else {
- $c->addbadmessage($c->maketext(
- 'There are [_1] files that already exist including [_2] and [_3]. '
- . 'Check "Overwrite existing files silently" to unpack these files',
- scalar(@outside_files),
- $outside_files[0],
- $outside_files[1]
- ));
- }
- }
-
Archive::Zip::setErrorHandler();
-
- $c->addgoodmessage($c->maketext('[quant,_1,file] unpacked successfully', $num_extracted)) if $num_extracted;
} elsif ($archive =~ m/\.(tar(\.gz)?|tgz)$/) {
local $Archive::Tar::WARN = 0;
@@ -512,43 +491,87 @@ sub unpack_archive ($c, $archive) {
$tar->setcwd($dir->to_string);
- my @members = $tar->list_files;
+ @members = $tar->list_files;
for (@members) {
+ my $out_file = $dir->child($_)->realpath;
+ if ($out_file !~ /^$dir/) {
+ push(@outside_files, $_);
+ next;
+ }
+
if (!$c->param('overwrite') && -e $dir->child($_)) {
push(@existing_files, $_);
next;
}
+
unless ($tar->extract_file($_)) {
$c->addbadmessage($tar->error);
next;
}
++$num_extracted;
}
-
- $c->addgoodmessage($c->maketext('[quant,_1,file] unpacked successfully', $num_extracted)) if $num_extracted;
} else {
$c->addbadmessage($c->maketext('Unsupported archive type in file "[_1]"', $archive));
return 0;
}
- if (@existing_files) {
- if (scalar(@existing_files) == 1) {
- $c->addbadmessage($c->maketext(
- 'The file "[_1]" already exists. '
- . 'Check "Overwrite existing files silently" to unpack this file.',
- $existing_files[0]
- ));
- } else {
- $c->addbadmessage($c->maketext(
- 'There are [_1] files that already exist including [_2] and [_3]. '
- . 'Check "Overwrite existing files silently" to unpack these files',
- scalar(@existing_files),
- $existing_files[0],
- $existing_files[1]
- ));
- }
+ if (@outside_files) {
+ $c->addbadmessage(
+ $c->tag(
+ 'p',
+ $c->maketext(
+ 'The following [plural,_1,file is,files are] outside the current working directory '
+ . 'and can not be safely unpacked.',
+ scalar(@outside_files),
+ )
+ )
+ . $c->tag(
+ 'div',
+ $c->tag(
+ 'ul',
+ $c->c(
+ (map { $c->tag('li', $_) } @outside_files[ 0 .. min(29, $#outside_files) ]),
+ (
+ @outside_files > 30
+ ? $c->tag('li',
+ $c->maketext('[quant,_1,more file,more files] not shown', @outside_files - 30))
+ : ()
+ )
+ )->join('')
+ )
+ )
+ );
}
+ if (@existing_files) {
+ $c->addbadmessage(
+ $c->tag(
+ 'p',
+ $c->maketext(
+ 'The following [plural,_1,file already exists,files already exist]. '
+ . 'Check "Overwrite existing files silently" to unpack [plural,_1,this file,these files].',
+ scalar(@existing_files),
+ )
+ )
+ . $c->tag(
+ 'div',
+ $c->tag(
+ 'ul',
+ $c->c(
+ (map { $c->tag('li', $_) } @existing_files[ 0 .. min(29, $#existing_files) ]),
+ (
+ @existing_files > 30
+ ? $c->tag('li',
+ $c->maketext('[quant,_1,more file,more files] not shown', @existing_files - 30))
+ : ()
+ )
+ )->join('')
+ )
+ )
+ );
+ }
+
+ $c->addgoodmessage($c->maketext('[quant,_1,file] unpacked successfully', $num_extracted)) if $num_extracted;
return $num_extracted == @members;
}
@@ -630,9 +653,8 @@ sub Upload ($c) {
$c->Confirm(
$c->tag(
'p',
- $c->b(
- $c->maketext('File [_1] already exists. Overwrite it, or rename it as:', $name)
- )
+ $c->b($c->maketext(
+ 'File [_1] already exists. Overwrite it, or rename it as:', $name))
),
uniqueName($dir, $name),
$c->maketext('Rename'),
@@ -768,9 +790,8 @@ sub directoryListing ($c, $pwd) {
for my $name (@values) {
my $file = "$dir/$name->[1]";
my ($size, $date) = (lstat($file))[ 7, 9 ];
- $name->[0] =
- $c->b(
- sprintf("%-${len}s%-16s%10s", $name->[0], -d $file ? ('', '') : (getDate($date), getSize($size)))
+ $name->[0] = $c->b(
+ sprintf("%-${len}s%-16s%10s", $name->[0], -d $file ? ('', '') : (getDate($date), getSize($size)))
=~ s/\s/ /gr);
}
}
diff --git a/lib/WeBWorK/Utils.pm b/lib/WeBWorK/Utils.pm
index 8a799b884b..5f9edbfaf3 100644
--- a/lib/WeBWorK/Utils.pm
+++ b/lib/WeBWorK/Utils.pm
@@ -81,6 +81,7 @@ our @EXPORT_OK = qw(
list2hash
listFilesRecursive
makeTempDirectory
+ min
max
nfreeze_base64
not_blank
@@ -1049,15 +1050,22 @@ sub thaw_base64 {
}
-sub max(@) {
- my $soFar;
- foreach my $item (@_) {
- $soFar = $item unless defined $soFar;
- if ($item > $soFar) {
- $soFar = $item;
- }
+sub min {
+ my @items = @_;
+ my $min = (shift @items) // 0;
+ for my $item (@items) {
+ $min = $item if ($item < $min);
+ }
+ return $min;
+}
+
+sub max {
+ my @items = @_;
+ my $max = (shift @items) // 0;
+ for my $item (@items) {
+ $max = $item if ($item > $max);
}
- return defined $soFar ? $soFar : 0;
+ return $max;
}
sub wwRound(@) {