| … | |
… | |
| 28 | use CGI qw(); |
28 | use CGI qw(); |
| 29 | use WeBWorK::HTML::ComboBox qw/comboBox/; |
29 | use WeBWorK::HTML::ComboBox qw/comboBox/; |
| 30 | use WeBWorK::Utils qw(readDirectory list2hash listFilesRecursive max); |
30 | use WeBWorK::Utils qw(readDirectory list2hash listFilesRecursive max); |
| 31 | use WeBWorK::DB::Record::Set; |
31 | use WeBWorK::DB::Record::Set; |
| 32 | use WeBWorK::Utils::Tasks qw(renderProblems); |
32 | use WeBWorK::Utils::Tasks qw(renderProblems); |
|
|
33 | use WeBWorK::Debug; |
| 33 | |
34 | |
| 34 | # Important Note: the following two sets of constants may seem similar |
35 | # Important Note: the following two sets of constants may seem similar |
| 35 | # but they are functionally and semantically different |
36 | # but they are functionally and semantically different |
| 36 | |
37 | |
| 37 | # these constants determine which fields belong to what type of record |
38 | # these constants determine which fields belong to what type of record |
| … | |
… | |
| 79 | default => "", |
80 | default => "", |
| 80 | }, |
81 | }, |
| 81 | open_date => { |
82 | open_date => { |
| 82 | name => "Opens", |
83 | name => "Opens", |
| 83 | type => "edit", |
84 | type => "edit", |
| 84 | size => "24", |
85 | size => "26", |
| 85 | override => "any", |
86 | override => "any", |
| 86 | labels => { |
87 | labels => { |
| 87 | 0 => "None Specified", |
88 | 0 => "None Specified", |
| 88 | "" => "None Specified", |
89 | "" => "None Specified", |
| 89 | }, |
90 | }, |
| 90 | }, |
91 | }, |
| 91 | due_date => { |
92 | due_date => { |
| 92 | name => "Answers Due", |
93 | name => "Answers Due", |
| 93 | type => "edit", |
94 | type => "edit", |
| 94 | size => "24", |
95 | size => "26", |
| 95 | override => "any", |
96 | override => "any", |
| 96 | labels => { |
97 | labels => { |
| 97 | 0 => "None Specified", |
98 | 0 => "None Specified", |
| 98 | "" => "None Specified", |
99 | "" => "None Specified", |
| 99 | }, |
100 | }, |
| 100 | }, |
101 | }, |
| 101 | answer_date => { |
102 | answer_date => { |
| 102 | name => "Answers Available", |
103 | name => "Answers Available", |
| 103 | type => "edit", |
104 | type => "edit", |
| 104 | size => "24", |
105 | size => "26", |
| 105 | override => "any", |
106 | override => "any", |
| 106 | labels => { |
107 | labels => { |
| 107 | 0 => "None Specified", |
108 | 0 => "None Specified", |
| 108 | "" => "None Specified", |
109 | "" => "None Specified", |
| 109 | }, |
110 | }, |
| … | |
… | |
| 127 | default => "", |
128 | default => "", |
| 128 | }, |
129 | }, |
| 129 | value => { |
130 | value => { |
| 130 | name => "Weight", |
131 | name => "Weight", |
| 131 | type => "edit", |
132 | type => "edit", |
| 132 | size => 5, |
133 | size => 6, |
| 133 | override => "any", |
134 | override => "any", |
| 134 | }, |
135 | }, |
| 135 | max_attempts => { |
136 | max_attempts => { |
| 136 | name => "Max attempts", |
137 | name => "Max attempts", |
| 137 | type => "edit", |
138 | type => "edit", |
| 138 | size => 5, |
139 | size => 6, |
| 139 | override => "any", |
140 | override => "any", |
| 140 | labels => { |
141 | labels => { |
| 141 | "-1" => "unlimited", |
142 | "-1" => "unlimited", |
| 142 | }, |
143 | }, |
| 143 | }, |
144 | }, |
| 144 | problem_seed => { |
145 | problem_seed => { |
| 145 | name => "Seed", |
146 | name => "Seed", |
| 146 | type => "edit", |
147 | type => "edit", |
| 147 | size => 5, |
148 | size => 6, |
| 148 | override => "one", |
149 | override => "one", |
| 149 | |
150 | |
| 150 | }, |
151 | }, |
| 151 | status => { |
152 | status => { |
| 152 | name => "Status", |
153 | name => "Status", |
| 153 | type => "edit", |
154 | type => "edit", |
| 154 | size => 5, |
155 | size => 6, |
| 155 | override => "any", |
156 | override => "one", |
| 156 | default => 0, |
157 | default => 0, |
| 157 | }, |
158 | }, |
| 158 | attempted => { |
159 | attempted => { |
| 159 | name => "Attempted", |
160 | name => "Attempted", |
| 160 | type => "hidden", |
161 | type => "hidden", |
| … | |
… | |
| 498 | my @values = $r->param("set.$setID.$_"); |
499 | my @values = $r->param("set.$setID.$_"); |
| 499 | my $value = $values[0] || $values[1] || ""; |
500 | my $value = $values[0] || $values[1] || ""; |
| 500 | $r->param("set.$setID.$_", $value); |
501 | $r->param("set.$setID.$_", $value); |
| 501 | } |
502 | } |
| 502 | |
503 | |
|
|
504 | ##################################################################### |
|
|
505 | # Check date information |
|
|
506 | ##################################################################### |
|
|
507 | |
|
|
508 | my ($open_date, $due_date, $answer_date); |
|
|
509 | my $error = 0; |
| 503 | if (defined $r->param('submit_changes')) { |
510 | if (defined $r->param('submit_changes')) { |
| 504 | |
511 | |
|
|
512 | my $od_param = $r->param("set.$setID.open_date"); |
|
|
513 | my $dd_param = $r->param("set.$setID.due_date"); |
|
|
514 | my $ad_param = $r->param("set.$setID.answer_date"); |
|
|
515 | my $setRecord = $db->getGlobalSet($setID); |
|
|
516 | |
|
|
517 | $open_date = $od_param ? $self->parseDateTime($od_param) : $setRecord->open_date; |
|
|
518 | $due_date = $dd_param ? $self->parseDateTime($dd_param) : $setRecord->due_date; |
|
|
519 | $answer_date = $ad_param ? $self->parseDateTime($ad_param) : $setRecord->answer_date; |
|
|
520 | |
|
|
521 | if ($answer_date < $due_date || $answer_date < $open_date) { |
|
|
522 | $self->addbadmessage("Answers cannot be made available until on or after the due date!"); |
|
|
523 | $error = $r->param('submit_changes'); |
|
|
524 | } |
|
|
525 | |
|
|
526 | if ($due_date < $open_date) { |
|
|
527 | $self->addbadmessage("Answers cannot be due until on or after the open date!"); |
|
|
528 | $error = $r->param('submit_changes'); |
|
|
529 | } |
|
|
530 | |
|
|
531 | if ($error) { |
|
|
532 | $self->addbadmessage("No changes were saved!"); |
|
|
533 | } |
|
|
534 | } |
|
|
535 | |
|
|
536 | |
|
|
537 | if (defined $r->param('submit_changes') && !$error) { |
|
|
538 | |
| 505 | my $setRecord = $db->getGlobalSet($setID); |
539 | my $setRecord = $db->getGlobalSet($setID); |
| 506 | |
540 | |
| 507 | ##################################################################### |
541 | ##################################################################### |
| 508 | # Save general set information (including headers) |
542 | # Save general set information (including headers) |
| 509 | ##################################################################### |
543 | ##################################################################### |
| … | |
… | |
| 529 | } |
563 | } |
| 530 | } |
564 | } |
| 531 | $db->putUserSet($record); |
565 | $db->putUserSet($record); |
| 532 | } |
566 | } |
| 533 | } else { |
567 | } else { |
| 534 | |
|
|
| 535 | foreach my $field ( @{ SET_FIELDS() } ) { |
568 | foreach my $field ( @{ SET_FIELDS() } ) { |
| 536 | next unless canChange($forUsers, $field); |
569 | next unless canChange($forUsers, $field); |
| 537 | |
570 | |
| 538 | my $param = $r->param("set.$setID.$field"); |
571 | my $param = $r->param("set.$setID.$field"); |
| 539 | $param = $properties{$field}->{default} || "" unless defined $param && $param ne ""; |
572 | $param = $properties{$field}->{default} || "" unless defined $param && $param ne ""; |
| … | |
… | |
| 549 | |
582 | |
| 550 | ##################################################################### |
583 | ##################################################################### |
| 551 | # Save problem information |
584 | # Save problem information |
| 552 | ##################################################################### |
585 | ##################################################################### |
| 553 | |
586 | |
| 554 | my @problemIDs = $db->listGlobalProblems($setID); |
587 | my @problemIDs = sort { $a <=> $b } $db->listGlobalProblems($setID);; |
| 555 | my @problemRecords = $db->getGlobalProblems(map { [$setID, $_] } @problemIDs); |
588 | my @problemRecords = $db->getGlobalProblems(map { [$setID, $_] } @problemIDs); |
| 556 | foreach my $problemRecord (@problemRecords) { |
589 | foreach my $problemRecord (@problemRecords) { |
| 557 | my $problemID = $problemRecord->problem_id; |
590 | my $problemID = $problemRecord->problem_id; |
| 558 | die "Global problem $problemID for set $setID not found." unless $problemRecord; |
591 | die "Global problem $problemID for set $setID not found." unless $problemRecord; |
| 559 | |
592 | |
| 560 | if ($forUsers) { |
593 | if ($forUsers) { |
| 561 | |
|
|
| 562 | # Since we're editing for specific users, we don't allow the GlobalProblem record to be altered on that same page |
594 | # Since we're editing for specific users, we don't allow the GlobalProblem record to be altered on that same page |
| 563 | # So we only need to make changes to the UserProblem record and only then if we are overriding a value |
595 | # So we only need to make changes to the UserProblem record and only then if we are overriding a value |
| 564 | # in the GlobalProblem record or for fields unique to the UserProblem record. |
596 | # in the GlobalProblem record or for fields unique to the UserProblem record. |
| 565 | |
597 | |
| 566 | my @userIDs = @editForUser; |
598 | my @userIDs = @editForUser; |
| … | |
… | |
| 597 | $record->$field($param); |
629 | $record->$field($param); |
| 598 | } |
630 | } |
| 599 | $db->putUserProblem($record) if $changed; |
631 | $db->putUserProblem($record) if $changed; |
| 600 | } |
632 | } |
| 601 | } else { |
633 | } else { |
| 602 | |
|
|
| 603 | # Since we're editing for ALL set users, we will make changes to the GlobalProblem record. |
634 | # Since we're editing for ALL set users, we will make changes to the GlobalProblem record. |
| 604 | # We may also have instances where a field is unique to the UserProblem record but we want |
635 | # We may also have instances where a field is unique to the UserProblem record but we want |
| 605 | # all users to (at least initially) have the same value |
636 | # all users to (at least initially) have the same value |
| 606 | |
637 | |
| 607 | # this only edits a globalProblem record |
638 | # this only edits a globalProblem record |
| … | |
… | |
| 635 | if (keys %useful) { |
666 | if (keys %useful) { |
| 636 | my @userIDs = $db->listProblemUsers($setID, $problemID); |
667 | my @userIDs = $db->listProblemUsers($setID, $problemID); |
| 637 | my @userProblemIDs = map { [$_, $setID, $problemID] } @userIDs; |
668 | my @userProblemIDs = map { [$_, $setID, $problemID] } @userIDs; |
| 638 | my @userProblemRecords = $db->getUserProblems(@userProblemIDs); |
669 | my @userProblemRecords = $db->getUserProblems(@userProblemIDs); |
| 639 | foreach my $record (@userProblemRecords) { |
670 | foreach my $record (@userProblemRecords) { |
| 640 | my $copy = $record; |
|
|
| 641 | my $changed = 0; # keep track of any changes, if none are made, avoid unnecessary db accesses |
671 | my $changed = 0; # keep track of any changes, if none are made, avoid unnecessary db accesses |
| 642 | foreach my $field ( @{ USER_PROBLEM_FIELDS() } ) { |
672 | foreach my $field ( keys %useful ) { |
| 643 | next unless canChange($forUsers, $field); |
673 | next unless canChange($forUsers, $field); |
| 644 | next unless $useful{$field}; |
674 | |
| 645 | |
|
|
| 646 | my $param = $r->param("problem.$problemID.$field"); |
675 | my $param = $r->param("problem.$problemID.$field"); |
| 647 | $param = $properties{$field}->{default} || "" unless defined $param && $param ne ""; |
676 | $param = $properties{$field}->{default} || "" unless defined $param && $param ne ""; |
| 648 | $param = $undoLabels{$field}->{$param} || $param; |
677 | $param = $undoLabels{$field}->{$param} || $param; |
| 649 | $changed ||= changed($record->$field, $param); |
678 | $changed ||= changed($record->$field, $param); |
| 650 | $record->$field($param); |
679 | $record->$field($param); |
| … | |
… | |
| 662 | |
691 | |
| 663 | # Sets the specified header to "" so that the default file will get used. |
692 | # Sets the specified header to "" so that the default file will get used. |
| 664 | foreach my $header ($r->param('defaultHeader')) { |
693 | foreach my $header ($r->param('defaultHeader')) { |
| 665 | $setRecord->$header(""); |
694 | $setRecord->$header(""); |
| 666 | } |
695 | } |
| 667 | } elsif (defined $r->param('undo_changes')) { |
|
|
| 668 | |
696 | |
| 669 | # reset all the parameters dealing with set/problem/header information |
697 | # Mark the specified problems as correct for all users |
| 670 | # if the current naming scheme is changed/broken, this could reek havoc |
698 | foreach my $problemID ($r->param('markCorrect')) { |
| 671 | # on all kinds of things |
699 | my @userProblemIDs = map { [$_, $setID, $problemID] } ($forUsers ? @editForUser : $db->listProblemUsers($setID, $problemID)); |
| 672 | foreach my $param ($r->param) { |
700 | my @userProblemRecords = $db->getUserProblems(@userProblemIDs); |
| 673 | $r->param($param, "") if $param =~ /^(set|problem|header)\./; |
701 | foreach my $record (@userProblemRecords) { |
|
|
702 | $self->addbadmessage($record->user_id); |
|
|
703 | if (defined $record && ($record->status eq "" || $record->status < 1)) { |
|
|
704 | $record->status(1); |
|
|
705 | $record->attempted(1); |
|
|
706 | $db->putUserProblem($record); |
|
|
707 | } |
| 674 | } |
708 | } |
| 675 | } |
709 | } |
|
|
710 | } |
| 676 | |
711 | |
| 677 | # Leftover code from when there were up/down buttons |
712 | # Leftover code from when there were up/down buttons |
| 678 | |
713 | |
| 679 | # } else { |
714 | # } else { |
| 680 | # # Look for up and down buttons |
715 | # # Look for up and down buttons |
| … | |
… | |
| 693 | # } |
728 | # } |
| 694 | # $index++; |
729 | # $index++; |
| 695 | # } |
730 | # } |
| 696 | # } |
731 | # } |
| 697 | |
732 | |
| 698 | |
|
|
| 699 | |
733 | |
| 700 | # handle renumbering of problems if necessary |
734 | # This erases any sticky fields if the user saves changes, resets the form, or reorders problems |
| 701 | print CGI::a({name=>"problems"}); |
735 | # It may not be obvious why this is necessary when saving changes or reordering problems |
| 702 | |
736 | # but when the problems are reorder the param problem.1.source_file needs to be the source |
| 703 | my %newProblemNumbers = (); |
737 | # file of the problem that is NOW #1 and not the problem that WAS #1. |
| 704 | my $maxProblemNumber = -1; |
738 | unless (defined $r->param('refresh')) { |
| 705 | for my $jj ($db->listGlobalProblems($setID)) { |
739 | |
| 706 | $newProblemNumbers{$jj} = $r->param('problem_num_' . $jj); |
740 | # reset all the parameters dealing with set/problem/header information |
| 707 | $maxProblemNumber = $jj if $jj > $maxProblemNumber; |
741 | # if the current naming scheme is changed/broken, this could reek havoc |
|
|
742 | # on all kinds of things |
|
|
743 | foreach my $param ($r->param) { |
|
|
744 | $r->param($param, "") if $param =~ /^(set|problem|header)\./; |
| 708 | } |
745 | } |
| 709 | |
746 | } |
| 710 | my $forceRenumber = $r->param('force_renumber') || 0; |
747 | |
| 711 | handle_problem_numbers(\%newProblemNumbers, $maxProblemNumber, $db, $setID, $forceRenumber); |
|
|
| 712 | $self->{maxProblemNumber} = $maxProblemNumber; |
|
|
| 713 | } |
748 | } |
| 714 | |
749 | |
| 715 | # helper method for debugging |
750 | # helper method for debugging |
| 716 | sub debug ($) { |
751 | sub definedness ($) { |
| 717 | my ($variable) = @_; |
752 | my ($variable) = @_; |
| 718 | |
753 | |
| 719 | return "undefined" unless defined $variable; |
754 | return "undefined" unless defined $variable; |
| 720 | return "empty" unless $variable ne ""; |
755 | return "empty" unless $variable ne ""; |
| 721 | return $variable; |
756 | return $variable; |
| … | |
… | |
| 782 | my $db = $r->db; |
817 | my $db = $r->db; |
| 783 | my $ce = $r->ce; |
818 | my $ce = $r->ce; |
| 784 | my $authz = $r->authz; |
819 | my $authz = $r->authz; |
| 785 | my $userID = $r->param('user'); |
820 | my $userID = $r->param('user'); |
| 786 | my $urlpath = $r->urlpath; |
821 | my $urlpath = $r->urlpath; |
| 787 | my $courseID = $urlpath->arg("courseID"); |
822 | my $courseID = $urlpath->arg("courseID"); |
| 788 | my $setID = $urlpath->arg("setID"); |
823 | my $setID = $urlpath->arg("setID"); |
| 789 | my $setRecord = $db->getGlobalSet($setID); |
824 | my $setRecord = $db->getGlobalSet($setID) or die "No record for global set $setID."; |
| 790 | die "Global set $setID not found." unless $setRecord; |
825 | |
|
|
826 | my $userRecord = $db->getUser($userID) or die "No record for user $userID."; |
|
|
827 | # Check permissions |
|
|
828 | return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") |
|
|
829 | unless $authz->hasPermissions($userRecord->user_id, "access_instructor_tools"); |
|
|
830 | |
|
|
831 | return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify problems.") |
|
|
832 | unless $authz->hasPermissions($userRecord->user_id, "modify_problem_sets"); |
|
|
833 | |
| 791 | my @editForUser = $r->param('editForUser'); |
834 | my @editForUser = $r->param('editForUser'); |
| 792 | |
835 | |
|
|
836 | # Check that every user that we're editing for has a valid UserSet |
|
|
837 | my @assignedUsers; |
|
|
838 | my @unassignedUsers; |
|
|
839 | if (scalar @editForUser) { |
|
|
840 | foreach my $ID (@editForUser) { |
|
|
841 | if ($db->getUserSet($ID, $setID)) { |
|
|
842 | unshift @assignedUsers, $ID; |
|
|
843 | } else { |
|
|
844 | unshift @unassignedUsers, $ID; |
|
|
845 | } |
|
|
846 | } |
|
|
847 | @editForUser = @assignedUsers; |
|
|
848 | $r->param("editForUser", \@editForUser); |
|
|
849 | |
|
|
850 | if (scalar @editForUser && scalar @unassignedUsers) { |
|
|
851 | print CGI::div({class=>"ResultsWithError"}, "The following users are NOT assigned to this set and will be ignored: " . CGI::b(join(", ", @unassignedUsers))); |
|
|
852 | } elsif (scalar @editForUser == 0) { |
|
|
853 | print CGI::div({class=>"ResultsWithError"}, "None of the selected users are assigned to this set: " . CGI::b(join(", ", @unassignedUsers))); |
|
|
854 | print CGI::div({class=>"ResultsWithError"}, "Global set data will be shown instead of user specific data"); |
|
|
855 | } |
|
|
856 | } |
|
|
857 | |
| 793 | # some useful booleans |
858 | # some useful booleans |
| 794 | my $forUsers = scalar(@editForUser); |
859 | my $forUsers = scalar(@editForUser); |
| 795 | my $forOneUser = $forUsers == 1; |
860 | my $forOneUser = $forUsers == 1; |
| 796 | |
861 | |
| 797 | # If you're editing for users, initially they're records will be different but |
862 | # If you're editing for users, initially their records will be different but |
| 798 | # if you make any changes to them they will be the same. |
863 | # if you make any changes to them they will be the same. |
| 799 | # if you're editing for one user, the problems shown should be his/hers |
864 | # if you're editing for one user, the problems shown should be his/hers |
| 800 | my $userToShow = $forUsers ? $editForUser[0] : $userID; |
865 | my $userToShow = $forUsers ? $editForUser[0] : $userID; |
| 801 | |
|
|
| 802 | # Check permissions |
|
|
| 803 | return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") |
|
|
| 804 | unless $authz->hasPermissions($r->param("user"), "access_instructor_tools"); |
|
|
| 805 | |
866 | |
| 806 | return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify problems.") |
|
|
| 807 | unless $authz->hasPermissions($r->param("user"), "modify_problem_sets"); |
|
|
| 808 | |
|
|
| 809 | |
|
|
| 810 | |
|
|
| 811 | |
|
|
| 812 | my $userCount = $db->listUsers(); |
867 | my $userCount = $db->listUsers(); |
| 813 | my $setCount = $db->listGlobalSets() if $forOneUser; |
868 | my $setCount = $db->listGlobalSets() if $forOneUser; |
| 814 | my $setUserCount = $db->countSetUsers($setID); |
869 | my $setUserCount = $db->countSetUsers($setID); |
| 815 | my $userSetCount = $db->countUserSets($editForUser[0]) if $forOneUser; |
870 | my $userSetCount = $db->countUserSets($editForUser[0]) if $forOneUser; |
|
|
871 | |
|
|
872 | |
| 816 | my $editUsersAssignedToSetURL = $self->systemLink( |
873 | my $editUsersAssignedToSetURL = $self->systemLink( |
| 817 | $urlpath->newFromModule( |
874 | $urlpath->newFromModule( |
| 818 | "WeBWorK::ContentGenerator::Instructor::UsersAssignedToSet", |
875 | "WeBWorK::ContentGenerator::Instructor::UsersAssignedToSet", |
| 819 | courseID => $courseID, setID => $setID)); |
876 | courseID => $courseID, setID => $setID)); |
| 820 | my $editSetsAssignedToUserURL = $self->systemLink( |
877 | my $editSetsAssignedToUserURL = $self->systemLink( |
| … | |
… | |
| 840 | } |
897 | } |
| 841 | } else { |
898 | } else { |
| 842 | print CGI::p($userCountMessage); |
899 | print CGI::p($userCountMessage); |
| 843 | } |
900 | } |
| 844 | |
901 | |
| 845 | |
902 | # handle renumbering of problems if necessary |
|
|
903 | print CGI::a({name=>"problems"}); |
|
|
904 | |
|
|
905 | my %newProblemNumbers = (); |
|
|
906 | my $maxProblemNumber = -1; |
|
|
907 | for my $jj (sort { $a <=> $b } $db->listGlobalProblems($setID)) { |
|
|
908 | $newProblemNumbers{$jj} = $r->param('problem_num_' . $jj); |
|
|
909 | $maxProblemNumber = $jj if $jj > $maxProblemNumber; |
|
|
910 | } |
|
|
911 | |
|
|
912 | my $forceRenumber = $r->param('force_renumber') || 0; |
|
|
913 | handle_problem_numbers(\%newProblemNumbers, $maxProblemNumber, $db, $setID, $forceRenumber) unless defined $r->param('undo_changes'); |
| 846 | |
914 | |
| 847 | my %properties = %{ FIELD_PROPERTIES() }; |
915 | my %properties = %{ FIELD_PROPERTIES() }; |
| 848 | |
916 | |
| 849 | my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()}; |
917 | my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()}; |
| 850 | my @active_modes = grep { exists $display_modes{$_} } @{$r->ce->{pg}->{displayModes}}; |
918 | my @active_modes = grep { exists $display_modes{$_} } @{$r->ce->{pg}->{displayModes}}; |
| … | |
… | |
| 995 | |
1063 | |
| 996 | ##################################################################### |
1064 | ##################################################################### |
| 997 | # Display problem information |
1065 | # Display problem information |
| 998 | ##################################################################### |
1066 | ##################################################################### |
| 999 | |
1067 | |
| 1000 | my @problemIDList = $db->listGlobalProblems($setID); |
1068 | my @problemIDList = sort { $a <=> $b } $db->listGlobalProblems($setID); |
| 1001 | if (scalar @problemIDList) { |
1069 | if (scalar @problemIDList) { |
| 1002 | |
|
|
| 1003 | my $maxProblemNumber = $self->{maxProblemNumber}; |
|
|
| 1004 | |
1070 | |
| 1005 | print CGI::start_table({border=>1, cellpadding=>4}); |
1071 | print CGI::start_table({border=>1, cellpadding=>4}); |
| 1006 | print CGI::Tr({}, CGI::th({}, [ |
1072 | print CGI::Tr({}, CGI::th({}, [ |
| 1007 | "Problems", |
1073 | "Problems", |
| 1008 | "Data", |
1074 | "Data", |
| … | |
… | |
| 1019 | if ($forOneUser) { |
1085 | if ($forOneUser) { |
| 1020 | $problemRecord = $db->getMergedProblem($editForUser[0], $setID, $problemID); |
1086 | $problemRecord = $db->getMergedProblem($editForUser[0], $setID, $problemID); |
| 1021 | } else { |
1087 | } else { |
| 1022 | $problemRecord = $db->getGlobalProblem($setID, $problemID); |
1088 | $problemRecord = $db->getGlobalProblem($setID, $problemID); |
| 1023 | } |
1089 | } |
|
|
1090 | |
|
|
1091 | #$self->addgoodmessage(""); |
|
|
1092 | #$self->addbadmessage($problemRecord->toString()); |
|
|
1093 | |
| 1024 | |
1094 | |
| 1025 | my $editProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID => $problemID }); |
1095 | my $editProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID => $problemID }); |
| 1026 | my $editProblemLink = $self->systemLink($editProblemPage, params => { make_local_copy => 0 }); |
1096 | my $editProblemLink = $self->systemLink($editProblemPage, params => { make_local_copy => 0 }); |
| 1027 | |
1097 | |
| 1028 | # FIXME: should we have an "act as" type link here when editing for multiple users? |
1098 | # FIXME: should we have an "act as" type link here when editing for multiple users? |
| … | |
… | |
| 1059 | CGI::Tr({}, CGI::td({}, problem_number_popup($problemID, $maxProblemNumber))) . |
1129 | CGI::Tr({}, CGI::td({}, problem_number_popup($problemID, $maxProblemNumber))) . |
| 1060 | CGI::Tr({}, CGI::td({}, CGI::a({href => $editProblemLink}, "Edit it"))) . |
1130 | CGI::Tr({}, CGI::td({}, CGI::a({href => $editProblemLink}, "Edit it"))) . |
| 1061 | CGI::Tr({}, CGI::td({}, CGI::a({href => $viewProblemLink}, "Try it" . ($forOneUser ? " (as $editForUser[0])" : "")))) . |
1131 | CGI::Tr({}, CGI::td({}, CGI::a({href => $viewProblemLink}, "Try it" . ($forOneUser ? " (as $editForUser[0])" : "")))) . |
| 1062 | ($forUsers ? "" : CGI::Tr({}, CGI::td({}, CGI::checkbox({name => "deleteProblem", value => $problemID, label => "Delete it?"})))) . |
1132 | ($forUsers ? "" : CGI::Tr({}, CGI::td({}, CGI::checkbox({name => "deleteProblem", value => $problemID, label => "Delete it?"})))) . |
| 1063 | # CGI::Tr({}, CGI::td({}, "Delete it?" . CGI::input({type => "checkbox", name => "deleteProblem", value => $problemID}))) . |
1133 | # CGI::Tr({}, CGI::td({}, "Delete it?" . CGI::input({type => "checkbox", name => "deleteProblem", value => $problemID}))) . |
|
|
1134 | ($forOneUser ? "" : CGI::Tr({}, CGI::td({}, CGI::checkbox({name => "markCorrect", value => $problemID, label => "Mark Correct?"})))) . |
| 1064 | CGI::end_table(), |
1135 | CGI::end_table(), |
| 1065 | $self->FieldTable($userToShow, $setID, $problemID), |
1136 | $self->FieldTable($userToShow, $setID, $problemID), |
| 1066 | # A comprehensive list of problems is just TOO big to be handled well |
1137 | # A comprehensive list of problems is just TOO big to be handled well |
| 1067 | # comboBox({ |
1138 | # comboBox({ |
| 1068 | # name => "set.$setID.$problemID", |
1139 | # name => "set.$setID.$problemID", |
| … | |
… | |
| 1087 | label=> "Force problems to be numbered consecutively from one", |
1158 | label=> "Force problems to be numbered consecutively from one", |
| 1088 | name=>"force_renumber", value=>"1"}), |
1159 | name=>"force_renumber", value=>"1"}), |
| 1089 | |
1160 | |
| 1090 | CGI::br(); |
1161 | CGI::br(); |
| 1091 | print CGI::input({type=>"submit", name=>"submit_changes", value=>"Save Changes"}); |
1162 | print CGI::input({type=>"submit", name=>"submit_changes", value=>"Save Changes"}); |
|
|
1163 | print CGI::input({type=>"submit", name=>"handle_numbers", value=>"Reorder problems only"}) . "(Any unsaved changes will be lost.)"; |
| 1092 | print CGI::p(<<HERE); |
1164 | print CGI::p(<<HERE); |
| 1093 | Any time problem numbers are intentionally changed, the problems will |
1165 | Any time problem numbers are intentionally changed, the problems will |
| 1094 | always be renumbered consecutively, starting from one. When deleting |
1166 | always be renumbered consecutively, starting from one. When deleting |
| 1095 | problems, gaps will be left in the numbering unless the box above is |
1167 | problems, gaps will be left in the numbering unless the box above is |
| 1096 | checked. |
1168 | checked. |