--- trunk/webwork2/lib/WeBWorK/ContentGenerator/Hardcopy.pm 2003/02/18 06:51:49 736 +++ trunk/webwork2/lib/WeBWorK/ContentGenerator/Hardcopy.pm 2003/02/18 07:04:13 737 @@ -15,7 +15,6 @@ use strict; use warnings; use base qw(WeBWorK::ContentGenerator); -#use Apache::Constants qw(:common); use CGI qw(); use File::Path qw(rmtree); use File::Temp qw(tempdir); @@ -24,27 +23,83 @@ use WeBWorK::Form; use WeBWorK::Utils qw(readFile); -sub texBlockComment(@) { return "\n".("%"x80)."\n%% ".join("", @_)."\n".("%"x80)."\n\n"; } - -sub initialize { - my ($self, $singleSet, undef) = @_; +sub go { + my ($self, $singleSet) = @_; my $r = $self->{r}; my $ce = $self->{courseEnvironment}; - my @sets = $r->param("set"); + my @sets = $r->param("hcSet"); + my @users = $r->param("hcUser"); + # add singleSet to the list of sets if (length $singleSet > 0) { $singleSet =~ s/^set//; - unshift @sets, $singleSet; + unshift @sets, $singleSet unless grep { $_ eq $singleSet } @sets; + } + + # default user is the effectiveUser + unless (@users) { + unshift @users, $r->param("effectiveUser"); } - $self->{cldb} = WeBWorK::DB::Classlist->new($ce); - $self->{wwdb} = WeBWorK::DB::WW->new($ce); - $self->{sets} = \@sets; - $self->{errors} = []; + $self->{cldb} = WeBWorK::DB::Classlist->new($ce); + $self->{authdb} = WeBWorK::DB::Auth->new($ce); + $self->{wwdb} = WeBWorK::DB::WW->new($ce); + $self->{user} = $self->{cldb}->getUser($r->param("user")); + $self->{permissionLevel} = $self->{authdb}->getPermissions($r->param("user")); + $self->{effectiveUser} = $self->{cldb}->getUser($r->param("effectiveUser")); + $self->{sets} = \@sets; + $self->{users} = \@users; + $self->{errors} = []; $self->{warnings} = []; + + # security checks - these have to be put somewhere + my $multiSet = $self->{permissionLevel} > 0; + my $multiUser = $self->{permissionLevel} > 0; + if (@sets > 1 and not $multiSet) { + $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for multiple sets. Please select a single set and try again."]; + } + if (@users > 1 and not $multiUser) { + $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for multiple users. Please select a single user and try again."]; + } + if ($users[0] ne $self->{effectiveUser}->id and not $multiUser) { + $self->{generationError} = ["SIMPLE", "You are not permitted to generate hardcopy for other users."]; + } + + unless ($self->{generationError}) { + if ($r->param("generateHardcopy")) { + my ($tempDir, $fileName) = eval { $self->generateHardcopy() }; + if ($@) { + $self->{generationError} = $@; + } else { + my $filePath = "$tempDir/$fileName"; + + $r->content_type("application/x-pdf"); + # as per RFC2183: + $r->header_out("Content-Disposition", "attachment; filename=$fileName"); + $r->send_http_header(); + + local *INPUTFILE; + open INPUTFILE, "<", $filePath + or die "Failed to read $filePath: $!"; + my $buf; + while (read INPUTFILE, $buf, 16384) { + print $buf; + } + close INPUTFILE; + + return; + } + } + } + + $r->content_type("text/html"); + $r->send_http_header(); + $self->template($ce->{templates}->{system}, $singleSet); } +# ----- + sub path { my ($self, undef, $args) = @_; @@ -65,115 +120,189 @@ sub body { my $self = shift; - STUFF: { - my $courseName = $self->{courseEnvironment}->{courseName}; - my $effectiveUserName = $self->{r}->param("effectiveUser"); - my @sets = @{$self->{sets}}; - - unless (@sets) { - print CGI::p("No problem sets were specified."); - last STUFF; - } - - # determine where hardcopy is going to go - my $tempDir = $self->{courseEnvironment}->{courseDirs}->{html_temp} - . "/hardcopy"; - my $tempURL = $self->{courseEnvironment}->{courseURLs}->{html_temp} - . "/hardcopy"; - - # make sure tempDir exists - unless (-e $tempDir) { - if (system "mkdir", "-p", $tempDir) { - print CGI::p("An error occured while trying to generate your PDF hardcopy:"); - print CGI::blockquote(CGI::pre("Failed to mkdir $tempDir: $!\n")); + if ($self->{generationError}) { + if (ref $self->{generationError} eq "ARRAY") { + my ($disposition, @rest) = @{$self->{generationError}}; + if ($disposition eq "PGFAIL") { + print $self->multiErrorOutput(@{$self->{errors}}); + return ""; + } elsif ($disposition eq "FAIL") { + print $self->errorOutput(@rest); + return ""; + } elsif ($disposition eq "RETRY") { + print $self->errorOutput(@rest); + } else { # a "simple" error + print CGI::p(CGI::font({-color=>"red"}, @rest)); } - } - - # determine name of PDF file - my $fileName; - if (@sets > 1) { - # multiset output - $fileName = "$courseName.$effectiveUserName.multiset.pdf" - } elsif (@sets == 1) { - # only one set - my $setName = $sets[0]; - $fileName = "$courseName.$effectiveUserName.$setName.pdf"; } else { - $fileName = "$courseName.$effectiveUserName.pdf"; + # not something we were expecting... + die $self->{generationError}; } + } + $self->displayForm(); +} - # determine full URL - my $fullURL = "$tempURL/$fileName"; - - # generate TeX from sets - my $tex = $self->getMultiSetTeX(@sets); - #print CGI::pre($tex); - - # check for PG errors (fatal) - if (@{$self->{errors}}) { - my @errors = @{$self->{errors}}; - print CGI::h2("Software Errors"); - print CGI::p(<{set}, ", Problem: ", $error->{problem}); - print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($error->{message})); - print CGI::h4("Error context"), CGI::blockquote(CGI::pre($error->{context})); - } - - last STUFF; - } + foreach my $error (@errors) { + print CGI::h3("Set: ", $error->{set}, ", Problem: ", $error->{problem}); + print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($error->{message})); + print CGI::h4("Error context"), CGI::blockquote(CGI::pre($error->{context})); + } +} - # "try" to generate hardcopy - eval { $self->latex2pdf($tex, $tempDir, $fileName) }; - if ($@) { - print CGI::p("An error occured while trying to generate your PDF hardcopy:"); - print CGI::blockquote(CGI::pre($@)); - last STUFF; - } else { - print CGI::p({-align=>"center"}, - CGI::big(CGI::a({-href=>$fullURL}, "Download PDF Hardcopy")) - ); - } +# ----- - # check for PG warnings (non-fatal) - if (@{$self->{warnings}}) { - my @warnings = @{$self->{warnings}}; - print CGI::h2("Software Warnings"); - print CGI::p(<{set}, ", Problem: ", $warning->{problem}); - print CGI::h4("Warning messages"), CGI::blockquote(CGI::pre($warning->{message})); +sub displayForm($) { + my $self = shift; + my $r = $self->{r}; + + print CGI::start_p(), "Select the problem sets for which to generate hardcopy versions."; + if ($self->{permissionLevel} > 0) { + print "You may also select multiple users from the users list. You will receive hardcopy for each (set, user) pair."; + } + print CGI::end_p(); + + print CGI::start_form(-method=>"POST", -action=>$r->uri); + print $self->hidden_authen_fields(); + print CGI::start_table({-width=>"100%"}), CGI::start_Tr({-valign=>"top"}); + + my $multiSet = $self->{permissionLevel} > 0; + my $multiUser = $self->{permissionLevel} > 0; + + # set selection menu + { + print CGI::start_td(); + print CGI::h3("Sets"); + print CGI::start_table(); + my @sets; + push @sets, $self->{wwdb}->getSet($self->{effectiveUser}->id, $_) + foreach ($self->{wwdb}->getSets($self->{effectiveUser}->id)); + @sets = sort { $a->id cmp $b->id } @sets; + foreach my $set (@sets) { + my $checked = grep { $_ eq $set->id } @{$self->{sets}}; + my $control; + if (time < $set->open_date) { + $control = ""; + } else { + if ($multiSet) { + $control = CGI::checkbox( + -name=>"hcSet", + -value=>$set->id, + -label=>"", + -checked=>$checked + ); + } else { + $control = CGI::radio_group( + -name=>"hcSet", + -values=>[$set->id], + -default=>($checked ? $set->id : "-"), + -labels=>{$set->id => ""} + ); + } } + print CGI::Tr(CGI::td([ + $control, + $set->id, + ])); } + print CGI::end_table(); + print CGI::end_td(); } - # feedback form - my $ce = $self->{courseEnvironment}; - my $root = $ce->{webworkURLs}->{root}; - my $courseName = $ce->{courseName}; - my $feedbackURL = "$root/$courseName/feedback/"; - print - CGI::startform("POST", $feedbackURL), - $self->hidden_authen_fields, - CGI::hidden("module", __PACKAGE__), - CGI::p({-align=>"right"}, - CGI::submit(-name=>"feedbackForm", -label=>"Send Feedback") - ), - CGI::endform(); + # user selection menu + if ($multiUser) { + print CGI::start_td(); + print CGI::h3("Users"); + print CGI::start_table(); + #print CGI::Tr( + # CGI::td(CGI::checkbox(-name=>"hcAllUsers", -value=>"1", -label=>"")), + # CGI::td({-colspan=>"2"}, "All Users"), + #); + #print CGI::Tr(CGI::td({-colspan=>"3"}, " ")); + my @users; + push @users, $self->{cldb}->getUser($_) + foreach ($self->{cldb}->getUsers()); + @users = sort { $a->last_name cmp $b->last_name } @users; + foreach my $user (@users) { + my $checked = grep { $_ eq $user->id } @{$self->{users}}; + print CGI::Tr(CGI::td([ + CGI::checkbox(-name=>"hcUser", -value=>$user->id, -label=>"", -checked=>$checked), + $user->id, + $user->last_name.", ".$user->first_name, + ])); + } + print CGI::end_table(); + print CGI::end_td(); + } + + print CGI::end_Tr(), CGI::end_table(); + print CGI::p({-align=>"center"}, + CGI::submit(-name=>"generateHardcopy", -label=>"Generate Hardcopy")); + print CGI::end_form(); return ""; } +sub generateHardcopy($) { + my $self = shift; + my @sets = @{$self->{sets}}; + my @users = @{$self->{users}}; + my $multiSet = $self->{permissionLevel} > 0; + my $multiUser = $self->{permissionLevel} > 0; + # sanity checks + unless (@sets) { + die ["RETRY", "No sets were specified."]; + } + unless (@users) { + die ["RETRY", "No users were specified."]; + } + + # determine where hardcopy is going to go + #my $tempDir = $self->{courseEnvironment}->{courseDirs}->{html_temp} . "/hardcopy"; + my $tempDir = tempdir("webwork-hardcopy-XXXXXXXX", TMPDIR => 1); + + # make sure tempDir exists + #unless (-e $tempDir) { + # if (system "mkdir", "-p", $tempDir) { + # die ["FAIL", "Failed to mkdir $tempDir", $!]; + # } + #} + + # determine name of PDF file + my $courseName = $self->{courseEnvironment}->{courseName}; + my $fileNameSet = (@sets > 1 ? "multiset" : $sets[0]); + my $fileNameUser = (@users > 1 ? "multiuser" : $users[0]); + my $fileName = "$courseName.$fileNameUser.$fileNameSet.pdf"; + + # for each user ... generate TeX for each set + my $tex; + foreach my $user (@users) { + $tex .= $self->getMultiSetTeX(@sets); + } + + # deal with PG errors + if (@{$self->{errors}}) { + die ["PGFAIL"]; + } + + # "try" to generate pdf + eval { $self->latex2pdf($tex, $tempDir, $fileName) }; + if ($@) { + die ["FAIL", "Failed to generate PDF from tex", $@]; + } + + return $tempDir, $fileName; +} + # ----- sub latex2pdf { @@ -213,6 +342,8 @@ # ----- +sub texBlockComment(@) { return "\n".("%"x80)."\n%% ".join("", @_)."\n".("%"x80)."\n\n"; } + sub getMultiSetTeX { my ($self, @sets) = @_; my $ce = $self->{courseEnvironment}; @@ -239,7 +370,7 @@ my ($self, $setName) = @_; my $ce = $self->{courseEnvironment}; my $wwdb = $self->{wwdb}; - my $effectiveUserName = $self->{r}->param("effectiveUser"); + my $effectiveUserName = $self->{effectiveUser}->id; my @problemNumbers = sort { $a <=> $b } $wwdb->getProblems($effectiveUserName, $setName); # get header and footer @@ -281,7 +412,7 @@ my $wwdb = $self->{wwdb}; my $cldb = $self->{cldb}; - my $effectiveUser = $cldb->getUser($r->param("effectiveUser")); + my $effectiveUser = $self->{effectiveUser}; my $set = $wwdb->getSet($effectiveUser->id, $setName); my $psvn = $wwdb->getPSVN($effectiveUser->id, $setName); @@ -354,3 +485,117 @@ } 1; + +__END__ + +sub body { + my $self = shift; + + STUFF: { + my $courseName = $self->{courseEnvironment}->{courseName}; + my $effectiveUserName = $self->{r}->param("effectiveUser"); + my @sets = @{$self->{sets}}; + + unless (@sets) { + print CGI::p("No problem sets were specified."); + last STUFF; + } + + # determine where hardcopy is going to go + my $tempDir = $self->{courseEnvironment}->{courseDirs}->{html_temp} + . "/hardcopy"; + my $tempURL = $self->{courseEnvironment}->{courseURLs}->{html_temp} + . "/hardcopy"; + + # make sure tempDir exists + unless (-e $tempDir) { + if (system "mkdir", "-p", $tempDir) { + print CGI::p("An error occured while trying to generate your PDF hardcopy:"); + print CGI::blockquote(CGI::pre("Failed to mkdir $tempDir: $!\n")); + } + } + + # determine name of PDF file + my $fileName; + if (@sets > 1) { + # multiset output + $fileName = "$courseName.$effectiveUserName.multiset.pdf" + } elsif (@sets == 1) { + # only one set + my $setName = $sets[0]; + $fileName = "$courseName.$effectiveUserName.$setName.pdf"; + } else { + $fileName = "$courseName.$effectiveUserName.pdf"; + } + + # determine full URL + my $fullURL = "$tempURL/$fileName"; + + # generate TeX from sets + my $tex = $self->getMultiSetTeX(@sets); + #print CGI::pre($tex); + + # check for PG errors (fatal) + if (@{$self->{errors}}) { + my @errors = @{$self->{errors}}; + print CGI::h2("Software Errors"); + print CGI::p(<{set}, ", Problem: ", $error->{problem}); + print CGI::h4("Error messages"), CGI::blockquote(CGI::pre($error->{message})); + print CGI::h4("Error context"), CGI::blockquote(CGI::pre($error->{context})); + } + + last STUFF; + } + + # "try" to generate hardcopy + eval { $self->latex2pdf($tex, $tempDir, $fileName) }; + if ($@) { + print CGI::p("An error occured while trying to generate your PDF hardcopy:"); + print CGI::blockquote(CGI::pre($@)); + last STUFF; + } else { + print CGI::p({-align=>"center"}, + CGI::big(CGI::a({-href=>$fullURL}, "Download PDF Hardcopy")) + ); + } + + # check for PG warnings (non-fatal) + if (@{$self->{warnings}}) { + my @warnings = @{$self->{warnings}}; + print CGI::h2("Software Warnings"); + print CGI::p(<{set}, ", Problem: ", $warning->{problem}); + print CGI::h4("Warning messages"), CGI::blockquote(CGI::pre($warning->{message})); + } + } + } + + # feedback form + my $ce = $self->{courseEnvironment}; + my $root = $ce->{webworkURLs}->{root}; + my $courseName = $ce->{courseName}; + my $feedbackURL = "$root/$courseName/feedback/"; + print + CGI::startform("POST", $feedbackURL), + $self->hidden_authen_fields, + CGI::hidden("module", __PACKAGE__), + CGI::p({-align=>"right"}, + CGI::submit(-name=>"feedbackForm", -label=>"Send Feedback") + ), + CGI::endform(); + + return ""; +}