| … | |
… | |
| 329 | |
329 | |
| 330 | # needed for gateway output |
330 | # needed for gateway output |
| 331 | my $gwFields = ''; |
331 | my $gwFields = ''; |
| 332 | # $isGWset will come in undef if we don't need to worry about it |
332 | # $isGWset will come in undef if we don't need to worry about it |
| 333 | $isGWset = 0 if ( ! defined( $isGWset ) ); |
333 | $isGWset = 0 if ( ! defined( $isGWset ) ); |
|
|
334 | # are we editing a set version? |
|
|
335 | my $setVersion = (defined($userRecord) && $userRecord->can("version_id")) ? 1 : 0; |
| 334 | |
336 | |
| 335 | # needed for ip restrictions |
337 | # needed for ip restrictions |
| 336 | my $ipFields = ''; |
338 | my $ipFields = ''; |
| 337 | my $ipDefaults; |
339 | my $ipDefaults; |
| 338 | my $numLocations = 0; |
340 | my $numLocations = 0; |
| … | |
… | |
| 362 | my %properties = %{ FIELD_PROPERTIES()->{$field} }; |
364 | my %properties = %{ FIELD_PROPERTIES()->{$field} }; |
| 363 | |
365 | |
| 364 | # we don't show the ip restriction option if there are |
366 | # we don't show the ip restriction option if there are |
| 365 | # no defined locations, nor the relax_restrict_ip option |
367 | # no defined locations, nor the relax_restrict_ip option |
| 366 | # if we're not restricting ip access |
368 | # if we're not restricting ip access |
| 367 | next if ( $field eq 'restrict_ip' && ! $numLocations ); |
369 | next if ( $field eq 'restrict_ip' && ( ! $numLocations || $setVersion ) ); |
| 368 | next if ($field eq 'relax_restrict_ip' && |
370 | next if ($field eq 'relax_restrict_ip' && |
| 369 | (! $numLocations || |
371 | (! $numLocations || $setVersion || |
| 370 | ($forUsers && $userRecord->restrict_ip eq 'No') || |
372 | ($forUsers && $userRecord->restrict_ip eq 'No') || |
| 371 | (! $forUsers && |
373 | (! $forUsers && |
| 372 | ( $globalRecord->restrict_ip eq '' || |
374 | ( $globalRecord->restrict_ip eq '' || |
| 373 | $globalRecord->restrict_ip eq 'No' ) ) ) ); |
375 | $globalRecord->restrict_ip eq 'No' ) ) ) ); |
| 374 | |
376 | |
| … | |
… | |
| 601 | |
603 | |
| 602 | # finally, figure out what ip selector fields we want to include |
604 | # finally, figure out what ip selector fields we want to include |
| 603 | my @locations = sort {$a cmp $b} ($db->listLocations()); |
605 | my @locations = sort {$a cmp $b} ($db->listLocations()); |
| 604 | $numLocations = @locations; |
606 | $numLocations = @locations; |
| 605 | |
607 | |
|
|
608 | # we don't show ip selector fields if we're editing a set version |
|
|
609 | if ( defined( $userRecord ) && ! $userRecord->can("version_id") ) { |
| 606 | if ( ( ! $forUsers && $globalRecord->restrict_ip && |
610 | if ( ( ! $forUsers && $globalRecord->restrict_ip && |
| 607 | $globalRecord->restrict_ip ne 'No' ) || |
611 | $globalRecord->restrict_ip ne 'No' ) || |
| 608 | ( $forUsers && $userRecord->restrict_ip ne 'No' ) ) { |
612 | ( $forUsers && $userRecord->restrict_ip ne 'No' ) ) { |
| 609 | |
613 | |
| 610 | my @globalLocations = $db->listGlobalSetLocations($setID); |
614 | my @globalLocations = $db->listGlobalSetLocations($setID); |
| 611 | # what ip locations should be selected? |
615 | # what ip locations should be selected? |
| 612 | my @defaultLocations = (); |
616 | my @defaultLocations = (); |
| 613 | if ( $forUsers && |
617 | if ( $forUsers && |
| 614 | ! $db->countUserSetLocations($userID, $setID) ) { |
618 | ! $db->countUserSetLocations($userID, $setID) ) { |
| 615 | @defaultLocations = @globalLocations; |
619 | @defaultLocations = @globalLocations; |
| 616 | $ipOverride = 0; |
620 | $ipOverride = 0; |
| 617 | } elsif ( $forUsers ) { |
621 | } elsif ( $forUsers ) { |
| 618 | @defaultLocations = $db->listUserSetLocations($userID, $setID); |
622 | @defaultLocations = $db->listUserSetLocations($userID, $setID); |
| 619 | $ipOverride = 1; |
623 | $ipOverride = 1; |
| 620 | } else { |
624 | } else { |
| 621 | @defaultLocations = @globalLocations; |
625 | @defaultLocations = @globalLocations; |
| 622 | } |
626 | } |
| 623 | my $ipDefaults = join(', ', @globalLocations); |
627 | my $ipDefaults = join(', ', @globalLocations); |
| 624 | |
628 | |
| 625 | my $ipSelector = CGI::scrolling_list({ |
629 | my $ipSelector = CGI::scrolling_list({ |
| 626 | -name => "set.$setID.selected_ip_locations", |
630 | -name => "set.$setID.selected_ip_locations", |
| 627 | -values => [ @locations ], |
631 | -values => [ @locations ], |
| 628 | -default => [ @defaultLocations ], |
632 | -default => [ @defaultLocations ], |
| 629 | -size => 5, |
633 | -size => 5, |
| 630 | -multiple => 'true'}); |
634 | -multiple => 'true'}); |
| 631 | |
635 | |
| 632 | my $override = ($forUsers) ? |
636 | my $override = ($forUsers) ? |
| 633 | CGI::checkbox({ type => "checkbox", |
637 | CGI::checkbox({ type => "checkbox", |
| 634 | name => "set.$setID.selected_ip_locations.override", |
638 | name => "set.$setID.selected_ip_locations.override", |
| 635 | label => "", |
639 | label => "", |
| 636 | checked => $ipOverride }) : ''; |
640 | checked => $ipOverride }) : ''; |
| 637 | $ipFields .= CGI::Tr({-valign=>'top'}, |
641 | $ipFields .= CGI::Tr({-valign=>'top'}, |
| 638 | CGI::td({}, [ $override, |
642 | CGI::td({}, [ $override, |
| 639 | 'Restrict Locations', |
643 | 'Restrict Locations', |
| 640 | $ipSelector, |
644 | $ipSelector, |
| 641 | $forUsers ? |
645 | $forUsers ? |
| 642 | " $ipDefaults" : '', ] |
646 | " $ipDefaults" : '', ] |
| 643 | ), |
647 | ), |
| 644 | ); |
648 | ); |
|
|
649 | } |
| 645 | } |
650 | } |
| 646 | return($gwFields, $ipFields, $numLocations, $procFields); |
651 | return($gwFields, $ipFields, $numLocations, $procFields); |
| 647 | } |
652 | } |
| 648 | |
653 | |
| 649 | sub proctoredFieldHTML { |
654 | sub proctoredFieldHTML { |
| … | |
… | |
| 854 | my $db = $r->db; |
859 | my $db = $r->db; |
| 855 | my $ce = $r->ce; |
860 | my $ce = $r->ce; |
| 856 | my $authz = $r->authz; |
861 | my $authz = $r->authz; |
| 857 | my $user = $r->param('user'); |
862 | my $user = $r->param('user'); |
| 858 | my $setID = $r->urlpath->arg("setID"); |
863 | my $setID = $r->urlpath->arg("setID"); |
|
|
864 | |
|
|
865 | ## we're now allowing setID to come in as setID,v# to edit a set |
|
|
866 | ## version; catch this first |
|
|
867 | my $editingSetVersion = 0; |
|
|
868 | if ( $setID =~ /,v(\d+)$/ ) { |
|
|
869 | $editingSetVersion = $1; |
|
|
870 | $setID =~ s/,v(\d+)$//; |
|
|
871 | } |
|
|
872 | |
| 859 | my $setRecord = $db->getGlobalSet($setID); # checked |
873 | my $setRecord = $db->getGlobalSet($setID); # checked |
| 860 | die "global set $setID not found." unless $setRecord; |
874 | die "global set $setID not found." unless $setRecord; |
| 861 | |
875 | |
| 862 | $self->{set} = $setRecord; |
876 | $self->{set} = $setRecord; |
| 863 | my @editForUser = $r->param('editForUser'); |
877 | my @editForUser = $r->param('editForUser'); |
| … | |
… | |
| 867 | |
881 | |
| 868 | # Check permissions |
882 | # Check permissions |
| 869 | return unless ($authz->hasPermissions($user, "access_instructor_tools")); |
883 | return unless ($authz->hasPermissions($user, "access_instructor_tools")); |
| 870 | return unless ($authz->hasPermissions($user, "modify_problem_sets")); |
884 | return unless ($authz->hasPermissions($user, "modify_problem_sets")); |
| 871 | |
885 | |
|
|
886 | ## if we're editing a versioned set, it only makes sense to be |
|
|
887 | ## editing it for one user |
|
|
888 | return if ( $editingSetVersion && ! $forOneUser ); |
| 872 | |
889 | |
| 873 | my %properties = %{ FIELD_PROPERTIES() }; |
890 | my %properties = %{ FIELD_PROPERTIES() }; |
| 874 | |
891 | |
| 875 | # takes a hash of hashes and inverts it |
892 | # takes a hash of hashes and inverts it |
| 876 | my %undoLabels; |
893 | my %undoLabels; |
| … | |
… | |
| 952 | # not the most robust treatment of the problem |
969 | # not the most robust treatment of the problem |
| 953 | # (FIXME) |
970 | # (FIXME) |
| 954 | |
971 | |
| 955 | # DBFIXME use a WHERE clause, iterator |
972 | # DBFIXME use a WHERE clause, iterator |
| 956 | my @userRecords = $db->getUserSets(map { [$_, $setID] } @editForUser); |
973 | my @userRecords = $db->getUserSets(map { [$_, $setID] } @editForUser); |
|
|
974 | # if we're editing a set version, we want to edit |
|
|
975 | # edit that instead of the userset, so get it |
|
|
976 | # too. |
|
|
977 | my $userSet = $userRecords[0]; |
|
|
978 | my $setVersion = 0; |
|
|
979 | if ( $editingSetVersion ) { |
|
|
980 | $setVersion = |
|
|
981 | $db->getSetVersion($editForUser[0], |
|
|
982 | $setID, |
|
|
983 | $editingSetVersion); |
|
|
984 | @userRecords = ( $setVersion ); |
|
|
985 | } |
|
|
986 | |
| 957 | foreach my $record (@userRecords) { |
987 | foreach my $record (@userRecords) { |
| 958 | foreach my $field ( @{ SET_FIELDS() } ) { |
988 | foreach my $field ( @{ SET_FIELDS() } ) { |
| 959 | next unless canChange($forUsers, $field); |
989 | next unless canChange($forUsers, $field); |
| 960 | my $override = $r->param("set.$setID.$field.override"); |
990 | my $override = $r->param("set.$setID.$field.override"); |
| 961 | |
991 | |
| … | |
… | |
| 1004 | # $set->hide_score_by_problem eq 'N' |
1034 | # $set->hide_score_by_problem eq 'N' |
| 1005 | # if ( $record->hide_score eq 'N' ) { |
1035 | # if ( $record->hide_score eq 'N' ) { |
| 1006 | # $record->hide_score_by_problem('N'); |
1036 | # $record->hide_score_by_problem('N'); |
| 1007 | # } |
1037 | # } |
| 1008 | #################### |
1038 | #################### |
|
|
1039 | if ( $editingSetVersion ) { |
|
|
1040 | $db->putSetVersion( $record ); |
|
|
1041 | } else { |
| 1009 | $db->putUserSet($record); |
1042 | $db->putUserSet($record); |
|
|
1043 | } |
| 1010 | } |
1044 | } |
| 1011 | |
1045 | |
| 1012 | ####################################################### |
1046 | ####################################################### |
| 1013 | # Save IP restriction Location information |
1047 | # Save IP restriction Location information |
| 1014 | ####################################################### |
1048 | ####################################################### |
| 1015 | # FIXME: it would be nice to have this in the field values |
1049 | # FIXME: it would be nice to have this in the field values |
| 1016 | # hash, so that we don't have to assume that we can |
1050 | # hash, so that we don't have to assume that we can |
| 1017 | # override this information for users |
1051 | # override this information for users |
| 1018 | |
1052 | |
|
|
1053 | ## should we allow resetting set locations for set versions? this |
|
|
1054 | ## requires either putting in a new set of database routines |
|
|
1055 | ## to deal with the versioned setID, or fudging it at this end |
|
|
1056 | ## by manually putting in the versioned ID setID,v#. neither |
|
|
1057 | ## of these seems desirable, so for now it's not allowed |
|
|
1058 | if ( ! $editingSetVersion ) { |
| 1019 | if ( $r->param("set.$setID.selected_ip_locations.override") ) { |
1059 | if ( $r->param("set.$setID.selected_ip_locations.override") ) { |
| 1020 | foreach my $record ( @userRecords ) { |
1060 | foreach my $record ( @userRecords ) { |
| 1021 | my $userID = $record->user_id; |
1061 | my $userID = $record->user_id; |
| 1022 | my @selectedLocations = $r->param("set.$setID.selected_ip_locations"); |
1062 | my @selectedLocations = $r->param("set.$setID.selected_ip_locations"); |
| 1023 | my @userSetLocations = $db->listUserSetLocations($userID,$setID); |
1063 | my @userSetLocations = $db->listUserSetLocations($userID,$setID); |
| 1024 | my @addSetLocations = (); |
1064 | my @addSetLocations = (); |
| 1025 | my @delSetLocations = (); |
1065 | my @delSetLocations = (); |
| 1026 | foreach my $loc ( @selectedLocations ) { |
1066 | foreach my $loc ( @selectedLocations ) { |
| 1027 | push( @addSetLocations, $loc ) if ( ! grep( /^$loc$/, @userSetLocations ) ); |
1067 | push( @addSetLocations, $loc ) if ( ! grep( /^$loc$/, @userSetLocations ) ); |
|
|
1068 | } |
|
|
1069 | foreach my $loc ( @userSetLocations ) { |
|
|
1070 | push( @delSetLocations, $loc ) if ( ! grep( /^$loc$/, @selectedLocations ) ); |
|
|
1071 | } |
|
|
1072 | # then update the user set_locations |
|
|
1073 | foreach ( @addSetLocations ) { |
|
|
1074 | my $Loc = $db->newUserSetLocation; |
|
|
1075 | $Loc->set_id( $setID ); |
|
|
1076 | $Loc->user_id( $userID ); |
|
|
1077 | $Loc->location_id($_); |
|
|
1078 | $db->addUserSetLocation($Loc); |
|
|
1079 | } |
|
|
1080 | foreach ( @delSetLocations ) { |
|
|
1081 | $db->deleteUserSetLocation($userID,$setID,$_); |
|
|
1082 | } |
| 1028 | } |
1083 | } |
| 1029 | foreach my $loc ( @userSetLocations ) { |
|
|
| 1030 | push( @delSetLocations, $loc ) if ( ! grep( /^$loc$/, @selectedLocations ) ); |
|
|
| 1031 | } |
|
|
| 1032 | # then update the user set_locations |
|
|
| 1033 | foreach ( @addSetLocations ) { |
|
|
| 1034 | my $Loc = $db->newUserSetLocation; |
|
|
| 1035 | $Loc->set_id( $setID ); |
|
|
| 1036 | $Loc->user_id( $userID ); |
|
|
| 1037 | $Loc->location_id($_); |
|
|
| 1038 | $db->addUserSetLocation($Loc); |
|
|
| 1039 | } |
|
|
| 1040 | foreach ( @delSetLocations ) { |
|
|
| 1041 | $db->deleteUserSetLocation($userID,$setID,$_); |
|
|
| 1042 | } |
|
|
| 1043 | } |
|
|
| 1044 | } else { |
1084 | } else { |
| 1045 | # if override isn't selected, then we want |
1085 | # if override isn't selected, then we want |
| 1046 | # to be sure that there are no |
1086 | # to be sure that there are no |
| 1047 | # set_locations_user entries setting around |
1087 | # set_locations_user entries setting around |
| 1048 | foreach my $record ( @userRecords ) { |
1088 | foreach my $record ( @userRecords ) { |
| 1049 | my $userID = $record->user_id; |
1089 | my $userID = $record->user_id; |
| 1050 | my @userLocations = $db->listUserSetLocations($userID,$setID); |
1090 | my @userLocations = $db->listUserSetLocations($userID,$setID); |
| 1051 | foreach ( @userLocations ) { |
1091 | foreach ( @userLocations ) { |
| 1052 | $db->deleteUserSetLocation($userID,$setID,$_); |
1092 | $db->deleteUserSetLocation($userID,$setID,$_); |
|
|
1093 | } |
| 1053 | } |
1094 | } |
| 1054 | } |
1095 | } |
| 1055 | } |
1096 | } |
| 1056 | } else { |
1097 | } else { |
| 1057 | foreach my $field ( @{ SET_FIELDS() } ) { |
1098 | foreach my $field ( @{ SET_FIELDS() } ) { |
| … | |
… | |
| 1224 | # Since we're editing for specific users, we don't allow the GlobalProblem record to be altered on that same page |
1265 | # Since we're editing for specific users, we don't allow the GlobalProblem record to be altered on that same page |
| 1225 | # So we only need to make changes to the UserProblem record and only then if we are overriding a value |
1266 | # So we only need to make changes to the UserProblem record and only then if we are overriding a value |
| 1226 | # in the GlobalProblem record or for fields unique to the UserProblem record. |
1267 | # in the GlobalProblem record or for fields unique to the UserProblem record. |
| 1227 | |
1268 | |
| 1228 | my @userIDs = @editForUser; |
1269 | my @userIDs = @editForUser; |
|
|
1270 | |
|
|
1271 | my @userProblemRecords; |
|
|
1272 | if ( ! $editingSetVersion ) { |
| 1229 | my @userProblemIDs = map { [$_, $setID, $problemID] } @userIDs; |
1273 | my @userProblemIDs = map { [$_, $setID, $problemID] } @userIDs; |
| 1230 | # DBFIXME where clause? iterator? |
1274 | # DBFIXME where clause? iterator? |
| 1231 | my @userProblemRecords = $db->getUserProblems(@userProblemIDs); |
1275 | @userProblemRecords = $db->getUserProblems(@userProblemIDs); |
|
|
1276 | } else { |
|
|
1277 | ## (we know that we're only editing for one user) |
|
|
1278 | @userProblemRecords = |
|
|
1279 | ( $db->getMergedProblemVersion( $userIDs[0], $setID, $editingSetVersion, $problemID ) ); |
|
|
1280 | } |
|
|
1281 | |
| 1232 | foreach my $record (@userProblemRecords) { |
1282 | foreach my $record (@userProblemRecords) { |
| 1233 | |
1283 | |
| 1234 | my $changed = 0; # keep track of any changes, if none are made, avoid unnecessary db accesses |
1284 | my $changed = 0; # keep track of any changes, if none are made, avoid unnecessary db accesses |
| 1235 | foreach my $field ( @{ PROBLEM_FIELDS() } ) { |
1285 | foreach my $field ( @{ PROBLEM_FIELDS() } ) { |
| 1236 | next unless canChange($forUsers, $field); |
1286 | next unless canChange($forUsers, $field); |
| … | |
… | |
| 1259 | my $unlabel = $undoLabels{$field}->{$param}; |
1309 | my $unlabel = $undoLabels{$field}->{$param}; |
| 1260 | $param = $unlabel if defined $unlabel; |
1310 | $param = $unlabel if defined $unlabel; |
| 1261 | $changed ||= changed($record->$field, $param); |
1311 | $changed ||= changed($record->$field, $param); |
| 1262 | $record->$field($param); |
1312 | $record->$field($param); |
| 1263 | } |
1313 | } |
|
|
1314 | if ( ! $editingSetVersion ) { |
| 1264 | $db->putUserProblem($record) if $changed; |
1315 | $db->putUserProblem($record) if $changed; |
|
|
1316 | } else { |
|
|
1317 | $db->putProblemVersion($record) if $changed; |
|
|
1318 | } |
| 1265 | } |
1319 | } |
| 1266 | } else { |
1320 | } else { |
| 1267 | # Since we're editing for ALL set users, we will make changes to the GlobalProblem record. |
1321 | # Since we're editing for ALL set users, we will make changes to the GlobalProblem record. |
| 1268 | # We may also have instances where a field is unique to the UserProblem record but we want |
1322 | # We may also have instances where a field is unique to the UserProblem record but we want |
| 1269 | # all users to (at least initially) have the same value |
1323 | # all users to (at least initially) have the same value |
| … | |
… | |
| 1318 | } |
1372 | } |
| 1319 | } |
1373 | } |
| 1320 | } |
1374 | } |
| 1321 | } |
1375 | } |
| 1322 | |
1376 | |
| 1323 | # Mark the specified problems as correct for all users |
1377 | # Mark the specified problems as correct for all users (not applicable when editing a set |
|
|
1378 | # version, because this only shows up when editing for users or editing the |
|
|
1379 | # global set/problem, not for one user) |
| 1324 | foreach my $problemID ($r->param('markCorrect')) { |
1380 | foreach my $problemID ($r->param('markCorrect')) { |
| 1325 | # DBFIXME where clause, iterator |
1381 | # DBFIXME where clause, iterator |
| 1326 | my @userProblemIDs = map { [$_, $setID, $problemID] } ($forUsers ? @editForUser : $db->listProblemUsers($setID, $problemID)); |
1382 | my @userProblemIDs = map { [$_, $setID, $problemID] } ($forUsers ? @editForUser : $db->listProblemUsers($setID, $problemID)); |
| 1327 | # if the set is not a gateway set, this requires going through the |
1383 | # if the set is not a gateway set, this requires going through the |
| 1328 | # user_problems and resetting their status; if it's a gateway set, |
1384 | # user_problems and resetting their status; if it's a gateway set, |
| … | |
… | |
| 1355 | } |
1411 | } |
| 1356 | } |
1412 | } |
| 1357 | } |
1413 | } |
| 1358 | } |
1414 | } |
| 1359 | |
1415 | |
| 1360 | # Delete all problems marked for deletion |
1416 | # Delete all problems marked for deletion (not applicable when editing |
|
|
1417 | # for users) |
| 1361 | foreach my $problemID ($r->param('deleteProblem')) { |
1418 | foreach my $problemID ($r->param('deleteProblem')) { |
| 1362 | $db->deleteGlobalProblem($setID, $problemID); |
1419 | $db->deleteGlobalProblem($setID, $problemID); |
| 1363 | } |
1420 | } |
| 1364 | |
1421 | |
| 1365 | ##################################################################### |
1422 | ##################################################################### |
| … | |
… | |
| 1521 | my $authz = $r->authz; |
1578 | my $authz = $r->authz; |
| 1522 | my $userID = $r->param('user'); |
1579 | my $userID = $r->param('user'); |
| 1523 | my $urlpath = $r->urlpath; |
1580 | my $urlpath = $r->urlpath; |
| 1524 | my $courseID = $urlpath->arg("courseID"); |
1581 | my $courseID = $urlpath->arg("courseID"); |
| 1525 | my $setID = $urlpath->arg("setID"); |
1582 | my $setID = $urlpath->arg("setID"); |
|
|
1583 | |
|
|
1584 | ## we're now allowing setID to come in as setID,v# to edit a set |
|
|
1585 | ## version; catch this first |
|
|
1586 | my $editingSetVersion = 0; |
|
|
1587 | my $fullSetID = $setID; |
|
|
1588 | if ( $setID =~ /,v(\d+)$/ ) { |
|
|
1589 | $editingSetVersion = $1; |
|
|
1590 | $setID =~ s/,v(\d+)$//; |
|
|
1591 | } |
|
|
1592 | |
| 1526 | my $setRecord = $db->getGlobalSet($setID) or die "No record for global set $setID."; |
1593 | my $setRecord = $db->getGlobalSet($setID) or die "No record for global set $setID."; |
| 1527 | |
1594 | |
| 1528 | my $userRecord = $db->getUser($userID) or die "No record for user $userID."; |
1595 | my $userRecord = $db->getUser($userID) or die "No record for user $userID."; |
| 1529 | # Check permissions |
1596 | # Check permissions |
| 1530 | return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") |
1597 | return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") |
| … | |
… | |
| 1532 | |
1599 | |
| 1533 | return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify problems.") |
1600 | return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify problems.") |
| 1534 | unless $authz->hasPermissions($userRecord->user_id, "modify_problem_sets"); |
1601 | unless $authz->hasPermissions($userRecord->user_id, "modify_problem_sets"); |
| 1535 | |
1602 | |
| 1536 | my @editForUser = $r->param('editForUser'); |
1603 | my @editForUser = $r->param('editForUser'); |
|
|
1604 | |
|
|
1605 | return CGI::div({class=>"ResultsWithError"}, "Versions of a set can only be " . |
|
|
1606 | "edited for one user at a time.") if ( $editingSetVersion && @editForUser != 1 ); |
| 1537 | |
1607 | |
| 1538 | # Check that every user that we're editing for has a valid UserSet |
1608 | # Check that every user that we're editing for has a valid UserSet |
| 1539 | my @assignedUsers; |
1609 | my @assignedUsers; |
| 1540 | my @unassignedUsers; |
1610 | my @unassignedUsers; |
| 1541 | if (scalar @editForUser) { |
1611 | if (scalar @editForUser) { |
| … | |
… | |
| 1555 | } elsif (scalar @editForUser == 0) { |
1625 | } elsif (scalar @editForUser == 0) { |
| 1556 | print CGI::div({class=>"ResultsWithError"}, "None of the selected users are assigned to this set: " . CGI::b(join(", ", @unassignedUsers))); |
1626 | print CGI::div({class=>"ResultsWithError"}, "None of the selected users are assigned to this set: " . CGI::b(join(", ", @unassignedUsers))); |
| 1557 | print CGI::div({class=>"ResultsWithError"}, "Global set data will be shown instead of user specific data"); |
1627 | print CGI::div({class=>"ResultsWithError"}, "Global set data will be shown instead of user specific data"); |
| 1558 | } |
1628 | } |
| 1559 | } |
1629 | } |
| 1560 | |
1630 | |
| 1561 | # some useful booleans |
1631 | # some useful booleans |
| 1562 | my $forUsers = scalar(@editForUser); |
1632 | my $forUsers = scalar(@editForUser); |
| 1563 | my $forOneUser = $forUsers == 1; |
1633 | my $forOneUser = $forUsers == 1; |
|
|
1634 | |
|
|
1635 | # and check that if we're editing a set version for a user, that |
|
|
1636 | # it exists as well |
|
|
1637 | if ( $editingSetVersion && ! $db->existsSetVersion( $editForUser[0], $setID, $editingSetVersion ) ) { |
|
|
1638 | return CGI::div({class=>"ResultsWithError"}, "The set-version ($setID, version $editingSetVersion) is not assigned to user $editForUser[0]."); |
|
|
1639 | } |
| 1564 | |
1640 | |
| 1565 | # If you're editing for users, initially their records will be different but |
1641 | # If you're editing for users, initially their records will be different but |
| 1566 | # if you make any changes to them they will be the same. |
1642 | # if you make any changes to them they will be the same. |
| 1567 | # if you're editing for one user, the problems shown should be his/hers |
1643 | # if you're editing for one user, the problems shown should be his/hers |
| 1568 | my $userToShow = $forUsers ? $editForUser[0] : $userID; |
1644 | my $userToShow = $forUsers ? $editForUser[0] : $userID; |
| … | |
… | |
| 1572 | |
1648 | |
| 1573 | # DBFIXME no need to get ID lists -- counts would be fine |
1649 | # DBFIXME no need to get ID lists -- counts would be fine |
| 1574 | my $userCount = $db->listUsers(); |
1650 | my $userCount = $db->listUsers(); |
| 1575 | my $setCount = $db->listGlobalSets(); # if $forOneUser; |
1651 | my $setCount = $db->listGlobalSets(); # if $forOneUser; |
| 1576 | my $setUserCount = $db->countSetUsers($setID); |
1652 | my $setUserCount = $db->countSetUsers($setID); |
| 1577 | my $userSetCount = $db->countUserSets($editForUser[0]) if $forOneUser; |
1653 | # if $forOneUser; |
|
|
1654 | my $userSetCount = ($forOneUser && @editForUser) ? $db->countUserSets($editForUser[0]) : 0; |
| 1578 | |
1655 | |
| 1579 | |
1656 | |
| 1580 | my $editUsersAssignedToSetURL = $self->systemLink( |
1657 | my $editUsersAssignedToSetURL = $self->systemLink( |
| 1581 | $urlpath->newFromModule( |
1658 | $urlpath->newFromModule( |
| 1582 | "WeBWorK::ContentGenerator::Instructor::UsersAssignedToSet", |
1659 | "WeBWorK::ContentGenerator::Instructor::UsersAssignedToSet", |
| … | |
… | |
| 1586 | "WeBWorK::ContentGenerator::Instructor::UserDetail", |
1663 | "WeBWorK::ContentGenerator::Instructor::UserDetail", |
| 1587 | courseID => $courseID, userID => $editForUser[0])) if $forOneUser; |
1664 | courseID => $courseID, userID => $editForUser[0])) if $forOneUser; |
| 1588 | |
1665 | |
| 1589 | |
1666 | |
| 1590 | my $setDetailPage = $urlpath -> newFromModule($urlpath->module, courseID => $courseID, setID => $setID); |
1667 | my $setDetailPage = $urlpath -> newFromModule($urlpath->module, courseID => $courseID, setID => $setID); |
|
|
1668 | my $fullsetDetailPage = $urlpath -> newFromModule($urlpath->module, courseID => $courseID, setID => $fullSetID); |
| 1591 | my $setDetailURL = $self->systemLink($setDetailPage, authen=>0); |
1669 | my $setDetailURL = $self->systemLink($fullsetDetailPage, authen=>0); |
| 1592 | |
|
|
| 1593 | |
1670 | |
| 1594 | my $userCountMessage = CGI::a({href=>$editUsersAssignedToSetURL}, $self->userCountMessage($setUserCount, $userCount)); |
1671 | my $userCountMessage = CGI::a({href=>$editUsersAssignedToSetURL}, $self->userCountMessage($setUserCount, $userCount)); |
| 1595 | my $setCountMessage = CGI::a({href=>$editSetsAssignedToUserURL}, $self->setCountMessage($userSetCount, $setCount)) if $forOneUser; |
1672 | my $setCountMessage = CGI::a({href=>$editSetsAssignedToUserURL}, $self->setCountMessage($userSetCount, $setCount)) if $forOneUser; |
| 1596 | |
1673 | |
| 1597 | $userCountMessage = "The set $setID is assigned to " . $userCountMessage . "."; |
1674 | $userCountMessage = "The set $setID is assigned to " . $userCountMessage . "."; |
| … | |
… | |
| 1601 | ############################################## |
1678 | ############################################## |
| 1602 | # calculate links for the users being edited: |
1679 | # calculate links for the users being edited: |
| 1603 | ############################################## |
1680 | ############################################## |
| 1604 | my @userLinks = (); |
1681 | my @userLinks = (); |
| 1605 | foreach my $userID (@editForUser) { |
1682 | foreach my $userID (@editForUser) { |
| 1606 | my $u = $db->getUser($userID); |
1683 | my $u = $db->getUser($userID); |
| 1607 | my $email_address = $u->email_address; |
1684 | my $email_address = $u->email_address; |
| 1608 | my $line = $u->last_name.", ".$u->first_name." (".CGI::a({-href=>"mailto:$email_address"},"email "). $u->user_id."). Assigned to "; |
1685 | my $line = $u->last_name.", " . $u->first_name . " (" . |
|
|
1686 | CGI::a({-href=>"mailto:$email_address"},"email "). $u->user_id . |
|
|
1687 | "). "; |
|
|
1688 | if ( ! $editingSetVersion ) { |
|
|
1689 | $line .= "Assigned to "; |
| 1609 | my $editSetsAssignedToUserURL = $self->systemLink( |
1690 | my $editSetsAssignedToUserURL = $self->systemLink( |
| 1610 | $urlpath->newFromModule( |
1691 | $urlpath->newFromModule( |
| 1611 | "WeBWorK::ContentGenerator::Instructor::UserDetail", |
1692 | "WeBWorK::ContentGenerator::Instructor::UserDetail", |
| 1612 | courseID => $courseID, userID => $u->user_id)); |
1693 | courseID => $courseID, userID => $u->user_id)); |
| 1613 | $line .= CGI::a({href=>$editSetsAssignedToUserURL}, |
1694 | $line .= CGI::a({href=>$editSetsAssignedToUserURL}, |
| 1614 | $self->setCountMessage($db->countUserSets($u->user_id), $setCount)); |
1695 | $self->setCountMessage($db->countUserSets($u->user_id), |
|
|
1696 | $setCount)); |
|
|
1697 | } else { |
|
|
1698 | my $editSetLink = $self->systemLink( $setDetailPage, |
|
|
1699 | params=>{effectiveUser=>$u->user_id, |
|
|
1700 | editForUser =>$u->user_id} ); |
|
|
1701 | $line .= "Edit set " . CGI::a({href=>$editSetLink},$setID) . |
|
|
1702 | " for this user."; |
|
|
1703 | } |
| 1615 | unshift @userLinks,$line; |
1704 | unshift @userLinks,$line; |
| 1616 | } |
1705 | } |
| 1617 | @userLinks = sort @userLinks; |
1706 | @userLinks = sort @userLinks; |
| 1618 | |
1707 | |
|
|
1708 | # handy messages when editing gateway sets |
|
|
1709 | my $gwmsg = ( $isGatewaySet && ! $editingSetVersion ) ? |
|
|
1710 | CGI::br() . CGI::em("To edit a specific student version of this set, " . |
|
|
1711 | "edit (all of) her/his assigned sets.") : ""; |
|
|
1712 | my $vermsg = ( $editingSetVersion ) ? ", test $editingSetVersion" : ""; |
|
|
1713 | |
| 1619 | print CGI::table({border=>2,cellpadding=>10}, |
1714 | print CGI::table({border=>2,cellpadding=>10}, |
| 1620 | CGI::Tr({}, |
1715 | CGI::Tr({}, |
| 1621 | CGI::td([ |
1716 | CGI::td([ |
| 1622 | "Editing problem set ".CGI::strong($setID)." data for these individual students:".CGI::br(). |
1717 | "Editing problem set ".CGI::strong($setID . $vermsg)." data for these individual students:".CGI::br(). |
| 1623 | CGI::strong(join CGI::br(), @userLinks), |
1718 | CGI::strong(join CGI::br(), @userLinks), |
| 1624 | CGI::a({href=>$self->systemLink($setDetailPage) },"Edit set ".CGI::strong($setID)." data for ALL students assigned to this set."), |
1719 | CGI::a({href=>$self->systemLink($setDetailPage) },"Edit set ".CGI::strong($setID)." data for ALL students assigned to this set.") . $gwmsg, |
| 1625 | |
1720 | |
| 1626 | ]) |
1721 | ]) |
| 1627 | ) |
1722 | ) |
| 1628 | ); |
1723 | ); |
| 1629 | } else { |
1724 | } else { |
| … | |
… | |
| 1711 | |
1806 | |
| 1712 | # this is kind of a hack -- we need to get a user record here, so we can |
1807 | # this is kind of a hack -- we need to get a user record here, so we can |
| 1713 | # pass it to FieldTable, so FieldTable can pass it to FieldHTML, so |
1808 | # pass it to FieldTable, so FieldTable can pass it to FieldHTML, so |
| 1714 | # FieldHTML doesn't have to fetch it itself. |
1809 | # FieldHTML doesn't have to fetch it itself. |
| 1715 | my $userSetRecord = $db->getUserSet($userToShow, $setID); |
1810 | my $userSetRecord = $db->getUserSet($userToShow, $setID); |
|
|
1811 | |
|
|
1812 | my $templateUserSetRecord; |
|
|
1813 | # send in the set version if we're editing for versions |
|
|
1814 | if ( $editingSetVersion ) { |
|
|
1815 | $templateUserSetRecord = $userSetRecord; |
|
|
1816 | $userSetRecord = $db->getSetVersion( $userToShow, $setID, $editingSetVersion ); |
|
|
1817 | } |
| 1716 | |
1818 | |
| 1717 | print CGI::Tr({}, CGI::td({}, [ |
1819 | print CGI::Tr({}, CGI::td({}, [ |
| 1718 | $self->FieldTable($userToShow, $setID, undef, $setRecord, $userSetRecord), |
1820 | $self->FieldTable($userToShow, $setID, undef, $setRecord, $userSetRecord), |
| 1719 | ])); |
1821 | ])); |
| 1720 | print CGI::end_table(); |
1822 | print CGI::end_table(); |
| … | |
… | |
| 1850 | my (%UserProblems, %MergedProblems); |
1952 | my (%UserProblems, %MergedProblems); |
| 1851 | if ($forOneUser) { |
1953 | if ($forOneUser) { |
| 1852 | my @userKeypartsRef = map { [$editForUser[0], $setID, $_] } @problemIDList; |
1954 | my @userKeypartsRef = map { [$editForUser[0], $setID, $_] } @problemIDList; |
| 1853 | # DBFIXME shouldn't need to get key list here |
1955 | # DBFIXME shouldn't need to get key list here |
| 1854 | @UserProblems{@problemIDList} = $db->getUserProblems(@userKeypartsRef); |
1956 | @UserProblems{@problemIDList} = $db->getUserProblems(@userKeypartsRef); |
|
|
1957 | if ( ! $editingSetVersion ) { |
| 1855 | @MergedProblems{@problemIDList} = $db->getMergedProblems(@userKeypartsRef); |
1958 | @MergedProblems{@problemIDList} = $db->getMergedProblems(@userKeypartsRef); |
|
|
1959 | } else { |
|
|
1960 | my @userversionKeypartsRef = map { [$editForUser[0], $setID, $editingSetVersion, $_] } @problemIDList; |
|
|
1961 | @MergedProblems{@problemIDList} = $db->getMergedProblemVersions(@userversionKeypartsRef); |
|
|
1962 | } |
| 1856 | } |
1963 | } |
| 1857 | |
1964 | |
| 1858 | if (scalar @problemIDList) { |
1965 | if (scalar @problemIDList) { |
| 1859 | |
1966 | |
| 1860 | print CGI::start_table({border=>1, cellpadding=>4}); |
1967 | print CGI::start_table({border=>1, cellpadding=>4}); |
| … | |
… | |
| 1877 | $problemRecord = $MergedProblems{$problemID}; # already fetched above --sam |
1984 | $problemRecord = $MergedProblems{$problemID}; # already fetched above --sam |
| 1878 | } else { |
1985 | } else { |
| 1879 | #$problemRecord = $db->getGlobalProblem($setID, $problemID); |
1986 | #$problemRecord = $db->getGlobalProblem($setID, $problemID); |
| 1880 | $problemRecord = $GlobalProblems{$problemID}; # already fetched above --sam |
1987 | $problemRecord = $GlobalProblems{$problemID}; # already fetched above --sam |
| 1881 | } |
1988 | } |
| 1882 | |
1989 | |
| 1883 | #$self->addgoodmessage(""); |
1990 | #$self->addgoodmessage(""); |
| 1884 | #$self->addbadmessage($problemRecord->toString()); |
1991 | #$self->addbadmessage($problemRecord->toString()); |
| 1885 | |
1992 | |
| 1886 | |
1993 | # when we're editing a set version, we want to be sure to |
|
|
1994 | # use the merged problem in the edit, because we could |
|
|
1995 | # be using problem groups (for which the problem is generated |
|
|
1996 | # and then stored in the problem version) |
|
|
1997 | my $problemToShow = ( $editingSetVersion ) ? |
|
|
1998 | $MergedProblems{$problemID} : $UserProblems{$problemID}; |
|
|
1999 | |
| 1887 | my $editProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID => $problemID }); |
2000 | my $editProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $fullSetID, problemID => $problemID }); |
| 1888 | my $editProblemLink = $self->systemLink($editProblemPage, params => { make_local_copy => 0 }); |
2001 | my $editProblemLink = $self->systemLink($editProblemPage, params => { make_local_copy => 0 }); |
| 1889 | |
2002 | |
| 1890 | |
2003 | |
| 1891 | # FIXME: should we have an "act as" type link here when editing for multiple users? |
2004 | # FIXME: should we have an "act as" type link here when editing for multiple users? |
| 1892 | my $viewProblemPage = $urlpath->new(type => 'problem_detail', args => { courseID => $courseID, setID => $setID, problemID => $problemID }); |
2005 | my $viewProblemPage = $urlpath->new(type => 'problem_detail', args => { courseID => $courseID, setID => $setID, problemID => $problemID }); |
| … | |
… | |
| 1933 | CGI::Tr({}, CGI::td({}, CGI::a({href => $viewProblemLink, target=>"WW_View"}, "Try it" . ($forOneUser ? " (as $editForUser[0])" : "")))) . |
2046 | CGI::Tr({}, CGI::td({}, CGI::a({href => $viewProblemLink, target=>"WW_View"}, "Try it" . ($forOneUser ? " (as $editForUser[0])" : "")))) . |
| 1934 | ($forUsers ? "" : CGI::Tr({}, CGI::td({}, CGI::checkbox({name => "deleteProblem", value => $problemID, label => "Delete it?"})))) . |
2047 | ($forUsers ? "" : CGI::Tr({}, CGI::td({}, CGI::checkbox({name => "deleteProblem", value => $problemID, label => "Delete it?"})))) . |
| 1935 | # CGI::Tr({}, CGI::td({}, "Delete it?" . CGI::input({type => "checkbox", name => "deleteProblem", value => $problemID}))) . |
2048 | # CGI::Tr({}, CGI::td({}, "Delete it?" . CGI::input({type => "checkbox", name => "deleteProblem", value => $problemID}))) . |
| 1936 | ($forOneUser ? "" : CGI::Tr({}, CGI::td({}, CGI::checkbox({name => "markCorrect", value => $problemID, label => "Mark Correct?"})))) . |
2049 | ($forOneUser ? "" : CGI::Tr({}, CGI::td({}, CGI::checkbox({name => "markCorrect", value => $problemID, label => "Mark Correct?"})))) . |
| 1937 | CGI::end_table(), |
2050 | CGI::end_table(), |
| 1938 | $self->FieldTable($userToShow, $setID, $problemID, $GlobalProblems{$problemID}, $UserProblems{$problemID}, $isGatewaySet), |
2051 | $self->FieldTable($userToShow, $setID, $problemID, $GlobalProblems{$problemID}, $problemToShow, $isGatewaySet), |
| 1939 | # A comprehensive list of problems is just TOO big to be handled well |
2052 | # A comprehensive list of problems is just TOO big to be handled well |
| 1940 | # comboBox({ |
2053 | # comboBox({ |
| 1941 | # name => "set.$setID.$problemID", |
2054 | # name => "set.$setID.$problemID", |
| 1942 | # request => $r, |
2055 | # request => $r, |
| 1943 | # default => $problemRecord->{problem_id}, |
2056 | # default => $problemRecord->{problem_id}, |
| … | |
… | |
| 1948 | join ("\n", $self->FieldHTML( |
2061 | join ("\n", $self->FieldHTML( |
| 1949 | $userToShow, |
2062 | $userToShow, |
| 1950 | $setID, |
2063 | $setID, |
| 1951 | $problemID, |
2064 | $problemID, |
| 1952 | $GlobalProblems{$problemID}, # pass previously fetched global record to FieldHTML --sam |
2065 | $GlobalProblems{$problemID}, # pass previously fetched global record to FieldHTML --sam |
| 1953 | $UserProblems{$problemID}, # pass previously fetched user record to FieldHTML --sam |
2066 | $problemToShow, # pass previously fetched user record to FieldHTML --sam |
| 1954 | "source_file" |
2067 | "source_file" |
| 1955 | )) . |
2068 | )) . |
| 1956 | CGI::br() . |
2069 | CGI::br() . |
| 1957 | ($error ? |
2070 | ($error ? |
| 1958 | CGI::div({class=>"ResultsWithError", style=>"font-weight: bold"}, $error) |
2071 | CGI::div({class=>"ResultsWithError", style=>"font-weight: bold"}, $error) |
| … | |
… | |
| 1978 | print CGI::p("When changing problem numbers, we will move the problem to be ". CGI::em("before"). " the chosen number."); |
2091 | print CGI::p("When changing problem numbers, we will move the problem to be ". CGI::em("before"). " the chosen number."); |
| 1979 | |
2092 | |
| 1980 | } else { |
2093 | } else { |
| 1981 | print CGI::p(CGI::b("This set doesn't contain any problems yet.")); |
2094 | print CGI::p(CGI::b("This set doesn't contain any problems yet.")); |
| 1982 | } |
2095 | } |
| 1983 | # always allow one to add a new problem. |
2096 | # always allow one to add a new problem, unless we're editing a set version |
| 1984 | print CGI::checkbox({ |
2097 | if ( ! $editingSetVersion ) { |
| 1985 | label=> "Add", |
2098 | print CGI::checkbox({ label=> "Add", |
| 1986 | name=>"add_blank_problem", value=>"1"} |
2099 | name=>"add_blank_problem", value=>"1"} |
| 1987 | ),CGI::input({ |
2100 | ),CGI::input({ |
| 1988 | name=>"add_n_problems", |
2101 | name=>"add_n_problems", |
| 1989 | size=>2, |
2102 | size=>2, |
| 1990 | value=>1 |
2103 | value=>1 }, |
| 1991 | }, |
|
|
| 1992 | "blank problem template(s) to end of homework set" |
2104 | "blank problem template(s) to end of homework set" |
| 1993 | ), |
2105 | ); |
|
|
2106 | } |
| 1994 | CGI::br(),CGI::br(), |
2107 | print CGI::br(),CGI::br(), |
| 1995 | CGI::input({type=>"submit", name=>"submit_changes", value=>"Save Changes"}), |
2108 | CGI::input({type=>"submit", name=>"submit_changes", value=>"Save Changes"}), |
| 1996 | CGI::input({type=>"submit", name=>"handle_numbers", value=>"Reorder problems only"}), |
2109 | CGI::input({type=>"submit", name=>"handle_numbers", value=>"Reorder problems only"}), |
| 1997 | "(Any unsaved changes will be lost.)" |
2110 | "(Any unsaved changes will be lost.)"; |
| 1998 | ; |
|
|
| 1999 | |
2111 | |
| 2000 | |
|
|
| 2001 | |
|
|
| 2002 | #my $editNewProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID =>'new_problem' }); |
2112 | #my $editNewProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID =>'new_problem' }); |
| 2003 | #my $editNewProblemLink = $self->systemLink($editNewProblemPage, params => { make_local_copy => 1, file_type => 'blank_problem' }); |
2113 | #my $editNewProblemLink = $self->systemLink($editNewProblemPage, params => { make_local_copy => 1, file_type => 'blank_problem' }); |
| 2004 | # This next feature isn't fully supported and is causing problems. Remove for now. #FIXME |
2114 | # This next feature isn't fully supported and is causing problems. Remove for now. #FIXME |
| 2005 | #print CGI::p( CGI::a({href=>$editNewProblemLink},'Edit'). ' a new blank problem'); |
2115 | #print CGI::p( CGI::a({href=>$editNewProblemLink},'Edit'). ' a new blank problem'); |
| 2006 | |
2116 | |