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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6885 - (view) (download) (as text)

1 : sh002i 449 ################################################################################
2 : sh002i 1663 # WeBWorK Online Homework Delivery System
3 : sh002i 5319 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/
4 : gage 6285 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Problem.pm,v 1.225 2010/05/28 21:29:48 gage Exp $
5 : sh002i 1663 #
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 : sh002i 449 ################################################################################
16 :    
17 : malsyned 353 package WeBWorK::ContentGenerator::Problem;
18 : gage 6885 use base qw(WeBWorK WeBWorK::ContentGenerator WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil);
19 : malsyned 396
20 : sh002i 455 =head1 NAME
21 : gage 1039
22 : sh002i 455 WeBWorK::ContentGenerator::Problem - Allow a student to interact with a problem.
23 :    
24 :     =cut
25 : sh002i 1536
26 : malsyned 396 use strict;
27 :     use warnings;
28 : gage 4235 #use CGI qw(-nosticky );
29 :     use WeBWorK::CGI;
30 : sh002i 919 use File::Path qw(rmtree);
31 : sh002i 3485 use WeBWorK::Debug;
32 : sh002i 455 use WeBWorK::Form;
33 :     use WeBWorK::PG;
34 : sh002i 1234 use WeBWorK::PG::ImageGenerator;
35 : sh002i 623 use WeBWorK::PG::IO;
36 : sh002i 4504 use WeBWorK::Utils qw(readFile writeLog writeCourseLog encodeAnswers decodeAnswers
37 :     ref2string makeTempDirectory path_is_subdir sortByName before after between);
38 : sh002i 4566 use WeBWorK::DB::Utils qw(global2user user2global);
39 : gage 3034 use URI::Escape;
40 : sh002i 1223
41 : jj 2415 use WeBWorK::Utils::Tasks qw(fake_set fake_problem);
42 : sh002i 1536
43 : sh002i 2505 ################################################################################
44 :     # CGI param interface to this module (up-to-date as of v1.153)
45 :     ################################################################################
46 :    
47 :     # Standard params:
48 : sh002i 449 #
49 : sh002i 2505 # user - user ID of real user
50 :     # key - session key
51 :     # effectiveUser - user ID of effective user
52 : sh002i 415 #
53 : sh002i 2505 # Integration with PGProblemEditor:
54 : sh002i 415 #
55 : sh002i 2505 # editMode - if set, indicates alternate problem source location.
56 :     # can be "temporaryFile" or "savedFile".
57 : sh002i 425 #
58 : sh002i 2505 # sourceFilePath - path to file to be edited
59 :     # problemSeed - force problem seed to value
60 :     # success - success message to display
61 :     # failure - failure message to display
62 : toenail 2398 #
63 : sh002i 2505 # Rendering options:
64 :     #
65 :     # displayMode - name of display mode to use
66 :     #
67 :     # showOldAnswers - request that last entered answer be shown (if allowed)
68 :     # showCorrectAnswers - request that correct answers be shown (if allowed)
69 :     # showHints - request that hints be shown (if allowed)
70 :     # showSolutions - request that solutions be shown (if allowed)
71 :     #
72 :     # Problem interaction:
73 :     #
74 :     # AnSwEr# - answer blanks in problem
75 :     #
76 :     # redisplay - name of the "Redisplay Problem" button
77 :     # submitAnswers - name of "Submit Answers" button
78 :     # checkAnswers - name of the "Check Answers" button
79 :     # previewAnswers - name of the "Preview Answers" button
80 : gage 392
81 : sh002i 2505 ################################################################################
82 :     # "can" methods
83 :     ################################################################################
84 : malsyned 1426
85 : sh002i 2505 # Subroutines to determine if a user "can" perform an action. Each subroutine is
86 :     # called with the following arguments:
87 :     #
88 : sh002i 2762 # ($self, $User, $EffectiveUser, $Set, $Problem)
89 : sh002i 2505
90 : glarose 3377 # 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 :    
94 : sh002i 2505 sub can_showOldAnswers {
95 : sh002i 2762 #my ($self, $User, $EffectiveUser, $Set, $Problem) = @_;
96 : sh002i 2505
97 :     return 1;
98 :     }
99 :    
100 :     sub can_showCorrectAnswers {
101 : sh002i 2762 my ($self, $User, $EffectiveUser, $Set, $Problem) = @_;
102 : sh002i 2505 my $authz = $self->r->authz;
103 :    
104 :     return
105 :     after($Set->answer_date)
106 :     ||
107 :     $authz->hasPermissions($User->user_id, "show_correct_answers_before_answer_date")
108 :     ;
109 :     }
110 :    
111 :     sub can_showHints {
112 : sh002i 2762 #my ($self, $User, $EffectiveUser, $Set, $Problem) = @_;
113 : sh002i 2505
114 :     return 1;
115 :     }
116 :    
117 :     sub can_showSolutions {
118 : sh002i 2762 my ($self, $User, $EffectiveUser, $Set, $Problem) = @_;
119 : sh002i 2505 my $authz = $self->r->authz;
120 :    
121 :     return
122 :     after($Set->answer_date)
123 :     ||
124 :     $authz->hasPermissions($User->user_id, "show_solutions_before_answer_date")
125 :     ;
126 :     }
127 :    
128 :     sub can_recordAnswers {
129 : sh002i 2762 my ($self, $User, $EffectiveUser, $Set, $Problem, $submitAnswers) = @_;
130 : sh002i 2505 my $authz = $self->r->authz;
131 : jj 2685 my $thisAttempt = $submitAnswers ? 1 : 0;
132 : sh002i 2505 if ($User->user_id ne $EffectiveUser->user_id) {
133 :     return $authz->hasPermissions($User->user_id, "record_answers_when_acting_as_student");
134 :     }
135 :     if (before($Set->open_date)) {
136 :     return $authz->hasPermissions($User->user_id, "record_answers_before_open_date");
137 :     } elsif (between($Set->open_date, $Set->due_date)) {
138 :     my $max_attempts = $Problem->max_attempts;
139 : jj 2685 my $attempts_used = $Problem->num_correct + $Problem->num_incorrect + $thisAttempt;
140 : sh002i 2505 if ($max_attempts == -1 or $attempts_used < $max_attempts) {
141 :     return $authz->hasPermissions($User->user_id, "record_answers_after_open_date_with_attempts");
142 :     } else {
143 :     return $authz->hasPermissions($User->user_id, "record_answers_after_open_date_without_attempts");
144 :     }
145 : sh002i 2512 } elsif (between($Set->due_date, $Set->answer_date)) {
146 : sh002i 2505 return $authz->hasPermissions($User->user_id, "record_answers_after_due_date");
147 : sh002i 2512 } elsif (after($Set->answer_date)) {
148 : sh002i 2505 return $authz->hasPermissions($User->user_id, "record_answers_after_answer_date");
149 :     }
150 :     }
151 :    
152 :     sub can_checkAnswers {
153 : sh002i 2762 my ($self, $User, $EffectiveUser, $Set, $Problem, $submitAnswers) = @_;
154 : sh002i 2505 my $authz = $self->r->authz;
155 : jj 2685 my $thisAttempt = $submitAnswers ? 1 : 0;
156 : sh002i 2505
157 :     if (before($Set->open_date)) {
158 :     return $authz->hasPermissions($User->user_id, "check_answers_before_open_date");
159 :     } elsif (between($Set->open_date, $Set->due_date)) {
160 :     my $max_attempts = $Problem->max_attempts;
161 : jj 2685 my $attempts_used = $Problem->num_correct + $Problem->num_incorrect + $thisAttempt;
162 : sh002i 2505 if ($max_attempts == -1 or $attempts_used < $max_attempts) {
163 :     return $authz->hasPermissions($User->user_id, "check_answers_after_open_date_with_attempts");
164 :     } else {
165 :     return $authz->hasPermissions($User->user_id, "check_answers_after_open_date_without_attempts");
166 :     }
167 : sh002i 2512 } elsif (between($Set->due_date, $Set->answer_date)) {
168 : sh002i 2505 return $authz->hasPermissions($User->user_id, "check_answers_after_due_date");
169 : sh002i 2512 } elsif (after($Set->answer_date)) {
170 : sh002i 2505 return $authz->hasPermissions($User->user_id, "check_answers_after_answer_date");
171 :     }
172 :     }
173 :    
174 : jj 3564 # Reset the default in some cases
175 :     sub set_showOldAnswers_default {
176 :     my ($self, $ce, $userName, $authz, $set) = @_;
177 : jj 3580 # 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 : jj 3564 # 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 : dpvc 3567 $ce->{pg}->{options}->{showOldAnswers} = 0 if $set->{due_date} && after($set->{due_date});
185 : jj 3564 }
186 :    
187 : sh002i 2505 ################################################################################
188 :     # output utilities
189 :     ################################################################################
190 :    
191 : glarose 3377 # Note: the substance of attemptResults is lifted into GatewayQuiz.pm,
192 :     # with some changes to the output format
193 :    
194 : sh002i 2505 sub attemptResults {
195 :     my $self = shift;
196 :     my $pg = shift;
197 :     my $showAttemptAnswers = shift;
198 :     my $showCorrectAnswers = shift;
199 :     my $showAttemptResults = $showAttemptAnswers && shift;
200 :     my $showSummary = shift;
201 :     my $showAttemptPreview = shift || 0;
202 :    
203 :     my $ce = $self->r->ce;
204 :    
205 : gage 6659 # for color coding the responses.
206 :     my @correct_ids = ();
207 :     my @incorrect_ids = ();
208 :    
209 :    
210 : sh002i 2505 my $problemResult = $pg->{result}; # the overall result of the problem
211 :     my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} };
212 : gage 6885
213 : sh002i 2505 my $showMessages = $showAttemptAnswers && grep { $pg->{answers}->{$_}->{ans_message} } @answerNames;
214 :    
215 :     my $basename = "equation-" . $self->{set}->psvn. "." . $self->{problem}->problem_id . "-preview";
216 :    
217 :     # to make grabbing these options easier, we'll pull them out now...
218 :     my %imagesModeOptions = %{$ce->{pg}->{displayModeOptions}->{images}};
219 :    
220 :     my $imgGen = WeBWorK::PG::ImageGenerator->new(
221 :     tempDir => $ce->{webworkDirs}->{tmp},
222 :     latex => $ce->{externalPrograms}->{latex},
223 :     dvipng => $ce->{externalPrograms}->{dvipng},
224 :     useCache => 1,
225 :     cacheDir => $ce->{webworkDirs}->{equationCache},
226 :     cacheURL => $ce->{webworkURLs}->{equationCache},
227 :     cacheDB => $ce->{webworkFiles}->{equationCacheDB},
228 :     dvipng_align => $imagesModeOptions{dvipng_align},
229 :     dvipng_depth_db => $imagesModeOptions{dvipng_depth_db},
230 :     );
231 :    
232 : gage 6842 my $showEvaluatedAnswers = $ce->{pg}->{options}->{showEvaluatedAnswers};
233 :    
234 : sh002i 2505 my $header;
235 :     #$header .= CGI::th("Part");
236 : gage 6842 if ($showEvaluatedAnswers) {
237 :     $header .= $showAttemptAnswers ? CGI::th("Entered") : "";
238 :     }
239 : sh002i 2505 $header .= $showAttemptPreview ? CGI::th("Answer Preview") : "";
240 :     $header .= $showCorrectAnswers ? CGI::th("Correct") : "";
241 :     $header .= $showAttemptResults ? CGI::th("Result") : "";
242 :     $header .= $showMessages ? CGI::th("Messages") : "";
243 : sh002i 2761 my $fully = '';
244 : sh002i 2505 my @tableRows = ( $header );
245 :     my $numCorrect = 0;
246 : gage 3391 my $numBlanks =0;
247 : sh002i 3356 my $tthPreambleCache;
248 : sh002i 2505 foreach my $name (@answerNames) {
249 :     my $answerResult = $pg->{answers}->{$name};
250 :     my $studentAnswer = $answerResult->{student_ans}; # original_student_ans
251 :     my $preview = ($showAttemptPreview
252 : sh002i 3356 ? $self->previewAnswer($answerResult, $imgGen, \$tthPreambleCache)
253 : sh002i 2505 : "");
254 : gage 6284 my $correctAnswerPreview = $self->previewCorrectAnswer($answerResult, $imgGen, \$tthPreambleCache);
255 : sh002i 2505 my $correctAnswer = $answerResult->{correct_ans};
256 :     my $answerScore = $answerResult->{score};
257 :     my $answerMessage = $showMessages ? $answerResult->{ans_message} : "";
258 : sh002i 2767 $answerMessage =~ s/\n/<BR>/g;
259 : sh002i 2761 $numCorrect += $answerScore >= 1;
260 : dpvc 3653 $numBlanks++ unless $studentAnswer =~/\S/ || $answerScore >= 1; # unless student answer contains entry
261 : gage 6659 my $resultString = $answerScore >= 1 ? CGI::span({class=>"ResultsWithoutError"}, "correct") :
262 : sh002i 2761 $answerScore > 0 ? int($answerScore*100)."% correct" :
263 : gage 6659 CGI::span({class=>"ResultsWithError"}, "incorrect");
264 : sh002i 2761 $fully = 'completely ' if $answerScore >0 and $answerScore < 1;
265 : sh002i 2505
266 : gage 6659 push @correct_ids, $name if $answerScore == 1;
267 :     push @incorrect_ids, $name if $answerScore < 1;
268 :    
269 : gage 6826 # need to capture auxiliary answers as well and identify their ids.
270 : sh002i 2505
271 : gage 6826
272 : sh002i 2505 my $row;
273 :     #$row .= CGI::td($name);
274 : gage 6842 if ($showEvaluatedAnswers) {
275 :     $row .= $showAttemptAnswers ? CGI::td($self->nbsp($studentAnswer)) : "";
276 :     }
277 : Mgage 6293 $row .= $showAttemptPreview ? CGI::td({onmouseover=>qq!Tip('$studentAnswer',SHADOW, true,
278 :     DELAY, 1000, FADEIN, 300, FADEOUT, 300, STICKY, 1, OFFSETX, -20, CLOSEBTN, true, CLICKCLOSE, false,
279 :     BGCOLOR, '#F4FF91', TITLE, 'Entered:',TITLEBGCOLOR, '#F4FF91', TITLEFONTCOLOR, '#000000')!},
280 :     $self->nbsp($preview)) : "";
281 :     $row .= $showCorrectAnswers ? CGI::td({onmouseover=> qq!Tip('$correctAnswer',SHADOW, true,
282 :     DELAY, 1000, FADEIN, 300, FADEOUT, 300, STICKY, 1, OFFSETX, -20, CLOSEBTN, true, CLICKCLOSE, false,
283 :     BGCOLOR, '#F4FF91', TITLE, 'Entered:',TITLEBGCOLOR, '#F4FF91', TITLEFONTCOLOR, '#000000')!},
284 :     $self->nbsp($correctAnswerPreview)) : "";
285 : sh002i 2505 $row .= $showAttemptResults ? CGI::td($self->nbsp($resultString)) : "";
286 : sh002i 2767 $row .= $showMessages ? CGI::td({-class=>"Message"},$self->nbsp($answerMessage)) : "";
287 : sh002i 2505 push @tableRows, $row;
288 :     }
289 :    
290 :     # render equation images
291 :     $imgGen->render(refresh => 1);
292 :    
293 :     # my $numIncorrectNoun = scalar @answerNames == 1 ? "question" : "questions";
294 :     my $scorePercent = sprintf("%.0f%%", $problemResult->{score} * 100);
295 :     # FIXME -- I left the old code in in case we have to back out.
296 :     # my $summary = "On this attempt, you answered $numCorrect out of "
297 :     # . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent.";
298 :     my $summary = "";
299 : gage 3391 unless (defined($problemResult->{summary}) and $problemResult->{summary} =~ /\S/) {
300 :     if (scalar @answerNames == 1) { #default messages
301 :     if ($numCorrect == scalar @answerNames) {
302 : dpvc 4158 $summary .= CGI::div({class=>"ResultsWithoutError"},"The answer above is correct.");
303 : gage 3391 } else {
304 : dpvc 4158 $summary .= CGI::div({class=>"ResultsWithError"},"The answer above is NOT ${fully}correct.");
305 : gage 3391 }
306 :     } else {
307 :     if ($numCorrect == scalar @answerNames) {
308 : dpvc 4158 $summary .= CGI::div({class=>"ResultsWithoutError"},"All of the answers above are correct.");
309 : gage 3391 }
310 : gage 6826 #unless ($numCorrect + $numBlanks == scalar( @answerNames)) { # this allowed you to figure out if you got one answer right.
311 :     elsif ($numBlanks != scalar( @answerNames)) {
312 : dpvc 4158 $summary .= CGI::div({class=>"ResultsWithError"},"At least one of the answers above is NOT ${fully}correct.");
313 : gage 3391 }
314 :     if ($numBlanks) {
315 :     my $s = ($numBlanks>1)?'':'s';
316 :     $summary .= CGI::div({class=>"ResultsAlert"},"$numBlanks of the questions remain$s unanswered.");
317 :     }
318 :     }
319 : sh002i 2505 } else {
320 : gage 3391 $summary = $problemResult->{summary}; # summary has been defined by grader
321 : sh002i 2505 }
322 : gage 6659
323 :     $self->{correct_ids}=[@correct_ids] if @correct_ids;
324 :     $self->{incorrect_ids} = [@incorrect_ids] if @incorrect_ids;
325 :    
326 : sh002i 2505 return
327 :     CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows))
328 : sh002i 3768 . ($showSummary ? CGI::p({class=>'attemptResultsSummary'},$summary) : "");
329 : sh002i 2505 }
330 :    
331 :    
332 : glarose 3377 # Note: previewAnswer is lifted into GatewayQuiz.pm
333 :    
334 : sh002i 2505 sub previewAnswer {
335 : sh002i 3356 my ($self, $answerResult, $imgGen, $tthPreambleCache) = @_;
336 : sh002i 2505 my $ce = $self->r->ce;
337 :     my $effectiveUser = $self->{effectiveUser};
338 :     my $set = $self->{set};
339 :     my $problem = $self->{problem};
340 :     my $displayMode = $self->{displayMode};
341 :    
342 :     # note: right now, we have to do things completely differently when we are
343 :     # rendering math from INSIDE the translator and from OUTSIDE the translator.
344 :     # so we'll just deal with each case explicitly here. there's some code
345 :     # duplication that can be dealt with later by abstracting out tth/dvipng/etc.
346 :    
347 :     my $tex = $answerResult->{preview_latex_string};
348 :    
349 :     return "" unless defined $tex and $tex ne "";
350 :    
351 :     if ($displayMode eq "plainText") {
352 :     return $tex;
353 :     } elsif ($displayMode eq "formattedText") {
354 : sh002i 3356
355 :     # read the TTH preamble, or use the cached copy passed in from the caller
356 : glarose 3570 my $tthPreamble='';
357 : sh002i 3356 if (defined $$tthPreambleCache) {
358 :     $tthPreamble = $$tthPreambleCache;
359 :     } else {
360 :     my $tthPreambleFile = $ce->{courseDirs}->{templates} . "/tthPreamble.tex";
361 :     if (-r $tthPreambleFile) {
362 :     $tthPreamble = readFile($tthPreambleFile);
363 :     # thanks to Jim Martino. each line in the definition file should end with
364 :     #a % to prevent adding supurious paragraphs to output:
365 :     $tthPreamble =~ s/(.)\n/$1%\n/g;
366 :     # solves the problem if the file doesn't end with a return:
367 :     $tthPreamble .="%\n";
368 :     # store preamble in cache:
369 :     $$tthPreambleCache = $tthPreamble;
370 :     } else {
371 :     }
372 :     }
373 :    
374 :     # construct TTH command line
375 : sh002i 2505 my $tthCommand = $ce->{externalPrograms}->{tth}
376 : dpvc 3362 . " -L -f5 -u -r 2> /dev/null <<END_OF_INPUT; echo > /dev/null\n"
377 :     . $tthPreamble . "\\[" . $tex . "\\]\n"
378 : sh002i 2505 . "END_OF_INPUT\n";
379 :    
380 :     # call tth
381 :     my $result = `$tthCommand`;
382 :     if ($?) {
383 :     return "<b>[tth failed: $? $@]</b>";
384 :     } else {
385 : dpvc 3362 # avoid border problems in tables and remove unneeded initial <br>
386 :     $result =~ s/(<table [^>]*)>/$1 CLASS="ArrayLayout">/gi;
387 :     $result =~ s!\s*<br clear="all" />!!;
388 : sh002i 2505 return $result;
389 :     }
390 : sh002i 3356
391 : sh002i 2505 } elsif ($displayMode eq "images") {
392 :     $imgGen->add($tex);
393 : gage 6425 } elsif ($displayMode eq "MathJax") {
394 :     return '<span class="MathJax_Preview">[math]</span><script type="math/tex; mode=display">'.$tex.'</script>';
395 : sh002i 2505 } elsif ($displayMode eq "jsMath") {
396 : gage 6425 $tex =~ s/&/&amp;/g; $tex =~ s/</&lt;/g; $tex =~ s/>/&gt;/g;
397 : dpvc 3337 return '<SPAN CLASS="math">\\displaystyle{'.$tex.'}</SPAN>';
398 : sh002i 2505 }
399 :     }
400 : gage 6284 sub previewCorrectAnswer {
401 :     my ($self, $answerResult, $imgGen, $tthPreambleCache) = @_;
402 :     my $ce = $self->r->ce;
403 :     my $effectiveUser = $self->{effectiveUser};
404 :     my $set = $self->{set};
405 :     my $problem = $self->{problem};
406 :     my $displayMode = $self->{displayMode};
407 :    
408 :     # note: right now, we have to do things completely differently when we are
409 :     # rendering math from INSIDE the translator and from OUTSIDE the translator.
410 :     # so we'll just deal with each case explicitly here. there's some code
411 :     # duplication that can be dealt with later by abstracting out tth/dvipng/etc.
412 :    
413 : gage 6295 my $tex = $answerResult->{correct_ans_latex_string};
414 : gage 6304 return $answerResult->{correct_ans} unless defined $tex and $tex=~/\S/; # some answers don't have latex strings defined
415 :     # return "" unless defined $tex and $tex ne "";
416 : gage 6284
417 :     if ($displayMode eq "plainText") {
418 :     return $tex;
419 :     } elsif ($displayMode eq "formattedText") {
420 :    
421 :     # read the TTH preamble, or use the cached copy passed in from the caller
422 :     my $tthPreamble='';
423 :     if (defined $$tthPreambleCache) {
424 :     $tthPreamble = $$tthPreambleCache;
425 :     } else {
426 :     my $tthPreambleFile = $ce->{courseDirs}->{templates} . "/tthPreamble.tex";
427 :     if (-r $tthPreambleFile) {
428 :     $tthPreamble = readFile($tthPreambleFile);
429 :     # thanks to Jim Martino. each line in the definition file should end with
430 :     #a % to prevent adding supurious paragraphs to output:
431 :     $tthPreamble =~ s/(.)\n/$1%\n/g;
432 :     # solves the problem if the file doesn't end with a return:
433 :     $tthPreamble .="%\n";
434 :     # store preamble in cache:
435 :     $$tthPreambleCache = $tthPreamble;
436 :     } else {
437 :     }
438 :     }
439 :    
440 :     # construct TTH command line
441 :     my $tthCommand = $ce->{externalPrograms}->{tth}
442 :     . " -L -f5 -u -r 2> /dev/null <<END_OF_INPUT; echo > /dev/null\n"
443 :     . $tthPreamble . "\\[" . $tex . "\\]\n"
444 :     . "END_OF_INPUT\n";
445 :    
446 :     # call tth
447 :     my $result = `$tthCommand`;
448 :     if ($?) {
449 :     return "<b>[tth failed: $? $@]</b>";
450 :     } else {
451 :     # avoid border problems in tables and remove unneeded initial <br>
452 :     $result =~ s/(<table [^>]*)>/$1 CLASS="ArrayLayout">/gi;
453 :     $result =~ s!\s*<br clear="all" />!!;
454 :     return $result;
455 :     }
456 :    
457 :     } elsif ($displayMode eq "images") {
458 :     $imgGen->add($tex);
459 : gage 6425 } elsif ($displayMode eq "MathJax") {
460 :     return '<span class="MathJax_Preview">[math]</span><script type="math/tex; mode=display">'.$tex.'</script>';
461 : gage 6284 } elsif ($displayMode eq "jsMath") {
462 : gage 6425 $tex =~ s/&/&amp;/g; $tex =~ s/</&lt;/g; $tex =~ s/>/&gt;/g;
463 : gage 6284 return '<SPAN CLASS="math">\\displaystyle{'.$tex.'}</SPAN>';
464 :     }
465 :     }
466 : sh002i 2505
467 :     ################################################################################
468 :     # Template escape implementations
469 :     ################################################################################
470 :    
471 : sh002i 476 sub pre_header_initialize {
472 : sh002i 1841 my ($self) = @_;
473 : sh002i 1908 my $r = $self->r;
474 :     my $ce = $r->ce;
475 :     my $db = $r->db;
476 : toenail 2349 my $authz = $r->authz;
477 : sh002i 1908 my $urlpath = $r->urlpath;
478 :    
479 :     my $setName = $urlpath->arg("setID");
480 : sh002i 1841 my $problemNumber = $r->urlpath->arg("problemID");
481 : sh002i 1908 my $userName = $r->param('user');
482 :     my $effectiveUserName = $r->param('effectiveUser');
483 :     my $key = $r->param('key');
484 : sh002i 3816 my $editMode = $r->param("editMode");
485 : gage 1007
486 : sh002i 1636 my $user = $db->getUser($userName); # checked
487 :     die "record for user $userName (real user) does not exist."
488 :     unless defined $user;
489 :    
490 :     my $effectiveUser = $db->getUser($effectiveUserName); # checked
491 :     die "record for user $effectiveUserName (effective user) does not exist."
492 :     unless defined $effectiveUser;
493 :    
494 : sh002i 1197 # obtain the merged set for $effectiveUser
495 : sh002i 1636 my $set = $db->getMergedSet($effectiveUserName, $setName); # checked
496 : sh002i 2738
497 : jj 3564 $self->set_showOldAnswers_default($ce, $userName, $authz, $set);
498 :    
499 : gage 6655 # Database fix (in case of undefined visiblity state values)
500 :     # this is only necessary because some people keep holding to ww1.9 which did not have a visible field
501 :     # make sure visible is set to 0 or 1
502 :     if ( $set and $set->visible ne "0" and $set->visible ne "1") {
503 : sh002i 2738 my $globalSet = $db->getGlobalSet($set->set_id);
504 : gage 6655 $globalSet->visible("1"); # defaults to visible
505 : sh002i 2738 $db->putGlobalSet($globalSet);
506 :     $set = $db->getMergedSet($effectiveUserName, $setName);
507 :     } else {
508 :     # don't do anything just yet, maybe we're a professor and we're
509 :     # fabricating a set or haven't assigned it to ourselves just yet
510 :     }
511 : apizer 6158 # When a set is created enable_reduced_scoring is null, so we have to set it
512 :     if ( $set and $set->enable_reduced_scoring ne "0" and $set->enable_reduced_scoring ne "1") {
513 :     my $globalSet = $db->getGlobalSet($set->set_id);
514 :     $globalSet->enable_reduced_scoring("0"); # defaults to disabled
515 :     $db->putGlobalSet($globalSet);
516 :     $set = $db->getMergedSet($effectiveUserName, $setName);
517 :     }
518 : gage 992
519 : apizer 6158
520 : sh002i 1197 # obtain the merged problem for $effectiveUser
521 : sh002i 1636 my $problem = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked
522 : toenail 2097
523 : toenail 2349 if ($authz->hasPermissions($userName, "modify_problem_sets")) {
524 : sh002i 1197 # professors are allowed to fabricate sets and problems not
525 :     # assigned to them (or anyone). this allows them to use the
526 :     # editor to
527 : sh002i 2738
528 :     # if a User Set does not exist for this user and this set
529 :     # then we check the Global Set
530 :     # if that does not exist we create a fake set
531 :     # if it does, we add fake user data
532 : sh002i 1197 unless (defined $set) {
533 :     my $userSetClass = $db->{set_user}->{record};
534 : sh002i 1636 my $globalSet = $db->getGlobalSet($setName); # checked
535 : sh002i 2738
536 :     if (not defined $globalSet) {
537 : jj 2020 $set = fake_set($db);
538 :     } else {
539 :     $set = global2user($userSetClass, $globalSet);
540 :     $set->psvn(0);
541 :     }
542 : sh002i 1197 }
543 :    
544 :     # if that is not yet defined obtain the global problem,
545 :     # convert it to a user problem, and add fake user data
546 :     unless (defined $problem) {
547 :     my $userProblemClass = $db->{problem_user}->{record};
548 : sh002i 1636 my $globalProblem = $db->getGlobalProblem($setName, $problemNumber); # checked
549 :     # if the global problem doesn't exist either, bail!
550 : jj 2020 if(not defined $globalProblem) {
551 :     my $sourceFilePath = $r->param("sourceFilePath");
552 : sh002i 4051 die "sourceFilePath is unsafe!" unless path_is_subdir($sourceFilePath, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
553 : jj 2421 # These are problems from setmaker. If declared invalid, they won't come up
554 : jj 2422 $self->{invalidProblem} = $self->{invalidSet} = 1 unless defined $sourceFilePath;
555 : toenail 2398 # die "Problem $problemNumber in set $setName does not exist" unless defined $sourceFilePath;
556 : jj 2020 $problem = fake_problem($db);
557 :     $problem->problem_id(1);
558 :     $problem->source_file($sourceFilePath);
559 :     $problem->user_id($effectiveUserName);
560 :     } else {
561 :     $problem = global2user($userProblemClass, $globalProblem);
562 :     $problem->user_id($effectiveUserName);
563 :     $problem->problem_seed(0);
564 :     $problem->status(0);
565 :     $problem->attempted(0);
566 :     $problem->last_answer("");
567 :     $problem->num_correct(0);
568 :     $problem->num_incorrect(0);
569 :     }
570 : sh002i 1197 }
571 :    
572 :     # now we're sure we have valid UserSet and UserProblem objects
573 :     # yay!
574 :    
575 :     # now deal with possible editor overrides:
576 :    
577 :     # if the caller is asking to override the source file, and
578 :     # editMode calls for a temporary file, do so
579 :     my $sourceFilePath = $r->param("sourceFilePath");
580 : sh002i 4015 if (defined $editMode and $editMode eq "temporaryFile" and defined $sourceFilePath) {
581 : sh002i 4051 die "sourceFilePath is unsafe!" unless path_is_subdir($sourceFilePath, $ce->{courseDirs}->{templates}, 1); # 1==path can be relative to dir
582 : sh002i 1197 $problem->source_file($sourceFilePath);
583 :     }
584 :    
585 : toenail 2398 # if the problem does not have a source file or no source file has been passed in
586 :     # then this is really an invalid problem (probably from a bad URL)
587 :     $self->{invalidProblem} = not (defined $sourceFilePath or $problem->source_file);
588 :    
589 : sh002i 1197 # if the caller is asking to override the problem seed, do so
590 :     my $problemSeed = $r->param("problemSeed");
591 :     if (defined $problemSeed) {
592 :     $problem->problem_seed($problemSeed);
593 :     }
594 : toenail 2097
595 : gage 6655 my $visiblityStateClass = ($set->visible) ? "visible" : "hidden";
596 :     my $visiblityStateText = ($set->visible) ? "visible to students." : "hidden from students.";
597 :     $self->addmessage(CGI::span("This set is " . CGI::font({class=>$visiblityStateClass}, $visiblityStateText)));
598 : glarose 3377
599 : glarose 4909 # test for additional problem validity if it's not already invalid
600 : glarose 3377 } else {
601 : gage 6655 $self->{invalidProblem} = !(defined $problem and ($set->visible || $authz->hasPermissions($userName, "view_hidden_sets")));
602 : sh002i 2738
603 : toenail 2398 $self->addbadmessage(CGI::p("This problem will not count towards your grade.")) if $problem and not $problem->value and not $self->{invalidProblem};
604 : sh002i 1197 }
605 : toenail 2398
606 : sh002i 1197 $self->{userName} = $userName;
607 :     $self->{effectiveUserName} = $effectiveUserName;
608 :     $self->{user} = $user;
609 :     $self->{effectiveUser} = $effectiveUser;
610 :     $self->{set} = $set;
611 :     $self->{problem} = $problem;
612 :     $self->{editMode} = $editMode;
613 : sh002i 429
614 :     ##### form processing #####
615 :    
616 : sh002i 425 # set options from form fields (see comment at top of file for names)
617 : sh002i 1908 my $displayMode = $r->param("displayMode") || $ce->{pg}->{options}->{displayMode};
618 : sh002i 425 my $redisplay = $r->param("redisplay");
619 : sh002i 429 my $submitAnswers = $r->param("submitAnswers");
620 : sh002i 719 my $checkAnswers = $r->param("checkAnswers");
621 : sh002i 623 my $previewAnswers = $r->param("previewAnswers");
622 : gage 388
623 : sh002i 449 my $formFields = { WeBWorK::Form->new_from_paramable($r)->Vars };
624 :    
625 : sh002i 738 $self->{displayMode} = $displayMode;
626 :     $self->{redisplay} = $redisplay;
627 :     $self->{submitAnswers} = $submitAnswers;
628 :     $self->{checkAnswers} = $checkAnswers;
629 :     $self->{previewAnswers} = $previewAnswers;
630 :     $self->{formFields} = $formFields;
631 : toenail 2011
632 : toenail 2093 # get result and send to message
633 : gage 3034 my $status_message = $r->param("status_message");
634 : gage 3055 $self->addmessage(CGI::p("$status_message")) if $status_message;
635 : toenail 2093
636 : toenail 2011 # now that we've set all the necessary variables quit out if the set or problem is invalid
637 :     return if $self->{invalidSet} || $self->{invalidProblem};
638 : sh002i 738
639 : sh002i 449 ##### permissions #####
640 : glarose 4909
641 : sh002i 449 # what does the user want to do?
642 : gage 2998 #FIXME There is a problem with checkboxes -- if they are not checked they are invisible. Hence if the default mode in $ce is 1
643 :     # there is no way to override this. Probably this is ok for the last three options, but it was definitely not ok for showing
644 :     # saved answers which is normally on, but you want to be able to turn it off! This section should be moved to ContentGenerator
645 :     # so that you can set these options anywhere. We also need mechanisms for making them sticky.
646 : jj 3564 # Note: ProblemSet and ProblemSets might set showOldAnswers to '', which
647 :     # needs to be treated as if it is not set.
648 : sh002i 431 my %want = (
649 : jj 3564 showOldAnswers => (defined($r->param("showOldAnswers")) and $r->param("showOldAnswers") ne '') ? $r->param("showOldAnswers") : $ce->{pg}->{options}->{showOldAnswers},
650 : sh002i 1908 showCorrectAnswers => $r->param("showCorrectAnswers") || $ce->{pg}->{options}->{showCorrectAnswers},
651 :     showHints => $r->param("showHints") || $ce->{pg}->{options}->{showHints},
652 :     showSolutions => $r->param("showSolutions") || $ce->{pg}->{options}->{showSolutions},
653 : sh002i 719 recordAnswers => $submitAnswers,
654 : sh002i 747 checkAnswers => $checkAnswers,
655 : jj 2685 getSubmitButton => 1,
656 : sh002i 431 );
657 : sh002i 429
658 : sh002i 431 # are certain options enforced?
659 :     my %must = (
660 :     showOldAnswers => 0,
661 :     showCorrectAnswers => 0,
662 :     showHints => 0,
663 :     showSolutions => 0,
664 : sh002i 2505 recordAnswers => ! $authz->hasPermissions($userName, "avoid_recording_answers"),
665 : sh002i 747 checkAnswers => 0,
666 : jj 2685 getSubmitButton => 0,
667 : sh002i 431 );
668 : gage 3000
669 : sh002i 429 # does the user have permission to use certain options?
670 : sh002i 2762 my @args = ($user, $effectiveUser, $set, $problem);
671 : sh002i 431 my %can = (
672 : sh002i 2505 showOldAnswers => $self->can_showOldAnswers(@args),
673 :     showCorrectAnswers => $self->can_showCorrectAnswers(@args),
674 :     showHints => $self->can_showHints(@args),
675 :     showSolutions => $self->can_showSolutions(@args),
676 : jj 2685 recordAnswers => $self->can_recordAnswers(@args, 0),
677 :     checkAnswers => $self->can_checkAnswers(@args, $submitAnswers),
678 :     getSubmitButton => $self->can_recordAnswers(@args, $submitAnswers),
679 : sh002i 431 );
680 : sh002i 1908
681 : sh002i 429 # final values for options
682 : sh002i 431 my %will;
683 : sh002i 617 foreach (keys %must) {
684 : sh002i 431 $will{$_} = $can{$_} && ($want{$_} || $must{$_});
685 : gage 5636 #warn "final values for options $_ is can $can{$_}, want $want{$_}, must $must{$_}, will $will{$_}";
686 : sh002i 431 }
687 : sh002i 429
688 :     ##### sticky answers #####
689 :    
690 : sh002i 1234 if (not ($submitAnswers or $previewAnswers or $checkAnswers) and $will{showOldAnswers}) {
691 : sh002i 431 # do this only if new answers are NOT being submitted
692 : sh002i 429 my %oldAnswers = decodeAnswers($problem->last_answer);
693 :     $formFields->{$_} = $oldAnswers{$_} foreach keys %oldAnswers;
694 :     }
695 :    
696 :     ##### translation #####
697 : gage 1202
698 : sh002i 3485 debug("begin pg processing");
699 : sh002i 424 my $pg = WeBWorK::PG->new(
700 : sh002i 1908 $ce,
701 : malsyned 704 $effectiveUser,
702 : gage 1038 $key,
703 : sh002i 502 $set,
704 :     $problem,
705 : sh002i 1197 $set->psvn, # FIXME: this field should be removed
706 : sh002i 502 $formFields,
707 : sh002i 424 { # translation options
708 : sh002i 434 displayMode => $displayMode,
709 :     showHints => $will{showHints},
710 :     showSolutions => $will{showSolutions},
711 :     refreshMath2img => $will{showHints} || $will{showSolutions},
712 : dpvc 5314 processAnswers => 1,
713 : gage 5715 permissionLevel => $db->getPermissionLevel($userName)->permission,
714 : gage 6086 effectivePermissionLevel => $db->getPermissionLevel($effectiveUserName)->permission,
715 : sh002i 424 },
716 :     );
717 : gage 5715
718 : sh002i 3485 debug("end pg processing");
719 : sh002i 2505
720 : sh002i 684 ##### fix hint/solution options #####
721 :    
722 : gage 1582 $can{showHints} &&= $pg->{flags}->{hintExists}
723 :     &&= $pg->{flags}->{showHintLimit}<=$pg->{state}->{num_of_incorrect_ans};
724 : sh002i 684 $can{showSolutions} &&= $pg->{flags}->{solutionExists};
725 :    
726 : sh002i 449 ##### store fields #####
727 :    
728 :     $self->{want} = \%want;
729 :     $self->{must} = \%must;
730 :     $self->{can} = \%can;
731 :     $self->{will} = \%will;
732 :     $self->{pg} = $pg;
733 :     }
734 :    
735 : sh002i 558 sub if_errors($$) {
736 :     my ($self, $arg) = @_;
737 : sh002i 1908
738 :     if ($self->{isOpen}) {
739 :     return $self->{pg}->{flags}->{error_flag} ? $arg : !$arg;
740 :     } else {
741 :     return !$arg;
742 :     }
743 : sh002i 558 }
744 :    
745 : sh002i 562 sub head {
746 : sh002i 1908 my ($self) = @_;
747 : glarose 4909
748 :     return "" if ( $self->{invalidSet} );
749 : sh002i 555 return $self->{pg}->{head_text} if $self->{pg}->{head_text};
750 :     }
751 : sh002i 476
752 : sh002i 3481 sub options {
753 :     my ($self) = @_;
754 :     #warn "doing options in Problem";
755 :    
756 :     # don't show options if we don't have anything to show
757 : sh002i 3798 return "" if $self->{invalidSet} or $self->{invalidProblem};
758 : sh002i 3481
759 :     my $displayMode = $self->{displayMode};
760 :     my %can = %{ $self->{can} };
761 :    
762 :     my @options_to_show = "displayMode";
763 :     push @options_to_show, "showOldAnswers" if $can{showOldAnswers};
764 :     push @options_to_show, "showHints" if $can{showHints};
765 :     push @options_to_show, "showSolutions" if $can{showSolutions};
766 :    
767 :     return $self->optionsMacro(
768 :     options_to_show => \@options_to_show,
769 :     extra_params => ["editMode", "sourceFilePath"],
770 :     );
771 :     }
772 : sh002i 1131
773 : sh002i 476 sub siblings {
774 : sh002i 1908 my ($self) = @_;
775 :     my $r = $self->r;
776 :     my $db = $r->db;
777 :     my $urlpath = $r->urlpath;
778 : sh002i 476
779 : toenail 2011 # can't show sibling problems if the set is invalid
780 :     return "" if $self->{invalidSet};
781 :    
782 : sh002i 1908 my $courseID = $urlpath->arg("courseID");
783 :     my $setID = $self->{set}->set_id;
784 :     my $eUserID = $r->param("effectiveUser");
785 :     my @problemIDs = sort { $a <=> $b } $db->listUserProblems($eUserID, $setID);
786 : sh002i 526
787 : sh002i 3963 print CGI::start_div({class=>"info-box", id=>"fisheye"});
788 :     print CGI::h2("Problems");
789 : sh002i 3836 #print CGI::start_ul({class=>"LinksMenu"});
790 :     #print CGI::start_li();
791 :     #print CGI::span({style=>"font-size:larger"}, "Problems");
792 : sh002i 1908 print CGI::start_ul();
793 : toenail 2211
794 : sh002i 1908 foreach my $problemID (@problemIDs) {
795 :     my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem",
796 :     courseID => $courseID, setID => $setID, problemID => $problemID);
797 : gage 2998 print CGI::li(CGI::a( {href=>$self->systemLink($problemPage,
798 :     params=>{ displayMode => $self->{displayMode},
799 : gage 3000 showOldAnswers => $self->{will}->{showOldAnswers}
800 : gage 2998 })}, "Problem $problemID")
801 :     );
802 : sh002i 476 }
803 : dpvc 5966
804 : sh002i 1908 print CGI::end_ul();
805 : sh002i 3836 #print CGI::end_li();
806 :     #print CGI::end_ul();
807 : sh002i 3963 print CGI::end_div();
808 : dpvc 5966
809 : sh002i 1223 return "";
810 : sh002i 476 }
811 :    
812 :     sub nav {
813 : sh002i 1908 my ($self, $args) = @_;
814 :     my $r = $self->r;
815 :     my $db = $r->db;
816 :     my $urlpath = $r->urlpath;
817 : dpvc 5966
818 : glarose 4909 return "" if ( $self->{invalidSet} );
819 :    
820 : sh002i 1908 my $courseID = $urlpath->arg("courseID");
821 : toenail 2011 my $setID = $self->{set}->set_id if !($self->{invalidSet});
822 :     my $problemID = $self->{problem}->problem_id if !($self->{invalidProblem});
823 : sh002i 1908 my $eUserID = $r->param("effectiveUser");
824 : dpvc 5966
825 : sh002i 1908 my ($prevID, $nextID);
826 : toenail 2011
827 : dpvc 5966 if (!$self->{invalidProblem}) {
828 : toenail 2011 my @problemIDs = $db->listUserProblems($eUserID, $setID);
829 :     foreach my $id (@problemIDs) {
830 :     $prevID = $id if $id < $problemID
831 :     and (not defined $prevID or $id > $prevID);
832 :     $nextID = $id if $id > $problemID
833 :     and (not defined $nextID or $id < $nextID);
834 :     }
835 : sh002i 1223 }
836 : dpvc 5966
837 : sh002i 1908 my @links;
838 : dpvc 5966
839 : sh002i 1908 if ($prevID) {
840 :     my $prevPage = $urlpath->newFromModule(__PACKAGE__,
841 :     courseID => $courseID, setID => $setID, problemID => $prevID);
842 :     push @links, "Previous Problem", $r->location . $prevPage->path, "navPrev";
843 :     } else {
844 : dpvc 5605 push @links, "Previous Problem", "", "navPrevGrey";
845 : sh002i 1908 }
846 : dpvc 5966
847 :     if (defined($setID) && $setID ne 'Undefined_Set') {
848 :     push @links, "Problem List", $r->location . $urlpath->parent->path, "navProbList";
849 :     } else {
850 :     push @links, "Problem List", "", "navProbListGrey";
851 :     }
852 :    
853 : sh002i 1908 if ($nextID) {
854 :     my $nextPage = $urlpath->newFromModule(__PACKAGE__,
855 :     courseID => $courseID, setID => $setID, problemID => $nextID);
856 :     push @links, "Next Problem", $r->location . $nextPage->path, "navNext";
857 :     } else {
858 : dpvc 5605 push @links, "Next Problem", "", "navNextGrey";
859 : sh002i 1908 }
860 : dpvc 5966
861 : sh002i 3798 my $tail = "";
862 : dpvc 5966
863 : sh002i 3798 $tail .= "&displayMode=".$self->{displayMode} if defined $self->{displayMode};
864 :     $tail .= "&showOldAnswers=".$self->{will}->{showOldAnswers}
865 :     if defined $self->{will}->{showOldAnswers};
866 : sh002i 1908 return $self->navMacro($args, $tail, @links);
867 : sh002i 476 }
868 :    
869 : sh002i 449 sub title {
870 : sh002i 1908 my ($self) = @_;
871 : toenail 2011
872 :     # using the url arguments won't break if the set/problem are invalid
873 : jj 3533 my $setID = WeBWorK::ContentGenerator::underscore2nbsp($self->r->urlpath->arg("setID"));
874 : toenail 2011 my $problemID = $self->r->urlpath->arg("problemID");
875 : dpvc 5966
876 : sh002i 2767 return "$setID: Problem $problemID";
877 : sh002i 449 }
878 :    
879 : gage 6885
880 :     # now altered to outsource most output operations to the template, main functions now are simply error checking and answer processing - ghe3
881 :     # sub body {
882 :     # my $self = shift;
883 :     # my $r = $self->r;
884 :     # my $ce = $r->ce;
885 :     # my $db = $r->db;
886 :     # my $authz = $r->authz;
887 :     # my $urlpath = $r->urlpath;
888 :     # my $user = $r->param('user');
889 :     # my $effectiveUser = $r->param('effectiveUser');
890 :     # if ( $self->{invalidSet} ) {
891 :     # return CGI::div({class=>"ResultsWithError"},
892 :     # CGI::p("The selected problem set (" .
893 :     # $urlpath->arg("setID") . ") is not " .
894 :     # "a valid set for $effectiveUser:"),
895 :     # CGI::p($self->{invalidSet}));
896 :     # }
897 :     #
898 :     # if ($self->{invalidProblem}) {
899 :     # return CGI::div({class=>"ResultsWithError"},
900 :     # CGI::p("The selected problem (" . $urlpath->arg("problemID") . ") is not a valid problem for set " . $self->{set}->set_id . "."));
901 :     # }
902 :     #
903 :     # # unpack some useful variables
904 :     # my $set = $self->{set};
905 :     # my $problem = $self->{problem};
906 :     # my $editMode = $self->{editMode};
907 :     # my $submitAnswers = $self->{submitAnswers};
908 :     # my $checkAnswers = $self->{checkAnswers};
909 :     # my $previewAnswers = $self->{previewAnswers};
910 :     # my %want = %{ $self->{want} };
911 :     # my %can = %{ $self->{can} };
912 :     # my %must = %{ $self->{must} };
913 :     # my %will = %{ $self->{will} };
914 :     # my $pg = $self->{pg};
915 :     #
916 :     # my $courseName = $urlpath->arg("courseID");
917 :     #
918 :     # # FIXME: move editor link to top, next to problem number.
919 :     # # format as "[edit]" like we're doing with course info file, etc.
920 :     # # add edit link for set as well.
921 :     # my $editorLink = "";
922 :     # # if we are here without a real homework set, carry that through
923 :     # my $forced_field = [];
924 :     # $forced_field = ['sourceFilePath' => $r->param("sourceFilePath")] if
925 :     # ($set->set_id eq 'Undefined_Set');
926 :     # if ($authz->hasPermissions($user, "modify_problem_sets")) {
927 :     # my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor",
928 :     # courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id);
929 :     # my $editorURL = $self->systemLink($editorPage, params=>$forced_field);
930 :     # $editorLink = CGI::p(CGI::a({href=>$editorURL,target =>'WW_Editor'}, "Edit this problem"));
931 :     # }
932 :     #
933 :     # ##### translation errors? #####
934 :     #
935 :     # if ($pg->{flags}->{error_flag}) {
936 :     # if ($authz->hasPermissions($user, "view_problem_debugging_info")) {
937 :     # print $self->errorOutput($pg->{errors}, $pg->{body_text});
938 :     # } else {
939 :     # print $self->errorOutput($pg->{errors}, "You do not have permission to view the details of this error.");
940 :     # }
941 :     # print $editorLink;
942 :     # return "";
943 :     # }
944 :     #
945 :     # ##### answer processing #####
946 :     # debug("begin answer processing");
947 :     # # if answers were submitted:
948 :     # my $scoreRecordedMessage;
949 :     # my $pureProblem;
950 :     # if ($submitAnswers) {
951 :     # # get a "pure" (unmerged) UserProblem to modify
952 :     # # this will be undefined if the problem has not been assigned to this user
953 :     # $pureProblem = $db->getUserProblem($problem->user_id, $problem->set_id, $problem->problem_id); # checked
954 :     # if (defined $pureProblem) {
955 :     # # store answers in DB for sticky answers
956 :     # my %answersToStore;
957 :     # my %answerHash = %{ $pg->{answers} };
958 :     # $answersToStore{$_} = $self->{formFields}->{$_} #$answerHash{$_}->{original_student_ans} -- this may have been modified for fields with multiple values. Don't use it!!
959 :     # foreach (keys %answerHash);
960 :     #
961 :     # # There may be some more answers to store -- one which are auxiliary entries to a primary answer. Evaluating
962 :     # # matrices works in this way, only the first answer triggers an answer evaluator, the rest are just inputs
963 :     # # however we need to store them. Fortunately they are still in the input form.
964 :     # my @extra_answer_names = @{ $pg->{flags}->{KEPT_EXTRA_ANSWERS}};
965 :     # $answersToStore{$_} = $self->{formFields}->{$_} foreach (@extra_answer_names);
966 :     #
967 :     # # Now let's encode these answers to store them -- append the extra answers to the end of answer entry order
968 :     # my @answer_order = (@{$pg->{flags}->{ANSWER_ENTRY_ORDER}}, @extra_answer_names);
969 :     # my $answerString = encodeAnswers(%answersToStore,
970 :     # @answer_order);
971 :     #
972 :     # # store last answer to database
973 :     # $problem->last_answer($answerString);
974 :     # $pureProblem->last_answer($answerString);
975 :     # $db->putUserProblem($pureProblem);
976 :     #
977 :     # # store state in DB if it makes sense
978 :     # if ($will{recordAnswers}) {
979 :     # $problem->status($pg->{state}->{recorded_score});
980 :     # $problem->sub_status($pg->{state}->{sub_recorded_score});
981 :     # $problem->attempted(1);
982 :     # $problem->num_correct($pg->{state}->{num_of_correct_ans});
983 :     # $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans});
984 :     # $pureProblem->status($pg->{state}->{recorded_score});
985 :     # $pureProblem->sub_status($pg->{state}->{sub_recorded_score});
986 :     # $pureProblem->attempted(1);
987 :     # $pureProblem->num_correct($pg->{state}->{num_of_correct_ans});
988 :     # $pureProblem->num_incorrect($pg->{state}->{num_of_incorrect_ans});
989 :     # if ($db->putUserProblem($pureProblem)) {
990 :     # $scoreRecordedMessage = "Your score was recorded.";
991 :     # } else {
992 :     # $scoreRecordedMessage = "Your score was not recorded because there was a failure in storing the problem record to the database.";
993 :     # }
994 :     # # write to the transaction log, just to make sure
995 :     # writeLog($self->{ce}, "transaction",
996 :     # $problem->problem_id."\t".
997 :     # $problem->set_id."\t".
998 :     # $problem->user_id."\t".
999 :     # $problem->source_file."\t".
1000 :     # $problem->value."\t".
1001 :     # $problem->max_attempts."\t".
1002 :     # $problem->problem_seed."\t".
1003 :     # $pureProblem->status."\t".
1004 :     # $pureProblem->attempted."\t".
1005 :     # $pureProblem->last_answer."\t".
1006 :     # $pureProblem->num_correct."\t".
1007 :     # $pureProblem->num_incorrect
1008 :     # );
1009 :     # } else {
1010 :     # if (before($set->open_date) or after($set->due_date)) {
1011 :     # $scoreRecordedMessage = "Your score was not recorded because this homework set is closed.";
1012 :     # } else {
1013 :     # $scoreRecordedMessage = "Your score was not recorded.";
1014 :     # }
1015 :     # }
1016 :     # } else {
1017 :     # $scoreRecordedMessage = "Your score was not recorded because this problem has not been assigned to you.";
1018 :     # }
1019 :     # }
1020 :     #
1021 :     # # logging student answers
1022 :     #
1023 :     # my $answer_log = $self->{ce}->{courseFiles}->{logs}->{'answer_log'};
1024 :     # if ( defined($answer_log ) and defined($pureProblem)) {
1025 :     # if ($submitAnswers && !$authz->hasPermissions($effectiveUser, "dont_log_past_answers")) {
1026 :     # my $answerString = ""; my $scores = "";
1027 :     # my %answerHash = %{ $pg->{answers} };
1028 :     # # FIXME this is the line 552 error. make sure original student ans is defined.
1029 :     # # The fact that it is not defined is probably due to an error in some answer evaluator.
1030 :     # # But I think it is useful to suppress this error message in the log.
1031 :     # foreach (sortByName(undef, keys %answerHash)) {
1032 :     # my $orig_ans = $answerHash{$_}->{original_student_ans};
1033 :     # my $student_ans = defined $orig_ans ? $orig_ans : '';
1034 :     # $answerString .= $student_ans."\t";
1035 :     # $scores .= $answerHash{$_}->{score} >= 1 ? "1" : "0";
1036 :     # }
1037 :     # $answerString = '' unless defined($answerString); # insure string is defined.
1038 :     # writeCourseLog($self->{ce}, "answer_log",
1039 :     # join("",
1040 :     # '|', $problem->user_id,
1041 :     # '|', $problem->set_id,
1042 :     # '|', $problem->problem_id,
1043 :     # '|', $scores, "\t",
1044 :     # time(),"\t",
1045 :     # $answerString,
1046 :     # ),
1047 :     # );
1048 :     #
1049 :     # }
1050 :     # }
1051 :     #
1052 :     # debug("end answer processing");
1053 :     # ##### javaScripts #############
1054 :     # my $site_url = $ce->{webworkURLs}->{htdocs};
1055 :     # print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/wz_tooltip.js"}), CGI::end_script();
1056 :     #
1057 :     # ##### output #####
1058 :     # # custom message for editor
1059 :     # if ($authz->hasPermissions($user, "modify_problem_sets") and defined $editMode) {
1060 :     # if ($editMode eq "temporaryFile") {
1061 :     # print CGI::p(CGI::div({class=>'temporaryFile'}, "Viewing temporary file: ", $problem->source_file));
1062 :     # } elsif ($editMode eq "savedFile") {
1063 :     # # taken care of in the initialization phase
1064 :     # }
1065 :     # }
1066 :     # print CGI::start_div({class=>"problemHeader"});
1067 :     #
1068 :     #
1069 :     #
1070 :     # # attempt summary
1071 :     # #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything.
1072 :     # # until after the due date
1073 :     # # do I need to check $will{showCorrectAnswers} to make preflight work??
1074 :     # if (($pg->{flags}->{showPartialCorrectAnswers} >= 0 and $submitAnswers) ) {
1075 :     # # print this if user submitted answers OR requested correct answers
1076 :     #
1077 :     # print $self->attemptResults($pg, 1,
1078 :     # $will{showCorrectAnswers},
1079 :     # $pg->{flags}->{showPartialCorrectAnswers}, 1, 1);
1080 :     # } elsif ($checkAnswers) {
1081 :     # # print this if user previewed answers
1082 :     # print CGI::div({class=>'ResultsWithError'},"ANSWERS ONLY CHECKED -- ANSWERS NOT RECORDED"), CGI::br();
1083 :     # print $self->attemptResults($pg, 1, $will{showCorrectAnswers}, 1, 1, 1);
1084 :     # # show attempt answers
1085 :     # # show correct answers if asked
1086 :     # # show attempt results (correctness)
1087 :     # # show attempt previews
1088 :     # } elsif ($previewAnswers) {
1089 :     # # print this if user previewed answers
1090 :     # print CGI::div({class=>'ResultsWithError'},"PREVIEW ONLY -- ANSWERS NOT RECORDED"),CGI::br(),$self->attemptResults($pg, 1, 0, 0, 0, 1);
1091 :     # # show attempt answers
1092 :     # # don't show correct answers
1093 :     # # don't show attempt results (correctness)
1094 :     # # show attempt previews
1095 :     # }
1096 :     #
1097 :     # print CGI::end_div();
1098 :     #
1099 :     #
1100 :     # ###########################
1101 :     # # print style sheet for correct and incorrect answers
1102 :     # ###########################
1103 :     # # always show colors for checkAnswers
1104 :     # # show colors for submit answer if
1105 :     # if (($self->{checkAnswers}) or ($self->{submitAnswers} and $pg->{flags}->{showPartialCorrectAnswers}) ) {
1106 :     # print CGI::start_style({type=>"text/css"});
1107 :     # #FIXME -- this hack is no longer needed?
1108 :     # # my $string ="";
1109 :     # # foreach my $ans_name (@{ $self->{correct_ids} }) {
1110 :     # # $string .= '#'. ( $ans_name ). $ce->{pg}{options}{correct_answer}."\n";
1111 :     # # }
1112 :     # # print $string;
1113 :     # # $string ="";
1114 :     # # foreach my $ans_name (@{ $self->{incorrect_ids} }) {
1115 :     # # $string .= '#'. ($ ans_name). $ce->{pg}{options}{incorrect_answer}."\n";
1116 :     # # }
1117 :     # # print $string;
1118 :     # # the above method keeps one bad array ID from ruining all of the assignments.
1119 :     # print '#'.join(', #', @{ $self->{correct_ids} }), $ce->{pg}{options}{correct_answer},"\n" if ref( $self->{correct_ids} )=~/ARRAY/; #correct green
1120 :     # print '#'.join(', #', @{ $self->{incorrect_ids} }), $ce->{pg}{options}{incorrect_answer},"\n" if ref( $self->{incorrect_ids})=~/ARRAY/; #incorrect reddish
1121 :     # print CGI::end_style();
1122 :     # }
1123 :     # ###########################
1124 :     # # post_header material
1125 :     # ###########################
1126 :     # print CGI::p($pg->{post_header_text});
1127 :     # ###########################
1128 :     # # main form
1129 :     # ###########################
1130 :     # print "\n";
1131 :     #
1132 :     # print CGI::start_form(-method=>"POST", -action=> $r->uri,-name=>"problemMainForm", onsubmit=>"submitAction()");
1133 :     # print $self->hidden_authen_fields;
1134 :     # print "\n";
1135 :     # print CGI::start_div({class=>"problem"});
1136 :     # print CGI::p($pg->{body_text});
1137 :     # print CGI::p(CGI::b("Note: "). CGI::i($pg->{result}->{msg})) if $pg->{result}->{msg};
1138 :     # print $editorLink; # this is empty unless it is appropriate to have an editor link.
1139 :     # print CGI::end_div();
1140 :     #
1141 :     # print CGI::start_p();
1142 :     #
1143 :     # if ($can{showCorrectAnswers}) {
1144 :     # print CGI::checkbox(
1145 :     # -name => "showCorrectAnswers",
1146 :     # -checked => $will{showCorrectAnswers},
1147 :     # -label => "Show correct answers",
1148 :     # -value => 1,
1149 :     # );
1150 :     # }
1151 :     # if ($can{showHints}) {
1152 :     # print CGI::div({style=>"color:red"},
1153 :     # CGI::checkbox(
1154 :     # -name => "showHints",
1155 :     # -checked => $will{showHints},
1156 :     # -label => "Show Hints",
1157 :     # -value =>1,
1158 :     # )
1159 :     # );
1160 :     # }
1161 :     # if ($can{showSolutions}) {
1162 :     # print CGI::checkbox(
1163 :     # -name => "showSolutions",
1164 :     # -checked => $will{showSolutions},
1165 :     # -label => "Show Solutions",
1166 :     # -value => 1,
1167 :     # );
1168 :     # }
1169 :     #
1170 :     # if ($can{showCorrectAnswers} or $can{showHints} or $can{showSolutions}) {
1171 :     # print CGI::br();
1172 :     # }
1173 :     #
1174 :     # print CGI::submit(-name=>"previewAnswers", -label=>"Preview Answers");
1175 :     # if ($can{checkAnswers}) {
1176 :     # print CGI::submit(-name=>"checkAnswers", -label=>"Check Answers");
1177 :     # }
1178 :     # if ($can{getSubmitButton}) {
1179 :     # if ($user ne $effectiveUser) {
1180 :     # # if acting as a student, make it clear that answer submissions will
1181 :     # # apply to the student's records, not the professor's.
1182 :     # print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers for $effectiveUser");
1183 :     # } else {
1184 :     # #print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>"alert('submit button clicked')");
1185 :     # print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>"");
1186 :     # # FIXME for unknown reasons the -onclick label seems to have to be there in order to allow the forms onsubmit to trigger
1187 :     # # WFT???
1188 :     # }
1189 :     # }
1190 :     #
1191 :     # print CGI::end_p();
1192 :     #
1193 :     # print CGI::start_div({class=>"scoreSummary"});
1194 :     #
1195 :     # # score summary
1196 :     # my $attempts = $problem->num_correct + $problem->num_incorrect;
1197 :     # my $attemptsNoun = $attempts != 1 ? "times" : "time";
1198 :     # my $problem_status = $problem->status || 0;
1199 :     # my $lastScore = sprintf("%.0f%%", $problem_status * 100); # Round to whole number
1200 :     # my ($attemptsLeft, $attemptsLeftNoun);
1201 :     # if ($problem->max_attempts == -1) {
1202 :     # # unlimited attempts
1203 :     # $attemptsLeft = "unlimited";
1204 :     # $attemptsLeftNoun = "attempts";
1205 :     # } else {
1206 :     # $attemptsLeft = $problem->max_attempts - $attempts;
1207 :     # $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts";
1208 :     # }
1209 :     #
1210 :     # my $setClosed = 0;
1211 :     # my $setClosedMessage;
1212 :     # if (before($set->open_date) or after($set->due_date)) {
1213 :     # $setClosed = 1;
1214 :     # if (before($set->open_date)) {
1215 :     # $setClosedMessage = "This homework set is not yet open.";
1216 :     # } elsif (after($set->due_date)) {
1217 :     # $setClosedMessage = "This homework set is closed.";
1218 :     # }
1219 :     # }
1220 :     # #if (before($set->open_date) or after($set->due_date)) {
1221 :     # # $setClosed = 1;
1222 :     # # $setClosedMessage = "This homework set is closed.";
1223 :     # # if ($authz->hasPermissions($user, "view_answers")) {
1224 :     # # $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded.";
1225 :     # # } else {
1226 :     # # $setClosedMessage .= " Additional attempts will not be recorded.";
1227 :     # # }
1228 :     # #}
1229 :     # unless (defined( $pg->{state}->{state_summary_msg}) and $pg->{state}->{state_summary_msg}=~/\S/) {
1230 :     # my $notCountedMessage = ($problem->value) ? "" : "(This problem will not count towards your grade.)";
1231 :     # print CGI::p(join("",
1232 :     # $submitAnswers ? $scoreRecordedMessage . CGI::br() : "",
1233 :     # "You have attempted this problem $attempts $attemptsNoun.", CGI::br(),
1234 :     # $submitAnswers ?"You received a score of ".sprintf("%.0f%%", $pg->{result}->{score} * 100)." for this attempt.".CGI::br():'',
1235 :     # $problem->attempted
1236 :     # ? "Your overall recorded score is $lastScore. $notCountedMessage" . CGI::br()
1237 :     # : "",
1238 :     # $setClosed ? $setClosedMessage : "You have $attemptsLeft $attemptsLeftNoun remaining."
1239 :     # ));
1240 :     # }else {
1241 :     # print CGI::p($pg->{state}->{state_summary_msg});
1242 :     # }
1243 :     #
1244 :     # print CGI::end_div();
1245 :     # print CGI::start_div();
1246 :     #
1247 :     # my $pgdebug = join(CGI::br(), @{$pg->{pgcore}->{flags}->{DEBUG_messages}} );
1248 :     # my $pgwarning = join(CGI::br(), @{$pg->{pgcore}->{flags}->{WARNING_messages}} );
1249 :     # my $pginternalerrors = join(CGI::br(), @{$pg->{pgcore}->get_internal_debug_messages} );
1250 :     # my $pgerrordiv = $pgdebug||$pgwarning||$pginternalerrors; # is 1 if any of these are non-empty
1251 :     #
1252 :     # print CGI::p({style=>"color:red;"}, "Checking additional error messages") if $pgerrordiv ;
1253 :     # print CGI::p("pg debug<br/> $pgdebug" ) if $pgdebug ;
1254 :     # print CGI::p("pg warning<br/>$pgwarning" ) if $pgwarning ;
1255 :     # print CGI::p("pg internal errors<br/> $pginternalerrors") if $pginternalerrors;
1256 :     # print CGI::end_div() if $pgerrordiv ;
1257 :     #
1258 :     # # save state for viewOptions
1259 :     # print CGI::hidden(
1260 :     # -name => "showOldAnswers",
1261 :     # -value => $will{showOldAnswers}
1262 :     # ),
1263 :     #
1264 :     # CGI::hidden(
1265 :     # -name => "displayMode",
1266 :     # -value => $self->{displayMode}
1267 :     # );
1268 :     # print( CGI::hidden(
1269 :     # -name => 'editMode',
1270 :     # -value => $self->{editMode},
1271 :     # )
1272 :     # ) if defined($self->{editMode}) and $self->{editMode} eq 'temporaryFile';
1273 :     #
1274 :     # # this is a security risk -- students can use this to find the source code for the problem
1275 :     #
1276 :     # my $permissionLevel = $db->getPermissionLevel($user)->permission;
1277 :     # my $professorPermissionLevel = $ce->{userRoles}->{professor};
1278 :     # print( CGI::hidden(
1279 :     # -name => 'sourceFilePath',
1280 :     # -value => $self->{problem}->{source_file}
1281 :     # )) if defined($self->{problem}->{source_file}) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors
1282 :     #
1283 :     # print( CGI::hidden(
1284 :     # -name => 'problemSeed',
1285 :     # -value => $r->param("problemSeed")
1286 :     # )) if defined($r->param("problemSeed")) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors
1287 :     #
1288 :     #
1289 :     # # end of main form
1290 :     # print CGI::endform();
1291 :     #
1292 :     # print CGI::start_div({class=>"problemFooter"});
1293 :     #
1294 :     #
1295 :     # my $pastAnswersPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ShowAnswers",
1296 :     # courseID => $courseName);
1297 :     # my $showPastAnswersURL = $self->systemLink($pastAnswersPage, authen => 0); # no authen info for form action
1298 :     #
1299 :     # # print answer inspection button
1300 :     # if ($authz->hasPermissions($user, "view_answers")) {
1301 :     # print "\n",
1302 :     # CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"WW_Info"),"\n",
1303 :     # $self->hidden_authen_fields,"\n",
1304 :     # CGI::hidden(-name => 'courseID', -value=>$courseName), "\n",
1305 :     # CGI::hidden(-name => 'problemID', -value=>$problem->problem_id), "\n",
1306 :     # CGI::hidden(-name => 'setID', -value=>$problem->set_id), "\n",
1307 :     # CGI::hidden(-name => 'studentUser', -value=>$problem->user_id), "\n",
1308 :     # CGI::p( {-align=>"left"},
1309 :     # CGI::submit(-name => 'action', -value=>'Show Past Answers')
1310 :     # ), "\n",
1311 :     # CGI::endform();
1312 :     # }
1313 :     #
1314 :     #
1315 :     # print $self->feedbackMacro(
1316 :     # module => __PACKAGE__,
1317 :     # set => $self->{set}->set_id,
1318 :     # problem => $problem->problem_id,
1319 :     # displayMode => $self->{displayMode},
1320 :     # showOldAnswers => $will{showOldAnswers},
1321 :     # showCorrectAnswers => $will{showCorrectAnswers},
1322 :     # showHints => $will{showHints},
1323 :     # showSolutions => $will{showSolutions},
1324 :     # pg_object => $pg,
1325 :     # );
1326 :     #
1327 :     # print CGI::end_div();
1328 :     #
1329 :     # # debugging stuff
1330 :     # if (0) {
1331 :     # print
1332 :     # CGI::hr(),
1333 :     # CGI::h2("debugging information"),
1334 :     # CGI::h3("form fields"),
1335 :     # ref2string($self->{formFields}),
1336 :     # CGI::h3("user object"),
1337 :     # ref2string($self->{user}),
1338 :     # CGI::h3("set object"),
1339 :     # ref2string($set),
1340 :     # CGI::h3("problem object"),
1341 :     # ref2string($problem),
1342 :     # CGI::h3("PG object"),
1343 :     # ref2string($pg, {'WeBWorK::PG::Translator' => 1});
1344 :     # }
1345 :     # debug("leaving body of Problem.pm");
1346 :     # return "";
1347 :     # }
1348 :    
1349 :     # now altered to outsource most output operations to the template, main functions now are simply error checking and answer processing - ghe3
1350 : sh002i 449 sub body {
1351 :     my $self = shift;
1352 : gage 6885 my $set = $self->{set};
1353 :     my $problem = $self->{problem};
1354 :     my $pg = $self->{pg};
1355 :    
1356 :     my $valid = WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::check_invalid($self);
1357 :     unless($valid eq "valid"){
1358 :     return $valid;
1359 :     }
1360 :    
1361 :     ####################################################
1362 :     # Move to header in new templates
1363 :     #print $self->output_tabber_JS();
1364 :     print $self->output_coloring_JS();
1365 :    
1366 :     ##### javaScripts #############
1367 :     # WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::output_JS($self);
1368 :     print $self->output_JS;
1369 :    
1370 :     ####################################################
1371 :    
1372 :     # my $editorLink = WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::process_editorLink($self);
1373 :     # if($editorLink eq "permission_error"){
1374 :     # return "";
1375 :     # }
1376 :    
1377 :    
1378 :     ##### answer processing #####
1379 :     debug("begin answer processing");
1380 :     # if answers were submitted:
1381 :     my $scoreRecordedMessage = WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::process_and_log_answer($self);
1382 :     debug("end answer processing");
1383 :    
1384 :     ###########################
1385 :     # print style sheet for correct and incorrect answers
1386 :     ###########################
1387 :    
1388 :     # WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::output_CSS($self);
1389 :     print $self->output_CSS;
1390 :    
1391 :     ##### output #####
1392 :     # WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::output_summary($self);
1393 :     print $self->output_custom_edit_message();
1394 :     print $self->output_summary();
1395 :     print $self->output_form_start();
1396 :     print $self->output_problem_body();
1397 :     print $self->output_message();
1398 :     print $self->output_editorLink();
1399 :     print $self->output_checkboxes();
1400 :     print $self->output_submit_buttons();
1401 :     print $self->output_score_summary();
1402 :     print $self->output_misc();
1403 :     print "\n</form>\n";
1404 :    
1405 :    
1406 :     $self->output_email_instructor();
1407 :     $self->output_past_answer_button();
1408 :    
1409 :    
1410 :     #
1411 :     #
1412 :     # ###########################
1413 :     # # main form
1414 :     # ###########################
1415 :     #
1416 :     # # WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::output_main_form($self,$editorLink);
1417 :     #
1418 :     # # WeBWorK::ContentGenerator::ProblemUtil::ProblemUtil::output_footer($self);
1419 :    
1420 :     # debugging stuff
1421 :     if (0) {
1422 :     print
1423 :     CGI::hr(),
1424 :     CGI::h2("debugging information"),
1425 :     CGI::h3("form fields"),
1426 :     ref2string($self->{formFields}),
1427 :     CGI::h3("user object"),
1428 :     ref2string($self->{user}),
1429 :     CGI::h3("set object"),
1430 :     ref2string($set),
1431 :     CGI::h3("problem object"),
1432 :     ref2string($problem),
1433 :     CGI::h3("PG object"),
1434 :     ref2string($pg, {'WeBWorK::PG::Translator' => 1});
1435 :     }
1436 :     debug("leaving body of Problem.pm");
1437 :     return "";
1438 :     }
1439 :    
1440 :     # output_form_start subroutine
1441 :    
1442 :     # prints out the beginning of the main form, and the necessary hidden authentication fields
1443 :    
1444 :     sub output_form_start{
1445 :     my $self = shift;
1446 : sh002i 1908 my $r = $self->r;
1447 : gage 6885 print CGI::start_form(-method=>"POST", -action=> $r->uri,-name=>"problemMainForm", onsubmit=>"submitAction()");
1448 :     print $self->hidden_authen_fields;
1449 :     return "";
1450 :     }
1451 :    
1452 :     # output_problem_body subroutine
1453 :    
1454 :     # prints out the body of the current problem
1455 :    
1456 :     sub output_problem_body{
1457 :     my $self = shift;
1458 :     my $pg = $self->{pg};
1459 :    
1460 :     print "\n";
1461 :     print CGI::p($pg->{body_text});
1462 :     return "";
1463 :     }
1464 :    
1465 :     # output_message subroutine
1466 :    
1467 :     # prints out a message about the problem
1468 :    
1469 :     sub output_message{
1470 :     my $self = shift;
1471 :     my $pg = $self->{pg};
1472 :    
1473 :     print CGI::p(CGI::b("Note: "). CGI::i($pg->{result}->{msg})) if $pg->{result}->{msg};
1474 :     return "";
1475 :     }
1476 :    
1477 :     # output_editorLink subroutine
1478 :    
1479 :     # processes and prints out the correct link to the editor of the current problem
1480 :    
1481 :     sub output_editorLink{
1482 :    
1483 :     my $self = shift;
1484 :    
1485 :     my $set = $self->{set};
1486 :     my $problem = $self->{problem};
1487 :     my $pg = $self->{pg};
1488 :    
1489 :     my $r = $self->r;
1490 :    
1491 : toenail 2349 my $authz = $r->authz;
1492 : sh002i 1908 my $urlpath = $r->urlpath;
1493 : toenail 2349 my $user = $r->param('user');
1494 : sh002i 449
1495 : sh002i 1908 my $courseName = $urlpath->arg("courseID");
1496 : gage 1592
1497 : sh002i 2505 # FIXME: move editor link to top, next to problem number.
1498 :     # format as "[edit]" like we're doing with course info file, etc.
1499 :     # add edit link for set as well.
1500 : sh002i 1908 my $editorLink = "";
1501 : dpvc 4158 # if we are here without a real homework set, carry that through
1502 : jj 2020 my $forced_field = [];
1503 :     $forced_field = ['sourceFilePath' => $r->param("sourceFilePath")] if
1504 :     ($set->set_id eq 'Undefined_Set');
1505 : toenail 2398 if ($authz->hasPermissions($user, "modify_problem_sets")) {
1506 : sh002i 1908 my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor",
1507 :     courseID => $courseName, setID => $set->set_id, problemID => $problem->problem_id);
1508 : jj 2020 my $editorURL = $self->systemLink($editorPage, params=>$forced_field);
1509 : gage 3888 $editorLink = CGI::p(CGI::a({href=>$editorURL,target =>'WW_Editor'}, "Edit this problem"));
1510 : gage 1592 }
1511 : sh002i 1908
1512 : sh002i 449 ##### translation errors? #####
1513 : gage 2861
1514 : sh002i 425 if ($pg->{flags}->{error_flag}) {
1515 : sh002i 3795 if ($authz->hasPermissions($user, "view_problem_debugging_info")) {
1516 :     print $self->errorOutput($pg->{errors}, $pg->{body_text});
1517 :     } else {
1518 :     print $self->errorOutput($pg->{errors}, "You do not have permission to view the details of this error.");
1519 :     }
1520 : gage 6885 print "";
1521 :     }
1522 :     else{
1523 : sh002i 1908 print $editorLink;
1524 : sh002i 425 }
1525 : gage 6885 return "";
1526 :     }
1527 : gage 1387
1528 : gage 6885 # output_checkboxes subroutine
1529 : gage 6659
1530 : gage 6885 # prints out the checkbox input elements that are available for the current problem
1531 : toenail 2349
1532 : gage 6885 sub output_checkboxes{
1533 :     my $self = shift;
1534 :     my %can = %{ $self->{can} };
1535 :     my %will = %{ $self->{will} };
1536 :    
1537 : sh002i 2505 if ($can{showCorrectAnswers}) {
1538 : gage 6885 print WeBWorK::CGI_labeled_input(
1539 :     -type => "checkbox",
1540 :     -id => "showCorrectAnswers_id",
1541 :     -label_text => "Show correct answers",
1542 :     -input_attr => $will{showCorrectAnswers} ?
1543 :     {
1544 :     -name => "showCorrectAnswers",
1545 :     -checked => "checked",
1546 :     -value => 1,
1547 :     }
1548 :     :
1549 :     {
1550 :     -name => "showCorrectAnswers",
1551 :     -value => 1,
1552 :     }
1553 : malsyned 755 );
1554 : sh002i 2505 }
1555 :     if ($can{showHints}) {
1556 :     print CGI::div({style=>"color:red"},
1557 : gage 6885 WeBWorK::CGI_labeled_input(
1558 :     -type => "checkbox",
1559 :     -id => "showHints_id",
1560 :     -label_text => "Show Hints",
1561 :     -input_attr => $will{showHints} ?
1562 :     {
1563 :     -name => "showHints",
1564 :     -checked => "checked",
1565 :     -value => 1,
1566 :     }
1567 :     :
1568 :     {
1569 :     -name => "showCorrectAnswers",
1570 :     -value => 1,
1571 :     }
1572 : sh002i 2505 )
1573 :     );
1574 :     }
1575 :     if ($can{showSolutions}) {
1576 : gage 6885 print WeBWorK::CGI_labeled_input(
1577 :     -type => "checkbox",
1578 :     -id => "showSolutions_id",
1579 :     -label_text => "Show Solutions",
1580 :     -input_attr => $will{showSolutions} ?
1581 :     {
1582 :     -name => "showSolutions",
1583 :     -checked => "checked",
1584 :     -value => 1,
1585 :     }
1586 :     :
1587 :     {
1588 :     -name => "showCorrectAnswers",
1589 :     -value => 1,
1590 :     }
1591 : sh002i 2505 );
1592 :     }
1593 :    
1594 :     if ($can{showCorrectAnswers} or $can{showHints} or $can{showSolutions}) {
1595 :     print CGI::br();
1596 :     }
1597 : gage 6885
1598 :     return "";
1599 :     }
1600 :    
1601 :     # output_submit_buttons
1602 :    
1603 :     # prints out the submit button input elements that are available for the current problem
1604 :    
1605 :     sub output_submit_buttons{
1606 :     my $self = shift;
1607 :     my $r = $self->r;
1608 :     my %can = %{ $self->{can} };
1609 :    
1610 :     my $user = $r->param('user');
1611 :     my $effectiveUser = $r->param('effectiveUser');
1612 :    
1613 :     print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"previewAnswers_id", -input_attr=>{-name=>"previewAnswers", -value=>"Preview Answers"});
1614 : sh002i 2505 if ($can{checkAnswers}) {
1615 : gage 6885 print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"checkAnswers_id", -input_attr=>{-name=>"checkAnswers", -value=>"Check Answers"});
1616 : sh002i 2505 }
1617 : jj 2685 if ($can{getSubmitButton}) {
1618 : sh002i 2507 if ($user ne $effectiveUser) {
1619 :     # if acting as a student, make it clear that answer submissions will
1620 :     # apply to the student's records, not the professor's.
1621 : gage 6885 print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"submitAnswers_id", -input_attr=>{-name=>"submitAnswers", -value=>"Submit Answers for $effectiveUser"});
1622 : sh002i 2507 } else {
1623 : gage 5678 #print CGI::submit(-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>"alert('submit button clicked')");
1624 : gage 6885 print WeBWorK::CGI_labeled_input(-type=>"submit", -id=>"submitAnswers_id", -input_attr=>{-name=>"submitAnswers", -label=>"Submit Answers", -onclick=>""});
1625 : gage 5678 # FIXME for unknown reasons the -onclick label seems to have to be there in order to allow the forms onsubmit to trigger
1626 :     # WFT???
1627 : sh002i 2507 }
1628 : sh002i 2505 }
1629 :    
1630 : gage 6885 return "";
1631 :     }
1632 :    
1633 :     # output_score_summary subroutine
1634 :    
1635 :     # prints out a summary of the student's current progress and status on the current problem
1636 :    
1637 :     sub output_score_summary{
1638 :     my $self = shift;
1639 :     my $problem = $self->{problem};
1640 :     my $set = $self->{set};
1641 :     my $pg = $self->{pg};
1642 :     my $scoreRecordedMessage = "";
1643 :     unless(defined $self->{scoreRecordedMessage}){
1644 :     $scoreRecordedMessage = $self->{scoreRecordedMessage};
1645 :     }
1646 :     my $submitAnswers = $self->{submitAnswers};
1647 : sh002i 2505
1648 : sh002i 431 # score summary
1649 :     my $attempts = $problem->num_correct + $problem->num_incorrect;
1650 :     my $attemptsNoun = $attempts != 1 ? "times" : "time";
1651 : gage 2875 my $problem_status = $problem->status || 0;
1652 :     my $lastScore = sprintf("%.0f%%", $problem_status * 100); # Round to whole number
1653 : sh002i 431 my ($attemptsLeft, $attemptsLeftNoun);
1654 :     if ($problem->max_attempts == -1) {
1655 :     # unlimited attempts
1656 :     $attemptsLeft = "unlimited";
1657 :     $attemptsLeftNoun = "attempts";
1658 :     } else {
1659 :     $attemptsLeft = $problem->max_attempts - $attempts;
1660 :     $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts";
1661 :     }
1662 : malsyned 755
1663 :     my $setClosed = 0;
1664 : sh002i 476 my $setClosedMessage;
1665 : sh002i 2505 if (before($set->open_date) or after($set->due_date)) {
1666 : malsyned 755 $setClosed = 1;
1667 : sh002i 3318 if (before($set->open_date)) {
1668 : sh002i 3357 $setClosedMessage = "This homework set is not yet open.";
1669 : sh002i 3318 } elsif (after($set->due_date)) {
1670 : sh002i 3357 $setClosedMessage = "This homework set is closed.";
1671 : sh002i 476 }
1672 :     }
1673 : sh002i 3318 #if (before($set->open_date) or after($set->due_date)) {
1674 :     # $setClosed = 1;
1675 : sh002i 3357 # $setClosedMessage = "This homework set is closed.";
1676 : sh002i 3318 # if ($authz->hasPermissions($user, "view_answers")) {
1677 :     # $setClosedMessage .= " However, since you are a privileged user, additional attempts will be recorded.";
1678 :     # } else {
1679 :     # $setClosedMessage .= " Additional attempts will not be recorded.";
1680 :     # }
1681 :     #}
1682 : gage 3391 unless (defined( $pg->{state}->{state_summary_msg}) and $pg->{state}->{state_summary_msg}=~/\S/) {
1683 :     my $notCountedMessage = ($problem->value) ? "" : "(This problem will not count towards your grade.)";
1684 : gage 4235 print CGI::p(join("",
1685 : gage 3391 $submitAnswers ? $scoreRecordedMessage . CGI::br() : "",
1686 :     "You have attempted this problem $attempts $attemptsNoun.", CGI::br(),
1687 :     $submitAnswers ?"You received a score of ".sprintf("%.0f%%", $pg->{result}->{score} * 100)." for this attempt.".CGI::br():'',
1688 :     $problem->attempted
1689 :     ? "Your overall recorded score is $lastScore. $notCountedMessage" . CGI::br()
1690 :     : "",
1691 :     $setClosed ? $setClosedMessage : "You have $attemptsLeft $attemptsLeftNoun remaining."
1692 : gage 4235 ));
1693 : gage 3391 }else {
1694 :     print CGI::p($pg->{state}->{state_summary_msg});
1695 :     }
1696 : gage 6885
1697 :     return "";
1698 :     }
1699 : gage 6310
1700 : gage 6885 # output_misc subroutine
1701 :    
1702 :     # prints out other necessary elements
1703 :    
1704 :     sub output_misc{
1705 :    
1706 :     my $self = shift;
1707 :     my $r = $self->r;
1708 :     my $ce = $r->ce;
1709 :     my $db = $r->db;
1710 :     my $pg = $self->{pg};
1711 :     my %will = %{ $self->{will} };
1712 :     my $user = $r->param('user');
1713 :    
1714 : gage 6310 print CGI::start_div();
1715 : gage 6320
1716 :     my $pgdebug = join(CGI::br(), @{$pg->{pgcore}->{flags}->{DEBUG_messages}} );
1717 :     my $pgwarning = join(CGI::br(), @{$pg->{pgcore}->{flags}->{WARNING_messages}} );
1718 :     my $pginternalerrors = join(CGI::br(), @{$pg->{pgcore}->get_internal_debug_messages} );
1719 :     my $pgerrordiv = $pgdebug||$pgwarning||$pginternalerrors; # is 1 if any of these are non-empty
1720 :    
1721 :     print CGI::p({style=>"color:red;"}, "Checking additional error messages") if $pgerrordiv ;
1722 :     print CGI::p("pg debug<br/> $pgdebug" ) if $pgdebug ;
1723 :     print CGI::p("pg warning<br/>$pgwarning" ) if $pgwarning ;
1724 :     print CGI::p("pg internal errors<br/> $pginternalerrors") if $pginternalerrors;
1725 :     print CGI::end_div() if $pgerrordiv ;
1726 :    
1727 : sh002i 1131 # save state for viewOptions
1728 : gage 1591 print CGI::hidden(
1729 :     -name => "showOldAnswers",
1730 :     -value => $will{showOldAnswers}
1731 :     ),
1732 : gage 1479
1733 : gage 1591 CGI::hidden(
1734 :     -name => "displayMode",
1735 :     -value => $self->{displayMode}
1736 :     );
1737 :     print( CGI::hidden(
1738 :     -name => 'editMode',
1739 :     -value => $self->{editMode},
1740 :     )
1741 :     ) if defined($self->{editMode}) and $self->{editMode} eq 'temporaryFile';
1742 : gage 6237
1743 :     # this is a security risk -- students can use this to find the source code for the problem
1744 : gage 6275
1745 : gage 6274 my $permissionLevel = $db->getPermissionLevel($user)->permission;
1746 :     my $professorPermissionLevel = $ce->{userRoles}->{professor};
1747 :     print( CGI::hidden(
1748 :     -name => 'sourceFilePath',
1749 :     -value => $self->{problem}->{source_file}
1750 :     )) if defined($self->{problem}->{source_file}) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors
1751 : jj 2251
1752 : gage 6274 print( CGI::hidden(
1753 :     -name => 'problemSeed',
1754 :     -value => $r->param("problemSeed")
1755 :     )) if defined($r->param("problemSeed")) and $permissionLevel>= $professorPermissionLevel; # only allow this for professors
1756 : gage 6885
1757 :     return "";
1758 :     }
1759 : gage 6275
1760 : gage 6885 # output_summary subroutine
1761 :    
1762 :     # prints out the summary of the questions that the student has answered for the current problem, along with available information about correctness
1763 :    
1764 :     sub output_summary{
1765 : sh002i 1131
1766 : gage 6885 my $self = shift;
1767 : sh002i 1131
1768 : gage 6885 my $editMode = $self->{editMode};
1769 :     my $problem = $self->{problem};
1770 :     my $pg = $self->{pg};
1771 :     my $submitAnswers = $self->{submitAnswers};
1772 :     my %will = %{ $self->{will} };
1773 :     my $checkAnswers = $self->{checkAnswers};
1774 :     my $previewAnswers = $self->{previewAnswers};
1775 : gage 794
1776 : gage 6885 my $r = $self->r;
1777 :    
1778 :     my $authz = $r->authz;
1779 :     my $user = $r->param('user');
1780 :    
1781 :     # attempt summary
1782 :     #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything.
1783 :     # until after the due date
1784 :     # do I need to check $will{showCorrectAnswers} to make preflight work??
1785 :     if (($pg->{flags}->{showPartialCorrectAnswers} >= 0 and $submitAnswers) ) {
1786 :     # print this if user submitted answers OR requested correct answers
1787 :    
1788 :     print $self->attemptResults($pg, 1,
1789 :     $will{showCorrectAnswers},
1790 :     $pg->{flags}->{showPartialCorrectAnswers}, 1, 1);
1791 :     } elsif ($checkAnswers) {
1792 :     # print this if user previewed answers
1793 :     print CGI::div({class=>'ResultsWithError'},"ANSWERS ONLY CHECKED -- ANSWERS NOT RECORDED"), CGI::br();
1794 :     print $self->attemptResults($pg, 1, $will{showCorrectAnswers}, 1, 1, 1);
1795 :     # show attempt answers
1796 :     # show correct answers if asked
1797 :     # show attempt results (correctness)
1798 :     # show attempt previews
1799 :     } elsif ($previewAnswers) {
1800 :     # print this if user previewed answers
1801 :     print CGI::div({class=>'ResultsWithError'},"PREVIEW ONLY -- ANSWERS NOT RECORDED"),CGI::br(),$self->attemptResults($pg, 1, 0, 0, 0, 1);
1802 :     # show attempt answers
1803 :     # don't show correct answers
1804 :     # don't show attempt results (correctness)
1805 :     # show attempt previews
1806 :     }
1807 :    
1808 :     return "";
1809 :     }
1810 :    
1811 :     # output_custom_edit_message
1812 :    
1813 :     # prints out a custom edit message
1814 :    
1815 :     sub output_custom_edit_message{
1816 :     my $self = shift;
1817 :     my $r = $self->r;
1818 :     my $authz = $r->authz;
1819 :     my $user = $r->param('user');
1820 :     my $editMode = $self->{editMode};
1821 :     my $problem = $self->{problem};
1822 :    
1823 :     # custom message for editor
1824 :     if ($authz->hasPermissions($user, "modify_problem_sets") and defined $editMode) {
1825 :     if ($editMode eq "temporaryFile") {
1826 :     print CGI::p(CGI::div({class=>'temporaryFile'}, "Viewing temporary file: ", $problem->source_file));
1827 :     } elsif ($editMode eq "savedFile") {
1828 :     # taken care of in the initialization phase
1829 :     }
1830 :     }
1831 :    
1832 :     return "";
1833 :     }
1834 :    
1835 :     # output_JS subroutine
1836 :    
1837 :     # prints out the wz_tooltip.js script for the current site.
1838 :    
1839 :     sub output_JS{
1840 :    
1841 :     my $self = shift;
1842 :     my $r = $self->r;
1843 :     my $ce = $r->ce;
1844 :    
1845 :     my $site_url = $ce->{webworkURLs}->{htdocs};
1846 :     print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/wz_tooltip.js"}), CGI::end_script();
1847 :     return "";
1848 :     }
1849 :    
1850 :     # output_CSS subroutine
1851 :    
1852 :     # prints the CSS scripts to page. Does some PERL trickery to form the styles for the correct answers and the incorrect answers (which may be substituted with JS sometime in the future).
1853 :    
1854 :     sub output_CSS{
1855 :    
1856 :     my $self = shift;
1857 :     my $r = $self->r;
1858 :     my $ce = $r->ce;
1859 :     my $pg = $self->{pg};
1860 :    
1861 :     # always show colors for checkAnswers
1862 :     # show colors for submit answer if
1863 :     if (($self->{checkAnswers}) or ($self->{submitAnswers} and $pg->{flags}->{showPartialCorrectAnswers}) ) {
1864 :     print CGI::start_style({type=>"text/css"});
1865 :     print '#'.join(', #', @{ $self->{correct_ids} }), $ce->{pg}{options}{correct_answer} if ref( $self->{correct_ids} )=~/ARRAY/; #correct green
1866 :     print '#'.join(', #', @{ $self->{incorrect_ids} }), $ce->{pg}{options}{incorrect_answer} if ref( $self->{incorrect_ids})=~/ARRAY/; #incorrect reddish
1867 :     print CGI::end_style();
1868 :     }
1869 :    
1870 :     return "";
1871 :     }
1872 :    
1873 :     # output_past_answer_button
1874 :    
1875 :     # prints out the "Show Past Answers" button
1876 :    
1877 :     sub output_past_answer_button{
1878 :     my $self = shift;
1879 :     my $r = $self->r;
1880 :     my $problem = $self->{problem};
1881 :    
1882 :     my $authz = $r->authz;
1883 :     my $urlpath = $r->urlpath;
1884 :     my $user = $r->param('user');
1885 :    
1886 :     my $courseName = $urlpath->arg("courseID");
1887 :    
1888 : sh002i 1908 my $pastAnswersPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ShowAnswers",
1889 :     courseID => $courseName);
1890 :     my $showPastAnswersURL = $self->systemLink($pastAnswersPage, authen => 0); # no authen info for form action
1891 :    
1892 : gage 794 # print answer inspection button
1893 : toenail 2398 if ($authz->hasPermissions($user, "view_answers")) {
1894 : gage 794 print "\n",
1895 : dpvc 4389 CGI::start_form(-method=>"POST",-action=>$showPastAnswersURL,-target=>"WW_Info"),"\n",
1896 : sh002i 1131 $self->hidden_authen_fields,"\n",
1897 : gage 1937 CGI::hidden(-name => 'courseID', -value=>$courseName), "\n",
1898 :     CGI::hidden(-name => 'problemID', -value=>$problem->problem_id), "\n",
1899 :     CGI::hidden(-name => 'setID', -value=>$problem->set_id), "\n",
1900 : gage 1395 CGI::hidden(-name => 'studentUser', -value=>$problem->user_id), "\n",
1901 : sh002i 1131 CGI::p( {-align=>"left"},
1902 :     CGI::submit(-name => 'action', -value=>'Show Past Answers')
1903 :     ), "\n",
1904 :     CGI::endform();
1905 :     }
1906 : sh002i 667
1907 : gage 6885 return "";
1908 :     }
1909 :    
1910 :     # output_email_instructor subroutine
1911 :    
1912 :     # prints out the "Email Instructor" button
1913 :    
1914 :     sub output_email_instructor{
1915 :     my $self = shift;
1916 :     my $problem = $self->{problem};
1917 :     my %will = %{ $self->{will} };
1918 :     my $pg = $self->{pg};
1919 :    
1920 : sh002i 3626 print $self->feedbackMacro(
1921 :     module => __PACKAGE__,
1922 :     set => $self->{set}->set_id,
1923 :     problem => $problem->problem_id,
1924 :     displayMode => $self->{displayMode},
1925 :     showOldAnswers => $will{showOldAnswers},
1926 :     showCorrectAnswers => $will{showCorrectAnswers},
1927 :     showHints => $will{showHints},
1928 :     showSolutions => $will{showSolutions},
1929 : gage 6041 pg_object => $pg,
1930 : sh002i 3626 );
1931 : sh002i 1908
1932 : gage 6885 return "";
1933 :     }
1934 :    
1935 :     sub output_hidden_info{
1936 :     my $self = shift;
1937 : gage 940
1938 : gage 6885 if(defined $self->{correct_ids}){
1939 :     my $correctRef = $self->{correct_ids};
1940 :     my @correct = @$correctRef;
1941 :     foreach(@correct){
1942 :     print CGI::hidden(-name=>"correct_ids", -value=>$_."_val");
1943 :     }
1944 : sh002i 738 }
1945 : gage 6885 if(defined $self->{incorrect_ids}){
1946 :     my $incorrectRef = $self->{incorrect_ids};
1947 :     my @incorrect = @$incorrectRef;
1948 :     foreach(@incorrect){
1949 :     print CGI::hidden(-name=>"incorrect_ids", -value=>$_."_val");
1950 :     }
1951 :     }
1952 :    
1953 : sh002i 424 return "";
1954 : malsyned 353 }
1955 :    
1956 : gage 6885 sub output_coloring_JS{
1957 :     my $self = shift;
1958 :     my $r = $self->r;
1959 :     my $ce = $r->ce;
1960 :    
1961 :     my $site_url = $ce->{webworkURLs}->{htdocs};
1962 :     print CGI::start_script({type=>"text/javascript", src=>"$site_url/js/color.js"}), CGI::end_script();
1963 :     return "";
1964 :     }
1965 :    
1966 : jj 2539 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9