From d2e95a641baf9ba9ce4d532e2e7425230008005d Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Wed, 8 Jul 2026 18:00:29 -0700 Subject: [PATCH 1/5] show file size of archived courses when unarchiving --- lib/WeBWorK/Utils/CourseManagement.pm | 26 +++++++++++++++++++ .../CourseAdmin/unarchive_course_form.html.ep | 14 +++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/lib/WeBWorK/Utils/CourseManagement.pm b/lib/WeBWorK/Utils/CourseManagement.pm index 5c695658e0..41073c1810 100644 --- a/lib/WeBWorK/Utils/CourseManagement.pm +++ b/lib/WeBWorK/Utils/CourseManagement.pm @@ -29,6 +29,7 @@ use WeBWorK::Utils::Instructor qw(assignSetsToUsers); our @EXPORT_OK = qw( listCourses listArchivedCourses + statArchivedCourses addCourse renameCourse retitleCourse @@ -120,6 +121,31 @@ sub listArchivedCourses { return @{ $archivesDir->list->grep(qr/\.tar\.gz$/)->map('basename') }; } +=item statArchivedCourses($ce) + +File info for the courses which have been archived (end in .tar.gz). + +=cut + +sub statArchivedCourses { + my ($ce) = @_; + my @archives = listArchivedCourses($ce); + my $archivesDir = path("$ce->{webworkDirs}{courses}/$ce->{admin_course_id}/archives"); + my %return; + for (@archives) { + my @stat = stat("$archivesDir/$_"); + my $size = $stat[7]; + my @units = qw(B KB MB GB); + my $unit_idx = 0; + while ($size >= 1024 && $unit_idx < @units - 1) { + $size /= 1024; + $unit_idx++; + } + $return{ $_ =~ s/\.tar\.gz$//ir } = { filename => $_, size => sprintf("%s %s", int($size), $units[$unit_idx]) }; + } + return %return; +} + ################################################################################ =item addCourse(%options) diff --git a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep index 0dcda1fbfc..0e0d634775 100644 --- a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep @@ -1,9 +1,10 @@ -% use WeBWorK::Utils::CourseManagement qw(listArchivedCourses); +% use WeBWorK::Utils::CourseManagement qw(statArchivedCourses); %

<%= maketext('Unarchive Course') %> <%= $c->helpMacro('AdminUnarchiveCourse') %>

% % # Find courses which have been archived. -% my @courseIDs = sort { lc($a) cmp lc($b) } listArchivedCourses($ce); +% my %stat = statArchivedCourses($ce); +% my @courseIDs = sort { lc($a) cmp lc($b) } keys %stat; % % if (@courseIDs) { <%= form_for current_route, method => 'POST', begin =%> @@ -16,10 +17,11 @@
<%= label_for 'unarchive_courseID' => maketext('Course Name:'), class => 'col-sm-4 col-form-label' =%>
- <%= select_field unarchive_courseID => \@courseIDs, - id => 'unarchive_courseID', - class => 'form-select', - size => 10 + <%= select_field + unarchive_courseID => [map {["$_ ($stat{$_}{size})" => $stat{$_}{filename}]} @courseIDs], + id => 'unarchive_courseID', + class => 'form-select', + size => 10 =%>
From bbaeb3b04d8f6ac0b1e5f5d59b324df5c2ef0a0c Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Wed, 8 Jul 2026 19:17:23 -0700 Subject: [PATCH 2/5] For pages in the admin course, use 'Course ID' instead of 'Course Name' --- .../ContentGenerator/CourseAdmin/archive_course_form.html.ep | 4 ++-- .../ContentGenerator/CourseAdmin/delete_course_form.html.ep | 4 ++-- .../CourseAdmin/hide_inactive_course_form.html.ep | 4 ++-- .../CourseAdmin/unarchive_course_form.html.ep | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/templates/ContentGenerator/CourseAdmin/archive_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/archive_course_form.html.ep index 39106e4990..fba07f6e3c 100644 --- a/templates/ContentGenerator/CourseAdmin/archive_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/archive_course_form.html.ep @@ -5,7 +5,7 @@ <%= maketext( 'Courses are listed either alphabetically or in order by the time of most recent login activity, oldest ' . 'first. To change the listing order check the mode you want and click "Refresh Listing". The ' - . 'listing format is: Course_Name (status :: date/time of most recent login) where status is "hidden" ' + . 'listing format is: Course_ID (status :: date/time of most recent login) where status is "hidden" ' . 'or "visible".' ) =%>

@@ -35,7 +35,7 @@ <%= $c->hidden_fields('subDisplay') =%>
<%= maketext('Select course(s) to archive.') %>
- <%= label_for archive_courseIDs => maketext('Course Name:'), class => 'col-auto col-form-label fw-bold' =%> + <%= label_for archive_courseIDs => maketext('Course ID:'), class => 'col-auto col-form-label fw-bold' =%>
<%= select_field archive_courseIDs => [ map { [ $courseLabels->{$_} => $_ ] } @$courseIDs ], id => 'archive_courseIDs', diff --git a/templates/ContentGenerator/CourseAdmin/delete_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/delete_course_form.html.ep index 0306c31f29..68e9915ef7 100644 --- a/templates/ContentGenerator/CourseAdmin/delete_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/delete_course_form.html.ep @@ -6,7 +6,7 @@ <%= maketext( 'Courses are listed either alphabetically or in order by the time of most recent login activity, ' . 'oldest first. To change the listing order check the mode you want and click "Refresh Listing". ' - . 'The listing format is: Course_Name (status :: date/time of most recent login) where status is ' + . 'The listing format is: Course_ID (status :: date/time of most recent login) where status is ' . '"hidden" or "visible".' ) =%>

@@ -35,7 +35,7 @@ <%= $c->hidden_fields('subDisplay') =%>
<%= maketext('Select a course to delete.') %>
- <%= label_for delete_courseID => maketext('Course Name:'), class => 'col-auto col-form-label fw-bold' =%> + <%= label_for delete_courseID => maketext('Course ID:'), class => 'col-auto col-form-label fw-bold' =%>
<%= select_field delete_courseID => [ map { [ $courseLabels->{$_} => $_ ] } @$courseIDs ], id => 'delete_courseID', diff --git a/templates/ContentGenerator/CourseAdmin/hide_inactive_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/hide_inactive_course_form.html.ep index 152bb1cbf2..be442fb54f 100644 --- a/templates/ContentGenerator/CourseAdmin/hide_inactive_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/hide_inactive_course_form.html.ep @@ -12,7 +12,7 @@ <%= maketext( 'Courses are listed either alphabetically or in order by the time of most recent login activity, ' . 'oldest first. To change the listing order check the mode you want and click "Refresh Listing". ' - . 'The listing format is: Course_Name (status :: date/time of most recent login) where status is "hidden" ' + . 'The listing format is: Course_ID (status :: date/time of most recent login) where status is "hidden" ' . 'or "visible".' ) =%>

@@ -43,7 +43,7 @@ %
<%= maketext('Select course(s) to hide or unhide.') %>
- <%= label_for hide_courseIDs => maketext('Course Name:'), class => 'col-auto col-form-label fw-bold' =%> + <%= label_for hide_courseIDs => maketext('Course ID:'), class => 'col-auto col-form-label fw-bold' =%>
<%= select_field hide_courseIDs => [ map { [ $courseLabels->{$_} => $_ ] } @$hideCourseIDs ], id => 'hide_courseIDs', diff --git a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep index 0e0d634775..cbd17b5187 100644 --- a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep @@ -15,7 +15,7 @@ %
- <%= label_for 'unarchive_courseID' => maketext('Course Name:'), class => 'col-sm-4 col-form-label' =%> + <%= label_for 'unarchive_courseID' => maketext('Course ID:'), class => 'col-sm-4 col-form-label' =%>
<%= select_field unarchive_courseID => [map {["$_ ($stat{$_}{size})" => $stat{$_}{filename}]} @courseIDs], @@ -26,7 +26,7 @@
- <%= label_for new_courseID => maketext('New Name:'), class => 'col-sm-4 col-form-label' =%> + <%= label_for new_courseID => maketext('New ID:'), class => 'col-sm-4 col-form-label' =%>
<%= text_field new_courseID => '', id => 'new_courseID', From adb42162f3fb5bd5112ab745ee463d5d9c7e7f83 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Wed, 8 Jul 2026 19:20:46 -0700 Subject: [PATCH 3/5] Style the 'Unarchive' page labels the same as similar pages --- .../CourseAdmin/unarchive_course_form.html.ep | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep index cbd17b5187..f87a5ff295 100644 --- a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep @@ -15,7 +15,7 @@ %
- <%= label_for 'unarchive_courseID' => maketext('Course ID:'), class => 'col-sm-4 col-form-label' =%> + <%= label_for 'unarchive_courseID' => maketext('Course ID:'), class => 'col-sm-4 col-form-label fw-bold' =%>
<%= select_field unarchive_courseID => [map {["$_ ($stat{$_}{size})" => $stat{$_}{filename}]} @courseIDs], @@ -26,7 +26,7 @@
- <%= label_for new_courseID => maketext('New ID:'), class => 'col-sm-4 col-form-label' =%> + <%= label_for new_courseID => maketext('New ID:'), class => 'col-sm-4 col-form-label fw-bold' =%>
<%= text_field new_courseID => '', id => 'new_courseID', From f6404cff0c3cbc44e8d15f9dfe4dc85f78598c13 Mon Sep 17 00:00:00 2001 From: Alex Jordan Date: Wed, 8 Jul 2026 19:56:55 -0700 Subject: [PATCH 4/5] Adjust column widths for certain pages in the admin course --- .../CourseAdmin/rename_course_form.html.ep | 24 +++++++++---------- .../CourseAdmin/unarchive_course_form.html.ep | 10 ++++---- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/templates/ContentGenerator/CourseAdmin/rename_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/rename_course_form.html.ep index 9cb34b3546..7d17ed2972 100644 --- a/templates/ContentGenerator/CourseAdmin/rename_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/rename_course_form.html.ep @@ -16,11 +16,11 @@ . 'string.' ) =%>

-
+
<%= label_for rename_oldCourseID => maketext('Course ID:'), - class => 'col-sm-6 col-form-label fw-bold' =%> -
+ class => 'col-lg-3 col-md-4 col-form-label fw-bold' =%> +
<%= select_field rename_oldCourseID => [ map { [ $_ => $_ ] } @courseIDs ], class => 'form-select', size => 10, @@ -28,43 +28,43 @@
-
+
-
+
<%= text_field rename_newCourseID => '', class => 'form-control', 'aria-labelledby' => 'rename_newCourseID_label' =%>
-
+
-
+
<%= text_field rename_newCourseTitle => '', class => 'form-control', 'aria-labelledby' => 'rename_newCourseTitle_label' =%>
-
+
-
+
<%= text_field rename_newCourseInstitution => '', class => 'form-control', 'aria-labelledby' => 'rename_newCourseInstitution_label' =%>
diff --git a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep index f87a5ff295..e5b2d4afc1 100644 --- a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep @@ -13,10 +13,10 @@ %
<%= maketext('Select a course to unarchive.') =%>
% -
+
- <%= label_for 'unarchive_courseID' => maketext('Course ID:'), class => 'col-sm-4 col-form-label fw-bold' =%> -
+ <%= label_for 'unarchive_courseID' => maketext('Course ID:'), class => 'col-lg-3 col-md-4 col-form-label fw-bold' =%> +
<%= select_field unarchive_courseID => [map {["$_ ($stat{$_}{size})" => $stat{$_}{filename}]} @courseIDs], id => 'unarchive_courseID', @@ -26,8 +26,8 @@
- <%= label_for new_courseID => maketext('New ID:'), class => 'col-sm-4 col-form-label fw-bold' =%> -
+ <%= label_for new_courseID => maketext('New ID:'), class => 'col-lg-3 col-md-4 col-form-label fw-bold' =%> +
<%= text_field new_courseID => '', id => 'new_courseID', size => 25, From da34ce9581b91e560bb4781753113302f4f69d89 Mon Sep 17 00:00:00 2001 From: Glenn Rice Date: Thu, 9 Jul 2026 06:18:38 -0500 Subject: [PATCH 5/5] Combine the `listArchiveCourses` and `statArchiveCourses` methods. The `listArchiveCourses` also stats the archive files found and returns the files as a hash whose keys are the course ids and the values are references to hashes containing the `filename` (the basename of the file including the `.tar.gz` extension) and file `size` Both of the places that use the `listArchiveCourses` method are updated to use this return value. --- lib/WeBWorK/Utils/CourseManagement.pm | 41 ++++++++++--------- .../ContentGenerator/CourseAdmin.html.ep | 5 ++- .../CourseAdmin/unarchive_course_form.html.ep | 22 ++++++---- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/lib/WeBWorK/Utils/CourseManagement.pm b/lib/WeBWorK/Utils/CourseManagement.pm index 41073c1810..d584758dfd 100644 --- a/lib/WeBWorK/Utils/CourseManagement.pm +++ b/lib/WeBWorK/Utils/CourseManagement.pm @@ -29,7 +29,6 @@ use WeBWorK::Utils::Instructor qw(assignSetsToUsers); our @EXPORT_OK = qw( listCourses listArchivedCourses - statArchivedCourses addCourse renameCourse retitleCourse @@ -110,7 +109,17 @@ sub listCourses { =item listArchivedCourses($ce) -Lists the courses which have been archived (end in .tar.gz). +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, + + { + myTestCourse => { + filename => 'myTestCourse.tar.gz', + size => '605 KB' + } + } =cut @@ -118,30 +127,24 @@ sub listArchivedCourses { my ($ce) = @_; my $archivesDir = path("$ce->{webworkDirs}{courses}/$ce->{admin_course_id}/archives"); surePathToFile($ce->{webworkDirs}{courses}, "$archivesDir/test"); # Ensure archives directory exists. - return @{ $archivesDir->list->grep(qr/\.tar\.gz$/)->map('basename') }; -} -=item statArchivedCourses($ce) + my $archives = $archivesDir->list->grep(qr/\.tar\.gz$/i); -File info for the courses which have been archived (end in .tar.gz). - -=cut - -sub statArchivedCourses { - my ($ce) = @_; - my @archives = listArchivedCourses($ce); - my $archivesDir = path("$ce->{webworkDirs}{courses}/$ce->{admin_course_id}/archives"); my %return; - for (@archives) { - my @stat = stat("$archivesDir/$_"); - my $size = $stat[7]; + for (@$archives) { + my $size = $_->stat->size; my @units = qw(B KB MB GB); my $unit_idx = 0; - while ($size >= 1024 && $unit_idx < @units - 1) { + while ($size >= 1024 && $unit_idx < $#units) { $size /= 1024; - $unit_idx++; + ++$unit_idx; } - $return{ $_ =~ s/\.tar\.gz$//ir } = { filename => $_, size => sprintf("%s %s", int($size), $units[$unit_idx]) }; + my $basename = $_->basename; + my $round = 10**($unit_idx > 0 ? $unit_idx - 1 : 0); + $return{ $basename =~ s/\.tar\.gz$//ir } = { + filename => $basename, + size => sprintf("%s %s", int($size * $round) / $round, $units[$unit_idx]) + }; } return %return; } diff --git a/templates/ContentGenerator/CourseAdmin.html.ep b/templates/ContentGenerator/CourseAdmin.html.ep index be8e357ad9..793a193ac0 100644 --- a/templates/ContentGenerator/CourseAdmin.html.ep +++ b/templates/ContentGenerator/CourseAdmin.html.ep @@ -65,8 +65,9 @@ ) =%>

    - % for (sort { lc($a) cmp lc($b) } listArchivedCourses($ce)) { -
  1. <%= $_ %>
  2. + % my %courseArchives = listArchivedCourses($ce); + % for (sort { lc($a) cmp lc($b) } keys %courseArchives) { +
  3. <%= "$_ ($courseArchives{$_}{size})" %>
  4. % }
% } diff --git a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep index e5b2d4afc1..1218c6bfe7 100644 --- a/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep +++ b/templates/ContentGenerator/CourseAdmin/unarchive_course_form.html.ep @@ -1,10 +1,10 @@ -% use WeBWorK::Utils::CourseManagement qw(statArchivedCourses); +% use WeBWorK::Utils::CourseManagement qw(listArchivedCourses); %

<%= maketext('Unarchive Course') %> <%= $c->helpMacro('AdminUnarchiveCourse') %>

% % # Find courses which have been archived. -% my %stat = statArchivedCourses($ce); -% my @courseIDs = sort { lc($a) cmp lc($b) } keys %stat; +% my %courseArchives = listArchivedCourses($ce); +% my @courseIDs = sort { lc($a) cmp lc($b) } keys %courseArchives; % % if (@courseIDs) { <%= form_for current_route, method => 'POST', begin =%> @@ -15,18 +15,22 @@ %
- <%= label_for 'unarchive_courseID' => maketext('Course ID:'), class => 'col-lg-3 col-md-4 col-form-label fw-bold' =%> + <%= label_for 'unarchive_courseID' => maketext('Course ID:'), + class => 'col-lg-3 col-md-4 col-form-label fw-bold' =%>
<%= select_field - unarchive_courseID => [map {["$_ ($stat{$_}{size})" => $stat{$_}{filename}]} @courseIDs], - id => 'unarchive_courseID', - class => 'form-select', - size => 10 + unarchive_courseID => [ + map { [ "$_ ($courseArchives{$_}{size})" => $courseArchives{$_}{filename} ] } @courseIDs + ], + id => 'unarchive_courseID', + class => 'form-select', + size => 10 =%>
- <%= label_for new_courseID => maketext('New ID:'), class => 'col-lg-3 col-md-4 col-form-label fw-bold' =%> + <%= label_for new_courseID => maketext('New ID:'), + class => 'col-lg-3 col-md-4 col-form-label fw-bold' =%>
<%= text_field new_courseID => '', id => 'new_courseID',