diff --git a/htdocs/js/PGProblemEditor/pgproblemeditor.js b/htdocs/js/PGProblemEditor/pgproblemeditor.js index 328a23547f..a009cfc50d 100644 --- a/htdocs/js/PGProblemEditor/pgproblemeditor.js +++ b/htdocs/js/PGProblemEditor/pgproblemeditor.js @@ -344,51 +344,11 @@ } const actionView = document.getElementById('view'); - const editorForm = document.getElementById('editor'); - - // Make sure this is reset on each click so that a new window isn't always opened once that has been done once. - if (editorForm) editorForm.target = '_self'; if (actionView && actionView.classList.contains('active')) { - if (document.getElementById('newWindowView')?.checked) { - document.getElementById('revert-tab')?.classList.remove('disabled'); - document.getElementById('revert-to-tmp-container')?.classList.remove('d-none'); - const revertRadio = document.getElementById('action_revert_type_revert_id'); - if (revertRadio && revertRadio.disabled) { - revertRadio.disabled = false; - revertRadio.checked = true; - } - - // Add the temporary file coloring and change the current file to the saved file. - document.querySelectorAll('.set-file-info').forEach((nfo) => nfo.classList.add('temporaryFile')); - for (const currentFile of document.querySelectorAll('.current-file')) { - currentFile.textContent = currentFile.dataset.tmpFile; - } - - if (editorForm) editorForm.target = 'WW_View'; - } else { - e.preventDefault(); - await render(); - - saveTempFile(); - } - } - - const actionSave = document.getElementById('save'); - if ( - actionSave && - actionSave.classList.contains('active') && - document.getElementById('newWindowSave')?.checked && - editorForm - ) { - if (document.getElementById('backupFile')?.checked) { - document.getElementById('show-backups-comment')?.classList.remove('d-none'); - const deleteBackupCheck = document.getElementById('deleteBackup'); - if (deleteBackupCheck) deleteBackupCheck.disabled = false; - } - document.getElementById('revert-tab')?.classList.remove('disabled'); - - editorForm.target = 'WW_View'; + e.preventDefault(); + await render(); + saveTempFile(); } const actionHardcopy = document.getElementById('hardcopy'); @@ -671,7 +631,8 @@ if (fileType === 'hardcopy_header') { renderArea.innerHTML = '
' + - 'Hardcopy header contents can only be viewed in a new window.
'; + 'Hardcopy header contents cannot be viewed in the problem editor. ' + + 'Use "Generate Hardcopy" to view the contents.'; resolve(); return; } diff --git a/lib/WeBWorK/Authz.pm b/lib/WeBWorK/Authz.pm index 6005d7e1c7..bafcfedcb6 100644 --- a/lib/WeBWorK/Authz.pm +++ b/lib/WeBWorK/Authz.pm @@ -189,10 +189,11 @@ sub checkSet ($self) { # First check to see if we have to worried about set-level access restrictions. return 0 - unless grep {/^$node_name$/} + unless grep { $node_name eq $_ } (qw( problem_list problem_detail + show_me_another gateway_quiz gateway_quiz_version proctored_gateway_quiz @@ -215,9 +216,6 @@ sub checkSet ($self) { if ($set && $set->set_id eq $setName && $set->user_id eq $effectiveUserName && $set->version_id == $setVersion) { # If we have all of this, then we can just use this set and skip the rest. - } elsif ($setName eq 'Undefined_Set' && $self->hasPermissions($userName, 'access_instructor_tools')) { - # This is the case of previewing a problem from a 'try it' link. - return 0; } else { if ($db->existsSetVersion($effectiveUserName, $setName, $setVersion)) { $set = $db->getMergedSetVersion($effectiveUserName, $setName, $setVersion); @@ -241,10 +239,6 @@ sub checkSet ($self) { } else { if ($db->existsUserSet($effectiveUserName, $setName)) { $set = $db->getMergedSet($effectiveUserName, $setName); - } elsif ($setName eq 'Undefined_Set' && $self->hasPermissions($userName, 'access_instructor_tools')) { - # This is the case of the library browser, when we don't actually have a set to look at. This only - # happens for instructor tool users. - return 0; } else { return $c->maketext('Requested set "[_1]" is not assigned to user [_2].', $setName, $effectiveUserName); } @@ -301,7 +295,10 @@ sub checkSet ($self) { } # Check to be sure that gateways are being sent to the correct content generator. - if (defined $set->assignment_type && $set->assignment_type =~ /gateway/ && $node_name eq 'problem_detail') { + if (defined $set->assignment_type + && $set->assignment_type =~ /gateway/ + && ($node_name eq 'problem_detail' || $node_name eq 'show_me_another')) + { return $c->maketext( 'Requested set "[_1]" is a test but the regular homework assignment content ' . 'generator [_2] was called. Try re-entering the set from the problem sets listing page.', diff --git a/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm b/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm index 6099a44e32..63d27a3cf4 100644 --- a/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm +++ b/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm @@ -13,13 +13,12 @@ use Mojo::JSON qw(encode_json decode_json); use WeBWorK::Utils qw(encodeAnswers decodeAnswers max wwRound); use WeBWorK::Utils::DateTime qw(before between after); -use WeBWorK::Utils::Files qw(path_is_subdir); use WeBWorK::Utils::Instructor qw(assignSetVersionToUser); use WeBWorK::Utils::Logs qw(writeLog writeCourseLog); use WeBWorK::Utils::ProblemProcessing qw(create_ans_str_from_responses compute_reduced_score); use WeBWorK::Utils::Rendering qw(getTranslatorDebuggingOptions renderPG); use WeBWorK::Utils::Sets qw(is_restricted); -use WeBWorK::DB::Utils qw(global2user fake_set fake_set_version fake_problem); +use WeBWorK::DB::Utils qw(global2user); use WeBWorK::Debug qw(debug); use PGrandom; use WeBWorK::Authen::LTI::GradePassback qw(passbackGradeOnSubmit); @@ -101,7 +100,6 @@ sub can_showProblemGrader ($c, $user, $effectiveUser, $set, $problem, $tmplSet) return ($authz->hasPermissions($user->user_id, 'access_instructor_tools') && $authz->hasPermissions($user->user_id, 'problem_grader') - && $set->set_id ne 'Undefined_Set' && !$c->{invalidSet}); } @@ -124,9 +122,6 @@ sub can_showSolutions ($c, $user, $effectiveUser, $set, $problem, $tmplSet) { sub can_recordAnswers ($c, $user, $effectiveUser, $set, $problem, $tmplSet = 0, $submitAnswers = 0) { my $authz = $c->authz; - # Never record answers for undefined sets - return 0 if $set->set_id eq 'Undefined_Set'; - if ($user->user_id ne $effectiveUser->user_id) { # If the user is not allowed to record answers as another user, return that permission. If the user is allowed # to record an unsubmitted test, allow that. If the user is allowed to record only set version answers, then @@ -331,7 +326,6 @@ async sub initialize ($c) { my $setID = $c->stash('setID'); my $userID = $c->authen->{user_id}; my $effectiveUserID = $c->param('effectiveUser'); - my $isFakeSet = 0; my $user = $c->authen->{user}; my $effectiveUser = $c->authen->{effective_user}; @@ -344,151 +338,81 @@ async sub initialize ($c) { # We need the template (user) set, the merged set version, and a problem from the set to be able to test whether # we're creating a new set version. - my ($tmplSet, $set, $problem) = (0, 0, 0); - - # If the set comes in as "Undefined_Set", then we're trying/editing a single problem in a set, and so create a fake - # set with which to work if the user has the authorization to do that. - if ($setID eq 'Undefined_Set') { - $isFakeSet = 1; - # Make sure these are defined - $requestedVersion = 1; - $c->{assignment_type} = 'gateway'; - - if (!$authz->hasPermissions($userID, 'modify_problem_sets')) { - $c->{invalidSet} = - $c->maketext('You do not have the authorization level required to view/edit undefined sets.'); - - # Define these so that we can drop through to report the error in body. - $tmplSet = fake_set($db); - $set = fake_set_version($db); - $problem = fake_problem($db); - } else { - # In this case we're creating a fake set from the input, so the input must include a source file. - if (!$c->param('sourceFilePath')) { - $c->{invalidSet} = $c->maketext( - 'An Undefined_Set was requested, but no source file for the contained problem was provided.'); - - # Define these so that we can drop through to report the error in body. - $tmplSet = fake_set($db); - $set = fake_set_version($db); - $problem = fake_problem($db); - - } else { - my $sourceFPath = $c->param('sourceFilePath'); - die('sourceFilePath is unsafe!') - unless path_is_subdir($sourceFPath, $ce->{courseDirs}{templates}, 1); - - $tmplSet = fake_set($db); - $set = fake_set_version($db); - $problem = fake_problem($db); - - my $creation_time = time; - - $tmplSet->assignment_type('gateway'); - $tmplSet->attempts_per_version(0); - $tmplSet->time_interval(0); - $tmplSet->versions_per_interval(1); - $tmplSet->version_time_limit(0); - $tmplSet->version_creation_time($creation_time); - $tmplSet->problem_randorder(0); - $tmplSet->problems_per_page(1); - $tmplSet->hide_score('N'); - $tmplSet->hide_score_by_problem('N'); - $tmplSet->hide_work('N'); - $tmplSet->time_limit_cap('0'); - $tmplSet->restrict_ip('No'); - - $set->assignment_type('gateway'); - $set->time_interval(0); - $set->versions_per_interval(1); - $set->version_time_limit(0); - $set->version_creation_time($creation_time); - $set->time_limit_cap('0'); - - $problem->problem_id(1); - $problem->source_file($sourceFPath); - $problem->user_id($effectiveUserID); - $problem->value(1); - $problem->problem_seed($c->param('problemSeed')) if ($c->param('problemSeed')); - } - } - } else { - # If there is a cap on problems per page, make sure that is respected in the global set in - # case something higher snuck in. - if ($ce->{test}{maxProblemsPerPage}) { - my $globalSet = $db->getGlobalSet($setID); - if ( - $ce->{test}{maxProblemsPerPage} - && ($globalSet->problems_per_page == 0 - || $globalSet->problems_per_page > $ce->{test}{maxProblemsPerPage}) - ) - { - $globalSet->problems_per_page($ce->{test}{maxProblemsPerPage}); - $db->putGlobalSet($globalSet); - } + my ($tmplSet, $set, $problem); + + # If there is a cap on problems per page, make sure that is respected in the global set in + # case something higher snuck in. + if ($ce->{test}{maxProblemsPerPage}) { + my $globalSet = $db->getGlobalSet($setID); + if ( + $ce->{test}{maxProblemsPerPage} + && ($globalSet->problems_per_page == 0 + || $globalSet->problems_per_page > $ce->{test}{maxProblemsPerPage}) + ) + { + $globalSet->problems_per_page($ce->{test}{maxProblemsPerPage}); + $db->putGlobalSet($globalSet); } + } - # Get the template set, i.e., the non-versioned set that's assigned to the user. - # If this failed in authz->checkSet, then $c->{invalidSet} is set. - $tmplSet = $db->getMergedSet($effectiveUserID, $setID); - - # Now that is has been validated that this is a gateway test, save the assignment test for the processing of - # graded proctored tests. If a set was not obtained from the database, store a fake value here - # to be able to continue. - $c->{assignment_type} = $tmplSet->assignment_type || 'gateway'; + # Get the template set, i.e., the non-versioned set that's assigned to the user. + $tmplSet = $db->getMergedSet($effectiveUserID, $setID); - # If there is a cap on problems per page, make sure that is respected in case something higher snuck in. - if ($ce->{test}{maxProblemsPerPage} - && ($tmplSet->problems_per_page == 0 || $tmplSet->problems_per_page > $ce->{test}{maxProblemsPerPage})) - { - $tmplSet->problems_per_page($ce->{test}{maxProblemsPerPage}); - } + # Now that is has been validated that this is a gateway test, save the assignment type for the processing of + # graded proctored tests. Fallback to 'gateway' in case assignment_type was empty. + $c->{assignment_type} = $tmplSet->assignment_type || 'gateway'; - # next, get the latest (current) version of the set if we don't have a - # requested version number - my @allVersionIds = $db->listSetVersions($effectiveUserID, $setID); - my $latestVersion = (@allVersionIds ? $allVersionIds[-1] : 0); + # If there is a cap on problems per page, make sure that is respected in case something higher snuck in. + if ($ce->{test}{maxProblemsPerPage} + && ($tmplSet->problems_per_page == 0 || $tmplSet->problems_per_page > $ce->{test}{maxProblemsPerPage})) + { + $tmplSet->problems_per_page($ce->{test}{maxProblemsPerPage}); + } - # Double check that the requested version makes sense. - $requestedVersion = $latestVersion - if ($requestedVersion !~ /^\d+$/ - || $requestedVersion > $latestVersion - || $requestedVersion < 0); + # next, get the latest (current) version of the set if we don't have a + # requested version number + my @allVersionIds = $db->listSetVersions($effectiveUserID, $setID); + my $latestVersion = (@allVersionIds ? $allVersionIds[-1] : 0); + + # Double check that the requested version makes sense. + $requestedVersion = $latestVersion + if ($requestedVersion !~ /^\d+$/ + || $requestedVersion > $latestVersion + || $requestedVersion < 0); + + die('No requested version when returning to problem?!') + if ( + ( + $c->param('previewAnswers') + || $c->param('checkAnswers') + || $c->param('submitAnswers') + || $c->param('newPage') + ) + && !$requestedVersion + ); - die('No requested version when returning to problem?!') - if ( - ( - $c->param('previewAnswers') - || $c->param('checkAnswers') - || $c->param('submitAnswers') - || $c->param('newPage') - ) - && !$requestedVersion - ); - - # To check for a proctored test, the set version is needed, not the template. So get that. - if ($requestedVersion) { - $set = $db->getMergedSetVersion($effectiveUserID, $setID, $requestedVersion); - } elsif ($latestVersion) { - $set = $db->getMergedSetVersion($effectiveUserID, $setID, $latestVersion); - } else { - # If there is not a requested version or a latest version, then create dummy set to proceed. - # FIXME RETURN TO: should this be global2version? - $set = global2user($db->{set_version}{record}, $db->getGlobalSet($setID)); - $set->user_id($effectiveUserID); - $set->psvn('000'); - $set->set_id($setID); # redundant? - $set->version_id(0); - } + # To check for a proctored test, the set version is needed, not the template. So get that. + if ($requestedVersion) { + $set = $db->getMergedSetVersion($effectiveUserID, $setID, $requestedVersion); + } elsif ($latestVersion) { + $set = $db->getMergedSetVersion($effectiveUserID, $setID, $latestVersion); + } else { + # If there is not a requested version or a latest version, then create dummy set to proceed. + # FIXME RETURN TO: should this be global2version? + $set = global2user($db->{set_version}{record}, $db->getGlobalSet($setID)); + $set->user_id($effectiveUserID); + $set->psvn('000'); + $set->set_id($setID); # redundant? + $set->version_id(0); + } - # If there is a cap on problems per page, make sure that is respected in case something higher snuck in. - if ($ce->{test}{maxProblemsPerPage} - && ($set->problems_per_page == 0 || $set->problems_per_page > $ce->{test}{maxProblemsPerPage})) - { - $set->problems_per_page($ce->{test}{maxProblemsPerPage}); - } + # If there is a cap on problems per page, make sure that is respected in case something higher snuck in. + if ($ce->{test}{maxProblemsPerPage} + && ($set->problems_per_page == 0 || $set->problems_per_page > $ce->{test}{maxProblemsPerPage})) + { + $set->problems_per_page($ce->{test}{maxProblemsPerPage}); } - my $setVersionNumber = $set ? $set->version_id : 0; + my $setVersionNumber = $set->version_id; # Assemble gateway parameters @@ -498,16 +422,14 @@ async sub initialize ($c) { # date. If a specific version has not been requested and conditional release is enabled, then this also checks to # see if the conditions have been met for a conditional release. my $isOpen = ( - $requestedVersion ? ($set && $set->open_date && after($set->open_date, $c->submitTime)) : ($tmplSet - && $tmplSet->open_date + $requestedVersion ? ($set->open_date && after($set->open_date, $c->submitTime)) : ($tmplSet->open_date && after($tmplSet->open_date, $c->submitTime) && !($ce->{options}{enableConditionalRelease} && is_restricted($db, $tmplSet, $effectiveUserID))) ) || $authz->hasPermissions($userID, 'view_unopened_sets'); my $isClosed = - $tmplSet - && $tmplSet->due_date + $tmplSet->due_date && after($tmplSet->due_date, $c->submitTime) && !$authz->hasPermissions($userID, 'record_answers_after_due_date'); @@ -515,17 +437,14 @@ async sub initialize ($c) { # version. Among other things, the number of attempts is a property of the problem, so get a problem to check that. # Note that for a gateway quiz all problems will have the same number of attempts. This means that if the set # doesn't have any problems we're up a creek, so check for that here and bail if that is the case. - my @setPNum = $setID eq 'Undefined_Set' ? (1) : $db->listUserProblems($effectiveUser->user_id, $setID); - die("Set $setID contains no problems.") if (!@setPNum); - - # If we assigned a fake problem above, $problem is already defined. Otherwise, get the problem, or define it to be - # undefined if the set hasn't been versioned to the user yet. This is fixed when we assign the setVersion. - if (!$problem) { - $problem = - $setVersionNumber - ? $db->getMergedProblemVersion($effectiveUser->user_id, $setID, $setVersionNumber, $setPNum[0]) - : undef; - } + my @setPNum = $db->listUserProblems($effectiveUser->user_id, $setID); + die("Set $setID contains no problems.") if !@setPNum; + + # Get the problem if there is a versioned set, otherwise a problem will be defined when creating a versioned test. + $problem = + $setVersionNumber + ? $db->getMergedProblemVersion($effectiveUser->user_id, $setID, $setVersionNumber, $setPNum[0]) + : undef; my $maxAttemptsPerVersion = $tmplSet->attempts_per_version || 0; my $timeInterval = $tmplSet->time_interval || 0; @@ -549,7 +468,7 @@ async sub initialize ($c) { my $currentNumVersions = 0; # this is the number of versions in the time interval my $totalNumVersions = 0; - if ($setVersionNumber && !$c->{invalidSet} && $setID ne 'Undefined_Set') { + if ($setVersionNumber) { my @setVersionIDs = $db->listSetVersions($effectiveUserID, $setID); my @setVersions = $db->getSetVersions(map { [ $effectiveUserID, $setID, $_ ] } @setVersionIDs); for (@setVersions) { @@ -563,7 +482,7 @@ async sub initialize ($c) { my $versionIsOpen = 0; - if ($isOpen && !$isClosed && !$c->{invalidSet}) { + if ($isOpen && !$isClosed) { # If specific version was not requested, then create a new one if needed. if (!$requestedVersion) { if ( @@ -741,7 +660,7 @@ async sub initialize ($c) { } } - } elsif (!$c->{invalidSet} && !$requestedVersion) { + } elsif (!$requestedVersion) { $c->{invalidSet} = $c->maketext('This test is closed. No new test versions may be taken.'); } @@ -867,12 +786,7 @@ async sub initialize ($c) { # Set up problem numbering and multipage variables. - my @problemNumbers; - if ($setID eq 'Undefined_Set') { - @problemNumbers = (1); - } else { - @problemNumbers = $db->listProblemVersions($effectiveUserID, $setID, $setVersionNumber); - } + my @problemNumbers = $db->listProblemVersions($effectiveUserID, $setID, $setVersionNumber); # To speed up processing of long (multi-page) tests, we want to only translate those problems that are being # submitted or are currently being displayed. So determine which problems are on the current page. @@ -924,12 +838,7 @@ async sub initialize ($c) { my @pg_results; # Process the problems as needed. - my @mergedProblems; - if ($setID eq 'Undefined_Set') { - @mergedProblems = ($problem); - } else { - @mergedProblems = $db->getAllMergedProblemVersions($effectiveUserID, $setID, $setVersionNumber); - } + my @mergedProblems = $db->getAllMergedProblemVersions($effectiveUserID, $setID, $setVersionNumber); my @renderPromises; @@ -1299,8 +1208,7 @@ sub path ($c, $args) { $args, 'WeBWorK' => $navigation_allowed ? $c->url_for('root') : '', $courseName => $navigation_allowed ? $c->url_for('set_list') : '', - $setID eq 'Undefined_Set' - || $c->{invalidSet} || $c->{actingCreationError} || $c->stash->{actingConfirmation} + $c->{invalidSet} || $c->{actingCreationError} || $c->stash->{actingConfirmation} ? ( defined $c->stash('versionID') ? ($setID => $c->url_for('problem_list', setID => $setID), 'v' . $c->stash('versionID') => '') @@ -1321,10 +1229,7 @@ sub nav ($c, $args) { # Set up and display a student navigation for those that have permission to act as a student. if ($c->authz->hasPermissions($userID, 'become_student')) { - my $setID = $c->{set}->set_id; - - return '' if $setID eq 'Undefined_Set'; - + my $setID = $c->{set}->set_id; my $setVersion = $c->{set}->version_id; # Find all versions of this set that have been taken (excluding those taken by the current user). diff --git a/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm b/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm index 59c1e9ce1d..a9831edf14 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -18,15 +18,6 @@ The course information and problems are located in the course templates directory. Course information has the name defined by $ce->{courseFiles}{course_info} -editMode = temporaryFile | savedFile - -This flag is read by Problem.pm and ProblemSet.pm (perhaps others). - -The value of temporaryFile means view the temp file defined by -fname.user_name.tmp instead of the file fname. - -The value of savedFile means to use fname directly. - The suffix for a temporary file is "user_name.tmp" by default. =head2 File types (file_type) which can be edited. @@ -182,8 +173,11 @@ sub initialize ($c) { return; } - # Clean up sourceFilePath and check that sourceFilePath is relative to the templates folder - if ($c->{file_type} eq 'source_path_for_problem_file') { + # Clean up sourceFilePath and check that sourceFilePath is relative to the templates folder. + # This is also needed for header files not attached to a set. + if ($c->{file_type} eq 'source_path_for_problem_file' + || ($c->{file_type} =~ /header/ && !defined $c->{setID} && ($c->param('sourceFilePath') // '') =~ /\S/)) + { $c->{sourceFilePath} = $c->getRelativeSourceFilePath($c->param('sourceFilePath')); } @@ -330,8 +324,15 @@ sub page_title ($c) { return $c->maketext('Editor') unless $c->{file_type}; - return $c->maketext('Set Header for set [_1]', format_set_name_display($setID)) if $c->{file_type} eq 'set_header'; - return $c->maketext('Hardcopy Header for set [_1]', format_set_name_display($setID)) + return + defined $setID + ? $c->maketext('Set Header for set [_1]', format_set_name_display($setID)) + : $c->maketext('Set Header') + if $c->{file_type} eq 'set_header'; + return + defined $setID + ? $c->maketext('Hardcopy Header for set [_1]', format_set_name_display($setID)) + : $c->maketext('Hardcopy Header') if $c->{file_type} eq 'hardcopy_header'; return $c->maketext('Hardcopy Theme') if $c->{file_type} eq 'hardcopy_theme'; return $c->maketext('Course Information for course [_1]', $c->stash('courseID')) @@ -374,6 +375,8 @@ sub getRelativeSourceFilePath ($c, $sourceFilePath) { # This does not create the directories in the path to the file. # It returns an absolute path to the file. # $path should be an absolute path to the original file. +# FIXME: With the removal of Undefined_Set handling, should +# problems without a setID use something else in their path? sub determineTempEditFilePath ($c, $path) { my $user = $c->authen->{user_id}; my $setID = $c->{setID} // 'Undefined_Set'; @@ -487,9 +490,14 @@ sub getFilePaths ($c) { $editFilePath = "$ce->{webworkDirs}{hardcopyThemes}/" . $c->param('hardcopy_theme'); } } elsif ($c->{file_type} eq 'set_header' || $c->{file_type} eq 'hardcopy_header') { - my $set_record = $db->getGlobalSet($c->{setID}); + my $header_file = ''; + if (defined $c->{setID}) { + my $set_record = $db->getGlobalSet($c->{setID}); + $header_file = $set_record->{ $c->{file_type} } if $set_record; + } elsif (($c->param('sourceFilePath') // '') =~ /\S/) { + $header_file = $c->getRelativeSourceFilePath($c->param('sourceFilePath')); + } - my $header_file = defined $set_record ? $set_record->{ $c->{file_type} } : ''; if ($header_file && $header_file ne 'defaultHeader') { if ($header_file =~ m|^/|) { # Absolute address @@ -730,92 +738,13 @@ sub fixProblemContents { return $problemContents =~ s/(\r\n)|(\r)/\n/gr; } +# The view action is handled by javascript. If this is called, save the +# editor contents to the temporary file so the changes are not lost. sub view_handler ($c) { - my $problemSeed = $c->param('action.view.seed') // DEFAULT_SEED(); - my $displayMode = $c->param('action.view.displayMode') // 'MathJax'; - - # Grab the problemContents from the form in order to save it to the tmp file. + return unless defined $c->param('problemContents'); $c->{r_problemContents} = \(fixProblemContents($c->param('problemContents'))); - $c->saveFileChanges($c->{tempFilePath}); - - my $relativeTempFilePath = $c->getRelativeSourceFilePath($c->{tempFilePath}); - - # Construct redirect URL and redirect to it. - if ($c->{file_type} eq 'problem' || $c->{file_type} eq 'source_path_for_problem_file') { - # Redirect to Problem.pm or GatewayQuiz.pm. - # We need to know if the set is a gateway set to determine the redirect. - my $globalSet = defined $c->{setID} ? $c->db->getGlobalSet($c->{setID}) : undef; - - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink( - defined $globalSet && $globalSet->assignment_type =~ /gateway/ - ? $c->url_for('gateway_quiz', setID => 'Undefined_Set') - : $c->url_for( - 'problem_detail', - setID => $c->{setID} // 'Undefined_Set', - problemID => $c->{problemID} // 1 - ), - params => { - displayMode => $displayMode, - problemSeed => $problemSeed, - editMode => 'temporaryFile', - sourceFilePath => $relativeTempFilePath - } - )); - } elsif ($c->{file_type} eq 'blank_problem' || $c->{file_type} eq 'sample_problem') { - # Redirect to Problem.pm. - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink( - $c->url_for('problem_detail', setID => 'Undefined_Set', problemID => 1), - params => { - displayMode => $displayMode, - problemSeed => $problemSeed, - editMode => 'temporaryFile', - sourceFilePath => $relativeTempFilePath - } - )); - } elsif ($c->{file_type} eq 'set_header') { - # Redirect to ProblemSet - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink( - $c->url_for('problem_list', setID => $c->{setID}), - params => { - set_header => $c->{tempFilePath}, - displayMode => $displayMode, - problemSeed => $problemSeed, - editMode => 'temporaryFile', - sourceFilePath => $relativeTempFilePath - } - )); - } elsif ($c->{file_type} eq 'hardcopy_header') { - # Redirect to ProblemSet?? It's difficult to view temporary changes for hardcopy headers. - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink( - $c->url_for('problem_list', setID => $c->{setID}), - params => { - set_header => $c->{tempFilePath}, - displayMode => $displayMode, - problemSeed => $problemSeed, - editMode => 'temporaryFile', - sourceFilePath => $relativeTempFilePath - } - )); - } elsif ($c->{file_type} eq 'course_info') { - # Redirect to ProblemSets.pm. - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink( - $c->url_for('set_list'), - params => { - course_info => $c->{tempFilePath}, - editMode => 'temporaryFile', - sourceFilePath => $relativeTempFilePath - } - )); - } else { - die "I don't know how to redirect this file type $c->{file_type}."; - } - + $c->{inputFilePath} = $c->{tempFilePath} if -r $c->{tempFilePath}; return; } @@ -908,7 +837,6 @@ sub add_problem_handler ($c) { params => { displayMode => $c->{displayMode}, problemSeed => $c->{problemSeed}, - editMode => 'savedFile', sourceFilePath => $c->getRelativeSourceFilePath($sourceFilePath), file_type => 'problem', } @@ -937,7 +865,7 @@ sub add_problem_handler ($c) { $c->authen->flash(status_message => $c->{status_message}->join('')); $c->reply_with_redirect($c->systemLink( $c->url_for('problem_list', setID => $targetSetName), - params => { displayMode => $c->{displayMode}, editMode => 'savedFile' } + params => { displayMode => $c->{displayMode} } )); } elsif ($targetFileType eq 'hardcopy_header') { # Update set record @@ -963,7 +891,7 @@ sub add_problem_handler ($c) { $c->authen->flash(status_message => $c->{status_message}->join('')); $c->reply_with_redirect($c->systemLink( $c->url_for('hardcopy_preselect_set', setID => $targetSetName), - params => { displayMode => $c->{displayMode}, editMode => 'savedFile' } + params => { displayMode => $c->{displayMode} } )); } else { die "Unsupported target file type $targetFileType"; @@ -988,90 +916,6 @@ sub save_handler ($c) { $c->saveFileChanges($c->{editFilePath}, scalar($c->param('backupFile'))); } - # Don't redirect unless it was requested to open in a new window. - return unless $c->param('newWindowSave'); - - if ($c->{file_type} eq 'problem' || $c->{file_type} eq 'source_path_for_problem_file') { - # Redirect to Problem.pm or GatewayQuiz.pm. - # We need to know if the set is a gateway set to determine the redirect. - my $globalSet = defined $c->{setID} ? $c->db->getGlobalSet($c->{setID}) : undef; - - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink( - defined $globalSet && $globalSet->assignment_type =~ /gateway/ - ? $c->url_for('gateway_quiz', setID => 'Undefined_Set') - : $c->url_for( - 'problem_detail', - setID => $c->{setID} // 'Undefined_Set', - problemID => $c->{problemID} // 1 - ), - params => { - displayMode => $c->{displayMode}, - problemSeed => $c->{problemSeed}, - editMode => 'savedFile', - sourceFilePath => $c->getRelativeSourceFilePath($c->{editFilePath}) - } - )); - } elsif ($c->{file_type} eq 'set_header') { - # Redirect to ProblemSet.pm - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink( - $c->url_for('problem_list', setID => $c->{setID}), - params => { - displayMode => $c->{displayMode}, - problemSeed => $c->{problemSeed}, - editMode => 'savedFile' - } - )); - } elsif ($c->{file_type} eq 'hardcopy_header') { - # Redirect to Hardcopy.pm - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink( - $c->url_for('hardcopy_preselect_set', setID => $c->{setID}), - params => { - displayMode => $c->{displayMode}, - problemSeed => $c->{problemSeed}, - editMode => 'savedFile' - } - )); - } elsif ($c->{file_type} eq 'hardcopy_theme') { - # Redirect to PGProblemEditor.pm - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink( - $c->url_for('instructor_problem_editor'), - params => { - editMode => 'savedFile', - hardcopy_theme => $c->{hardcopy_theme}, - file_type => 'hardcopy_theme', - sourceFilePath => $c->getRelativeSourceFilePath($c->{editFilePath}), - } - )); - } elsif ($c->{file_type} eq 'course_info') { - # Redirect to ProblemSets.pm - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink($c->url_for('set_list'), params => { editMode => 'savedFile' })); - } elsif ($c->{file_type} eq 'source_path_for_problem_file') { - # Redirect to PGProblemEditor.pm - $c->authen->flash(status_message => $c->{status_message}->join('')); - $c->reply_with_redirect($c->systemLink( - $c->url_for( - 'instructor_problem_editor_withset_withproblem', - setID => $c->{setID}, - problemID => $c->{problemID} - ), - params => { - displayMode => $c->{displayMode}, - problemSeed => $c->{problemSeed}, - editMode => 'savedFile', - # The path relative to the templates directory is required. - sourceFilePath => $c->{editFilePath}, - file_type => 'source_path_for_problem_file' - } - )); - } else { - die "Unsupported save file type $c->{file_type}."; - } - return; } @@ -1127,9 +971,8 @@ sub save_as_handler ($c) { )); # If the save mode is 'add_to_set_as_new_problem', but this problem is not in a set (for example for a sample - # problem), then the redirect for this save mode will fail since there is no set or problem id. So switch to the - # 'new_independent_file' save mode. That will work since it fills in the 'Undefined_Set' for the set id and 1 - # for the problem id. + # problem), then the redirect for this save mode will fail since there is no set or problem id. So switch to + # the 'new_independent_file' save mode which does not need a set or problem id. $saveMode = 'new_independent_file' if $saveMode eq 'add_to_set_as_new_problem' && !defined $c->{setID}; } else { $c->{editFilePath} = $outputFilePath; @@ -1261,7 +1104,7 @@ sub save_as_handler ($c) { $c->shortPath($outputFilePath) )); $c->addgoodmessage($c->maketext('No changes have been made to set [_1]', $c->{setID})) - if defined $c->{setID} && $c->{setID} ne 'Undefined_Set'; + if defined $c->{setID}; } else { $c->addbadmessage($c->maketext('Unknown saveMode: [_1].', $saveMode)); return; @@ -1277,8 +1120,7 @@ sub save_as_handler ($c) { $problemPage = $c->url_for('instructor_problem_editor'); $new_file_type = 'course_info'; } elsif ($saveMode eq 'new_independent_file') { - $problemPage = - $c->url_for('instructor_problem_editor_withset_withproblem', setID => 'Undefined_Set', problemID => 1); + $problemPage = $c->url_for('instructor_problem_editor'); $new_file_type = $file_type =~ /header/ ? $file_type : 'source_path_for_problem_file'; } elsif ($saveMode eq 'new_hardcopy_theme') { $problemPage = $c->url_for('instructor_problem_editor'); diff --git a/lib/WeBWorK/ContentGenerator/Problem.pm b/lib/WeBWorK/ContentGenerator/Problem.pm index 36dcdbff64..92673a772d 100644 --- a/lib/WeBWorK/ContentGenerator/Problem.pm +++ b/lib/WeBWorK/ContentGenerator/Problem.pm @@ -10,7 +10,6 @@ WeBWorK::ContentGenerator::Problem - Allow a student to interact with a problem. use WeBWorK::Debug qw(debug); use WeBWorK::Utils qw(decodeAnswers wwRound); use WeBWorK::Utils::DateTime qw(before between after); -use WeBWorK::Utils::Files qw(path_is_subdir); use WeBWorK::Utils::JITAR qw(seq_to_jitar_id jitar_id_to_seq is_jitar_problem_hidden is_jitar_problem_closed jitar_problem_finished jitar_problem_adjusted_status); use WeBWorK::Utils::LanguageAndDirection qw(get_problem_lang_and_dir); @@ -19,7 +18,6 @@ use WeBWorK::Utils::ProblemProcessing qw(process_and_log_answer jitar_send_wa use WeBWorK::Utils::Rendering qw(getTranslatorDebuggingOptions renderPG); use WeBWorK::Utils::Sets qw(is_restricted format_set_name_display); use WeBWorK::AchievementEvaluator qw(checkForAchievements); -use WeBWorK::DB::Utils qw(global2user fake_set fake_problem); use WeBWorK::Localize; use WeBWorK::AchievementEvaluator; use WeBWorK::HTML::SingleProblemGrader; @@ -32,16 +30,6 @@ use WeBWorK::HTML::StudentNav qw(studentNav); # user - user ID of real user # effectiveUser - user ID of effective user # -# Integration with PGProblemEditor: -# -# editMode - if set, indicates alternate problem source location. -# can be "temporaryFile" or "savedFile". -# -# sourceFilePath - path to file to be edited -# problemSeed - force problem seed to value -# success - success message to display -# failure - failure message to display -# # Rendering options: # # showOldAnswers - request that last entered answer be shown (if allowed) @@ -81,7 +69,6 @@ sub can_showProblemGrader ($c, $user, $effectiveUser, $set, $problem) { return ($authz->hasPermissions($user->user_id, 'access_instructor_tools') && $authz->hasPermissions($user->user_id, 'problem_grader') - && $set->set_id ne 'Undefined_Set' && !$c->{invalidSet}); } @@ -247,7 +234,6 @@ async sub initialize ($c) { my $problemID = $c->stash('problemID'); my $userID = $c->authen->{user_id}; my $effectiveUserID = $c->param('effectiveUser'); - $c->{editMode} = $c->param('editMode'); my $user = $c->authen->{user}; my $effectiveUser = $c->authen->{effective_user}; @@ -258,12 +244,10 @@ async sub initialize ($c) { # Obtain the merged set for $effectiveUser $c->{set} = $db->getMergedSet($effectiveUserID, $setID); - # Determine if the set should be considered open. - # It is open if the user can view unopened sets or is an instructor editing a problem from the problem editor, - # or it is after the set open date and is not conditionally restricted and is not jitar hidden or closed. + # Determine if the set should be considered open. It is open if the user can view unopened sets, or it + # is after the set open date and is not conditionally restricted and is not jitar hidden or closed. return unless $authz->hasPermissions($userID, 'view_unopened_sets') - || $setID eq 'Undefined_Set' || ( after($c->{set}->open_date, $c->submitTime) && !( @@ -277,7 +261,7 @@ async sub initialize ($c) { ); # When a set is created enable_reduced_scoring is null, so we have to set it - if ($c->{set} && $c->{set}->enable_reduced_scoring ne '0' && $c->{set}->enable_reduced_scoring ne '1') { + if ($c->{set}->enable_reduced_scoring ne '0' && $c->{set}->enable_reduced_scoring ne '1') { my $globalSet = $db->getGlobalSet($c->{set}->set_id); $globalSet->enable_reduced_scoring('0'); $db->putGlobalSet($globalSet); @@ -287,88 +271,16 @@ async sub initialize ($c) { # Obtain the merged problem for the effective user. my $problem = $db->getMergedProblem($effectiveUserID, $setID, $problemID); - if ($authz->hasPermissions($userID, 'modify_problem_sets')) { - # This is the case of the problem editor for a user that can modify problem sets. - - # If a user set does not exist for this user and this set, then check - # the global set. If that does not exist, then create a fake set. If it does, then add fake user data. - unless (defined $c->{set}) { - my $userSetClass = $db->{set_user}->{record}; - my $globalSet = $db->getGlobalSet($setID); - - if (not defined $globalSet) { - $c->{set} = fake_set($db); - } else { - $c->{set} = global2user($userSetClass, $globalSet); - $c->{set}->psvn(0); - } - } - - # If a problem is not defined obtain the global problem, convert it to a user problem, and add fake user data. - unless (defined $problem) { - my $globalProblem = $db->getGlobalProblem($setID, $problemID); - - # If the global problem doesn't exist either, bail! - if (!defined $globalProblem) { - my $sourceFilePath = $c->param('sourceFilePath'); - - # These are problems from setmaker. If declared invalid, they won't come up. - if (defined $sourceFilePath) { - die 'sourceFilePath is unsafe!' - unless path_is_subdir($sourceFilePath, $ce->{courseDirs}{templates}, 1); - } else { - $c->{invalidProblem} = $c->{invalidSet} = 1; - return; - } - - $problem = fake_problem($db); - $problem->problem_id(1); - $problem->source_file($sourceFilePath); - $problem->user_id($effectiveUserID); - } else { - $problem = global2user($db->{problem_user}{record}, $globalProblem); - $problem->user_id($effectiveUserID); - $problem->problem_seed(0); - $problem->status(0); - $problem->attempted(0); - $problem->last_answer(''); - $problem->num_correct(0); - $problem->num_incorrect(0); - } - } - - # Now we're sure we have valid UserSet and UserProblem objects - - # Deal with possible editor overrides. - # If the caller is asking to override the source file, and editMode calls for a temporary file, do so. - my $sourceFilePath = $c->param('sourceFilePath'); - if (defined $c->{editMode} && $c->{editMode} eq 'temporaryFile' && defined $sourceFilePath) { - die 'sourceFilePath is unsafe!' - unless path_is_subdir($sourceFilePath, $ce->{courseDirs}->{templates}, 1); - $problem->source_file($sourceFilePath); - } - - # If the problem does not have a source file or no source file has been passed in - # then this is really an invalid problem (probably from a bad URL). - $c->{invalidProblem} = !(defined $sourceFilePath || $problem->source_file); - - # If the caller is asking to override the problem seed, do so. - my $problemSeed = $c->param('problemSeed'); - if (defined $problemSeed && $problemSeed =~ /^[+-]?\d+$/) { - $problem->problem_seed($problemSeed); - } + # Set the problem as invalid if it was not assigned to the effective user or is hidden. + $c->{invalidProblem} = + !(defined $problem && ($c->{set}->visible || $authz->hasPermissions($userID, 'view_hidden_sets'))); + $c->addbadmessage($c->maketext('This problem will not count toward your grade.')) + if !$c->{invalidProblem} && !$problem->value; + if ($authz->hasPermissions($userID, 'modify_problem_sets')) { $c->addmessage($c->{set}->visible ? $c->tag('p', class => 'font-visible m-0', $c->maketext('This set is visible to students.')) : $c->tag('p', class => 'font-hidden m-0', $c->maketext('This set is hidden from students.'))); - - } else { - # Test for additional problem validity if it's not already invalid. - $c->{invalidProblem} = - !(defined $problem && ($c->{set}->visible || $authz->hasPermissions($userID, 'view_hidden_sets'))); - - $c->addbadmessage($c->maketext('This problem will not count toward your grade.')) - if $problem && !$problem->value && !$c->{invalidProblem}; } $c->{userID} = $userID; @@ -391,8 +303,7 @@ async sub initialize ($c) { # Check for a page refresh which causes a cached form resubmission. In that case this is # not a valid submission of answers. if ( - $c->{set}->set_id ne 'Undefined_Set' - && $c->{submitAnswers} + $c->{submitAnswers} && ( !defined $formFields->{num_attempts} || (defined $formFields->{num_attempts} @@ -472,12 +383,12 @@ async sub initialize ($c) { my $rerandomizePeriod = $ce->{pg}{options}{periodicRandomizationPeriod} // 0; $problem->{prPeriod} = $ce->{problemDefaults}{prPeriod} - if (defined $problem->{prPeriod} && $problem->{prPeriod} =~ /^\s*$/); + if defined $problem->{prPeriod} && $problem->{prPeriod} =~ /^\s*$/; $rerandomizePeriod = $problem->{prPeriod} - if (defined $problem->{prPeriod} && $problem->{prPeriod} > -1); + if defined $problem->{prPeriod} && $problem->{prPeriod} > -1; - $prEnabled = 0 if ($rerandomizePeriod < 1 || $c->{editMode}); + $prEnabled = 0 if $rerandomizePeriod < 1; my $canUpdateProblemRandomization = $userID eq $effectiveUserID || $authz->hasPermissions($userID, 'record_answers_when_acting_as_student'); @@ -637,7 +548,7 @@ sub siblings ($c) { my @problemRecords = $db->getMergedProblemsWhere({ user_id => $eUserID, set_id => $setID }, 'problem_id'); my @problemIDs = map { $_->problem_id } @problemRecords; - my $isJitarSet = $setID ne 'Undefined_Set' && $c->{set}->assignment_type eq 'jitar' ? 1 : 0; + my $isJitarSet = $c->{set}->assignment_type eq 'jitar' ? 1 : 0; # Variables for the progress bar my $num_of_problems = 0; @@ -761,48 +672,42 @@ sub nav ($c, $args) { my $ce = $c->ce; my $authz = $c->authz; - my $setID = $c->{set}->set_id; - my $problemID = $c->{problem}->problem_id; - my $userID = $c->authen->{user_id}; - my $eUserID = $c->param('effectiveUser'); - - my $mergedSet = $db->getMergedSet($eUserID, $setID); - return '' if !$mergedSet; - - my $isJitarSet = $mergedSet->assignment_type eq 'jitar'; + my $setID = $c->{set}->set_id; + my $problemID = $c->{problem}->problem_id; + my $userID = $c->authen->{user_id}; + my $eUserID = $c->param('effectiveUser'); + my $isJitarSet = $c->{set}->assignment_type eq 'jitar'; my ($prevID, $nextID); # Find the next or previous problem, and determine if it is actually open for a jitar set. - if (!$c->{invalidProblem}) { - my @problemIDs = - map { $_->[3] } $db->listUserProblemsWhere({ user_id => $eUserID, set_id => $setID }, 'problem_id'); - - if ($isJitarSet) { - my @processedProblemIDs; - for my $id (@problemIDs) { - push @processedProblemIDs, $id - unless !$authz->hasPermissions($eUserID, 'view_unopened_sets') - && is_jitar_problem_hidden($db, $eUserID, $setID, $id); - } - @problemIDs = @processedProblemIDs; + my @problemIDs = + map { $_->[3] } $db->listUserProblemsWhere({ user_id => $eUserID, set_id => $setID }, 'problem_id'); + + if ($isJitarSet) { + my @processedProblemIDs; + for my $id (@problemIDs) { + push @processedProblemIDs, $id + unless !$authz->hasPermissions($eUserID, 'view_unopened_sets') + && is_jitar_problem_hidden($db, $eUserID, $setID, $id); } + @problemIDs = @processedProblemIDs; + } - my $curr_index = 0; - - for (my $i = 0; $i <= $#problemIDs; $i++) { - $curr_index = $i if $problemIDs[$i] == $problemID; - } + my $curr_index = 0; - $prevID = $problemIDs[ $curr_index - 1 ] if $curr_index - 1 >= 0; - $nextID = $problemIDs[ $curr_index + 1 ] if $curr_index + 1 <= $#problemIDs; - $nextID = '' - if ($isJitarSet - && $nextID - && !$authz->hasPermissions($eUserID, 'view_unopened_sets') - && is_jitar_problem_closed($db, $ce, $eUserID, $setID, $nextID)); + for (my $i = 0; $i <= $#problemIDs; $i++) { + $curr_index = $i if $problemIDs[$i] == $problemID; } + $prevID = $problemIDs[ $curr_index - 1 ] if $curr_index - 1 >= 0; + $nextID = $problemIDs[ $curr_index + 1 ] if $curr_index + 1 <= $#problemIDs; + $nextID = '' + if ($isJitarSet + && $nextID + && !$authz->hasPermissions($eUserID, 'view_unopened_sets') + && is_jitar_problem_closed($db, $ce, $eUserID, $setID, $nextID)); + my @links; if ($prevID) { @@ -813,12 +718,8 @@ sub nav ($c, $args) { push @links, $c->maketext('Previous Problem'), '', $c->maketext('Previous Problem'); } - if (defined $setID && $setID ne 'Undefined_Set') { - push @links, $c->maketext('Problem List'), $c->url_for('problem_list', setID => $setID), - $c->maketext('Problem List'); - } else { - push @links, $c->maketext('Problem List'), '', $c->maketext('Problem List'); - } + push @links, $c->maketext('Problem List'), $c->url_for('problem_list', setID => $setID), + $c->maketext('Problem List'); if ($nextID) { push @links, $c->maketext('Next Problem'), @@ -910,17 +811,11 @@ sub page_title ($c) { 'span', class => 'ms-2', $c->link_to( - $c->maketext('Edit') => $c->systemLink( - $c->url_for( - 'instructor_problem_editor_withset_withproblem', - setID => $c->{set}->set_id, - problemID => $c->{problem}->problem_id - ), - # If we are here without a real homework set, carry that through. - $c->{set}->set_id eq 'Undefined_Set' - ? (params => [ 'sourceFilePath' => $c->param('sourceFilePath') ]) - : () - ), + $c->maketext('Edit') => $c->systemLink($c->url_for( + 'instructor_problem_editor_withset_withproblem', + setID => $c->{set}->set_id, + problemID => $c->{problem}->problem_id + )), target => 'WW_Editor', class => 'btn btn-sm btn-secondary' ) @@ -1084,7 +979,7 @@ sub output_score_summary ($c) { } # Print jitar specific information for students (and notify instructor if necessary). - if ($set->set_id ne 'Undefined_Set' && $set->assignment_type() eq 'jitar') { + if ($set->assignment_type eq 'jitar') { my @problemIDs = map { $_->[3] } $db->listUserProblemsWhere({ user_id => $effectiveUser, set_id => $set->set_id }, 'problem_id'); @@ -1228,15 +1123,6 @@ sub output_score_summary ($c) { sub output_misc ($c) { my $output = $c->c; - # Only allow file editing for users that have the permission to modify problem sets. - if ($c->authz->hasPermissions($c->authen->{user_id}, 'modify_problem_sets')) { - push(@$output, $c->hidden_field(editMode => $c->{editMode})) - if defined $c->{editMode} && $c->{editMode} eq 'temporaryFile'; - push(@$output, $c->hidden_field(sourceFilePath => $c->{problem}{source_file})) - if defined $c->{problem}{source_file}; - push(@$output, $c->hidden_field(problemSeed => $c->param('problemSeed'))) if defined $c->param('problemSeed'); - } - # Make sure the student nav filter setting is preserved when the problem form is submitted. push(@$output, $c->hidden_field(studentNavFilter => $c->param('studentNavFilter'))) if $c->param('studentNavFilter'); @@ -1325,7 +1211,7 @@ sub output_summary ($c) { ) ) if $c->{resubmitDetected}; - if ($c->{set}->set_id ne 'Undefined_Set' && $c->{set}->assignment_type eq 'jitar') { + if ($c->{set}->assignment_type eq 'jitar') { my $hasChildren = 0; my @problemIDs = @@ -1387,8 +1273,7 @@ sub output_achievement_message ($c) { # check to see if there are new achievements and output them. if ($c->ce->{achievementsEnabled} && $c->{will}{recordAnswers} - && $c->{submitAnswers} - && $c->{problem}->set_id ne 'Undefined_Set') + && $c->{submitAnswers}) { return checkForAchievements($c->{problem}, $c); } @@ -1396,22 +1281,6 @@ sub output_achievement_message ($c) { return ''; } -# Output a custom edit message -sub output_custom_edit_message ($c) { - if ($c->authz->hasPermissions($c->authen->{user_id}, 'modify_problem_sets') - && $c->{editMode} - && $c->{editMode} eq 'temporaryFile') - { - return $c->tag( - 'p', - class => 'temporaryFile', - $c->maketext('Viewing temporary file: [_1]', $c->{problem}->source_file) - ); - } - - return ''; -} - # Output the "Show Past Answers" button sub output_past_answer_button ($c) { if ($c->authz->hasPermissions($c->authen->{user_id}, 'view_answers')) { diff --git a/lib/WeBWorK/ContentGenerator/ProblemSet.pm b/lib/WeBWorK/ContentGenerator/ProblemSet.pm index 0dc9c44016..373b882cc9 100644 --- a/lib/WeBWorK/ContentGenerator/ProblemSet.pm +++ b/lib/WeBWorK/ContentGenerator/ProblemSet.pm @@ -11,7 +11,6 @@ problem set. use WeBWorK::Debug qw(debug); use WeBWorK::Utils qw(wwRound); use WeBWorK::Utils::DateTime qw(after); -use WeBWorK::Utils::Files qw(path_is_subdir); use WeBWorK::Utils::Rendering qw(renderPG); use WeBWorK::Utils::Sets qw(is_restricted grade_set format_set_name_display test_version_is_complete); use WeBWorK::Localize; @@ -116,19 +115,6 @@ async sub initialize ($c) { ? $ce->{webworkFiles}{screenSnippets}{setHeader} : $c->{set}->set_header; - if ($authz->hasPermissions($userID, 'modify_problem_sets')) { - if (defined $c->param('editMode') && $c->param('editMode') eq 'temporaryFile') { - $screenSetHeader = $c->param('sourceFilePath'); - $screenSetHeader = "$ce->{courseDirs}{templates}/$screenSetHeader" unless $screenSetHeader =~ m!^/!; - die 'sourceFilePath is unsafe!' unless path_is_subdir($screenSetHeader, $ce->{courseDirs}{templates}); - $c->addmessage($c->tag( - 'p', - class => 'temporaryFile', - $c->maketext('Viewing temporary file: [_1]', $screenSetHeader) - )); - } - } - return unless $screenSetHeader; my $problem = WeBWorK::DB::Record::UserProblem->new( diff --git a/lib/WeBWorK/ContentGenerator/ProblemSets.pm b/lib/WeBWorK/ContentGenerator/ProblemSets.pm index 1e24ab1aad..22a956d8bb 100644 --- a/lib/WeBWorK/ContentGenerator/ProblemSets.pm +++ b/lib/WeBWorK/ContentGenerator/ProblemSets.pm @@ -10,7 +10,7 @@ WeBWorK::ContentGenerator::ProblemSets - Display a list of built problem sets. use WeBWorK::Debug qw(debug); use WeBWorK::Utils qw(sortByName); use WeBWorK::Utils::DateTime qw(after); -use WeBWorK::Utils::Files qw(readFile path_is_subdir); +use WeBWorK::Utils::Files qw(readFile); use WeBWorK::Utils::Sets qw(restricted_set_message); use WeBWorK::Localize; @@ -82,25 +82,6 @@ sub initialize ($c) { my $course_info_path = "$ce->{courseDirs}{templates}/$ce->{courseFiles}{course_info}"; - if ($authz->hasPermissions($user, 'access_instructor_tools')) { - if (defined $c->param('editMode') && $c->param('editMode') eq 'temporaryFile') { - $course_info_path = $c->param('sourceFilePath'); - $course_info_path = "$ce->{courseDirs}{templates}/$course_info_path" - unless $course_info_path =~ m!^/!; - - unless (path_is_subdir($course_info_path, $ce->{courseDirs}{templates})) { - $c->addbadmessage('sourceFilePath is unsafe!'); - return ''; - } - - $c->addmessage($c->tag( - 'p', - class => 'temporaryFile', - $c->maketext('Viewing temporary file: [_1]', $course_info_path) - )); - } - } - if (-f $course_info_path) { $c->stash->{course_info_contents} = eval { readFile($course_info_path) }; $c->stash->{course_info_error} = $@ if $@; diff --git a/templates/ContentGenerator/GatewayQuiz.html.ep b/templates/ContentGenerator/GatewayQuiz.html.ep index 2647fb4338..21248b4678 100644 --- a/templates/ContentGenerator/GatewayQuiz.html.ep +++ b/templates/ContentGenerator/GatewayQuiz.html.ep @@ -366,7 +366,7 @@ % } % - % if ($c->{can}{showWork} && $setID ne 'Undefined_Set') { + % if ($c->{can}{showWork}) {
@@ -853,6 +853,6 @@ % % # If achievements enabled, check to see if there are new ones and output them. Use the first % # problem to seed the data. However, all of the problems will be provided to the achievement evaluator. -% if ($ce->{achievementsEnabled} && $c->{will}{recordAnswers} && $c->{submitAnswers} && $setID ne 'Undefined_Set') { +% if ($ce->{achievementsEnabled} && $c->{will}{recordAnswers} && $c->{submitAnswers}) { <%= checkForAchievements($problems->[0], $c, setVersion => $setVersionID) =%> % } diff --git a/templates/ContentGenerator/Instructor/PGProblemEditor/save_as_form.html.ep b/templates/ContentGenerator/Instructor/PGProblemEditor/save_as_form.html.ep index 0d1a7ffc49..c47b441bc4 100644 --- a/templates/ContentGenerator/Instructor/PGProblemEditor/save_as_form.html.ep +++ b/templates/ContentGenerator/Instructor/PGProblemEditor/save_as_form.html.ep @@ -20,9 +20,9 @@ % # If it is an absolute path make it relative. % $shortFilePath =~ s|^/*|| if $shortFilePath =~ m|^/|; % -% # Don't add to or replace problems in sets or replace set/hardcopy headers if the set is the Undefined_Set or +% # Don't add to or replace problems in sets or replace set/hardcopy headers if the set is undefined or % # if the problem is the blank_problem or a sample problem. -% my $fileIsAssociatedWithSet = defined $c->{setID} && $c->{setID} ne 'Undefined_Set' && !$isTemplate; +% my $fileIsAssociatedWithSet = defined $c->{setID} && !$isTemplate; %
diff --git a/templates/ContentGenerator/Instructor/PGProblemEditor/save_form.html.ep b/templates/ContentGenerator/Instructor/PGProblemEditor/save_form.html.ep index fc4d746893..c628b0128d 100644 --- a/templates/ContentGenerator/Instructor/PGProblemEditor/save_form.html.ep +++ b/templates/ContentGenerator/Instructor/PGProblemEditor/save_form.html.ep @@ -6,16 +6,6 @@
<%== maketext('Save to [_1] and View', tag('b', dir => 'ltr', $c->shortPath($c->{editFilePath}))) =%>
- % unless ($c->{file_type} eq 'hardcopy_theme') { -
- <%= check_box - newWindowSave => 1, - id => 'newWindowSave', - class => 'form-check-input', - $c->{file_type} eq 'hardcopy_header' ? (checked => undef) : () =%> - <%= label_for newWindowSave => maketext('Open in new window'), class => 'form-check-label' =%> -
- % } <%= hidden_field 'action.save.source_file' => $c->{editFilePath} =%> % # Backup options diff --git a/templates/ContentGenerator/Instructor/PGProblemEditor/view_form.html.ep b/templates/ContentGenerator/Instructor/PGProblemEditor/view_form.html.ep index 86044cb581..a5208323f5 100644 --- a/templates/ContentGenerator/Instructor/PGProblemEditor/view_form.html.ep +++ b/templates/ContentGenerator/Instructor/PGProblemEditor/view_form.html.ep @@ -35,12 +35,4 @@
% } - % unless ($c->{file_type} eq 'hardcopy_theme') { -
-
- <%= check_box 'newWindowView' => 1, id => 'newWindowView', class => 'form-check-input' =%> - <%= label_for newWindowView => maketext('Open in new window'), class => 'form-check-label' =%> -
-
- % }
diff --git a/templates/ContentGenerator/Instructor/ProblemSetDetail.html.ep b/templates/ContentGenerator/Instructor/ProblemSetDetail.html.ep index 81d5824dc6..f30c026e6a 100644 --- a/templates/ContentGenerator/Instructor/ProblemSetDetail.html.ep +++ b/templates/ContentGenerator/Instructor/ProblemSetDetail.html.ep @@ -579,8 +579,7 @@ <% end =%> % my $problemLink; % if ($isGatewaySet) { - % # If editing a set version, then open the actual set version. - % # Otherwise, the best that can be done is to use an undefined set. + % # Only link to a test when editing a test version. % if ($editingSetVersion) { % my ($problemNumber, $pageNumber) = % get_test_problem_position($db, $problemToShow); @@ -596,25 +595,6 @@ % currentPage => $pageNumber % } % )->fragment("prob$problemNumber"); - % } else { - % $problemLink = $c->systemLink( - % url_for( - % 'gateway_quiz', - % setID => 'Undefined_Set', - % problemID => '1' - % ), - % params => { - % effectiveUser => @editForUser == 1 ? $editForUser[0] - % : $authen->{user_id}, - % problemSeed => $problemToShow - % ? $problemToShow->problem_seed - % : '', - % sourceFilePath => $problemToShow - % && $problemToShow->source_file - % ? $problemToShow->source_file - % : $globalProblems->{$problemID}->source_file - % } - % ); % } % } else { % $problemLink = $c->systemLink( @@ -630,19 +610,21 @@ % } % ); % } - <%= link_to $problemLink, - class => 'psd_view btn btn-secondary btn-sm' - . (@editForUser ? '' : ' px-1 px-sm-2'), - target => 'WW_View', - data => { - bs_toggle => 'tooltip', - bs_placement => 'top', - bs_title => maketext('Open in New Window') - }, - begin =%> - - <%= maketext('View') =%> - <% end =%> + % if ($problemLink) { + <%= link_to $problemLink, + class => 'psd_view btn btn-secondary btn-sm' + . (@editForUser ? '' : ' px-1 px-sm-2'), + target => 'WW_View', + data => { + bs_toggle => 'tooltip', + bs_placement => 'top', + bs_title => maketext('Open in New Window') + }, + begin =%> + + <%= maketext('View') =%> + <% end =%> + % } % } % if ($authz->hasPermissions($authen->{user_id}, 'problem_grader')) { <%= link_to $c->systemLink(url_for( diff --git a/templates/ContentGenerator/Instructor/SetMaker/problem_row.html.ep b/templates/ContentGenerator/Instructor/SetMaker/problem_row.html.ep index 33d5822c4e..ed4ba0ceed 100644 --- a/templates/ContentGenerator/Instructor/SetMaker/problem_row.html.ep +++ b/templates/ContentGenerator/Instructor/SetMaker/problem_row.html.ep @@ -8,11 +8,6 @@ % ($isMO, $isstatic) = $c->getDBextras($sourceFileName); % } % -% # Determine if the target set is a gateway assignment. -% my $setRecord = - % (defined param('local_sets') && param('local_sets') ne '') ? $db->getGlobalSet(param('local_sets')) : undef; -% my $isGatewaySet = defined $setRecord && $setRecord->assignment_type =~ /gateway/; -% % my $noshowclass = $sourceFileData->{morelt} ? "MLT$sourceFileData->{morelt}" : "NS$cnt"; % if ($sourceFileData->{children}) { % my $numchild = @{ $sourceFileData->{children} }; @@ -278,11 +273,7 @@ % } <%= link_to $c->systemLink( - url_for( - 'instructor_problem_editor_withset_withproblem', - setID => 'Undefined_Set', - problemID => '1' - ), + url_for('instructor_problem_editor'), params => { sourceFilePath => $sourceFileName, problemSeed => $c->{problem_seed} @@ -300,30 +291,6 @@ <%= maketext('Edit') =%> <% end =%> - <%= link_to $c->systemLink( - url_for( - $isGatewaySet ? 'gateway_quiz' : 'problem_detail', - setID => 'Undefined_Set', - $isGatewaySet ? () : (problemID => '1') - ), - params => { - effectiveUser => $authen->{user_id}, - editMode => 'SetMaker', - problemSeed => $c->{problem_seed}, - sourceFilePath => $sourceFileName, - } - ), - id => "tryit$cnt", - class => 'text-decoration-none btn btn-sm btn-secondary', - target => 'WW_View', - data => { - bs_title => maketext('Try it'), - bs_toggle => 'tooltip', - bs_placement => 'top' - }, - begin =%> - - <% end =%> <%== maketext('Debugging information is shown below.') %>
% } -
<%= $c->output_custom_edit_message %>
<%= $c->output_summary %>
<%= $c->output_achievement_message %>
diff --git a/templates/HelpFiles/InstructorPGProblemEditor.html.ep b/templates/HelpFiles/InstructorPGProblemEditor.html.ep index 38cad5850f..6ea0c98639 100644 --- a/templates/HelpFiles/InstructorPGProblemEditor.html.ep +++ b/templates/HelpFiles/InstructorPGProblemEditor.html.ep @@ -151,9 +151,7 @@

