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

View of /trunk/webwork2/lib/WeBWorK/ContentGenerator/Problem.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 7066 - (download) (as text) (annotate)
Wed Oct 5 11:55:53 2011 UTC (19 months, 2 weeks ago) by gage
File size: 53612 byte(s)
changed "label" to "value" in defining the name of the submit button
so that this works in firefox and some other browsers.

Seems to have been a typo since "value" was used in defining 
the preview and check answer buttons.



    1 ################################################################################
    2 # WeBWorK Online Homework Delivery System
    3 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
    4 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Problem.pm,v 1.225 2010/05/28 21:29:48 gage Exp $
    5 #
    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
    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.
   10 #
   11 # This program is distributed in the hope that it will be useful, but WITHOUT
   12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
   13 # FOR A PARTICULAR PURPOSE.  See either the GNU General Public License or the
   14 # Artistic License for more details.
   15 ################################################################################
   16 
   17 package WeBWorK::ContentGenerator::Problem;
   18 use base qw(WeBWorK);
   19 #use base qw(WeBWorK::ContentGenerator);
   20 use base qw(WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil);  # not needed?
   21 
   22 =head1 NAME
   23 
   24 WeBWorK::ContentGenerator::Problem - Allow a student to interact with a problem.
   25 
   26 =cut
   27 
   28 use strict;
   29 use warnings;
   30 #use CGI qw(-nosticky );
   31 use WeBWorK::CGI;
   32 use File::Path qw(rmtree);
   33 use WeBWorK::Debug;
   34 use WeBWorK::Form;
   35 use WeBWorK::PG;
   36 use WeBWorK::PG::ImageGenerator;
   37 use WeBWorK::PG::IO;
   38 use WeBWorK::Utils qw(readFile writeLog writeCourseLog encodeAnswers decodeAnswers
   39   ref2string makeTempDirectory path_is_subdir sortByName before after between);
   40 use WeBWorK::DB::Utils qw(global2user user2global);
   41 use URI::Escape;
   42 use WeBWorK::Localize;
   43 use WeBWorK::Utils::Tasks qw(fake_set fake_problem);
   44 
   45 ################################################################################
   46 # CGI param interface to this module (up-to-date as of v1.153)
   47 ################################################################################
   48 
   49 # Standard params:
   50 #
   51 #     user - user ID of real user
   52 #     key - session key
   53 #     effectiveUser - user ID of effective user
   54 #
   55 # Integration with PGProblemEditor:
   56 #
   57 #     editMode - if set, indicates alternate problem source location.
   58 #                can be "temporaryFile" or "savedFile".
   59 #
   60 #     sourceFilePath - path to file to be edited
   61 #     problemSeed - force problem seed to value
   62 #     success - success message to display
   63 #     failure - failure message to display
   64 #
   65 # Rendering options:
   66 #
   67 #     displayMode - name of display mode to use
   68 #
   69 #     showOldAnswers - request that last entered answer be shown (if allowed)
   70 #     showCorrectAnswers - request that correct answers be shown (if allowed)
   71 #     showHints - request that hints be shown (if allowed)
   72 #     showSolutions - request that solutions be shown (if allowed)
   73 #
   74 # Problem interaction:
   75 #
   76 #     AnSwEr# - answer blanks in problem
   77 #
   78 #     redisplay - name of the "Redisplay Problem" button
   79 #     submitAnswers - name of "Submit Answers" button
   80 #     checkAnswers - name of the "Check Answers" button
   81 #     previewAnswers - name of the "Preview Answers" button
   82 
   83 ################################################################################
   84 # "can" methods
   85 ################################################################################
   86 
   87 # Subroutines to determine if a user "can" perform an action. Each subroutine is
   88 # called with the following arguments:
   89 #
   90 #     ($self, $User, $EffectiveUser, $Set, $Problem)
   91 
   92 # Note that significant parts of the "can" methods are lifted into the
   93 # GatewayQuiz module.  It isn't direct, however, because of the necessity
   94 # of dealing with versioning there.
   95 
   96 sub can_showOldAnswers {
   97   #my ($self, $User, $EffectiveUser, $Set, $Problem) = @_;
   98 
   99   return 1;
  100 }
  101 
  102 sub can_showCorrectAnswers {
  103   my ($self, $User, $EffectiveUser, $Set, $Problem) = @_;
  104   my $authz = $self->r->authz;
  105 
  106   return
  107     after($Set->answer_date)
  108       ||
  109     $authz->hasPermissions($User->user_id, "show_correct_answers_before_answer_date")
  110     ;
  111 }
  112 
  113 sub can_showHints {
  114   #my ($self, $User, $EffectiveUser, $Set, $Problem) = @_;
  115 
  116   return 1;
  117 }
  118 
  119 sub can_showSolutions {
  120   my ($self, $User, $EffectiveUser, $Set, $Problem) = @_;
  121   my $authz = $self->r->authz;
  122 
  123   return
  124     after($Set->answer_date)
  125       ||
  126     $authz->hasPermissions($User->user_id, "show_solutions_before_answer_date")
  127     ;
  128 }
  129 
  130 sub can_recordAnswers {
  131   my ($self, $User, $EffectiveUser, $Set, $Problem, $submitAnswers) = @_;
  132   my $authz = $self->r->authz;
  133   my $thisAttempt = $submitAnswers ? 1 : 0;
  134   if ($User->user_id ne $EffectiveUser->user_id) {
  135     return $authz->hasPermissions($User->user_id, "record_answers_when_acting_as_student");
  136   }
  137   if (before($Set->open_date)) {
  138     return $authz->hasPermissions($User->user_id, "record_answers_before_open_date");
  139   } elsif (between($Set->open_date, $Set->due_date)) {
  140     my $max_attempts = $Problem->max_attempts;
  141     my $attempts_used = $Problem->num_correct + $Problem->num_incorrect + $thisAttempt;
  142     if ($max_attempts == -1 or $attempts_used < $max_attempts) {
  143       return $authz->hasPermissions($User->user_id, "record_answers_after_open_date_with_attempts");
  144     } else {
  145       return $authz->hasPermissions($User->user_id, "record_answers_after_open_date_without_attempts");
  146     }
  147   } elsif (between($Set->due_date, $Set->answer_date)) {
  148     return $authz->hasPermissions($User->user_id, "record_answers_after_due_date");
  149   } elsif (after($Set->answer_date)) {
  150     return $authz->hasPermissions($User->user_id, "record_answers_after_answer_date");
  151   }
  152 }
  153 
  154 sub can_checkAnswers {
  155   my ($self, $User, $EffectiveUser, $Set, $Problem, $submitAnswers) = @_;
  156   my $authz = $self->r->authz;
  157   my $thisAttempt = $submitAnswers ? 1 : 0;
  158 
  159   if (before($Set->open_date)) {
  160     return $authz->hasPermissions($User->user_id, "check_answers_before_open_date");
  161   } elsif (between($Set->open_date, $Set->due_date)) {
  162     my $max_attempts = $Problem->max_attempts;
  163     my $attempts_used = $Problem->num_correct + $Problem->num_incorrect + $thisAttempt;
  164     if ($max_attempts == -1 or $attempts_used < $max_attempts) {
  165       return $authz->hasPermissions($User->user_id, "check_answers_after_open_date_with_attempts");
  166     } else {
  167       return $authz->hasPermissions($User->user_id, "check_answers_after_open_date_without_attempts");
  168     }
  169   } elsif (between($Set->due_date, $Set->answer_date)) {
  170     return $authz->hasPermissions($User->user_id, "check_answers_after_due_date");
  171   } elsif (after($Set->answer_date)) {
  172     return $authz->hasPermissions($User->user_id, "check_answers_after_answer_date");
  173   }
  174 }
  175 
  176 # Reset the default in some cases
  177 sub set_showOldAnswers_default {
  178   my ($self, $ce, $userName, $authz, $set) = @_;
  179   # these people always use the system/course default, so don't
  180   # override the value of ...->{showOldAnswers}
  181   return if $authz->hasPermissions($userName, "can_always_use_show_old_answers_default");
  182   # this person should always default to 0
  183   $ce->{pg}->{options}->{showOldAnswers} = 0
  184     unless ($authz->hasPermissions($userName, "can_show_old_answers_by_default"));
  185   # we are after the due date, so default to not showing it
  186   $ce->{pg}->{options}->{showOldAnswers} = 0 if $set->{due_date} && after($set->{due_date});
  187 }
  188 
  189 ################################################################################
  190 # output utilities
  191 ################################################################################
  192 
  193 # Note: the substance of attemptResults is lifted into GatewayQuiz.pm,
  194 # with some changes to the output format
  195 
  196 sub attemptResults {
  197   my $self = shift;
  198   my $r = $self->r;
  199   my $pg = shift;
  200   my $showAttemptAnswers = shift;
  201   my $showCorrectAnswers = shift;
  202   my $showAttemptResults = $showAttemptAnswers && shift;
  203   my $showSummary = shift;
  204   my $showAttemptPreview = shift || 0;
  205 
  206   my $ce = $self->r->ce;
  207 
  208   # for color coding the responses.
  209   my @correct_ids = ();
  210   my @incorrect_ids = ();
  211 
  212 
  213   my $problemResult = $pg->{result}; # the overall result of the problem
  214   my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} };
  215 
  216   my $showMessages = $showAttemptAnswers && grep { $pg->{answers}->{$_}->{ans_message} } @answerNames;
  217 
  218   my $basename = "equation-" . $self->{set}->psvn. "." . $self->{problem}->problem_id . "-preview";
  219 
  220   # to make grabbing these options easier, we'll pull them out now...
  221   my %imagesModeOptions = %{$ce->{pg}->{displayModeOptions}->{images}};
  222 
  223   my $imgGen = WeBWorK::PG::ImageGenerator->new(
  224     tempDir         => $ce->{webworkDirs}->{tmp},
  225     latex         => $ce->{externalPrograms}->{latex},
  226     dvipng          => $ce->{externalPrograms}->{dvipng},
  227     useCache        => 1,
  228     cacheDir        => $ce->{webworkDirs}->{equationCache},
  229     cacheURL        => $ce->{webworkURLs}->{equationCache},
  230     cacheDB         => $ce->{webworkFiles}->{equationCacheDB},
  231     dvipng_align    => $imagesModeOptions{dvipng_align},
  232     dvipng_depth_db => $imagesModeOptions{dvipng_depth_db},
  233   );
  234 
  235   my $showEvaluatedAnswers = $ce->{pg}->{options}->{showEvaluatedAnswers};
  236 
  237   my $header;
  238   #$header .= CGI::th("Part");
  239   if ($showEvaluatedAnswers) {
  240     $header .= $showAttemptAnswers ? CGI::th($r->maketext("Entered"))  : "";
  241   }
  242   $header .= $showAttemptPreview ? CGI::th($r->maketext("Answer Preview"))  : "";
  243   $header .= $showCorrectAnswers ? CGI::th($r->maketext("Correct"))  : "";
  244   $header .= $showAttemptResults ? CGI::th($r->maketext("Result"))   : "";
  245   $header .= $showMessages       ? CGI::th($r->maketext("Messages")) : "";
  246   my $fully = '';
  247   my @tableRows = ( $header );
  248   my $numCorrect = 0;
  249   my $numBlanks  =0;
  250   my $tthPreambleCache;
  251   foreach my $name (@answerNames) {
  252     my $answerResult  = $pg->{answers}->{$name};
  253     my $studentAnswer = $answerResult->{student_ans}; # original_student_ans
  254     my $preview       = ($showAttemptPreview
  255                           ? $self->previewAnswer($answerResult, $imgGen, \$tthPreambleCache)
  256                           : "");
  257     my $correctAnswerPreview = $self->previewCorrectAnswer($answerResult, $imgGen, \$tthPreambleCache);
  258     my $correctAnswer = $answerResult->{correct_ans};
  259     my $answerScore   = $answerResult->{score};
  260     my $answerMessage = $showMessages ? $answerResult->{ans_message} : "";
  261     $answerMessage =~ s/\n/<BR>/g;
  262     $numCorrect += $answerScore >= 1;
  263     $numBlanks++ unless $studentAnswer =~/\S/ || $answerScore >= 1;   # unless student answer contains entry
  264     my $resultString = $answerScore >= 1 ? CGI::span({class=>"ResultsWithoutError"}, $r->maketext("correct")) :
  265                        $answerScore > 0  ? $r->maketext("[_1]% correct", int($answerScore*100)) :
  266                                                        CGI::span({class=>"ResultsWithError"}, $r->maketext("incorrect"));
  267     $fully = $r->maketext("completely") if $answerScore >0 and $answerScore < 1;
  268 
  269     push @correct_ids,   $name if $answerScore == 1;
  270     push @incorrect_ids, $name if $answerScore < 1;
  271 
  272     # need to capture auxiliary answers as well and identify their ids.
  273 
  274 
  275     my $row;
  276     #$row .= CGI::td($name);
  277     if ($showEvaluatedAnswers) {
  278       $row .= $showAttemptAnswers ? CGI::td($self->nbsp($studentAnswer)) : "";
  279     }
  280     $row .= $showAttemptPreview ? CGI::td({onmouseover=>qq!Tip('$studentAnswer',SHADOW, true,
  281                         DELAY, 1000, FADEIN, 300, FADEOUT, 300, STICKY, 1, OFFSETX, -20, CLOSEBTN, true, CLICKCLOSE, false,
  282                         BGCOLOR, '#F4FF91', TITLE, 'Entered:',TITLEBGCOLOR, '#F4FF91', TITLEFONTCOLOR, '#000000')!},
  283                         $self->nbsp($preview))       : "";
  284     $row .= $showCorrectAnswers ? CGI::td({onmouseover=> qq!Tip('$correctAnswer',SHADOW, true,
  285                         DELAY, 1000, FADEIN, 300, FADEOUT, 300, STICKY, 1, OFFSETX, -20, CLOSEBTN, true, CLICKCLOSE, false,
  286                         BGCOLOR, '#F4FF91', TITLE, 'Entered:',TITLEBGCOLOR, '#F4FF91', TITLEFONTCOLOR, '#000000')!},
  287                       $self->nbsp($correctAnswerPreview)) : "";
  288     $row .= $showAttemptResults ? CGI::td($self->nbsp($resultString))  : "";
  289     $row .= $showMessages       ? CGI::td({-class=>"Message"},$self->nbsp($answerMessage)) : "";
  290     push @tableRows, $row;
  291   }
  292 
  293   # render equation images
  294   $imgGen->render(refresh => 1);
  295 
  296 # my $numIncorrectNoun = scalar @answerNames == 1 ? "question" : "questions";
  297   my $scorePercent = sprintf("%.0f%%", $problemResult->{score} * 100);
  298 #   FIXME  -- I left the old code in in case we have to back out.
  299 # my $summary = "On this attempt, you answered $numCorrect out of "
  300 #   . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent.";
  301   my $summary = "";
  302   unless (defined($problemResult->{summary}) and $problemResult->{summary} =~ /\S/) {
  303     if (scalar @answerNames == 1) {  #default messages
  304         if ($numCorrect == scalar @answerNames) {
  305           $summary .= CGI::div({class=>"ResultsWithoutError"},$r->maketext("The answer above is correct."));
  306          } else {
  307            $summary .= CGI::div({class=>"ResultsWithError"},$r->maketext("The answer above is NOT [_1]correct.", $fully));
  308          }
  309     } else {
  310         if ($numCorrect == scalar @answerNames) {
  311           $summary .= CGI::div({class=>"ResultsWithoutError"},$r->maketext("All of the answers above are correct."));
  312          }
  313          #unless ($numCorrect + $numBlanks == scalar( @answerNames)) { # this allowed you to figure out if you got one answer right.
  314          elsif ($numBlanks != scalar( @answerNames)) {
  315           $summary .= CGI::div({class=>"ResultsWithError"},$r->maketext("At least one of the answers above is NOT [_1]correct.", $fully));
  316          }
  317          if ($numBlanks) {
  318           my $s = ($numBlanks>1)?'':'s';
  319           $summary .= CGI::div({class=>"ResultsAlert"},$r->maketext("[quant,_1,of the questions remains,of the questions remain] unanswered.", $numBlanks));
  320          }
  321     }
  322   } else {
  323     $summary = $problemResult->{summary};   # summary has been defined by grader
  324   }
  325 
  326   $self->{correct_ids}=[@correct_ids]       if @correct_ids;
  327   $self->{incorrect_ids} = [@incorrect_ids] if @incorrect_ids;
  328 
  329   return
  330     CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows))
  331     . ($showSummary ? CGI::p({class=>'attemptResultsSummary'},$summary) : "");
  332 }
  333 
  334 
  335 # Note: previewAnswer is lifted into GatewayQuiz.pm
  336 
  337 sub previewAnswer {
  338   my ($self, $answerResult, $imgGen, $tthPreambleCache) = @_;
  339   my $ce            = $self->r->ce;
  340   my $effectiveUser = $self->{effectiveUser};
  341   my $set           = $self->{set};
  342   my $problem       = $self->{problem};
  343   my $displayMode   = $self->{displayMode};
  344 
  345   # note: right now, we have to do things completely differently when we are
  346   # rendering math from INSIDE the translator and from OUTSIDE the translator.
  347   # so we'll just deal with each case explicitly here. there's some code
  348   # duplication that can be dealt with later by abstracting out tth/dvipng/etc.
  349 
  350   my $tex = $answerResult->{preview_latex_string};
  351 
  352   return "" unless defined $tex and $tex ne "";
  353 
  354   if ($displayMode eq "plainText") {
  355     return $tex;
  356   } elsif ($displayMode eq "formattedText") {
  357 
  358     # read the TTH preamble, or use the cached copy passed in from the caller
  359     my $tthPreamble='';
  360     if (defined $$tthPreambleCache) {
  361       $tthPreamble = $$tthPreambleCache;
  362     } else {
  363       my $tthPreambleFile = $ce->{courseDirs}->{templates} . "/tthPreamble.tex";
  364       if (-r $tthPreambleFile) {
  365         $tthPreamble = readFile($tthPreambleFile);
  366         # thanks to Jim Martino. each line in the definition file should end with
  367         #a % to prevent adding supurious paragraphs to output:
  368         $tthPreamble =~ s/(.)\n/$1%\n/g;
  369         # solves the problem if the file doesn't end with a return:
  370         $tthPreamble .="%\n";
  371         # store preamble in cache:
  372         $$tthPreambleCache = $tthPreamble;
  373       } else {
  374       }
  375     }
  376 
  377     # construct TTH command line
  378     my $tthCommand = $ce->{externalPrograms}->{tth}
  379       . " -L -f5 -u -r  2> /dev/null <<END_OF_INPUT; echo > /dev/null\n"
  380       . $tthPreamble . "\\[" . $tex . "\\]\n"
  381       . "END_OF_INPUT\n";
  382 
  383     # call tth
  384     my $result = `$tthCommand`;
  385     if ($?) {
  386       return "<b>[tth failed: $? $@]</b>";
  387     } else {
  388       #  avoid border problems in tables and remove unneeded initial <br>
  389       $result =~ s/(<table [^>]*)>/$1 CLASS="ArrayLayout">/gi;
  390       $result =~ s!\s*<br clear="all" />!!;
  391       return $result;
  392     }
  393 
  394   } elsif ($displayMode eq "images") {
  395     $imgGen->add($tex);
  396   } elsif ($displayMode eq "MathJax") {
  397     return '<span class="MathJax_Preview">[math]</span><script type="math/tex; mode=display">'.$tex.'</script>';
  398   } elsif ($displayMode eq "jsMath") {
  399     $tex =~ s/&/&amp;/g; $tex =~ s/</&lt;/g; $tex =~ s/>/&gt;/g;
  400     return '<SPAN CLASS="math">\\displaystyle{'.$tex.'}</SPAN>';
  401   }
  402 }
  403 sub previewCorrectAnswer {
  404   my ($self, $answerResult, $imgGen, $tthPreambleCache) = @_;
  405   my $ce            = $self->r->ce;
  406   my $effectiveUser = $self->{effectiveUser};
  407   my $set           = $self->{set};
  408   my $problem       = $self->{problem};
  409   my $displayMode   = $self->{displayMode};
  410 
  411   # note: right now, we have to do things completely differently when we are
  412   # rendering math from INSIDE the translator and from OUTSIDE the translator.
  413   # so we'll just deal with each case explicitly here. there's some code
  414   # duplication that can be dealt with later by abstracting out tth/dvipng/etc.
  415 
  416   my $tex = $answerResult->{correct_ans_latex_string};
  417   return $answerResult->{correct_ans} unless defined $tex and $tex=~/\S/;   # some answers don't have latex strings defined
  418   # return "" unless defined $tex and $tex ne "";
  419 
  420   if ($displayMode eq "plainText") {
  421     return $tex;
  422   } elsif ($displayMode eq "formattedText") {
  423 
  424     # read the TTH preamble, or use the cached copy passed in from the caller
  425     my $tthPreamble='';
  426     if (defined $$tthPreambleCache) {
  427       $tthPreamble = $$tthPreambleCache;
  428     } else {
  429       my $tthPreambleFile = $ce->{courseDirs}->{templates} . "/tthPreamble.tex";
  430       if (-r $tthPreambleFile) {
  431         $tthPreamble = readFile($tthPreambleFile);
  432         # thanks to Jim Martino. each line in the definition file should end with
  433         #a % to prevent adding supurious paragraphs to output:
  434         $tthPreamble =~ s/(.)\n/$1%\n/g;
  435         # solves the problem if the file doesn't end with a return:
  436         $tthPreamble .="%\n";
  437         # store preamble in cache:
  438         $$tthPreambleCache = $tthPreamble;
  439       } else {
  440       }
  441     }
  442 
  443     # construct TTH command line
  444     my $tthCommand = $ce->{externalPrograms}->{tth}
  445       . " -L -f5 -u -r  2> /dev/null <<END_OF_INPUT; echo > /dev/null\n"
  446       . $tthPreamble . "\\[" . $tex . "\\]\n"
  447       . "END_OF_INPUT\n";
  448 
  449     # call tth
  450     my $result = `$tthCommand`;
  451     if ($?) {
  452       return "<b>[tth failed: $? $@]</b>";
  453     } else {
  454       #  avoid border problems in tables and remove unneeded initial <br>
  455       $result =~ s/(<table [^>]*)>/$1 CLASS="ArrayLayout">/gi;
  456       $result =~ s!\s*<br clear="all" />!!;
  457       return $result;
  458     }
  459 
  460   } elsif ($displayMode eq "images") {
  461     $imgGen->add($tex);
  462   } elsif ($displayMode eq "MathJax") {
  463     return '<span class="MathJax_Preview">[math]</span><script type="math/tex; mode=display">'.$tex.'</script>';
  464   } elsif ($displayMode eq "jsMath") {
  465     $tex =~ s/&/&amp;/g; $tex =~ s/</&lt;/g; $tex =~ s/>/&gt;/g;
  466     return '<SPAN CLASS="math">\\displaystyle{'.$tex.'}</SPAN>';
  467   }
  468 }
  469 
  470 ################################################################################
  471 # Template escape implementations
  472 ################################################################################
  473 
  474 sub pre_header_initialize {
  475   my ($self) = @_;
  476   my $r = $self->r;
  477   my $ce = $r->ce;
  478   my $db = $r->db;
  479   my $authz = $r->authz;
  480   my $urlpath = $r->urlpath;
  481 
  482   my $setName = $urlpath->arg("setID");
  483   my $problemNumber = $r->urlpath->arg("problemID");
  484   my $userName = $r->param('user');
  485   my $effectiveUserName = $r->param('effectiveUser');
  486   my $key = $r->param('key');
  487   my $editMode = $r->param("editMode");
  488 
  489   my $user = $db->getUser($userName); # checked
  490   die "record for user $userName (real user) does not exist."
  491     unless defined $user;
  492 
  493   my $effectiveUser = $db->getUser($effectiveUserName); # checked
  494   die "record for user $effectiveUserName (effective user) does not exist."
  495     unless defined $effectiveUser;
  496 
  497   # obtain the merged set for $effectiveUser
  498   my $set = $db->getMergedSet($effectiveUserName, $setName); # checked
  499 
  500   $self->set_showOldAnswers_default($ce, $userName, $authz, $set);
  501 
  502   # Database fix (in case of undefined visiblity state values)
  503   # this is only necessary because some people keep holding to ww1.9 which did not have a visible field
  504   # make sure visible is set to 0 or 1
  505   if ( $set and $set->visible ne "0" and $set->visible ne "1") {
  506     my $globalSet = $db->getGlobalSet($set->set_id);
  507     $globalSet->visible("1"); # defaults to visible
  508     $db->putGlobalSet($globalSet);
  509     $set = $db->getMergedSet($effectiveUserName, $setName);
  510   } else {
  511     # don't do anything just yet, maybe we're a professor and we're
  512     # fabricating a set or haven't assigned it to ourselves just yet
  513   }
  514     # When a set is created enable_reduced_scoring is null, so we have to set it
  515   if ( $set and $set->enable_reduced_scoring ne "0" and $set->enable_reduced_scoring ne "1") {
  516     my $globalSet = $db->getGlobalSet($set->set_id);
  517     $globalSet->enable_reduced_scoring("0");  # defaults to disabled
  518     $db->putGlobalSet($globalSet);
  519     $set = $db->getMergedSet($effectiveUserName, $setName);
  520   }
  521 
  522 
  523   # obtain the merged problem for $effectiveUser
  524   my $problem = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked
  525 
  526   # A very hacky and temporary solution to the max_attempts problem
  527   # if($problem->max_attempts == ""){
  528     # $problem->max_attempts = -1;
  529   # }
  530 
  531   if ($authz->hasPermissions($userName, "modify_problem_sets")) {
  532     # professors are allowed to fabricate sets and problems not
  533     # assigned to them (or anyone). this allows them to use the
  534     # editor to
  535 
  536     # if a User Set does not exist for this user and this set
  537     # then we check the Global Set
  538     # if that does not exist we create a fake set
  539     # if it does, we add fake user data
  540     unless (defined $set) {
  541       my $userSetClass = $db->{set_user}->{record};
  542       my $globalSet = $db->getGlobalSet($setName); # checked
  543 
  544       if (not defined $globalSet) {
  545         $set = fake_set($db);
  546       } else {
  547         $set = global2user($userSetClass, $globalSet);
  548         $set->psvn(0);
  549       }
  550     }
  551 
  552     # if that is not yet defined obtain the global problem,
  553     # convert it to a user problem, and add fake user data
  554     unless (defined $problem) {
  555       my $userProblemClass = $db->{problem_user}->{record};
  556       my $globalProblem = $db->getGlobalProblem($setName, $problemNumber); # checked
  557       # if the global problem doesn't exist either, bail!
  558       if(not defined $globalProblem) {
  559         my $sourceFilePath = $r->param("sourceFilePath");
  560         die "sourceFilePath is unsafe!" unless path_is_subdir($sourceFilePath, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
  561         # These are problems from setmaker.  If declared invalid, they won't come up
  562         $self->{invalidProblem} = $self->{invalidSet} = 1 unless defined $sourceFilePath;
  563 #       die "Problem $problemNumber in set $setName does not exist" unless defined $sourceFilePath;
  564         $problem = fake_problem($db);
  565         $problem->problem_id(1);
  566         $problem->source_file($sourceFilePath);
  567         $problem->user_id($effectiveUserName);
  568       } else {
  569         $problem = global2user($userProblemClass, $globalProblem);
  570         $problem->user_id($effectiveUserName);
  571         $problem->problem_seed(0);
  572         $problem->status(0);
  573         $problem->attempted(0);
  574         $problem->last_answer("");
  575         $problem->num_correct(0);
  576         $problem->num_incorrect(0);
  577       }
  578     }
  579 
  580     # now we're sure we have valid UserSet and UserProblem objects
  581     # yay!
  582 
  583     # now deal with possible editor overrides:
  584 
  585     # if the caller is asking to override the source file, and
  586     # editMode calls for a temporary file, do so
  587     my $sourceFilePath = $r->param("sourceFilePath");
  588     if (defined $editMode and $editMode eq "temporaryFile" and defined $sourceFilePath) {
  589       die "sourceFilePath is unsafe!" unless path_is_subdir($sourceFilePath, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
  590       $problem->source_file($sourceFilePath);
  591     }
  592 
  593     # if the problem does not have a source file or no source file has been passed in
  594     # then this is really an invalid problem (probably from a bad URL)
  595     $self->{invalidProblem} = not (defined $sourceFilePath or $problem->source_file);
  596 
  597     # if the caller is asking to override the problem seed, do so
  598     my $problemSeed = $r->param("problemSeed");
  599     if (defined $problemSeed) {
  600       $problem->problem_seed($problemSeed);
  601     }
  602 
  603     my $visiblityStateClass = ($set->visible) ? $r->maketext("visible") : $r->maketext("hidden");
  604     my $visiblityStateText = ($set->visible) ? $r->maketext("visible to students")."." : $r->maketext("hidden from students").".";
  605     $self->addmessage(CGI::span($r->maketext("This set is [_1]", CGI::font({class=>$visiblityStateClass}, $visiblityStateText))));
  606 
  607   # test for additional problem validity if it's not already invalid
  608         } else {
  609     $self->{invalidProblem} = !(defined $problem and ($set->visible || $authz->hasPermissions($userName, "view_hidden_sets")));
  610 
  611     $self->addbadmessage(CGI::p($r->maketext("This problem will not count towards your grade."))) if $problem and not $problem->value and not $self->{invalidProblem};
  612   }
  613 
  614   $self->{userName}          = $userName;
  615   $self->{effectiveUserName} = $effectiveUserName;
  616   $self->{user}              = $user;
  617   $self->{effectiveUser}     = $effectiveUser;
  618   $self->{set}               = $set;
  619   $self->{problem}           = $problem;
  620   $self->{editMode}          = $editMode;
  621 
  622   ##### form processing #####
  623 
  624   # set options from form fields (see comment at top of file for names)
  625   my $displayMode        = $r->param("displayMode") || $ce->{pg}->{options}->{displayMode};
  626   my $redisplay          = $r->param("redisplay");
  627   my $submitAnswers      = $r->param("submitAnswers");
  628   my $checkAnswers       = $r->param("checkAnswers");
  629   my $previewAnswers     = $r->param("previewAnswers");
  630 
  631   my $formFields = { WeBWorK::Form->new_from_paramable($r)->Vars };
  632 
  633   $self->{displayMode}    = $displayMode;
  634   $self->{redisplay}      = $redisplay;
  635   $self->{submitAnswers}  = $submitAnswers;
  636   $self->{checkAnswers}   = $checkAnswers;
  637   $self->{previewAnswers} = $previewAnswers;
  638   $self->{formFields}     = $formFields;
  639 
  640   # get result and send to message
  641   my $status_message = $r->param("status_message");
  642   $self->addmessage(CGI::p("$status_message")) if $status_message;
  643 
  644   # now that we've set all the necessary variables quit out if the set or problem is invalid
  645   return if $self->{invalidSet} || $self->{invalidProblem};
  646 
  647   ##### permissions #####
  648 
  649   # what does the user want to do?
  650   #FIXME  There is a problem with checkboxes -- if they are not checked they are invisible.  Hence if the default mode in $ce is 1
  651   # there is no way to override this.  Probably this is ok for the last three options, but it was definitely not ok for showing
  652   # saved answers which is normally on, but you want to be able to turn it off!  This section should be moved to ContentGenerator
  653   # so that you can set these options anywhere.  We also need mechanisms for making them sticky.
  654   # Note: ProblemSet and ProblemSets might set showOldAnswers to '', which
  655   #       needs to be treated as if it is not set.
  656   my %want = (
  657     showOldAnswers     => (defined($r->param("showOldAnswers")) and $r->param("showOldAnswers") ne '') ? $r->param("showOldAnswers")  : $ce->{pg}->{options}->{showOldAnswers},
  658     showCorrectAnswers => $r->param("showCorrectAnswers") || $ce->{pg}->{options}->{showCorrectAnswers},
  659     showHints          => $r->param("showHints")          || $ce->{pg}->{options}->{showHints},
  660     showSolutions      => $r->param("showSolutions")      || $ce->{pg}->{options}->{showSolutions},
  661     recordAnswers      => $submitAnswers,
  662     checkAnswers       => $checkAnswers,
  663     getSubmitButton    => 1,
  664   );
  665 
  666   # are certain options enforced?
  667   my %must = (
  668     showOldAnswers     => 0,
  669     showCorrectAnswers => 0,
  670     showHints          => 0,
  671     showSolutions      => 0,
  672     recordAnswers      => ! $authz->hasPermissions($userName, "avoid_recording_answers"),
  673     checkAnswers       => 0,
  674     getSubmitButton    => 0,
  675   );
  676 
  677   # does the user have permission to use certain options?
  678   my @args = ($user, $effectiveUser, $set, $problem);
  679   my %can = (
  680     showOldAnswers     => $self->can_showOldAnswers(@args),
  681     showCorrectAnswers => $self->can_showCorrectAnswers(@args),
  682     showHints          => $self->can_showHints(@args),
  683     showSolutions      => $self->can_showSolutions(@args),
  684     recordAnswers      => $self->can_recordAnswers(@args, 0),
  685     checkAnswers       => $self->can_checkAnswers(@args, $submitAnswers),
  686     getSubmitButton    => $self->can_recordAnswers(@args, $submitAnswers),
  687   );
  688 
  689   # final values for options
  690   my %will;
  691   foreach (keys %must) {
  692     $will{$_} = $can{$_} && ($want{$_} || $must{$_});
  693     #warn "final values for options $_ is can $can{$_}, want $want{$_}, must $must{$_}, will $will{$_}";
  694   }
  695 
  696   ##### sticky answers #####
  697 
  698   if (not ($submitAnswers or $previewAnswers or $checkAnswers) and $will{showOldAnswers}) {
  699     # do this only if new answers are NOT being submitted
  700     my %oldAnswers = decodeAnswers($problem->last_answer);
  701     $formFields->{$_} = $oldAnswers{$_} foreach keys %oldAnswers;
  702   }
  703 
  704   ##### translation #####
  705 
  706   debug("begin pg processing");
  707   my $pg = WeBWorK::PG->new(
  708     $ce,
  709     $effectiveUser,
  710     $key,
  711     $set,
  712     $problem,
  713     $set->psvn, # FIXME: this field should be removed
  714     $formFields,
  715     { # translation options
  716       displayMode     => $displayMode,
  717       showHints       => $will{showHints},
  718       showSolutions   => $will{showSolutions},
  719       refreshMath2img => $will{showHints} || $will{showSolutions},
  720       processAnswers  => 1,
  721       permissionLevel => $db->getPermissionLevel($userName)->permission,
  722       effectivePermissionLevel => $db->getPermissionLevel($effectiveUserName)->permission,
  723     },
  724   );
  725 
  726   debug("end pg processing");
  727 
  728   ##### fix hint/solution options #####
  729 
  730   $can{showHints}     &&= $pg->{flags}->{hintExists}
  731                       &&= $pg->{flags}->{showHintLimit}<=$pg->{state}->{num_of_incorrect_ans};
  732   $can{showSolutions} &&= $pg->{flags}->{solutionExists};
  733 
  734   ##### store fields #####
  735 
  736   $self->{want} = \%want;
  737   $self->{must} = \%must;
  738   $self->{can}  = \%can;
  739   $self->{will} = \%will;
  740   $self->{pg} = $pg;
  741 }
  742 
  743 sub if_errors($$) {
  744   my ($self, $arg) = @_;
  745 
  746   if ($self->{isOpen}) {
  747     return $self->{pg}->{flags}->{error_flag} ? $arg : !$arg;
  748   } else {
  749     return !$arg;
  750   }
  751 }
  752 
  753 sub head {
  754   my ($self) = @_;
  755 
  756   return "" if ( $self->{invalidSet} );
  757   return $self->{pg}->{head_text} if $self->{pg}->{head_text};
  758 }
  759 
  760 sub post_header_text {
  761   my ($self) = @_;
  762   return "" if ( $self->{invalidSet} );
  763     return $self->{pg}->{post_header_text} if $self->{pg}->{post_header_text};
  764 }
  765 
  766 sub options {
  767   my ($self) = @_;
  768   #warn "doing options in Problem";
  769 
  770   # don't show options if we don't have anything to show
  771   return "" if $self->{invalidSet} or $self->{invalidProblem};
  772 
  773   my $displayMode = $self->{displayMode};
  774   my %can = %{ $self->{can} };
  775 
  776   my @options_to_show = "displayMode";
  777   push @options_to_show, "showOldAnswers" if $can{showOldAnswers};
  778   push @options_to_show, "showHints" if $can{showHints};
  779   push @options_to_show, "showSolutions" if $can{showSolutions};
  780 
  781   return $self->optionsMacro(
  782     options_to_show => \@options_to_show,
  783     extra_params => ["editMode", "sourceFilePath"],
  784   );
  785 }
  786 
  787 sub siblings {
  788   my ($self) = @_;
  789   my $r = $self->r;
  790   my $db = $r->db;
  791   my $urlpath = $r->urlpath;
  792 
  793   # can't show sibling problems if the set is invalid
  794   return "" if $self->{invalidSet};
  795 
  796   my $courseID = $urlpath->arg("courseID");
  797   my $setID = $self->{set}->set_id;
  798   my $eUserID = $r->param("effectiveUser");
  799   my @problemIDs = sort { $a <=> $b } $db->listUserProblems($eUserID, $setID);
  800 
  801   print CGI::start_div({class=>"info-box", id=>"fisheye"});
  802   print CGI::h2($r->maketext("Problems"));
  803   #print CGI::start_ul({class=>"LinksMenu"});
  804   #print CGI::start_li();
  805   #print CGI::span({style=>"font-size:larger"}, "Problems");
  806   print CGI::start_ul();
  807 
  808   foreach my $problemID (@problemIDs) {
  809     my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", $r,
  810       courseID => $courseID, setID => $setID, problemID => $problemID);
  811     print CGI::li(CGI::a( {href=>$self->systemLink($problemPage,
  812                           params=>{  displayMode => $self->{displayMode},
  813                                  showOldAnswers => $self->{will}->{showOldAnswers}
  814                               })},  $r->maketext("Problem [_1]",$problemID))
  815      );
  816   }
  817 
  818   print CGI::end_ul();
  819   #print CGI::end_li();
  820   #print CGI::end_ul();
  821   print CGI::end_div();
  822 
  823   return "";
  824 }
  825 
  826 sub nav {
  827   my ($self, $args) = @_;
  828   my $r = $self->r;
  829   my $db = $r->db;
  830   my $urlpath = $r->urlpath;
  831 
  832   return "" if ( $self->{invalidSet} );
  833 
  834   my $courseID = $urlpath->arg("courseID");
  835   my $setID = $self->{set}->set_id if !($self->{invalidSet});
  836   my $problemID = $self->{problem}->problem_id if !($self->{invalidProblem});
  837   my $eUserID = $r->param("effectiveUser");
  838 
  839   my ($prevID, $nextID);
  840 
  841   if (!$self->{invalidProblem}) {
  842     my @problemIDs = $db->listUserProblems($eUserID, $setID);
  843     foreach my $id (@problemIDs) {
  844       $prevID = $id if $id < $problemID
  845         and (not defined $prevID or $id > $prevID);
  846       $nextID = $id if $id > $problemID
  847         and (not defined $nextID or $id < $nextID);
  848     }
  849   }
  850 
  851   my @links;
  852 
  853   if ($prevID) {
  854     my $prevPage = $urlpath->newFromModule(__PACKAGE__, $r,
  855       courseID => $courseID, setID => $setID, problemID => $prevID);
  856     push @links, $r->maketext("Previous Problem"), $r->location . $prevPage->path, $r->maketext("navPrev");
  857   } else {
  858     push @links, $r->maketext("Previous Problem"), "", $r->maketext("navPrevGrey");
  859   }
  860 
  861   if (defined($setID) && $setID ne 'Undefined_Set') {
  862     push @links, $r->maketext("Problem List"), $r->location . $urlpath->parent->path, $r->maketext("navProbList");
  863   } else {
  864     push @links, $r->maketext("Problem List"), "", $r->maketext("navProbListGrey");
  865   }
  866 
  867   if ($nextID) {
  868     my $nextPage = $urlpath->newFromModule(__PACKAGE__, $r,
  869       courseID => $courseID, setID => $setID, problemID => $nextID);
  870     push @links, $r->maketext("Next Problem"), $r->location . $nextPage->path, $r->maketext("navNext");
  871   } else {
  872     push @links, $r->maketext("Next Problem"), "", $r->maketext("navNextGrey");
  873   }
  874 
  875   my $tail = "";
  876 
  877   $tail .= "&displayMode=".$self->{displayMode} if defined $self->{displayMode};
  878   $tail .= "&showOldAnswers=".$self->{will}->{showOldAnswers}
  879     if defined $self->{will}->{showOldAnswers};
  880   return $self->navMacro($args, $tail, @links);
  881 }
  882 
  883 sub title {
  884   my ($self) = @_;
  885   my $r = $self->r;
  886   # using the url arguments won't break if the set/problem are invalid
  887   my $setID = WeBWorK::ContentGenerator::underscore2nbsp($self->r->urlpath->arg("setID"));
  888   my $problemID = $self->r->urlpath->arg("problemID");
  889 
  890   return $r->maketext("[_1]: Problem [_2]",$setID, $problemID);
  891 }
  892 
  893 
  894 # now altered to outsource most output operations to the template, main functions now are simply error checking and answer processing - ghe3
  895 sub body {
  896   my $self = shift;
  897   my $set = $self->{set};
  898   my $problem = $self->{problem};
  899   my $pg = $self->{pg};
  900 
  901   my $valid = WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::check_invalid($self);
  902   unless($valid eq "valid"){
  903     return $valid;
  904   }
  905 
  906 
  907 
  908   ##### answer processing #####
  909   debug("begin answer processing");
  910   # if answers were submitted:
  911   #my $scoreRecordedMessage = WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::process_and_log_answer($self);
  912   debug("end answer processing");
  913   # output for templates that only use body instead of calling the body parts individually
  914   $self ->output_JS;
  915   $self ->output_custom_edit_message;
  916   $self ->output_summary;
  917   $self ->output_hidden_info;
  918   $self ->output_form_start();
  919   $self ->output_problem_body;
  920   $self ->output_message;
  921   $self ->output_editorLink;
  922   $self ->output_checkboxes;
  923   $self ->output_submit_buttons;
  924   $self ->output_score_summary;
  925   $self ->output_misc;
  926   print "</form>";
  927   # debugging stuff
  928   if (0) {
  929     print
  930       CGI::hr(),
  931       CGI::h2("debugging information"),
  932       CGI::h3("form fields"),
  933       ref2string($self->{formFields}),
  934       CGI::h3("user object"),
  935       ref2string($self->{user}),
  936       CGI::h3("set object"),
  937       ref2string($set),
  938       CGI::h3("problem object"),
  939       ref2string($problem),
  940       CGI::h3("PG object"),
  941       ref2string($pg, {'WeBWorK::PG::Translator' => 1});
  942   }
  943   debug("leaving body of Problem.pm");
  944   return "";
  945 }
  946 
  947 # output_form_start subroutine
  948 
  949 # prints out the beginning of the main form, and the necessary hidden authentication fields
  950 
  951 sub output_form_start{
  952   my $self = shift;
  953   my $r = $self->r;
  954   print CGI::start_form(-method=>"POST", -action=> $r->uri,-name=>"problemMainForm", onsubmit=>"submitAction()");
  955   print $self->hidden_authen_fields;
  956   return "";
  957 }
  958 
  959 # output_problem_body subroutine
  960 
  961 # prints out the body of the current problem
  962 
  963 sub output_problem_body{
  964   my $self = shift;
  965   my $pg = $self->{pg};
  966 
  967   print "\n";
  968   print CGI::p($pg->{body_text});
  969   return "";
  970 }
  971 
  972 # output_message subroutine
  973 
  974 # prints out a message about the problem
  975 
  976 sub output_message{
  977   my $self = shift;
  978   my $pg = $self->{pg};
  979   my $r = $self->r;
  980 
  981   print CGI::p(CGI::b($r->maketext("Note").": "). CGI::i($pg->{result}->{msg})) if $pg->{result}->{msg};
  982   return "";
  983 }
  984 
  985 # output_editorLink subroutine
  986 
  987 # processes and prints out the correct link to the editor of the current problem
  988 
  989 sub output_editorLink{
  990 
  991   my $self = shift;
  992 
  993   my $set             = $self->{set};
  994   my $problem         = $self->{problem};
  995   my $pg              = $self->{pg};
  996 
  997   my $r = $self->r;
  998 
  999   my $authz = $r->authz;
 1000   my $urlpath = $r->urlpath;
 1001   my $user = $r->param('user');
 1002 
 1003   my $courseName = $urlpath->arg("courseID");
 1004 
 1005   # FIXME: move editor link to top, next to problem number.
 1006   # format as "[edit]" like we're doing with course info file, etc.
 1007   # add edit link for set as well.
 1008   my $editorLink = "";
 1009   # if we are here without a real homework set, carry that through
 1010   my $forced_field = [];
 1011   $forced_field = ['sourceFilePath' =>  $r->param("sourceFilePath")] if
 1012     ($set->set_id eq 'Undefined_Set');
 1013   if ($authz->hasPermissions($user, "modify_problem_sets")) {
 1014     my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", $r,
 1015       courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id);
 1016     my $editorURL = $self->systemLink($editorPage, params=>$forced_field);
 1017     $editorLink = CGI::p(CGI::a({href=>$editorURL,target =>'WW_Editor'}, $r->maketext("Edit this problem")));
 1018   }
 1019 
 1020   ##### translation errors? #####
 1021 
 1022   if ($pg->{flags}->{error_flag}) {
 1023     if ($authz->hasPermissions($user, "view_problem_debugging_info")) {
 1024       print $self->errorOutput($pg->{errors}, $pg->{body_text});
 1025     } else {
 1026       print $self->errorOutput($pg->{errors}, $r->maketext("You do not have permission to view the details of this error."));
 1027     }
 1028     print "";
 1029   }
 1030   else{
 1031     print $editorLink;
 1032   }
 1033   return "";
 1034 }
 1035 
 1036 # output_checkboxes subroutine
 1037 
 1038 # prints out the checkbox input elements that are available for the current problem
 1039 
 1040 sub output_checkboxes{
 1041   my $self = shift;
 1042   my $r = $self->r;
 1043   my %can = %{ $self->{can} };
 1044   my %will = %{ $self->{will} };
 1045 
 1046   if ($can{showCorrectAnswers}) {
 1047     print WeBWorK::CGI_labeled_input(
 1048       -type  => "checkbox",
 1049       -id    => "showCorrectAnswers_id",
 1050       -label_text => $r->maketext("Show correct answers"),
 1051       -input_attr => $will{showCorrectAnswers} ?
 1052       {
 1053         -name    => "showCorrectAnswers",
 1054         -checked => "checked",
 1055         -value   => 1,
 1056       }
 1057       :
 1058       {
 1059         -name    => "showCorrectAnswers",
 1060         -value   => 1,
 1061       }
 1062     ),"&nbsp;";
 1063   }
 1064   if ($can{showHints}) {
 1065     print CGI::span({style=>"color:red"},
 1066       WeBWorK::CGI_labeled_input(
 1067         -type  => "checkbox",
 1068         -id    => "showHints_id",
 1069         -label_text => $r->maketext("Show Hints"),
 1070         -input_attr => $will{showHints} ?
 1071         {
 1072           -name    => "showHints",
 1073           -checked => "checked",
 1074           -value   => 1,
 1075         }
 1076         :
 1077         {
 1078           -name    => "showHints",
 1079           -value   => 1,
 1080         }
 1081       )
 1082     ),"&nbsp;";
 1083   }
 1084   if ($can{showSolutions}) {
 1085     print WeBWorK::CGI_labeled_input(
 1086       -type  => "checkbox",
 1087       -id    => "showSolutions_id",
 1088       -label_text => $r->maketext("Show Solutions"),
 1089       -input_attr => $will{showSolutions} ?
 1090       {
 1091         -name    => "showSolutions",
 1092         -checked => "checked",
 1093         -value   => 1,
 1094       }
 1095       :
 1096       {
 1097         -name    => "showSolutions",
 1098         -value   => 1,
 1099       }
 1100     ),"&nbsp;";
 1101   }
 1102 
 1103   if ($can{showCorrectAnswers} or $can{showHints} or $can{showSolutions}) {
 1104     print CGI::br();
 1105   }
 1106 
 1107   return "";
 1108 }
 1109 
 1110 # output_submit_buttons
 1111 
 1112 # prints out the submit button input elements that are available for the current problem
 1113 
 1114 sub output_submit_buttons{
 1115   my $self = shift;
 1116   my $r = $self->r;
 1117   my %can = %{ $self->{can} };
 1118 
 1119   my $user = $r->param('user');
 1120   my $effectiveUser = $r->param('effectiveUser');
 1121 
 1122   print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"previewAnswers_id", -input_attr=>{-name=>"previewAnswers", -value=>$r->maketext("Preview Answers")});
 1123   if ($can{checkAnswers}) {
 1124     print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"checkAnswers_id", -input_attr=>{-name=>"checkAnswers", -value=>$r->maketext("Check Answers")});
 1125   }
 1126   if ($can{getSubmitButton}) {
 1127     if ($user ne $effectiveUser) {
 1128       # if acting as a student, make it clear that answer submissions will
 1129       # apply to the student's records, not the professor's.
 1130       print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"submitAnswers_id", -input_attr=>{-name=>$r->maketext("submitAnswers"), -value=>$r->maketext("Submit Answers for [_1]", $effectiveUser)});
 1131     } else {
 1132       #print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>"alert('submit button clicked')");
 1133       print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"submitAnswers_id", -input_attr=>{-name=>"submitAnswers", -value=>$r->maketext("Submit Answers"), -onclick=>""});
 1134       # FIXME  for unknown reasons the -onclick label seems to have to be there in order to allow the forms onsubmit to trigger
 1135       # WTF???
 1136     }
 1137   }
 1138 
 1139   return "";
 1140 }
 1141 
 1142 # output_score_summary subroutine
 1143 
 1144 # prints out a summary of the student's current progress and status on the current problem
 1145 
 1146 sub output_score_summary{
 1147   my $self = shift;
 1148   my $r = $self->r;
 1149   my $problem = $self->{problem};
 1150   my $set = $self->{set};
 1151   my $pg = $self->{pg};
 1152   my $scoreRecordedMessage = WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::process_and_log_answer($self) || "";
 1153   my $submitAnswers = $self->{submitAnswers};
 1154 
 1155   # score summary
 1156   warn "num_correct =", $problem->num_correct,"num_incorrect=",$problem->num_incorrect
 1157           unless defined($problem->num_correct) and defined($problem->num_incorrect) ;
 1158   my $attempts = $problem->num_correct + $problem->num_incorrect;
 1159   #my $attemptsNoun = $attempts != 1 ? $r->maketext("times") : $r->maketext("time");
 1160   my $problem_status    = $problem->status || 0;
 1161   my $lastScore = sprintf("%.0f%%", $problem_status * 100); # Round to whole number
 1162   my $attemptsLeft = $problem->max_attempts - $attempts;
 1163 
 1164 
 1165   my $setClosed = 0;
 1166   my $setClosedMessage;
 1167   if (before($set->open_date) or after($set->due_date)) {
 1168     $setClosed = 1;
 1169     if (before($set->open_date)) {
 1170       $setClosedMessage = $r->maketext("This homework set is not yet open.");
 1171     } elsif (after($set->due_date)) {
 1172       $setClosedMessage = $r->maketext("This homework set is closed.");
 1173     }
 1174   }
 1175   #if (before($set->open_date) or after($set->due_date)) {
 1176   # $setClosed = 1;
 1177   # $setClosedMessage = "This homework set is closed.";
 1178   # if ($authz->hasPermissions($user, "view_answers")) {
 1179   #   $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded.";
 1180   # } else {
 1181   #   $setClosedMessage .= " Additional attempts will not be recorded.";
 1182   # }
 1183   #}
 1184   unless (defined( $pg->{state}->{state_summary_msg}) and $pg->{state}->{state_summary_msg}=~/\S/) {
 1185     my $notCountedMessage = ($problem->value) ? "" : $r->maketext("(This problem will not count towards your grade.)");
 1186     print CGI::p(join("",
 1187       $submitAnswers ? $scoreRecordedMessage . CGI::br() : "",
 1188       $r->maketext("You have attempted this problem [quant,_1,time,times].",$attempts), CGI::br(),
 1189       $submitAnswers ? $r->maketext("You received a score of [_1] for this attempt.",sprintf("%.0f%%", $pg->{result}->{score} * 100)) . CGI::br():'',
 1190       $problem->attempted
 1191         ? $r->maketext("Your overall recorded score is [_1].  [_2]",$lastScore,$notCountedMessage) . CGI::br()
 1192         : "",
 1193       $setClosed ? $setClosedMessage : $r->maketext("You have [negquant,_1,unlimited attempts,attempt,attempts] remaining.",$attemptsLeft)
 1194     ));
 1195   }else {
 1196     print CGI::p($pg->{state}->{state_summary_msg});
 1197   }
 1198 
 1199   return "";
 1200 }
 1201 
 1202 # output_misc subroutine
 1203 
 1204 # prints out other necessary elements
 1205 
 1206 sub output_misc{
 1207 
 1208   my $self = shift;
 1209   my $r = $self->r;
 1210   my $ce = $r->ce;
 1211   my $db = $r->db;
 1212   my $pg = $self->{pg};
 1213   my %will = %{ $self->{will} };
 1214   my $user = $r->param('user');
 1215 
 1216   print CGI::start_div();
 1217 
 1218   my $pgdebug = join(CGI::br(), @{$pg->{pgcore}->{flags}->{DEBUG_messages}} );
 1219   my $pgwarning = join(CGI::br(), @{$pg->{pgcore}->{flags}->{WARNING_messages}} );
 1220   my $pginternalerrors = join(CGI::br(),  @{$pg->{pgcore}->get_internal_debug_messages}   );
 1221   my $pgerrordiv = $pgdebug||$pgwarning||$pginternalerrors;  # is 1 if any of these are non-empty
 1222 
 1223   print CGI::p({style=>"color:red;"}, $r->maketext("Checking additional error messages")) if $pgerrordiv  ;
 1224   print CGI::p("pg debug<br/> $pgdebug"                   ) if $pgdebug ;
 1225   print CGI::p("pg warning<br/>$pgwarning"                ) if $pgwarning ;
 1226   print CGI::p("pg internal errors<br/> $pginternalerrors") if $pginternalerrors;
 1227   print CGI::end_div()                                      if $pgerrordiv ;
 1228 
 1229   # save state for viewOptions
 1230   print  CGI::hidden(
 1231          -name  => "showOldAnswers",
 1232          -value => $will{showOldAnswers}
 1233        ),
 1234 
 1235        CGI::hidden(
 1236          -name  => "displayMode",
 1237          -value => $self->{displayMode}
 1238        );
 1239   print( CGI::hidden(
 1240          -name    => 'editMode',
 1241          -value   => $self->{editMode},
 1242        )
 1243   ) if defined($self->{editMode}) and $self->{editMode} eq 'temporaryFile';
 1244 
 1245   # this is a security risk -- students can use this to find the source code for the problem
 1246 
 1247   my $permissionLevel = $db->getPermissionLevel($user)->permission;
 1248   my $professorPermissionLevel = $ce->{userRoles}->{professor};
 1249   print( CGI::hidden(
 1250           -name   => 'sourceFilePath',
 1251           -value  =>  $self->{problem}->{source_file}
 1252   ))  if defined($self->{problem}->{source_file}) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors
 1253 
 1254   print( CGI::hidden(
 1255           -name   => 'problemSeed',
 1256           -value  =>  $r->param("problemSeed")
 1257   ))  if defined($r->param("problemSeed")) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors
 1258 
 1259   return "";
 1260 }
 1261 
 1262 # output_summary subroutine
 1263 
 1264 # prints out the summary of the questions that the student has answered for the current problem, along with available information about correctness
 1265 
 1266 sub output_summary{
 1267 
 1268   my $self = shift;
 1269 
 1270   my $editMode = $self->{editMode};
 1271   my $problem = $self->{problem};
 1272   my $pg = $self->{pg};
 1273   my $submitAnswers = $self->{submitAnswers};
 1274   my %will = %{ $self->{will} };
 1275   my $checkAnswers = $self->{checkAnswers};
 1276   my $previewAnswers = $self->{previewAnswers};
 1277 
 1278   my $r = $self->r;
 1279 
 1280   my $authz = $r->authz;
 1281   my $user = $r->param('user');
 1282 
 1283   # attempt summary
 1284   #FIXME -- the following is a kludge:  if showPartialCorrectAnswers is negative don't show anything.
 1285   # until after the due date
 1286   # do I need to check $will{showCorrectAnswers} to make preflight work??
 1287   if (($pg->{flags}->{showPartialCorrectAnswers} >= 0 and $submitAnswers) ) {
 1288     # print this if user submitted answers OR requested correct answers
 1289 
 1290     print $self->attemptResults($pg, 1,
 1291       $will{showCorrectAnswers},
 1292       $pg->{flags}->{showPartialCorrectAnswers}, 1, 1);
 1293   } elsif ($checkAnswers) {
 1294     # print this if user previewed answers
 1295     print CGI::div({class=>'ResultsWithError'},$r->maketext("ANSWERS ONLY CHECKED -- ANSWERS NOT RECORDED")), CGI::br();
 1296     print $self->attemptResults($pg, 1, $will{showCorrectAnswers}, 1, 1, 1);
 1297       # show attempt answers
 1298       # show correct answers if asked
 1299       # show attempt results (correctness)
 1300       # show attempt previews
 1301   } elsif ($previewAnswers) {
 1302     # print this if user previewed answers
 1303     print CGI::div({class=>'ResultsWithError'},$r->maketext("PREVIEW ONLY -- ANSWERS NOT RECORDED")),CGI::br(),$self->attemptResults($pg, 1, 0, 0, 0, 1);
 1304       # show attempt answers
 1305       # don't show correct answers
 1306       # don't show attempt results (correctness)
 1307       # show attempt previews
 1308   }
 1309 
 1310   return "";
 1311 }
 1312 
 1313 # output_custom_edit_message
 1314 
 1315 # prints out a custom edit message
 1316 
 1317 sub output_custom_edit_message{
 1318   my $self = shift;
 1319   my $r = $self->r;
 1320   my $authz = $r->authz;
 1321   my $user = $r->param('user');
 1322   my $editMode = $self->{editMode};
 1323   my $problem = $self->{problem};
 1324 
 1325   # custom message for editor
 1326   if ($authz->hasPermissions($user, "modify_problem_sets") and defined $editMode) {
 1327     if ($editMode eq "temporaryFile") {
 1328       print CGI::p(CGI::div({class=>'temporaryFile'}, $r->maketext("Viewing temporary file: "), $problem->source_file));
 1329     } elsif ($editMode eq "savedFile") {
 1330       # taken care of in the initialization phase
 1331     }
 1332   }
 1333 
 1334   return "";
 1335 }
 1336 
 1337 
 1338 
 1339 
 1340 # output_past_answer_button
 1341 
 1342 # prints out the "Show Past Answers" button
 1343 
 1344 sub output_past_answer_button{
 1345   my $self = shift;
 1346   my $r = $self->r;
 1347   my $problem = $self->{problem};
 1348 
 1349   my $authz = $r->authz;
 1350   my $urlpath = $r->urlpath;
 1351   my $user = $r->param('user');
 1352 
 1353   my $courseName = $urlpath->arg("courseID");
 1354 
 1355   my $pastAnswersPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ShowAnswers", $r,
 1356     courseID => $courseName);
 1357   my $showPastAnswersURL = $self->systemLink($pastAnswersPage, authen => 0); # no authen info for form action
 1358 
 1359   # print answer inspection button
 1360   if ($authz->hasPermissions($user, "view_answers")) {
 1361     print "\n",
 1362       CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"WW_Info"),"\n",
 1363       $self->hidden_authen_fields,"\n",
 1364       CGI::hidden(-name => 'courseID',  -value=>$courseName), "\n",
 1365       CGI::hidden(-name => 'problemID', -value=>$problem->problem_id), "\n",
 1366       CGI::hidden(-name => 'setID',  -value=>$problem->set_id), "\n",
 1367       CGI::hidden(-name => 'studentUser',    -value=>$problem->user_id), "\n",
 1368       CGI::p( {-align=>"left"},
 1369         CGI::submit(-name => 'action',  -value=>$r->maketext("Show Past Answers"))
 1370       ), "\n",
 1371       CGI::endform();
 1372   }
 1373 
 1374   return "";
 1375 }
 1376 
 1377 # output_email_instructor subroutine
 1378 
 1379 # prints out the "Email Instructor" button
 1380 
 1381 sub output_email_instructor{
 1382   my $self = shift;
 1383   my $problem = $self->{problem};
 1384   my %will = %{ $self->{will} };
 1385   my $pg = $self->{pg};
 1386 
 1387   print $self->feedbackMacro(
 1388     module             => __PACKAGE__,
 1389     set                => $self->{set}->set_id,
 1390     problem            => $problem->problem_id,
 1391     displayMode        => $self->{displayMode},
 1392     showOldAnswers     => $will{showOldAnswers},
 1393     showCorrectAnswers => $will{showCorrectAnswers},
 1394     showHints          => $will{showHints},
 1395     showSolutions      => $will{showSolutions},
 1396     pg_object          => $pg,
 1397   );
 1398 
 1399   return "";
 1400 }
 1401 
 1402 # output_hidden_info subroutine
 1403 
 1404 # outputs the hidden fields required for the form
 1405 
 1406 sub output_hidden_info{
 1407   my $self = shift;
 1408   my $previewAnswers = $self->{previewAnswers};
 1409   my $checkAnswers   = $self->{checkAnswers};
 1410   my $showPartialCorrectAnswers = $self->{pg}->{flags}->{showPartialCorrectAnswers};
 1411   if($previewAnswers){  # never color previewed answers
 1412     return "";
 1413   }
 1414   elsif (   ($checkAnswers  )
 1415            or $showPartialCorrectAnswers )    { # color answers when partialCorrectAnswers is set
 1416                                                 # or when checkAnswers is submitted
 1417     if(defined $self->{correct_ids}){
 1418       my $correctRef = $self->{correct_ids};
 1419       my @correct = @$correctRef;
 1420       foreach(@correct){
 1421         print CGI::hidden(-name=>"correct_ids", -value=>$_."_val");
 1422       }
 1423     }
 1424     if(defined $self->{incorrect_ids}){
 1425       my $incorrectRef = $self->{incorrect_ids};
 1426       my @incorrect = @$incorrectRef;
 1427       foreach(@incorrect){
 1428         print CGI::hidden(-name=>"incorrect_ids", -value=>$_."_val");
 1429       }
 1430     }
 1431     return "";
 1432   } else {
 1433     return "";
 1434   }
 1435 }
 1436 
 1437 # output_JS subroutine
 1438 
 1439 # prints out the wz_tooltip.js script for the current site.
 1440 
 1441 sub output_wztooltip_JS{
 1442 
 1443   my $self = shift;
 1444   my $r = $self->r;
 1445   my $ce = $r->ce;
 1446 
 1447   my $site_url = $ce->{webworkURLs}->{htdocs};
 1448 
 1449   print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/wz_tooltip.js"}), CGI::end_script();
 1450   return "";
 1451 }
 1452 
 1453 # outputs all of the Javascript needed for this page.
 1454 # The main javascript needed here is color.js, which colors input fields based on whether or not
 1455 # they are correct when answers are submitted.  When a problem attempts results, it prints out hidden fields containing identification
 1456 # information for the fields that were correct and the fields that were incorrect.  color.js collects of the correct and incorrect fields into
 1457 # two arrays using the information gathered from the hidden fields, and then loops through and changes the styles so
 1458 # that the colors will show up correctly.
 1459 
 1460 sub output_JS{
 1461   my $self = shift;
 1462   my $r = $self->r;
 1463   my $ce = $r->ce;
 1464 
 1465   my $site_url = $ce->{webworkURLs}->{htdocs};
 1466 
 1467   # This file declares a function called addOnLoadEvent which allows multiple different scripts to add to a single onLoadEvent handler on a page.
 1468   print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/addOnLoadEvent.js"}), CGI::end_script();
 1469 
 1470   # This is a file which initializes the proper JAVA applets should they be needed for the current problem.
 1471   print CGI::start_script({type=>"tesxt/javascript", src=>"$site_url/js/java_init.js"}), CGI::end_script();
 1472 
 1473   # The color.js file, which uses javascript to color the input fields based on whether they are correct or incorrect.
 1474   print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/color.js"}), CGI::end_script();
 1475   return "";
 1476 }
 1477 
 1478 # Simply here to indicate to the template that this page has body part methods which can be called
 1479 
 1480 sub can_body_parts{
 1481   return "";
 1482 }
 1483 
 1484 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9