@@ -141,9 +141,12 @@ sub can_recordAnswers ($c, $user, $permissionLevel, $effectiveUser, $set, $probl
141141
142142 if ($user -> user_id ne $effectiveUser -> user_id) {
143143 # If the user is not allowed to record answers as another user, return that permission. If the user is allowed
144- # to record only set version answers, then allow that between the open and close dates, and so drop out of this
145- # conditional to the usual one.
146- return 1 if $authz -> hasPermissions($user -> user_id, ' record_answers_when_acting_as_student' );
144+ # to record an unsubmitted test, allow that. If the user is allowed to record only set version answers, then
145+ # allow that between the open and close dates, and so drop out of this conditional to the usual one.
146+ return 1
147+ if $authz -> hasPermissions($user -> user_id, ' record_answers_when_acting_as_student' )
148+ || $c -> can_gradeUnsubmittedTest($user , $permissionLevel , $effectiveUser , $set , $problem , $tmplSet ,
149+ $submitAnswers );
147150 return 0 if !$authz -> hasPermissions($user -> user_id, ' record_set_version_answers_when_acting_as_student' );
148151 }
149152
@@ -224,6 +227,15 @@ sub can_checkAnswers ($c, $user, $permissionLevel, $effectiveUser, $set, $proble
224227 return 0;
225228}
226229
230+ # If user can use the problem grader, and the test is past due and has not been submitted, allow them to submit.
231+ sub can_gradeUnsubmittedTest ($c , $user , $permissionLevel , $effectiveUser , $set , $problem , $tmplSet , $submitAnswers = 0)
232+ {
233+ return
234+ !$submitAnswers
235+ && $c -> can_showProblemGrader($user , $permissionLevel , $effectiveUser , $set , $problem , $tmplSet )
236+ && (after($set -> due_date + $c -> ce-> {gatewayGracePeriod }) && !$set -> version_last_attempt_time);
237+ }
238+
227239sub can_showScore ($c , $user , $permissionLevel , $effectiveUser , $set , $problem , $tmplSet ) {
228240 return
229241 $c -> authz-> hasPermissions($user -> user_id, ' view_hidden_work' )
@@ -533,7 +545,7 @@ async sub pre_header_initialize ($c) {
533545 $authz -> hasPermissions($userID , ' record_answers_when_acting_as_student' )
534546 || $authz -> hasPermissions($userID , ' create_new_set_version_when_acting_as_student' )
535547 )
536- && $c -> param(' createnew_ok ' )
548+ && $c -> param(' submit_for_student_ok ' )
537549 )
538550 )
539551 )
@@ -597,24 +609,18 @@ async sub pre_header_initialize ($c) {
597609 || $authz -> hasPermissions($userID , ' create_new_set_version_when_acting_as_student' ))
598610 )
599611 {
600-
601- $c -> {invalidSet } = $c -> maketext(
612+ $c -> stash-> {actingConfirmation } = $c -> maketext(
602613 ' You are acting as user [_1]. If you continue, you will create a new version of '
603614 . ' this test for that user, which will count against their allowed maximum '
604615 . ' number of versions for the current time interval. In general, this is not '
605616 . ' what you want to do. Please be sure that you want to do this before clicking '
606617 . ' the "Create New Test Version" button below. Alternatively, click "Cancel".' ,
607618 $effectiveUserID
608619 );
609- $c -> { invalidVersionCreation } = 1 ;
620+ $c -> stash -> { actingConfirmationButton } = $c -> maketext( ' Create New Test Version ' ) ;
610621
611622 } elsif ($effectiveUserID ne $userID ) {
612- $c -> {invalidSet } = $c -> maketext(
613- ' You are acting as user [_1], and do not have the permission to create a new test version '
614- . ' when acting as another user.' ,
615- $effectiveUserID
616- );
617- $c -> {invalidVersionCreation } = 2;
623+ $c -> {actingCreationError } = 1;
618624
619625 } elsif (($maxAttemptsPerVersion == 0 || $currentNumAttempts < $maxAttemptsPerVersion )
620626 && $c -> submitTime < $set -> due_date() + $ce -> {gatewayGracePeriod })
@@ -641,11 +647,29 @@ async sub pre_header_initialize ($c) {
641647 if (
642648 ($currentNumAttempts < $maxAttemptsPerVersion )
643649 && ($effectiveUserID eq $userID
644- || $authz -> hasPermissions($userID , ' record_set_version_answers_when_acting_as_student' ))
650+ || $authz -> hasPermissions($userID , ' record_set_version_answers_when_acting_as_student' )
651+ || $authz -> hasPermissions($userID , ' record_answers_when_acting_as_student' ))
645652 )
646653 {
647654 if (between($set -> open_date(), $set -> due_date() + $ce -> {gatewayGracePeriod }, $c -> submitTime)) {
648655 $versionIsOpen = 1;
656+
657+ # If acting as another user, then the user has permissions to record answers for the
658+ # student which is dangerous for open test versions. Give a warning unless the user
659+ # has already confirmed they understand the risk.
660+ if ($effectiveUserID ne $userID && !$c -> param(' submit_for_student_ok' )) {
661+ $c -> stash-> {actingConfirmation } = $c -> maketext(
662+ ' You are trying to view an open test version for [_1] and have the permission to submit '
663+ . ' answers for that user. This is dangerous, as your answers can overwrite the '
664+ . q/ student's answers as you move between test pages, preview, or check answers. /
665+ . ' If you are planing to submit answers for this student, click "View Test Version" '
666+ . ' below to continue. If you only want to view the test version, click "Cancel" '
667+ . ' below, then disable the permission to record answers when acting as a student '
668+ . ' before viewing open test versions.' ,
669+ $effectiveUserID
670+ );
671+ $c -> stash-> {actingConfirmationButton } = $c -> maketext(' View Test Version' );
672+ }
649673 }
650674 }
651675 }
@@ -654,6 +678,13 @@ async sub pre_header_initialize ($c) {
654678 $c -> {invalidSet } = $c -> maketext(' This test is closed. No new test versions may be taken.' );
655679 }
656680
681+ if ($c -> stash-> {actingConfirmation }) {
682+ # Store session while waiting for confirmation for proctored tests.
683+ $c -> authen-> session(acting_proctor => 1) if $c -> {assignment_type } eq ' proctored_gateway' ;
684+ return ;
685+ }
686+ delete $c -> authen-> session-> {acting_proctor };
687+
657688 # If the proctor session key does not have a set version id, then add it. This occurs when a student
658689 # initially enters a proctored test, since the version id is not determined until just above.
659690 if ($c -> authen-> session(' proctor_authorization_granted' )
@@ -663,8 +694,8 @@ async sub pre_header_initialize ($c) {
663694 else { delete $c -> authen-> session-> {proctor_authorization_granted }; }
664695 }
665696
666- # If the set or problem is invalid, then delete any proctor session keys and return.
667- if ($c -> {invalidSet }) {
697+ # If the set is invalid, then delete any proctor session keys and return.
698+ if ($c -> {invalidSet } || $c -> { actingCreationError } ) {
668699 if (defined $c -> {assignment_type } && $c -> {assignment_type } eq ' proctored_gateway' ) {
669700 delete $c -> authen-> session-> {proctor_authorization_granted };
670701 }
@@ -740,6 +771,7 @@ async sub pre_header_initialize ($c) {
740771 checkAnswers => $c -> can_checkAnswers(@args ),
741772 recordAnswersNextTime => $c -> can_recordAnswers(@args , $c -> {submitAnswers }),
742773 checkAnswersNextTime => $c -> can_checkAnswers(@args , $c -> {submitAnswers }),
774+ gradeUnsubmittedTest => $c -> can_gradeUnsubmittedTest(@args , $c -> {submitAnswers }),
743775 showScore => $c -> can_showScore(@args ),
744776 showProblemScores => $c -> can_showProblemScores(@args ),
745777 showWork => $c -> can_showWork(@args ),
@@ -754,6 +786,12 @@ async sub pre_header_initialize ($c) {
754786 $c -> {can } = \%can ;
755787 $c -> {will } = \%will ;
756788
789+ # Issue a warning if a test has not been submitted, but can still be graded by the instructor.
790+ $c -> addbadmessage(
791+ $c -> maketext(
792+ ' This test version is past due, but has not been graded. You can still grade the test for this user.' )
793+ ) if $can {gradeUnsubmittedTest } && $userID ne $effectiveUserID ;
794+
757795 # Set up problem numbering and multipage variables.
758796
759797 my @problemNumbers ;
@@ -1330,7 +1368,8 @@ sub path ($c, $args) {
13301368 $args ,
13311369 ' WeBWorK' => $navigation_allowed ? $c -> url_for(' root' ) : ' ' ,
13321370 $courseName => $navigation_allowed ? $c -> url_for(' set_list' ) : ' ' ,
1333- $setID eq ' Undefined_Set' || $c -> {invalidSet }
1371+ $setID eq ' Undefined_Set'
1372+ || $c -> {invalidSet } || $c -> {actingCreationError } || $c -> stash-> {actingConfirmation }
13341373 ? ($setID => ' ' )
13351374 : (
13361375 $c -> {set }-> set_id => $c -> url_for(' problem_list' , setID => $c -> {set }-> set_id),
@@ -1344,7 +1383,7 @@ sub nav ($c, $args) {
13441383 my $userID = $c -> param(' user' );
13451384 my $effectiveUserID = $c -> param(' effectiveUser' );
13461385
1347- return ' ' if $c -> {invalidSet };
1386+ return ' ' if $c -> {invalidSet } || $c -> { actingCreationError } || $c -> stash -> { actingConfirmation } ;
13481387
13491388 # Set up and display a student navigation for those that have permission to act as a student.
13501389 if ($c -> authz-> hasPermissions($userID , ' become_student' ) && $effectiveUserID ne $userID ) {
0 commit comments