<%= maketext('Render or re-render the problem, course info file, or set header being edited. This does not ' . 'change the permanent file on the disk. You can view different versions of the same problem by ' - . 'changing the seed. You can also change the manner in which the mathematics is typeset. If "Open ' - . 'in new window" is checked, then the problem will open in a new tab or window showing the ' - . 'problem as it will be rendered for students in the set.') =%> + . 'changing the seed. You can also change the manner in which the mathematics is typeset.') =%>

<%= maketext('This tab is available for all file types except hardcopy header files.') =%> @@ -205,12 +203,11 @@

<%= maketext('Save the contents of the editor window to the file on disk and re-render the problem. If ' - . '"Open in new window" is checked, then the problem will open in a new tab or window showing the ' - . 'problem as it will be rendered for students in the set. If "Create backup" is checked then a ' - . 'backup of the current file on disk will be created before overwriting it with the contents of ' - . 'the editor window. If a backup already exists, then a "Delete oldest backup" check box will be ' - . 'present. If that is checked, then the oldest backup will be deleted when "Save" is clicked. ' - . 'These backups can be reverted to and managed on the "Revert" tab.') =%> + . '"Create backup" is checked then a backup of the current file on disk will be created before ' + . 'overwriting it with the contents of the editor window. If a backup already exists, then a ' + . '"Delete oldest backup" check box will be present. If that is checked, then the oldest backup ' + . 'will be deleted when "Save" is clicked. These backups can be reverted to and managed on the ' + . '"Revert" tab.') =%>

<%= maketext('This tab is available for all files, but is only active for files in the course templates '