Parent Directory
|
Revision Log
This commit was manufactured by cvs2svn to create branch 'rel-2-3-dev'.
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/ProblemSet.pm,v 1.84 2006/07/16 02:40:34 gage Exp $ 5 # 6 # This program is free software; you can redistribute it and/or modify it under 7 # the terms of either: (a) the GNU General Public License as published by the 8 # Free Software Foundation; either version 2, or (at your option) any later 9 # version, or (b) the "Artistic License" which comes with this package. 10 # 11 # This program is distributed in the hope that it will be useful, but WITHOUT 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 14 # Artistic License for more details. 15 ################################################################################ 16 17 package WeBWorK::ContentGenerator::ProblemSet; 18 use base qw(WeBWorK::ContentGenerator); 19 20 =head1 NAME 21 22 WeBWorK::ContentGenerator::ProblemSet - display an index of the problems in a 23 problem set. 24 25 =cut 26 27 use strict; 28 use warnings; 29 #use CGI qw(-nosticky *ul *li); 30 use WeBWorK::CGI; 31 use WeBWorK::PG; 32 use URI::Escape; 33 use WeBWorK::Debug; 34 use WeBWorK::Utils qw(sortByName path_is_subdir); 35 36 sub initialize { 37 my ($self) = @_; 38 my $r = $self->r; 39 my $db = $r->db; 40 my $urlpath = $r->urlpath; 41 my $authz = $r->authz; 42 43 my $setName = $urlpath->arg("setID"); 44 my $userName = $r->param("user"); 45 my $effectiveUserName = $r->param("effectiveUser"); 46 $self->{displayMode} = $r->param('displayMode') || $r->ce->{pg}->{options}->{displayMode}; 47 48 49 my $user = $db->getUser($userName); # checked 50 my $effectiveUser = $db->getUser($effectiveUserName); # checked 51 my $set = $db->getMergedSet($effectiveUserName, $setName); # checked 52 53 die "user $user (real user) not found." unless $user; 54 die "effective user $effectiveUserName not found. One 'acts as' the effective user." unless $effectiveUser; 55 56 # FIXME: some day it would be nice to take out this code and consolidate the two checks 57 58 # get result and send to message 59 my $status_message = $r->param("status_message"); 60 $self->addmessage(CGI::p("$status_message")) if $status_message; 61 62 # because of the database fix, we have to split our invalidSet check into two parts 63 # First, if $set is undefined then $setName was never valid 64 $self->{invalidSet} = not defined $set; 65 return if $self->{invalidSet}; 66 67 # now that the set is valid, make sure that GatewayQuiz assignments don't get 68 # entered through this module 69 die "set $setName is a GatewayQuiz. Enter through the GatewayQuiz " . 70 "module." if ( defined( $set->assignment_type() ) && 71 $set->assignment_type() =~ /gateway/ ); 72 73 # Database fix (in case of undefined published values) 74 # this is only necessary because some people keep holding to ww1.9 which did not have a published field 75 # make sure published is set to 0 or 1 76 if ($set->published ne "0" and $set->published ne "1") { 77 my $globalSet = $db->getGlobalSet($set->set_id); 78 $globalSet->published("1"); # defaults to published 79 $db->putGlobalSet($globalSet); 80 $set = $db->getMergedSet($effectiveUserName, $set->set_id); 81 } 82 83 # Second, a set is invalid if it is still unpublished and the user does not have the right permissions 84 $self->{invalidSet} = !($set->published || $authz->hasPermissions($userName, "view_unpublished_sets")); 85 return if $self->{invalidSet}; 86 87 my $publishedText = ($set->published) ? "visible to students." : "hidden from students."; 88 my $publishedClass = ($set->published) ? "Published" : "Unpublished"; 89 $self->addmessage(CGI::p("This set is " . CGI::font({class=>$publishedClass}, $publishedText))) if $authz->hasPermissions($userName, "view_unpublished_sets"); 90 91 $self->{userName} = $userName; 92 $self->{user} = $user; 93 $self->{effectiveUser} = $effectiveUser; 94 $self->{set} = $set; 95 96 ##### permissions ##### 97 98 $self->{isOpen} = time >= $set->open_date || $authz->hasPermissions($userName, "view_unopened_sets"); 99 } 100 101 sub nav { 102 my ($self, $args) = @_; 103 my $r = $self->r; 104 my $urlpath = $r->urlpath; 105 106 my $courseID = $urlpath->arg("courseID"); 107 #my $problemSetsPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", courseID => $courseID); 108 my $problemSetsPage = $urlpath->parent; 109 110 my @links = ("Homework Sets" , $r->location . $problemSetsPage->path, "navUp"); 111 # CRAP ALERT: this line relies on the hacky options() implementation in ContentGenerator. 112 # we need to find a better way to do this -- long range dependencies like this are dangerous! 113 #my $tail = "&displayMode=".$self->{displayMode}."&showOldAnswers=".$self->{will}->{showOldAnswers}; 114 # here is a hack to get some functionality back, but I don't even think it's that important to 115 # have this, since there are SO MANY PLACES where we lose the displayMode, etc. 116 # (oh boy, do we need a session table in the database!) 117 my $displayMode = $r->param("displayMode") || ""; 118 my $showOldAnswers = $r->param("showOldAnswers") || ""; 119 my $tail = "&displayMode=$displayMode&showOldAnswers=$showOldAnswers"; 120 return $self->navMacro($args, $tail, @links); 121 } 122 123 sub siblings { 124 my ($self) = @_; 125 my $r = $self->r; 126 my $db = $r->db; 127 my $authz = $r->authz; 128 my $urlpath = $r->urlpath; 129 130 131 my $courseID = $urlpath->arg("courseID"); 132 my $user = $r->param('user'); 133 my $eUserID = $r->param("effectiveUser"); 134 135 # note that listUserSets does not list versioned sets 136 my @setIDs = sortByName(undef, $db->listUserSets($eUserID)); 137 138 # do not show unpublished siblings unless user is allowed to view unpublished sets, and 139 # exclude gateway tests in all cases 140 if ( $authz->hasPermissions($user, "view_unpublished_sets") ) { 141 @setIDs = grep {my $gs = $db->getGlobalSet( $_ ); 142 $gs->assignment_type() !~ /gateway/} @setIDs; 143 144 } else { 145 # @setIDs = grep {my $visible = $db->getGlobalSet( $_)->published; (defined($visible))? $visible : 1} 146 @setIDs = grep {my $gs = $db->getGlobalSet( $_ ); 147 $gs->assignment_type() !~ /gateway/ && 148 ( defined($gs->published()) ? $gs->published() : 1 )} 149 @setIDs; 150 } 151 152 print CGI::start_div({class=>"info-box", id=>"fisheye"}); 153 print CGI::h2("Sets"); 154 #print CGI::start_ul({class=>"LinksMenu"}); 155 #print CGI::start_li(); 156 #print CGI::span({style=>"font-size:larger"}, "Homework Sets"); 157 print CGI::start_ul(); 158 159 # FIXME: setIDs contain no info on published/unpublished so unpublished sets are still printed 160 debug("Begin printing sets from listUserSets()"); 161 foreach my $setID (@setIDs) { 162 my $setPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", 163 courseID => $courseID, setID => $setID); 164 my $pretty_set_id = $setID; 165 $pretty_set_id =~ s/_/ /g; 166 print CGI::li(CGI::a({title=>$pretty_set_id, href=>$self->systemLink($setPage), 167 params=>{ displayMode => $self->{displayMode}, 168 showOldAnswers => $self->{will}->{showOldAnswers} 169 }}, $pretty_set_id) 170 ) ; 171 } 172 debug("End printing sets from listUserSets()"); 173 174 # FIXME: when database calls are faster, this will get rid of unpublished sibling links 175 #debug("Begin printing sets from getMergedSets()"); 176 #my @userSetIDs = map {[$eUserID, $_]} @setIDs; 177 #my @sets = $db->getMergedSets(@userSetIDs); 178 #foreach my $set (@sets) { 179 # my $setPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", courseID => $courseID, setID => $set->set_id); 180 # print CGI::li(CGI::a({href=>$self->systemLink($setPage)}, $set->set_id)) unless !(defined $set && ($set->published || $authz->hasPermissions($user, "view_unpublished_sets")); 181 #} 182 #debug("Begin printing sets from getMergedSets()"); 183 184 print CGI::end_ul(); 185 #print CGI::end_li(); 186 #print CGI::end_ul(); 187 print CGI::end_div(); 188 189 return ""; 190 } 191 192 sub info { 193 my ($self) = @_; 194 my $r = $self->r; 195 my $ce = $r->ce; 196 my $db = $r->db; 197 my $authz = $r->authz; 198 my $urlpath = $r->urlpath; 199 200 return "" unless $self->{isOpen}; 201 202 my $courseID = $urlpath->arg("courseID"); 203 my $setID = $r->urlpath->arg("setID"); 204 205 my $userID = $r->param("user"); 206 my $eUserID = $r->param("effectiveUser"); 207 208 my $effectiveUser = $db->getUser($eUserID); # checked 209 my $set = $db->getMergedSet($eUserID, $setID); # checked 210 211 die "effective user $eUserID not found. One 'acts as' the effective user." unless $effectiveUser; 212 # FIXME: this was already caught in initialize() 213 die "set $setID for effectiveUser $eUserID not found." unless $set; 214 215 my $psvn = $set->psvn(); 216 217 my $screenSetHeader = $set->set_header || $ce->{webworkFiles}->{screenSnippets}->{setHeader}; 218 my $displayMode = $r->param("displayMode") || $ce->{pg}->{options}->{displayMode}; 219 220 if ($authz->hasPermissions($userID, "modify_problem_sets")) { 221 if (defined $r->param("editMode") and $r->param("editMode") eq "temporaryFile") { 222 $screenSetHeader = $r->param('sourceFilePath'); 223 $screenSetHeader = $ce->{courseDirs}{templates}.'/'.$screenSetHeader unless $screenSetHeader =~ m!^/!; 224 die "sourceFilePath is unsafe!" unless path_is_subdir($screenSetHeader, $ce->{courseDirs}->{templates}); 225 $self->addmessage(CGI::div({class=>'temporaryFile'}, "Viewing temporary file: ", 226 $screenSetHeader)); 227 $displayMode = $r->param("displayMode") if $r->param("displayMode"); 228 } 229 } 230 231 return "" unless defined $screenSetHeader and $screenSetHeader; 232 233 # decide what to do about problem number 234 my $problem = WeBWorK::DB::Record::UserProblem->new( 235 problem_id => 0, 236 set_id => $set->set_id, 237 login_id => $effectiveUser->user_id, 238 source_file => $screenSetHeader, 239 # the rest of Problem's fields are not needed, i think 240 ); 241 242 my $pg = WeBWorK::PG->new( 243 $ce, 244 $effectiveUser, 245 $r->param('key'), 246 $set, 247 $problem, 248 $psvn, 249 {}, # no form fields! 250 { # translation options 251 displayMode => $displayMode, 252 showHints => 0, 253 showSolutions => 0, 254 processAnswers => 0, 255 }, 256 ); 257 258 my $editorURL; 259 if (defined($set) and $authz->hasPermissions($userID, "modify_problem_sets")) { 260 my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", 261 courseID => $courseID, setID => $set->set_id, problemID => 0); 262 $editorURL = $self->systemLink($editorPage, params => { file_type => 'set_header'}); 263 } 264 265 print CGI::start_div({class=>"info-box", id=>"InfoPanel"}); 266 267 if ($editorURL) { 268 print CGI::h2({},"Set Info", CGI::a({href=>$editorURL, target=>"WW_Editor"}, "[edit]")); 269 } else { 270 print CGI::h2("Set Info"); 271 } 272 273 if ($pg->{flags}->{error_flag}) { 274 print CGI::div({class=>"ResultsWithError"}, $self->errorOutput($pg->{errors}, $pg->{body_text})); 275 } else { 276 print $pg->{body_text}; 277 } 278 279 print CGI::end_div(); 280 281 return ""; 282 } 283 284 sub options { shift->optionsMacro } 285 286 sub body { 287 my ($self) = @_; 288 my $r = $self->r; 289 my $ce = $r->ce; 290 my $db = $r->db; 291 my $urlpath = $r->urlpath; 292 293 my $courseID = $urlpath->arg("courseID"); 294 my $setName = $urlpath->arg("setID"); 295 my $effectiveUser = $r->param('effectiveUser'); 296 297 my $set = $db->getMergedSet($effectiveUser, $setName); # checked 298 # FIXME: this was already caught in initialize() 299 # die "set $setName for user $effectiveUser not found" unless $set; 300 301 if ($self->{invalidSet}) { 302 return CGI::div({class=>"ResultsWithError"}, 303 CGI::p("The selected homework set ($setName) is not a valid set for $effectiveUser.")); 304 } 305 306 unless ($self->{isOpen}) { 307 return CGI::div({class=>"ResultsWithError"}, 308 CGI::p("This homework set is not available because it is not yet open.")); 309 } 310 311 #my $hardcopyURL = 312 # $ce->{webworkURLs}->{root} . "/" 313 # . $ce->{courseName} . "/" 314 # . "hardcopy/$setName/?" . $self->url_authen_args; 315 316 my $hardcopyPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Hardcopy", 317 courseID => $courseID, setID => $setName); 318 my $hardcopyURL = $self->systemLink($hardcopyPage); 319 320 print CGI::p(CGI::a({href=>$hardcopyURL}, "Download a hardcopy of this homework set.")); 321 322 my @problemNumbers = $db->listUserProblems($effectiveUser, $setName); 323 324 if (@problemNumbers) { 325 print CGI::start_table(); 326 print CGI::Tr({}, 327 CGI::th("Name"), 328 CGI::th("Attempts"), 329 CGI::th("Remaining"), 330 CGI::th("Worth"), 331 CGI::th("Status"), 332 ); 333 334 foreach my $problemNumber (sort { $a <=> $b } @problemNumbers) { 335 my $problem = $db->getMergedProblem($effectiveUser, $setName, $problemNumber); # checked 336 die "problem $problemNumber in set $setName for user $effectiveUser not found." unless $problem; 337 print $self->problemListRow($set, $problem); 338 } 339 340 print CGI::end_table(); 341 } else { 342 print CGI::p("This homework set contains no problems."); 343 } 344 345 ## feedback form url 346 #my $feedbackPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Feedback", 347 # courseID => $courseID); 348 #my $feedbackURL = $self->systemLink($feedbackPage, authen => 0); # no authen info for form action 349 # 350 ##print feedback form 351 #print 352 # CGI::start_form(-method=>"POST", -action=>$feedbackURL),"\n", 353 # $self->hidden_authen_fields,"\n", 354 # CGI::hidden("module", __PACKAGE__),"\n", 355 # CGI::hidden("set", $self->{set}->set_id),"\n", 356 # CGI::hidden("problem", ''),"\n", 357 # CGI::hidden("displayMode", $self->{displayMode}),"\n", 358 # CGI::hidden("showOldAnswers", ''),"\n", 359 # CGI::hidden("showCorrectAnswers", ''),"\n", 360 # CGI::hidden("showHints", ''),"\n", 361 # CGI::hidden("showSolutions", ''),"\n", 362 # CGI::p({-align=>"left"}, 363 # CGI::submit(-name=>"feedbackForm", -label=>"Email instructor") 364 # ), 365 # CGI::endform(),"\n"; 366 367 print $self->feedbackMacro( 368 module => __PACKAGE__, 369 set => $self->{set}->set_id, 370 problem => "", 371 displayMode => $self->{displayMode}, 372 showOldAnswers => "", 373 showCorrectAnswers => "", 374 showHints => "", 375 showSolutions => "", 376 ); 377 378 return ""; 379 } 380 381 sub problemListRow($$$) { 382 my ($self, $set, $problem) = @_; 383 my $r = $self->r; 384 my $urlpath = $r->urlpath; 385 386 my $courseID = $urlpath->arg("courseID"); 387 my $setID = $set->set_id; 388 my $problemID = $problem->problem_id; 389 390 my $interactiveURL = $self->systemLink( 391 $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", 392 courseID => $courseID, setID => $setID, problemID => $problemID 393 ), 394 params=>{ displayMode => $self->{displayMode}, 395 showOldAnswers => $self->{will}->{showOldAnswers} 396 } 397 ); 398 399 my $interactive = CGI::a({-href=>$interactiveURL}, "Problem $problemID"); 400 my $attempts = $problem->num_correct + $problem->num_incorrect; 401 my $remaining = $problem->max_attempts < 0 402 ? "unlimited" 403 : $problem->max_attempts - $attempts; 404 my $rawStatus = $problem->status || 0; 405 my $status; 406 $status = eval{ sprintf("%.0f%%", $rawStatus * 100)}; # round to whole number 407 $status = 'unknown(FIXME)' if $@; # use a blank if problem status was not defined or not numeric. 408 # FIXME -- this may not cover all cases. 409 410 # my $msg = ($problem->value) ? "" : "(This problem will not count towards your grade.)"; 411 412 return CGI::Tr({}, 413 CGI::td({-nowrap=>1, -align=>"left"},$interactive), 414 CGI::td({-nowrap=>1, -align=>"center"}, 415 [ 416 $attempts, 417 $remaining, 418 $problem->value, 419 $status, 420 ])); 421 } 422 423 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |