[system] / branches / gage_dev / webwork2 / lib / WeBWorK / ContentGenerator / Problem.pm Repository:
ViewVC logotype

Diff of /branches/gage_dev/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

Revision 2767 Revision 6149
1################################################################################ 1################################################################################
2# WeBWorK Online Homework Delivery System 2# WeBWorK Online Homework Delivery System
3# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ 3# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
4# $CVSHeader$ 4# $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Problem.pm,v 1.218 2009/07/18 02:52:51 gage Exp $
5# 5#
6# This program is free software; you can redistribute it and/or modify it under 6# This program is free software; you can redistribute it and/or modify it under
7# the terms of either: (a) the GNU General Public License as published by the 7# the terms of either: (a) the GNU General Public License as published by the
8# Free Software Foundation; either version 2, or (at your option) any later 8# Free Software Foundation; either version 2, or (at your option) any later
9# version, or (b) the "Artistic License" which comes with this package. 9# version, or (b) the "Artistic License" which comes with this package.
23 23
24=cut 24=cut
25 25
26use strict; 26use strict;
27use warnings; 27use warnings;
28use CGI qw(); 28#use CGI qw(-nosticky );
29use WeBWorK::CGI;
29use File::Path qw(rmtree); 30use File::Path qw(rmtree);
31use WeBWorK::Debug;
30use WeBWorK::Form; 32use WeBWorK::Form;
31use WeBWorK::PG; 33use WeBWorK::PG;
32use WeBWorK::PG::ImageGenerator; 34use WeBWorK::PG::ImageGenerator;
33use WeBWorK::PG::IO; 35use WeBWorK::PG::IO;
34use WeBWorK::Utils qw(writeLog writeCourseLog encodeAnswers decodeAnswers ref2string makeTempDirectory); 36use WeBWorK::Utils qw(readFile writeLog writeCourseLog encodeAnswers decodeAnswers
37 ref2string makeTempDirectory path_is_subdir sortByName before after between);
35use WeBWorK::DB::Utils qw(global2user user2global findDefaults); 38use WeBWorK::DB::Utils qw(global2user user2global);
36use WeBWorK::Timing; 39use URI::Escape;
37 40
38use WeBWorK::Utils::Tasks qw(fake_set fake_problem); 41use WeBWorK::Utils::Tasks qw(fake_set fake_problem);
39 42
40################################################################################ 43################################################################################
41# CGI param interface to this module (up-to-date as of v1.153) 44# CGI param interface to this module (up-to-date as of v1.153)
82# Subroutines to determine if a user "can" perform an action. Each subroutine is 85# Subroutines to determine if a user "can" perform an action. Each subroutine is
83# called with the following arguments: 86# called with the following arguments:
84# 87#
85# ($self, $User, $EffectiveUser, $Set, $Problem) 88# ($self, $User, $EffectiveUser, $Set, $Problem)
86 89
90# Note that significant parts of the "can" methods are lifted into the
91# GatewayQuiz module. It isn't direct, however, because of the necessity
92# of dealing with versioning there.
93
87sub can_showOldAnswers { 94sub can_showOldAnswers {
88 #my ($self, $User, $EffectiveUser, $Set, $Problem) = @_; 95 #my ($self, $User, $EffectiveUser, $Set, $Problem) = @_;
89 96
90 return 1; 97 return 1;
91} 98}
162 } elsif (after($Set->answer_date)) { 169 } elsif (after($Set->answer_date)) {
163 return $authz->hasPermissions($User->user_id, "check_answers_after_answer_date"); 170 return $authz->hasPermissions($User->user_id, "check_answers_after_answer_date");
164 } 171 }
165} 172}
166 173
167# Helper functions for calculating times 174# Reset the default in some cases
168sub before { return time <= $_[0] } 175sub set_showOldAnswers_default {
169sub after { return time >= $_[0] } 176 my ($self, $ce, $userName, $authz, $set) = @_;
170sub between { my $t = time; return $t > $_[0] && $t < $_[1] } 177 # these people always use the system/course default, so don't
178 # override the value of ...->{showOldAnswers}
179 return if $authz->hasPermissions($userName, "can_always_use_show_old_answers_default");
180 # this person should always default to 0
181 $ce->{pg}->{options}->{showOldAnswers} = 0
182 unless ($authz->hasPermissions($userName, "can_show_old_answers_by_default"));
183 # we are after the due date, so default to not showing it
184 $ce->{pg}->{options}->{showOldAnswers} = 0 if $set->{due_date} && after($set->{due_date});
185}
171 186
172################################################################################ 187################################################################################
173# output utilities 188# output utilities
174################################################################################ 189################################################################################
190
191# Note: the substance of attemptResults is lifted into GatewayQuiz.pm,
192# with some changes to the output format
175 193
176sub attemptResults { 194sub attemptResults {
177 my $self = shift; 195 my $self = shift;
178 my $pg = shift; 196 my $pg = shift;
179 my $showAttemptAnswers = shift; 197 my $showAttemptAnswers = shift;
214 $header .= $showAttemptResults ? CGI::th("Result") : ""; 232 $header .= $showAttemptResults ? CGI::th("Result") : "";
215 $header .= $showMessages ? CGI::th("Messages") : ""; 233 $header .= $showMessages ? CGI::th("Messages") : "";
216 my $fully = ''; 234 my $fully = '';
217 my @tableRows = ( $header ); 235 my @tableRows = ( $header );
218 my $numCorrect = 0; 236 my $numCorrect = 0;
237 my $numBlanks =0;
238 my $tthPreambleCache;
219 foreach my $name (@answerNames) { 239 foreach my $name (@answerNames) {
220 my $answerResult = $pg->{answers}->{$name}; 240 my $answerResult = $pg->{answers}->{$name};
221 my $studentAnswer = $answerResult->{student_ans}; # original_student_ans 241 my $studentAnswer = $answerResult->{student_ans}; # original_student_ans
222 my $preview = ($showAttemptPreview 242 my $preview = ($showAttemptPreview
223 ? $self->previewAnswer($answerResult, $imgGen) 243 ? $self->previewAnswer($answerResult, $imgGen, \$tthPreambleCache)
224 : ""); 244 : "");
225 my $correctAnswer = $answerResult->{correct_ans}; 245 my $correctAnswer = $answerResult->{correct_ans};
226 my $answerScore = $answerResult->{score}; 246 my $answerScore = $answerResult->{score};
227 my $answerMessage = $showMessages ? $answerResult->{ans_message} : ""; 247 my $answerMessage = $showMessages ? $answerResult->{ans_message} : "";
228 $answerMessage =~ s/\n/<BR>/g; 248 $answerMessage =~ s/\n/<BR>/g;
229 $numCorrect += $answerScore >= 1; 249 $numCorrect += $answerScore >= 1;
250 $numBlanks++ unless $studentAnswer =~/\S/ || $answerScore >= 1; # unless student answer contains entry
230 my $resultString = $answerScore >= 1 ? "correct" : 251 my $resultString = $answerScore >= 1 ? "correct" :
231 $answerScore > 0 ? int($answerScore*100)."% correct" : 252 $answerScore > 0 ? int($answerScore*100)."% correct" :
232 "incorrect"; 253 "incorrect";
233 $fully = 'completely ' if $answerScore >0 and $answerScore < 1; 254 $fully = 'completely ' if $answerScore >0 and $answerScore < 1;
234 255
253 my $scorePercent = sprintf("%.0f%%", $problemResult->{score} * 100); 274 my $scorePercent = sprintf("%.0f%%", $problemResult->{score} * 100);
254# FIXME -- I left the old code in in case we have to back out. 275# FIXME -- I left the old code in in case we have to back out.
255# my $summary = "On this attempt, you answered $numCorrect out of " 276# my $summary = "On this attempt, you answered $numCorrect out of "
256# . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent."; 277# . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent.";
257 my $summary = ""; 278 my $summary = "";
279 unless (defined($problemResult->{summary}) and $problemResult->{summary} =~ /\S/) {
258 if (scalar @answerNames == 1) { 280 if (scalar @answerNames == 1) { #default messages
259 if ($numCorrect == scalar @answerNames) { 281 if ($numCorrect == scalar @answerNames) {
260 $summary .= CGI::div({class=>"ResultsWithoutError"},"The above answer is correct."); 282 $summary .= CGI::div({class=>"ResultsWithoutError"},"The answer above is correct.");
261 } else { 283 } else {
262 $summary .= CGI::div({class=>"ResultsWithError"},"The above answer is NOT ${fully}correct."); 284 $summary .= CGI::div({class=>"ResultsWithError"},"The answer above is NOT ${fully}correct.");
263 } 285 }
286 } else {
287 if ($numCorrect == scalar @answerNames) {
288 $summary .= CGI::div({class=>"ResultsWithoutError"},"All of the answers above are correct.");
289 }
290 unless ($numCorrect + $numBlanks == scalar( @answerNames)) {
291 $summary .= CGI::div({class=>"ResultsWithError"},"At least one of the answers above is NOT ${fully}correct.");
292 }
293 if ($numBlanks) {
294 my $s = ($numBlanks>1)?'':'s';
295 $summary .= CGI::div({class=>"ResultsAlert"},"$numBlanks of the questions remain$s unanswered.");
296 }
297 }
264 } else { 298 } else {
265 if ($numCorrect == scalar @answerNames) { 299 $summary = $problemResult->{summary}; # summary has been defined by grader
266 $summary .= CGI::div({class=>"ResultsWithoutError"},"All of the above answers are correct.");
267 } else {
268 $summary .= CGI::div({class=>"ResultsWithError"},"At least one of the above answers is NOT ${fully}correct.");
269 }
270 } 300 }
271
272 return 301 return
273 CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows)) 302 CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows))
274 . ($showSummary ? CGI::p({class=>'emphasis'},$summary) : ""); 303 . ($showSummary ? CGI::p({class=>'attemptResultsSummary'},$summary) : "");
275} 304}
276 305
277sub viewOptions {
278 my ($self) = @_;
279 my $ce = $self->r->ce;
280 306
281 # don't show options if we don't have anything to show 307# Note: previewAnswer is lifted into GatewayQuiz.pm
282 return if $self->{invalidSet} or $self->{invalidProblem};
283 return unless $self->{isOpen};
284
285 my $displayMode = $self->{displayMode};
286 my %must = %{ $self->{must} };
287 my %can = %{ $self->{can} };
288 my %will = %{ $self->{will} };
289
290 my $optionLine;
291 $can{showOldAnswers} and $optionLine .= join "",
292 "Show: &nbsp;".CGI::br(),
293 CGI::checkbox(
294 -name => "showOldAnswers",
295 -checked => $will{showOldAnswers},
296 -label => "Saved answers",
297 ), "&nbsp;&nbsp;".CGI::br();
298
299 $optionLine and $optionLine .= join "", CGI::br();
300
301 my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()};
302 my @active_modes = grep { exists $display_modes{$_} }
303 @{$ce->{pg}->{displayModes}};
304 my $modeLine = (scalar(@active_modes) > 1) ?
305 "View&nbsp;equations&nbsp;as:&nbsp;&nbsp;&nbsp;&nbsp;".CGI::br().
306 CGI::radio_group(
307 -name => "displayMode",
308 -values => \@active_modes,
309 -default => $displayMode,
310 -linebreak=>'true',
311 -labels => {
312 plainText => "plain",
313 formattedText => "formatted",
314 images => "images",
315 jsMath => "jsMath",
316 asciimath => "asciimath",
317 },
318 ). CGI::br().CGI::hr() : '';
319
320 return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"},
321 $modeLine,
322 $optionLine,
323 CGI::submit(-name=>"redisplay", -label=>"Apply Options"),
324 );
325}
326 308
327sub previewAnswer { 309sub previewAnswer {
328 my ($self, $answerResult, $imgGen) = @_; 310 my ($self, $answerResult, $imgGen, $tthPreambleCache) = @_;
329 my $ce = $self->r->ce; 311 my $ce = $self->r->ce;
330 my $effectiveUser = $self->{effectiveUser}; 312 my $effectiveUser = $self->{effectiveUser};
331 my $set = $self->{set}; 313 my $set = $self->{set};
332 my $problem = $self->{problem}; 314 my $problem = $self->{problem};
333 my $displayMode = $self->{displayMode}; 315 my $displayMode = $self->{displayMode};
342 return "" unless defined $tex and $tex ne ""; 324 return "" unless defined $tex and $tex ne "";
343 325
344 if ($displayMode eq "plainText") { 326 if ($displayMode eq "plainText") {
345 return $tex; 327 return $tex;
346 } elsif ($displayMode eq "formattedText") { 328 } elsif ($displayMode eq "formattedText") {
329
330 # read the TTH preamble, or use the cached copy passed in from the caller
331 my $tthPreamble='';
332 if (defined $$tthPreambleCache) {
333 $tthPreamble = $$tthPreambleCache;
334 } else {
335 my $tthPreambleFile = $ce->{courseDirs}->{templates} . "/tthPreamble.tex";
336 if (-r $tthPreambleFile) {
337 $tthPreamble = readFile($tthPreambleFile);
338 # thanks to Jim Martino. each line in the definition file should end with
339 #a % to prevent adding supurious paragraphs to output:
340 $tthPreamble =~ s/(.)\n/$1%\n/g;
341 # solves the problem if the file doesn't end with a return:
342 $tthPreamble .="%\n";
343 # store preamble in cache:
344 $$tthPreambleCache = $tthPreamble;
345 } else {
346 }
347 }
348
349 # construct TTH command line
347 my $tthCommand = $ce->{externalPrograms}->{tth} 350 my $tthCommand = $ce->{externalPrograms}->{tth}
348 . " -L -f5 -r 2> /dev/null <<END_OF_INPUT; echo > /dev/null\n" 351 . " -L -f5 -u -r 2> /dev/null <<END_OF_INPUT; echo > /dev/null\n"
349 . "\\(".$tex."\\)\n" 352 . $tthPreamble . "\\[" . $tex . "\\]\n"
350 . "END_OF_INPUT\n"; 353 . "END_OF_INPUT\n";
351 354
352 # call tth 355 # call tth
353 my $result = `$tthCommand`; 356 my $result = `$tthCommand`;
354 if ($?) { 357 if ($?) {
355 return "<b>[tth failed: $? $@]</b>"; 358 return "<b>[tth failed: $? $@]</b>";
356 } else { 359 } else {
360 # avoid border problems in tables and remove unneeded initial <br>
361 $result =~ s/(<table [^>]*)>/$1 CLASS="ArrayLayout">/gi;
362 $result =~ s!\s*<br clear="all" />!!;
357 return $result; 363 return $result;
358 } 364 }
365
359 } elsif ($displayMode eq "images") { 366 } elsif ($displayMode eq "images") {
360 $imgGen->add($tex); 367 $imgGen->add($tex);
361 } elsif ($displayMode eq "jsMath") { 368 } elsif ($displayMode eq "jsMath") {
362 return '<DIV CLASS="math">'.$tex.'</DIV>' ; 369 $tex =~ s/</&lt;/g; $tex =~ s/>/&gt;/g;
370 return '<SPAN CLASS="math">\\displaystyle{'.$tex.'}</SPAN>';
363 } 371 }
364} 372}
365 373
366################################################################################ 374################################################################################
367# Template escape implementations 375# Template escape implementations
378 my $setName = $urlpath->arg("setID"); 386 my $setName = $urlpath->arg("setID");
379 my $problemNumber = $r->urlpath->arg("problemID"); 387 my $problemNumber = $r->urlpath->arg("problemID");
380 my $userName = $r->param('user'); 388 my $userName = $r->param('user');
381 my $effectiveUserName = $r->param('effectiveUser'); 389 my $effectiveUserName = $r->param('effectiveUser');
382 my $key = $r->param('key'); 390 my $key = $r->param('key');
391 my $editMode = $r->param("editMode");
383 392
384 my $user = $db->getUser($userName); # checked 393 my $user = $db->getUser($userName); # checked
385 die "record for user $userName (real user) does not exist." 394 die "record for user $userName (real user) does not exist."
386 unless defined $user; 395 unless defined $user;
387 396
389 die "record for user $effectiveUserName (effective user) does not exist." 398 die "record for user $effectiveUserName (effective user) does not exist."
390 unless defined $effectiveUser; 399 unless defined $effectiveUser;
391 400
392 # obtain the merged set for $effectiveUser 401 # obtain the merged set for $effectiveUser
393 my $set = $db->getMergedSet($effectiveUserName, $setName); # checked 402 my $set = $db->getMergedSet($effectiveUserName, $setName); # checked
403
404 $self->set_showOldAnswers_default($ce, $userName, $authz, $set);
394 405
395 # Database fix (in case of undefined published values) 406 # Database fix (in case of undefined published values)
396 # this is only necessary because some people keep holding to ww1.9 which did not have a published field 407 # this is only necessary because some people keep holding to ww1.9 which did not have a published field
397 # make sure published is set to 0 or 1 408 # make sure published is set to 0 or 1
398 if ( $set and $set->published ne "0" and $set->published ne "1") { 409 if ( $set and $set->published ne "0" and $set->published ne "1") {
406 } 417 }
407 418
408 # obtain the merged problem for $effectiveUser 419 # obtain the merged problem for $effectiveUser
409 my $problem = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked 420 my $problem = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked
410 421
411 my $editMode = $r->param("editMode");
412
413 if ($authz->hasPermissions($userName, "modify_problem_sets")) { 422 if ($authz->hasPermissions($userName, "modify_problem_sets")) {
414 # professors are allowed to fabricate sets and problems not 423 # professors are allowed to fabricate sets and problems not
415 # assigned to them (or anyone). this allows them to use the 424 # assigned to them (or anyone). this allows them to use the
416 # editor to 425 # editor to
417 426
437 my $userProblemClass = $db->{problem_user}->{record}; 446 my $userProblemClass = $db->{problem_user}->{record};
438 my $globalProblem = $db->getGlobalProblem($setName, $problemNumber); # checked 447 my $globalProblem = $db->getGlobalProblem($setName, $problemNumber); # checked
439 # if the global problem doesn't exist either, bail! 448 # if the global problem doesn't exist either, bail!
440 if(not defined $globalProblem) { 449 if(not defined $globalProblem) {
441 my $sourceFilePath = $r->param("sourceFilePath"); 450 my $sourceFilePath = $r->param("sourceFilePath");
451 die "sourceFilePath is unsafe!" unless path_is_subdir($sourceFilePath, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
442 # These are problems from setmaker. If declared invalid, they won't come up 452 # These are problems from setmaker. If declared invalid, they won't come up
443 $self->{invalidProblem} = $self->{invalidSet} = 1 unless defined $sourceFilePath; 453 $self->{invalidProblem} = $self->{invalidSet} = 1 unless defined $sourceFilePath;
444# die "Problem $problemNumber in set $setName does not exist" unless defined $sourceFilePath; 454# die "Problem $problemNumber in set $setName does not exist" unless defined $sourceFilePath;
445 $problem = fake_problem($db); 455 $problem = fake_problem($db);
446 $problem->problem_id(1); 456 $problem->problem_id(1);
464 # now deal with possible editor overrides: 474 # now deal with possible editor overrides:
465 475
466 # if the caller is asking to override the source file, and 476 # if the caller is asking to override the source file, and
467 # editMode calls for a temporary file, do so 477 # editMode calls for a temporary file, do so
468 my $sourceFilePath = $r->param("sourceFilePath"); 478 my $sourceFilePath = $r->param("sourceFilePath");
469 if (defined $sourceFilePath and 479 if (defined $editMode and $editMode eq "temporaryFile" and defined $sourceFilePath) {
470 (not defined $editMode or $editMode eq "temporaryFile")) { 480 die "sourceFilePath is unsafe!" unless path_is_subdir($sourceFilePath, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
471 $problem->source_file($sourceFilePath); 481 $problem->source_file($sourceFilePath);
472 } 482 }
473 483
474 # if the problem does not have a source file or no source file has been passed in 484 # if the problem does not have a source file or no source file has been passed in
475 # then this is really an invalid problem (probably from a bad URL) 485 # then this is really an invalid problem (probably from a bad URL)
481 $problem->problem_seed($problemSeed); 491 $problem->problem_seed($problemSeed);
482 } 492 }
483 493
484 my $publishedClass = ($set->published) ? "Published" : "Unpublished"; 494 my $publishedClass = ($set->published) ? "Published" : "Unpublished";
485 my $publishedText = ($set->published) ? "visible to students." : "hidden from students."; 495 my $publishedText = ($set->published) ? "visible to students." : "hidden from students.";
486 $self->addmessage(CGI::p("This set is " . CGI::font({class=>$publishedClass}, $publishedText))); 496 $self->addmessage(CGI::span("This set is " . CGI::font({class=>$publishedClass}, $publishedText)));
487 } else { 497
488 498 # test for additional problem validity if it's not already invalid
489 # A set is valid if it exists and if it is either published or the user is privileged. 499 } else {
490 $self->{invalidSet} = !(defined $set and ($set->published || $authz->hasPermissions($userName, "view_unpublished_sets")));
491 $self->{invalidProblem} = !(defined $problem and ($set->published || $authz->hasPermissions($userName, "view_unpublished_sets"))); 500 $self->{invalidProblem} = !(defined $problem and ($set->published || $authz->hasPermissions($userName, "view_unpublished_sets")));
492 501
493 $self->addbadmessage(CGI::p("This problem will not count towards your grade.")) if $problem and not $problem->value and not $self->{invalidProblem}; 502 $self->addbadmessage(CGI::p("This problem will not count towards your grade.")) if $problem and not $problem->value and not $self->{invalidProblem};
494 } 503 }
495 504
518 $self->{checkAnswers} = $checkAnswers; 527 $self->{checkAnswers} = $checkAnswers;
519 $self->{previewAnswers} = $previewAnswers; 528 $self->{previewAnswers} = $previewAnswers;
520 $self->{formFields} = $formFields; 529 $self->{formFields} = $formFields;
521 530
522 # get result and send to message 531 # get result and send to message
523 my $success = $r->param("sucess"); 532 my $status_message = $r->param("status_message");
524 my $failure = $r->param("failure");
525 $self->addbadmessage(CGI::p($failure)) if $failure;
526 $self->addgoodmessage(CGI::p($success)) if $success; 533 $self->addmessage(CGI::p("$status_message")) if $status_message;
527 534
528 # now that we've set all the necessary variables quit out if the set or problem is invalid 535 # now that we've set all the necessary variables quit out if the set or problem is invalid
529 return if $self->{invalidSet} || $self->{invalidProblem}; 536 return if $self->{invalidSet} || $self->{invalidProblem};
530 537
531 ##### permissions ##### 538 ##### permissions #####
532 539
533 # are we allowed to view this problem?
534 $self->{isOpen} = after($set->open_date) || $authz->hasPermissions($userName, "view_unopened_sets");
535 return unless $self->{isOpen};
536
537 # what does the user want to do? 540 # what does the user want to do?
541 #FIXME There is a problem with checkboxes -- if they are not checked they are invisible. Hence if the default mode in $ce is 1
542 # there is no way to override this. Probably this is ok for the last three options, but it was definitely not ok for showing
543 # saved answers which is normally on, but you want to be able to turn it off! This section should be moved to ContentGenerator
544 # so that you can set these options anywhere. We also need mechanisms for making them sticky.
545 # Note: ProblemSet and ProblemSets might set showOldAnswers to '', which
546 # needs to be treated as if it is not set.
538 my %want = ( 547 my %want = (
539 showOldAnswers => $r->param("showOldAnswers") || $ce->{pg}->{options}->{showOldAnswers}, 548 showOldAnswers => (defined($r->param("showOldAnswers")) and $r->param("showOldAnswers") ne '') ? $r->param("showOldAnswers") : $ce->{pg}->{options}->{showOldAnswers},
540 showCorrectAnswers => $r->param("showCorrectAnswers") || $ce->{pg}->{options}->{showCorrectAnswers}, 549 showCorrectAnswers => $r->param("showCorrectAnswers") || $ce->{pg}->{options}->{showCorrectAnswers},
541 showHints => $r->param("showHints") || $ce->{pg}->{options}->{showHints}, 550 showHints => $r->param("showHints") || $ce->{pg}->{options}->{showHints},
542 showSolutions => $r->param("showSolutions") || $ce->{pg}->{options}->{showSolutions}, 551 showSolutions => $r->param("showSolutions") || $ce->{pg}->{options}->{showSolutions},
543 recordAnswers => $submitAnswers, 552 recordAnswers => $submitAnswers,
544 checkAnswers => $checkAnswers, 553 checkAnswers => $checkAnswers,
553 showSolutions => 0, 562 showSolutions => 0,
554 recordAnswers => ! $authz->hasPermissions($userName, "avoid_recording_answers"), 563 recordAnswers => ! $authz->hasPermissions($userName, "avoid_recording_answers"),
555 checkAnswers => 0, 564 checkAnswers => 0,
556 getSubmitButton => 0, 565 getSubmitButton => 0,
557 ); 566 );
558 567
559 # does the user have permission to use certain options? 568 # does the user have permission to use certain options?
560 my @args = ($user, $effectiveUser, $set, $problem); 569 my @args = ($user, $effectiveUser, $set, $problem);
561 my %can = ( 570 my %can = (
562 showOldAnswers => $self->can_showOldAnswers(@args), 571 showOldAnswers => $self->can_showOldAnswers(@args),
563 showCorrectAnswers => $self->can_showCorrectAnswers(@args), 572 showCorrectAnswers => $self->can_showCorrectAnswers(@args),
570 579
571 # final values for options 580 # final values for options
572 my %will; 581 my %will;
573 foreach (keys %must) { 582 foreach (keys %must) {
574 $will{$_} = $can{$_} && ($want{$_} || $must{$_}); 583 $will{$_} = $can{$_} && ($want{$_} || $must{$_});
584 #warn "final values for options $_ is can $can{$_}, want $want{$_}, must $must{$_}, will $will{$_}";
575 } 585 }
576 586
577 ##### sticky answers ##### 587 ##### sticky answers #####
578 588
579 if (not ($submitAnswers or $previewAnswers or $checkAnswers) and $will{showOldAnswers}) { 589 if (not ($submitAnswers or $previewAnswers or $checkAnswers) and $will{showOldAnswers}) {
582 $formFields->{$_} = $oldAnswers{$_} foreach keys %oldAnswers; 592 $formFields->{$_} = $oldAnswers{$_} foreach keys %oldAnswers;
583 } 593 }
584 594
585 ##### translation ##### 595 ##### translation #####
586 596
587 $WeBWorK::timer->continue("begin pg processing") if defined($WeBWorK::timer); 597 debug("begin pg processing");
588 my $pg = WeBWorK::PG->new( 598 my $pg = WeBWorK::PG->new(
589 $ce, 599 $ce,
590 $effectiveUser, 600 $effectiveUser,
591 $key, 601 $key,
592 $set, 602 $set,
597 displayMode => $displayMode, 607 displayMode => $displayMode,
598 showHints => $will{showHints}, 608 showHints => $will{showHints},
599 showSolutions => $will{showSolutions}, 609 showSolutions => $will{showSolutions},
600 refreshMath2img => $will{showHints} || $will{showSolutions}, 610 refreshMath2img => $will{showHints} || $will{showSolutions},
601 processAnswers => 1, 611 processAnswers => 1,
612 permissionLevel => $db->getPermissionLevel($userName)->permission,
613 effectivePermissionLevel => $db->getPermissionLevel($effectiveUserName)->permission,
602 }, 614 },
603 ); 615 );
604 616
605 $WeBWorK::timer->continue("end pg processing") if defined($WeBWorK::timer); 617 debug("end pg processing");
606 618
607 ##### fix hint/solution options ##### 619 ##### fix hint/solution options #####
608 620
609 $can{showHints} &&= $pg->{flags}->{hintExists} 621 $can{showHints} &&= $pg->{flags}->{hintExists}
610 &&= $pg->{flags}->{showHintLimit}<=$pg->{state}->{num_of_incorrect_ans}; 622 &&= $pg->{flags}->{showHintLimit}<=$pg->{state}->{num_of_incorrect_ans};
629 } 641 }
630} 642}
631 643
632sub head { 644sub head {
633 my ($self) = @_; 645 my ($self) = @_;
634 646
635 return "" unless $self->{isOpen}; 647 return "" if ( $self->{invalidSet} );
636 return $self->{pg}->{head_text} if $self->{pg}->{head_text}; 648 return $self->{pg}->{head_text} if $self->{pg}->{head_text};
637} 649}
638 650
639sub options { 651sub options {
640 my ($self) = @_; 652 my ($self) = @_;
653 #warn "doing options in Problem";
641 654
655 # don't show options if we don't have anything to show
642 return "" if $self->{invalidProblem}; 656 return "" if $self->{invalidSet} or $self->{invalidProblem};
643 my $sourceFilePathfield = '';
644 if($self->r->param("sourceFilePath")) {
645 $sourceFilePathfield = CGI::hidden(-name => "sourceFilePath",
646 -value => $self->r->param("sourceFilePath"));
647 }
648 657
649 return join("", 658 my $displayMode = $self->{displayMode};
650 CGI::start_form("POST", $self->{r}->uri), 659 my %can = %{ $self->{can} };
651 $self->hidden_authen_fields, 660
652 $sourceFilePathfield, 661 my @options_to_show = "displayMode";
653 CGI::hr(), 662 push @options_to_show, "showOldAnswers" if $can{showOldAnswers};
654 CGI::start_div({class=>"viewOptions"}), 663 push @options_to_show, "showHints" if $can{showHints};
655 $self->viewOptions(), 664 push @options_to_show, "showSolutions" if $can{showSolutions};
656 CGI::end_div(), 665
657 CGI::end_form() 666 return $self->optionsMacro(
667 options_to_show => \@options_to_show,
668 extra_params => ["editMode", "sourceFilePath"],
658 ); 669 );
659} 670}
660 671
661sub siblings { 672sub siblings {
662 my ($self) = @_; 673 my ($self) = @_;
670 my $courseID = $urlpath->arg("courseID"); 681 my $courseID = $urlpath->arg("courseID");
671 my $setID = $self->{set}->set_id; 682 my $setID = $self->{set}->set_id;
672 my $eUserID = $r->param("effectiveUser"); 683 my $eUserID = $r->param("effectiveUser");
673 my @problemIDs = sort { $a <=> $b } $db->listUserProblems($eUserID, $setID); 684 my @problemIDs = sort { $a <=> $b } $db->listUserProblems($eUserID, $setID);
674 685
686 print CGI::start_div({class=>"info-box", id=>"fisheye"});
687 print CGI::h2("Problems");
675 print CGI::start_ul({class=>"LinksMenu"}); 688 #print CGI::start_ul({class=>"LinksMenu"});
676 print CGI::start_li(); 689 #print CGI::start_li();
677 print CGI::span({style=>"font-size:larger"}, "Problems"); 690 #print CGI::span({style=>"font-size:larger"}, "Problems");
678 print CGI::start_ul(); 691 print CGI::start_ul();
679 692
680 foreach my $problemID (@problemIDs) { 693 foreach my $problemID (@problemIDs) {
681 my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", 694 my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem",
682 courseID => $courseID, setID => $setID, problemID => $problemID); 695 courseID => $courseID, setID => $setID, problemID => $problemID);
683 print CGI::li(CGI::a({href=>$self->systemLink($problemPage, params=>{displayMode => $self->{displayMode}})}, "Problem $problemID")); 696 print CGI::li(CGI::a( {href=>$self->systemLink($problemPage,
697 params=>{ displayMode => $self->{displayMode},
698 showOldAnswers => $self->{will}->{showOldAnswers}
699 })}, "Problem $problemID")
700 );
684 } 701 }
685 702
686 print CGI::end_ul(); 703 print CGI::end_ul();
687 print CGI::end_li(); 704 #print CGI::end_li();
688 print CGI::end_ul(); 705 #print CGI::end_ul();
689 706 print CGI::end_div();
707
690 return ""; 708 return "";
691} 709}
692 710
693sub nav { 711sub nav {
694 my ($self, $args) = @_; 712 my ($self, $args) = @_;
695 my $r = $self->r; 713 my $r = $self->r;
696 my $db = $r->db; 714 my $db = $r->db;
697 my $urlpath = $r->urlpath; 715 my $urlpath = $r->urlpath;
698 716
717 return "" if ( $self->{invalidSet} );
718
699 my $courseID = $urlpath->arg("courseID"); 719 my $courseID = $urlpath->arg("courseID");
700 my $setID = $self->{set}->set_id if !($self->{invalidSet}); 720 my $setID = $self->{set}->set_id if !($self->{invalidSet});
701 my $problemID = $self->{problem}->problem_id if !($self->{invalidProblem}); 721 my $problemID = $self->{problem}->problem_id if !($self->{invalidProblem});
702 my $eUserID = $r->param("effectiveUser"); 722 my $eUserID = $r->param("effectiveUser");
703 723
704 my ($prevID, $nextID); 724 my ($prevID, $nextID);
705 725
706 if (!$self->{invalidProblem}) { 726 if (!$self->{invalidProblem}) {
707 my @problemIDs = $db->listUserProblems($eUserID, $setID); 727 my @problemIDs = $db->listUserProblems($eUserID, $setID);
708 foreach my $id (@problemIDs) { 728 foreach my $id (@problemIDs) {
709 $prevID = $id if $id < $problemID 729 $prevID = $id if $id < $problemID
710 and (not defined $prevID or $id > $prevID); 730 and (not defined $prevID or $id > $prevID);
711 $nextID = $id if $id > $problemID 731 $nextID = $id if $id > $problemID
712 and (not defined $nextID or $id < $nextID); 732 and (not defined $nextID or $id < $nextID);
713 } 733 }
714 } 734 }
715 735
716 my @links; 736 my @links;
717 737
718 if ($prevID) { 738 if ($prevID) {
719 my $prevPage = $urlpath->newFromModule(__PACKAGE__, 739 my $prevPage = $urlpath->newFromModule(__PACKAGE__,
720 courseID => $courseID, setID => $setID, problemID => $prevID); 740 courseID => $courseID, setID => $setID, problemID => $prevID);
721 push @links, "Previous Problem", $r->location . $prevPage->path, "navPrev"; 741 push @links, "Previous Problem", $r->location . $prevPage->path, "navPrev";
722 } else { 742 } else {
723 push @links, "Previous Problem", "", "navPrev"; 743 push @links, "Previous Problem", "", "navPrevGrey";
724 } 744 }
725 745
746 if (defined($setID) && $setID ne 'Undefined_Set') {
726 push @links, "Problem List", $r->location . $urlpath->parent->path, "navProbList"; 747 push @links, "Problem List", $r->location . $urlpath->parent->path, "navProbList";
727 748 } else {
749 push @links, "Problem List", "", "navProbListGrey";
750 }
751
728 if ($nextID) { 752 if ($nextID) {
729 my $nextPage = $urlpath->newFromModule(__PACKAGE__, 753 my $nextPage = $urlpath->newFromModule(__PACKAGE__,
730 courseID => $courseID, setID => $setID, problemID => $nextID); 754 courseID => $courseID, setID => $setID, problemID => $nextID);
731 push @links, "Next Problem", $r->location . $nextPage->path, "navNext"; 755 push @links, "Next Problem", $r->location . $nextPage->path, "navNext";
732 } else { 756 } else {
733 push @links, "Next Problem", "", "navNext"; 757 push @links, "Next Problem", "", "navNextGrey";
734 } 758 }
735 759
736 my $tail = "&displayMode=".$self->{displayMode}; 760 my $tail = "";
761
762 $tail .= "&displayMode=".$self->{displayMode} if defined $self->{displayMode};
763 $tail .= "&showOldAnswers=".$self->{will}->{showOldAnswers}
764 if defined $self->{will}->{showOldAnswers};
737 return $self->navMacro($args, $tail, @links); 765 return $self->navMacro($args, $tail, @links);
738} 766}
739 767
740sub title { 768sub title {
741 my ($self) = @_; 769 my ($self) = @_;
742 770
743 # using the url arguments won't break if the set/problem are invalid 771 # using the url arguments won't break if the set/problem are invalid
744 my $setID = $self->r->urlpath->arg("setID"); 772 my $setID = WeBWorK::ContentGenerator::underscore2nbsp($self->r->urlpath->arg("setID"));
745 my $problemID = $self->r->urlpath->arg("problemID"); 773 my $problemID = $self->r->urlpath->arg("problemID");
746 774
747 return "$setID: Problem $problemID"; 775 return "$setID: Problem $problemID";
748} 776}
749 777
750sub body { 778sub body {
751 my $self = shift; 779 my $self = shift;
755 my $authz = $r->authz; 783 my $authz = $r->authz;
756 my $urlpath = $r->urlpath; 784 my $urlpath = $r->urlpath;
757 my $user = $r->param('user'); 785 my $user = $r->param('user');
758 my $effectiveUser = $r->param('effectiveUser'); 786 my $effectiveUser = $r->param('effectiveUser');
759 787
760 if ($self->{invalidSet}) { 788 if ( $self->{invalidSet} ) {
761 return CGI::div({class=>"ResultsWithError"}, 789 return CGI::div({class=>"ResultsWithError"},
762 CGI::p("The selected problem set (" . $urlpath->arg("setID") . ") is not a valid set for " . $r->param("effectiveUser") . ".")); 790 CGI::p("The selected problem set (" .
791 $urlpath->arg("setID") . ") is not " .
792 "a valid set for $effectiveUser:"),
793 CGI::p($self->{invalidSet}));
763 } 794 }
764 795
765 if ($self->{invalidProblem}) { 796 if ($self->{invalidProblem}) {
766 return CGI::div({class=>"ResultsWithError"}, 797 return CGI::div({class=>"ResultsWithError"},
767 CGI::p("The selected problem (" . $urlpath->arg("problemID") . ") is not a valid problem for set " . $self->{set}->set_id . ".")); 798 CGI::p("The selected problem (" . $urlpath->arg("problemID") . ") is not a valid problem for set " . $self->{set}->set_id . "."));
768 } 799 }
769 800
770 unless ($self->{isOpen}) {
771 return CGI::div({class=>"ResultsWithError"},
772 CGI::p("This problem is not available because the problem set that contains it is not yet open."));
773 }
774 # unpack some useful variables 801 # unpack some useful variables
775 my $set = $self->{set}; 802 my $set = $self->{set};
776 my $problem = $self->{problem}; 803 my $problem = $self->{problem};
777 my $editMode = $self->{editMode}; 804 my $editMode = $self->{editMode};
778 my $submitAnswers = $self->{submitAnswers}; 805 my $submitAnswers = $self->{submitAnswers};
788 815
789 # FIXME: move editor link to top, next to problem number. 816 # FIXME: move editor link to top, next to problem number.
790 # format as "[edit]" like we're doing with course info file, etc. 817 # format as "[edit]" like we're doing with course info file, etc.
791 # add edit link for set as well. 818 # add edit link for set as well.
792 my $editorLink = ""; 819 my $editorLink = "";
793 # if we are here without a real problem set, carry that through 820 # if we are here without a real homework set, carry that through
794 my $forced_field = []; 821 my $forced_field = [];
795 $forced_field = ['sourceFilePath' => $r->param("sourceFilePath")] if 822 $forced_field = ['sourceFilePath' => $r->param("sourceFilePath")] if
796 ($set->set_id eq 'Undefined_Set'); 823 ($set->set_id eq 'Undefined_Set');
797 if ($authz->hasPermissions($user, "modify_problem_sets")) { 824 if ($authz->hasPermissions($user, "modify_problem_sets")) {
798 my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", 825 my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor",
799 courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id); 826 courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id);
800 my $editorURL = $self->systemLink($editorPage, params=>$forced_field); 827 my $editorURL = $self->systemLink($editorPage, params=>$forced_field);
801 $editorLink = CGI::a({href=>$editorURL}, "Edit this problem"); 828 $editorLink = CGI::p(CGI::a({href=>$editorURL,target =>'WW_Editor'}, "Edit this problem"));
802 } 829 }
803 830
804 ##### translation errors? ##### 831 ##### translation errors? #####
805 832
806 if ($pg->{flags}->{error_flag}) { 833 if ($pg->{flags}->{error_flag}) {
834 if ($authz->hasPermissions($user, "view_problem_debugging_info")) {
807 print $self->errorOutput($pg->{errors}, $pg->{body_text}); 835 print $self->errorOutput($pg->{errors}, $pg->{body_text});
836 } else {
837 print $self->errorOutput($pg->{errors}, "You do not have permission to view the details of this error.");
838 }
808 print $editorLink; 839 print $editorLink;
809 return ""; 840 return "";
810 } 841 }
811 842
812 ##### answer processing ##### 843 ##### answer processing #####
813 $WeBWorK::timer->continue("begin answer processing") if defined($WeBWorK::timer); 844 debug("begin answer processing");
814 # if answers were submitted: 845 # if answers were submitted:
815 my $scoreRecordedMessage; 846 my $scoreRecordedMessage;
816 my $pureProblem; 847 my $pureProblem;
817 if ($submitAnswers) { 848 if ($submitAnswers) {
818 # get a "pure" (unmerged) UserProblem to modify 849 # get a "pure" (unmerged) UserProblem to modify
842 $db->putUserProblem($pureProblem); 873 $db->putUserProblem($pureProblem);
843 874
844 # store state in DB if it makes sense 875 # store state in DB if it makes sense
845 if ($will{recordAnswers}) { 876 if ($will{recordAnswers}) {
846 $problem->status($pg->{state}->{recorded_score}); 877 $problem->status($pg->{state}->{recorded_score});
878 $problem->sub_status($pg->{state}->{sub_recorded_score});
847 $problem->attempted(1); 879 $problem->attempted(1);
848 $problem->num_correct($pg->{state}->{num_of_correct_ans}); 880 $problem->num_correct($pg->{state}->{num_of_correct_ans});
849 $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans}); 881 $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans});
850 $pureProblem->status($pg->{state}->{recorded_score}); 882 $pureProblem->status($pg->{state}->{recorded_score});
883 $pureProblem->sub_status($pg->{state}->{sub_recorded_score});
851 $pureProblem->attempted(1); 884 $pureProblem->attempted(1);
852 $pureProblem->num_correct($pg->{state}->{num_of_correct_ans}); 885 $pureProblem->num_correct($pg->{state}->{num_of_correct_ans});
853 $pureProblem->num_incorrect($pg->{state}->{num_of_incorrect_ans}); 886 $pureProblem->num_incorrect($pg->{state}->{num_of_incorrect_ans});
854 if ($db->putUserProblem($pureProblem)) { 887 if ($db->putUserProblem($pureProblem)) {
855 $scoreRecordedMessage = "Your score was recorded."; 888 $scoreRecordedMessage = "Your score was recorded.";
871 $pureProblem->num_correct."\t". 904 $pureProblem->num_correct."\t".
872 $pureProblem->num_incorrect 905 $pureProblem->num_incorrect
873 ); 906 );
874 } else { 907 } else {
875 if (before($set->open_date) or after($set->due_date)) { 908 if (before($set->open_date) or after($set->due_date)) {
876 $scoreRecordedMessage = "Your score was not recorded because this problem set is closed."; 909 $scoreRecordedMessage = "Your score was not recorded because this homework set is closed.";
877 } else { 910 } else {
878 $scoreRecordedMessage = "Your score was not recorded."; 911 $scoreRecordedMessage = "Your score was not recorded.";
879 } 912 }
880 } 913 }
881 } else { 914 } else {
891 my $answerString = ""; my $scores = ""; 924 my $answerString = ""; my $scores = "";
892 my %answerHash = %{ $pg->{answers} }; 925 my %answerHash = %{ $pg->{answers} };
893 # FIXME this is the line 552 error. make sure original student ans is defined. 926 # FIXME this is the line 552 error. make sure original student ans is defined.
894 # The fact that it is not defined is probably due to an error in some answer evaluator. 927 # The fact that it is not defined is probably due to an error in some answer evaluator.
895 # But I think it is useful to suppress this error message in the log. 928 # But I think it is useful to suppress this error message in the log.
896 foreach (sort keys %answerHash) { 929 foreach (sortByName(undef, keys %answerHash)) {
897 my $orig_ans = $answerHash{$_}->{original_student_ans}; 930 my $orig_ans = $answerHash{$_}->{original_student_ans};
898 my $student_ans = defined $orig_ans ? $orig_ans : ''; 931 my $student_ans = defined $orig_ans ? $orig_ans : '';
899 $answerString .= $student_ans."\t"; 932 $answerString .= $student_ans."\t";
900 $scores .= $answerHash{$_}->{score} >= 1 ? "1" : "0"; 933 $scores .= $answerHash{$_}->{score} >= 1 ? "1" : "0";
901 } 934 }
912 ); 945 );
913 946
914 } 947 }
915 } 948 }
916 949
917 $WeBWorK::timer->continue("end answer processing") if defined($WeBWorK::timer); 950 debug("end answer processing");
918 951
919 ##### output ##### 952 ##### output #####
920
921 print CGI::start_div({class=>"problemHeader"});
922
923 # custom message for editor 953 # custom message for editor
924 if ($authz->hasPermissions($user, "modify_problem_sets") and defined $editMode) { 954 if ($authz->hasPermissions($user, "modify_problem_sets") and defined $editMode) {
925 if ($editMode eq "temporaryFile") { 955 if ($editMode eq "temporaryFile") {
926 print CGI::p(CGI::i("Editing temporary file: ", $problem->source_file)); 956 print CGI::p(CGI::div({class=>'temporaryFile'}, "Viewing temporary file: ", $problem->source_file));
927 } elsif ($editMode eq "savedFile") { 957 } elsif ($editMode eq "savedFile") {
928 # taken care of in the initialization phase 958 # taken care of in the initialization phase
929 } 959 }
930 } 960 }
961 print CGI::start_div({class=>"problemHeader"});
962
963
931 964
932 # attempt summary 965 # attempt summary
933 #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything. 966 #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything.
934 # until after the due date 967 # until after the due date
935 # do I need to check $will{showCorrectAnswers} to make preflight work?? 968 # do I need to check $will{showCorrectAnswers} to make preflight work??
957 } 990 }
958 991
959 print CGI::end_div(); 992 print CGI::end_div();
960 993
961 # main form 994 # main form
962 print CGI::startform("POST", $r->uri); 995 print "\n";
996 print CGI::start_form(-method=>"POST", -action=> $r->uri,-name=>"problemMainForm", onsubmit=>"submitAction()");
963 print $self->hidden_authen_fields; 997 print $self->hidden_authen_fields;
964 998 print "\n";
965 print CGI::start_div({class=>"problem"}); 999 print CGI::start_div({class=>"problem"});
966 print CGI::p($pg->{body_text}); 1000 print CGI::p($pg->{body_text});
967 print CGI::p(CGI::b("Note: "), CGI::i($pg->{result}->{msg})) if $pg->{result}->{msg}; 1001 print CGI::p(CGI::b("Note: "). CGI::i($pg->{result}->{msg})) if $pg->{result}->{msg};
1002 print $editorLink; # this is empty unless it is appropriate to have an editor link.
968 print CGI::end_div(); 1003 print CGI::end_div();
969 1004
970 print CGI::start_p(); 1005 print CGI::start_p();
971 1006
972 if ($can{showCorrectAnswers}) { 1007 if ($can{showCorrectAnswers}) {
973 print CGI::checkbox( 1008 print CGI::checkbox(
974 -name => "showCorrectAnswers", 1009 -name => "showCorrectAnswers",
975 -checked => $will{showCorrectAnswers}, 1010 -checked => $will{showCorrectAnswers},
976 -label => "Show correct answers", 1011 -label => "Show correct answers",
1012 -value => 1,
977 ); 1013 );
978 } 1014 }
979 if ($can{showHints}) { 1015 if ($can{showHints}) {
980 print CGI::div({style=>"color:red"}, 1016 print CGI::div({style=>"color:red"},
981 CGI::checkbox( 1017 CGI::checkbox(
982 -name => "showHints", 1018 -name => "showHints",
983 -checked => $will{showHints}, 1019 -checked => $will{showHints},
984 -label => "Show Hints", 1020 -label => "Show Hints",
1021 -value =>1,
985 ) 1022 )
986 ); 1023 );
987 } 1024 }
988 if ($can{showSolutions}) { 1025 if ($can{showSolutions}) {
989 print CGI::checkbox( 1026 print CGI::checkbox(
990 -name => "showSolutions", 1027 -name => "showSolutions",
991 -checked => $will{showSolutions}, 1028 -checked => $will{showSolutions},
992 -label => "Show Solutions", 1029 -label => "Show Solutions",
1030 -value => 1,
993 ); 1031 );
994 } 1032 }
995 1033
996 if ($can{showCorrectAnswers} or $can{showHints} or $can{showSolutions}) { 1034 if ($can{showCorrectAnswers} or $can{showHints} or $can{showSolutions}) {
997 print CGI::br(); 1035 print CGI::br();
1005 if ($user ne $effectiveUser) { 1043 if ($user ne $effectiveUser) {
1006 # if acting as a student, make it clear that answer submissions will 1044 # if acting as a student, make it clear that answer submissions will
1007 # apply to the student's records, not the professor's. 1045 # apply to the student's records, not the professor's.
1008 print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers for $effectiveUser"); 1046 print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers for $effectiveUser");
1009 } else { 1047 } else {
1048 #print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>"alert('submit button clicked')");
1010 print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers"); 1049 print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>"");
1050 # FIXME for unknown reasons the -onclick label seems to have to be there in order to allow the forms onsubmit to trigger
1051 # WFT???
1011 } 1052 }
1012 } 1053 }
1013 1054
1014 print CGI::end_p(); 1055 print CGI::end_p();
1015 1056
1016 print CGI::start_div({class=>"scoreSummary"}); 1057 print CGI::start_div({class=>"scoreSummary"});
1017 1058
1018 # score summary 1059 # score summary
1019 my $attempts = $problem->num_correct + $problem->num_incorrect; 1060 my $attempts = $problem->num_correct + $problem->num_incorrect;
1020 my $attemptsNoun = $attempts != 1 ? "times" : "time"; 1061 my $attemptsNoun = $attempts != 1 ? "times" : "time";
1062 my $problem_status = $problem->status || 0;
1021 my $lastScore = sprintf("%.0f%%", $problem->status * 100); # Round to whole number 1063 my $lastScore = sprintf("%.0f%%", $problem_status * 100); # Round to whole number
1022 my ($attemptsLeft, $attemptsLeftNoun); 1064 my ($attemptsLeft, $attemptsLeftNoun);
1023 if ($problem->max_attempts == -1) { 1065 if ($problem->max_attempts == -1) {
1024 # unlimited attempts 1066 # unlimited attempts
1025 $attemptsLeft = "unlimited"; 1067 $attemptsLeft = "unlimited";
1026 $attemptsLeftNoun = "attempts"; 1068 $attemptsLeftNoun = "attempts";
1031 1073
1032 my $setClosed = 0; 1074 my $setClosed = 0;
1033 my $setClosedMessage; 1075 my $setClosedMessage;
1034 if (before($set->open_date) or after($set->due_date)) { 1076 if (before($set->open_date) or after($set->due_date)) {
1035 $setClosed = 1; 1077 $setClosed = 1;
1078 if (before($set->open_date)) {
1079 $setClosedMessage = "This homework set is not yet open.";
1080 } elsif (after($set->due_date)) {
1036 $setClosedMessage = "This problem set is closed."; 1081 $setClosedMessage = "This homework set is closed.";
1082 }
1083 }
1084 #if (before($set->open_date) or after($set->due_date)) {
1085 # $setClosed = 1;
1086 # $setClosedMessage = "This homework set is closed.";
1037 if ($authz->hasPermissions($user, "view_answers")) { 1087 # if ($authz->hasPermissions($user, "view_answers")) {
1038 $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded."; 1088 # $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded.";
1039 } else { 1089 # } else {
1040 $setClosedMessage .= " Additional attempts will not be recorded."; 1090 # $setClosedMessage .= " Additional attempts will not be recorded.";
1041 } 1091 # }
1042 } 1092 #}
1043 1093 unless (defined( $pg->{state}->{state_summary_msg}) and $pg->{state}->{state_summary_msg}=~/\S/) {
1044 my $notCountedMessage = ($problem->value) ? "" : "(This problem will not count towards your grade.)"; 1094 my $notCountedMessage = ($problem->value) ? "" : "(This problem will not count towards your grade.)";
1045 print CGI::p( 1095 print CGI::p(join("",
1046 $submitAnswers ? $scoreRecordedMessage . CGI::br() : "", 1096 $submitAnswers ? $scoreRecordedMessage . CGI::br() : "",
1047 "You have attempted this problem $attempts $attemptsNoun.", CGI::br(), 1097 "You have attempted this problem $attempts $attemptsNoun.", CGI::br(),
1098 $submitAnswers ?"You received a score of ".sprintf("%.0f%%", $pg->{result}->{score} * 100)." for this attempt.".CGI::br():'',
1048 $problem->attempted 1099 $problem->attempted
1049 ? "Your recorded score is $lastScore. $notCountedMessage" . CGI::br() 1100 ? "Your overall recorded score is $lastScore. $notCountedMessage" . CGI::br()
1050 : "", 1101 : "",
1051 $setClosed ? $setClosedMessage : "You have $attemptsLeft $attemptsLeftNoun remaining." 1102 $setClosed ? $setClosedMessage : "You have $attemptsLeft $attemptsLeftNoun remaining."
1052 ); 1103 ));
1104 }else {
1105 print CGI::p($pg->{state}->{state_summary_msg});
1106 }
1053 print CGI::end_div(); 1107 print CGI::end_div();
1054 1108
1055 # save state for viewOptions 1109 # save state for viewOptions
1056 print CGI::hidden( 1110 print CGI::hidden(
1057 -name => "showOldAnswers", 1111 -name => "showOldAnswers",
1095 my $showPastAnswersURL = $self->systemLink($pastAnswersPage, authen => 0); # no authen info for form action 1149 my $showPastAnswersURL = $self->systemLink($pastAnswersPage, authen => 0); # no authen info for form action
1096 1150
1097 # print answer inspection button 1151 # print answer inspection button
1098 if ($authz->hasPermissions($user, "view_answers")) { 1152 if ($authz->hasPermissions($user, "view_answers")) {
1099 print "\n", 1153 print "\n",
1100 CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"information"),"\n", 1154 CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"WW_Info"),"\n",
1101 $self->hidden_authen_fields,"\n", 1155 $self->hidden_authen_fields,"\n",
1102 CGI::hidden(-name => 'courseID', -value=>$courseName), "\n", 1156 CGI::hidden(-name => 'courseID', -value=>$courseName), "\n",
1103 CGI::hidden(-name => 'problemID', -value=>$problem->problem_id), "\n", 1157 CGI::hidden(-name => 'problemID', -value=>$problem->problem_id), "\n",
1104 CGI::hidden(-name => 'setID', -value=>$problem->set_id), "\n", 1158 CGI::hidden(-name => 'setID', -value=>$problem->set_id), "\n",
1105 CGI::hidden(-name => 'studentUser', -value=>$problem->user_id), "\n", 1159 CGI::hidden(-name => 'studentUser', -value=>$problem->user_id), "\n",
1107 CGI::submit(-name => 'action', -value=>'Show Past Answers') 1161 CGI::submit(-name => 'action', -value=>'Show Past Answers')
1108 ), "\n", 1162 ), "\n",
1109 CGI::endform(); 1163 CGI::endform();
1110 } 1164 }
1111 1165
1112 # feedback form url 1166 ## feedback form url
1113 my $feedbackPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Feedback", 1167 #my $feedbackPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Feedback",
1114 courseID => $courseName); 1168 # courseID => $courseName);
1115 my $feedbackURL = $self->systemLink($feedbackPage, authen => 0); # no authen info for form action 1169 #my $feedbackURL = $self->systemLink($feedbackPage, authen => 0); # no authen info for form action
1116 1170 #
1117 #print feedback form 1171 ##print feedback form
1118 print 1172 #print
1119 CGI::start_form(-method=>"POST", -action=>$feedbackURL),"\n", 1173 # CGI::start_form(-method=>"POST", -action=>$feedbackURL),"\n",
1120 $self->hidden_authen_fields,"\n", 1174 # $self->hidden_authen_fields,"\n",
1121 CGI::hidden("module", __PACKAGE__),"\n", 1175 # CGI::hidden("module", __PACKAGE__),"\n",
1122 CGI::hidden("set", $set->set_id),"\n", 1176 # CGI::hidden("set", $set->set_id),"\n",
1123 CGI::hidden("problem", $problem->problem_id),"\n", 1177 # CGI::hidden("problem", $problem->problem_id),"\n",
1124 CGI::hidden("displayMode", $self->{displayMode}),"\n", 1178 # CGI::hidden("displayMode", $self->{displayMode}),"\n",
1125 CGI::hidden("showOldAnswers", $will{showOldAnswers}),"\n", 1179 # CGI::hidden("showOldAnswers", $will{showOldAnswers}),"\n",
1126 CGI::hidden("showCorrectAnswers", $will{showCorrectAnswers}),"\n", 1180 # CGI::hidden("showCorrectAnswers", $will{showCorrectAnswers}),"\n",
1127 CGI::hidden("showHints", $will{showHints}),"\n", 1181 # CGI::hidden("showHints", $will{showHints}),"\n",
1128 CGI::hidden("showSolutions", $will{showSolutions}),"\n", 1182 # CGI::hidden("showSolutions", $will{showSolutions}),"\n",
1129 CGI::p({-align=>"left"}, 1183 # CGI::p({-align=>"left"},
1130 CGI::submit(-name=>"feedbackForm", -label=>"Email instructor") 1184 # CGI::submit(-name=>"feedbackForm", -label=>"Email instructor")
1131 ), 1185 # ),
1132 CGI::endform(),"\n"; 1186 # CGI::endform(),"\n";
1133 1187
1134 # FIXME print editor link 1188 print $self->feedbackMacro(
1135 print $editorLink; #empty unless it is appropriate to have an editor link. 1189 module => __PACKAGE__,
1190 set => $self->{set}->set_id,
1191 problem => $problem->problem_id,
1192 displayMode => $self->{displayMode},
1193 showOldAnswers => $will{showOldAnswers},
1194 showCorrectAnswers => $will{showCorrectAnswers},
1195 showHints => $will{showHints},
1196 showSolutions => $will{showSolutions},
1197 pg_object => $pg,
1198 );
1136 1199
1137 print CGI::end_div(); 1200 print CGI::end_div();
1138 1201
1139 # debugging stuff 1202 # debugging stuff
1140 if (0) { 1203 if (0) {

Legend:
Removed from v.2767  
changed lines
  Added in v.6149

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9