| … | |
… | |
| 83 | use WeBWorK::Utils qw(readFile listFilesRecursive cryptPassword sortByName); |
83 | use WeBWorK::Utils qw(readFile listFilesRecursive cryptPassword sortByName); |
| 84 | |
84 | |
| 85 | use constant HIDE_SETS_THRESHOLD => 50; |
85 | use constant HIDE_SETS_THRESHOLD => 50; |
| 86 | use constant DEFAULT_PUBLISHED_STATE => 1; |
86 | use constant DEFAULT_PUBLISHED_STATE => 1; |
| 87 | |
87 | |
| 88 | use constant EDIT_FORMS => [qw(cancelEdit saveEdit)]; |
88 | use constant EDIT_FORMS => [qw(cancelEdit saveEdit duplicate)]; |
| 89 | use constant VIEW_FORMS => [qw(filter sort edit publish import export score create delete)]; |
89 | use constant VIEW_FORMS => [qw(filter sort edit publish import export score create delete)]; |
| 90 | use constant EXPORT_FORMS => [qw(cancelExport saveExport)]; |
90 | use constant EXPORT_FORMS => [qw(cancelExport saveExport)]; |
| 91 | |
91 | |
| 92 | use constant VIEW_FIELD_ORDER => [ qw( select set_id problems users published open_date due_date answer_date set_header hardcopy_header) ]; |
92 | use constant VIEW_FIELD_ORDER => [ qw( select set_id problems users published open_date due_date answer_date) ]; |
| 93 | use constant EDIT_FIELD_ORDER => [ qw( set_id published open_date due_date answer_date set_header hardcopy_header) ]; |
93 | use constant EDIT_FIELD_ORDER => [ qw( set_id published open_date due_date answer_date) ]; |
| 94 | use constant EXPORT_FIELD_ORDER => [ qw( select set_id filename) ]; |
94 | use constant EXPORT_FIELD_ORDER => [ qw( select set_id filename) ]; |
| 95 | |
95 | |
| 96 | # permissions needed to perform a given action |
96 | # permissions needed to perform a given action |
| 97 | use constant FORM_PERMS => { |
97 | use constant FORM_PERMS => { |
| 98 | saveEdit => "modify_problem_sets", |
98 | saveEdit => "modify_problem_sets", |
| … | |
… | |
| 132 | access => "readonly", |
132 | access => "readonly", |
| 133 | }, |
133 | }, |
| 134 | set_header => { |
134 | set_header => { |
| 135 | type => "filelist", |
135 | type => "filelist", |
| 136 | size => 10, |
136 | size => 10, |
| 137 | access => "readwrite", |
137 | access => "readonly", |
| 138 | }, |
138 | }, |
| 139 | hardcopy_header => { |
139 | hardcopy_header => { |
| 140 | type => "filelist", |
140 | type => "filelist", |
| 141 | size => 10, |
141 | size => 10, |
| 142 | access => "readwrite", |
142 | access => "readonly", |
| 143 | }, |
143 | }, |
| 144 | open_date => { |
144 | open_date => { |
| 145 | type => "text", |
145 | type => "text", |
| 146 | size => 20, |
146 | size => 20, |
| 147 | access => "readwrite", |
147 | access => "readwrite", |
| … | |
… | |
| 879 | CGI::textfield( |
879 | CGI::textfield( |
| 880 | -name => "action.create.name", |
880 | -name => "action.create.name", |
| 881 | -value => $actionParams{"action.create.name"}->[0] || "", |
881 | -value => $actionParams{"action.create.name"}->[0] || "", |
| 882 | -width => "50", |
882 | -width => "50", |
| 883 | -onchange => $onChange, |
883 | -onchange => $onChange, |
|
|
884 | ), |
|
|
885 | " as ", |
|
|
886 | CGI::popup_menu( |
|
|
887 | -name => "action.create.type", |
|
|
888 | -values => [qw(empty copy)], |
|
|
889 | -default => $actionParams{"action.create.type"}->[0] || "empty", |
|
|
890 | -labels => { |
|
|
891 | empty => "a new empty set.", |
|
|
892 | copy => "a duplicate of the first selected set.", |
|
|
893 | }, |
|
|
894 | -onchange => $onChange, |
| 884 | ); |
895 | ); |
|
|
896 | |
| 885 | } |
897 | } |
| 886 | |
898 | |
| 887 | sub create_handler { |
899 | sub create_handler { |
| 888 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
900 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
| 889 | |
901 | |
| 890 | my $r = $self->r; |
902 | my $r = $self->r; |
| 891 | my $db = $r->db; |
903 | my $db = $r->db; |
| 892 | |
904 | |
| 893 | my $newSetRecord = $db->newGlobalSet; |
905 | my $newSetRecord = $db->newGlobalSet; |
|
|
906 | my $oldSetID = $self->{selectedSetIDs}->[0]; |
| 894 | my $newSetName = $actionParams->{"action.create.name"}->[0]; |
907 | my $newSetID = $actionParams->{"action.create.name"}->[0]; |
| 895 | return CGI::div({class => "ResultsWithError"}, "Failed to create new set: no set name specified!") unless $newSetName =~ /\S/; |
908 | return CGI::div({class => "ResultsWithError"}, "Failed to create new set: no set name specified!") unless $newSetID =~ /\S/; |
|
|
909 | |
|
|
910 | my $type = $actionParams->{"action.create.type"}->[0]; |
|
|
911 | if ($type eq "empty") { |
| 896 | $newSetRecord->set_id($newSetName); |
912 | $newSetRecord->set_id($newSetID); |
| 897 | $newSetRecord->set_header(""); |
913 | $newSetRecord->set_header(""); |
| 898 | $newSetRecord->hardcopy_header(""); |
914 | $newSetRecord->hardcopy_header(""); |
| 899 | $newSetRecord->open_date("0"); |
915 | $newSetRecord->open_date("0"); |
| 900 | $newSetRecord->due_date("0"); |
916 | $newSetRecord->due_date("0"); |
| 901 | $newSetRecord->answer_date("0"); |
917 | $newSetRecord->answer_date("0"); |
| 902 | $newSetRecord->published(DEFAULT_PUBLISHED_STATE); # don't want students to see an empty set |
918 | $newSetRecord->published(DEFAULT_PUBLISHED_STATE); # don't want students to see an empty set |
| 903 | eval {$db->addGlobalSet($newSetRecord)}; |
919 | eval {$db->addGlobalSet($newSetRecord)}; |
|
|
920 | } elsif ($type eq "copy") { |
|
|
921 | return CGI::div({class => "ResultsWithError"}, "Failed to duplicate set: no set selected for duplication!") unless $oldSetID =~ /\S/; |
|
|
922 | $newSetRecord = $db->getGlobalSet($oldSetID); |
|
|
923 | $newSetRecord->set_id($newSetID); |
|
|
924 | eval {$db->addGlobalSet($newSetID)}; |
|
|
925 | |
|
|
926 | # take all the problems from the old set and make them part of the new set |
|
|
927 | foreach ($db->getAllGlobalProblems($oldSetID)) { |
|
|
928 | $_->set_id($newSetID); |
|
|
929 | $db->addGlobalProblem($_); |
|
|
930 | } |
|
|
931 | } |
|
|
932 | |
|
|
933 | push @{ $self->{visibleSetIDs} }, $newSetID; |
|
|
934 | push @{ $self->{allSetIds} }, $newSetID; |
| 904 | |
935 | |
| 905 | return CGI::div({class => "ResultsWithError"}, "Failed to create new set: $@") if $@; |
936 | return CGI::div({class => "ResultsWithError"}, "Failed to create new set: $@") if $@; |
| 906 | |
937 | |
| 907 | return "Successfully created new set $newSetName"; |
938 | return "Successfully created new set $newSetID"; |
| 908 | |
939 | |
| 909 | } |
940 | } |
| 910 | |
941 | |
| 911 | sub import_form { |
942 | sub import_form { |
| 912 | my ($self, $onChange, %actionParams) = @_; |
943 | my ($self, $onChange, %actionParams) = @_; |
| … | |
… | |
| 987 | |
1018 | |
| 988 | my $numAdded = @$added; |
1019 | my $numAdded = @$added; |
| 989 | my $numSkipped = @$skipped; |
1020 | my $numSkipped = @$skipped; |
| 990 | |
1021 | |
| 991 | return $numAdded . " set" . ($numAdded == 1 ? "" : "s") . " added, " |
1022 | return $numAdded . " set" . ($numAdded == 1 ? "" : "s") . " added, " |
| 992 | . $numSkipped . " set" . ($numSkipped == 1 ? "" : "s") . " skipped."; |
1023 | . $numSkipped . " set" . ($numSkipped == 1 ? "" : "s") . " skipped" |
|
|
1024 | . " (" . join (", ", @$skipped) . ") "; |
| 993 | } |
1025 | } |
| 994 | |
1026 | |
| 995 | sub export_form { |
1027 | sub export_form { |
| 996 | my ($self, $onChange, %actionParams) = @_; |
1028 | my ($self, $onChange, %actionParams) = @_; |
| 997 | |
1029 | |
| … | |
… | |
| 1169 | } |
1201 | } |
| 1170 | |
1202 | |
| 1171 | $self->{editMode} = 0; |
1203 | $self->{editMode} = 0; |
| 1172 | |
1204 | |
| 1173 | return "changes saved"; |
1205 | return "changes saved"; |
|
|
1206 | } |
|
|
1207 | |
|
|
1208 | sub duplicate_form { |
|
|
1209 | my ($self, $onChange, %actionParams) = @_; |
|
|
1210 | |
|
|
1211 | return join ("", |
|
|
1212 | "Duplicate this set and name it: ", |
|
|
1213 | CGI::textfield( |
|
|
1214 | -name => "action.duplicate.name", |
|
|
1215 | -value => $actionParams{"action.duplicate.name"}->[0] || "", |
|
|
1216 | -width => "50", |
|
|
1217 | -onchange => $onChange, |
|
|
1218 | ), |
|
|
1219 | ); |
|
|
1220 | } |
|
|
1221 | |
|
|
1222 | sub duplicate_handler { |
|
|
1223 | my ($self, $genericParams, $actionParams, $tableParams) = @_; |
|
|
1224 | |
|
|
1225 | my $r = $self->r; |
|
|
1226 | my $db = $r->db; |
|
|
1227 | |
|
|
1228 | my $oldSetID = $self->{selectedSetIDs}->[0]; |
|
|
1229 | return CGI::div({class => "ResultsWithError"}, "Failed to duplicate set: no set selected for duplication!") unless $oldSetID =~ /\S/; |
|
|
1230 | my $newSetID = $actionParams->{"action.duplicate.name"}->[0]; |
|
|
1231 | return CGI::div({class => "ResultsWithError"}, "Failed to duplicate set: no set name specified!") unless $newSetID =~ /\S/; |
|
|
1232 | return CGI::div({class => "ResultsWithError"}, "Failed to duplicate set: set $newSetID already exists!") if defined $db->getGlobalSet($newSetID); |
|
|
1233 | |
|
|
1234 | my $newSet = $db->getGlobalSet($oldSetID); |
|
|
1235 | $newSet->set_id($newSetID); |
|
|
1236 | eval {$db->addGlobalSet($newSet)}; |
|
|
1237 | |
|
|
1238 | # take all the problems from the old set and make them part of the new set |
|
|
1239 | foreach ($db->getAllGlobalProblems($oldSetID)) { |
|
|
1240 | $_->set_id($newSetID); |
|
|
1241 | $db->addGlobalProblem($_); |
|
|
1242 | } |
|
|
1243 | |
|
|
1244 | push @{ $self->{visibleSetIDs} }, $newSetID; |
|
|
1245 | |
|
|
1246 | return CGI::div({class => "ResultsWithError"}, "Failed to duplicate set: $@") if $@; |
|
|
1247 | |
|
|
1248 | return "SUCCESS"; |
| 1174 | } |
1249 | } |
| 1175 | |
1250 | |
| 1176 | ################################################################################ |
1251 | ################################################################################ |
| 1177 | # sorts |
1252 | # sorts |
| 1178 | ################################################################################ |
1253 | ################################################################################ |
| … | |
… | |
| 1442 | |
1517 | |
| 1443 | my $openDate = $self->formatDateTime($setRecord->open_date); |
1518 | my $openDate = $self->formatDateTime($setRecord->open_date); |
| 1444 | my $dueDate = $self->formatDateTime($setRecord->due_date); |
1519 | my $dueDate = $self->formatDateTime($setRecord->due_date); |
| 1445 | my $answerDate = $self->formatDateTime($setRecord->answer_date); |
1520 | my $answerDate = $self->formatDateTime($setRecord->answer_date); |
| 1446 | my $setHeader = $setRecord->set_header; |
1521 | my $setHeader = $setRecord->set_header; |
|
|
1522 | my $paperHeader = $setRecord->hardcopy_header; |
| 1447 | my @problemList = $db->listGlobalProblems($set); |
1523 | my @problemList = $db->listGlobalProblems($set); |
| 1448 | |
1524 | |
| 1449 | my $problemList = ''; |
1525 | my $problemList = ''; |
| 1450 | foreach my $prob (sort {$a <=> $b} @problemList) { |
1526 | foreach my $prob (sort {$a <=> $b} @problemList) { |
| 1451 | my $problemRecord = $db->getGlobalProblem($set, $prob); # checked |
1527 | my $problemRecord = $db->getGlobalProblem($set, $prob); # checked |
| … | |
… | |
| 1462 | my $fileContents = <<EOF; |
1538 | my $fileContents = <<EOF; |
| 1463 | |
1539 | |
| 1464 | openDate = $openDate |
1540 | openDate = $openDate |
| 1465 | dueDate = $dueDate |
1541 | dueDate = $dueDate |
| 1466 | answerDate = $answerDate |
1542 | answerDate = $answerDate |
| 1467 | paperHeaderFile = $setHeader |
1543 | paperHeaderFile = $paperHeader |
| 1468 | screenHeaderFile = $setHeader |
1544 | screenHeaderFile = $setHeader |
| 1469 | problemList = |
1545 | problemList = |
| 1470 | |
1546 | |
| 1471 | $problemList |
1547 | $problemList |
| 1472 | |
1548 | |
| … | |
… | |
| 1585 | my $users = $db->countSetUsers($Set->set_id); |
1661 | my $users = $db->countSetUsers($Set->set_id); |
| 1586 | my $totalUsers = $self->{totalUsers}; |
1662 | my $totalUsers = $self->{totalUsers}; |
| 1587 | my $problems = $db->listGlobalProblems($Set->set_id); |
1663 | my $problems = $db->listGlobalProblems($Set->set_id); |
| 1588 | |
1664 | |
| 1589 | my $usersAssignedToSetURL = $self->systemLink($urlpath->new(type=>'instructor_users_assigned_to_set', args=>{courseID => $courseName, setID => $Set->set_id} )); |
1665 | my $usersAssignedToSetURL = $self->systemLink($urlpath->new(type=>'instructor_users_assigned_to_set', args=>{courseID => $courseName, setID => $Set->set_id} )); |
| 1590 | my $problemListURL = $self->systemLink($urlpath->new(type=>'instructor_problem_list', args=>{courseID => $courseName, setID => $Set->set_id} )); |
1666 | my $problemListURL = $self->systemLink($urlpath->new(type=>'instructor_set_detail', args=>{courseID => $courseName, setID => $Set->set_id} )); |
| 1591 | my $problemSetListURL = $self->systemLink($urlpath->new(type=>'instructor_set_list', args=>{courseID => $courseName, setID => $Set->set_id})) . "&editMode=1&visible_sets=" . $Set->set_id; |
1667 | my $problemSetListURL = $self->systemLink($urlpath->new(type=>'instructor_set_list', args=>{courseID => $courseName, setID => $Set->set_id})) . "&editMode=1&visible_sets=" . $Set->set_id; |
| 1592 | my $imageURL = $ce->{webworkURLs}->{htdocs}."/images/edit.gif"; |
1668 | my $imageURL = $ce->{webworkURLs}->{htdocs}."/images/edit.gif"; |
| 1593 | my $imageLink = CGI::a({href => $problemSetListURL}, CGI::img({src=>$imageURL, border=>0})); |
1669 | my $imageLink = CGI::a({href => $problemSetListURL}, CGI::img({src=>$imageURL, border=>0})); |
| 1594 | |
1670 | |
| 1595 | my @tableCells; |
1671 | my @tableCells; |
| … | |
… | |
| 1755 | ) unless @Sets; |
1831 | ) unless @Sets; |
| 1756 | } |
1832 | } |
| 1757 | |
1833 | |
| 1758 | 1; |
1834 | 1; |
| 1759 | |
1835 | |
|
|
1836 | =head1 AUTHOR |
|
|
1837 | |
|
|
1838 | Written by Robert Van Dam, toenail (at) cif.rochester.edu |
|
|
1839 | |
|
|
1840 | =cut |