| … | |
… | |
| 239 | if (defined $r->param("visible_users")) { |
239 | if (defined $r->param("visible_users")) { |
| 240 | $self->{visibleUserIDs} = [ $r->param("visible_users") ]; |
240 | $self->{visibleUserIDs} = [ $r->param("visible_users") ]; |
| 241 | } elsif (defined $r->param("no_visible_users")) { |
241 | } elsif (defined $r->param("no_visible_users")) { |
| 242 | $self->{visibleUserIDs} = []; |
242 | $self->{visibleUserIDs} = []; |
| 243 | } else { |
243 | } else { |
|
|
244 | if (@allUserIDs > HIDE_USERS_THRESHHOLD) { |
|
|
245 | $self->{visibleUserIDs} = []; |
|
|
246 | } else { |
| 244 | $self->{visibleUserIDs} = [ @allUserIDs ]; |
247 | $self->{visibleUserIDs} = [ @allUserIDs ]; |
|
|
248 | } |
| 245 | } |
249 | } |
| 246 | |
250 | |
| 247 | $self->{prevVisibleUserIDs} = $self->{visibleUserIDs}; |
251 | $self->{prevVisibleUserIDs} = $self->{visibleUserIDs}; |
| 248 | |
252 | |
| 249 | if (defined $r->param("selected_users")) { |
253 | if (defined $r->param("selected_users")) { |
| … | |
… | |
| 269 | $genericParams{$param} = [ $r->param($param) ]; |
273 | $genericParams{$param} = [ $r->param($param) ]; |
| 270 | } |
274 | } |
| 271 | my %actionParams = $self->getActionParams($actionID); |
275 | my %actionParams = $self->getActionParams($actionID); |
| 272 | my %tableParams = $self->getTableParams(); |
276 | my %tableParams = $self->getTableParams(); |
| 273 | print CGI::p( |
277 | print CGI::p( |
| 274 | "Action: ", |
278 | "Result of last action performed: ", |
| 275 | CGI::i($self->$actionHandler(\%genericParams, \%actionParams, \%tableParams)) |
279 | CGI::i($self->$actionHandler(\%genericParams, \%actionParams, \%tableParams)) |
| 276 | ); |
280 | ); |
| 277 | } |
281 | } |
| 278 | |
282 | |
| 279 | ########## retrieve possibly changed values for member fields |
283 | ########## retrieve possibly changed values for member fields |
| … | |
… | |
| 347 | |
351 | |
| 348 | print CGI::hidden(-name=>"sortField", -value=>$sortField); |
352 | print CGI::hidden(-name=>"sortField", -value=>$sortField); |
| 349 | |
353 | |
| 350 | print "\n<!-- state data here -->\n"; |
354 | print "\n<!-- state data here -->\n"; |
| 351 | |
355 | |
| 352 | ########## print table |
|
|
| 353 | |
|
|
| 354 | print CGI::p("Showing ", scalar @visibleUserIDs, " out of ", scalar @allUserIDs, " users."); |
|
|
| 355 | |
|
|
| 356 | $self->printTableHTML(\@Users, \@PermissionLevels, \%prettyFieldNames, |
|
|
| 357 | editMode => $editMode, |
|
|
| 358 | selectedUserIDs => \@selectedUserIDs, |
|
|
| 359 | ); |
|
|
| 360 | |
|
|
| 361 | ########## print action forms |
356 | ########## print action forms |
| 362 | |
357 | |
| 363 | print CGI::start_table({}); |
358 | print CGI::start_table({}); |
| 364 | print CGI::Tr({}, CGI::td({-colspan=>2}, "Select an action to perform:")); |
359 | print CGI::Tr({}, CGI::td({-colspan=>2}, "Select an action to perform:")); |
| 365 | |
360 | |
| … | |
… | |
| 387 | print CGI::Tr({}, CGI::td({-colspan=>2, -align=>"center"}, |
382 | print CGI::Tr({}, CGI::td({-colspan=>2, -align=>"center"}, |
| 388 | CGI::submit(-value=>"Take Action!")) |
383 | CGI::submit(-value=>"Take Action!")) |
| 389 | ); |
384 | ); |
| 390 | print CGI::end_table(); |
385 | print CGI::end_table(); |
| 391 | |
386 | |
|
|
387 | ########## print table |
|
|
388 | |
|
|
389 | print CGI::p("Showing ", scalar @visibleUserIDs, " out of ", scalar @allUserIDs, " users."); |
|
|
390 | |
|
|
391 | $self->printTableHTML(\@Users, \@PermissionLevels, \%prettyFieldNames, |
|
|
392 | editMode => $editMode, |
|
|
393 | selectedUserIDs => \@selectedUserIDs, |
|
|
394 | ); |
|
|
395 | |
|
|
396 | |
| 392 | ########## print end of form |
397 | ########## print end of form |
| 393 | |
398 | |
| 394 | print CGI::end_form(); |
399 | print CGI::end_form(); |
| 395 | |
400 | |
| 396 | return ""; |
401 | return ""; |
| … | |
… | |
| 425 | } |
430 | } |
| 426 | |
431 | |
| 427 | ################################################################################ |
432 | ################################################################################ |
| 428 | # actions and action triggers |
433 | # actions and action triggers |
| 429 | ################################################################################ |
434 | ################################################################################ |
|
|
435 | |
|
|
436 | # filter, edit, cancelEdit, and saveEdit should stay with the display module and |
|
|
437 | # not be real "actions". that way, all actions are shown in view mode and no |
|
|
438 | # actions are shown in edit mode. |
| 430 | |
439 | |
| 431 | sub filter_form { |
440 | sub filter_form { |
| 432 | my ($self, $onChange, %actionParams) = @_; |
441 | my ($self, $onChange, %actionParams) = @_; |
| 433 | return join("", |
442 | return join("", |
| 434 | "Show ", |
443 | "Show ", |
| … | |
… | |
| 473 | return join("", |
482 | return join("", |
| 474 | "Edit ", |
483 | "Edit ", |
| 475 | CGI::popup_menu( |
484 | CGI::popup_menu( |
| 476 | -name => "action.edit.scope", |
485 | -name => "action.edit.scope", |
| 477 | -values => [qw(all visible selected)], |
486 | -values => [qw(all visible selected)], |
| 478 | -default => $actionParams{"action.edit.scope"}->[0] || "selected", |
487 | -default => $actionParams{"action.edit.scope"}->[0] || "visible", |
| 479 | -labels => { |
488 | -labels => { |
| 480 | all => "all users", |
489 | all => "all users", |
| 481 | visible => "visible users", |
490 | visible => "visible users", |
| 482 | selected => "selected users" |
491 | selected => "selected users" |
| 483 | }, |
492 | }, |
| … | |
… | |
| 511 | my ($self, $onChange, %actionParams) = @_; |
520 | my ($self, $onChange, %actionParams) = @_; |
| 512 | return join("", |
521 | return join("", |
| 513 | "Delete ", |
522 | "Delete ", |
| 514 | CGI::popup_menu( |
523 | CGI::popup_menu( |
| 515 | -name => "action.delete.scope", |
524 | -name => "action.delete.scope", |
| 516 | -values => [qw(all visible selected)], |
525 | -values => [qw(visible selected)], |
| 517 | -default => $actionParams{"action.delete.scope"}->[0] || "selected", |
526 | -default => $actionParams{"action.delete.scope"}->[0] || "selected", |
| 518 | -labels => { |
527 | -labels => { |
| 519 | all => "all users", |
|
|
| 520 | visible => "visible users", |
528 | visible => "visible users", |
| 521 | selected => "selected users" |
529 | selected => "selected users" |
| 522 | }, |
530 | }, |
| 523 | -onchange => $onChange, |
531 | -onchange => $onChange, |
| 524 | ), |
532 | ), |
| 525 | CGI::em(" Deletion is not undoable!"), |
533 | CGI::em(" Deletion destroys all user-related data and is not undoable!"), |
| 526 | CGI::br(), |
|
|
| 527 | CGI::em("Deleting a user destroys the user-specific set and problem data for that user."), |
|
|
| 528 | ); |
534 | ); |
| 529 | } |
535 | } |
| 530 | |
536 | |
| 531 | sub delete_handler { |
537 | sub delete_handler { |
| 532 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
538 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
| 533 | my $db = $self->{db}; |
539 | my $db = $self->{db}; |
| 534 | my $scope = $actionParams->{"action.delete.scope"}->[0]; |
540 | my $scope = $actionParams->{"action.delete.scope"}->[0]; |
| 535 | |
541 | |
| 536 | my @userIDsToDelete; |
542 | my @userIDsToDelete; |
| 537 | if ($scope eq "all") { |
|
|
| 538 | @userIDsToDelete = @{ $self->{allUserIDs} }; |
|
|
| 539 | } elsif ($scope eq "visible") { |
543 | if ($scope eq "visible") { |
| 540 | @userIDsToDelete = @{ $self->{visibleUserIDs} }; |
544 | @userIDsToDelete = @{ $self->{visibleUserIDs} }; |
| 541 | } elsif ($scope eq "selected") { |
545 | } elsif ($scope eq "selected") { |
| 542 | @userIDsToDelete = @{ $self->{selectedUserIDs} }; |
546 | @userIDsToDelete = @{ $self->{selectedUserIDs} }; |
| 543 | } |
547 | } |
| 544 | |
548 | |
| … | |
… | |
| 568 | CGI::popup_menu( |
572 | CGI::popup_menu( |
| 569 | -name => "action.import.source", |
573 | -name => "action.import.source", |
| 570 | -values => [ "", $self->getCSVList() ], |
574 | -values => [ "", $self->getCSVList() ], |
| 571 | -default => $actionParams{"action.import.source"}->[0] || "", |
575 | -default => $actionParams{"action.import.source"}->[0] || "", |
| 572 | -onchange => $onChange, |
576 | -onchange => $onChange, |
| 573 | ), CGI::br(), |
577 | ), |
| 574 | "replacing", |
578 | "replacing", |
| 575 | CGI::popup_menu( |
579 | CGI::popup_menu( |
| 576 | -name => "action.import.replace", |
580 | -name => "action.import.replace", |
| 577 | -values => [qw(any visible selected none)], |
581 | -values => [qw(any visible selected none)], |
| 578 | -default => $actionParams{"action.import.replace"}->[0] || "none", |
582 | -default => $actionParams{"action.import.replace"}->[0] || "none", |
| … | |
… | |
| 582 | selected => "selected", |
586 | selected => "selected", |
| 583 | none => "no", |
587 | none => "no", |
| 584 | }, |
588 | }, |
| 585 | -onchange => $onChange, |
589 | -onchange => $onChange, |
| 586 | ), |
590 | ), |
| 587 | "existing users", CGI::br(), |
591 | "existing users and adding", |
| 588 | " and adding", |
|
|
| 589 | CGI::popup_menu( |
592 | CGI::popup_menu( |
| 590 | -name => "action.import.add", |
593 | -name => "action.import.add", |
| 591 | -values => [qw(any none)], |
594 | -values => [qw(any none)], |
| 592 | -default => $actionParams{"action.import.add"}->[0] || "any", |
595 | -default => $actionParams{"action.import.add"}->[0] || "any", |
| 593 | -labels => { |
596 | -labels => { |
| … | |
… | |
| 624 | } |
627 | } |
| 625 | |
628 | |
| 626 | my ($replaced, $added, $skipped) |
629 | my ($replaced, $added, $skipped) |
| 627 | = $self->importUsersFromCSV($fileName, $createNew, $replaceExisting, @replaceList); |
630 | = $self->importUsersFromCSV($fileName, $createNew, $replaceExisting, @replaceList); |
| 628 | |
631 | |
|
|
632 | # make new users visible... do we really want to do this? probably. |
|
|
633 | push @{ $self->{visibleUserIDs} }, @$added; |
|
|
634 | |
| 629 | my $numReplaced = @$replaced; |
635 | my $numReplaced = @$replaced; |
| 630 | my $numAdded = @$added; |
636 | my $numAdded = @$added; |
| 631 | my $numSkipped = @$skipped; |
637 | my $numSkipped = @$skipped; |
| 632 | |
638 | |
| 633 | return $numReplaced . " user" . ($numReplaced == 1 ? "" : "s") . " replaced, " |
639 | return $numReplaced . " user" . ($numReplaced == 1 ? "" : "s") . " replaced, " |
| … | |
… | |
| 635 | . $numSkipped . " user" . ($numSkipped == 1 ? "" : "s") . " skipped."; |
641 | . $numSkipped . " user" . ($numSkipped == 1 ? "" : "s") . " skipped."; |
| 636 | } |
642 | } |
| 637 | |
643 | |
| 638 | sub export_form { |
644 | sub export_form { |
| 639 | my ($self, $onChange, %actionParams) = @_; |
645 | my ($self, $onChange, %actionParams) = @_; |
| 640 | return join(" ", |
646 | return join("", |
| 641 | "Export", |
647 | "Export ", |
| 642 | CGI::popup_menu( |
648 | CGI::popup_menu( |
| 643 | -name => "action.export.scope", |
649 | -name => "action.export.scope", |
| 644 | -values => [qw(all visible selected)], |
650 | -values => [qw(all visible selected)], |
| 645 | -default => $actionParams{"action.export.scope"}->[0] || "selected", |
651 | -default => $actionParams{"action.export.scope"}->[0] || "visible", |
| 646 | -labels => { |
652 | -labels => { |
| 647 | all => "all users", |
653 | all => "all users", |
| 648 | visible => "visible users", |
654 | visible => "visible users", |
| 649 | selected => "selected users" |
655 | selected => "selected users" |
| 650 | }, |
656 | }, |
| 651 | -onchange => $onChange, |
657 | -onchange => $onChange, |
| 652 | ), |
658 | ), |
| 653 | " -- not yet implemented", |
659 | " to ", |
| 654 | CGI::br(), |
660 | CGI::popup_menu( |
| 655 | CGI::input({ |
|
|
| 656 | -type => "radio", |
|
|
| 657 | -name => "action.export.target", |
661 | -name=>"action.export.target", |
| 658 | -value => "existing", |
662 | -values => [ "new", $self->getCSVList() ], |
|
|
663 | -labels => { new => "a new file named:" }, |
|
|
664 | -default => $actionParams{"action.export.target"}->[0] || "", |
| 659 | -onchange => $onChange, |
665 | -onchange => $onChange, |
| 660 | }), |
666 | ), |
| 661 | " Overwrite the existing classlist file named ", |
|
|
| 662 | CGI::popup_menu( |
|
|
| 663 | -name=>"action.export.existingName", |
|
|
| 664 | -values => [ "", $self->getCSVList() ], |
|
|
| 665 | -default => $actionParams{"action.export.existingName"}->[0] || "", |
|
|
| 666 | ), CGI::br(), |
667 | #CGI::br(), |
| 667 | CGI::input({ |
668 | #"new file to create: ", |
| 668 | -type => "radio", |
669 | CGI::textfield( |
| 669 | -name => "action.export.target", |
670 | -name => "action.export.new", |
| 670 | -value => "new", |
671 | -value => $actionParams{"action.export.new"}->[0] || "",, |
|
|
672 | -width => "50", |
| 671 | -onchange => $onChange, |
673 | -onchange => $onChange, |
| 672 | }), |
|
|
| 673 | " Create a new classlist file named ", |
|
|
| 674 | CGI::textfield( |
|
|
| 675 | -name => "action.export.newName", |
|
|
| 676 | -value => "", |
|
|
| 677 | -width => "31", |
|
|
| 678 | ), |
674 | ), |
|
|
675 | CGI::tt(".lst"), |
| 679 | ); |
676 | ); |
|
|
677 | } |
|
|
678 | |
|
|
679 | sub export_handler { |
|
|
680 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
|
|
681 | |
|
|
682 | my $scope = $actionParams->{"action.export.scope"}->[0]; |
|
|
683 | my $target = $actionParams->{"action.export.target"}->[0]; |
|
|
684 | my $new = $actionParams->{"action.export.new"}->[0]; |
|
|
685 | |
|
|
686 | my $fileName; |
|
|
687 | if ($target eq "new") { |
|
|
688 | $fileName = $new; |
|
|
689 | } else { |
|
|
690 | $fileName = $target; |
|
|
691 | } |
|
|
692 | |
|
|
693 | $fileName .= ".lst" unless $fileName =~ m/\.lst$/; |
|
|
694 | |
|
|
695 | my @userIDsToExport; |
|
|
696 | if ($scope eq "all") { |
|
|
697 | @userIDsToExport = @{ $self->{allUserIDs} }; |
|
|
698 | } elsif ($scope eq "visible") { |
|
|
699 | @userIDsToExport = @{ $self->{visibleUserIDs} }; |
|
|
700 | } elsif ($scope eq "selected") { |
|
|
701 | @userIDsToExport = @{ $self->{selectedUserIDs} }; |
|
|
702 | } |
|
|
703 | |
|
|
704 | $self->exportUsersToCSV($fileName, @userIDsToExport); |
|
|
705 | |
|
|
706 | return scalar @userIDsToExport . " users exported"; |
| 680 | } |
707 | } |
| 681 | |
708 | |
| 682 | sub cancelEdit_form { |
709 | sub cancelEdit_form { |
| 683 | my ($self, $onChange, %actionParams) = @_; |
710 | my ($self, $onChange, %actionParams) = @_; |
| 684 | return "Abandon changes"; |
711 | return "Abandon changes"; |
| … | |
… | |
| 861 | push @added, $user_id; |
888 | push @added, $user_id; |
| 862 | } |
889 | } |
| 863 | } |
890 | } |
| 864 | |
891 | |
| 865 | return \@replaced, \@added, \@skipped; |
892 | return \@replaced, \@added, \@skipped; |
|
|
893 | } |
|
|
894 | |
|
|
895 | sub exportUsersToCSV { |
|
|
896 | my ($self, $fileName, @userIDsToExport) = @_; |
|
|
897 | my $ce = $self->{ce}; |
|
|
898 | my $db = $self->{db}; |
|
|
899 | my $dir = $ce->{courseDirs}->{templates}; |
|
|
900 | |
|
|
901 | die "illegal character in input: \"/\"" if $fileName =~ m|/|; |
|
|
902 | |
|
|
903 | open my $fh, ">", "$dir/$fileName" |
|
|
904 | or die "failed to open file $dir/$fileName for writing: $!\n"; |
|
|
905 | |
|
|
906 | foreach my $userID (@userIDsToExport) { |
|
|
907 | my $User = $db->getUser($userID); |
|
|
908 | my @fields = ( |
|
|
909 | $User->student_id, |
|
|
910 | $User->last_name, |
|
|
911 | $User->first_name, |
|
|
912 | $User->status, |
|
|
913 | $User->comment, |
|
|
914 | $User->section, |
|
|
915 | $User->recitation, |
|
|
916 | $User->email_address, |
|
|
917 | $User->user_id, |
|
|
918 | ); |
|
|
919 | my $string = join ",", @fields; |
|
|
920 | print $fh "$string\n"; |
|
|
921 | } |
|
|
922 | |
|
|
923 | close $fh; |
| 866 | } |
924 | } |
| 867 | |
925 | |
| 868 | ################################################################################ |
926 | ################################################################################ |
| 869 | # "display" methods |
927 | # "display" methods |
| 870 | ################################################################################ |
928 | ################################################################################ |