Parent Directory
|
Revision Log
added pop up list of set header files fixed false successful save message
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2003 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetEditor.pm,v 1.50 2004/05/13 16:02:55 toenail 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 formatDateTime parseDateTime list2hash readDirectory 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 problem_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 81 # FIXME: this or something similar could get pulled up to Instructor.pm 82 sub recurseDirectory { 83 84 my ($self, $dir, $pattern) = @_; 85 86 my @dirs = grep {$_ ne "." and $_ ne ".." and $_ ne "Library" and $_ ne "CVS" and -d "$dir/$_"} readDirectory($dir); 87 88 my @files = map { "$dir/$_" } $self->read_dir($dir, $pattern); 89 90 foreach (@dirs) { 91 push (@files, $self->recurseDirectory("$dir/$_", $pattern)); 92 } 93 94 return @files; 95 } 96 97 98 99 # Initialize does all of the form processing. It's extensive, and could probably be cleaned up and 100 # consolidated with a little abstraction. 101 sub initialize { 102 my ($self) = @_; 103 my $r = $self->r; 104 my $db = $r->db; 105 my $ce = $r->ce; 106 my $authz = $r->authz; 107 my $user = $r->param('user'); 108 #my $setName = $self->getSetName(@components); 109 my $setName = $r->urlpath->arg("setID"); 110 my $setRecord = $db->getGlobalSet($setName); #checked 111 die "global set $setName not found." unless $setRecord; 112 113 $self->{set} = $setRecord; 114 my @editForUser = $r->param('editForUser'); 115 # some useful booleans 116 my $forUsers = scalar(@editForUser); 117 my $forOneUser = $forUsers == 1; 118 119 # build a quick lookup table 120 my %overrides = list2hash $r->param('override'); 121 122 unless ($authz->hasPermissions($user, "modify_problem_sets")) { 123 $self->addmessage(CGI::div({class=>"ResultsWithError"}, CGI::p("You are not authorized to modify problem sets"))); 124 return; 125 } 126 127 ################################################### 128 # The set form was submitted with the save button pressed 129 # Save changes to the set 130 ################################################### 131 132 if (defined($r->param('submit_set_changes'))) { 133 134 if (!$forUsers) { 135 foreach (@{SET_FIELDS()}) { 136 if (defined($r->param($_))) { 137 if (m/_date$/) { 138 $setRecord->$_(parseDateTime($r->param($_))); 139 } else { 140 $setRecord->$_($r->param($_)); 141 if($_ eq 'set_header') { 142 # be nice and copy the default file here if it doesn't exist yet 143 # empty set headers lead to trouble 144 my $newheaderpath = $r->{ce}->{courseDirs}->{templates} . '/'. $r->param('set_header'); 145 unless(($r->param('set_header') !~ /\S/) or -e $newheaderpath) { 146 my $default_header = $ce->{webworkFiles}->{screenSnippets}->{setHeader}; 147 File::Copy::copy($default_header, $newheaderpath); 148 } 149 } 150 } 151 } else { 152 if (m/published$/) { 153 $setRecord->$_(0); 154 } 155 } 156 } 157 158 159 160 161 ################################################### 162 # Check that the open, due and answer dates are in increasing order. 163 # Bail if this is not correct. 164 ################################################### 165 if ($setRecord->open_date > $setRecord->due_date) { 166 $self->addmessage(CGI::div({class=>'ResultsWithError'},'Error: Due date must come after open date')); 167 return; 168 } 169 if ($setRecord->due_date > $setRecord->answer_date) { 170 $self->addmessage(CGI::div({class=>'ResultsWithError'},'Error: Answer date must come after due date')); 171 return; 172 } 173 ################################################### 174 # End date check section. 175 ################################################### 176 $self->addmessage(CGI::div({class=>'ResultsWithoutError'}, "Changes to set $setName were successfully saved.")); 177 $db->putGlobalSet($setRecord); 178 } else { 179 180 my $userSetRecord = $db->getUserSet($editForUser[0], $setName); #checked 181 die "set $setName not found for $editForUser[0]." unless $userSetRecord; 182 foreach my $field (@{SET_FIELDS()}) { 183 if (defined $r->param("${field}_override")) { 184 if (exists $overrides{$field}) { 185 if ($field =~ m/_date$/) { 186 $userSetRecord->$field(parseDateTime($r->param("${field}_override"))); 187 } else { 188 $userSetRecord->$field($r->param("${field}_override")); 189 } 190 } else { 191 $userSetRecord->$field(undef); 192 } 193 } 194 } 195 ################################################### 196 # Check that the open, due and answer dates are in increasing order. 197 # Bail if this is not correct. 198 ################################################### 199 my $active_open_date = $userSetRecord->open_date ? $userSetRecord->open_date : $setRecord->open_date; 200 my $active_due_date = $userSetRecord->due_date ? $userSetRecord->due_date : $setRecord->due_date; 201 my $active_answer_date = $userSetRecord->answer_date ? $userSetRecord->answer_date : $setRecord->answer_date; 202 if ( $active_open_date > $active_due_date ) { 203 $self->addmessage(CGI::div({class=>'ResultsWithError'},'Error: Due date override must come after open date')); 204 return; 205 } 206 if ( $active_due_date > $active_answer_date ) { 207 $self->addmessage(CGI::div({class=>'ResultsWithError'},'Error: Answer date override must come after due date')); 208 return; 209 } 210 ################################################### 211 # End date check section. 212 ################################################### 213 $self->addmessage(CGI::div({class=>'ResultsWithoutError'}, "Changes to set $setName for user ",CGI::b($editForUser[0]) ,"were successfully saved.")); 214 $db->putUserSet($userSetRecord); 215 } 216 217 } 218 219 ################################################### 220 # The set form was submitted with the export button pressed 221 # Export the set structure to a set definition file 222 ################################################### 223 224 if ( defined($r->param('export_set')) ) { 225 my $fileName = $r->param('export_file_name'); 226 die "Please specify a file name for saving the set definition" unless $fileName; 227 $fileName .= '.def' unless $fileName =~ /\.def$/; 228 my $filePath = $ce->{courseDirs}->{templates}.'/'.$fileName; 229 # back up existing file 230 if(-e $filePath) { 231 rename($filePath,"$filePath.bak") or 232 die "Can't rename $filePath to $filePath.bak ", 233 "Check permissions for webserver on directories. $!"; 234 } 235 my $openDate = formatDateTime($setRecord->open_date); 236 my $dueDate = formatDateTime($setRecord->due_date); 237 my $answerDate = formatDateTime($setRecord->answer_date); 238 my $setHeader = $setRecord->set_header; 239 240 my @problemList = $db->listGlobalProblems($setName); 241 my $problemList = ''; 242 foreach my $prob (sort {$a <=> $b} @problemList) { 243 my $problemRecord = $db->getGlobalProblem($setName, $prob); # checked 244 die "global problem $prob for set $setName not found" unless defined($problemRecord); 245 my $source_file = $problemRecord->source_file(); 246 my $value = $problemRecord->value(); 247 my $max_attempts = $problemRecord->max_attempts(); 248 $problemList .= "$source_file, $value, $max_attempts \n"; 249 } 250 my $fileContents = <<EOF; 251 252 openDate = $openDate 253 dueDate = $dueDate 254 answerDate = $answerDate 255 paperHeaderFile = $setHeader 256 screenHeaderFile = $setHeader 257 problemList = 258 259 $problemList 260 261 262 263 EOF 264 265 266 $self->saveProblem($fileContents, $filePath); 267 $self->addmessage(CGI::div({class=>"ResultsWithoutError"}, CGI::p("Set definition saved to $filePath"))); 268 269 } 270 } 271 272 273 sub body { 274 my ($self, @components) = @_; 275 my $r = $self->r; 276 my $urlpath = $r->urlpath; 277 my $db = $r->db; 278 my $ce = $r->ce; 279 my $authz = $r->authz; 280 my $user = $r->param('user'); 281 my $courseName = $urlpath->arg("courseID"); 282 my $setName = $urlpath->arg("setID"); 283 my $setRecord = $db->getGlobalSet($setName); # checked 284 die "global set $setName not found." unless $setRecord; 285 my @editForUser = $r->param('editForUser'); 286 # some useful booleans 287 my $forUsers = scalar(@editForUser); 288 my $forOneUser = $forUsers == 1; 289 290 return CGI::em("You are not authorized to access the Instructor tools.") unless $authz->hasPermissions($user, "access_instructor_tools"); 291 292 ## Set Form ## 293 my $userSetRecord; 294 my %overrideArgs; 295 if ($forOneUser) { 296 $userSetRecord = $db->getUserSet($editForUser[0], $setName); #checked 297 die "set $setName not found for user $editForUser[0]." unless $userSetRecord; 298 foreach my $field (@{SET_FIELDS()}) { 299 $overrideArgs{$field} = [defined $userSetRecord->$field, ($field =~ /_date$/ ? formatDateTime($userSetRecord->$field) : $userSetRecord->$field)]; 300 } 301 } else { 302 foreach my $field (@{SET_FIELDS()}) { 303 $overrideArgs{$field} = [undef, undef]; 304 } 305 } 306 print CGI::h2({}, "Set Data"), "\n"; 307 if (@editForUser) { 308 print CGI::p("Editing user-specific overrides for ". CGI::b(join ", ", @editForUser)); 309 } 310 311 my @headers = $self->recurseDirectory($self->{ce}->{courseDirs}->{templates}, '(?i)header.*?\\.pg$'); 312 map { s|^$self->{ce}->{courseDirs}->{templates}/?|| } @headers; 313 @headers = sort @headers; 314 315 print CGI::start_form({method=>"post", action=>$r->uri}), "\n"; 316 print CGI::table({}, 317 CGI::Tr({}, [ 318 setRowHTML( "Open Date:", 319 "open_date", 320 formatDateTime($setRecord->open_date), 321 undef, 322 @{$overrideArgs{open_date}})."\n", 323 setRowHTML( "Due Date:", 324 "due_date", 325 formatDateTime($setRecord->due_date), 326 undef, 327 @{$overrideArgs{due_date}})."\n", 328 setRowHTML( "Answer Date:", 329 "answer_date", 330 formatDateTime($setRecord->answer_date), 331 undef, 332 @{$overrideArgs{answer_date}})."\n", 333 # setRowHTML( "Set Header:", "set_header", 334 # $setRecord->set_header, 335 # 32, 336 # @{$overrideArgs{set_header}})."\n", 337 # 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. 338 # setRowHTML( "Problem Header:", 339 # "problem_header", 340 # $setRecord->problem_header, 341 # undef, 342 # @{$overrideArgs{problem_header}})."\n", 343 CGI::td({}, ["Set Header:" , ($forOneUser) ? $setRecord->set_header 344 : CGI::popup_menu(-name=>'set_header', 345 -values=>\@headers, 346 -default=>$setRecord->set_header)]) . "\n", 347 ]) 348 ); 349 350 if (@editForUser) { 351 my $publishedColor = ($setRecord->published) ? "Published" : "Unpublished"; 352 print CGI::p("This set is currently", CGI::font({class=>$publishedColor}, (($setRecord->published) ? "Published" : "Unpublished")), CGI::br(), "(You cannot publish/unpublish a set for specific users.)"); 353 } else { 354 print CGI::checkbox({type=>"checkbox", name=>"published", label=>"Published", value=>"1", checked=>(($setRecord->published) ? 1 : 0)}), CGI::br(); 355 356 } 357 358 print $self->hiddenEditForUserFields(@editForUser), 359 $self->hidden_authen_fields, 360 CGI::input({type=>"submit", name=>"submit_set_changes", value=>"Save Set", style=>"{width: 13ex}"}), 361 ' '; 362 363 #### link to edit setHeader 364 my $PGProblemEditor = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", 365 courseID => $courseName, 366 setID => $setName, 367 problemID => '0' 368 ); 369 my $setHeaderEditLink = $self->systemLink($PGProblemEditor); 370 if (defined($setRecord) and $setRecord->set_header) { 371 print CGI::a({-href=>$setHeaderEditLink},'Edit set header: '.$setRecord->set_header); 372 } 373 374 print CGI::br(), 375 CGI::submit({ name=>"export_set", label=>"Export Set", style=>"{width: 13ex}"} ), 376 ' as ', 377 CGI::input({type=>'text',name=>'export_file_name',value=>"set$setName.def",size=>32}); 378 379 print CGI::br(); 380 381 382 383 print CGI::end_form(); 384 385 my $problemCount = $db->listGlobalProblems($setName); 386 print CGI::h2({}, "Problems"), "\n"; 387 print CGI::p({}, "This set contains $problemCount problem" . ($problemCount == 1 ? "" : "s")."."); 388 #FIXME 389 # the code below doesn't work --- 390 # get message 391 #no type matches module WeBWorK::ContentGenerator::Instructor::SetsAssignedToUser with args at 392 # /home/gage/webwork/webwork-modperl/lib/WeBWorK/URLPath.pm line 497. 393 # error in URLPath.pm?????? 394 my $problemSetListPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::ProblemList", 395 courseID => $courseName, 396 setID => $setName 397 ); 398 399 my $editProblemsURL = $self->systemLink($problemSetListPage, 400 params => ['editForUser'] # include all editForUser parameters 401 ); 402 my $usersAssignedToSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::UsersAssignedToSet", 403 courseID => $courseName, 404 setID => $setName 405 ); 406 407 my $editUsersAssignedToSetURL = $self->systemLink($usersAssignedToSetPage, 408 409 ); 410 print CGI::a({href=>$editProblemsURL}, 411 (@editForUser) ? "Edit the list of problems in this set for ". CGI::b(join ", ", @editForUser) : 412 "Edit the list of problems in this set"); 413 414 unless (@editForUser) { # this is not needed when we are editing details for a user 415 my $userCount = $db->listUsers; 416 my $usersOfSet = $db->countSetUsers($setName); 417 print CGI::h2({}, "Users"), "\n"; 418 print CGI::p({}, "This set is assigned to ".$self->userCountMessage($usersOfSet, $userCount)."."); 419 print CGI::a({href=>$editUsersAssignedToSetURL}, "Determine who this set is assigned to"); 420 } 421 422 return ""; 423 } 424 ########################################################################### 425 # utility 426 ########################################################################### 427 sub saveProblem { 428 my $self = shift; 429 my ($body, $probFileName)= @_; 430 local(*PROBLEM); 431 open (PROBLEM, ">$probFileName") || 432 $self->submission_error("Could not open $probFileName for writing. 433 Check that the permissions for this problem are 660 (-rw-rw----)"); 434 print PROBLEM $body; 435 close PROBLEM; 436 chmod 0660, "$probFileName" || 437 $self->submission_error(" 438 CAN'T CHANGE PERMISSIONS ON FILE $probFileName"); 439 } 440 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |