From c2872167bd30863b96248f85ad3a9ca7c484393e 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 | 10 ++--- 3 files changed, 21 insertions(+), 32 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 793a193ac0..9b0c224115 100644 --- a/templates/ContentGenerator/CourseAdmin.html.ep +++ b/templates/ContentGenerator/CourseAdmin.html.ep @@ -67,7 +67,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 d1e8082fb2..d2b47162c4 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') =%> @@ -19,9 +19,9 @@ class => 'col-md-2 pe-0 col-form-label fw-bold' =%>
<%= select_field - unarchive_courseID => [ - map { [ "$_ ($courseArchives{$_}{size})" => $courseArchives{$_}{filename} ] } @courseIDs - ], + unarchive_courseID => + [ map { [ "$courseArchives{$_}{courseID} ($_, $courseArchives{$_}{size})" => $_ ] } + @archiveFiles ], id => 'unarchive_courseID', class => 'form-select', size => 10 From d2ee409d579593c1f866bb78e1ea46d688227e90 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 9b0c224115..a569248962 100644 --- a/templates/ContentGenerator/CourseAdmin.html.ep +++ b/templates/ContentGenerator/CourseAdmin.html.ep @@ -67,7 +67,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 d2b47162c4..5790aa3e50 100644 --- a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep @@ -19,9 +19,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