| 1 | ################################################################################ |
1 | ################################################################################ |
| 2 | # WeBWorK mod_perl (c) 2000-2002 WeBWorK Project |
2 | # WeBWorK Online Homework Delivery System |
| 3 | # $Id$ |
3 | # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ |
|
|
4 | # $CVSHeader: webwork2/lib/WeBWorK.pm,v 1.74 2005/08/17 16:05:48 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. |
| 4 | ################################################################################ |
15 | ################################################################################ |
| 5 | |
16 | |
| 6 | package WeBWorK; |
17 | package WeBWorK; |
| 7 | |
18 | |
| 8 | =head1 NAME |
19 | =head1 NAME |
| … | |
… | |
| 19 | |
30 | |
| 20 | C<WeBWorK> is the dispatcher for the WeBWorK system. Given an Apache request |
31 | C<WeBWorK> is the dispatcher for the WeBWorK system. Given an Apache request |
| 21 | object, it performs authentication and determines which subclass of |
32 | object, it performs authentication and determines which subclass of |
| 22 | C<WeBWorK::ContentGenerator> to call. |
33 | C<WeBWorK::ContentGenerator> to call. |
| 23 | |
34 | |
| 24 | =head1 REQUEST FORMAT |
|
|
| 25 | |
|
|
| 26 | FIXME: write this part |
|
|
| 27 | summary: the URI controls |
|
|
| 28 | |
|
|
| 29 | =cut |
35 | =cut |
| 30 | |
36 | |
| 31 | BEGIN { $main::VERSION = "2.0"; } |
37 | BEGIN { $main::VERSION = "2.1"; } |
| 32 | |
|
|
| 33 | my $timingON = 0; |
|
|
| 34 | |
|
|
| 35 | |
38 | |
| 36 | use strict; |
39 | use strict; |
| 37 | use warnings; |
40 | use warnings; |
| 38 | use Apache::Constants qw(:common REDIRECT DONE); |
41 | use Apache::Constants qw(:common REDIRECT DONE); |
| 39 | use Apache::Request; |
42 | use Time::HiRes qw/time/; |
|
|
43 | |
|
|
44 | # load WeBWorK::Constants before anything else |
|
|
45 | # this sets package variables in several packages |
|
|
46 | use WeBWorK::Constants; |
|
|
47 | |
|
|
48 | # the rest of these are modules that are acutally used by this one |
| 40 | use WeBWorK::Authen; |
49 | use WeBWorK::Authen; |
| 41 | use WeBWorK::Authz; |
50 | use WeBWorK::Authz; |
| 42 | use WeBWorK::ContentGenerator::Feedback; |
|
|
| 43 | use WeBWorK::ContentGenerator::GatewayQuiz; |
|
|
| 44 | use WeBWorK::ContentGenerator::Hardcopy; |
|
|
| 45 | use WeBWorK::ContentGenerator::Instructor::AddUsers; |
|
|
| 46 | use WeBWorK::ContentGenerator::Instructor::Assigner; |
|
|
| 47 | use WeBWorK::ContentGenerator::Instructor::Index; |
|
|
| 48 | #use WeBWorK::ContentGenerator::Instructor::Index2; |
|
|
| 49 | use WeBWorK::ContentGenerator::Instructor::PGProblemEditor; |
|
|
| 50 | use WeBWorK::ContentGenerator::Instructor::ProblemList; |
|
|
| 51 | use WeBWorK::ContentGenerator::Instructor::ProblemSetEditor; |
|
|
| 52 | use WeBWorK::ContentGenerator::Instructor::ProblemSetList; |
|
|
| 53 | use WeBWorK::ContentGenerator::Instructor::UserList; |
|
|
| 54 | use WeBWorK::ContentGenerator::Instructor::SendMail; |
|
|
| 55 | use WeBWorK::ContentGenerator::Instructor::ShowAnswers; |
|
|
| 56 | use WeBWorK::ContentGenerator::Instructor::Scoring; |
|
|
| 57 | use WeBWorK::ContentGenerator::Instructor::ScoringDownload; |
|
|
| 58 | use WeBWorK::ContentGenerator::Instructor::ScoringTotals; |
|
|
| 59 | use WeBWorK::ContentGenerator::Instructor::Stats; |
|
|
| 60 | use WeBWorK::ContentGenerator::Login; |
|
|
| 61 | use WeBWorK::ContentGenerator::Logout; |
|
|
| 62 | use WeBWorK::ContentGenerator::Options; |
|
|
| 63 | use WeBWorK::ContentGenerator::Problem; |
|
|
| 64 | use WeBWorK::ContentGenerator::ProblemSet; |
|
|
| 65 | use WeBWorK::ContentGenerator::ProblemSets; |
|
|
| 66 | use WeBWorK::ContentGenerator::Test; |
|
|
| 67 | use WeBWorK::CourseEnvironment; |
51 | use WeBWorK::CourseEnvironment; |
| 68 | use WeBWorK::DB; |
52 | use WeBWorK::DB; |
| 69 | use WeBWorK::Timing; |
53 | use WeBWorK::Debug; |
|
|
54 | use WeBWorK::Request; |
|
|
55 | use WeBWorK::Upload; |
|
|
56 | use WeBWorK::URLPath; |
|
|
57 | use WeBWorK::Utils qw(runtime_use writeTimingLogEntry); |
| 70 | |
58 | |
| 71 | =head1 THE C<&dispatch> FUNCTION |
59 | use constant AUTHEN_MODULE => "WeBWorK::ContentGenerator::Login"; |
|
|
60 | use constant PROCTOR_AUTHEN_MODULE => "WeBWorK::ContentGenerator::LoginProctor"; |
|
|
61 | use constant FIXDB_MODULE => "WeBWorK::ContentGenerator::FixDB"; |
| 72 | |
62 | |
| 73 | The C<&dispatch> function takes an Apache request object (REQUEST) and returns |
63 | our %SeedCE; |
| 74 | an apache status code. Below is an overview of its operation: |
|
|
| 75 | |
|
|
| 76 | =over |
|
|
| 77 | |
|
|
| 78 | =cut |
|
|
| 79 | |
64 | |
| 80 | sub dispatch($) { |
65 | sub dispatch($) { |
| 81 | my ($apache) = @_; |
66 | my ($apache) = @_; |
| 82 | my $r = Apache::Request->new($apache); |
67 | my $r = new WeBWorK::Request $apache; |
| 83 | # have to deal with unpredictable GET or POST data, and sift |
|
|
| 84 | # through it for the key. So use Apache::Request |
|
|
| 85 | |
68 | |
| 86 | # This stuff is pretty much copied out of the O'Reilly mod_perl book. |
69 | my $method = $r->method; |
| 87 | # It's for figuring out the basepath. I may change this up if I find a |
70 | my $location = $r->location; |
| 88 | # better way to do it. |
71 | my $uri = $r->uri; |
| 89 | my $path_info = $r->path_info || ""; |
72 | my $path_info = $r->path_info | ""; |
| 90 | $path_info =~ s!/+!/!g; # strip multiple forward slashes |
|
|
| 91 | my $current_uri = $r->uri; |
|
|
| 92 | my $args = $r->args; |
73 | my $args = $r->args || ""; |
|
|
74 | #my $webwork_root = $r->dir_config("webwork_root"); |
|
|
75 | #my $pg_root = $r->dir_config("pg_root"); |
| 93 | |
76 | |
| 94 | my ($urlRoot) = $current_uri =~ m/^(.*)$path_info/; |
77 | debug("\n\n===> Begin " . __PACKAGE__ . "::dispatch() <===\n\n"); |
| 95 | |
78 | debug("Hi, I'm the new dispatcher!\n"); |
| 96 | =item Ensure that the URI ends with a "/" |
79 | debug(("-" x 80) . "\n"); |
| 97 | |
|
|
| 98 | Parts of WeBWorK assume that the current URI of a request ends with a "/". If |
|
|
| 99 | this is not the case, a redirection is issued to add the "/". This action will |
|
|
| 100 | discard any POST data associated with the request, so it is essential that all |
|
|
| 101 | POST requests include a "/" at the end of the URI. |
|
|
| 102 | |
|
|
| 103 | =cut |
|
|
| 104 | |
80 | |
| 105 | # If it's a valid WeBWorK URI, it ends in a /. This is assumed |
81 | debug("Okay, I got some basic information:\n"); |
| 106 | # alllll over the place. |
82 | debug("The apache location is $location\n"); |
| 107 | unless (substr($current_uri,-1) eq '/') { |
83 | debug("The request method is $method\n"); |
| 108 | $r->header_out(Location => "$current_uri/" . ($args ? "?$args" : "")); |
84 | debug("The URI is $uri\n"); |
| 109 | return REDIRECT; |
85 | debug("The path-info is $path_info\n"); |
| 110 | # *** any post data gets lost here -- fix that. |
86 | debug("The argument string is $args\n"); |
| 111 | # (actually, it's not a problem, since all URLs generated |
87 | #debug("The WeBWorK root directory is $webwork_root\n"); |
| 112 | # from within the system have trailing slashes, and we don't |
88 | #debug("The PG root directory is $pg_root\n"); |
| 113 | # need POST data from outside the system anyway!) |
89 | debug(("-" x 80) . "\n"); |
|
|
90 | |
|
|
91 | debug("The first thing we need to do is munge the path a little:\n"); |
|
|
92 | |
|
|
93 | my ($path) = $uri =~ m/$location(.*)/; |
|
|
94 | $path = "/" if $path eq ""; # no path at all |
|
|
95 | |
|
|
96 | debug("We can't trust the path-info, so we make our own path.\n"); |
|
|
97 | debug("path-info claims: $path_info\n"); |
|
|
98 | debug("but it's really: $path\n"); |
|
|
99 | debug("(if it's empty, we set it to \"/\".)\n"); |
|
|
100 | |
|
|
101 | $path =~ s|/+|/|g; |
|
|
102 | debug("...and here it is without repeated slashes: $path\n"); |
|
|
103 | |
|
|
104 | # lookbehind assertion for "not a slash" |
|
|
105 | # matches the boundary after the last char |
|
|
106 | $path =~ s|(?<=[^/])$|/|; |
|
|
107 | debug("...and here it is with a trailing slash: $path\n"); |
|
|
108 | |
|
|
109 | debug(("-" x 80) . "\n"); |
|
|
110 | |
|
|
111 | debug("Now we need to look at the path a little to figure out where we are\n"); |
|
|
112 | |
|
|
113 | debug("-------------------- call to WeBWorK::URLPath::newFromPath\n"); |
|
|
114 | my $urlPath = WeBWorK::URLPath->newFromPath($path); |
|
|
115 | debug("-------------------- call to WeBWorK::URLPath::newFromPath\n"); |
|
|
116 | |
|
|
117 | unless ($urlPath) { |
|
|
118 | debug("This path is invalid... see you later!\n"); |
|
|
119 | die "The path '$path' is not valid.\n"; |
|
|
120 | } |
|
|
121 | |
|
|
122 | my $displayModule = $urlPath->module; |
|
|
123 | my %displayArgs = $urlPath->args; |
|
|
124 | |
|
|
125 | unless ($displayModule) { |
|
|
126 | debug("The display module is empty, so we can DECLINE here.\n"); |
|
|
127 | die "No display module found for path '$path'."; |
|
|
128 | } |
|
|
129 | |
|
|
130 | debug("The display module for this path is: $displayModule\n"); |
|
|
131 | debug("...and here are the arguments we'll pass to it:\n"); |
|
|
132 | foreach my $key (keys %displayArgs) { |
|
|
133 | debug("\t$key => $displayArgs{$key}\n"); |
|
|
134 | } |
|
|
135 | |
|
|
136 | my $selfPath = $urlPath->path; |
|
|
137 | my $parent = $urlPath->parent; |
|
|
138 | my $parentPath = $parent ? $parent->path : "<no parent>"; |
|
|
139 | |
|
|
140 | debug("Reconstructing the original path gets us: $selfPath\n"); |
|
|
141 | debug("And we can generate the path to our parent, too: $parentPath\n"); |
|
|
142 | debug("(We could also figure out who our children are, but we'd need to supply additional arguments.)\n"); |
|
|
143 | debug(("-" x 80) . "\n"); |
|
|
144 | |
|
|
145 | debug("The URLPath looks good, we'll add it to the request.\n"); |
|
|
146 | $r->urlpath($urlPath); |
|
|
147 | |
|
|
148 | debug("Now we want to look at the parameters we got.\n"); |
|
|
149 | |
|
|
150 | debug("The raw params:\n"); |
|
|
151 | foreach my $key ($r->param) { |
|
|
152 | debug("\t$key\n"); |
|
|
153 | debug("\t\t$_\n") foreach $r->param($key); |
|
|
154 | } |
|
|
155 | |
|
|
156 | #mungeParams($r); |
|
|
157 | |
|
|
158 | debug("The munged params:\n"); |
|
|
159 | foreach my $key ($r->param) { |
|
|
160 | debug("\t$key\n"); |
|
|
161 | debug("\t\t$_\n") foreach $r->param($key); |
|
|
162 | } |
|
|
163 | |
|
|
164 | debug(("-" x 80) . "\n"); |
|
|
165 | |
|
|
166 | debug("We need to get a course environment (with or without a courseID!)\n"); |
|
|
167 | my $ce = eval { new WeBWorK::CourseEnvironment({ |
|
|
168 | #webworkRoot => $r->dir_config("webwork_root"), |
|
|
169 | #webworkURLRoot => $location, |
|
|
170 | #pgRoot => $r->dir_config("pg_root"), |
|
|
171 | %SeedCE, |
|
|
172 | courseName => $displayArgs{courseID}, |
|
|
173 | }) }; |
|
|
174 | $@ and die "Failed to initialize course environment: $@\n"; |
|
|
175 | debug("Here's the course environment: $ce\n"); |
|
|
176 | $r->ce($ce); |
|
|
177 | |
|
|
178 | my @uploads = $r->upload; |
|
|
179 | foreach my $u (@uploads) { |
|
|
180 | # make sure it's a "real" upload |
|
|
181 | next unless $u->filename; |
|
|
182 | |
|
|
183 | # store the upload |
|
|
184 | my $upload = WeBWorK::Upload->store($u, |
|
|
185 | dir => $ce->{webworkDirs}->{uploadCache} |
|
|
186 | ); |
|
|
187 | |
|
|
188 | # store the upload ID and hash in the file upload field |
|
|
189 | my $id = $upload->id; |
|
|
190 | my $hash = $upload->hash; |
|
|
191 | $r->param($u->name => "$id $hash"); |
|
|
192 | } |
|
|
193 | |
|
|
194 | my ($db, $authz); |
|
|
195 | |
|
|
196 | if ($displayArgs{courseID}) { |
|
|
197 | debug("We got a courseID from the URLPath, now we can do some stuff:\n"); |
|
|
198 | |
|
|
199 | unless (-e $ce->{courseDirs}->{root}) { |
|
|
200 | die "Course '$displayArgs{courseID}' not found: $!"; |
| 114 | } |
201 | } |
| 115 | |
202 | |
| 116 | # Create the @components array, which contains the path specified in the URL |
203 | debug("...we can create a database object...\n"); |
| 117 | my($junk, @components) = split "/", $path_info; |
204 | $db = new WeBWorK::DB($ce->{dbLayout}); |
| 118 | my $webwork_root = $r->dir_config('webwork_root'); # From a PerlSetVar in httpd.conf |
205 | debug("(here's the DB handle: $db)\n"); |
| 119 | my $pg_root = $r->dir_config('pg_root'); # From a PerlSetVar in httpd.conf |
206 | $r->db($db); |
| 120 | my $course = shift @components; |
207 | |
| 121 | |
208 | debug("Now we check the database...\n"); |
| 122 | =item Read the course environment |
209 | debug("(we can detect if a hash-style database from WW1 has not be converted properly.)\n"); |
| 123 | |
210 | my ($dbOK, @dbMessages) = $db->hashDatabaseOK(0); # 0 == don't fix |
| 124 | C<WeBWorK::CourseEnvironment> is used to read the F<global.conf> configuration |
211 | if (not $dbOK) { |
| 125 | file. If a course name was given in the request's URI, it is passed to |
212 | debug("hashDatabaseOK() returned $dbOK -- looks like trouble...\n"); |
| 126 | C<WeBWorK::CourseEnvironment>. In this case, the course-specific configuration |
213 | $displayModule = FIXDB_MODULE; |
| 127 | file (usually F<course.conf>) is also read by C<WeBWorK::CourseEnvironment> at |
214 | debug("set displayModule to $displayModule\n"); |
| 128 | this point. |
|
|
| 129 | |
|
|
| 130 | See also L<WeBWorK::CourseEnvironment>. |
|
|
| 131 | |
|
|
| 132 | =cut |
|
|
| 133 | |
|
|
| 134 | # Try to get the course environment. |
|
|
| 135 | my $ce = eval {WeBWorK::CourseEnvironment->new($webwork_root, $urlRoot, $pg_root, $course);}; |
|
|
| 136 | if ($@) { # If there was an error getting the requested course |
|
|
| 137 | die "Failed to read course environment for $course: $@"; |
|
|
| 138 | } |
|
|
| 139 | |
|
|
| 140 | =item If no course was given, go to the site home page |
|
|
| 141 | |
|
|
| 142 | If the URI did not include the name of a course, a redirection is issued to the |
|
|
| 143 | site home page, given but the course environemnt variable |
|
|
| 144 | C<$ce-E<gt>{webworkURLs}-E<gt>{home}>. |
|
|
| 145 | |
|
|
| 146 | =cut |
|
|
| 147 | |
|
|
| 148 | # If no course was specified, redirect to the home URL |
|
|
| 149 | unless (defined $course) { |
|
|
| 150 | $r->header_out(Location => $ce->{webworkURLs}->{home}); |
|
|
| 151 | return REDIRECT; |
|
|
| 152 | } |
|
|
| 153 | |
|
|
| 154 | =item If the given course does not exist, fail |
|
|
| 155 | |
|
|
| 156 | If the URI did include the name of a course, but the course directory was not |
|
|
| 157 | found, an exception is thrown. |
|
|
| 158 | |
|
|
| 159 | =cut |
|
|
| 160 | |
|
|
| 161 | # Freak out if the requested course doesn't exist. For now, this is just a |
|
|
| 162 | # check to see if the course directory exists. |
|
|
| 163 | my $courseDir = $ce->{webworkDirs}->{courses} . "/$course"; |
|
|
| 164 | unless (-e $courseDir) { |
|
|
| 165 | die "Course directory for $course ($courseDir) not found. Perhaps the course does not exist?"; |
|
|
| 166 | } |
|
|
| 167 | |
|
|
| 168 | =item Initialize the database system |
|
|
| 169 | |
|
|
| 170 | A C<WeBWorK::DB> object is created from the current course environment. |
|
|
| 171 | |
|
|
| 172 | See also L<WeBWorK::DB>. |
|
|
| 173 | |
|
|
| 174 | =cut |
|
|
| 175 | |
|
|
| 176 | # Bring up a connection to the database (for Authen/Authz, and eventually |
|
|
| 177 | # to be passed to content generators, when we clean this file up). |
|
|
| 178 | my $db = WeBWorK::DB->new($ce); |
|
|
| 179 | |
|
|
| 180 | ### Begin dispatching ### |
|
|
| 181 | |
|
|
| 182 | #my $dispatchTimer = WeBWorK::Timing->new(__PACKAGE__."::dispatch"); |
|
|
| 183 | #$dispatchTimer->start; |
|
|
| 184 | |
|
|
| 185 | my $result; |
|
|
| 186 | |
|
|
| 187 | =item Check authentication |
|
|
| 188 | |
|
|
| 189 | Use C<WeBWorK::Authen> to verify that the remote user has authenticated. |
|
|
| 190 | |
|
|
| 191 | See also L<WeBWorK::Authen>. |
|
|
| 192 | |
|
|
| 193 | =cut |
|
|
| 194 | |
|
|
| 195 | # WeBWorK::Authen::verify erases the passwd field and sets the key field |
|
|
| 196 | # if login is successful. |
|
|
| 197 | if (!WeBWorK::Authen->new($r, $ce, $db)->verify) { |
|
|
| 198 | $result = WeBWorK::ContentGenerator::Login->new($r, $ce, $db)->go; |
|
|
| 199 | } else { |
215 | } else { |
| 200 | |
216 | debug("hashDatabaseOK() returned $dbOK -- leaving displayModule as-is\n"); |
| 201 | =item Determine if the user is allowed to set C<effectiveUser> |
217 | } |
| 202 | |
218 | |
| 203 | Use C<WeBWorK::Authz> to determine if the user is allowed to set |
219 | debug("Create an authz object (Authen needs it to check login permission)...\n"); |
| 204 | C<effectiveUser>. If so, set it to the requested value (or set it to the real |
220 | $authz = new WeBWorK::Authz($r); |
| 205 | user name if no value is supplied). If not, set it to the real user name. |
221 | debug("(here's the authz object: $authz)\n"); |
| 206 | |
222 | $r->authz($authz); |
| 207 | See also L<WeBWorK::Authz>. |
223 | |
| 208 | |
224 | debug("...and now we can authenticate the remote user...\n"); |
| 209 | =cut |
225 | my $authen = new WeBWorK::Authen($r); |
| 210 | |
226 | my $authenOK = $authen->verify; |
| 211 | # After we are authenticated, there are some things that need to be |
227 | if ($authenOK) { |
| 212 | # sorted out, Authorization-wize, before we start dispatching to individual |
|
|
| 213 | # content generators. |
|
|
| 214 | my $user = $r->param("user"); |
228 | my $userID = $r->param("user"); |
| 215 | my $effectiveUser = $r->param("effectiveUser") || $user; |
229 | debug("Hi, $userID, glad you made it.\n"); |
| 216 | my $su_authorized = WeBWorK::Authz->new($r, $ce, $db)->hasPermissions($user, "become_student", $effectiveUser); |
|
|
| 217 | $effectiveUser = $user unless $su_authorized; |
|
|
| 218 | $r->param("effectiveUser", $effectiveUser); |
|
|
| 219 | |
|
|
| 220 | =item Create and call the appropriate subclass of C<WeBWorK::ContentGenerator> based on the URI. |
|
|
| 221 | |
|
|
| 222 | The dispatcher logic currently looks like this: |
|
|
| 223 | |
|
|
| 224 | FIXME: write this part |
|
|
| 225 | for now, consult the code |
|
|
| 226 | |
|
|
| 227 | =cut |
|
|
| 228 | |
|
|
| 229 | my $arg = shift @components; |
|
|
| 230 | if (!defined $arg) { # We want the list of problem sets |
|
|
| 231 | $result = WeBWorK::ContentGenerator::ProblemSets->new($r, $ce, $db)->go; |
|
|
| 232 | } elsif ($arg eq "hardcopy") { |
|
|
| 233 | |
230 | |
| 234 | my $hardcopyArgument = shift @components; |
231 | # tell authorizer to cache this user's permission level |
| 235 | $hardcopyArgument = "" unless defined $hardcopyArgument; |
232 | $authz->setCachedUser($userID); |
| 236 | $WeBWorK::timer1 = WeBWorK::Timing->new("hardcopy: $hardcopyArgument") if $timingON == 1; |
|
|
| 237 | $WeBWorK::timer1->start if $timingON == 1; |
|
|
| 238 | |
233 | |
| 239 | my $result = WeBWorK::ContentGenerator::Hardcopy->new($r, $ce, $db)->go($hardcopyArgument); |
234 | debug("Now we deal with the effective user:\n"); |
| 240 | $WeBWorK::timer1 ->stop if $timingON == 1; |
235 | my $eUserID = $r->param("effectiveUser") || $userID; |
| 241 | $WeBWorK::timer1 ->save if $timingON == 1; |
236 | debug("userID=$userID eUserID=$eUserID\n"); |
| 242 | return $result; |
237 | # FIXME: hasPermissions does nothing with $eUserID, and lately we want it to |
| 243 | } elsif ($arg eq "instructor2") { |
238 | # only accept two arguments, so we're removing $eUserID from this call. |
| 244 | my $instructorArgument = shift @components; |
239 | #my $su_authorized = $authz->hasPermissions($userID, "become_student", $eUserID); |
| 245 | if (!defined $instructorArgument) { |
240 | my $su_authorized = $authz->hasPermissions($userID, "become_student"); |
| 246 | $result = WeBWorK::ContentGenerator::Instructor::Index2->new($r, $ce, $db)->go; |
241 | if ($su_authorized) { |
|
|
242 | debug("Ok, looks like you're allowed to become $eUserID. Whoopie!\n"); |
|
|
243 | } else { |
|
|
244 | debug("Uh oh, you're not allowed to become $eUserID. Nice try!\n"); |
|
|
245 | $eUserID = $userID; |
| 247 | } |
246 | } |
| 248 | } elsif ($arg eq "instructor") { |
247 | $r->param("effectiveUser" => $eUserID); |
| 249 | my $instructorArgument = shift @components; |
248 | # if we're doing a proctored test, after the user has been authenticated |
| 250 | if (!defined $instructorArgument) { |
249 | # we need to also check on the proctor. note that in the gateway quiz |
| 251 | $WeBWorK::timer2 = WeBWorK::Timing->new("Instructor index $course:") if $timingON == 1; |
250 | # module we double check this, to be sure that someone isn't taking a |
| 252 | $WeBWorK::timer2->start if $timingON == 1; |
251 | # proctored quiz but calling the unproctored ContentGenerator |
| 253 | $result = WeBWorK::ContentGenerator::Instructor::Index->new($r, $ce, $db)->go; |
252 | my $urlProducedPath = $urlPath->path(); |
| 254 | $WeBWorK::timer2->continue("Listing instructor page is done") if $timingON == 1; |
|
|
| 255 | $WeBWorK::timer2->stop if $timingON == 1; |
|
|
| 256 | $WeBWorK::timer2->save if $timingON == 1; |
|
|
| 257 | } elsif ($instructorArgument eq "scoring") { |
|
|
| 258 | $result = WeBWorK::ContentGenerator::Instructor::Scoring->new($r, $ce, $db)->go; #FIXME!!!! |
|
|
| 259 | } elsif ($instructorArgument eq "add_users") { |
|
|
| 260 | $result = WeBWorK::ContentGenerator::Instructor::AddUsers->new($r, $ce, $db)->go; #FIXME!!!! |
|
|
| 261 | } elsif ($instructorArgument eq "scoringDownload") { |
|
|
| 262 | $result = WeBWorK::ContentGenerator::Instructor::ScoringDownload->new($r, $ce, $db)->go; |
|
|
| 263 | } elsif ($instructorArgument eq "scoring_totals") { |
|
|
| 264 | $result = WeBWorK::ContentGenerator::Instructor::ScoringTotals->new($r, $ce, $db)->go; |
|
|
| 265 | } elsif ($instructorArgument eq "users") { |
|
|
| 266 | $result = WeBWorK::ContentGenerator::Instructor::UserList->new($r, $ce, $db)->go; |
|
|
| 267 | } elsif ($instructorArgument eq "sets") { |
|
|
| 268 | my $setID = shift @components; |
|
|
| 269 | if (defined $setID) { |
|
|
| 270 | my $setArg = shift @components; |
|
|
| 271 | if (!defined $setArg) { |
|
|
| 272 | $result = WeBWorK::ContentGenerator::Instructor::ProblemSetEditor->new($r, $ce, $db)->go($setID); |
|
|
| 273 | } elsif ($setArg eq "problems") { |
|
|
| 274 | $result = WeBWorK::ContentGenerator::Instructor::ProblemList->new($r, $ce, $db)->go($setID); |
|
|
| 275 | } elsif ($setArg eq "users") { |
|
|
| 276 | $result = WeBWorK::ContentGenerator::Instructor::Assigner->new($r, $ce, $db)->go($setID); |
|
|
| 277 | } |
|
|
| 278 | } else { |
|
|
| 279 | $WeBWorK::timer2 = WeBWorK::Timing->new("Problem Set List $course:") if $timingON == 1; |
|
|
| 280 | $WeBWorK::timer2->start if $timingON == 1; |
|
|
| 281 | $result = WeBWorK::ContentGenerator::Instructor::ProblemSetList->new($r, $ce, $db)->go; |
|
|
| 282 | $WeBWorK::timer2->continue("Problem Set List is done"); |
|
|
| 283 | $WeBWorK::timer2->stop if $timingON == 1; |
|
|
| 284 | $WeBWorK::timer2->save if $timingON == 1; |
|
|
| 285 | |
253 | |
|
|
254 | if ( $urlProducedPath =~ /proctored_quiz_mode/i ) { |
|
|
255 | my $procAuthOK = $authen->verifyProctor(); |
|
|
256 | |
|
|
257 | if ( $procAuthOK ) { |
|
|
258 | my $proctorUserID = $r->param("proctor_user"); |
|
|
259 | my $proctor_authorized = |
|
|
260 | $authz->hasPermissions($proctorUserID, |
|
|
261 | "proctor_quiz", $userID); |
|
|
262 | if ( ! $proctor_authorized ) { |
|
|
263 | $r->notes("authen_error", |
|
|
264 | "Proctor $proctorUserID is not " . |
|
|
265 | "authorized to proctor tests in " . |
|
|
266 | "this course."); |
|
|
267 | $displayModule = PROCTOR_AUTHEN_MODULE; |
| 286 | } |
268 | } |
| 287 | } elsif ($instructorArgument eq "pgProblemEditor") { |
269 | |
| 288 | $result = WeBWorK::ContentGenerator::Instructor::PGProblemEditor->new($r, $ce, $db)->go(@components); |
270 | } else { |
| 289 | } elsif ($instructorArgument eq "send_mail") { |
271 | $displayModule = PROCTOR_AUTHEN_MODULE; |
| 290 | $result = WeBWorK::ContentGenerator::Instructor::SendMail->new($r, $ce, $db)->go(@components); |
272 | } |
| 291 | } elsif ($instructorArgument eq "show_answers") { |
|
|
| 292 | $result = WeBWorK::ContentGenerator::Instructor::ShowAnswers->new($r, $ce, $db)->go(@components); |
|
|
| 293 | } elsif ($instructorArgument eq "stats") { |
|
|
| 294 | $result = WeBWorK::ContentGenerator::Instructor::Stats->new($r, $ce, $db)->go(@components); |
|
|
| 295 | } |
273 | } |
| 296 | } elsif ($arg eq "options") { |
|
|
| 297 | $result = WeBWorK::ContentGenerator::Options->new($r, $ce, $db)->go; |
|
|
| 298 | } elsif ($arg eq "feedback") { |
|
|
| 299 | $result = WeBWorK::ContentGenerator::Feedback->new($r, $ce, $db)->go; |
|
|
| 300 | } elsif ($arg eq "logout") { |
|
|
| 301 | $result = WeBWorK::ContentGenerator::Logout->new($r, $ce, $db)->go; |
|
|
| 302 | } elsif ($arg eq "test") { |
|
|
| 303 | $result = WeBWorK::ContentGenerator::Test->new($r, $ce, $db)->go; |
|
|
| 304 | } elsif ($arg eq "quiz_mode" ) { |
|
|
| 305 | # Gateway quiz capability -- very similar to problem set (initially) |
|
|
| 306 | $result = WeBWorK::ContentGenerator::GatewayQuiz->new($r, $ce, $db)->go(@components); |
|
|
| 307 | } else { # We've got the name of a problem set. |
|
|
| 308 | my $problem_set = $arg; |
|
|
| 309 | my $ps_arg = shift @components; |
|
|
| 310 | |
274 | |
| 311 | if (!defined $ps_arg) { |
275 | } else { |
| 312 | # list the problems in the problem set |
276 | debug("Bad news: authentication failed!\n"); |
| 313 | $WeBWorK::timer0 = WeBWorK::Timing->new("Problem $course:$problem_set") if $timingON == 1; |
277 | $displayModule = AUTHEN_MODULE; |
| 314 | $WeBWorK::timer0->start if $timingON == 1; |
278 | debug("set displayModule to $displayModule\n"); |
| 315 | $result = WeBWorK::ContentGenerator::ProblemSet->new($r, $ce, $db)->go($problem_set); |
279 | } |
| 316 | $WeBWorK::timer0->continue("problem set listing is done") if $timingON == 1; |
280 | } |
| 317 | $WeBWorK::timer0->stop if $timingON == 1; |
281 | |
| 318 | $WeBWorK::timer0->save if $timingON == 1; |
282 | ## if a course ID was given in the URL and resulted in an error (as stored in $!) |
|
|
283 | ## it probably means that the course does not exist or was misspelled |
|
|
284 | #if ($displayArgs{courseID} and $ce->{'!'}) { |
|
|
285 | # debug("Something was wrong with the courseID: \n"); |
|
|
286 | # debug("\t\t" . $ce->{'!'} . "\n"); |
|
|
287 | # debug("Time to bail!\n"); |
|
|
288 | # die "An error occured while accessing '$displayArgs{courseID}': '", $ce->{'!'}, "'.\n"; |
|
|
289 | #} |
|
|
290 | |
|
|
291 | # store the time before we invoke the content generator |
|
|
292 | my $cg_start = time; # this is Time::HiRes's time, which gives floating point values |
|
|
293 | |
|
|
294 | debug(("-" x 80) . "\n"); |
|
|
295 | debug("Finally, we'll load the display module...\n"); |
|
|
296 | |
|
|
297 | runtime_use($displayModule); |
|
|
298 | |
|
|
299 | debug("...instantiate it...\n"); |
|
|
300 | |
|
|
301 | my $instance = $displayModule->new($r); |
|
|
302 | |
|
|
303 | debug("...and call it:\n"); |
|
|
304 | debug("-------------------- call to ${displayModule}::go\n"); |
|
|
305 | |
|
|
306 | my $result = $instance->go(); |
|
|
307 | |
|
|
308 | debug("-------------------- call to ${displayModule}::go\n"); |
|
|
309 | |
|
|
310 | my $cg_end = time; |
|
|
311 | my $cg_duration = $cg_end - $cg_start; |
|
|
312 | writeTimingLogEntry($ce, "[".$r->uri."]", sprintf("runTime = %.3f sec", $cg_duration)." ".$ce->{dbLayoutName}, ""); |
|
|
313 | |
|
|
314 | debug("returning result: " . (defined $result ? $result : "UNDEF") . "\n"); |
|
|
315 | |
|
|
316 | return $result; |
|
|
317 | } |
|
|
318 | |
|
|
319 | sub mungeParams { |
|
|
320 | my ($r) = @_; |
|
|
321 | |
|
|
322 | my @paramQueue; |
|
|
323 | |
|
|
324 | # remove all the params from the request, and store them in the param queue |
|
|
325 | foreach my $key ($r->param) { |
|
|
326 | push @paramQueue, [ $key => [ $r->param($key) ] ]; |
|
|
327 | $r->parms->unset($key) |
|
|
328 | } |
|
|
329 | |
|
|
330 | # exhaust the param queue, decoding encoded params |
|
|
331 | while (@paramQueue) { |
|
|
332 | my ($key, $values) = @{ shift @paramQueue }; |
|
|
333 | |
|
|
334 | if ($key =~ m/\,/) { |
|
|
335 | # we have multiple params encoded in a single param |
|
|
336 | # split them up and add them to the end of the queue |
|
|
337 | push @paramQueue, map { [ $_, $values ] } split m/\,/, $key; |
|
|
338 | } elsif ($key =~ m/\:/) { |
|
|
339 | # we have a whole param encoded in a key |
|
|
340 | # split it up and add it to the end of the queue |
|
|
341 | my ($newKey, $newValue) = split m/\:/, $key; |
|
|
342 | push @paramQueue, [ $newKey, [ $newValue ] ]; |
|
|
343 | } else { |
|
|
344 | # this is a "normal" param |
|
|
345 | # add it to the param list |
|
|
346 | if (defined $r->param($key)) { |
|
|
347 | # the param already exists -- append the values we have |
|
|
348 | $r->param($key => [ $r->param($key), @$values ]); |
| 319 | } else { |
349 | } else { |
| 320 | # We've got the name of a problem |
350 | # the param doesn't exist -- create it with the values we have |
| 321 | my $problem = $ps_arg; |
351 | $r->param($key => $values); |
| 322 | |
|
|
| 323 | $WeBWorK::timer0 = WeBWorK::Timing->new("Problem $course:$problem_set/$problem") if $timingON == 1; |
|
|
| 324 | $WeBWorK::timer0->start if $timingON == 1; |
|
|
| 325 | # my $pid = fork(); |
|
|
| 326 | # if ($pid) { |
|
|
| 327 | # wait; |
|
|
| 328 | # } else { |
|
|
| 329 | my $result = WeBWorK::ContentGenerator::Problem->new($r, $ce, $db)->go($problem_set, $problem); |
|
|
| 330 | # $WeBWorK::timer0->continue("Exiting child process"); |
|
|
| 331 | # #$WeBWorK::timer0->stop; |
|
|
| 332 | # #$WeBWorK::timer0->save; |
|
|
| 333 | # eval{ APACHE::exit(0);} || warn "Error in leaving child |$@|"; |
|
|
| 334 | # # We REALLY REALLY want this grandchild to exit. But not the child. How to do this |
|
|
| 335 | # # cleanly???? FIXME |
|
|
| 336 | # } |
|
|
| 337 | $WeBWorK::timer0->continue("Problem done)") if $timingON == 1; |
|
|
| 338 | $WeBWorK::timer0->stop if $timingON == 1; |
|
|
| 339 | $WeBWorK::timer0->save if $timingON == 1; |
|
|
| 340 | return $result; |
|
|
| 341 | |
|
|
| 342 | |
|
|
| 343 | } |
352 | } |
| 344 | } |
353 | } |
| 345 | } |
354 | } |
| 346 | |
355 | } |
| 347 | #$dispatchTimer->stop; |
|
|
| 348 | |
356 | |
| 349 | =item Return the result of calling the content generator |
|
|
| 350 | |
357 | |
| 351 | The return value of the content generator's C<&go> function is returned. |
358 | =head1 AUTHOR |
|
|
359 | |
|
|
360 | Written by Dennis Lambe, malsyned at math.rochester.edu. Modified by Sam |
|
|
361 | Hathaway, sh002i at math.rochester.edu. |
| 352 | |
362 | |
| 353 | =cut |
363 | =cut |
| 354 | |
|
|
| 355 | return $result; |
|
|
| 356 | } |
|
|
| 357 | |
|
|
| 358 | =back |
|
|
| 359 | |
|
|
| 360 | =head1 AUTHOR |
|
|
| 361 | |
|
|
| 362 | Written by Dennis Lambe, malsyned at math.rochester.edu. |
|
|
| 363 | |
|
|
| 364 | =cut |
|
|
| 365 | |
364 | |
| 366 | 1; |
365 | 1; |