################################################################################ # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project # $Id$ ################################################################################ package WeBWorK::ContentGenerator; =head1 NAME WeBWorK::ContentGenerator - base class for modules that generate page content. =cut use strict; use warnings; use Apache::Constants qw(:common); use CGI qw(); use URI::Escape; use WeBWorK::Authz; use WeBWorK::DB; use WeBWorK::Utils qw(readFile); ################################################################################ # This is a very unruly file, so I'm going to use very large comments to divide # it into logical sections. ################################################################################ # new(Apache::Request, WeBWorK::CourseEnvironment, WeBWorK::DB) - create a new # instance of a content generator. Usually only called by the dispatcher, although # one might be able to use it for things like "sub-requests". Uh... uh... I have # to think about that one. The dispatcher uses this idiom: # # WeBWorK::ContentGenerator::WHATEVER->new($r, $ce, $db)->go(@whatever); # # and throws away the result ;) # sub new { my ($invocant, $r, $ce, $db) = @_; my $class = ref($invocant) || $invocant; my $self = { r => $r, ce => $ce, db => $db, authz => WeBWorK::Authz->new($r, $ce, $db), noContent => undef, }; bless $self, $class; return $self; } ################################################################################ # Invocation and template processing ################################################################################ # go(@otherArguments) - render a page, using methods from the particular # subclass of ContentGenerator. @otherArguments is passed to each method, so # that the dispatcher can pass CG-specific data. The order of calls looks like # this: # # * &pre_header_initialize - give subclasses a chance to do initialization # necessary for generating the HTTP header. # * &header - this class provides a standard HTTP header with Content-Type # text/html. Subclasses are welcome to overload this for things like # an image-creation content generator or a PDF generator. # In addition, if &header returns a value, that will be the value # returned by the entire PerlHandler. # * &initialize - let subclasses do post-header initialization. # * any "template escapes" defined in the system template and supported by # the subclass. # (if &content exists on a content generator, it is called # and no template processing occurs.) # # If &pre_header_initialize or &header sets $self->{noContent} to a true value, # &initialize will not be run and the content or template processing code # will not be executed. This is probably only desirable if a redirect has been # issued. sub go { my $self = shift; my $r = $self->{r}; my $ce = $self->{ce}; my $returnValue = OK; $self->pre_header_initialize(@_) if $self->can("pre_header_initialize"); my $headerReturn = $self->header(@_); $returnValue = $headerReturn if defined $headerReturn; return $returnValue if $r->header_only or $self->{noContent}; # if the sendFile flag is set, send the file and exit; if ($self->{sendFile}) { return $self->sendFile; } $self->initialize(@_) if $self->can("initialize"); # A content generator will have a "content" method if it does not # wish to be passed through template processing, but wishes to be # completely responsible for it's own output. if ($self->can("content")) { $self->content(@_); } else { # if the content generator specifies a custom template name, use that # field in the $ce->{templates} hash instead of "system" if it exists. my $templateName; if ($self->can("templateName")) { $templateName = $self->templateName; } else { $templateName = "system"; } $templateName = "system" unless exists $ce->{templates}->{$templateName}; $self->template($ce->{templates}->{$templateName}, @_); } return $returnValue; } sub sendFile { my ($self) = @_; my $file = $self->{sendFile}->{source}; return NOT_FOUND unless -e $file; return FORBIDDEN unless -r $file; open my $fh, "<", $file or return SERVER_ERROR; while (<$fh>) { print $_; } close $fh; return OK; } # template(STRING, @otherArguments) - parse a template, looking for escapes of # the form and calling a member function NAME # (if available) for each NAME. The escapes are called like: # # $self->NAME(@otherArguments, \%escapeArguments) # # where @otherArguments originates in the dispatcher and %escapeArguments is # parsed out of the escape itself (i.e. ARG1 => FOO, ARG2 => BAR) # sub template { my ($self, $templateFile) = (shift, shift); my $r = $self->{r}; my $courseEnvironment = $self->{ce}; my @ifstack = (1); # Start off in printing mode # say $ifstack[-1] to get the result of the last <#!--if--> # so even though the variable $/ APPEARS to contain a newline, #