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