From 6f6aa10c5568b0179a56558be56809e7182fecb7 Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Wed, 5 Aug 2026 17:36:51 -0500 Subject: [PATCH 1/2] Show both the course id and the archive file when listing course archives in the admin course. The previous setup did not take into account what would happen if multiple archive files contained the same course id. So instead of the `listArchivedCourse` method returning a hash of the form ```perl { myTestCourse => { filename => 'myTestCourse.tar.gz', size => '605 KB' } } ``` it now returns a hash of the form ```perl { 'myTestCourse.tar.gz' => { courseID => 'myTestCourse', size => '605 KB', lastModified => 1778667472 } } ``` Note that the `lastModified` key is only included because that is what is saved in the cache file, and that is just directly returned if the archive file has not been modified. Although, at this point it is not used elsewhere. Both the courseID and the filename are then displayed. The format displayed is `myTestCourse (myTestCourse.tar.gz, 605 KB)`. If there is also an archive file `myTestCourseAlt.tar.gz` then it will also be listed as something like`myTestCourse (myTestCourseAlt.tar.gz, 1.6 MB)`. --- lib/WeBWorK/Utils/CourseManagement.pm | 41 +++++++------------ .../ContentGenerator/CourseAdmin.html.ep | 2 +- .../CourseAdmin/unarchive_course_form.html.ep | 7 ++-- 3 files changed, 20 insertions(+), 30 deletions(-) diff --git a/lib/WeBWorK/Utils/CourseManagement.pm b/lib/WeBWorK/Utils/CourseManagement.pm index 514d086132..4563b0514a 100644 --- a/lib/WeBWorK/Utils/CourseManagement.pm +++ b/lib/WeBWorK/Utils/CourseManagement.pm @@ -111,14 +111,15 @@ sub listCourses { =item listArchivedCourses($ce) Lists the courses which have been archived (end in .tar.gz). The courses found -are returned as a hash whose keys are the course ids and the values are -references to hashes containing the C (the basename of the file -including the .tar.gz extension) and file C. For example, +are returned as a hash whose keys are the filenames (the basename of the file +including the .tar.gz extension) and the values are references to hashes +containing the C, file C, and C time. For example, { - myTestCourse => { - filename => 'myTestCourse.tar.gz', - size => '605 KB' + 'myTestCourse.tar.gz' => { + courseID => 'myTestCourse', + size => '605 KB', + lastModified => 1778667472 } } @@ -134,23 +135,19 @@ sub listArchivedCourses { my $archiveDataFile = $archivesDir->child('archive-cache.json'); my $archiveData = eval { decode_json($archiveDataFile->slurp) } || {}; - my $archiveDataUpdated = 0; + my %updatedArchiveData; my %return; for (@$archives) { my $basename = $_->basename; my $lastModified = $_->stat->mtime; if ($archiveData->{$basename} && $archiveData->{$basename}{lastModified} >= $lastModified) { - $return{ $archiveData->{$basename}{courseID} } = { - filename => $basename, - size => $archiveData->{$basename}{size} - } - if defined $archiveData->{$basename}{courseID}; + $updatedArchiveData{$basename} = $archiveData->{$basename}; + $return{$basename} = $updatedArchiveData{$basename} if defined $archiveData->{$basename}{courseID}; next; } - $archiveDataUpdated = 1; - $archiveData->{$basename} = { lastModified => $lastModified }; + $updatedArchiveData{$basename} = { lastModified => $lastModified }; my $archive = Archive::Tar->new($_); my %top_level; @@ -164,20 +161,12 @@ sub listArchivedCourses { } my ($currCourseID) = keys %top_level; - $archiveData->{$basename}{courseID} = $currCourseID; - $archiveData->{$basename}{size} = getHumanReadableFileSize($_); - $return{$currCourseID} = { filename => $basename, size => $archiveData->{$basename}{size} }; - } - - my %archives = map { $_->basename => 1 } @$archives; - for (keys %$archiveData) { - unless ($archives{$_}) { - delete $archiveData->{$_}; - $archiveDataUpdated = 1; - } + $updatedArchiveData{$basename}{courseID} = $currCourseID; + $updatedArchiveData{$basename}{size} = getHumanReadableFileSize($_); + $return{$basename} = $updatedArchiveData{$basename}; } - $archiveDataFile->spew(encode_json($archiveData)) if $archiveDataUpdated; + $archiveDataFile->spew(encode_json(\%updatedArchiveData)); return %return; } diff --git a/templates/ContentGenerator/CourseAdmin.html.ep b/templates/ContentGenerator/CourseAdmin.html.ep index 4b56860c53..88dfdf7d8a 100644 --- a/templates/ContentGenerator/CourseAdmin.html.ep +++ b/templates/ContentGenerator/CourseAdmin.html.ep @@ -65,7 +65,7 @@
    % my %courseArchives = listArchivedCourses($ce); % for (sort { lc($a) cmp lc($b) } keys %courseArchives) { -
  1. <%= "$_ ($courseArchives{$_}{size})" %>
  2. +
  3. <%= "$courseArchives{$_}{courseID} ($_, $courseArchives{$_}{size})" %>
  4. % }
% } diff --git a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep index b42250fbd1..1746d22985 100644 --- a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep @@ -4,9 +4,9 @@ % % # Find courses which have been archived. % my %courseArchives = listArchivedCourses($ce); -% my @courseIDs = sort { lc($a) cmp lc($b) } keys %courseArchives; +% my @archiveFiles = sort { lc($a) cmp lc($b) } keys %courseArchives; % -% if (@courseIDs) { +% if (@archiveFiles) { <%= form_for current_route, method => 'POST', begin =%> <%= $c->hidden_authen_fields =%> <%= $c->hidden_fields('subDisplay') =%> @@ -21,7 +21,8 @@
<%= select_field unarchive_courseID => - [ map { [ "$_ ($courseArchives{$_}{size})" => $courseArchives{$_}{filename} ] } @courseIDs ], + [ map { [ "$courseArchives{$_}{courseID} ($_, $courseArchives{$_}{size})" => $_ ] } + @archiveFiles ], id => 'unarchive_courseID', class => 'form-select', size => 10 =%> From d3c04e3e1451d5cead23db1cba82f6f5c040db33 Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Wed, 5 Aug 2026 20:31:17 -0500 Subject: [PATCH 2/2] Only show the filename if it without the extension it differs from the courseID. --- templates/ContentGenerator/CourseAdmin.html.ep | 6 +++++- .../CourseAdmin/unarchive_course_form.html.ep | 8 +++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/templates/ContentGenerator/CourseAdmin.html.ep b/templates/ContentGenerator/CourseAdmin.html.ep index 88dfdf7d8a..343ae27e2d 100644 --- a/templates/ContentGenerator/CourseAdmin.html.ep +++ b/templates/ContentGenerator/CourseAdmin.html.ep @@ -65,7 +65,11 @@
    % my %courseArchives = listArchivedCourses($ce); % for (sort { lc($a) cmp lc($b) } keys %courseArchives) { -
  1. <%= "$courseArchives{$_}{courseID} ($_, $courseArchives{$_}{size})" %>
  2. +
  3. + <%= "$courseArchives{$_}{courseID} (" + . ($courseArchives{$_}{courseID} eq ($_ =~ s/\.tar\.gz$//ir) ? '' : "$_, ") + . "$courseArchives{$_}{size})" %> +
  4. % }
% } diff --git a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep index 1746d22985..6405592f57 100644 --- a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep @@ -20,9 +20,11 @@ class => 'col-md-2 pe-0 col-form-label fw-bold' =%>
<%= select_field - unarchive_courseID => - [ map { [ "$courseArchives{$_}{courseID} ($_, $courseArchives{$_}{size})" => $_ ] } - @archiveFiles ], + unarchive_courseID => [ + map { [ "$courseArchives{$_}{courseID} (" + . ($courseArchives{$_}{courseID} eq ($_ =~ s/\.tar\.gz$//ir) ? '' : "$_, ") + . "$courseArchives{$_}{size})" => $_ ] } @archiveFiles + ], id => 'unarchive_courseID', class => 'form-select', size => 10 =%>