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

Diff of /branches/rel-2-1-patches/webwork-modperl/lib/WeBWorK/ContentGenerator.pm

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

Revision 353 Revision 2260
1################################################################################
2# WeBWorK Online Homework Delivery System
3# Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/
4# $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator.pm,v 1.103 2004/06/06 00:21:26 gage Exp $
5#
6# This program is free software; you can redistribute it and/or modify it under
7# the terms of either: (a) the GNU General Public License as published by the
8# Free Software Foundation; either version 2, or (at your option) any later
9# version, or (b) the "Artistic License" which comes with this package.
10#
11# This program is distributed in the hope that it will be useful, but WITHOUT
12# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the
14# Artistic License for more details.
15################################################################################
16
1package WeBWorK::ContentGenerator; 17package WeBWorK::ContentGenerator;
2 18
3use CGI qw(-compile :html :form); 19=head1 NAME
20
21WeBWorK::ContentGenerator - base class for modules that generate page content.
22
23=head1 SYNOPSIS
24
25 # start with a WeBWorK::Request object: $r
26
27 use WeBWorK::ContentGenerator::SomeSubclass;
28
29 my $cg = WeBWorK::ContentGenerator::SomeSubclass->new($r);
30 my $result = $cg->go();
31
32=head1 DESCRIPTION
33
34WeBWorK::ContentGenerator provides the framework for generating page content.
35"Content generators" are subclasses of this class which provide content for
36particular parts of the system.
37
38Default versions of methods used by the templating system are provided. Several
39useful methods are provided for rendering common output idioms and some
40miscellaneous utilities are provided.
41
42=cut
43
44use strict;
45use warnings;
4use Apache::Constants qw(:common); 46use Apache::Constants qw(:response);
47use Carp;
48use CGI::Pretty qw(*ul *li);
49use URI::Escape;
50use WeBWorK::Template qw(template);
5 51
6# This is a superclass for Apache::WeBWorK's content generators. 52################################################################################
7# You are /definitely/ encouraged to read this file, since there are
8# "abstract" functions here which show aproximately what form you would
9# want over-ridden sub-classes to follow. go() is a particularly pertinent
10# example.
11 53
12# new(Apache::Request, WeBWorK::CourseEnvironment) 54=head1 CONSTRUCTOR
55
56=over
57
58=item new($r)
59
60Creates a new instance of a content generator. Supply a WeBWorK::Request object
61$r.
62
63=cut
64
13sub new($$$) { 65sub new {
14 my $invocant = shift; 66 my ($invocant, $r) = @_;
15 my $class = ref($invocant) || $invocant; 67 my $class = ref($invocant) || $invocant;
16 my $self = {}; 68 my $self = {
17 ($self->{r}, $self->{courseEnvironment}) = @_; 69 r => $r, # this is now a WeBWorK::Request
70 ce => $r->ce(), # these three are here for
71 db => $r->db(), # backward-compatability
72 authz => $r->authz(), # with unconverted CGs
73 noContent => undef, # this should get clobbered at some point
74 };
18 bless $self, $class; 75 bless $self, $class;
19 return $self; 76 return $self;
20} 77}
21 78
79=back
22 80
23# This is a quick and dirty function to print out all (or almost all) of the 81=cut
24# fields in a form in a specified format. As you can see from the print 82
25# statement, it just prints out $begining$name$middle$value$end for every 83################################################################################
26# field who's name doesn't match $qr_omit, a quoted regex. 84
27# In it's current incarnation, it should be called from subclasses only, 85=head1 INVOCATION
28# by saying $self->print_form_data. Of course, you could construct a 86
29# hashref with ->{r} being an Apache::Request, I suppose. 87=over
88
89=item go()
90
91Generates a page, using methods from the particular subclass of ContentGenerator
92that is instantiated. Generatoion is broken up into several steps, to give
93subclasses ample control over the process.
94
95=over
96
97=item 1
98
99go() will attempt to call the method pre_header_initialize(). This method may be
100implemented in subclasses which must do processing before the HTTP header is
101emitted.
102
103=item 2
104
105go() will attempt to call the method header(). This method emits the HTTP
106header. It is defined in this class (see below), but may be overridden in
107subclasses which need to send different header information. For some reason, the
108return value of header() will be used as the result of this function, if it is
109defined.
110
111FIXME: figure out what the deal is with the return value of header(). If we sent
112a header, it's too late to set the status by returning. If we didn't, header()
113didn't perform its function!
114
115=item 3
116
117At this point, go() will terminate if the request is a HEAD request or if the
118field $self->{noContent} contains a true value.
119
120FIXME: I don't think we'll need noContent after reply_with_redirect() is
121adopted by all modules.
122
123=item 4
124
125go() then attempts to call the method initialize(). This method may be
126implemented in subclasses which must do processing after the HTTP header is sent
127but before any content is sent.
128
129=item 6
130
131The method content() is called to send the page content to client.
132
133=back
134
135=cut
136
137sub go {
138 my ($self) = @_;
139 my $r = $self->r;
140 my $ce = $r->ce;
141
142 my $returnValue = OK;
143
144 $self->pre_header_initialize(@_) if $self->can("pre_header_initialize");
145
146 # send a file instead of a normal reply (reply_with_file() sets this field)
147 defined $self->{reply_with_file} and do {
148 return $self->do_reply_with_file($self->{reply_with_file});
149 };
150
151 # send a Location: header instead of a normal reply (reply_with_redirect() sets this field)
152 defined $self->{reply_with_redirect} and do {
153 return $self->do_reply_with_redirect($self->{reply_with_redirect});
154 };
155
156 my $headerReturn = $self->header(@_);
157 $returnValue = $headerReturn if defined $headerReturn;
158 # FIXME: we won't need noContent after reply_with_redirect() is adopted
159 return $returnValue if $r->header_only or $self->{noContent};
160
161 $self->initialize() if $self->can("initialize");
162
163 $self->content();
164
165 return $returnValue;
166}
167
168=item r()
169
170Returns a reference to the WeBWorK::Request object associated with this
171instance.
172
173=cut
174
175sub r {
176 my ($self) = @_;
177
178 return $self->{r};
179}
180
181=item do_reply_with_file($fileHash)
182
183Handler for reply_with_file(), used by go(). DO NOT CALL THIS METHOD DIRECTLY.
184
185=cut
186
187sub do_reply_with_file {
188 my ($self, $fileHash) = @_;
189 my $r = $self->r;
190
191 my $type = $fileHash->{type};
192 my $source = $fileHash->{source};
193 my $name = $fileHash->{name};
194 my $delete_after = $fileHash->{delete_after};
195
196 # if there was a problem, we return here and let go() worry about sending the reply
197 return NOT_FOUND unless -e $source;
198 return FORBIDDEN unless -r $source;
199
200 # open the file now, so we can send the proper error status is we fail
201 open my $fh, "<", $source or return SERVER_ERROR;
202
203 # send our custom HTTP header
204 $r->status(OK);
205 $r->content_type($type);
206 $r->header_out("Content-Disposition" => "attachment; filename=\"$name\"");
207 $r->send_http_header;
208
209 # send the file
210 $r->send_fd($fh);
211
212 # close the file and go home
213 close $fh;
214
215 if ($delete_after) {
216 unlink $source or warn "failed to unlink $source after sending: $!";
217 }
218}
219
220=item do_reply_with_redirect($url)
221
222Handler for reply_with_redirect(), used by go(). DO NOT CALL THIS METHOD DIRECTLY.
223
224=cut
225
226sub do_reply_with_redirect {
227 my ($self, $url) = @_;
228 my $r = $self->r;
229
230 $r->status(REDIRECT);
231 $r->header_out(Location => $url);
232 $r->send_http_header();
233}
234
235=back
236
237=cut
238
239################################################################################
240
241=head1 DATA MODIFIERS
242
243Modifiers allow the caller to register a piece of data for later retrieval in a
244standard way.
245
246=over
247
248=item reply_with_file($type, $source, $name, $delete_after)
249
250Enables file sending mode, causing go() to send the file specified by $source to
251the client after calling pre_header_initialize(). The content type sent is
252$type, and the suggested client-side file name is $name. If $delete_after is
253true, $source is deleted after it is sent.
254
255Must be called before the HTTP header is sent. Usually called from
256pre_header_initialize().
257
258=cut
259
260sub reply_with_file {
261 my ($self, $type, $source, $name, $delete_after) = @_;
262 $delete_after ||= "";
263
264 $self->{reply_with_file} = {
265 type => $type,
266 source => $source,
267 name => $name,
268 delete_after => $delete_after,
269 };
270}
271
272=item reply_with_redirect($url)
273
274Enables redirect mode, causing go() to redirect to the given URL after calling
275pre_header_initialize().
276
277Must be called before the HTTP header is sent. Usually called from
278pre_header_initialize().
279
280=cut
281
282sub reply_with_redirect {
283 my ($self, $url) = @_;
284
285 $self->{reply_with_redirect} = $url;
286}
287
288=item addmessage($message)
289
290Adds a message to the list of messages to be printed by the message() template
291escape handler.
292
293Must be called before the message() template escape is invoked.
294
295=cut
296
297# FIXME: we should probably
298
299sub addmessage {
300 my ($self, $message) = @_;
301 $self->{status_message} .= $message;
302}
303
304=item addgoodmessage($message)
305
306Adds a success message to the list of messages to be printed by the
307message() template escape handler.
308
309=cut
310
311
312sub addgoodmessage {
313 my ($self, $message) = @_;
314 $self->addmessage(CGI::div({class=>"ResultsWithoutError"}, $message));
315}
316
317=item addbadmessage($message)
318
319Adds a failure message to the list of messages to be printed by the
320message() template escape handler.
321
322=cut
323
324
325sub addbadmessage {
326 my ($self, $message) = @_;
327 $self->addmessage(CGI::div({class=>"ResultsWithError"}, $message));
328}
329
330=back
331
332=cut
333
334################################################################################
335
336=head1 STANDARD METHODS
337
338The following are the standard content generator methods. Some are defined here,
339but may be overridden in a subclass. Others are not defined unless they are
340defined in a subclass.
341
342=over
343
344=item pre_header_initialize()
345
346Not defined in this package.
347
348May be defined by a subclass to perform any processing that must occur before
349the HTTP header is sent.
350
351=cut
352
353#sub pre_header_initialize { }
354
355=item header()
356
357Defined in this package.
358
359Generates and sends a default HTTP header, specifying the "text/html" content
360type.
361
362=cut
363
364sub header {
365 my $self = shift;
366 my $r = $self->r;
367
368 $r->content_type("text/html");
369 $r->send_http_header();
370 return OK;
371}
372
373=item initialize()
374
375Not defined in this package.
376
377May be defined by a subclass to perform any processing that must occur after the
378HTTP header is sent but before any content is sent.
379
380=cut
381
382#sub initialize { }
383
384=item content()
385
386Defined in this package.
387
388Print the content of the generated page.
389
390The implementation in this package uses WeBWorK::Template to define the content
391of the page. See WeBWorK::Template for details.
392
393If a method named templateName() exists, it it called to determine the name of
394the template to use. If not, the default template, "system", is used. The
395location of the template is looked up in the course environment.
396
397=cut
398
399sub content {
400 my ($self) = @_;
401 my $ce = $self->r->ce;
402
403 # if the content generator specifies a custom template name, use that
404 # field in the $ce->{templates} hash instead of "system" if it exists.
405 my $templateName;
406 if ($self->can("templateName")) {
407 $templateName = $self->templateName;
408 } else {
409 $templateName = "system";
410 }
411 $templateName = "system" unless exists $ce->{templates}->{$templateName};
412 template($ce->{templates}->{$templateName}, $self);
413}
414
415=back
416
417=cut
418
419# ------------------------------------------------------------------------------
420
421=head2 Template escape handlers
422
423Template escape handlers are invoked when the template processor encounters a
424matching escape sequence in the template. The escapse sequence's arguments are
425passed to the methods as a reference to a hash.
426
427For more information, refer to WeBWorK::Template.
428
429The following template escapes handlers are defined here or may be defined in
430subclasses. For methods that are not defined in this package, the documentation
431defines the interface and behavior that any subclass implementation must follow.
432
433=over
434
435=item head()
436
437Not defined in this package.
438
439Any tags that should appear in the HEAD of the document.
440
441=cut
442
443#sub head { }
444
445=item info()
446
447Not defined in this package.
448
449Auxiliary information related to the content displayed in the C<body>.
450
451=cut
452
453#sub info { }
454
455=item links()
456
457Defined in this package.
458
459Links that should appear on every page.
460
461=cut
462
463sub links {
464 my ($self) = @_;
465 my $r = $self->r;
466 my $db = $r->db;
467 my $urlpath = $r->urlpath;
468
469 # we're linking to other places in the same course, so grab the courseID from the current path
470 my $courseID = $urlpath->arg("courseID");
471
472 # to make things more concise
473 my %args = ( courseID => $courseID );
474 my $pfx = "WeBWorK::ContentGenerator::";
475
476 my $sets = $urlpath->newFromModule("${pfx}ProblemSets", %args);
477 my $options = $urlpath->newFromModule("${pfx}Options", %args);
478 my $grades = $urlpath->newFromModule("${pfx}Grades", %args);
479 my $logout = $urlpath->newFromModule("${pfx}Logout", %args);
480
481 print "\n<!-- BEGIN " . __PACKAGE__ . "::links -->\n";
482 print CGI::start_ul({class=>"LinksMenu"});
483 print CGI::li(CGI::span({style=>"font-size:larger"},
484 CGI::a({href=>$self->systemLink($sets)}, 'Problem&nbsp;Sets')));
485 print CGI::li(CGI::a({href=>$self->systemLink($options)}, space2nbsp($options->name)));
486 print CGI::li(CGI::a({href=>$self->systemLink($grades)}, space2nbsp($grades->name)));
487 print CGI::li(CGI::a({href=>$self->systemLink($logout)}, space2nbsp($logout->name)));
488
489 my $PermissionLevel = $db->getPermissionLevel($r->param("user")); # checked
490 my $permLevel = $PermissionLevel ? $PermissionLevel->permission : 0;
491
492 if ($permLevel > 0) {
493 my $ipfx = "${pfx}Instructor::";
494
495 my $userID = $r->param("effectiveUser");
496 my $setID = $urlpath->arg("setID");
497 $setID = "" if (defined $setID && !(grep /$setID/, $db->listUserSets($userID)));
498 my $problemID = $urlpath->arg("problemID");
499 $problemID = "" if (defined $problemID && !(grep /$problemID/, $db->listUserProblems($userID, $setID)));
500
501 my $instr = $urlpath->newFromModule("${ipfx}Index", %args);
502 my $addUsers = $urlpath->newFromModule("${ipfx}AddUsers", %args);
503 my $userList = $urlpath->newFromModule("${ipfx}UserList", %args);
504
505 # set list links
506 my $setList = $urlpath->newFromModule("${ipfx}ProblemSetList", %args);
507 my $setDetail = $urlpath->newFromModule("${ipfx}ProblemSetEditor", %args, setID => $setID);
508 my $problemEditor = $urlpath->newFromModule("${ipfx}PGProblemEditor", %args, setID => $setID, problemID => $problemID);
509
510 my $maker = $urlpath->newFromModule("${ipfx}SetMaker", %args);
511 my $assigner = $urlpath->newFromModule("${ipfx}Assigner", %args);
512 my $mail = $urlpath->newFromModule("${ipfx}SendMail", %args);
513 my $scoring = $urlpath->newFromModule("${ipfx}Scoring", %args);
514
515 # statistics links
516 my $stats = $urlpath->newFromModule("${ipfx}Stats", %args);
517 my $userStats = $urlpath->newFromModule("${ipfx}Stats", %args, statType => "student", userID => $userID);
518 my $setStats = $urlpath->newFromModule("${ipfx}Stats", %args, statType => "set", setID => $setID);
519
520 # progress links
521 my $progress = $urlpath->newFromModule("${ipfx}StudentProgress", %args);
522 my $userProgress = $urlpath->newFromModule("${ipfx}StudentProgress", %args, statType => "student", userID => $userID);
523 my $setProgress = $urlpath->newFromModule("${ipfx}StudentProgress", %args, statType => "set", setID => $setID);
524
525
526 my $files = $urlpath->newFromModule("${ipfx}FileXfer", %args);
527
528 print CGI::hr();
529 print CGI::start_li();
530 print CGI::span({style=>"font-size:larger"}, CGI::a({href=>$self->systemLink($instr)}, space2nbsp($instr->name)));
531 print CGI::start_ul();
532 print CGI::li(CGI::a({href=>$self->systemLink($addUsers)}, space2nbsp($addUsers->name)));
533 print CGI::li(CGI::a({href=>$self->systemLink($userList)}, space2nbsp($userList->name)));
534 print CGI::start_li();
535 print CGI::a({href=>$self->systemLink($setList)}, space2nbsp($setList->name));
536 if (defined $setID and $setID ne "") {
537 print CGI::start_ul();
538 print CGI::start_li();
539 print CGI::a({href=>$self->systemLink($setDetail)}, $setID);
540 if (defined $problemID and $problemID ne "") {
541 print CGI::ul(
542 CGI::li(CGI::a({href=>$self->systemLink($problemEditor)}, $problemID))
543 );
544 }
545 print CGI::end_li();
546 print CGI::end_ul();
547 }
548 print CGI::end_li();
549 print CGI::li(CGI::a({href=>$self->systemLink($maker)}, space2nbsp($maker->name)));
550 print CGI::li(CGI::a({href=>$self->systemLink($assigner)}, space2nbsp($assigner->name)));
551 print CGI::li(CGI::a({href=>$self->systemLink($mail)}, space2nbsp($mail->name)));
552 print CGI::li(CGI::a({href=>$self->systemLink($scoring)}, space2nbsp($scoring->name)));
553 print CGI::start_li();
554 print CGI::a({href=>$self->systemLink($stats)}, space2nbsp($stats->name));
555 if (defined $userID and $userID ne "") {
556 print CGI::ul(
557 CGI::li(CGI::a({href=>$self->systemLink($userStats)}, $userID))
558 );
559 }
560 if (defined $setID and $setID ne "") {
561 print CGI::ul(
562 CGI::li(CGI::a({href=>$self->systemLink($setStats)}, space2nbsp($setID)))
563 );
564 }
565 print CGI::end_li();
566
567 ## Added Link for Student Progress
568 print CGI::start_li();
569 print CGI::a({href=>$self->systemLink($progress)}, space2nbsp($progress->name));
570 if (defined $userID and $userID ne "") {
571 print CGI::ul(
572 CGI::li(CGI::a({href=>$self->systemLink($userProgress)}, $userID))
573 );
574 }
575 if (defined $setID and $setID ne "") {
576 print CGI::ul(
577 CGI::li(CGI::a({href=>$self->systemLink($setProgress)}, space2nbsp($setID)))
578 );
579 }
580 print CGI::end_li();
581
582 print CGI::li(CGI::a({href=>$self->systemLink($files)}, space2nbsp($files->name)));
583 print CGI::end_ul();
584 print CGI::end_li();
585 }
586
587 print CGI::end_ul();
588 print "<!-- end " . __PACKAGE__ . "::links -->\n";
589
590 return "";
591}
592
593=item loginstatus()
594
595Defined in this package.
596
597Print a notification message announcing the current real user and effective
598user, a link to stop acting as the effective user, and a link to logout.
599
600=cut
601
602sub loginstatus {
603 my ($self) = @_;
604 my $r = $self->r;
605 my $urlpath = $r->urlpath;
606
607 my $key = $r->param("key");
608
609 if ($key) {
610 my $courseID = $urlpath->arg("courseID");
611 my $userID = $r->param("user");
612 my $eUserID = $r->param("effectiveUser");
613
614 my $stopActingURL = $self->systemLink($urlpath, # current path
615 params => { effectiveUser => $userID },
616 );
617 my $logoutURL = $self->systemLink($urlpath->newFromModule(__PACKAGE__ . "::Logout", courseID => $courseID));
618
619 print "\n<!-- BEGIN " . __PACKAGE__ . "::loginstatus -->\n";
620
621 print "Logged in as $userID. ", CGI::br();
622 print CGI::a({href=>$logoutURL}, "Log Out");
623
624 if ($eUserID ne $userID) {
625 print " | Acting as $eUserID. ";
626 print CGI::a({href=>$stopActingURL}, "Stop Acting");
627 }
628
629 print "<!-- END " . __PACKAGE__ . "::loginstatus -->\n";
630 }
631
632 return "";
633}
634
635=item nav($args)
636
637Not defined in this package.
638
639Links to the previous, next, and parent objects.
640
641$args is a reference to a hash containing the following fields:
642
643 style => text|image
644 imageprefix => prefix to prepend to base image URL
645 imagesuffix => suffix to append to base image URL
646 separator => HTML to place in between links
647
648If C<style> is "image", image URLs are constructed by prepending C<imageprefix>
649and postpending C<imagesuffix> to the image base names defined by the
650implementor. (Examples of base names include "Prev", "Next", "ProbSet", and
651"Up"). Each concatenated string should form an absolute URL to an image file.
652For example:
653
654 <!--#nav style="images" imageprefix="/webwork2_files/images/nav"
655 imagesuffix=".gif" separator=" "-->
656
657=cut
658
659#sub nav { }
660
661=item options()
662
663Not defined in this package.
664
665Print an auxiliary options form, related to the content displayed in the
666C<body>.
667
668=item path($args)
669
670Defined in this package.
671
672Print "breadcrubs" from the root of the virtual hierarchy to the current page.
673$args is a reference to a hash containing the following fields:
674
675 style => type of separator: text|image
676 image => if style=image, URL of image to use as path separator
677 text => if style=text, text to use as path separator
678 if style=image, the ALT text of each separator image
679 textonly => suppress all HTML, return only plain text
680
681The implementation in this package takes information from the WeBWorK::URLPath
682associated with the current request.
683
684=cut
685
686sub path {
687 my ($self, $args) = @_;
688 my $r = $self->r;
689
690 my @path;
691
692 my $urlpath = $r->urlpath;
693 do {
694 unshift @path, $urlpath->name, $r->location . $urlpath->path;
695 } while ($urlpath = $urlpath->parent);
696
697 $path[$#path] = ""; # we don't want the last path element to be a link
698
699 #print "\n<!-- BEGIN " . __PACKAGE__ . "::path -->\n";
700 print $self->pathMacro($args, @path);
701 #print "<!-- END " . __PACKAGE__ . "::path -->\n";
702
703 return "";
704}
705
706=item siblings()
707
708Not defined in this package.
709
710Print links to siblings of the current object.
711
712=cut
713
714#sub siblings { }
715
716=item timestamp()
717
718Defined in this package.
719
720Display the current time and date using default format "3:37pm on Jan 7, 2004".
721The display format can be adjusted by giving a style in the template.
722For example,
723
724 <!--#timestamp style="%m/%d/%y at %I:%M%P"-->
725
726will give standard WeBWorK time format. Wording and other formatting
727can be done in the template itself.
728=cut
729
730sub timestamp {
731 my ($self, $args) = @_;
732 my $formatstring = "%l:%M%P on %b %e, %Y";
733 $formatstring = $args->{style} if(defined($args->{style}));
734 return(Date::Format::time2str($formatstring, time()));
735}
736
737=item submiterror()
738
739Defined in this package.
740
741Print any error messages resulting from the last form submission.
742
743This method is deprecated -- use message() instead
744
745The implementation in this package prints the value of the field
746$self->{submitError}, if it is present.
747
748=cut
749
750sub submiterror {
751 my ($self) = @_;
752
753 print "\n<!-- BEGIN " . __PACKAGE__ . "::submiterror -->\n";
754 print $self->{submitError} if exists $self->{submitError};
755 print "<!-- END " . __PACKAGE__ . "::submiterror -->\n";
756
757 return "";
758}
759
760=item message()
761
762Defined in this package.
763
764Print any messages (error or non-error) resulting from the last form submission.
765This could be used to give Sucess and Failure messages after an action is performed by a module.
766
767The implementation in this package prints the value of the field
768$self->{status_message}, if it is present.
769
770=cut
771
772sub message {
773 my ($self) = @_;
774
775 print "\n<!-- BEGIN " . __PACKAGE__ . "::message -->\n";
776 print $self->{status_message} if exists $self->{status_message};
777 print "<!-- END " . __PACKAGE__ . "::message -->\n";
778
779 return "";
780}
781
782=item title()
783
784Defined in this package.
785
786Print the title of the current page.
787
788The implementation in this package takes information from the WeBWorK::URLPath
789associated with the current request.
790
791=cut
792
793sub title {
794 my ($self, $args) = @_;
795 my $r = $self->r;
796
797
798 #print "\n<!-- BEGIN " . __PACKAGE__ . "::title -->\n";
799 print $r->urlpath->name;
800 #print "<!-- END " . __PACKAGE__ . "::title -->\n";
801
802 return "";
803}
804
805=item warnings()
806
807Defined in this package.
808
809Print accumulated warnings.
810
811The implementation in this package checks for a note in the request named
812"warnings". If present, its contents are formatted and returned.
813
814=cut
815
816=item helpMacro($name)
817
818This escape is represented by a question mark which links to an html page in the
819helpFiles directory. Currently the link is made to the file $name.html
820
821=cut
822
823sub helpMacro {
824 my $self = shift;
825 my $name = shift;
826 my $ce = $self->r->ce;
827 my $basePath = $ce->{webworkDirs}->{local_help};
828 $name = 'no_help' unless -e "$basePath/$name.html";
829 my $path = "$basePath/$name.html";
830 my $url = $ce->{webworkURLs}->{local_help}."/$name.html";
831 my $imageURL = $ce->{webworkURLs}->{htdocs}."/images/question_mark.png";
832 return CGI::a({href => $url,
833 target => 'ww_help',
834 onclick => "window.open(this.href,this.target,'width=550,height=350,scrollbars=yes,resizable=on')"},
835 CGI::img({src=>$imageURL}));
836}
837
838sub help {
839 my $self = shift;
840 my $args = shift;
841 my $name = $args->{name};
842 $name = lc($self->r->urlpath->name) unless defined($name);
843 $name =~ s/\s/_/g;
844 $self->helpMacro($name);
845}
846
847sub warnings {
848 my ($self) = @_;
849 my $r = $self->r;
850
851 print "\n<!-- BEGIN " . __PACKAGE__ . "::warnings -->\n";
852 print $self->warningOutput($r->notes("warnings")) if $r->notes("warnings");
853 print "<!-- END " . __PACKAGE__ . "::warnings -->\n";
854
855 return "";
856}
857
858=back
859
860=cut
861
862# ------------------------------------------------------------------------------
863
864=head2 Conditional predicates
865
866Conditional predicate methods are invoked when the C<#if> escape sequence is
867encountered in the template. If a method named C<if_predicate> is defined in
868here or in the instantiated subclass, it is invoked.
869
870The following predicates are currently defined:
871
872=over
873
874=item if_can($function)
875
876If a function named $function is present in the current content generator (or
877any superclass), a true value is returned. Otherwise, a false value is returned.
878
879The implementation in this package uses the method UNIVERSAL->can(function) to
880arrive at the result.
881
882A subclass could redefine this method to, for example, "hide" a method from the
883template:
884
885 sub if_can {
886 my ($self, $arg) = @_;
887
888 if ($arg eq "floobar") {
889 return 0;
890 } else {
891 return $self->SUPER::if_can($arg);
892 }
893 }
894
895=cut
896
897sub if_can {
898 my ($self, $arg) = @_;
899
900 return $self->can($arg) ? 1 : 0;
901}
902
903=item if_loggedin($arg)
904
905If the user is currently logged in, $arg is returned. Otherwise, the inverse of
906$arg is returned.
907
908The implementation in this package always returns $arg, since most content
909generators are only reachable when the user is authenticated. It is up to
910classes that can be reached without logging in to override this method and
911provide the correct behavior.
912
913This is suboptimal, and may change in the future.
914
915=cut
916
917sub if_loggedin {
918 my ($self, $arg) = @_;
919
920 return $arg;
921}
922
923=item if_submiterror($arg)
924
925If the last form submission generated an error, $arg is returned. Otherwise, the
926inverse of $arg is returned.
927
928The implementation in this package checks for the field $self->{submitError} to
929determine if an error condition is present.
930
931If a subclass uses some other method to classify submission results, this method could be
932redefined to handle that variance:
933
934 sub if_submiterror {
935 my ($self, $arg) = @_;
936
937 my $status = $self->{processReturnValue};
938 if ($status != 0) {
939 return $arg;
940 } else {
941 return !$arg;
942 }
943 }
944
945=cut
946
947sub if_submiterror {
948 my ($self, $arg) = @_;
949
950 if (exists $self->{submitError}) {
951 return $arg;
952 } else {
953 return !$arg;
954 }
955}
956
957=item if_message($arg)
958
959If the last form submission generated a message, $arg is returned. Otherwise, the
960inverse of $arg is returned.
961
962The implementation in this package checks for the field $self->{status_message} to
963determine if a message is present.
964
965If a subclass uses some other method to classify submission results, this method could be
966redefined to handle that variance:
967
968 sub if_message {
969 my ($self, $arg) = @_;
970
971 my $status = $self->{processReturnValue};
972 if ($status != 0) {
973 return $arg;
974 } else {
975 return !$arg;
976 }
977 }
978
979=cut
980
981sub if_message {
982 my ($self, $arg) = @_;
983
984 if (exists $self->{status_message}) {
985 return $arg;
986 } else {
987 return !$arg;
988 }
989}
990
991=item if_warnings
992
993If warnings have been emitted while handling this request, $arg is returned.
994Otherwise, the inverse of $arg is returned.
995
996The implementation in this package checks for a note in the request named
997"warnings". This is set by the WARN handler in Apache::WeBWorK when a warning is
998handled.
999
1000=cut
1001
1002sub if_warnings {
1003 my ($self, $arg) = @_;
1004 my $r = $self->r;
1005
1006 if ($r->notes("warnings")) {
1007 return $arg;
1008 } else {
1009 !$arg;
1010 }
1011}
1012
1013=back
1014
1015=cut
1016
1017################################################################################
1018
1019=head1 HTML MACROS
1020
1021Various routines are defined in this package for rendering common WeBWorK
1022idioms.
1023
1024FIXME: some of these should be moved to WeBWorK::HTML:: modules!
1025
1026# ------------------------------------------------------------------------------
1027
1028=head2 Template escape handler macros
1029
1030These methods are used by implementations of the escape sequence handlers to
1031maintain a consistent style.
1032
1033=over
1034
1035=item pathMacro($args, @path)
1036
1037Helper macro for the C<#path> escape sequence: $args is a hash reference
1038containing the "style", "image", "text", and "textonly" arguments to the escape.
1039@path consists of ordered key-value pairs of the form:
1040
1041 "Page Name" => URL
1042
1043If the page should not have a link associated with it, the URL should be left
1044empty. Authentication data is added to each URL so you don't have to. A fully-
1045formed path line is returned, suitable for returning by a function implementing
1046the C<#path> escape.
1047
1048FIXME: authentication data probably shouldn't be added here any more, now that
1049we have systemLink().
1050
1051=cut
1052
1053sub pathMacro {
1054 my ($self, $args, @path) = @_;
1055 my %args = %$args;
1056 $args{style} = "text" if $args{textonly};
1057
1058 my $auth = $self->url_authen_args;
1059 my $sep;
1060 if ($args{style} eq "image") {
1061 $sep = CGI::img({-src=>$args{image}, -alt=>$args{text}});
1062 } else {
1063 $sep = $args{text};
1064 }
1065
1066 my @result;
1067 while (@path) {
1068 my $name = shift @path;
1069 my $url = shift @path;
1070 if ($url and not $args{textonly}) {
1071 push @result, CGI::a({-href=>"$url?$auth"}, $name);
1072 } else {
1073 push @result, $name;
1074 }
1075 }
1076
1077 return join($sep, @result), "\n";
1078}
1079
1080=item siblingsMacro(@siblings)
1081
1082Helper macro for the C<#siblings> escape sequence. @siblings consists of ordered
1083key-value pairs of the form:
1084
1085 "Sibling Name" => URL
1086
1087If the sibling should not have a link associated with it, the URL should be left
1088empty. Authentication data is added to each URL so you don't have to. A fully-
1089formed siblings block is returned, suitable for returning by a function
1090implementing the C<#siblings> escape.
1091
1092FIXME: authentication data probably shouldn't be added here any more, now that
1093we have systemLink().
1094
1095=cut
1096
1097sub siblingsMacro {
1098 my ($self, @siblings) = @_;
1099
1100 my $auth = $self->url_authen_args;
1101 my $sep = CGI::br();
1102
1103 my @result;
1104 while (@siblings) {
1105 my $name = shift @siblings;
1106 my $url = shift @siblings;
1107 push @result, $url
1108 ? CGI::a({-href=>"$url?$auth"}, $name)
1109 : $name;
1110 }
1111
1112 return join($sep, @result) . "\n";
1113}
1114
1115
1116
1117=item navMacro($args, $tail, @links)
1118
1119Helper macro for the C<#nav> escape sequence: $args is a hash reference
1120containing the "style", "imageprefix", "imagesuffix", and "separator" arguments
1121to the escape. @siblings consists of ordered tuples of the form:
1122
1123 "Link Name", URL, ImageBaseName
1124
1125If the sibling should not have a link associated with it, the URL should be left
1126empty. ImageBaseName is placed between the C<imageprefix> and C<imagesuffix>.
1127Authentication data is added to each URL so you don't have to. $tail is appended
1128to each URL, after the authentication information. A fully-formed nav line is
1129returned, suitable for returning by a function implementing the C<#nav> escape.
1130
1131=cut
1132
1133sub navMacro {
1134 my ($self, $args, $tail, @links) = @_;
1135 my $r = $self->r;
1136 my $ce = $r->ce;
1137 my %args = %$args;
1138
1139 my $auth = $self->url_authen_args;
1140 my $prefix = $ce->{webworkURLs}->{htdocs}."/images";
1141
1142 my @result;
1143 while (@links) {
1144 my $name = shift @links;
1145 my $url = shift @links;
1146 my $img = shift @links;
1147 my $html =
1148 ($img && $args{style} eq "images")
1149 ? CGI::img(
1150 {src=>($prefix."/".$img.$args{imagesuffix}),
1151 border=>"",
1152 alt=>"$name"})
1153 : $name;
1154 unless($img && !$url) {
1155 push @result, $url
1156 ? CGI::a({-href=>"$url?$auth$tail"}, $html)
1157 : $html;
1158 }
1159 }
1160
1161 return join($args{separator}, @result) . "\n";
1162}
1163
1164=back
1165
1166=cut
1167
1168# ------------------------------------------------------------------------------
1169
1170=head2 Parameter management
1171
1172Methods for formatting request parameters as hidden form fields or query string
1173fragments.
1174
1175=over
1176
1177=item hidden_fields(@fields)
1178
1179Return hidden <INPUT> tags for each field mentioned in @fields (or all fields if
1180list is empty), taking data from the current request.
1181
1182=cut
1183
1184sub hidden_fields {
1185 my ($self, @fields) = @_;
1186 my $r = $self->r;
1187
1188 @fields = $r->param unless @fields;
1189
1190 my $html = "";
1191 foreach my $param (@fields) {
1192 my @values = $r->param($param);
1193 $html .= CGI::hidden($param, @values);
1194 }
1195 return $html;
1196}
1197
1198=item hidden_authen_fields()
1199
1200Use hidden_fields to return hidden <INPUT> tags for request fields used in
1201authentication.
1202
1203=cut
1204
1205sub hidden_authen_fields {
1206 my ($self) = @_;
1207
1208 return $self->hidden_fields("user", "effectiveUser", "key");
1209}
1210
1211=item url_args(@fields)
1212
1213Return a URL query string (without the leading `?') containing values for each
1214field mentioned in @fields, or all fields if list is empty. Data is taken from
1215the current request.
1216
1217=cut
1218
1219sub url_args {
1220 my ($self, @fields) = @_;
1221 my $r = $self->r;
1222
1223 @fields = $r->param unless @fields;
1224
1225 my @pairs;
1226 foreach my $param (@fields) {
1227 my @values = $r->param($param);
1228 foreach my $value (@values) {
1229 push @pairs, uri_escape($param) . "=" . uri_escape($value);
1230 }
1231 }
1232
1233 return join("&", @pairs);
1234}
1235
1236=item url_authen_args()
1237
1238Use url_args to return a URL query string for request fields used in
1239authentication.
1240
1241=cut
1242
1243sub url_authen_args {
1244 my ($self) = @_;
1245
1246 return $self->url_args("user", "effectiveUser", "key");
1247}
1248
1249=item print_form_data($begin, $middle, $end, $omit)
1250
1251Return a string containing every request field not matched by the quoted reguar
1252expression $omit, placing $begin before each field name, $middle between each
1253field name and its value, and $end after each value. Values are taken from the
1254current request.
1255
1256=cut
30 1257
31sub print_form_data { 1258sub print_form_data {
32 my ($self, $begin, $middle, $end, $qr_omit) = @_; 1259 my ($self, $begin, $middle, $end, $qr_omit) = @_;
1260 my $r=$self->r;
1261 my @form_data = $r->param;
1262
33 my $return_string = ""; 1263 my $return_string = "";
34
35 $r=$self->{r};
36 my @form_data = $r->param;
37 foreach my $name (@form_data) { 1264 foreach my $name (@form_data) {
38 next if ($qr_omit and $name =~ /$qr_omit/); 1265 next if ($qr_omit and $name =~ /$qr_omit/);
39 my @values = $r->param($name); 1266 my @values = $r->param($name);
1267 foreach my $variable (qw(begin name middle value end)) {
1268 # FIXME: can this loop be moved out of the enclosing loop?
1269 no strict 'refs';
1270 ${$variable} = "" unless defined ${$variable};
1271 }
40 foreach my $value (@values) { 1272 foreach my $value (@values) {
41 $return_string .= "$begin$name$middle$value$end"; 1273 $return_string .= "$begin$name$middle$value$end";
42 } 1274 }
43 } 1275 }
44 1276
45 return $return_string; 1277 return $return_string;
46} 1278}
47 1279
48sub hidden_authen_fields { 1280=back
1281
1282=cut
1283
1284# ------------------------------------------------------------------------------
1285
1286=head2 Utilities
1287
1288=over
1289
1290=item systemLink($urlpath, %options)
1291
1292Generate a link to another part of the system. $urlpath is WeBWorK::URLPath
1293object from which the base path will be taken. %options can consist of:
1294
1295=over
1296
1297=item params
1298
1299Can be either a reference to an array or a reference to a hash.
1300
1301If it is a reference to a hash, it maps parmaeter names to values. These
1302parameters will be included in the generated link. If a value is an arrayref,
1303the values of the array referenced will be used. If a value is undefined, the
1304value from the current request will be used.
1305
1306If C<params> is an arrayref, it is interpreted as a list of parameter names.
1307These parameters will be included in the generated link, using the values from
1308the current request.
1309
1310Unless C<authen> is false (see below), the authentication parameters (C<user>,
1311C<effectiveUser>, and C<key>) are included with their default values.
1312
1313=item authen
1314
1315If set to a false value, the authentication parameters (C<user>,
1316C<effectiveUser>, and C<key>) are included in the the generated link unless
1317explicitly listed in C<params>.
1318
1319=back
1320
1321=cut
1322
1323# FIXME: there should probably be an option for prepending "http://hostname:port"
1324sub systemLink {
1325 my ($self, $urlpath, %options) = @_;
1326 my $r = $self->r;
1327
1328 my %params = ();
1329 if (exists $options{params}) {
1330 if (ref $options{params} eq "HASH") {
1331 %params = %{ $options{params} };
1332 } elsif (ref $options{params} eq "ARRAY") {
1333 my @names = @{ $options{params} };
1334 @params{@names} = ();
1335 } else {
1336 croak "option 'params' is not a hashref or an arrayref";
1337 }
1338 }
1339
1340 my $authen = exists $options{authen} ? $options{authen} : 1;
1341 if ($authen) {
1342 $params{user} = undef unless exists $params{user};
1343 $params{effectiveUser} = undef unless exists $params{effectiveUser};
1344 $params{key} = undef unless exists $params{key};
1345 }
1346
1347 my $url = $r->location . $urlpath->path;
1348 my $first = 1;
1349
1350 foreach my $name (keys %params) {
1351 my $value = $params{$name};
1352
1353 my @values;
1354 if (defined $value) {
1355 if (ref $value eq "ARRAY") {
1356 @values = @$value;
1357 } else {
1358 @values = $value;
1359 }
1360 } elsif (defined $r->param($name)) {
1361 @values = $r->param($name);
1362 }
1363
1364 if (@values) {
1365 if ($first) {
1366 $url .= "?";
1367 $first = 0;
1368 } else {
1369 $url .= "&";
1370 }
1371 $url .= join "&", map { "$name=$_" } @values;
1372 }
1373 }
1374
1375 return $url;
1376}
1377
1378=item nbsp($string)
1379
1380If string consists of only whitespace, the HTML entity C<&nbsp;> is returned.
1381Otherwise $string is returned.
1382
1383=cut
1384
1385sub nbsp {
49 my $self = shift; 1386 my $self = shift;
50 my $r = $self->{r}; 1387 my $str = shift;
51 my $courseEnvironment = $self->{courseEnvironment}; 1388 (defined $str && $str =~/\S/) ? $str : '&nbsp;';
52 my $html = "";
53
54 foreach $param ("user","key") {
55 my $value = $r->param($param);
56 $html .= input({-type=>"hidden",-name=>"$param",-value=>"$value"});
57 }
58 return $html;
59} 1389}
60 1390
61sub pre_header_initialize {} 1391=item space2nbsp($string)
62 1392
63sub header { 1393Replace spaces in the string with html non-breaking spaces.
1394
1395=cut
1396
1397sub space2nbsp {
64 my $self = shift; 1398 my $str = shift;
65 my $r=$self->{r}; 1399 $str =~ s/\s/&nbsp;/g;
66 $r->content_type('text/html'); 1400 return($str);
67 $r->send_http_header();
68} 1401}
69 1402
70sub initialize {} 1403=item errorOutput($error, $details)
71 1404
72sub title { 1405=cut
73 return "Superclass";
74}
75 1406
76sub body { 1407sub errorOutput($$$) {
77 print "Generated content"; 1408 my ($self, $error, $details) = @_;
78 "";
79}
80
81sub logo {
82 my $self = shift;
83 return $self->{courseEnvironment}->{urls}->{logo};
84}
85
86sub htdocs_base {
87 my $self = shift;
88 return $self->{courseEnvironment}->{urls}->{base};
89}
90
91sub go {
92 my $self = shift;
93 my $r = $self->{r};
94 my $courseEnvironment = $self->{courseEnvironment};
95
96 $self->pre_header_initialize(@_);
97 $self->header(@_); return OK if $r->header_only;
98 $self->initialize(@_);
99
100 my $templateFile = $courseEnvironment->{templates}->{system};
101
102 open(TEMPLATE, $templateFile) or die "Couldn't open template $templateFile";
103 my @template = <TEMPLATE>;
104 close TEMPLATE;
105
106 foreach my $line (@template) {
107 # This is incremental regex processing.
108 # the /c is so that pos($line) doesn't die when the regex fails.
109 while ($line =~ m/\G(.*?)<!--#(.*?)\s*-->/gc) {
110 print "$1";
111 print $self->$2(@_) if $self->can($2);
112 }
113 # I thought I could use pos($line) here, but /noooooo/
114 print substr $line, pos($line);
115 }
116
117 return OK; 1409 return
1410 CGI::h3("Software Error"),
1411 CGI::p(<<EOF),
1412WeBWorK has encountered a software error while attempting to process this
1413problem. It is likely that there is an error in the problem itself. If you are
1414a student, contact your professor to have the error corrected. If you are a
1415professor, please consut the error output below for more informaiton.
1416EOF
1417 # FIXME: this message shouldn't refer the the "problem" since it is for general error reporting
1418 CGI::h3("Error messages"), CGI::p(CGI::tt($error)),
1419 CGI::h3("Error context"), CGI::p(CGI::tt($details));
118} 1420}
1421
1422=item warningOutput($warnings)
1423
1424=cut
1425
1426sub warningOutput($$) {
1427 my ($self, $warnings) = @_;
1428
1429 my @warnings = split m/\n+/, $warnings;
1430
1431 return
1432 CGI::h3("Software Warnings"),
1433 CGI::p(<<EOF),
1434WeBWorK has encountered warnings while attempting to process this problem. It
1435is likely that this indicates an error or ambiguity in the problem itself. If
1436you are a student, contact your professor to have the problem corrected. If you
1437are a professor, please consut the warning output below for more informaiton.
1438EOF
1439 # FIXME: this message shouldn't refer the the "problem" since it is for general warning reporting
1440 CGI::h3("Warning messages"),
1441 CGI::ul(CGI::li(\@warnings));
1442}
1443
1444=back
1445
1446=head1 AUTHOR
1447
1448Written by Dennis Lambe Jr., malsyned (at) math.rochester.edu and Sam Hathaway,
1449sh002i (at) math.rochester.edu.
1450
1451=cut
119 1452
1201; 14531;

Legend:
Removed from v.353  
changed lines
  Added in v.2260

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9