Parent Directory
|
Revision Log
forward-port from rel-2-2-dev: (Moved snippet of ContentGenerator.pm that checked to see if the "report bugs" link could be showed inside the clause that requires the authentication to have been verified.) Although it was not noted in the original commit message, this commit also removes trailing newlines from error reporting messages in Authz. I assume this is so that the file/lineno is appened by Perl.
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader$ 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 qw(*ul *li escapeHTML); 49 use Date::Format; 50 use URI::Escape; 51 use WeBWorK::Debug; 52 use WeBWorK::PG; 53 use WeBWorK::Template qw(template); 54 55 ############################################################################### 56 57 =head1 CONSTRUCTOR 58 59 =over 60 61 =item new($r) 62 63 Creates a new instance of a content generator. Supply a WeBWorK::Request object 64 $r. 65 66 =cut 67 68 sub new { 69 my ($invocant, $r) = @_; 70 my $class = ref($invocant) || $invocant; 71 my $self = { 72 r => $r, # this is now a WeBWorK::Request 73 ce => $r->ce(), # these three are here for 74 db => $r->db(), # backward-compatability 75 authz => $r->authz(), # with unconverted CGs 76 noContent => undef, # FIXME this should get clobbered at some point 77 }; 78 bless $self, $class; 79 return $self; 80 } 81 82 =back 83 84 =cut 85 86 ################################################################################ 87 88 =head1 INVOCATION 89 90 =over 91 92 =item go() 93 94 Generates a page, using methods from the particular subclass of ContentGenerator 95 that is instantiated. Generatoion is broken up into several steps, to give 96 subclasses ample control over the process. 97 98 =over 99 100 =item 1 101 102 go() will attempt to call the method pre_header_initialize(). This method may be 103 implemented in subclasses which must do processing before the HTTP header is 104 emitted. 105 106 =item 2 107 108 go() will attempt to call the method header(). This method emits the HTTP 109 header. It is defined in this class (see below), but may be overridden in 110 subclasses which need to send different header information. For some reason, the 111 return value of header() will be used as the result of this function, if it is 112 defined. 113 114 FIXME: figure out what the deal is with the return value of header(). If we sent 115 a header, it's too late to set the status by returning. If we didn't, header() 116 didn't perform its function! 117 118 =item 3 119 120 At this point, go() will terminate if the request is a HEAD request or if the 121 field $self->{noContent} contains a true value. 122 123 FIXME: I don't think we'll need noContent after reply_with_redirect() is 124 adopted by all modules. 125 126 =item 4 127 128 go() then attempts to call the method initialize(). This method may be 129 implemented in subclasses which must do processing after the HTTP header is sent 130 but before any content is sent. 131 132 =item 6 133 134 The method content() is called to send the page content to client. 135 136 =back 137 138 =cut 139 140 sub go { 141 my ($self) = @_; 142 my $r = $self->r; 143 my $ce = $r->ce; 144 145 my $returnValue = OK; 146 147 # We only write to the activity log if it has been defined and if 148 # we are in a specific course. The latter check is to prevent attempts 149 # to write to a course log file when viewing the top-level list of 150 # courses page. 151 WeBWorK::Utils::writeCourseLog($ce, 'activity_log', 152 $self->prepare_activity_entry) if ( $r->urlpath->arg("courseID") and 153 $r->ce->{courseFiles}->{logs}->{activity_log}); 154 155 $self->pre_header_initialize(@_) if $self->can("pre_header_initialize"); 156 157 # send a file instead of a normal reply (reply_with_file() sets this field) 158 defined $self->{reply_with_file} and do { 159 return $self->do_reply_with_file($self->{reply_with_file}); 160 }; 161 162 # send a Location: header instead of a normal reply (reply_with_redirect() sets this field) 163 defined $self->{reply_with_redirect} and do { 164 return $self->do_reply_with_redirect($self->{reply_with_redirect}); 165 }; 166 167 my $headerReturn = $self->header(@_); 168 $returnValue = $headerReturn if defined $headerReturn; 169 # FIXME: we won't need noContent after reply_with_redirect() is adopted 170 return $returnValue if $r->header_only or $self->{noContent}; 171 172 $self->initialize() if $self->can("initialize"); 173 174 $self->content(); 175 176 return $returnValue; 177 } 178 179 =item r() 180 181 Returns a reference to the WeBWorK::Request object associated with this 182 instance. 183 184 =cut 185 186 sub r { 187 my ($self) = @_; 188 189 return $self->{r}; 190 } 191 192 =item do_reply_with_file($fileHash) 193 194 Handler for reply_with_file(), used by go(). DO NOT CALL THIS METHOD DIRECTLY. 195 196 =cut 197 198 sub do_reply_with_file { 199 my ($self, $fileHash) = @_; 200 my $r = $self->r; 201 202 my $type = $fileHash->{type}; 203 my $source = $fileHash->{source}; 204 my $name = $fileHash->{name}; 205 my $delete_after = $fileHash->{delete_after}; 206 207 # if there was a problem, we return here and let go() worry about sending the reply 208 return NOT_FOUND unless -e $source; 209 return FORBIDDEN unless -r $source; 210 211 # open the file now, so we can send the proper error status is we fail 212 open my $fh, "<", $source or return SERVER_ERROR; 213 214 # send our custom HTTP header 215 $r->content_type($type); 216 $r->header_out("Content-Disposition" => "attachment; filename=\"$name\""); 217 $r->send_http_header; 218 219 # send the file 220 $r->send_fd($fh); 221 222 # close the file and go home 223 close $fh; 224 225 if ($delete_after) { 226 unlink $source or warn "failed to unlink $source after sending: $!"; 227 } 228 } 229 230 =item do_reply_with_redirect($url) 231 232 Handler for reply_with_redirect(), used by go(). DO NOT CALL THIS METHOD DIRECTLY. 233 234 =cut 235 236 sub do_reply_with_redirect { 237 my ($self, $url) = @_; 238 my $r = $self->r; 239 240 $r->status(REDIRECT); 241 $r->header_out(Location => $url); 242 $r->send_http_header(); 243 } 244 245 =back 246 247 =cut 248 249 ################################################################################ 250 251 =head1 DATA MODIFIERS 252 253 Modifiers allow the caller to register a piece of data for later retrieval in a 254 standard way. 255 256 =over 257 258 =item reply_with_file($type, $source, $name, $delete_after) 259 260 Enables file sending mode, causing go() to send the file specified by $source to 261 the client after calling pre_header_initialize(). The content type sent is 262 $type, and the suggested client-side file name is $name. If $delete_after is 263 true, $source is deleted after it is sent. 264 265 Must be called before the HTTP header is sent. Usually called from 266 pre_header_initialize(). 267 268 =cut 269 270 sub reply_with_file { 271 my ($self, $type, $source, $name, $delete_after) = @_; 272 $delete_after ||= ""; 273 274 $self->{reply_with_file} = { 275 type => $type, 276 source => $source, 277 name => $name, 278 delete_after => $delete_after, 279 }; 280 } 281 282 =item reply_with_redirect($url) 283 284 Enables redirect mode, causing go() to redirect to the given URL after calling 285 pre_header_initialize(). 286 287 Must be called before the HTTP header is sent. Usually called from 288 pre_header_initialize(). 289 290 =cut 291 292 sub reply_with_redirect { 293 my ($self, $url) = @_; 294 295 $self->{reply_with_redirect} = $url; 296 } 297 298 =item addmessage($message) 299 300 Adds a message to the list of messages to be printed by the message() template 301 escape handler. 302 303 Must be called before the message() template escape is invoked. 304 305 =cut 306 307 308 309 sub addmessage { 310 my ($self, $message) = @_; 311 $self->{status_message} .= $message; 312 } 313 314 =item addgoodmessage($message) 315 316 Adds a success message to the list of messages to be printed by the 317 message() template escape handler. 318 319 =cut 320 321 322 sub addgoodmessage { 323 my ($self, $message) = @_; 324 $self->addmessage(CGI::div({class=>"ResultsWithoutError"}, $message)); 325 } 326 327 =item addbadmessage($message) 328 329 Adds a failure message to the list of messages to be printed by the 330 message() template escape handler. 331 332 =cut 333 334 335 sub addbadmessage { 336 my ($self, $message) = @_; 337 $self->addmessage(CGI::div({class=>"ResultsWithError"}, $message)); 338 } 339 340 =item prepare_activity_entry() 341 342 Prepare a string to be sent to the activity log, if it is turned on. 343 This can be overriden by different modules. 344 345 =cut 346 347 348 sub prepare_activity_entry { 349 my $self = shift; 350 my $r = $self->r; 351 my $string = $r->urlpath->path . " ---> ". 352 join("\t", (map { $_ eq 'key' || $_ eq 'passwd' ? '' : $_ ." => " . $r->param($_) } $r->param())); 353 $string =~ s/\t+/\t/g; 354 return($string); 355 } 356 357 =back 358 359 =cut 360 361 ################################################################################ 362 363 =head1 STANDARD METHODS 364 365 The following are the standard content generator methods. Some are defined here, 366 but may be overridden in a subclass. Others are not defined unless they are 367 defined in a subclass. 368 369 =over 370 371 =item pre_header_initialize() 372 373 Not defined in this package. 374 375 May be defined by a subclass to perform any processing that must occur before 376 the HTTP header is sent. 377 378 =cut 379 380 #sub pre_header_initialize { } 381 382 =item header() 383 384 Defined in this package. 385 386 Generates and sends a default HTTP header, specifying the "text/html" content 387 type. 388 389 =cut 390 391 sub header { 392 my $self = shift; 393 my $r = $self->r; 394 395 $r->content_type("text/html"); 396 $r->send_http_header(); 397 return OK; 398 } 399 400 =item initialize() 401 402 Not defined in this package. 403 404 May be defined by a subclass to perform any processing that must occur after the 405 HTTP header is sent but before any content is sent. 406 407 =cut 408 409 #sub initialize { } 410 411 =item content() 412 413 Defined in this package. 414 415 Print the content of the generated page. 416 417 The implementation in this package uses WeBWorK::Template to define the content 418 of the page. See WeBWorK::Template for details. 419 420 If a method named templateName() exists, it it called to determine the name of 421 the template to use. If not, the default template, "system", is used. The 422 location of the template is looked up in the course environment. 423 424 =cut 425 426 sub content { 427 my ($self) = @_; 428 my $r = $self->r; 429 my $ce = $r->ce; 430 431 my $themesDir = $ce->{webworkDirs}{templates}; 432 my $theme = $r->param("theme") || $ce->{defaultTheme}; 433 $theme = $ce->{defaultTheme} if $theme =~ m!(?:^|/)\.\.(?:/|$)!; 434 my $template = $self->can("templateName") ? $self->templateName : $ce->{defaultThemeTemplate}; 435 my $templateFile = "$themesDir/$theme/$template.template"; 436 437 template($templateFile, $self); 438 } 439 440 =back 441 442 =cut 443 444 # ------------------------------------------------------------------------------ 445 446 =head2 Template escape handlers 447 448 Template escape handlers are invoked when the template processor encounters a 449 matching escape sequence in the template. The escapse sequence's arguments are 450 passed to the methods as a reference to a hash. 451 452 For more information, refer to WeBWorK::Template. 453 454 The following template escapes handlers are defined here or may be defined in 455 subclasses. For methods that are not defined in this package, the documentation 456 defines the interface and behavior that any subclass implementation must follow. 457 458 =over 459 460 =item head() 461 462 Not defined in this package. 463 464 Any tags that should appear in the HEAD of the document. 465 466 =cut 467 468 #sub head { } 469 470 =item info() 471 472 Not defined in this package. 473 474 Auxiliary information related to the content displayed in the C<body>. 475 476 =cut 477 478 #sub info { } 479 480 =item links() 481 482 Defined in this package. 483 484 Links that should appear on every page. 485 486 =cut 487 488 sub links { 489 my ($self) = @_; 490 my $r = $self->r; 491 my $ce = $r->ce; 492 my $db = $r->db; 493 my $authen = $r->authen; 494 my $authz = $r->authz; 495 my $urlpath = $r->urlpath; 496 497 # we don't currently have any links to display if the user's not logged in. this may change, though. 498 #return "" unless $authen->was_verified; 499 500 # grab some interesting data from the request 501 my $courseID = $urlpath->arg("courseID"); 502 my $userID = $r->param('user'); 503 my $eUserID = $r->param("effectiveUser"); 504 my $setID = $urlpath->arg("setID"); 505 my $problemID = $urlpath->arg("problemID"); 506 507 # it's possible that the setID and the problemID are invalid, since they're just taken from the URL path info 508 if ($authen->was_verified) { 509 if (defined $setID and $db->getUserSet($eUserID, $setID)) { 510 if (defined $problemID and $db->getUserProblem($eUserID, $setID, $problemID)) { 511 # both set and poblem exist -- do nothing 512 } else { 513 $problemID = undef; 514 } 515 } else { 516 $setID = undef; 517 $problemID = undef; 518 } 519 } 520 521 # old method -- get rid of this after a while 522 #$setID = "" if (defined $setID && !(grep /$setID/, $db->listUserSets($eUserID))); 523 #$problemID = "" if (defined $problemID && !(grep /$problemID/, $db->listUserProblems($eUserID, $setID))); 524 525 # experimental subroutine for generating links, to clean up the rest of the 526 # code. ignore for now. (this is a closure over $self.) 527 my $makelink = sub { 528 my ($module, %options) = @_; 529 530 my $urlpath_args = $options{urlpath_args} || {}; 531 my $systemlink_args = $options{systemlink_args} || {}; 532 my $text = $options{text}; 533 my $active = $options{active}; 534 535 my $new_urlpath = $self->r->urlpath->newFromModule($module, %$urlpath_args); 536 my $new_systemlink = $self->systemLink($new_urlpath, %$systemlink_args); 537 538 defined $text or $text = $new_urlpath->name; 539 $text = sp2nbsp($text); # ugly hack to prevent text from wrapping 540 541 # try to set $active automatically by comparing 542 if (not defined $active) { 543 if ($urlpath->module eq $new_urlpath->module) { 544 my @args = sort keys %{{$urlpath->args}}; 545 my @new_args = sort keys %{{$new_urlpath->args}}; 546 if (@args == @new_args) { 547 foreach my $i (0 .. $#args) { 548 $active = 0; 549 last if $args[$i] ne $new_args[$i]; 550 $active = 1; 551 } 552 } else { 553 $active = 0; 554 } 555 } else { 556 $active = 0; 557 } 558 } 559 560 my $new_anchor; 561 if ($active) { 562 # add <strong> for old browsers 563 $new_anchor = CGI::strong(CGI::a({href=>$new_systemlink, class=>"active"}, $text)); 564 } else { 565 $new_anchor = CGI::a({href=>$new_systemlink}, $text); 566 } 567 568 return $new_anchor; 569 }; 570 571 # to make things more concise 572 my $pfx = "WeBWorK::ContentGenerator::"; 573 my %args = ( courseID => $courseID ); 574 575 # we'd like to preserve displayMode and showOldAnswers between pages, and we 576 # don't have a general way of preserving non-authen params between requests, 577 # so here is the hack: 578 my %params; 579 $params{displayMode} = $r->param("displayMode") if defined $r->param("displayMode"); 580 $params{showOldAnswers} = $r->param("showOldAnswers") if defined $r->param("showOldAnswers"); 581 # in the past, we were checking $self->{displayMode} and $self->{will}->{showOldAnswers} 582 # to set these args, but I don't wanna do that anymore, since it relies on 583 # fields specific to Problem.pm (pretty sure). The only differences in this 584 # approach are: 585 # (a) displayMode will not be set if it wasn't set in the current request, 586 # but this is ok since the resulting page will just use the default value 587 # (b) showOldAnswers will get set to the value specified in the current 588 # request, regardless of whether it is allowed, but this is OK since we 589 # always this value before using it. 590 my %systemlink_args; 591 $systemlink_args{params} = \%params if %params; 592 593 print CGI::start_ul(); 594 print CGI::start_li(); # Courses 595 print &$makelink("${pfx}Home", text=>"Courses", systemlink_args=>{authen=>0}); 596 597 if (defined $courseID) { 598 #print CGI::start_ul(); 599 #print CGI::start_li(); # $courseID 600 #print CGI::strong(CGI::span({class=>"active"}, $courseID)); 601 602 if ($authen->was_verified) { 603 print CGI::start_ul(); 604 print CGI::start_li(); # Homework Sets 605 print &$makelink("${pfx}ProblemSets", text=>"Homework Sets", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args); 606 607 if (defined $setID) { 608 print CGI::start_ul(); 609 print CGI::start_li(); # $setID 610 print &$makelink("${pfx}ProblemSet", text=>"$setID", urlpath_args=>{%args,setID=>$setID}, systemlink_args=>\%systemlink_args); 611 # FIXME i think we only want this if the problem set is not a gateway quiz 612 if (defined $problemID) { 613 print CGI::start_ul(); 614 print CGI::start_li(); # $problemID 615 print &$makelink("${pfx}Problem", text=>"Problem $problemID", urlpath_args=>{%args,setID=>$setID,problemID=>$problemID}, systemlink_args=>\%systemlink_args); 616 617 print CGI::end_li(); # end $problemID 618 print CGI::end_ul(); 619 } 620 print CGI::end_li(); # end $setID 621 print CGI::end_ul(); 622 } 623 print CGI::end_li(); # end Homework Sets 624 625 if ($authz->hasPermissions($userID, "change_password") or $authz->hasPermissions($userID, "change_email_address")) { 626 print CGI::li(&$makelink("${pfx}Options", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args)); 627 } 628 629 print CGI::li(&$makelink("${pfx}Grades", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args)); 630 631 if ($authz->hasPermissions($userID, "access_instructor_tools")) { 632 $pfx .= "Instructor::"; 633 634 print CGI::start_li(); # Instructor Tools 635 print &$makelink("${pfx}Index", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args); 636 print CGI::start_ul(); 637 638 print CGI::li(&$makelink("${pfx}UserList", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args)); 639 640 print CGI::start_li(); # Homework Set Editor 641 print &$makelink("${pfx}ProblemSetList", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args); 642 if (defined $setID) { 643 print CGI::start_ul(); 644 print CGI::start_li(); # $setID 645 print &$makelink("${pfx}ProblemSetDetail", text=>"$setID", urlpath_args=>{%args,setID=>$setID}, systemlink_args=>\%systemlink_args); 646 647 if (defined $problemID) { 648 print CGI::start_ul(); 649 print CGI::li(&$makelink("${pfx}PGProblemEditor", text=>"$problemID", urlpath_args=>{%args,setID=>$setID,problemID=>$problemID}, systemlink_args=>\%systemlink_args)); 650 print CGI::end_ul(); 651 } 652 653 print CGI::end_li(); # end $setID 654 print CGI::end_ul(); 655 } 656 print CGI::end_li(); # end Homework Set Editor 657 658 print CGI::li(&$makelink("${pfx}SetMaker", text=>"Library Browser", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args)); 659 660 print CGI::start_li(); # Stats 661 print &$makelink("${pfx}Stats", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args); 662 if ($userID ne $eUserID or defined $setID) { 663 print CGI::start_ul(); 664 if ($userID ne $eUserID) { 665 print CGI::li(&$makelink("${pfx}Stats", text=>"$eUserID", urlpath_args=>{%args,statType=>"student",userID=>$eUserID}, systemlink_args=>\%systemlink_args)); 666 } 667 if (defined $setID) { 668 print CGI::li(&$makelink("${pfx}Stats", text=>"$setID", urlpath_args=>{%args,statType=>"set",setID=>$setID}, systemlink_args=>\%systemlink_args)); 669 } 670 print CGI::end_ul(); 671 } 672 print CGI::end_li(); # end Stats 673 674 print CGI::start_li(); # Student Progress 675 print &$makelink("${pfx}StudentProgress", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args); 676 if ($userID ne $eUserID or defined $setID) { 677 print CGI::start_ul(); 678 if ($userID ne $eUserID) { 679 print CGI::li(&$makelink("${pfx}StudentProgress", text=>"$eUserID", urlpath_args=>{%args,statType=>"student",userID=>$eUserID}, systemlink_args=>\%systemlink_args)); 680 } 681 if (defined $setID) { 682 print CGI::li(&$makelink("${pfx}StudentProgress", text=>"$setID", urlpath_args=>{%args,statType=>"set",setID=>$setID}, systemlink_args=>\%systemlink_args)); 683 } 684 print CGI::end_ul(); 685 } 686 print CGI::end_li(); # end Student Progress 687 688 if ($authz->hasPermissions($userID, "score_sets")) { 689 print CGI::li(&$makelink("${pfx}Scoring", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args)); 690 } 691 692 if ($authz->hasPermissions($userID, "send_mail")) { 693 print CGI::li(&$makelink("${pfx}SendMail", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args)); 694 } 695 696 if ($authz->hasPermissions($userID, "manage_course_files")) { 697 print CGI::li(&$makelink("${pfx}FileManager", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args)); 698 } 699 700 if ($authz->hasPermissions($userID, "manage_course_files")) { 701 print CGI::li(&$makelink("${pfx}Config", urlpath_args=>{%args}, systemlink_args=>\%systemlink_args)); 702 } 703 print CGI::li( $self->helpMacro('instructor_links','Help'),$self->help() ); 704 print CGI::end_ul(); 705 print CGI::end_li(); # end Instructor Tools 706 } # /* access_instructor_tools */ 707 708 print CGI::end_ul(); 709 710 print CGI::start_ul(); 711 if (exists $ce->{webworkURLs}{bugReporter} and $ce->{webworkURLs}{bugReporter} ne "" 712 and $authz->hasPermissions($userID, "report_bugs")) { 713 print CGI::li(CGI::a({style=>'font-size:larger', href=>$ce->{webworkURLs}{bugReporter}}, "Report bugs")); 714 } 715 716 print CGI::end_ul(); 717 718 } # /* authentication was_verified */ 719 720 #print CGI::end_li(); # end $courseID 721 #print CGI::end_ul(); 722 } # /* defined $courseID */ 723 724 print CGI::end_li(); # end Courses 725 print CGI::end_ul(); 726 727 728 729 730 return ""; 731 } 732 733 =item loginstatus() 734 735 Defined in this package. 736 737 Print a notification message announcing the current real user and effective 738 user, a link to stop acting as the effective user, and a link to logout. 739 740 =cut 741 742 sub loginstatus { 743 my ($self) = @_; 744 my $r = $self->r; 745 my $authen = $r->authen; 746 my $urlpath = $r->urlpath; 747 748 if ($authen and $authen->was_verified) { 749 my $courseID = $urlpath->arg("courseID"); 750 my $userID = $r->param("user"); 751 my $eUserID = $r->param("effectiveUser"); 752 753 my $stopActingURL = $self->systemLink($urlpath, # current path 754 params => { effectiveUser => $userID }, 755 ); 756 my $logoutURL = $self->systemLink($urlpath->newFromModule(__PACKAGE__ . "::Logout", courseID => $courseID)); 757 758 if ($eUserID eq $userID) { 759 print "Logged in as $userID. " . CGI::br() . CGI::a({href=>$logoutURL}, "Log Out"); 760 } else { 761 print "Logged in as $userID. " . CGI::a({href=>$logoutURL}, "Log Out") . CGI::br(); 762 print "Acting as $eUserID. " . CGI::a({href=>$stopActingURL}, "Stop Acting"); 763 } 764 } else { 765 print "Not logged in."; 766 } 767 768 return ""; 769 } 770 771 =item nav($args) 772 773 Not defined in this package. 774 775 Links to the previous, next, and parent objects. 776 777 $args is a reference to a hash containing the following fields: 778 779 style => text|image 780 imageprefix => prefix to prepend to base image URL 781 imagesuffix => suffix to append to base image URL 782 separator => HTML to place in between links 783 784 If C<style> is "image", image URLs are constructed by prepending C<imageprefix> 785 and postpending C<imagesuffix> to the image base names defined by the 786 implementor. (Examples of base names include "Prev", "Next", "ProbSet", and 787 "Up"). Each concatenated string should form an absolute URL to an image file. 788 For example: 789 790 <!--#nav style="images" imageprefix="/webwork2_files/images/nav" 791 imagesuffix=".gif" separator=" "--> 792 793 =cut 794 795 #sub nav { } 796 797 =item options() 798 799 Not defined in this package. 800 801 View options related to the content displayed in the body or info areas. See also 802 optionsMacro(). 803 804 =cut 805 806 #sub options { } 807 808 =item path($args) 809 810 Defined in this package. 811 812 Print "breadcrubs" from the root of the virtual hierarchy to the current page. 813 $args is a reference to a hash containing the following fields: 814 815 style => type of separator: text|image 816 image => if style=image, URL of image to use as path separator 817 text => if style=text, text to use as path separator 818 if style=image, the ALT text of each separator image 819 textonly => suppress all HTML, return only plain text 820 821 The implementation in this package takes information from the WeBWorK::URLPath 822 associated with the current request. 823 824 =cut 825 826 sub path { 827 my ($self, $args) = @_; 828 my $r = $self->r; 829 830 my @path; 831 832 my $urlpath = $r->urlpath; 833 do { 834 unshift @path, $urlpath->name, $r->location . $urlpath->path; 835 } while ($urlpath = $urlpath->parent); 836 837 $path[$#path] = ""; # we don't want the last path element to be a link 838 839 #print "\n<!-- BEGIN " . __PACKAGE__ . "::path -->\n"; 840 print $self->pathMacro($args, @path); 841 #print "<!-- END " . __PACKAGE__ . "::path -->\n"; 842 843 return ""; 844 } 845 846 =item siblings() 847 848 Not defined in this package. 849 850 Print links to siblings of the current object. 851 852 =cut 853 854 #sub siblings { } 855 856 =item timestamp() 857 858 Defined in this package. 859 860 Display the current time and date using default format "3:37pm on Jan 7, 2004". 861 The display format can be adjusted by giving a style in the template. 862 For example, 863 864 <!--#timestamp style="%m/%d/%y at %I:%M%P"--> 865 866 will give standard WeBWorK time format. Wording and other formatting 867 can be done in the template itself. 868 =cut 869 870 sub timestamp { 871 my ($self, $args) = @_; 872 my $formatstring = "%l:%M%P on %b %e, %Y"; 873 $formatstring = $args->{style} if(defined($args->{style})); 874 return(Date::Format::time2str($formatstring, time())); 875 } 876 877 =item message() 878 879 Defined in this package. 880 881 Print any messages (error or non-error) resulting from the last form submission. 882 This could be used to give Sucess and Failure messages after an action is performed by a module. 883 884 The implementation in this package prints the value of the field 885 $self->{status_message}, if it is present. 886 887 =cut 888 889 sub message { 890 my ($self) = @_; 891 892 print "\n<!-- BEGIN " . __PACKAGE__ . "::message -->\n"; 893 print $self->{status_message} if exists $self->{status_message}; 894 895 print "<!-- END " . __PACKAGE__ . "::message -->\n"; 896 897 return ""; 898 } 899 900 =item title() 901 902 Defined in this package. 903 904 Print the title of the current page. 905 906 The implementation in this package takes information from the WeBWorK::URLPath 907 associated with the current request. 908 909 =cut 910 911 sub title { 912 my ($self, $args) = @_; 913 my $r = $self->r; 914 915 #print "\n<!-- BEGIN " . __PACKAGE__ . "::title -->\n"; 916 print underscore2nbsp($r->urlpath->name); 917 #print "<!-- END " . __PACKAGE__ . "::title -->\n"; 918 919 return ""; 920 } 921 922 =item warnings() 923 924 Defined in this package. 925 926 Print accumulated warnings. 927 928 The implementation in this package checks for a note in the request named 929 "warnings". If present, its contents are formatted and returned. 930 931 =cut 932 933 sub warnings { 934 my ($self) = @_; 935 my $r = $self->r; 936 937 print "\n<!-- BEGIN " . __PACKAGE__ . "::warnings -->\n"; 938 print $self->warningOutput($r->notes("warnings")) if $r->notes("warnings"); 939 print "<!-- END " . __PACKAGE__ . "::warnings -->\n"; 940 941 return ""; 942 } 943 944 =item help() 945 946 Display a link to context-sensitive help. If the argument C<name> is defined, 947 the link will be to the help document for that name. Otherwise the module of the 948 WeBWorK::URLPath node for the current system location will be used. 949 950 =cut 951 952 sub help { 953 my $self = shift; 954 my $args = shift; 955 my $name = $args->{name}; 956 957 # old naming scheme 958 #$name = lc($self->r->urlpath->name) unless defined($name); 959 #$name =~ s/\s/_/g; 960 961 $name = $self->r->urlpath->module unless defined($name); 962 $name =~ s/WeBWorK::ContentGenerator:://; 963 $name =~ s/://g; 964 965 $self->helpMacro($name); 966 } 967 968 =item url($args) 969 970 Defined in this package. 971 972 Returns the specified URL from either %webworkURLs or %courseURLs in the course 973 environment. $args is a reference to a hash containing the following fields: 974 975 type => type of URL: webwork|course 976 name => name of URL (key in URL hash) 977 978 =cut 979 980 sub url { 981 my ($self, $args) = @_; 982 my $ce = $self->r->ce; 983 my $type = $args->{type}; 984 my $name = $args->{name}; 985 986 if ($type eq "webwork") { 987 return $ce->{webworkURLs}->{$name}; 988 } elsif ($type eq "course") { 989 return $ce->{courseURLs}->{$name}; 990 } else { 991 warn __PACKAGE__."::url: unrecognized type '$type'.\n"; 992 } 993 } 994 995 =back 996 997 =cut 998 999 # ------------------------------------------------------------------------------ 1000 1001 =head2 Conditional predicates 1002 1003 Conditional predicate methods are invoked when the C<#if> escape sequence is 1004 encountered in the template. If a method named C<if_predicate> is defined in 1005 here or in the instantiated subclass, it is invoked. 1006 1007 The following predicates are currently defined: 1008 1009 =over 1010 1011 =item if_can($function) 1012 1013 If a function named $function is present in the current content generator (or 1014 any superclass), a true value is returned. Otherwise, a false value is returned. 1015 1016 The implementation in this package uses the method UNIVERSAL->can(function) to 1017 arrive at the result. 1018 1019 A subclass could redefine this method to, for example, "hide" a method from the 1020 template: 1021 1022 sub if_can { 1023 my ($self, $arg) = @_; 1024 1025 if ($arg eq "floobar") { 1026 return 0; 1027 } else { 1028 return $self->SUPER::if_can($arg); 1029 } 1030 } 1031 1032 =cut 1033 1034 sub if_can { 1035 my ($self, $arg) = @_; 1036 1037 return $self->can($arg) ? 1 : 0; 1038 } 1039 1040 =item if_loggedin($arg) 1041 1042 If the user is currently logged in, $arg is returned. Otherwise, the inverse of 1043 $arg is returned. 1044 1045 #The implementation in this package always returns $arg, since most content 1046 #generators are only reachable when the user is authenticated. It is up to 1047 #classes that can be reached without logging in to override this method and 1048 #provide the correct behavior. 1049 # 1050 #This is suboptimal, and may change in the future. 1051 1052 The implementation in this package uses WeBWorK::Authen::was_verified() to 1053 retrieve the result of the last call to WeBWorK::Authen::verify(). 1054 1055 =cut 1056 1057 sub if_loggedin { 1058 my ($self, $arg) = @_; 1059 1060 #return $arg; 1061 return 0 unless $self->r->authen; 1062 return $self->r->authen->was_verified() ? $arg : !$arg; 1063 } 1064 1065 =item if_message($arg) 1066 1067 If the last form submission generated a message, $arg is returned. Otherwise, the 1068 inverse of $arg is returned. 1069 1070 The implementation in this package checks for the field $self->{status_message} to 1071 determine if a message is present. 1072 1073 If a subclass uses some other method to classify submission results, this method could be 1074 redefined to handle that variance: 1075 1076 sub if_message { 1077 my ($self, $arg) = @_; 1078 1079 my $status = $self->{processReturnValue}; 1080 if ($status != 0) { 1081 return $arg; 1082 } else { 1083 return !$arg; 1084 } 1085 } 1086 1087 =cut 1088 1089 sub if_message { 1090 my ($self, $arg) = @_; 1091 1092 if (exists $self->{status_message}) { 1093 return $arg; 1094 } else { 1095 return !$arg; 1096 } 1097 } 1098 1099 =item if_warnings 1100 1101 If warnings have been emitted while handling this request, $arg is returned. 1102 Otherwise, the inverse of $arg is returned. 1103 1104 The implementation in this package checks for a note in the request named 1105 "warnings". This is set by the WARN handler in Apache::WeBWorK when a warning is 1106 handled. 1107 1108 =cut 1109 1110 sub if_warnings { 1111 my ($self, $arg) = @_; 1112 my $r = $self->r; 1113 1114 if ($r->notes("warnings")) { 1115 return $arg; 1116 } else { 1117 !$arg; 1118 } 1119 } 1120 1121 =back 1122 1123 =cut 1124 1125 ################################################################################ 1126 1127 =head1 HTML MACROS 1128 1129 Various routines are defined in this package for rendering common WeBWorK 1130 idioms. 1131 1132 FIXME: some of these should be moved to WeBWorK::HTML:: modules! 1133 1134 # ------------------------------------------------------------------------------ 1135 1136 =head2 Template escape handler macros 1137 1138 These methods are used by implementations of the escape sequence handlers to 1139 maintain a consistent style. 1140 1141 =over 1142 1143 =item pathMacro($args, @path) 1144 1145 Helper macro for the C<#path> escape sequence: $args is a hash reference 1146 containing the "style", "image", "text", and "textonly" arguments to the escape. 1147 @path consists of ordered key-value pairs of the form: 1148 1149 "Page Name" => URL 1150 1151 If the page should not have a link associated with it, the URL should be left 1152 empty. Authentication data is added to each URL so you don't have to. A fully- 1153 formed path line is returned, suitable for returning by a function implementing 1154 the C<#path> escape. 1155 1156 FIXME: authentication data probably shouldn't be added here any more, now that 1157 we have systemLink(). 1158 1159 =cut 1160 1161 sub pathMacro { 1162 my ($self, $args, @path) = @_; 1163 my %args = %$args; 1164 $args{style} = "text" if $args{textonly}; 1165 1166 my $auth = $self->url_authen_args; 1167 my $sep; 1168 if ($args{style} eq "image") { 1169 $sep = CGI::img({-src=>$args{image}, -alt=>$args{text}}); 1170 } else { 1171 $sep = $args{text}; 1172 } 1173 1174 my @result; 1175 while (@path) { 1176 my $name = shift @path; 1177 my $url = shift @path; 1178 if ($url and not $args{textonly}) { 1179 push @result, CGI::a({-href=>"$url?$auth"}, $name); 1180 } else { 1181 push @result, $name; 1182 } 1183 } 1184 1185 return join($sep, @result), "\n"; 1186 } 1187 1188 =item siblingsMacro(@siblings) 1189 1190 Helper macro for the C<#siblings> escape sequence. @siblings consists of ordered 1191 key-value pairs of the form: 1192 1193 "Sibling Name" => URL 1194 1195 If the sibling should not have a link associated with it, the URL should be left 1196 empty. Authentication data is added to each URL so you don't have to. A fully- 1197 formed siblings block is returned, suitable for returning by a function 1198 implementing the C<#siblings> escape. 1199 1200 FIXME: authentication data probably shouldn't be added here any more, now that 1201 we have systemLink(). 1202 1203 =cut 1204 1205 sub siblingsMacro { 1206 my ($self, @siblings) = @_; 1207 1208 my $auth = $self->url_authen_args; 1209 my $sep = CGI::br(); 1210 1211 my @result; 1212 while (@siblings) { 1213 my $name = shift @siblings; 1214 my $url = shift @siblings; 1215 push @result, $url 1216 ? CGI::a({-href=>"$url?$auth"}, $name) 1217 : $name; 1218 } 1219 1220 return join($sep, @result) . "\n"; 1221 } 1222 1223 1224 1225 =item navMacro($args, $tail, @links) 1226 1227 Helper macro for the C<#nav> escape sequence: $args is a hash reference 1228 containing the "style", "imageprefix", "imagesuffix", and "separator" arguments 1229 to the escape. @siblings consists of ordered tuples of the form: 1230 1231 "Link Name", URL, ImageBaseName 1232 1233 If the sibling should not have a link associated with it, the URL should be left 1234 empty. ImageBaseName is placed between the C<imageprefix> and C<imagesuffix>. 1235 Authentication data is added to each URL so you don't have to. $tail is appended 1236 to each URL, after the authentication information. A fully-formed nav line is 1237 returned, suitable for returning by a function implementing the C<#nav> escape. 1238 1239 =cut 1240 1241 sub navMacro { 1242 my ($self, $args, $tail, @links) = @_; 1243 my $r = $self->r; 1244 my $ce = $r->ce; 1245 my %args = %$args; 1246 1247 my $auth = $self->url_authen_args; 1248 my $prefix = $ce->{webworkURLs}->{htdocs}."/images"; 1249 1250 my @result; 1251 while (@links) { 1252 my $name = shift @links; 1253 my $url = shift @links; 1254 my $img = shift @links; 1255 my $html = 1256 ($img && $args{style} eq "images") 1257 ? CGI::img( 1258 {src=>($prefix."/".$img.$args{imagesuffix}), 1259 border=>"", 1260 alt=>"$name"}) 1261 : $name; 1262 unless($img && !$url) { 1263 push @result, $url 1264 ? CGI::a({-href=>"$url?$auth$tail"}, $html) 1265 : $html; 1266 } 1267 } 1268 1269 return join($args{separator}, @result) . "\n"; 1270 } 1271 1272 =item helpMacro($name) 1273 1274 This escape is represented by a question mark which links to an html page in the 1275 helpFiles directory. Currently the link is made to the file $name.html 1276 1277 =cut 1278 1279 sub helpMacro { 1280 my $self = shift; 1281 my $name = shift; 1282 my $label = shift; #optional 1283 my $ce = $self->r->ce; 1284 my $basePath = $ce->{webworkDirs}->{local_help}; 1285 $name = 'no_help' unless -e "$basePath/$name.html"; 1286 my $path = "$basePath/$name.html"; 1287 my $url = $ce->{webworkURLs}->{local_help}."/$name.html"; 1288 my $imageURL = $ce->{webworkURLs}->{htdocs}."/images/question_mark.png"; 1289 $label = CGI::img({src=>$imageURL, alt=>" ? "}) unless defined $label; 1290 return CGI::a({href => $url, 1291 target => 'ww_help', 1292 onclick => "window.open(this.href,this.target,'width=550,height=350,scrollbars=yes,resizable=yes')"}, 1293 $label); 1294 } 1295 1296 =item optionsMacro(options_to_show => \@options_to_show, extra_params => \@extra_params) 1297 1298 Helper macro for displaying the View Options panel. 1299 1300 @options_to_show lists the options to show, from among this list "displayMode", 1301 "showOldAnswers", "showHints", "showSolutions". If no options are given, 1302 "displayMode" is assumed. 1303 1304 @extraParams is dereferenced and passed to the hidden_fields() method. Use this 1305 to preserve state from the content generator calling optionsMacro(). 1306 1307 This macro is intended to be called from an implementation of the options() 1308 method. The simplest way to to this is: 1309 1310 sub options { shift->optionsMacro } 1311 1312 =cut 1313 1314 sub optionsMacro { 1315 my ($self, %options) = @_; 1316 1317 my @options_to_show = @{$options{options_to_show}} if exists $options{options_to_show}; 1318 @options_to_show = "displayMode" unless @options_to_show; 1319 my %options_to_show; @options_to_show{@options_to_show} = (); # make hash for easy lookups 1320 my @extra_params = @{$options{extra_params}} if exists $options{extra_params}; 1321 1322 my $result = CGI::start_form("POST", $self->r->uri); 1323 $result .= $self->hidden_authen_fields; 1324 $result .= $self->hidden_fields(@extra_params) if @extra_params; 1325 $result .= CGI::start_div({class=>"viewOptions"}); 1326 1327 if (exists $options_to_show{displayMode}) { 1328 my $curr_displayMode = $self->r->param("displayMode") || $self->r->ce->{pg}->{options}->{displayMode}; 1329 my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()}; 1330 my @active_modes = grep { exists $display_modes{$_} } @{$self->r->ce->{pg}->{displayModes}}; 1331 if (@active_modes > 1) { 1332 $result .= "View equations as: "; 1333 $result .= CGI::br(); 1334 $result .= CGI::radio_group( 1335 -name => "displayMode", 1336 -values => \@active_modes, 1337 -default => $curr_displayMode, 1338 -linebreak=>'true', 1339 ); 1340 $result .= CGI::br(); 1341 } 1342 } 1343 1344 if (exists $options_to_show{showOldAnswers}) { 1345 # Note, 0 is a legal value, so we can't use || in setting this 1346 my $curr_showOldAnswers = defined($self->r->param("showOldAnswers")) ? 1347 $self->r->param("showOldAnswers") : $self->r->ce->{pg}->{options}->{showOldAnswers}; 1348 $result .= "Show saved answers?"; 1349 $result .= CGI::br(); 1350 $result .= CGI::radio_group( 1351 -name => "showOldAnswers", 1352 -values => [1,0], 1353 -default => $curr_showOldAnswers, 1354 -labels => { 0=>'No', 1=>'Yes' }, 1355 ); 1356 $result .= CGI::br(); 1357 } 1358 1359 $result .= CGI::submit(-name=>"redisplay", -label=>"Apply Options"); 1360 $result .= CGI::end_div(); 1361 $result .= CGI::end_form(); 1362 1363 return $result; 1364 } 1365 1366 =item feedbackMacro(%params) 1367 1368 Helper macro for displaying the feedback form. Returns a button named "Email 1369 Instructor". %params contains the request parameters accepted by the Feedback 1370 module and their values. 1371 1372 =cut 1373 1374 sub feedbackMacro { 1375 my ($self, %params) = @_; 1376 my $r = $self->r; 1377 my $userID = $r->param("user"); 1378 my $authz = $r->authz; 1379 my $urlpath = $r->urlpath; 1380 my $courseID = $urlpath->arg("courseID"); 1381 1382 # don't do anything unless the user has permission to 1383 return "" unless $authz->hasPermissions($userID, "submit_feedback"); 1384 1385 # feedback form url 1386 my $feedbackPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Feedback", courseID => $courseID); 1387 my $feedbackURL = $self->systemLink($feedbackPage, authen => 0); # no authen info for form action 1388 1389 my $result = CGI::start_form(-method=>"POST", -action=>$feedbackURL) . "\n"; 1390 $result .= $self->hidden_authen_fields . "\n"; 1391 1392 while (my ($key, $value) = each %params) { 1393 $result .= CGI::hidden($key, $value) . "\n"; 1394 } 1395 1396 $result .= CGI::p({-align=>"left"}, CGI::submit(-name=>"feedbackForm", -label=>"Email instructor")); 1397 $result .= CGI::endform() . "\n"; 1398 1399 return $result; 1400 } 1401 1402 =back 1403 1404 =cut 1405 1406 # ------------------------------------------------------------------------------ 1407 1408 =head2 Parameter management 1409 1410 Methods for formatting request parameters as hidden form fields or query string 1411 fragments. 1412 1413 =over 1414 1415 =item hidden_fields(@fields) 1416 1417 Return hidden <INPUT> tags for each field mentioned in @fields (or all fields if 1418 list is empty), taking data from the current request. 1419 1420 =cut 1421 1422 sub hidden_fields { 1423 my ($self, @fields) = @_; 1424 my $r = $self->r; 1425 1426 @fields = $r->param unless @fields; 1427 1428 my $html = ""; 1429 foreach my $param (@fields) { 1430 my @values = $r->param($param); 1431 $html .= CGI::hidden($param, @values); 1432 } 1433 return $html; 1434 } 1435 1436 =item hidden_authen_fields() 1437 1438 Use hidden_fields to return hidden <INPUT> tags for request fields used in 1439 authentication. 1440 1441 =cut 1442 1443 sub hidden_authen_fields { 1444 my ($self) = @_; 1445 1446 return $self->hidden_fields("user", "effectiveUser", "key"); 1447 } 1448 1449 =item hidden_proctor_authen_fields() 1450 1451 Use hidden_fields to return hidden <INPUT> tags for request fields used in 1452 proctor authentication. 1453 1454 =cut 1455 1456 sub hidden_proctor_authen_fields { 1457 my $self = shift; 1458 if ( $self->r->param('proctor_user') ) { 1459 return $self->hidden_fields("proctor_user", "proctor_key"); 1460 } else { 1461 return ''; 1462 } 1463 } 1464 1465 =item hidden_state_fields() 1466 1467 Use hidden_fields to return hidden <INPUT> tags for request fields used to 1468 maintain state. Currently includes authentication fields and display option 1469 fields. 1470 1471 =cut 1472 1473 sub hidden_state_fields { 1474 my ($self) = @_; 1475 1476 return $self->hidden_authen_fields(); 1477 1478 # other things that may be state data: 1479 #$self->hidden_fields("displayMode", "showOldAnswers", "showCorrectAnswers", "showHints", "showSolutions"); 1480 } 1481 1482 =item url_args(@fields) 1483 1484 Return a URL query string (without the leading `?') containing values for each 1485 field mentioned in @fields, or all fields if list is empty. Data is taken from 1486 the current request. 1487 1488 =cut 1489 1490 sub url_args { 1491 my ($self, @fields) = @_; 1492 my $r = $self->r; 1493 1494 @fields = $r->param unless @fields; 1495 1496 my @pairs; 1497 foreach my $param (@fields) { 1498 my @values = $r->param($param); 1499 foreach my $value (@values) { 1500 push @pairs, uri_escape($param) . "=" . uri_escape($value); 1501 } 1502 } 1503 1504 return join("&", @pairs); 1505 } 1506 1507 =item url_authen_args() 1508 1509 Use url_args to return a URL query string for request fields used in 1510 authentication. 1511 1512 =cut 1513 1514 sub url_authen_args { 1515 my ($self) = @_; 1516 1517 return $self->url_args("user", "effectiveUser", "key"); 1518 } 1519 1520 =item url_state_args() 1521 1522 Use url_args to return a URL query string for request fields used to maintain 1523 state. Currently includes authentication fields and display option fields. 1524 1525 =cut 1526 1527 sub url_state_args { 1528 my ($self) = @_; 1529 1530 return $self->url_authen_args; 1531 1532 # other things that may be state data: 1533 #$self->url_args("displayMode", "showOldAnswers", "showCorrectAnswers", "showHints", "showSolutions"); 1534 } 1535 1536 # This method is not used anywhere! --sam(1-Aug-05) 1537 # 1538 #=item url_display_args() 1539 # 1540 #Use url_args to return a URL query string for request fields used in 1541 #authentication. 1542 # 1543 #=cut 1544 # 1545 #sub url_display_args { 1546 # my ($self) = @_; 1547 # 1548 # return $self->url_args("displayMode", "showOldAnswer"); 1549 #} 1550 1551 # This method is not used anywhere! --sam(1-Aug-05) 1552 # 1553 #=item print_form_data($begin, $middle, $end, $omit) 1554 # 1555 #Return a string containing every request field not matched by the quoted reguar 1556 #expression $omit, placing $begin before each field name, $middle between each 1557 #field name and its value, and $end after each value. Values are taken from the 1558 #current request. 1559 # 1560 #=cut 1561 # 1562 #sub print_form_data { 1563 # my ($self, $begin, $middle, $end, $qr_omit) = @_; 1564 # my $r=$self->r; 1565 # my @form_data = $r->param; 1566 # 1567 # my $return_string = ""; 1568 # foreach my $name (@form_data) { 1569 # next if ($qr_omit and $name =~ /$qr_omit/); 1570 # my @values = $r->param($name); 1571 # foreach my $variable (qw(begin name middle value end)) { 1572 # # FIXME: can this loop be moved out of the enclosing loop? 1573 # no strict 'refs'; 1574 # ${$variable} = "" unless defined ${$variable}; 1575 # } 1576 # foreach my $value (@values) { 1577 # $return_string .= "$begin$name$middle$value$end"; 1578 # } 1579 # } 1580 # 1581 # return $return_string; 1582 #} 1583 1584 =back 1585 1586 =cut 1587 1588 # ------------------------------------------------------------------------------ 1589 1590 =head2 Utilities 1591 1592 =over 1593 1594 =item systemLink($urlpath, %options) 1595 1596 Generate a link to another part of the system. $urlpath is WeBWorK::URLPath 1597 object from which the base path will be taken. %options can consist of: 1598 1599 =over 1600 1601 =item params 1602 1603 Can be either a reference to an array or a reference to a hash. 1604 1605 If it is a reference to a hash, it maps parmaeter names to values. These 1606 parameters will be included in the generated link. If a value is an arrayref, 1607 the values of the array referenced will be used. If a value is undefined, the 1608 value from the current request will be used. 1609 1610 If C<params> is an arrayref, it is interpreted as a list of parameter names. 1611 These parameters will be included in the generated link, using the values from 1612 the current request. 1613 1614 Unless C<authen> is false (see below), the authentication parameters (C<user>, 1615 C<effectiveUser>, and C<key>) are included with their default values. 1616 1617 =item authen 1618 1619 If set to a false value, the authentication parameters (C<user>, 1620 C<effectiveUser>, and C<key>) are included in the the generated link unless 1621 explicitly listed in C<params>. 1622 1623 =back 1624 1625 =cut 1626 1627 # FIXME: there should probably be an option for prepending "http://hostname:port" 1628 sub systemLink { 1629 my ($self, $urlpath, %options) = @_; 1630 my $r = $self->r; 1631 1632 my %params = (); 1633 if (exists $options{params}) { 1634 if (ref $options{params} eq "HASH") { 1635 %params = %{ $options{params} }; 1636 } elsif (ref $options{params} eq "ARRAY") { 1637 my @names = @{ $options{params} }; 1638 @params{@names} = (); 1639 } else { 1640 croak "option 'params' is not a hashref or an arrayref"; 1641 } 1642 } 1643 1644 my $authen = exists $options{authen} ? $options{authen} : 1; 1645 if ($authen) { 1646 $params{user} = undef unless exists $params{user}; 1647 $params{effectiveUser} = undef unless exists $params{effectiveUser}; 1648 $params{key} = undef unless exists $params{key}; 1649 } 1650 1651 my $url = $r->location . $urlpath->path; 1652 my $first = 1; 1653 1654 foreach my $name (keys %params) { 1655 my $value = $params{$name}; 1656 1657 my @values; 1658 if (defined $value) { 1659 if (ref $value eq "ARRAY") { 1660 @values = @$value; 1661 } else { 1662 @values = $value; 1663 } 1664 } elsif (defined $r->param($name)) { 1665 @values = $r->param($name); 1666 } 1667 #FIXME -- evntually we'd like to catch where this happens 1668 if ($name eq 'user' and @values >1 ) { 1669 warn "internal error -- user has been multiply defined! You may need to logout and log back in to correct this."; 1670 my $user = $r->param("user"); 1671 $r->param(user => $user); 1672 @values = ($user); 1673 warn "requesting page is ", $r->headers_in->{'Referer'}; 1674 warn "Parameters are ", join("|",$r->param()); 1675 1676 } 1677 1678 if (@values) { 1679 if ($first) { 1680 $url .= "?"; 1681 $first = 0; 1682 } else { 1683 $url .= "&"; 1684 } 1685 $url .= join "&", map { "$name=$_" } @values; 1686 } 1687 } 1688 1689 return $url; 1690 } 1691 1692 =item nbsp($string) 1693 1694 If string consists of only whitespace, the HTML entity C< > is returned. 1695 Otherwise $string is returned. 1696 1697 =cut 1698 1699 sub nbsp { 1700 my ($self, $str) = @_; 1701 return (defined $str && $str =~/\S/) ? $str : " "; 1702 } 1703 1704 =item sp2nbsp($string) 1705 1706 A copy of $string is returned with each space character replaced by the 1707 C< > entity. 1708 1709 =cut 1710 1711 sub sp2nbsp { 1712 my ($str) = @_; 1713 return unless defined $str; 1714 $str =~ s/\s/ /g; 1715 return $str; 1716 } 1717 1718 =item underscore2nbsp($string) 1719 1720 A copy of $string is returned with each underscore character replaced by the 1721 C< > entity. 1722 1723 =cut 1724 1725 sub underscore2nbsp { 1726 my ($str) = @_; 1727 return unless defined $str; 1728 $str =~ s/_/ /g; 1729 return $str; 1730 } 1731 1732 =item errorOutput($error, $details) 1733 1734 Used by Problem, ProblemSet, and Hardcopy to report errors encountered during 1735 problem rendering. 1736 1737 =cut 1738 1739 sub errorOutput($$$) { 1740 my ($self, $error, $details) = @_; 1741 my $r = $self->{r}; 1742 1743 my $time = time2str("%a %b %d %H:%M:%S %Y", time); 1744 my $method = $r->method; 1745 my $uri = $r->uri; 1746 my $headers = do { 1747 my %headers = $r->headers_in; 1748 join("", map { CGI::Tr(CGI::td(CGI::small($_)), CGI::td(CGI::small($headers{$_}))) } keys %headers); 1749 }; 1750 1751 # if it is a long report pass details by reference rather than by value 1752 # for consistency we automatically convert all forms of $details into 1753 # a reference to an array. 1754 1755 if (ref($details) =~ /SCALAR/i) { 1756 $details = [$$details]; 1757 } elsif (ref($details) =~/ARRAY/i) { 1758 # no change needed 1759 } else { 1760 $details = [$details]; 1761 } 1762 return 1763 CGI::h2("WeBWorK Error"), 1764 CGI::p(<<EOF), 1765 WeBWorK has encountered a software error while attempting to process this 1766 problem. It is likely that there is an error in the problem itself. If you are a 1767 student, report this error message to your professor to have it corrected. If 1768 you are a professor, please consult the error output below for more information. 1769 EOF 1770 CGI::h3("Error messages"), 1771 1772 CGI::p(CGI::code($error)), 1773 CGI::h3("Error details"), 1774 1775 CGI::start_code(), CGI::start_p(), 1776 @{ $details }, 1777 #CGI::code(CGI::p(@expandedDetails)), 1778 # not using inclusive CGI calls here saves about 30Meg of memory! 1779 CGI::end_p(),CGI::end_code(), 1780 1781 CGI::h3("Request information"), 1782 CGI::table({border=>"1"}, 1783 CGI::Tr(CGI::td("Time"), CGI::td($time)), 1784 CGI::Tr(CGI::td("Method"), CGI::td($method)), 1785 CGI::Tr(CGI::td("URI"), CGI::td($uri)), 1786 CGI::Tr(CGI::td("HTTP Headers"), CGI::td( 1787 CGI::table($headers), 1788 )), 1789 ), 1790 ; 1791 1792 } 1793 1794 =item warningOutput($warnings) 1795 1796 Used by warnings() in this class to report warnings caught during dispatching 1797 and content generation. 1798 1799 =cut 1800 1801 sub warningOutput($$) { 1802 my ($self, $warnings) = @_; 1803 my $r = $self->{r}; 1804 1805 my @warnings = split m/\n+/, $warnings; 1806 foreach my $warning (@warnings) { 1807 #$warning = escapeHTML($warning); # this would prevent using tables in output from answer evaluators 1808 $warning = CGI::li(CGI::code($warning)); 1809 } 1810 $warnings = join("", @warnings); 1811 1812 my $time = time2str("%a %b %d %H:%M:%S %Y", time); 1813 my $method = $r->method; 1814 my $uri = $r->uri; 1815 #my $headers = do { 1816 # my %headers = $r->headers_in; 1817 # join("", map { CGI::Tr(CGI::td(CGI::small($_)), CGI::td(CGI::small($headers{$_}))) } keys %headers); 1818 #}; 1819 1820 return 1821 CGI::h2("WeBWorK Warnings"), 1822 CGI::p(<<EOF), 1823 WeBWorK has encountered warnings while processing your request. If this occured 1824 when viewing a problem, it was likely caused by an error or ambiguity in that 1825 problem. Otherwise, it may indicate a problem with the WeBWorK system itself. If 1826 you are a student, report these warnings to your professor to have them 1827 corrected. If you are a professor, please consult the warning output below for 1828 more information. 1829 EOF 1830 CGI::h3("Warning messages"), 1831 CGI::ul($warnings), 1832 CGI::h3("Request information"), 1833 CGI::table({border=>"1"}, 1834 CGI::Tr(CGI::td("Time"), CGI::td($time)), 1835 CGI::Tr(CGI::td("Method"), CGI::td($method)), 1836 CGI::Tr(CGI::td("URI"), CGI::td($uri)), 1837 #CGI::Tr(CGI::td("HTTP Headers"), CGI::td( 1838 # CGI::table($headers), 1839 #)), 1840 ); 1841 } 1842 1843 =item $dateTime = parseDateTime($string, $display_tz) 1844 1845 Parses $string as a datetime. If $display_tz is given, $string is assumed to be 1846 in that timezone. Otherwise, the timezone defined in the course environment 1847 variable $siteDefaults{timezone} is used. The result, $dateTime, is an integer 1848 UNIX datetime (epoch) in the server's timezone. 1849 1850 =cut 1851 1852 sub parseDateTime { 1853 my ($self, $string, $display_tz) = @_; 1854 my $ce = $self->r->ce; 1855 $display_tz ||= $ce->{siteDefaults}{timezone}; 1856 return WeBWorK::Utils::parseDateTime($string, $display_tz); 1857 }; 1858 1859 =item $string = formatDateTime($dateTime, $display_tz) 1860 1861 Formats the UNIX datetime $dateTime in the standard WeBWorK datetime format. 1862 $dateTime is assumed to be in the server's time zone. If $display_tz is given, 1863 the datetime is converted from the server's timezone to the timezone specified. 1864 Otherwise, the timezone defined in the course environment variable 1865 $siteDefaults{timezone} is used. 1866 1867 =cut 1868 1869 sub formatDateTime { 1870 my ($self, $dateTime, $display_tz) = @_; 1871 my $ce = $self->r->ce; 1872 $display_tz ||= $ce->{siteDefaults}{timezone}; 1873 return WeBWorK::Utils::formatDateTime($dateTime, $display_tz); 1874 } 1875 1876 =back 1877 1878 =head1 AUTHOR 1879 1880 Written by Dennis Lambe Jr., malsyned (at) math.rochester.edu and Sam Hathaway, 1881 sh002i (at) math.rochester.edu. 1882 1883 =cut 1884 1885 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |