diff --git a/htdocs/js/FileManager/filemanager.js b/htdocs/js/FileManager/filemanager.js index e7e72552bc..40d17ddc40 100644 --- a/htdocs/js/FileManager/filemanager.js +++ b/htdocs/js/FileManager/filemanager.js @@ -43,19 +43,17 @@ }; // Used for the archive subpage to highlight all in the Select - const selectAllButton = document.getElementById('select-all-files-button'); - selectAllButton?.addEventListener('click', () => { - const n = document.getElementById('archive-files').options.length; - for (const opt of document.getElementById('archive-files').options) { - opt.selected = 'selected'; + document.getElementById('select-all-files-button')?.addEventListener('click', () => { + for (const option of document.getElementById('archive-files').options) { + option.selected = 'selected'; } }); - - for (const r of document.querySelectorAll('input[name="archive_type"]')) { - r.addEventListener('click', () => { - const suffix = document.querySelector('input[name="archive_type"]:checked').value; - document.getElementById('filename_suffix').innerText = '.' + suffix; + for (const archiveTypeInput of document.querySelectorAll('input[name="archive_type"]')) { + archiveTypeInput.addEventListener('click', () => { + document.getElementById('filename_suffix').innerText = `.${ + document.querySelector('input[name="archive_type"]:checked').value + }`; }); } diff --git a/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm b/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm index 0638353c9a..89da3a41d4 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/FileManager.pm @@ -27,8 +27,8 @@ use File::Copy; use File::Spec; use String::ShellQuote; use Archive::Extract; -use IO::Compress::Zip qw(zip $ZipError); use Archive::Tar; +use Archive::Zip qw(:ERROR_CODES); use WeBWorK::Utils qw(readDirectory readFile sortByName listFilesRecursive); use WeBWorK::Upload; @@ -356,14 +356,18 @@ sub MakeArchive ($c) { } my $dir = "$c->{courseRoot}/$c->{pwd}"; + if ($c->param('confirmed')) { - chdir($dir); - # remove any directories - my @files_to_compress = grep { -f $_ } @files; + my $action = $c->param('action') || 'Cancel'; + return $c->Refresh if $action eq 'Cancel' || $action eq $c->maketext('Cancel'); - unless ($c->param('archive_filename') && scalar(@files_to_compress) > 0) { - $c->addbadmessage($c->maketext('The filename cannot be empty.')) unless $c->param('archive_filename'); - $c->addbadmessage($c->maketext('At least one file must be selected')) unless scalar(@files_to_compress) > 0; + unless ($c->param('archive_filename')) { + $c->addbadmessage($c->maketext('The filename cannot be empty.')); + return $c->include('ContentGenerator/Instructor/FileManager/archive', dir => $dir, files => \@files); + } + + unless (@files > 0) { + $c->addbadmessage($c->maketext('At least one file must be selected')); return $c->include('ContentGenerator/Instructor/FileManager/archive', dir => $dir, files => \@files); } @@ -371,18 +375,40 @@ sub MakeArchive ($c) { my ($error, $ok); if ($c->param('archive_type') eq 'zip') { $archive .= '.zip'; - $ok = zip \@files_to_compress => $archive; - $error = $ZipError unless $ok; + my $zip = Archive::Zip->new(); + $zip->storeSymbolicLink(1); + for (@files) { + my $fullFile = "$dir/$_"; + + # Skip symbolic links for now. As of yet, I have not found a perl module that can add symbolic links to + # zip files correctly. Archive::Zip should be able to do this, but has permissions issues doing so. + next if -l $fullFile; + + if (-d $fullFile) { + $zip->addDirectory($fullFile => $_); + } else { + $zip->addFile($fullFile => $_); + } + } + $ok = $zip->writeToFileNamed("$dir/$archive") == AZ_OK; + # FIXME: This should check the error code, and give a more specific error message. + $error = 'Unable to create zip archive.' unless $ok; } else { $archive .= '.tgz'; - $ok = Archive::Tar->create_archive($archive, COMPRESS_GZIP, @files_to_compress); - $error = $Archive::Tar::error unless $ok; + my $tar = Archive::Tar->new; + $tar->add_files(map {"$dir/$_"} @files); + # Make file names in the archive relative to the current working directory. + for ($tar->get_files) { + $tar->rename($_->full_path, $_->full_path =~ s!^$dir/!!r); + } + $ok = $tar->write("$dir/$archive", COMPRESS_GZIP); + $error = $tar->error unless $ok; } if ($ok) { - my $n = scalar(@files); - $c->addgoodmessage($c->maketext('Archive "[_1]" created successfully ([quant,_2,file])', $archive, $n)); + $c->addgoodmessage( + $c->maketext('Archive "[_1]" created successfully ([quant,_2,file])', $archive, scalar(@files))); } else { - $c->addbadmessage($c->maketext(q{Can't create archive "[_1]": command returned [_2]}, $archive, $error)); + $c->addbadmessage($c->maketext(q{Can't create archive "[_1]": [_2]}, $archive, $error)); } return $c->Refresh; } else { diff --git a/templates/ContentGenerator/Instructor/FileManager/archive.html.ep b/templates/ContentGenerator/Instructor/FileManager/archive.html.ep index f70061c8ed..0e1b0d3bcc 100644 --- a/templates/ContentGenerator/Instructor/FileManager/archive.html.ep +++ b/templates/ContentGenerator/Instructor/FileManager/archive.html.ep @@ -1,12 +1,13 @@ -% # template for the archive subpage. -
<%= maketext('Create the archive of the select files?') %>