[system] / trunk / webwork-modperl / lib / WeBWorK.pm Repository:
ViewVC logotype

View of /trunk/webwork-modperl/lib/WeBWorK.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1648 - (download) (as text) (annotate)
Wed Dec 3 20:27:02 2003 UTC (9 years, 5 months ago) by sh002i
File size: 13476 byte(s)
formatting

    1 ################################################################################
    2 # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project
    3 # $Id$
    4 ################################################################################
    5 
    6 package WeBWorK;
    7 
    8 =head1 NAME
    9 
   10 WeBWorK - Dispatch requests to the appropriate content generator.
   11 
   12 
   13 =head1 SYNOPSIS
   14 
   15  my $r = Apache->request;
   16  my $result = eval { WeBWorK::dispatch($r) };
   17  die "something bad happened: $@" if $@;
   18 
   19 =head1 DESCRIPTION
   20 
   21 C<WeBWorK> is the dispatcher for the WeBWorK system. Given an Apache request
   22 object, it performs authentication and determines which subclass of
   23 C<WeBWorK::ContentGenerator> to call.
   24 
   25 =head1 REQUEST FORMAT
   26 
   27  FIXME: write this part
   28  summary: the URI controls
   29 
   30 =cut
   31 
   32 
   33 BEGIN { $main::VERSION = "2.0"; }
   34 
   35 my $timingON = 0;
   36 
   37 use strict;
   38 use warnings;
   39 use Apache::Constants qw(:common REDIRECT DONE);
   40 use Apache::Request;
   41 use WeBWorK::Authen;
   42 use WeBWorK::Authz;
   43 use WeBWorK::CourseEnvironment;
   44 use WeBWorK::DB;
   45 use WeBWorK::Timing;
   46 use WeBWorK::Upload;
   47 use WeBWorK::Utils qw(runtime_use);
   48 
   49 =head1 THE C<&dispatch> FUNCTION
   50 
   51 The C<&dispatch> function takes an Apache request object (REQUEST) and returns
   52 an apache status code. Below is an overview of its operation:
   53 
   54 =over
   55 
   56 =cut
   57 
   58 sub dispatch($) {
   59   my ($apache) = @_;
   60   my $r = Apache::Request->new($apache);
   61     # have to deal with unpredictable GET or POST data, and sift
   62     # through it for the key. So use Apache::Request
   63 
   64   # This stuff is pretty much copied out of the O'Reilly mod_perl book.
   65   # It's for figuring out the basepath. I may change this up if I find a
   66   # better way to do it.
   67   my $path_info = $r->path_info || "";
   68   $path_info =~ s!/+!/!g; # strip multiple forward slashes
   69   my $current_uri = $r->uri;
   70   my $args = $r->args;
   71 
   72   my ($urlRoot) = $current_uri =~ m/^(.*)$path_info/;
   73 
   74 =item Ensure that the URI ends with a "/"
   75 
   76 Parts of WeBWorK assume that the current URI of a request ends with a "/". If
   77 this is not the case, a redirection is issued to add the "/". This action will
   78 discard any POST data associated with the request, so it is essential that all
   79 POST requests include a "/" at the end of the URI.
   80 
   81 =cut
   82 
   83   # If it's a valid WeBWorK URI, it ends in a /.  This is assumed
   84   # alllll over the place.
   85   unless (substr($current_uri,-1) eq '/') {
   86     $r->header_out(Location => "$current_uri/" . ($args ? "?$args" : ""));
   87     return REDIRECT;
   88     # *** any post data gets lost here -- fix that.
   89     # (actually, it's not a problem, since all URLs generated
   90     # from within the system have trailing slashes, and we don't
   91     # need POST data from outside the system anyway!)
   92   }
   93 
   94   # Create the @components array, which contains the path specified in the URL
   95   my($junk, @components) = split "/", $path_info;
   96   my $webwork_root = $r->dir_config('webwork_root'); # From a PerlSetVar in httpd.conf
   97   my $pg_root = $r->dir_config('pg_root'); # From a PerlSetVar in httpd.conf
   98   my $course = shift @components;
   99 
  100 =item Read the course environment
  101 
  102 C<WeBWorK::CourseEnvironment> is used to read the F<global.conf> configuration
  103 file. If a course name was given in the request's URI, it is passed to
  104 C<WeBWorK::CourseEnvironment>. In this case, the course-specific configuration
  105 file (usually F<course.conf>) is also read by C<WeBWorK::CourseEnvironment> at
  106 this point.
  107 
  108 See also L<WeBWorK::CourseEnvironment>.
  109 
  110 =cut
  111 
  112   # Try to get the course environment.
  113   my $ce = eval {WeBWorK::CourseEnvironment->new($webwork_root, $urlRoot, $pg_root, $course);};
  114   if ($@) { # If there was an error getting the requested course
  115     die "Failed to read course environment for $course: $@";
  116   }
  117 
  118 =item If no course was given, go to the site home page
  119 
  120 If the URI did not include the name of a course, a redirection is issued to the
  121 site home page, given but the course environemnt variable
  122 C<$ce-E<gt>{webworkURLs}-E<gt>{home}>.
  123 
  124 =cut
  125 
  126   # If no course was specified, redirect to the home URL
  127   unless (defined $course) {
  128     $r->header_out(Location => $ce->{webworkURLs}->{home});
  129     return REDIRECT;
  130   }
  131 
  132 =item If the given course does not exist, fail
  133 
  134 If the URI did include the name of a course, but the course directory was not
  135 found, an exception is thrown.
  136 
  137 =cut
  138 
  139   # Freak out if the requested course doesn't exist.  For now, this is just a
  140   # check to see if the course directory exists.
  141   my $courseDir = $ce->{webworkDirs}->{courses} . "/$course";
  142   unless (-e $courseDir) {
  143     die "Course directory for $course ($courseDir) not found. Perhaps the course does not exist?";
  144   }
  145 
  146 =item Initialize the database system
  147 
  148 A C<WeBWorK::DB> object is created from the current course environment.
  149 
  150 See also L<WeBWorK::DB>.
  151 
  152 =cut
  153 
  154   # Bring up a connection to the database (for Authen/Authz, and eventually
  155   # to be passed to content generators, when we clean this file up).
  156   my $db = WeBWorK::DB->new($ce);
  157 
  158 =item Capture any uploads
  159 
  160 Before checking authentication, we store any uploads sent by the client
  161 and replace them with parameters referencing the stored uploads.
  162 
  163 =cut
  164 
  165   my @uploads = $r->upload;
  166   foreach my $u (@uploads) {
  167     # make sure it's a "real" upload
  168     next unless $u->filename;
  169 
  170     # store the upload
  171     my $upload = WeBWorK::Upload->store($u,
  172       dir => $ce->{webworkDirs}->{uploadCache}
  173     );
  174 
  175     # store the upload ID and hash in the file upload field
  176     my $id = $upload->id;
  177     my $hash = $upload->hash;
  178     $r->param($u->name => "$id $hash");
  179   }
  180 
  181 =item Check authentication
  182 
  183 Use C<WeBWorK::Authen> to verify that the remote user has authenticated.
  184 
  185 See also L<WeBWorK::Authen>.
  186 
  187 =cut
  188 
  189   ### Begin dispatching ###
  190 
  191   my $contentGenerator = "";
  192   my @arguments = ();
  193 
  194   # WeBWorK::Authen::verify erases the passwd field and sets the key field
  195   # if login is successful.
  196   if (!WeBWorK::Authen->new($r, $ce, $db)->verify) {
  197     $contentGenerator = "WeBWorK::ContentGenerator::Login";
  198     @arguments = ();
  199   }
  200   else {
  201 
  202 =item Determine if the user is allowed to set C<effectiveUser>
  203 
  204 Use C<WeBWorK::Authz> to determine if the user is allowed to set
  205 C<effectiveUser>. If so, set it to the requested value (or set it to the real
  206 user name if no value is supplied). If not, set it to the real user name.
  207 
  208 See also L<WeBWorK::Authz>.
  209 
  210 =cut
  211 
  212     # After we are authenticated, there are some things that need to be
  213     # sorted out, Authorization-wize, before we start dispatching to individual
  214     # content generators.
  215     my $user = $r->param("user");
  216     my $effectiveUser = $r->param("effectiveUser") || $user;
  217     my $authz = WeBWorK::Authz->new($r, $ce, $db);
  218     my $su_authorized = $authz->hasPermissions($user, "become_student", $effectiveUser);
  219     $effectiveUser = $user unless $su_authorized;
  220     $r->param("effectiveUser", $effectiveUser);
  221 
  222 =item Determine the appropriate subclass of C<WeBWorK::ContentGenerator> to call based on the URI.
  223 
  224 The dispatcher implements a virtual heirarchy that looks like this:
  225 
  226  $courseID ($courseID) - list of sets
  227   hardcopy (Hardcopy Generator) - generate hardcopy for user/set pairs
  228   options (User Options) - change email address and password
  229   feedback (Feedback) - send feedback to professor via email
  230   logout (Logout) - expire session and erase authentication tokens
  231   test (Test) - display request information
  232   quiz_mode (Quiz) - "quiz" containing all problems from a set
  233   instructor (Instructor Tools) - main menu for instructor tools
  234     add_users (Add Users) - to be removed
  235     scoring (Scoring Tools) - generate scoring files for problem sets
  236     scoringDownload - send a scoring file to the client
  237       scoring_totals - ???
  238     users (Users) - view/edit users
  239       $userID ($userID) - user detail for given user
  240         sets (Assigned Sets) - view/edit sets assigned to given user
  241     sets (Sets) - list of sets, add new sets, delete existing sets
  242       $setID - view/edit the given set
  243         problems (Problems) - view/edit problems in the given set
  244           $problemID - this is where the pg problem editor SHOULD be
  245         users (Users Assigned) - view/edit users to whom the given set is assigned
  246     pgProblemEditor (Problem Source) - edit the source of a problem
  247     send_mail (Mail Merge) - send mail to users in course
  248     show_answers (Answers Submitted) - show submitted answers
  249     stats (Statistics) - show statistics
  250     files (File Transfer) - transfer files to/from the client
  251   $setID ($setID) - list of problems in the given set
  252     $problemID ($problemID) - interactive display of problem
  253 
  254 =cut
  255 
  256     my $arg = shift @components || "";
  257     if (not $arg) { # We want the list of problem sets
  258       $contentGenerator = "WeBWorK::ContentGenerator::ProblemSets";
  259       @arguments = ();
  260     }
  261     elsif ($arg eq "hardcopy") {
  262       my $setID = shift @components || "";
  263       $contentGenerator = "WeBWorK::ContentGenerator::Hardcopy";
  264       @arguments = ($setID);
  265     }
  266     elsif ($arg eq "options") {
  267       $contentGenerator = "WeBWorK::ContentGenerator::Options";
  268       @arguments = ();
  269     }
  270     elsif ($arg eq "feedback") {
  271       $contentGenerator = "WeBWorK::ContentGenerator::Feedback";
  272       @arguments = ();
  273     }
  274     elsif ($arg eq "logout") {
  275       $contentGenerator = "WeBWorK::ContentGenerator::Logout";
  276       @arguments = ();
  277     }
  278     elsif ($arg eq "test") {
  279       $contentGenerator = "WeBWorK::ContentGenerator::Test";
  280       @arguments = ();
  281     }
  282     elsif ($arg eq "quiz_mode" ) {
  283       $contentGenerator = "WeBWorK::ContentGenerator::GatewayQuiz";
  284       @arguments = @components;
  285     }
  286     elsif ($arg eq "instructor") {
  287       my $instructorArgument = shift @components || "";
  288 
  289       if (not $instructorArgument) {
  290         $contentGenerator = "WeBWorK::ContentGenerator::Instructor::Index";
  291         @arguments = ();
  292       }
  293       elsif ($instructorArgument eq "add_users") {
  294         $contentGenerator = "WeBWorK::ContentGenerator::Instructor::AddUsers";
  295         @arguments = ();
  296       }
  297       elsif ($instructorArgument eq "scoring") {
  298         $contentGenerator = "WeBWorK::ContentGenerator::Instructor::Scoring";
  299         @arguments = ();
  300       }
  301 #       elsif ($instructorArgument eq "scoring_totals") {
  302 #         $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ScoringTotals";
  303 #         @arguments = ();
  304 #       }
  305       elsif ($instructorArgument eq "scoringDownload") {
  306         $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ScoringDownload";
  307         @arguments = ();
  308       }
  309       elsif ($instructorArgument eq "users") {
  310         my $userID = shift @components || "";
  311 
  312         if ($userID) {
  313           my $userArg = shift @components || "";
  314           if (defined $userArg) {
  315             if ($userArg eq "sets") {
  316               $contentGenerator = "WeBWorK::ContentGenerator::Instructor::SetsAssignedToUser";
  317               @arguments = ($userID);
  318             }
  319           }
  320           else {
  321             $contentGenerator = "WeBWorK::ContentGenerator::Instructor::UserDetail";
  322             @arguments = ($userID);
  323           }
  324         }
  325         else {
  326           $contentGenerator = "WeBWorK::ContentGenerator::Instructor::UserList";
  327           @arguments = ();
  328         }
  329       }
  330       elsif ($instructorArgument eq "sets") {
  331         my $setID = shift @components || "";
  332 
  333         if ($setID) {
  334           my $setArg = shift @components || "";
  335 
  336           if ($setArg) {
  337             if ($setArg eq "problems") {
  338               $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ProblemList";
  339               @arguments = ($setID);
  340             }
  341             elsif ($setArg eq "users") {
  342               $contentGenerator = "WeBWorK::ContentGenerator::Instructor::Assigner";
  343               @arguments = ($setID);
  344             }
  345           }
  346           else {
  347             $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ProblemSetEditor";
  348             @arguments = ($setID);
  349           }
  350         }
  351         else {
  352           $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ProblemSetList";
  353           @arguments = ();
  354 
  355         }
  356       }
  357       elsif ($instructorArgument eq "pgProblemEditor") {
  358         $contentGenerator = "WeBWorK::ContentGenerator::Instructor::PGProblemEditor";
  359         @arguments = @components;
  360       }
  361       elsif ($instructorArgument eq "send_mail") {
  362         $contentGenerator = "WeBWorK::ContentGenerator::Instructor::SendMail";
  363         @arguments = @components;
  364       }
  365       elsif ($instructorArgument eq "show_answers") {
  366         $contentGenerator = "WeBWorK::ContentGenerator::Instructor::ShowAnswers";
  367         @arguments = @components;
  368       }
  369       elsif ($instructorArgument eq "stats") {
  370         $contentGenerator = "WeBWorK::ContentGenerator::Instructor::Stats";
  371         @arguments = @components;
  372       }
  373       elsif ($instructorArgument eq "files") {
  374         $contentGenerator = "WeBWorK::ContentGenerator::Instructor::FileXfer";
  375         @arguments = @components;
  376       }
  377     }
  378     else {
  379       # $arg is a set ID
  380       my $setID = $arg;
  381       my $problemID = shift @components;
  382 
  383       if (defined $problemID) {
  384         $contentGenerator = "WeBWorK::ContentGenerator::Problem";
  385         @arguments = ($setID, $problemID);
  386       }
  387       else {
  388         $contentGenerator = "WeBWorK::ContentGenerator::ProblemSet";
  389         @arguments = ($setID);
  390       }
  391     }
  392   }
  393 
  394 =item Call the selected content generator
  395 
  396 Instantiate the selected subclass of content generator and call its C<&go> method. Store the result.
  397 
  398 =cut
  399 
  400   my $result;
  401   if ($contentGenerator) {
  402     runtime_use($contentGenerator);
  403     my $cg = $contentGenerator->new($r, $ce, $db);
  404 
  405     $WeBWorK::timer = WeBWorK::Timing->new("${contentGenerator}::go(@arguments)") if $timingON == 1;
  406     $WeBWorK::timer->start if $timingON == 1;
  407 
  408     $result = $cg->go(@arguments);
  409 
  410     $WeBWorK::timer->stop if $timingON == 1;
  411     $WeBWorK::timer->save if $timingON == 1;
  412   } else {
  413     return NOT_FOUND;
  414   }
  415 
  416 =item Return the result of calling the content generator
  417 
  418 The return value of the content generator's C<&go> function is returned.
  419 
  420 =cut
  421 
  422   return $result;
  423 }
  424 
  425 =back
  426 
  427 =head1 AUTHOR
  428 
  429 Written by Dennis Lambe, malsyned at math.rochester.edu. Modified by Sam
  430 Hathaway, sh002i at math.rochester.edu.
  431 
  432 =cut
  433 
  434 1;

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9