[system] / branches / rel-2-2-dev / webwork-modperl / lib / WeBWorK / ContentGenerator / Hardcopy.pm Repository:
ViewVC logotype

Diff of /branches/rel-2-2-dev/webwork-modperl/lib/WeBWorK/ContentGenerator/Hardcopy.pm

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

Revision 737 Revision 3377
1################################################################################ 1################################################################################
2# WeBWorK mod_perl (c) 2000-2002 WeBWorK Project 2# WeBWorK Online Homework Delivery System
3# $Id$ 3# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4# $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Hardcopy.pm,v 1.54 2005/06/29 02:46:08 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.
4################################################################################ 15################################################################################
5 16
6package WeBWorK::ContentGenerator::Hardcopy; 17package WeBWorK::ContentGenerator::Hardcopy;
18use base qw(WeBWorK::ContentGenerator);
19
7 20
8=head1 NAME 21=head1 NAME
9 22
10WeBWorK::ContentGenerator::Hardcopy - generate a PDF version of one or more 23WeBWorK::ContentGenerator::Hardcopy - generate a PDF version of one or more
11problem sets. 24problem sets.
12 25
13=cut 26=cut
14 27
28################################################################################
29##
30## WARNING: This file has been hacked so that it will download
31## TeX files rather than displaying them in the browser.
32## In particular, if a TeX file is requested then
33## the value of the variable $pdfFileURL (in spite of its name)
34## will be the URL for the texFile, i.e.,
35## $pdfFileURL = $texFileURL if TeX file is requested
36##
37## wheeler@indiana.edu, 7/9/04
38##
39################################################################################
40
15use strict; 41use strict;
16use warnings; 42use warnings;
17use base qw(WeBWorK::ContentGenerator);
18use CGI qw(); 43use CGI qw();
19use File::Path qw(rmtree); 44use File::Path qw(rmtree);
20use File::Temp qw(tempdir);
21use WeBWorK::DB::Classlist;
22use WeBWorK::DB::WW;
23use WeBWorK::Form; 45use WeBWorK::Form;
46use WeBWorK::PG;
24use WeBWorK::Utils qw(readFile); 47use WeBWorK::Utils qw(readFile makeTempDirectory);
48use Apache::Constants qw(:common REDIRECT);
25 49
26sub go { 50=head1 CONFIGURATION VARIABLES
27 my ($self, $singleSet) = @_; 51
28 52=over
53
54=item $PreserveTempFiles
55
56If true, don't delete temporary files.
57
58=cut
59
60our $PreserveTempFiles = 0 unless defined $PreserveTempFiles;
61
62=back
63
64=cut
65
66sub pre_header_initialize {
67 my ($self) = @_;
29 my $r = $self->{r}; 68 my $r = $self->r;
30 my $ce = $self->{courseEnvironment}; 69 my $ce = $r->ce;
70 my $db = $r->db;
71 my $authz = $r->authz;
72 my $userID = $r->param("user");
73
74 my $singleSet = $r->urlpath->arg("setID");
31 my @sets = $r->param("hcSet"); 75 my @sets = $r->param("hcSet");
32 my @users = $r->param("hcUser"); 76 my @users = $r->param("hcUser");
33 77 my $hardcopy_format = $r->param('hardcopy_format') ? $r->param('hardcopy_format') : '';
78
34 # add singleSet to the list of sets 79 # add singleSet to the list of sets
35 if (length $singleSet > 0) { 80 if (defined $singleSet and $singleSet ne "") {
36 $singleSet =~ s/^set//; 81 $singleSet =~ s/^set//;
37 unshift @sets, $singleSet unless grep { $_ eq $singleSet } @sets; 82 unshift @sets, $singleSet unless grep { $_ eq $singleSet } @sets;
38 } 83 }
39 84 #die "single set is $singleSet and sets is ", join("|",@sets);
40 # default user is the effectiveUser 85 # default user is the effectiveUser
41 unless (@users) { 86 unless (@users) {
42 unshift @users, $r->param("effectiveUser"); 87 unshift @users, $r->param("effectiveUser");
43 } 88 }
44 89
45 $self->{cldb} = WeBWorK::DB::Classlist->new($ce); 90 # this should never happen, but apparently it did once (see bug #714), so we check for it
46 $self->{authdb} = WeBWorK::DB::Auth->new($ce); 91 die "Parameter 'user' not defined. Can't continue." unless defined $userID;
47 $self->{wwdb} = WeBWorK::DB::WW->new($ce); 92
48 $self->{user} = $self->{cldb}->getUser($r->param("user")); 93 $self->{user} = $db->getUser($userID); # checked
49 $self->{permissionLevel} = $self->{authdb}->getPermissions($r->param("user")); 94 die "user ", $userID, " (real user) not found."
95 unless $self->{user};
96
50 $self->{effectiveUser} = $self->{cldb}->getUser($r->param("effectiveUser")); 97 $self->{effectiveUser} = $db->getUser($r->param("effectiveUser")); # checked
98 die "user ", $r->param("effectiveUser"), " (effective user) not found."
99 unless $self->{effectiveUser};
100
101 #my $PermissionLevel = $db->getPermissionLevel($r->param("user")); # checked
102 #if ($PermissionLevel) {
103 # $self->{permissionLevel} = $PermissionLevel->permission();
104 #} else {
105 # die "permission level for user ", $r->param("user"), " (real user) not found.";
106 #}
107
51 $self->{sets} = \@sets; 108 $self->{sets} = \@sets;
52 $self->{users} = \@users; 109 $self->{users} = \@users;
110 $self->{hardcopy_format} = $hardcopy_format;
53 $self->{errors} = []; 111 $self->{errors} = [];
54 $self->{warnings} = []; 112 $self->{warnings} = [];
55 113
56 # security checks - these have to be put somewhere 114 # is the user allowed to request multiple sets/users at a time?
57 my $multiSet = $self->{permissionLevel} > 0; 115 my $multiSet = $authz->hasPermissions($userID, "download_hardcopy_multiset");
58 my $multiUser = $self->{permissionLevel} > 0; 116 my $multiUser = $authz->hasPermissions($userID, "download_hardcopy_multiuser");
117
59 if (@sets > 1 and not $multiSet) { 118 if (@sets > 1 and not $multiSet) {
60 $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for multiple sets. Please select a single set and try again."]; 119 $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for multiple sets. Please select a single set and try again."];
61 } 120 }
62 if (@users > 1 and not $multiUser) { 121 if (@users > 1 and not $multiUser) {
63 $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for multiple users. Please select a single user and try again."]; 122 $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for multiple users. Please select a single user and try again."];
64 } 123 }
65 if ($users[0] ne $self->{effectiveUser}->id and not $multiUser) { 124 if ($users[0] ne $self->{effectiveUser}->user_id and not $multiUser) {
66 $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for other users."]; 125 $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for other users."];
67 } 126 }
68 127
69 unless ($self->{generationError}) { 128 unless ($self->{generationError}) {
70 if ($r->param("generateHardcopy")) { 129 if ($r->param("generateHardcopy")) {
71 my ($tempDir, $fileName) = eval { $self->generateHardcopy() }; 130 #my ($tempDir, $fileName) = eval { $self->generateHardcopy() };
72 if ($@) { 131 my ($pdfFileURL) = eval { $self->generateHardcopy() };
132
73 $self->{generationError} = $@; 133 $self->{generationError} = $@ if $@;
134 #warn "pdfFileURL is $pdfFileURL";
135 #warn "generation error is ".$self->{generationError};
136 #warn "hardcopy_format is ".$self->{hardcopy_format};
137 if ($self->{generationError}) {
138 # In this case no correct pdf file was generated.
139 # throw the error up higher.
140 # The error is reported in body.
141 # the tempDir was removed in generateHardcopy
142# } elsif ( $self->{hardcopy_format} eq 'tex') {
143# # Only tex output was asked for, proceed to have the tex output
144# # handled by the subroutine "body".
74 } else { 145 } else {
75 my $filePath = "$tempDir/$fileName"; 146 # information for redirect
76 147 $self->{pdfFileURL} = $pdfFileURL;
77 $r->content_type("application/x-pdf");
78 # as per RFC2183:
79 $r->header_out("Content-Disposition", "attachment; filename=$fileName");
80 $r->send_http_header();
81
82 local *INPUTFILE;
83 open INPUTFILE, "<", $filePath
84 or die "Failed to read $filePath: $!";
85 my $buf;
86 while (read INPUTFILE, $buf, 16384) {
87 print $buf;
88 }
89 close INPUTFILE;
90
91 return;
92 } 148 }
93 } 149 }
94 } 150 }
151}
152
153sub header {
154 my ($self) = @_;
155 my $r = $self->r;
95 156
157 if (exists $self->{pdfFileURL}) {
158 $r->header_out(Location => $self->{pdfFileURL} );
159 $self->{noContent} = 1;
160 return REDIRECT;
161 }
96 $r->content_type("text/html"); 162 $r->content_type("text/html");
97 $r->send_http_header(); 163 $r->send_http_header();
98 $self->template($ce->{templates}->{system}, $singleSet);
99} 164}
100 165
101# ----- 166# -----
102 167
103sub path { 168#sub path {
104 my ($self, undef, $args) = @_; 169# my ($self, $args) = @_;
105 170#
106 my $ce = $self->{courseEnvironment}; 171# my $ce = $self->{ce};
107 my $root = $ce->{webworkURLs}->{root}; 172# my $root = $ce->{webworkURLs}->{root};
108 my $courseName = $ce->{courseName}; 173# my $courseName = $ce->{courseName};
109 return $self->pathMacro($args, 174# return $self->pathMacro($args,
110 "Home" => "$root", 175# "Home" => "$root",
111 $courseName => "$root/$courseName", 176# $courseName => "$root/$courseName",
112 "Hardcopy Generator" => "", 177# "Hardcopy Generator" => "",
113 ); 178# );
114} 179#}
115 180#
116sub title { 181#sub title {
117 return "Hardcopy Generator"; 182# return "Hardcopy Generator";
118} 183#}
119 184
120sub body { 185sub body {
121 my $self = shift; 186 my ($self) = @_;
122 187
123 if ($self->{generationError}) { 188 if ($self->{generationError}) {
124 if (ref $self->{generationError} eq "ARRAY") { 189 if (ref $self->{generationError} eq "ARRAY") {
125 my ($disposition, @rest) = @{$self->{generationError}}; 190 my ($disposition, @rest) = @{$self->{generationError}};
126 if ($disposition eq "PGFAIL") { 191 if ($disposition eq "PGFAIL") {
127 print $self->multiErrorOutput(@{$self->{errors}}); 192 $self->multiErrorOutput(@{$self->{errors}});
128 return ""; 193 return "";
129 } elsif ($disposition eq "FAIL") { 194 } elsif ($disposition eq "FAIL") {
130 print $self->errorOutput(@rest); 195 print $self->errorOutput(@rest);
131 return ""; 196 return "";
132 } elsif ($disposition eq "RETRY") { 197 } elsif ($disposition eq "RETRY") {
137 } else { 202 } else {
138 # not something we were expecting... 203 # not something we were expecting...
139 die $self->{generationError}; 204 die $self->{generationError};
140 } 205 }
141 } 206 }
207 if (@{$self->{warnings}}) {
208 # FIXME: this code will only be reached if there was also a
209 # generation error, because otherwise the module will send
210 # the PDF instead. DAMN!
211 $self->multiWarningOutput(@{$self->{warnings}});
212 }
213# if ($self->{hardcopy_format} eq 'tex') {
214# my $r_tex_content = $self->{r_tex_content};
215# return $$r_tex_content;
216# }
142 $self->displayForm(); 217 $self->displayForm();
143} 218}
144 219
145sub multiErrorOutput($@) { 220sub multiErrorOutput($@) {
146 my ($self, @errors) = @_; 221 my ($self, @errors) = @_;
147 222
148 print CGI::h2("Software Errors"); 223 print CGI::h2("Compile Errors");
149 print CGI::p(<<EOF); 224 print CGI::p(<<EOF);
150WeBWorK has encountered one or more software errors while attempting to process these sets. 225WeBWorK has encountered one or more errors while attempting to process
151It is likely that there are error(s) in the problem itself. 226these homework sets. It is likely that there are errors in the problems
152If you are a student, contact your professor to have the error(s) corrected. 227themselves. If you are a student, contact your professor to have the errors
153If you are a professor, please consut the error output below for more informaiton. 228corrected. If you are a professor, please consult the error output below for
229more information.
154EOF 230EOF
155 foreach my $error (@errors) { 231 foreach my $error (@errors) {
232 my $user = $error->{user};
233 my $userName = $user->user_id . ' ('.$user->first_name.' '.$user->last_name. ')';
156 print CGI::h3("Set: ", $error->{set}, ", Problem: ", $error->{problem}); 234 print CGI::h3("Set: ", $error->{set}, ", Problem: ", $error->{problem}, "for $userName");
157 print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($error->{message})); 235 print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($error->{message}));
158 print CGI::h4("Error context"), CGI::blockquote(CGI::pre($error->{context})); 236 print CGI::h4("Error context"), CGI::blockquote(CGI::pre($error->{context}));
159 } 237 }
160} 238}
161 239
240sub multiWarningOutput($@) {
241 my ($self, @warnings) = @_;
242
243 print CGI::h2("Software Warnings");
244 print CGI::p(<<EOF);
245WeBWorK has encountered one or more warnings while attempting to process these
246homework sets. It is likely that this indicates errors or ambiguitiees in the
247problems themselves. If you are a student, contact your professor to have the
248problems corrected. If you are a professor, please consut the warning output
249below for more informaiton.
250EOF
251 foreach my $warning (@warnings) {
252 print CGI::h3("Set: ", $warning->{set}, ", Problem: ", $warning->{problem});
253 print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($warning->{message}));
254 }
255}
256
162# ----- 257# -----
163 258
164sub displayForm($) { 259sub displayForm($) {
165 my $self = shift; 260 my ($self) = @_;
166 my $r = $self->{r}; 261 my $r = $self->r;
262 my $db = $r->db;
263 my $authz = $r->authz;
264 my $userID = $r->param("user");
265 my $ss= '';
266 my $aa= ' a ';
267 if ($authz->hasPermissions($userID, "download_hardcopy_multiuser")) {
268 $ss= 's';
269 $aa= ' ';
270 }
167 271
168 print CGI::start_p(), "Select the problem sets for which to generate hardcopy versions."; 272 print CGI::start_p(), "Select the homework set$ss for which to generate${aa}hardcopy version$ss.";
169 if ($self->{permissionLevel} > 0) { 273 if ($authz->hasPermissions($userID, "download_hardcopy_multiuser")) {
170 print "You may also select multiple users from the users list. You will receive hardcopy for each (set, user) pair."; 274 print "You may also select multiple users from the users list. You will receive hardcopy for each (set, user) pair.";
171 } 275 }
172 print CGI::end_p(); 276 print CGI::end_p();
173 277
278 my $download_texQ = $authz->hasPermissions($userID, "download_hardcopy_format_tex");
279
280 # ##########construct action URL #################
281 my $ce = $r->ce;
282 my $root = $ce->{webworkURLs}->{root};
283 my $courseName = $ce->{courseName};
284 my $actionURL = "$root/$courseName/hardcopy/";
285 # ################################################
286
287 my $phrase_for_privileged_users = '';
288 $phrase_for_privileged_users ='to privileged users or' if $authz->hasPermissions($userID, "download_hardcopy_multiuser");
289
174 print CGI::start_form(-method=>"POST", -action=>$r->uri); 290 print CGI::start_form(-method=>"POST", -action=>$actionURL);
175 print $self->hidden_authen_fields(); 291 print $self->hidden_authen_fields();
292 print CGI::h3("Options");
293 print CGI::p("You may choose to show any of the following data. Correct answers and solutions are only available $phrase_for_privileged_users after the answer date of the homework set.");
294 print CGI::p(
295 CGI::checkbox(
296 -name => "showCorrectAnswers",
297 -checked => $r->param("showCorrectAnswers") || 0,
298 -label => "Correct answers",
299 ), CGI::br(),
300 CGI::checkbox(
301 -name => "showHints",
302 -checked => $r->param("showHints") || 0,
303 -label => "Hints",
304 ), CGI::br(),
305 CGI::checkbox(
306 -name => "showSolutions",
307 -checked => $r->param("showSolutions") || 0,
308 -label => "Solutions",
309 ),
310 );
176 print CGI::start_table({-width=>"100%"}), CGI::start_Tr({-valign=>"top"}); 311 print CGI::start_table({-width=>"100%"}), CGI::start_Tr({-valign=>"top"});
177 312
178 my $multiSet = $self->{permissionLevel} > 0; 313 my $multiSet = $authz->hasPermissions($userID, "download_hardcopy_multiset");
179 my $multiUser = $self->{permissionLevel} > 0; 314 my $multiUser = $authz->hasPermissions($userID, "download_hardcopy_multiuser");
315 my $preOpenSets = $authz->hasPermissions($userID, "view_unopened_sets");
316 my $unpublishedSets = $authz->hasPermissions($userID, "view_unpublished_sets");
317 my $effectiveUserName = $self->{effectiveUser}->user_id;
318 my @setNames = $db->listUserSets($effectiveUserName);
319 my @sets = $db->getMergedSets( map { [$effectiveUserName, $_] } @setNames ); # checked
320 @sets = grep { defined $_ and ($preOpenSets or $_->open_date < time) and ($unpublishedSets or $_->published) } @sets;
321 @sets = sort { $a->set_id cmp $b->set_id } @sets;
322 @setNames = map( {$_->set_id } @sets ); # get sorted version of setNames
323 my %setLabels = map( {($_->set_id, "set ".$_->set_id )} @sets );
324 my (@users, @userNames,%userLabels);
180 325
326 if ($multiUser) {
327 @userNames = $db->listUsers();
328 @users = $db->getUsers(@userNames); # checked
329 @users = grep { defined $_ } @users;
330 @users = sort { $a->last_name cmp $b->last_name } @users;
331 @userNames = map( {$_->user_id} @users ); # get sorted version of user names
332 %userLabels = map( {($_->user_id , $_->last_name .", ". $_->first_name ." --- ". $_->user_id ) } @users );
333 }
181 # set selection menu 334 # set selection menu
182 { 335 {
183 print CGI::start_td(); 336 print CGI::start_td();
184 print CGI::h3("Sets"); 337 my $number_of_sets = @{$self->{sets}};
185 print CGI::start_table(); 338 print CGI::h3("Sets: $number_of_sets pre-selected");
186 my @sets; 339 print CGI::scrolling_list(-name=>'hcSet',
187 push @sets, $self->{wwdb}->getSet($self->{effectiveUser}->id, $_) 340 -values=>\@setNames,
188 foreach ($self->{wwdb}->getSets($self->{effectiveUser}->id)); 341 -labels=>\%setLabels,
189 @sets = sort { $a->id cmp $b->id } @sets; 342 -size => 10,
190 foreach my $set (@sets) { 343 -multiple => $multiSet,
191 my $checked = grep { $_ eq $set->id } @{$self->{sets}}; 344 -defaults => $self->{sets},
192 my $control; 345 );
193 if (time < $set->open_date) {
194 $control = "";
195 } else {
196 if ($multiSet) {
197 $control = CGI::checkbox(
198 -name=>"hcSet",
199 -value=>$set->id,
200 -label=>"",
201 -checked=>$checked
202 );
203 } else {
204 $control = CGI::radio_group(
205 -name=>"hcSet",
206 -values=>[$set->id],
207 -default=>($checked ? $set->id : "-"),
208 -labels=>{$set->id => ""}
209 );
210 }
211 }
212 print CGI::Tr(CGI::td([
213 $control,
214 $set->id,
215 ]));
216 }
217 print CGI::end_table();
218 print CGI::end_td(); 346 print CGI::end_td();
219 } 347 }
220 348
221 # user selection menu 349 # user selection menu
222 if ($multiUser) { 350 if ($multiUser) {
223 print CGI::start_td(); 351 print CGI::start_td();
224 print CGI::h3("Users"); 352 my $number_of_users = @{$self->{users}};
225 print CGI::start_table(); 353 print CGI::h3("Users: $number_of_users pre-selected");
226 #print CGI::Tr( 354
227 # CGI::td(CGI::checkbox(-name=>"hcAllUsers", -value=>"1", -label=>"")), 355 print CGI::scrolling_list(-name=>'hcUser',
228 # CGI::td({-colspan=>"2"}, "All Users"), 356 -values=>\@userNames,
357 -labels=>\%userLabels,
358 -size => 10,
359 -multiple => 'true',
360 -defaults => $self->{users},
229 #); 361 );
230 #print CGI::Tr(CGI::td({-colspan=>"3"}, "&nbsp;"));
231 my @users;
232 push @users, $self->{cldb}->getUser($_)
233 foreach ($self->{cldb}->getUsers());
234 @users = sort { $a->last_name cmp $b->last_name } @users;
235 foreach my $user (@users) {
236 my $checked = grep { $_ eq $user->id } @{$self->{users}};
237 print CGI::Tr(CGI::td([
238 CGI::checkbox(-name=>"hcUser", -value=>$user->id, -label=>"", -checked=>$checked),
239 $user->id,
240 $user->last_name.", ".$user->first_name,
241 ]));
242 }
243 print CGI::end_table();
244 print CGI::end_td(); 362 print CGI::end_td();
245 } 363 }
246 364
247 print CGI::end_Tr(), CGI::end_table(); 365 print CGI::end_Tr(), CGI::end_table();
366 if ($download_texQ) { # provide choice of pdf or tex output
367 print CGI::p( {-align => "center"},
368 CGI::radio_group(
369 -name=>"hardcopy_format",
370 -values=>['pdf', 'tex'],
371 -default=>'pdf',
372 -labels=>{'tex'=>'TeX','pdf'=>'PDF'}
373 ),
374 );
375 } else { # only pdf output available
376 print CGI::hidden(-name=>'hardcopy_format',-value=>'pdf');
377 }
248 print CGI::p({-align=>"center"}, 378 print CGI::p({-align=>"center"},
249 CGI::submit(-name=>"generateHardcopy", -label=>"Generate Hardcopy")); 379 CGI::submit(-name=>"generateHardcopy", -label=>"Generate Hardcopy"));
250 print CGI::end_form(); 380 print CGI::end_form();
251 381
252 return ""; 382 return "";
253} 383}
254 384
255sub generateHardcopy($) { 385sub generateHardcopy($) {
256 my $self = shift; 386 my ($self) = @_;
387 my $r = $self->r;
388 my $ce = $r->ce;
389 my $authz = $r->authz;
390 my $userID = $r->param("user");
257 my @sets = @{$self->{sets}}; 391 my @sets = @{$self->{sets}};
258 my @users = @{$self->{users}}; 392 my @users = @{$self->{users}};
259 my $multiSet = $self->{permissionLevel} > 0; 393 my $multiSet = $authz->hasPermissions($userID, "download_hardcopy_multiset");
260 my $multiUser = $self->{permissionLevel} > 0; 394 my $multiUser = $authz->hasPermissions($userID, "download_hardcopy_multiuser");
261 # sanity checks 395 # sanity checks
262 unless (@sets) { 396 unless (@sets) {
263 die ["RETRY", "No sets were specified."]; 397 die ["RETRY", "No sets were specified."];
264 } 398 }
265 unless (@users) { 399 unless (@users) {
266 die ["RETRY", "No users were specified."]; 400 die ["RETRY", "No users were specified."];
267 } 401 }
268 402
269 # determine where hardcopy is going to go 403 # determine where hardcopy is going to go
270 #my $tempDir = $self->{courseEnvironment}->{courseDirs}->{html_temp} . "/hardcopy"; 404 my $tempDir = makeTempDirectory($ce->{webworkDirs}->{tmp}, "webwork-hardcopy");
271 my $tempDir = tempdir("webwork-hardcopy-XXXXXXXX", TMPDIR => 1); 405
272 406 # determine name of PDF file #FIXME it might be best to have the effective user in here somewhere
273 # make sure tempDir exists 407 my $courseName = $ce->{courseName};
274 #unless (-e $tempDir) {
275 # if (system "mkdir", "-p", $tempDir) {
276 # die ["FAIL", "Failed to mkdir $tempDir", $!];
277 # }
278 #}
279
280 # determine name of PDF file
281 my $courseName = $self->{courseEnvironment}->{courseName};
282 my $fileNameSet = (@sets > 1 ? "multiset" : $sets[0]); 408 my $fileNameSet = (@sets > 1 ? "multiset" : $sets[0]);
283 my $fileNameUser = (@users > 1 ? "multiuser" : $users[0]); 409 my $fileNameUser = (@users > 1 ? "multiuser" : $users[0]);
284 my $fileName = "$courseName.$fileNameUser.$fileNameSet.pdf"; 410 my $fileName = "$courseName.$fileNameUser.$fileNameSet.pdf";
285 411
286 # for each user ... generate TeX for each set 412 # for each user ... generate TeX for each set
287 my $tex; 413 my $tex;
414 #
415 # the document tex preamble
416 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{preamble});
417 # separate users by page break, or something
288 foreach my $user (@users) { 418 foreach my $user (@users) {
289 $tex .= $self->getMultiSetTeX(@sets); 419 $tex .= $self->getMultiSetTeX($user, @sets);
420 if (@users) {
421 # separate users, but not after the last set
422 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{userDivider});
290 } 423 }
424
425 }
426 # the document postamble
427 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{postamble});
291 428
292 # deal with PG errors 429 # deal with PG errors
293 if (@{$self->{errors}}) { 430 if (@{$self->{errors}}) {
294 die ["PGFAIL"]; 431 die ["PGFAIL"];
295 } 432 }
296 433
297 # "try" to generate pdf 434 # FIXME: add something like:
435 #if (@{$self->{warnings}}) {
436 # $self->{generationWarnings} = 1;
437 #}
438 # ???????
439
440 # "try" to generate pdf or return TeX file
441 my $pdfFileURL = undef;
442 if ($self->{hardcopy_format} eq 'pdf' ) {
443 my $errors = '';
298 eval { $self->latex2pdf($tex, $tempDir, $fileName) }; 444 $pdfFileURL = eval { $self->latex2pdf($tex, $tempDir, $fileName) };
299 if ($@) { 445 if ($@) {
300 die ["FAIL", "Failed to generate PDF from tex", $@]; 446 $errors = $@;
447 #$errors =~ s/\n/<br>/g; # make this readable on HTML FIXME make this a Utils. filter (Error2HTML)
448 # clean up temp directory
449 # FIXME this clean up done in latex2pdf? rmtree($tempDir);
450 die ["FAIL", "Failed to generate PDF from tex", $errors]; #throw error to subroutine body
451 } else {
452 # pass the relative temp file path back up to go subroutine
453 # to have an appropriate redirect generated.
454
455
301 } 456 }
457 } elsif ($self->{hardcopy_format} eq 'tex') {
458
459 my $TeXdownloadFileName = "$courseName.$fileNameUser.$fileNameSet.tex";
302 460
461 # Location for hardcopy file to be downloaded
462 # FIXME this should use surePathToTmpFile
463 my $hardcopyTempDirectory = $ce->{courseDirs}->{html_temp}."/hardcopy";
464 mkdir ($hardcopyTempDirectory) or die "Unable to make $hardcopyTempDirectory" unless -e $hardcopyTempDirectory;
465 my $hardcopyFilePath = "$hardcopyTempDirectory/$TeXdownloadFileName";
466 my $hardcopyFileURL = $ce->{courseURLs}->{html_temp}."/hardcopy/$TeXdownloadFileName";
467 $self->{hardcopyFilePath} = $hardcopyFilePath;
468 $self->{hardcopyFileURL} = $hardcopyFileURL;
469 # write the tex file
470 local *TEX;
471 open TEX, ">", $hardcopyFilePath or die "Failed to open $hardcopyFilePath: $!\n".CGI::br();
472 print TEX $tex;
473 close TEX;
474
475 $pdfFileURL = $hardcopyFileURL;
476
477 if ($PreserveTempFiles) {
478 warn "Temporary directory preserved at '$tempDir'.\n";
479 } else {
480 rmtree($tempDir);
481 }
482
483# $tex = protect_HTML($tex);
484# #$tex =~ s/\n/\<br\>\n/g;
485# $tex = join('', ("<pre>\n",$tex,"\n</pre>\n"));
486# $self->{r_tex_content} = \$tex;
487
488 } else {
489
490
491 die["FAIL", "Hard copy format |".$self->{hardcopy_format}. "| not recognized."];
492
493 }
303 return $tempDir, $fileName; 494 #return $tempDir, $fileName;
495 # return $pdfFilePath;
496 return $pdfFileURL;
304} 497}
305 498
306# ----- 499# -----
307 500
308sub latex2pdf { 501sub latex2pdf {
309 # this is a little ad-hoc function which I will replace with a LaTeX 502 # this is a little ad-hoc function which I will replace with a LaTeX
310 # module at some point (or put it in Utils). 503 # module at some point (or put it in Utils).
311 my ($self, $tex, $fileBase, $fileName) = @_; 504 my ($self, $tex, $tempDir, $fileName) = @_;
505 my $r = $self->r;
506 my $ce = $r->ce;
507
312 my $finalFile = "$fileBase/$fileName"; 508 my $finalFile = "$tempDir/$fileName";
313 my $ce = $self->{courseEnvironment};
314 509
510 # Location for hardcopy file to be downloaded
511 # FIXME this should use surePathToTmpFile
512 my $hardcopyTempDirectory = $ce->{courseDirs}->{html_temp}."/hardcopy";
513 mkdir ($hardcopyTempDirectory) or die "Unable to make $hardcopyTempDirectory" unless -e $hardcopyTempDirectory;
514 my $hardcopyFilePath = "$hardcopyTempDirectory/$fileName";
515 my $hardcopyFileURL = $ce->{courseURLs}->{html_temp}."/hardcopy/$fileName";
516 $self->{hardcopyFilePath} = $hardcopyFilePath;
517 $self->{hardcopyFileURL} = $hardcopyFileURL;
518
315 # create a temporary directory for tex to shit in 519 ## create a temporary directory for tex to shit in
316 my $wd = tempdir("webwork-hardcopy-XXXXXXXX", TMPDIR => 1); 520 # - we're using the existing temp dir. now
521
522 my $wd = $tempDir;
317 my $texFile = "$wd/hardcopy.tex"; 523 my $texFile = "$wd/hardcopy.tex";
318 my $pdfFile = "$wd/hardcopy.pdf"; 524 my $pdfFile = "$wd/hardcopy.pdf";
319 my $logFile = "$wd/hardcopy.log"; 525 my $logFile = "$wd/hardcopy.log";
320 526
321 # write the tex file 527 # write the tex file
322 local *TEX; 528 local *TEX;
323 open TEX, ">", $texFile or die "Failed to open $texFile: $!\n"; 529 open TEX, ">", $texFile or die "Failed to open $texFile: $!\n".CGI::br();
324 print TEX $tex; 530 print TEX $tex;
325 close TEX; 531 close TEX;
326 532
327 # call pdflatex - we don't want to chdir in the mod_perl process, as 533 # call pdflatex - we don't want to chdir in the mod_perl process, as
328 # that might step on the feet of other things (esp. in Apache 2.0) 534 # that might step on the feet of other things (esp. in Apache 2.0)
329 my $pdflatex = $ce->{externalPrograms}->{pdflatex}; 535 my $pdflatex = $ce->{externalPrograms}->{pdflatex};
330 system "cd $wd && $pdflatex $texFile" and die "Failed to call pdflatex: $!\n"; 536 my $pdflatexResult = system "cd $wd && $pdflatex $texFile";
331 537
538 # Even with errors there may be a valid pdfFile. Move it to where we can get it.
332 if (-e $pdfFile) { 539 if (-e $pdfFile) {
333 # move resulting PDF file to appropriate location 540
334 system "/bin/mv", $pdfFile, $finalFile and die "Failed to mv: $!\n"; 541 # moving to course tmp/hardcopy directory
542 system "/bin/mv", $pdfFile, $hardcopyFilePath
543 and die "Failed to mv: $pdfFile to $hardcopyFilePath<br> Quite likely this means that there ".
544 "is not sufficient write permission for some directory.<br>$!\n".CGI::br();
545 }
546 # Alert the world that the tex file did not process perfectly.
547 if ($pdflatexResult) {
548 # something bad happened
549 my $textErrorMessage = "Call to $pdflatex failed: $!\n".CGI::br();
550
551 if (-e $hardcopyFilePath ) {
552 # FIXME Misuse of html tags!!!
553 $textErrorMessage.= "<h4>Some pdf output was produced and is available ". CGI::a({-href=>$hardcopyFileURL},"here.</h4>").CGI::hr();
335 } 554 }
555 # report logfile
556 if (-e $logFile) {
557 $textErrorMessage .= "pdflatex ran, but did not succeed. This suggests an error in the TeX\n".CGI::br();
558 $textErrorMessage .= "version of one of the problems, or a problem with the pdflatex system.\n".CGI::br();
559 my $logFileContents = eval { readTexErrorLog($logFile) };
560 $logFileContents .= CGI::hr().CGI::hr();
561 $logFileContents .= eval { formatTexFile($texFile) };
562 if ($@) {
563 $textErrorMessage .= "Additionally, the pdflatex log file could not be read, though it exists.\n".CGI::br();
564 } else {
565 $textErrorMessage .= "The essential contents of the TeX log are as follows:\n".CGI::hr().CGI::br();
566 $textErrorMessage .= "$logFileContents\n".CGI::br().CGI::br();
567 }
568 } else {
569 $textErrorMessage .= "No log file was created, suggesting that pdflatex never ran. Check the WeBWorK\n".CGI::br();
570 $textErrorMessage .= "configuration to ensure that the path to pdflatex is correct.\n".CGI::br();
571 }
572 die $textErrorMessage;
573 }
336 574
575
576
337 # remove temporary directory 577 ## remove temporary directory
578 if ($PreserveTempFiles) {
579 warn "Working directory preserved at '$wd'.\n";
580 } else {
338 rmtree($wd, 0, 1); 581 rmtree($wd, 0, 0);
582 }
583
339 584
340 -e $finalFile or die "Failed to create $finalFile for no apparent reason.\n"; 585 -e $hardcopyFilePath or die "Failed to create $finalFile for no apparent reason.\n";
586 # return hardcopyFilePath;
587 return $hardcopyFileURL;
341} 588}
342 589
343# ----- 590# -----
591# FIXME move to Utils? probably not
344 592
593sub readTexErrorLog {
594 my $filePath = shift;
595 my $print_error_switch = 0;
596 my $line='';
597 my @message=();
598 #local($/ ) = "\n";
599 open(LOGFILE,"<$filePath") or die "Can't read $filePath";
600 while (<LOGFILE>) {
601 $line = $_;
602 $print_error_switch = 1 if $line =~ /^!/; # after a fatal error start printing messages
603 push(@message, protect_HTML($line)) if $print_error_switch;
604 }
605 close(LOGFILE);
606 join("<br>\n",@message);
607}
608
609sub formatTexFile {
610 my $texFilePath = shift;
611 open (TEXFILE, "$texFilePath")
612 or die "Can't open tex source file: path= $texFilePath: $!";
613
614 my @message = ();
615 push @message, '<BR>\n<h3>TeX Source File:</h3><BR>\n', ;
616
617 my $lineNumber = 1;
618 while (<TEXFILE>) {
619 push @message, protect_HTML("$lineNumber $_")."\n";
620 $lineNumber++;
621 }
622 close(TEXFILE);
623 #push @message, '</pre>';
624 join("<br>\n",@message);
625}
626sub protect_HTML {
627 my $line = shift;
628 chomp($line);
629 $line =~s/\&/&amp;/g;
630 $line =~s/</&lt;/g;
631 $line =~s/>/&gt;/g;
632 $line;
633}
345sub texBlockComment(@) { return "\n".("%"x80)."\n%% ".join("", @_)."\n".("%"x80)."\n\n"; } 634sub texBlockComment(@) { return "\n".("%"x80)."\n%% ".join("", @_)."\n".("%"x80)."\n\n"; }
346 635
347sub getMultiSetTeX { 636sub getMultiSetTeX {
348 my ($self, @sets) = @_; 637 my ($self, $effectiveUserName,@sets) = @_;
349 my $ce = $self->{courseEnvironment}; 638 my $ce = $self->r->ce;
350 my $tex = ""; 639 my $tex = "";
351 640
352 # the document preamble 641
353 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{preamble});
354 642
355 while (defined (my $setName = shift @sets)) { 643 while (defined (my $setName = shift @sets)) {
356 $tex .= $self->getSetTeX($setName); 644 $tex .= $self->getSetTeX($effectiveUserName, $setName);
357 if (@sets) { 645 if (@sets) {
358 # divide sets, but not after the last set 646 # divide sets, but not after the last set
359 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{setDivider}); 647 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{setDivider});
360 } 648 }
361 } 649 }
362 650
363 # the document postamble 651
364 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{postamble});
365 652
366 return $tex; 653 return $tex;
367} 654}
368 655
369sub getSetTeX { 656sub getSetTeX {
370 my ($self, $setName) = @_; 657 my ($self, $effectiveUserName, $setName) = @_;
371 my $ce = $self->{courseEnvironment}; 658 my $r = $self->r;
372 my $wwdb = $self->{wwdb}; 659 my $ce = $r->ce;
373 my $effectiveUserName = $self->{effectiveUser}->id; 660 my $db = $r->db;
374 my @problemNumbers = sort { $a <=> $b } $wwdb->getProblems($effectiveUserName, $setName); 661
662 # FIXME (debug code line next)
663 # print STDERR "Creating set $setName for $effectiveUserName \n";
664
665 # FIXME We could define a default for the effective user if no correct name is passed in.
666 # I'm not sure that it is wise.
667 my $effectiveUser = $db->getUser($effectiveUserName); # checked
668 die "effective user ($effectiveUserName) does not exist."
669 unless defined $effectiveUser;
670
671 my @problemNumbers = sort { $a <=> $b }
672 $db->listUserProblems($effectiveUserName, $setName);
375 673
376 # get header and footer 674 # get header and footer
377 my $setHeader = $wwdb->getSet($effectiveUserName, $setName)->set_header 675 my $set = $db->getMergedSet($effectiveUserName, $setName); # checked
378 || $ce->{webworkFiles}->{hardcopySnippets}->{setHeader}; 676 my $setHeader = (ref($set) && $set->hardcopy_header) ? $set->hardcopy_header: $ce->{webworkFiles}->{hardcopySnippets}->{setHeader};
379 # database doesn't support the following yet :( 677 # database doesn't support the following yet :(
380 #my $setFooter = $wwdb->getSet($effectiveUserName, $setName)->set_footer 678 #my $setFooter = $wwdb->getMergedSet($effectiveUserName, $setName)->set_footer
381 # || $ce->{webworkFiles}->{hardcopySnippets}->{setFooter}; 679 # || $ce->{webworkFiles}->{hardcopySnippets}->{setFooter};
382 # so we don't allow per-set customization, which is probably okay :) 680 # so we don't allow per-set customization, which is probably okay :)
383 my $setFooter = $ce->{webworkFiles}->{hardcopySnippets}->{setFooter}; 681 my $setFooter = $ce->{webworkFiles}->{hardcopySnippets}->{setFooter};
384 682
385 my $tex = ""; 683 my $tex = "";
386 684
387 # render header 685 # render header
388 $tex .= texBlockComment("BEGIN $setName : $setHeader"); 686 $tex .= texBlockComment("BEGIN $setName : $setHeader");
389 $tex .= $self->getProblemTeX($setName, 0, $setHeader); 687 $tex .= $self->getProblemTeX($effectiveUser,$setName, 0, $setHeader);
390 688
391 # render each problem 689 # render each problem
392 while (my $problemNumber = shift @problemNumbers) { 690 while (my $problemNumber = shift @problemNumbers) {
691 #
692 # DPVC -- do problem divider ABOVE the problem, rather than below it
693 #
694 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{problemDivider});
695 #
696 # /DPVC
697 #
393 $tex .= texBlockComment("BEGIN $setName : $problemNumber"); 698 $tex .= texBlockComment("BEGIN $setName : $problemNumber");
394 $tex .= $self->getProblemTeX($setName, $problemNumber); 699 $tex .= $self->getProblemTeX($effectiveUser,$setName, $problemNumber);
700 #
701 # DPVC -- no need for it here since we do it above
702 #
395 if (@problemNumbers) { 703 #if (@problemNumbers) {
396 # divide problems, but not after the last problem 704 # # divide problems, but not after the last problem
397 $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{problemDivider}); 705 # $tex .= $self->texInclude($ce->{webworkFiles}->{hardcopySnippets}->{problemDivider});
398 } 706 #}
707 #
708 # /DPVC
709 #
399 } 710 }
400 711
401 # render footer 712 # render footer
402 $tex .= texBlockComment("BEGIN $setName : $setFooter"); 713 $tex .= texBlockComment("BEGIN $setName : $setFooter");
403 $tex .= $self->getProblemTeX($setName, 0, $setFooter); 714 $tex .= $self->getProblemTeX($effectiveUser,$setName, 0, $setFooter);
404 715
405 return $tex; 716 return $tex;
406} 717}
407 718
408sub getProblemTeX { 719sub getProblemTeX {
720 $WeBWorK::timer1 ->continue("hardcopy: begin processing problem") if defined($WeBWorK::timer1);
409 my ($self, $setName, $problemNumber, $pgFile) = @_; 721 my ($self, $effectiveUser, $setName, $problemNumber, $pgFile) = @_;
410 my $r = $self->{r}; 722 my $r = $self->r;
411 my $ce = $self->{courseEnvironment}; 723 my $ce = $r->ce;
724 my $db = $r->db;
725 my $authz = $r->authz;
726 my $userID = $r->param("user");
727 # Should we provide a default user ? I think not FIXME
412 728
413 my $wwdb = $self->{wwdb}; 729 # $effectiveUser = $self->{effectiveUser} unless defined($effectiveUser);
414 my $cldb = $self->{cldb}; 730 my $permissionLevel = $self->{permissionLevel};
415 my $effectiveUser = $self->{effectiveUser};
416 my $set = $wwdb->getSet($effectiveUser->id, $setName); 731 my $set = $db->getMergedSet($effectiveUser->user_id, $setName); # checked
417 my $psvn = $wwdb->getPSVN($effectiveUser->id, $setName); 732 unless (ref($set) ) { # return error if no set is defined
733 push(@{$self->{warnings}},
734 setName => $setName,
735 problem => 0,
736 message => "No set $setName exists for ".$effectiveUser->first_name.' '.
737 $effectiveUser->last_name.' ('.$effectiveUser->user_id.' )'
738 );
739 return "No set $setName for ".$effectiveUser->user_id;
740 }
741
742 my $preOpenSets = $authz->hasPermissions($userID, "view_unopened_sets");
743 my $unpublishedSets = $authz->hasPermissions($userID, "view_unpublished_sets");
744 unless ( ($preOpenSets or $set->open_date < time) and ($unpublishedSets or $set->published) ) { # return error if set is invisible
745 push(@{$self->{warnings}},
746 setName => $setName,
747 problem => 0,
748 message => "The set $setName is hidden for ".$effectiveUser->first_name.' '.
749 $effectiveUser->last_name.' ('.$effectiveUser->user_id.' )'
750 );
751 return "The set $setName is not yet ready for ".$effectiveUser->user_id;
752 }
753 my $psvn = $set->psvn();
418 754
419 # decide what to do about problem number 755 # decide what to do about problem number
420 my $problem; 756 my $problem;
421 if ($problemNumber) { 757 if ($problemNumber) { # problem number defined and not zero
422 $problem = $wwdb->getProblem($effectiveUser->id, $setName, $problemNumber); 758 $problem = $db->getMergedProblem($effectiveUser->user_id, $setName, $problemNumber); # checked
423 } elsif ($pgFile) { 759 } elsif ($pgFile) {
424 $problem = WeBWorK::Problem->new( 760 $problem = WeBWorK::DB::Record::UserProblem->new(
425 id => 0,
426 set_id => $set->id, 761 set_id => $set->set_id,
762 problem_id => 0,
427 login_id => $effectiveUser->id, 763 login_id => $effectiveUser->user_id,
428 source_file => $pgFile, 764 source_file => $pgFile,
429 # the rest of Problem's fields are not needed, i think 765 # the rest of Problem's fields are not needed, i think
430 ); 766 );
431 } 767 }
432 768 unless (ref($problem) ) { # return error if no problem is defined
769 $problemNumber = 'undefined problem number' unless defined($problemNumber);
770 $setName = 'undefined set Name' unless defined($setName);
771 my $msg = "Problem $setName/problem $problemNumber not assigned to ".
772 $effectiveUser->first_name.' '.
773 $effectiveUser->last_name.' ('.$effectiveUser->user_id.' )';
774 push(@{$self->{warnings}},
775 setName => $setName,
776 problem => $problemNumber,
777 message => $msg,
778 );
779 $msg =~ s/_/\\_/; # escape underbars to protect them from TeX FIXME--this could be more general??
780 return $msg;
781 }
782 # figure out if we're allowed to get solutions and call PG->new accordingly.
783 my $showCorrectAnswers = $r->param("showCorrectAnswers") || 0;
784 my $showHints = $r->param("showHints") || 0;
785 my $showSolutions = $r->param("showSolutions") || 0;
786 unless ($authz->hasPermissions($userID, "view_answers") or time > $set->answer_date) {
787 $showCorrectAnswers = 0;
788 $showSolutions = 0;
789 }
790 ##FIXME -- there can be a problem if the $siteDefaults{timezone} is not defined? Why is this?
791 # why does it only occur with hardcopy?
433 my $pg = WeBWorK::PG->new( 792 my $pg = WeBWorK::PG->new(
434 $ce, 793 $ce,
435 $effectiveUser, 794 $effectiveUser,
436 $r->param('key'), 795 $r->param('key'),
437 $set, 796 $set,
438 $problem, 797 $problem,
439 $psvn, 798 $psvn,
440 {}, # no form fields! 799 {}, # no form fields!
441 { # translation options 800 { # translation options
442 displayMode => "tex", 801 displayMode => "tex",
443 showHints => 0, 802 showHints => ($showHints)? 1:0, # insure that this value is numeric
444 showSolutions => 0, 803 showSolutions => ($showSolutions)? 1:0,
445 processAnswers => 0, 804 processAnswers => ($showCorrectAnswers)? 1:0,
446 }, 805 },
447 ); 806 );
448 807
449 if ($pg->{warnings} ne "") { 808 if ($pg->{warnings} ne "") {
450 push @{$self->{warnings}}, { 809 push @{$self->{warnings}}, {
456 815
457 if ($pg->{flags}->{error_flag}) { 816 if ($pg->{flags}->{error_flag}) {
458 push @{$self->{errors}}, { 817 push @{$self->{errors}}, {
459 set => $setName, 818 set => $setName,
460 problem => $problemNumber, 819 problem => $problemNumber,
820 user => $effectiveUser,
461 message => $pg->{errors}, 821 message => $pg->{errors},
462 context => $pg->{body_text}, 822 context => $pg->{body_text},
463 }; 823 };
464 # if there was an error, body_text contains 824 # if there was an error, body_text contains
465 # the error context, not TeX code 825 # the error context, not TeX code FIXME (should this error context be used?)
466 $pg->{body_text} = undef; 826 $pg->{body_text} = ''; # FIXME using undef causes error unless it is caught undef;
827 } else {
828 # append list of correct answers to body text
829 if ($showCorrectAnswers && $problemNumber != 0) {
830 #
831 # DPVC -- Adjusted spacing here, and added \small and italics.
832 # Put the answer in verbatim mode to make it display as typed
833 # by the author, rather than use hacks for ^ and _. What about
834 # vectors (where TeX will complain about < and > outside of
835 # math mode)? Do we need hacks for them, too?
836 # This also fixes a bug when the answer begins with [
837 # where \item would think this was an optional parameter
838 # (otherwise we need to do "\\item{}$correctanswer\n").
839 #
840 my $correctTeX = "\\par{\\small{\\it Correct Answers:}\n"
841 . "\\vspace{-\\parskip}\\begin{itemize}\n";
842 foreach my $ansName (@{$pg->{flags}->{ANSWER_ENTRY_ORDER}}) {
843 my $correctAnswer = $pg->{answers}->{$ansName}->{correct_ans};
844 #$correctAnswer =~ s/\^/\\\^\{\}/g;
845 #$correctAnswer =~ s/\_/\\\_/g;
846 $correctTeX .= "\\item\\begin{verbatim}$correctAnswer\\end{verbatim}\n";
847 }
848 $correctTeX .= "\\end{itemize}}\\par\n";
849 #
850 # /DPVC
851 #
852 $pg->{body_text} .= $correctTeX;
467 } 853 }
468 854 }
855 $WeBWorK::timer1 ->continue("hardcopy: end processing problem") if defined($WeBWorK::timer1);
469 return $pg->{body_text}; 856 return $pg->{body_text};
470} 857}
471 858
472sub texInclude { 859sub texInclude {
473 my ($self, $texFile) = @_; 860 my ($self, $texFile) = @_;
483 870
484 return $tex; 871 return $tex;
485} 872}
486 873
4871; 8741;
488
489__END__
490
491sub body {
492 my $self = shift;
493
494 STUFF: {
495 my $courseName = $self->{courseEnvironment}->{courseName};
496 my $effectiveUserName = $self->{r}->param("effectiveUser");
497 my @sets = @{$self->{sets}};
498
499 unless (@sets) {
500 print CGI::p("No problem sets were specified.");
501 last STUFF;
502 }
503
504 # determine where hardcopy is going to go
505 my $tempDir = $self->{courseEnvironment}->{courseDirs}->{html_temp}
506 . "/hardcopy";
507 my $tempURL = $self->{courseEnvironment}->{courseURLs}->{html_temp}
508 . "/hardcopy";
509
510 # make sure tempDir exists
511 unless (-e $tempDir) {
512 if (system "mkdir", "-p", $tempDir) {
513 print CGI::p("An error occured while trying to generate your PDF hardcopy:");
514 print CGI::blockquote(CGI::pre("Failed to mkdir $tempDir: $!\n"));
515 }
516 }
517
518 # determine name of PDF file
519 my $fileName;
520 if (@sets > 1) {
521 # multiset output
522 $fileName = "$courseName.$effectiveUserName.multiset.pdf"
523 } elsif (@sets == 1) {
524 # only one set
525 my $setName = $sets[0];
526 $fileName = "$courseName.$effectiveUserName.$setName.pdf";
527 } else {
528 $fileName = "$courseName.$effectiveUserName.pdf";
529 }
530
531 # determine full URL
532 my $fullURL = "$tempURL/$fileName";
533
534 # generate TeX from sets
535 my $tex = $self->getMultiSetTeX(@sets);
536 #print CGI::pre($tex);
537
538 # check for PG errors (fatal)
539 if (@{$self->{errors}}) {
540 my @errors = @{$self->{errors}};
541 print CGI::h2("Software Errors");
542 print CGI::p(<<EOF);
543WeBWorK has encountered one or more software errors while attempting to process these sets.
544It is likely that there are error(s) in the problem itself.
545If you are a student, contact your professor to have the error(s) corrected.
546If you are a professor, please consut the error output below for more informaiton.
547EOF
548 foreach my $error (@errors) {
549 print CGI::h3("Set: ", $error->{set}, ", Problem: ", $error->{problem});
550 print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($error->{message}));
551 print CGI::h4("Error context"), CGI::blockquote(CGI::pre($error->{context}));
552 }
553
554 last STUFF;
555 }
556
557 # "try" to generate hardcopy
558 eval { $self->latex2pdf($tex, $tempDir, $fileName) };
559 if ($@) {
560 print CGI::p("An error occured while trying to generate your PDF hardcopy:");
561 print CGI::blockquote(CGI::pre($@));
562 last STUFF;
563 } else {
564 print CGI::p({-align=>"center"},
565 CGI::big(CGI::a({-href=>$fullURL}, "Download PDF Hardcopy"))
566 );
567 }
568
569 # check for PG warnings (non-fatal)
570 if (@{$self->{warnings}}) {
571 my @warnings = @{$self->{warnings}};
572 print CGI::h2("Software Warnings");
573 print CGI::p(<<EOF);
574WeBWorK has encountered warnings while attempting to process these sets.
575It is likely that this indicates an error or ambiguity in the problem(s) themselves.
576If you are a student, contact your professor to have the problem(s) corrected.
577If you are a professor, please consut the error output below for more informaiton.
578EOF
579 foreach my $warning (@warnings) {
580 print CGI::h3("Set: ", $warning->{set}, ", Problem: ", $warning->{problem});
581 print CGI::h4("Warning messages"), CGI::blockquote(CGI::pre($warning->{message}));
582 }
583 }
584 }
585
586 # feedback form
587 my $ce = $self->{courseEnvironment};
588 my $root = $ce->{webworkURLs}->{root};
589 my $courseName = $ce->{courseName};
590 my $feedbackURL = "$root/$courseName/feedback/";
591 print
592 CGI::startform("POST", $feedbackURL),
593 $self->hidden_authen_fields,
594 CGI::hidden("module", __PACKAGE__),
595 CGI::p({-align=>"right"},
596 CGI::submit(-name=>"feedbackForm", -label=>"Send Feedback")
597 ),
598 CGI::endform();
599
600 return "";
601}

Legend:
Removed from v.737  
changed lines
  Added in v.3377

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9