Parent Directory
|
Revision Log
timezone support
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm,v 1.62 2004/09/10 02:35:19 sh002i Exp $ 5 # 6 # This program is free software; you can redistribute it and/or modify it under 7 # the terms of either: (a) the GNU General Public License as published by the 8 # Free Software Foundation; either version 2, or (at your option) any later 9 # version, or (b) the "Artistic License" which comes with this package. 10 # 11 # This program is distributed in the hope that it will be useful, but WITHOUT 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 14 # Artistic License for more details. 15 ################################################################################ 16 17 package WeBWorK::ContentGenerator::Instructor::ProblemSetEditor; 18 use base qw(WeBWorK::ContentGenerator::Instructor); 19 20 =head1 NAME 21 22 WeBWorK::ContentGenerator::Instructor::ProblemSetEditor - Edit a set definition list 23 24 =cut 25 26 use strict; 27 use warnings; 28 use CGI qw(); 29 use File::Copy; 30 use WeBWorK::DB::Record::Problem; 31 use WeBWorK::Utils qw(readFile list2hash listFilesRecursive max); 32 33 our $rowheight = 20; #controls the length of the popup menus. 34 our $libraryName; #library directory name 35 36 use constant SET_FIELDS => [qw(open_date due_date answer_date set_header hardcopy_header published)]; 37 use constant PROBLEM_FIELDS =>[qw(source_file value max_attempts)]; 38 use constant PROBLEM_USER_FIELDS => [qw(problem_seed status num_correct num_incorrect)]; 39 40 sub getSetName { 41 my ($self, $pathSetName) = @_; 42 if (ref $pathSetName eq "HASH") { 43 $pathSetName = undef; 44 } 45 return $pathSetName; 46 } 47 48 # One wrinkle here: if $override is undefined, do the global thing, 49 # otherwise, it's truth value determines the checkbox and the current fieldValue is not directly editable 50 sub setRowHTML { 51 my ($description, $fieldName, $fieldValue, $size, $override, $overrideValue) = @_; 52 53 my $attributeHash = {type=>"text", name=>$fieldName, value=>$fieldValue}; 54 $attributeHash->{size} = $size if defined $size; 55 56 my $input = (defined $override) ? $fieldValue : CGI::input($attributeHash); 57 58 my $html = CGI::td({}, [$description, $input]); 59 60 if (defined $override) { 61 $attributeHash->{name}="${fieldName}_override"; 62 $attributeHash->{value}=($override ? $overrideValue : "" ); 63 64 $html .= CGI::td({}, [ 65 CGI::checkbox({ 66 type=>"checkbox", 67 name=>"override", 68 label=>"override with:", 69 value=>$fieldName, 70 checked=>($override ? 1 : 0) 71 }), 72 CGI::input($attributeHash) 73 ]); 74 } 75 76 return $html; 77 78 } 79 80 # Initialize does all of the form processing. It's extensive, and could probably be cleaned up and 81 # consolidated with a little abstraction. 82 sub initialize { 83 my ($self) = @_; 84 my $r = $self->r; 85 my $db = $r->db; 86 my $ce = $r->ce; 87 my $authz = $r->authz; 88 my $user = $r->param('user'); 89 #my $setName = $self->getSetName(@components); 90 my $setName = $r->urlpath->arg("setID"); 91 my $setRecord = $db->getGlobalSet($setName); #checked 92 die "global set $setName not found." unless $setRecord; 93 94 $self->{set} = $setRecord; 95 my @editForUser = $r->param('editForUser'); 96 # some useful booleans 97 my $forUsers = scalar(@editForUser); 98 my $forOneUser = $forUsers == 1; 99 100 # build a quick lookup table 101 my %overrides = list2hash $r->param('override'); 102 103 # Check permissions 104 return unless ($authz->hasPermissions($user, "access_instructor_tools")); 105 return unless ($authz->hasPermissions($user, "modify_problem_sets")); 106 107 ################################################### 108 # The set form was submitted with the save button pressed 109 # Save changes to the set 110 ################################################### 111 112 if (defined($r->param('submit_set_changes'))) { 113 114 if (!$forUsers) { 115 foreach (@{SET_FIELDS()}) { 116 if (defined($r->param($_))) { 117 if (m/_date$/) { 118 $setRecord->$_($self->parseDateTime($r->param($_))); 119 } else { 120 $setRecord->$_($r->param($_)) unless ($_ eq 'set_header' and $r->param($_) eq "Use System Default"); 121 122 if($_ eq 'set_header') { 123 # be nice and copy the default file here if it doesn't exist yet 124 # empty set headers lead to trouble 125 my $set_header = ($r->param($_) eq "Use System Default") ? $setRecord->set_header : $r->param($_); 126 127 my $newheaderpath = $r->{ce}->{courseDirs}->{templates} . '/'. $set_header; 128 unless(($set_header !~ /\S/) or -e $newheaderpath) { 129 my $default_header = $ce->{webworkFiles}->{screenSnippets}->{setHeader}; 130 File::Copy::copy($default_header, $newheaderpath); 131 } 132 } 133 } 134 } else { 135 if (m/published$/) { 136 $setRecord->$_(0); 137 } 138 } 139 } 140 141 142 143 144 ################################################### 145 # Check that the open, due and answer dates are in increasing order. 146 # Bail if this is not correct. 147 ################################################### 148 if ($setRecord->open_date > $setRecord->due_date) { 149 $self->addbadmessage('Error: Due date must come after open date'); 150 return; 151 } 152 if ($setRecord->due_date > $setRecord->answer_date) { 153 $self->addbadmessage('Error: Answer date must come after due date'); 154 return; 155 } 156 ################################################### 157 # End date check section. 158 ################################################### 159 $self->addgoodmessage("Changes to set $setName were successfully saved."); 160 $db->putGlobalSet($setRecord); 161 } else { 162 163 my $userSetRecord = $db->getUserSet($editForUser[0], $setName); #checked 164 die "set $setName not found for $editForUser[0]." unless $userSetRecord; 165 foreach my $field (@{SET_FIELDS()}) { 166 if (defined $r->param("${field}_override")) { 167 if (exists $overrides{$field}) { 168 if ($field =~ m/_date$/) { 169 $userSetRecord->$field($self->parseDateTime($r->param("${field}_override"))); 170 } else { 171 $userSetRecord->$field($r->param("${field}_override")); 172 } 173 } else { 174 $userSetRecord->$field(undef); 175 } 176 } 177 } 178 ################################################### 179 # Check that the open, due and answer dates are in increasing order. 180 # Bail if this is not correct. 181 ################################################### 182 my $active_open_date = $userSetRecord->open_date ? $userSetRecord->open_date : $setRecord->open_date; 183 my $active_due_date = $userSetRecord->due_date ? $userSetRecord->due_date : $setRecord->due_date; 184 my $active_answer_date = $userSetRecord->answer_date ? $userSetRecord->answer_date : $setRecord->answer_date; 185 if ( $active_open_date > $active_due_date ) { 186 $self->addbadmessage('Error: Due date override must come after open date'); 187 return; 188 } 189 if ( $active_due_date > $active_answer_date ) { 190 $self->addbadmessage('Error: Answer date override must come after due date'); 191 return; 192 } 193 ################################################### 194 # End date check section. 195 ################################################### 196 $self->addgoodmessage("Changes to set $setName for user ", CGI::b($editForUser[0]), "were successfully saved."); 197 $db->putUserSet($userSetRecord); 198 } 199 200 } 201 202 ################################################### 203 # The set form was submitted with the export button pressed 204 # Export the set structure to a set definition file 205 ################################################### 206 207 if ( defined($r->param('export_set')) ) { 208 my $fileName = $r->param('export_file_name'); 209 die "Please specify a file name for saving the set definition" unless $fileName; 210 $fileName .= '.def' unless $fileName =~ /\.def$/; 211 my $filePath = $ce->{courseDirs}->{templates}.'/'.$fileName; 212 # back up existing file 213 if(-e $filePath) { 214 rename($filePath,"$filePath.bak") or 215 die "Can't rename $filePath to $filePath.bak ", 216 "Check permissions for webserver on directories. $!"; 217 } 218 my $openDate = $self->formatDateTime($setRecord->open_date); 219 my $dueDate = $self->formatDateTime($setRecord->due_date); 220 my $answerDate = $self->formatDateTime($setRecord->answer_date); 221 my $setHeader = $setRecord->set_header; 222 223 my @problemList = $db->listGlobalProblems($setName); 224 my $problemList = ''; 225 foreach my $prob (sort {$a <=> $b} @problemList) { 226 my $problemRecord = $db->getGlobalProblem($setName, $prob); # checked 227 die "global problem $prob for set $setName not found" unless defined($problemRecord); 228 my $source_file = $problemRecord->source_file(); 229 my $value = $problemRecord->value(); 230 my $max_attempts = $problemRecord->max_attempts(); 231 $problemList .= "$source_file, $value, $max_attempts \n"; 232 } 233 my $fileContents = <<EOF; 234 235 openDate = $openDate 236 dueDate = $dueDate 237 answerDate = $answerDate 238 paperHeaderFile = $setHeader 239 screenHeaderFile = $setHeader 240 problemList = 241 242 $problemList 243 244 245 246 EOF 247 248 249 $self->saveProblem($fileContents, $filePath); 250 $self->addgoodmessage(CGI::p("Set definition saved to $filePath")); 251 252 } 253 } 254 255 256 sub body { 257 my ($self, @components) = @_; 258 my $r = $self->r; 259 my $urlpath = $r->urlpath; 260 my $db = $r->db; 261 my $ce = $r->ce; 262 my $authz = $r->authz; 263 my $user = $r->param('user'); 264 my $courseName = $urlpath->arg("courseID"); 265 my $setName = $urlpath->arg("setID"); 266 my $setRecord = $db->getGlobalSet($setName); # checked 267 die "global set $setName not found." unless $setRecord; 268 my @editForUser = $r->param('editForUser'); 269 # some useful booleans 270 my $forUsers = scalar(@editForUser); 271 my $forOneUser = $forUsers == 1; 272 273 # Check permissions 274 return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") 275 unless $authz->hasPermissions($r->param("user"), "access_instructor_tools"); 276 277 return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify problem sets.") 278 unless $authz->hasPermissions($r->param("user"), "modify_problem_sets"); 279 280 281 ## Set Form ## 282 my $userSetRecord; 283 my %overrideArgs; 284 if ($forOneUser) { 285 $userSetRecord = $db->getUserSet($editForUser[0], $setName); #checked 286 die "set $setName not found for user $editForUser[0]." unless $userSetRecord; 287 foreach my $field (@{SET_FIELDS()}) { 288 $overrideArgs{$field} = [defined $userSetRecord->$field && $userSetRecord->$field ne "", ($field =~ /_date$/ ? $self->formatDateTime($userSetRecord->$field) : $userSetRecord->$field)]; 289 } 290 } else { 291 foreach my $field (@{SET_FIELDS()}) { 292 $overrideArgs{$field} = [undef, undef]; 293 } 294 } 295 print CGI::h2({}, "Set Data"), "\n"; 296 if (@editForUser) { 297 print CGI::p("Editing user-specific overrides for ". CGI::b(join ", ", @editForUser)); 298 } 299 300 my $templates_dir = $r->ce->{courseDirs}->{templates}; 301 my %probLibs = %{ $r->ce->{courseFiles}->{problibs} }; 302 my $exempt_dirs = join("|", keys %probLibs); 303 my @headers = listFilesRecursive( 304 $templates_dir, 305 qr/header.*\.pg$/i, # match these files 306 qr/^(?:$exempt_dirs|CVS)$/, # prune these directories 307 0, # match against file name only 308 1, # prune against path relative to $templates_dir 309 ); 310 311 @headers = sort @headers; 312 unshift (@headers, "Use System Default"); 313 314 print CGI::start_form({method=>"post", action=>$r->uri}), "\n"; 315 print CGI::table({}, 316 CGI::Tr({}, [ 317 setRowHTML( "Open Date:", 318 "open_date", 319 $self->formatDateTime($setRecord->open_date), 320 undef, 321 @{$overrideArgs{open_date}})."\n", 322 setRowHTML( "Due Date:", 323 "due_date", 324 $self->formatDateTime($setRecord->due_date), 325 undef, 326 @{$overrideArgs{due_date}})."\n", 327 setRowHTML( "Answer Date:", 328 "answer_date", 329 $self->formatDateTime($setRecord->answer_date), 330 undef, 331 @{$overrideArgs{answer_date}})."\n", 332 # setRowHTML( "Set Header:", "set_header", 333 # $setRecord->set_header, 334 # 32, 335 # @{$overrideArgs{set_header}})."\n", 336 # FIXME we're not using this right at the moment as far as I know. There may someday be a use for it, so don't take this out yet. 337 # setRowHTML( "Problem Header:", 338 # "hardcopy_header", 339 # $setRecord->hardcopy_header, 340 # undef, 341 # @{$overrideArgs{hardcopy_header}})."\n", 342 CGI::td({}, [ "Set Header:" , 343 ($forOneUser) 344 ? $setRecord->set_header || "None selected." 345 : CGI::popup_menu( 346 -name=>'set_header', 347 -values=>\@headers, 348 -default=>0) . 349 "(currently: " . ($setRecord->set_header || "None selected.") . ")" . "\n", 350 ]) 351 ]) 352 ); 353 354 if (@editForUser) { 355 my $publishedClass = ($setRecord->published) ? "Published" : "Unpublished"; 356 my $publishedText = ($setRecord->published) ? "visible to students" : "hidden from students"; 357 print CGI::p("This set is currently", CGI::font({class=>$publishedClass}, $publishedText), 358 CGI::br(), "(You cannot hide or make a set visible for specific users.)"); 359 } else { 360 print CGI::checkbox({type=>"checkbox", name=>"published", label=>"Visible to students", value=>"1", checked=>(($setRecord->published) ? 1 : 0)}), CGI::br(); 361 362 } 363 364 print $self->hiddenEditForUserFields(@editForUser), 365 $self->hidden_authen_fields, 366 CGI::input({type=>"submit", name=>"submit_set_changes", value=>"Save Set", style=>"{width: 13ex}"}), 367 ' '; 368 369 #### link to edit setHeader 370 my $PGProblemEditor = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", 371 courseID => $courseName, 372 setID => $setName, 373 problemID => '0' 374 ); 375 my $setHeaderEditLink = $self->systemLink($PGProblemEditor); 376 if (defined($setRecord) and $setRecord->set_header) { 377 print CGI::a({-href=>$setHeaderEditLink},'Edit set header: '.$setRecord->set_header); 378 } 379 380 print CGI::br(), 381 CGI::submit({ name=>"export_set", label=>"Export Set", style=>"{width: 13ex}"} ), 382 ' as ', 383 CGI::input({type=>'text',name=>'export_file_name',value=>"set$setName.def",size=>32}); 384 385 print CGI::br(); 386 387 388 389 print CGI::end_form(); 390 391 my $problemCount = $db->listGlobalProblems($setName); 392 print CGI::h2({}, "Problems"), "\n"; 393 print CGI::p({}, "This set contains $problemCount problem" . ($problemCount == 1 ? "" : "s")."."); 394 #FIXME 395 # the code below doesn't work --- 396 # get message 397 #no type matches module WeBWorK::ContentGenerator::Instructor::SetsAssignedToUser with args at 398 # /home/gage/webwork/webwork-modperl/lib/WeBWorK/URLPath.pm line 497. 399 # error in URLPath.pm?????? 400 my $problemSetListPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ProblemList", 401 courseID => $courseName, 402 setID => $setName 403 ); 404 405 my $editProblemsURL = $self->systemLink($problemSetListPage, 406 params => ['editForUser'] # include all editForUser parameters 407 ); 408 my $usersAssignedToSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::UsersAssignedToSet", 409 courseID => $courseName, 410 setID => $setName 411 ); 412 413 my $editUsersAssignedToSetURL = $self->systemLink($usersAssignedToSetPage, 414 415 ); 416 print CGI::a({href=>$editProblemsURL}, 417 (@editForUser) ? "Edit the list of problems in this set for ". CGI::b(join ", ", @editForUser) : 418 "Edit the list of problems in this set"); 419 420 unless (@editForUser) { # this is not needed when we are editing details for a user 421 my $userCount = $db->listUsers; 422 my $usersOfSet = $db->countSetUsers($setName); 423 print CGI::h2({}, "Users"), "\n"; 424 print CGI::p({}, "This set is assigned to ".$self->userCountMessage($usersOfSet, $userCount)."."); 425 print CGI::a({href=>$editUsersAssignedToSetURL}, "Determine who this set is assigned to"); 426 } 427 428 return ""; 429 } 430 ########################################################################### 431 # utility 432 ########################################################################### 433 sub saveProblem { 434 my $self = shift; 435 my ($body, $probFileName)= @_; 436 local(*PROBLEM); 437 open (PROBLEM, ">$probFileName") || 438 $self->addbadmessage(CGI::p("Could not open $probFileName for writing. Check that the permissions for this problem are 660 (-rw-rw----)")); 439 print PROBLEM $body; 440 close PROBLEM; 441 chmod 0660, "$probFileName" || 442 $self->addbadmessage(CGI::p("CAN'T CHANGE PERMISSIONS ON FILE $probFileName")); 443 } 444 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |