You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(58) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(53) |
Feb
(56) |
Mar
|
Apr
|
May
(30) |
Jun
(78) |
Jul
(121) |
Aug
(155) |
Sep
(77) |
Oct
(61) |
Nov
(45) |
Dec
(94) |
2006 |
Jan
(116) |
Feb
(33) |
Mar
(11) |
Apr
(23) |
May
(60) |
Jun
(89) |
Jul
(130) |
Aug
(109) |
Sep
(124) |
Oct
(63) |
Nov
(82) |
Dec
(45) |
2007 |
Jan
(31) |
Feb
(35) |
Mar
(123) |
Apr
(36) |
May
(18) |
Jun
(134) |
Jul
(133) |
Aug
(241) |
Sep
(126) |
Oct
(31) |
Nov
(15) |
Dec
(5) |
2008 |
Jan
(11) |
Feb
(6) |
Mar
(16) |
Apr
(29) |
May
(43) |
Jun
(149) |
Jul
(27) |
Aug
(29) |
Sep
(37) |
Oct
(20) |
Nov
(4) |
Dec
(6) |
2009 |
Jan
(34) |
Feb
(30) |
Mar
(16) |
Apr
(6) |
May
(1) |
Jun
(32) |
Jul
(22) |
Aug
(7) |
Sep
(18) |
Oct
(50) |
Nov
(22) |
Dec
(8) |
2010 |
Jan
(17) |
Feb
(15) |
Mar
(10) |
Apr
(9) |
May
(67) |
Jun
(30) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
|
From: Gavin L. v. a. <we...@ma...> - 2005-02-22 23:23:34
|
Log Message: ----------- Gateway update: add URLPath.pm entries for LoginProctor ContentGenerator. Tags: ---- rel-2-1-a1 Modified Files: -------------- webwork2/lib/WeBWorK: URLPath.pm Revision Data ------------- Index: URLPath.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/URLPath.pm,v retrieving revision 1.15.2.1 retrieving revision 1.15.2.2 diff -Llib/WeBWorK/URLPath.pm -Llib/WeBWorK/URLPath.pm -u -r1.15.2.1 -r1.15.2.2 --- lib/WeBWorK/URLPath.pm +++ lib/WeBWorK/URLPath.pm @@ -45,6 +45,7 @@ feedback /$courseID/feedback/ gateway_quiz /$courseID/quiz_mode/$setID/ proctored_gateway_quiz /$courseID/proctored_quiz_mode/$setID/ + proctored_gateway_proctor_login /$courseID/proctored_quiz_mode/$setID/proctor_login/ grades /$courseID/grades/ hardcopy /$courseID/hardcopy/ hardcopy_preselect_set /$courseID/hardcopy/$setID/ @@ -167,6 +168,15 @@ capture => [ qw/setID/ ], produce => 'proctored_quiz_mode/$setID/', display => 'WeBWorK::ContentGenerator::GatewayQuiz', + }, + proctored_gateway_proctor_login => { + name => 'Proctored Gateway Quiz $setID Proctor Login', + parent => 'proctored_gateway_quiz', + kids => [ qw// ], + match => qr|^proctored_quiz_mode/([^/]+)/|, + capture => [ qw/setID/ ], + produce => 'proctored_quiz_mode/$setID/proctor_login', + display => 'WeBWorK::ContentGenerator::LoginProctor', }, grades => { name => 'Student Grades', |
From: Gavin L. v. a. <we...@ma...> - 2005-02-22 23:22:56
|
Log Message: ----------- Gateway update: Include code to check due dates against submission time, rather than current time, by adding a version_last_attempt_time entry to set_user. Also allow viewing of proctored sets without proctor authorization once they can no longer be submitted for a grade. Tags: ---- rel-2-1-a1 Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: GatewayQuiz.pm LoginProctor.pm Revision Data ------------- Index: GatewayQuiz.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm,v retrieving revision 1.9.4.8 retrieving revision 1.9.4.9 diff -Llib/WeBWorK/ContentGenerator/GatewayQuiz.pm -Llib/WeBWorK/ContentGenerator/GatewayQuiz.pm -u -r1.9.4.8 -r1.9.4.9 --- lib/WeBWorK/ContentGenerator/GatewayQuiz.pm +++ lib/WeBWorK/ContentGenerator/GatewayQuiz.pm @@ -125,20 +125,25 @@ # gateway change here: add $submitAnswers as an optional additional argument # to be included if it's defined +# we also allow for a version_last_attempt_time which is the time the set was +# submitted; if that's present we use that instead of the current time to +# decide if we can record the answers. this deals with the time between the +# submission time and the proctor authorization. sub can_recordAnswers { my ($self, $User, $PermissionLevel, $EffectiveUser, $Set, $Problem, $submitAnswers) = @_; my $authz = $self->r->authz; -# warn("canrecord: set open/close = " . $Set->open_date . ", " . $Set->due_date . "\n"); + my $submitTime = ( defined($Set->version_last_attempt_time()) && + $Set->version_last_attempt_time() ) ? + $Set->version_last_attempt_time() : time(); if ($User->user_id ne $EffectiveUser->user_id) { return $authz->hasPermissions($User->user_id, "record_answers_when_acting_as_student"); } - if (before($Set->open_date)) { -# warn("ack1\n"); + if (before($Set->open_date, $submitTime)) { return $authz->hasPermissions($User->user_id, "record_answers_before_open_date"); - } elsif (between($Set->open_date, $Set->due_date)) { + } elsif (between($Set->open_date, $Set->due_date, $submitTime)) { # gateway change here; we look at maximum attempts per version, not for the set, # to determine the number of attempts allowed @@ -146,31 +151,36 @@ my $addOne = defined( $submitAnswers ) ? $submitAnswers : 0; my $max_attempts = $Set->attempts_per_version(); my $attempts_used = $Problem->num_correct+$Problem->num_incorrect+$addOne; -# warn("max_attempts = $max_attempts, addOne = $addOne, attempts_used = $attempts_used\n"); if ($max_attempts == -1 or $attempts_used < $max_attempts) { return $authz->hasPermissions($User->user_id, "record_answers_after_open_date_with_attempts"); } else { return $authz->hasPermissions($User->user_id, "record_answers_after_open_date_without_attempts"); } - } elsif (between($Set->due_date, $Set->answer_date)) { -# warn("ack2\n"); + } elsif (between($Set->due_date, $Set->answer_date, $submitTime)) { return $authz->hasPermissions($User->user_id, "record_answers_after_due_date"); - } elsif (after($Set->answer_date)) { -# warn("ack3\n"); + } elsif (after($Set->answer_date, $submitTime)) { return $authz->hasPermissions($User->user_id, "record_answers_after_answer_date"); } } # gateway change here: add $submitAnswers as an optional additional argument # to be included if it's defined +# we also allow for a version_last_attempt_time which is the time the set was +# submitted; if that's present we use that instead of the current time to +# decide if we can check the answers. this deals with the time between the +# submission time and the proctor authorization. sub can_checkAnswers { my ($self, $User, $PermissionLevel, $EffectiveUser, $Set, $Problem, $submitAnswers) = @_; my $authz = $self->r->authz; - if (before($Set->open_date)) { + my $submitTime = ( defined($Set->version_last_attempt_time()) && + $Set->version_last_attempt_time() ) ? + $Set->version_last_attempt_time() : time(); + + if (before($Set->open_date, $submitTime)) { return $authz->hasPermissions($User->user_id, "check_answers_before_open_date"); - } elsif (between($Set->open_date, $Set->due_date)) { + } elsif (between($Set->open_date, $Set->due_date, $submitTime)) { # gateway change here; we look at maximum attempts per version, not for the set, # to determine the number of attempts allowed @@ -184,17 +194,19 @@ } else { return $authz->hasPermissions($User->user_id, "check_answers_after_open_date_without_attempts"); } - } elsif (between($Set->due_date, $Set->answer_date)) { + } elsif (between($Set->due_date, $Set->answer_date, $submitTime)) { return $authz->hasPermissions($User->user_id, "check_answers_after_due_date"); - } elsif (after($Set->answer_date)) { + } elsif (after($Set->answer_date, $submitTime)) { return $authz->hasPermissions($User->user_id, "check_answers_after_answer_date"); } } # Helper functions for calculating times -sub before { return time <= $_[0] } -sub after { return time >= $_[0] } -sub between { my $t = time; return $t > $_[0] && $t < $_[1] } +# gateway change here: we allow an optional additional argument to use as the +# time to check rather than time() +sub before { return (@_==2) ? $_[1] <= $_[0] : time <= $_[0] } +sub after { return (@_==2) ? $_[1] <= $_[0] : time >= $_[0] } +sub between { my $t = (@_==3) ? $_[2] : time; return $t > $_[0] && $t < $_[1] } ################################################################################ # output utilities @@ -300,9 +312,6 @@ # my $summary = "On this attempt, you answered $numCorrect out of " # . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent."; -# *GW* / FIXME we probably want to change this to give a more logical answer -# for gateway tests - my $summary = ""; if (scalar @answerNames == 1) { if ($numCorrect == scalar @answerNames) { @@ -510,6 +519,7 @@ # unproctored test. so we get the versioned set here my $set = $db->getMergedVersionedSet($effectiveUserName, $setName, $requestedVersion); + unless (defined $set) { my $userSetClass = $ce->{dbLayout}->{set_user}->{record}; $set = global2user($userSetClass, $db->getGlobalSet($setName)); @@ -521,22 +531,22 @@ my $setVersionName = $set->set_id(); my ($setVersionNumber) = ($setVersionName =~ /.*,v(\d+)$/); -# debugging global2user -# if ( defined($set) ) { -# my $str = ''; -# foreach ( map {$_} $db->newUserSet->FIELDS ) { -# $str .= "** $_ => " . $set->$_ . "\n"; -# } -# warn("** got set $setName (reqVer = $requestedVersion)\n$str\n"); -# } - -# FIXME: this needs to check the set version, not the template, for the -# case of a finished proctored test FIXME -# and check for a valid proctor login, to be sure that no one is trying -# to abuse the url path to sneak in the back door on a proctored test - die("Set $setName requires a valid proctor login.") - if ( $tmplSet->assignment_type() =~ /proctored/i && - ! WeBWorK::Authen->new($r, $ce, $db)->verifyProctor() ); +# proctor check to be sure that no one is trying to abuse the url path to sneak +# in the back door on a proctored test +# in the dispatcher we make sure that every call with a proctored url has a +# valid proctor authentication. so if we're here either we were called with +# an unproctored url, or we have a valid proctor authentication. +# this check is to be sure we have a valid proctor authentication for any test +# that has a proctored assignment type, preventing someone from trying to +# go to a proctored test with a hacked unproctored URL + if ( ( $requestedVersion && $set->assignment_type() =~ /proctored/i ) || + ( ! $requestedVersion && $tmplSet->assignment_type() =~ /proctored/i ) + ) { +# check against the requested set, if that is the one we're using, or against +# the template if no version was specified. + die("Set $setName requires a valid proctor login.") + if ( ! WeBWorK::Authen->new($r, $ce, $db)->verifyProctor() ); + } ################################# # assemble gateway parameters @@ -646,14 +656,7 @@ $setVersionName = "$setName,v$setVersionNumber"; $set = $db->getMergedVersionedSet($userName,$setName, $setVersionNumber); -# debugging problem merging -# if ( defined($set) ) { -# my $str = ''; -# foreach ( map {$_} $db->newUserSet->FIELDS ) { -# $str .= "** $_ => >" . $set->$_ . "<\n"; -# } -# warn("** got set $setName (reqVer = $requestedVersion)\n$str\n"); -# } + $Problem = $db->getMergedVersionedProblem($userName,$setName, $setVersionName,1); # because we're creating this on the fly, it should be published @@ -663,6 +666,7 @@ $set->open_date( $timeNow ); $set->due_date( $timeNow+$timeLimit ); $set->answer_date( $timeNow+$timeLimit ); + $set->version_last_attempt_time( 0 ); # put this new info into the database. note that this means that -all- of # the merged information gets put back into the database. as long as # the version doesn't have a long lifespan, this is ok... @@ -683,7 +687,7 @@ } elsif ( $currentNumAttempts < $maxAttemptsPerVersion && $timeNow < $set->due_date() ) { - if ( between($timeNow, $set->open_date(), $set->due_date()) ) { + if ( between($set->open_date(), $set->due_date(), $timeNow) ) { $versionIsOpen = 1; } else { $versionIsOpen = 0; # redundant; default is 0 @@ -731,7 +735,7 @@ $authz->hasPermissions($effectiveUserName, "record_answers_when_acting_as_student") ) ) { - if ( between($timeNow, $set->open_date(), $set->due_date()) ) { + if ( between($set->open_date(), $set->due_date(), $timeNow) ) { $versionIsOpen = 1; } else { $versionIsOpen = 0; # redundant; default is 0 @@ -899,10 +903,6 @@ # this is the actual translation of each problem. errors are stored in # @{$self->{errors}} in each case -# warn("going into gph: merged problem:\n"); -# foreach my $kk ( keys %$ProblemN ) { -# warn(" $kk => " . $ProblemN->{$kk} . "\n"); -# } my $pg = $self->getProblemHTML( $self->{effectiveUser}, $setVersionName, $formFields, $ProblemN ); push(@pg_results, $pg); @@ -972,6 +972,8 @@ my $user = $r->param('user'); my $effectiveUser = $r->param('effectiveUser'); + my $timeNow = time(); + ######################################### # preliminary error checking and output ######################################### @@ -1176,12 +1178,48 @@ join("", '|', $problems[$i]->user_id, '|', $problems[$i]->set_id, '|', $problems[$i]->problem_id, - '|', "\t", time(), "\t", + '|', "\t$timeNow\t", $answerString), ); } } } # end loop through problems + +# additional set-level submitAnswers database manipulation: this is all +# for versioned sets/gateway tests +# we want to save the time that a set was submitted, and for proctored +# tests we want to reset the assignment type after a set is submitted +# for the last time so that it's possible to look at it without getting +# proctor authorization + if ( $will{recordAnswers} || ( ! $can{recordAnswersNextTime} && + $set->assignment_type() eq 'proctored_gateway' ) ) { +# warn("in put set conditional\n"); + + my $setName = $set->set_id(); +# I started out getting the set back out of the database, but I don't think +# this is needed here. the only manipulation of the $set object is internal, +# so I think it's safe to just use the $set that we have +# # note that getMergedVersionedSet returns the requested set if the set name is +# # versioned ("name,vN"), or the latest set if no version is specified (that +# # is, it gives us the set we're working with) +# my $cleanSet = $db->getMergedVersionedSet($user,$setName); + + if ( $will{recordAnswers} ) { +# $cleanSet->version_last_attempt_time( $timeNow ); + $set->version_last_attempt_time( $timeNow ); +# warn("set last attempt time in clean set " . $set->set_id() . " to $timeNow\n"); + } + if ( ! $can{recordAnswersNextTime} && + $set->assignment_type() eq 'proctored_gateway' ) { +# $cleanSet->assignment_type( 'gateway' ); + $set->assignment_type( 'gateway' ); + } +# $db->putVersionedUserSet( $cleanSet ); + $db->putVersionedUserSet( $set ); + } + +# warn("in submitanswers conditional\n"); + } # end if submitAnswers conditional $WeBWorK::timer->continue("end answer processing") if defined( $WeBWorK::timer ); @@ -1202,10 +1240,6 @@ } } -# FIXME the following should probably be in the gateway -# template file, once that exists FIXME -# print CGI::h3("Gateway $setName"); - if ( $overallScore > -1 ) { my $divClass = ''; my $ansRecorded = 1; @@ -1234,6 +1268,8 @@ } if ( ! $can{recordAnswersNextTime} ) { +# if we can't record answers any more, then we're finished with this set +# version. print the appropriate message to that effect. print CGI::start_div({class=>"gwMessage"}); my $mesg = ( $requestedVersion ) ? '' : ", because you have used all available attempts on it or " . @@ -1245,12 +1281,12 @@ "You may, however, check your answers to see what you did" . " right or wrong."), "\n\n"; print CGI::end_div(); + } else { # FIXME: This assumes that there IS a time limit! # FIXME: We need to drop this out gracefully if there isn't! # set up a timer - my $timeNow = time(); my $timeLeft = $set->due_date() - $timeNow; # this is in seconds print CGI::start_div({class=>"gwTiming"}); print CGI::startform({-name=>"gwtimer", -method=>"POST", @@ -1275,8 +1311,16 @@ # "The current time is ", scalar(localtime())), "\n\n"; } +# this is a brutal hack to get a URL that won't require a proctor login if +# we've submitted a proctored test for the last time. above we've reset the +# assignment_type in this case, so we'll use that to decide if we should +# give a path to an unproctored test. note that this substitution leaves +# unproctored test URLs unchanged + my $action = $r->uri(); + $action =~ s/proctored_quiz_mode/quiz_mode/ + if ( $set->assignment_type() eq 'gateway' ); - print CGI::startform({-name=>"gwquiz", -method=>"POST", -action=>$r->uri}), $self->hidden_authen_fields, + print CGI::startform({-name=>"gwquiz", -method=>"POST", -action=>$action}), $self->hidden_authen_fields, $self->hidden_proctor_authen_fields; # FIXME RETURNTO @@ -1332,9 +1376,6 @@ # print CGI::start_div({class=>"problemHeader", -style=>"border: solid black 1px;"}); -# FIXME: I've coded the "ResultsWithError" style into the recordMessage strings -# FIXME: manually; this should be pushed out to the template or stylesheet. - if ($pg->{flags}->{showPartialCorrectAnswers} >= 0 && $submitAnswers) { if ( $scoreRecordedMessage[$probOrder[$i]] ne "Your score on this problem was recorded." ) { @@ -1513,11 +1554,6 @@ # FIXME I'm not sure that problem_id is what we want here FIXME my $problemNumber = $mergedProblem->problem_id; -# WARN STATEMENT HERE -# warn("merged problem:\n"); -# foreach my $kk ( keys %$mergedProblem ) { -# warn(" $kk => " . $mergedProblem->{$kk} . "\n"); -# } my $pg = WeBWorK::PG->new( $ce, @@ -1538,9 +1574,9 @@ }, ); -# FIXME is problem_id the correct thing in the following two stanzas? FIXME -# the original version had "problem number", which is what we want. I think -# problem_id will work, too +# FIXME is problem_id the correct thing in the following two stanzas? +# FIXME the original version had "problem number", which is what we want. +# FIXME I think problem_id will work, too if ($pg->{warnings} ne "") { push @{$self->{warnings}}, { set => $setVersionName, @@ -1561,8 +1597,7 @@ # the error context, not TeX code $pg->{body_text} = undef; } -# WARN STATEMENT HERE -# warn("body text = " . $pg->{body_text} . "\n"); + return $pg; } Index: LoginProctor.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Attic/LoginProctor.pm,v retrieving revision 1.1.2.3 retrieving revision 1.1.2.4 diff -Llib/WeBWorK/ContentGenerator/LoginProctor.pm -Llib/WeBWorK/ContentGenerator/LoginProctor.pm -u -r1.1.2.3 -r1.1.2.4 --- lib/WeBWorK/ContentGenerator/LoginProctor.pm +++ lib/WeBWorK/ContentGenerator/LoginProctor.pm @@ -28,14 +28,19 @@ use warnings; use CGI qw(); use WeBWorK::Utils qw(readFile dequote); +use WeBWorK::ContentGenerator::GatewayQuiz qw(can_recordAnswers); # This content generator is NOT logged in. +# FIXME I'm not sure this is really what we want for the proctor login, +# FIXME but I also don't know what this actually does, so I'm ignoring it +# FIXME for now. sub if_loggedin { my ($self, $arg) = @_; return !$arg; } +# FIXME This needs to be updated for LoginProctor sub info { my ($self) = @_; my $r = $self->r; @@ -74,34 +79,97 @@ my $db = $r->db; my $urlpath = $r->urlpath; - # get some stuff together - my $user = $r->param("effectiveUser") || ""; + # convenient data variables + my $effectiveUser = $r->param("effectiveUser") || ""; + my $user = $r->param("user"); my $proctorUser = $r->param("proctor_user") || ""; my $key = $r->param("proctor_key"); my $passwd = $r->param("proctor_passwd") || ""; my $course = $urlpath->arg("courseID"); + my $setID = $urlpath->arg("setID"); + my $timeNow = time(); + + # data collection + my $submitAnswers = $r->param("submitAnswers"); + my $EffectiveUser = $db->getUser($effectiveUser); + my $User = $db->getUser($user); + + my $effectiveUserFullName = $EffectiveUser->first_name() . " " . + $EffectiveUser->last_name(); + + # save the userset for use below + my $UserSet; + # version_last_attempt_time conditional: if we're submitting the set + # for the last time we need to save the submission time. + if ( $submitAnswers ) { + + # getMergedVersionedSet returns either the set requested (if the setID + # is versioned, "setName,vN") or the latest set (if not). This should + # be by default the set we want. + $UserSet = $db->getMergedVersionedSet($effectiveUser, $setID); + # this should never error out, but we'll check anyway + die("Proctor login generated for grade attempt on a nonexistent " . + "set?!\n") if ( ! defined($UserSet) ); + + # we need these to get a problem from the set + my $setVersionName = ( $setID =~ /,v(\d+)$/ ) ? $setID : + $UserSet->set_id(); + $setID =~ s/,v\d+$//; + + # we only save the submission time if the attempt will be recorded, + # so we have to do some research to determine if that's the case + my $PermissionLevel = $db->getPermissionLevel($user); + my $Problem = + $db->getMergedVersionedProblem($effectiveUser, $setID, + $setVersionName, 1); + # set last_attempt_time if appropriate + if ( WeBWorK::ContentGenerator::GatewayQuiz::can_recordAnswers($self,$User, $PermissionLevel, + $EffectiveUser, $UserSet, $Problem) ) { + $UserSet->version_last_attempt_time( $timeNow ); + $db->putVersionedUserSet( $UserSet ); + } + } - my $User = $db->getUser($user); # use this for user's name - my $username = $User->first_name() . " " . $User->last_name(); - # WeBWorK::Authen::verifyProctor will set the note "authen_error" - # if invalid authentication is found. If this is done, it's a signal to - # us to yell at the user for doing that, since Authen isn't a content- - # generating module. + print CGI::p(CGI::strong("Proctor authorization required."), "\n\n"); + # WeBWorK::Authen::verifyProctor will set the note "authen_error" + # if invalid authentication is found. If this is done, it's a signal to + # us to yell at the user for doing that, since Authen isn't a content- + # generating module. if ($r->notes("authen_error")) { print CGI::div({class=>"ResultsWithError"}, CGI::p($r->notes("authen_error")) ); } - print CGI::p(CGI::strong("This assignment requires a proctor " . - "authorization to start or grade an " . - "assignment.")),"\n\n", - CGI::div({style=>"background-color:#ddddff;"}, - CGI::p("User's uniqname is: ", CGI::strong("$user"),"\n", - CGI::br(), - "User's name is: ", CGI::strong("$username"),"\n")), - "\n"; + # also print a message about submission times if we're submitting + # an answer + if ( $submitAnswers ) { + my $dueTime = $UserSet->due_date(); + my $timeLimit = $UserSet->version_time_limit(); + my ($color, $msg) = ("#ddddff", ""); + + if ( $dueTime < $timeNow ) { + $color = "#ffffaa"; + $msg = CGI::br() . "\nThe time limit on this assignment " . + "was exceeded.\nThe assignment may be checked, but " . + "the result will not be counted.\n"; + } + my $style = "background-color: $color; color: black; " . + "border: solid black 1px; padding: 2px;"; + print CGI::div({-style=>$style}, + CGI::strong("Grading assignment: ", CGI::br(), + "Submission time: ", + scalar(localtime($timeNow)), CGI::br(), + "Due: ", + scalar(localtime($dueTime)), $msg)); + } + + print CGI::div({style=>"background-color:#ddddff;"}, + CGI::p("User's uniqname is: ", + CGI::strong("$effectiveUser"),"\n", + CGI::br(),"User's name is: ", + CGI::strong("$effectiveUserFullName"),"\n")),"\n"; print CGI::startform({-method=>"POST", -action=>$r->uri}); |
From: Sam H. v. a. <we...@ma...> - 2005-02-21 17:11:03
|
Log Message: ----------- updated for 2.1.2 Tags: ---- rel-2-1-patches Modified Files: -------------- webwork2: LICENSE README Revision Data ------------- Index: README =================================================================== RCS file: /webwork/cvs/system/webwork2/README,v retrieving revision 1.10.2.2 retrieving revision 1.10.2.3 diff -LREADME -LREADME -u -r1.10.2.2 -r1.10.2.3 --- README +++ README @@ -1,100 +1,52 @@ WeBWorK Online Homework Delivery System - Version 2.1.1 + Version 2.1.2 Copyright 2000-2005, The WeBWorK Project All rights reserved. -Introduction - - This release introduces several improvements and release fixes many - bugs in WeBWorK 2.1. Among the changes made are the following: + * [1]Introduction + * [2]Availability + * [3]Installation + * [4]Help + * [5]Bug Reports & Feature Requests + * [6]Patches - Enhancements +Introduction - 1. Added a login privilege to the permissions system - 2. Update jsMath to 1.7e, including support for additional LaTeX - environments - 3. Improved the mail merge user interface - 4. Improved the default stylesheet - 5. Added an option to rename the Change Display Settings submit - buttton - 6. Added summary column to the scoring module for the total value of - WeBWorK problems done - 7. Optimized checking of permission levels by caching the currently - logged-in user's permission level - 8. Added several features to the classlist editor - + ability to search on any data field ( bug_small.gif [9]Bug - #664) - + ability to define a subsort (i.e. first sort by last name, - then by first name) ( bug_small.gif [10]Bug #654) - + warning is given when users with duplicate names are imported - ( bug_small.gif [11]Bug #684) - 9. Added option to file manager to convert newlines on uploaded files - 10. Feedback messages now include course ID - 11. Leading and trailing whitespace is now removed on the add users - page - 12. Added the ability to add a new, blank problem to an existing - problem set - 13. Made changes to preserve problem display options across all pages - 14. Modified the default template to refer to the stylesheet by - reference, reducing page load times - - Bug fixes - - 1. Fixed a bug where the Show saved answers option could not be - turned off - 2. Fixed a big where an incorrect error message would be given when a - database error occurred - 3. Fixed "uninitialized value" error in grading ( bug_small.gif - [12]Bug #733) - 4. Fixed a bug where a weight of 0 would appear as a blank ( - bug_small.gif [13]Bug #730) - 5. Fixed a bug where fun_cmp would give the error "This answer is the - same as the one you just submitted" after a preview operation ( - bug_small.gif [14]Bug #557) - 6. Fixed a bug where the parameter user would be defined multiple - times in a form or URL - 7. Fixed a bug where non-overridden date values would be displayed as - 12/31/1969 on the problem set detail page - 8. Fixed several bugs in the file manager - 9. Fixed a bug where the scoring page would crash and burn if no set - was selected ( bug_small.gif [15]Bug #750) + This release builds on WeBWorK 2.1.1 by adding an improved PG Problem + Editor module and enabling a function to add a blank problem to an + existing problem set. Availability - WeBWorK 2.1.1 is available our CVS repository. Read - [16]WeBWorKCVSReadOnly for more information on how to set up a CVS + WeBWorK 2.1.2 is available from our CVS repository. Read + [7]WeBWorKCVSReadOnly for more information on how to set up a CVS connection. For those who already have a CVS connection, this update - can be obtained by updating to the release: rel-2-1-patches + can be obtained by updating to the tag rel-2-1-patches. - WeBWorK 2.1.1 is also available in tarball form from our SourceForge + WeBWorK 2.1.2 is also available as a tarball from our SourceForge project page: - [17]http://sourceforge.net/project/showfiles.php?group_id=93112 + [8]http://sourceforge.net/project/showfiles.php?group_id=93112 - We recommend you also install [18]PGLanguageRelease2pt1pt1 at the same - time you install WeBWorK 2.1.1. + We recommend you also install [9]PGLanguageRelease2pt1pt1 at the same + time you install WeBWorK 2.1.2. Installation - Read the section in the installation manual on [19]Upgrading WeBWorK. - - Changes were made to the configuration global.conf.dist in this - release. Be sure to compare your existing global.conf file with the - new global.conf.dist file and make any changes necessary. The utility - diff3 may be useful to you. + Read the section in the installation manual on [10]Upgrading WeBWorK. Help - If you need help installing or using WeBWorK 2.1.1, visit the - [20]WeBWorK discussion group and post your question there. The + If you need help installing or using WeBWorK 2.1.2, visit the + [11]WeBWorK discussion group and post your question there. The developers monitor this forum. Bug Reports & Feature Requests Submit bug reports and feature requests at - [21]http://bugs.webwork.rochester.edu/. We can't fix bugs and add + [12]http://bugs.webwork.rochester.edu/. We can't fix bugs and add features if you don't tell us about them! Patches @@ -103,24 +55,14 @@ CVS code, you save us and yourself time. A bug in this release may be fixed in CVS, and we can more easily handle patches against the latest code. Check out the latest development version from CVS and patch - against that. Consult the [22]WeBWorKCVS topic for more information. - - -- [23]SamHathaway - 08 Feb 2005 - -References + against that. Consult the [13]WeBWorKCVS topic for more information. - 9. http://bugs.webwork.rochester.edu/show_bug.cgi?id=664 - 10. http://bugs.webwork.rochester.edu/show_bug.cgi?id=654 - 11. http://bugs.webwork.rochester.edu/show_bug.cgi?id=684 - 12. http://bugs.webwork.rochester.edu/show_bug.cgi?id=733 - 13. http://bugs.webwork.rochester.edu/show_bug.cgi?id=730 - 14. http://bugs.webwork.rochester.edu/show_bug.cgi?id=557 - 15. http://bugs.webwork.rochester.edu/show_bug.cgi?id=750 - 16. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKCVSReadOnly - 17. http://sourceforge.net/project/showfiles.php?group_id=93112 - 18. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/PGLanguageRelease2pt1pt1 - 19. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1#Upgrading_WeBWorK - 20. http://webhost.math.rochester.edu/webworkdocs/discuss/ - 21. http://bugs.webwork.rochester.edu/ - 22. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKCVS - 23. http://devel.webwork.rochester.edu/twiki/bin/view/Main/SamHathaway + -- [14]SamHathaway - 21 Feb 2005 + 7. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKCVSReadOnly + 8. http://sourceforge.net/project/showfiles.php?group_id=93112 + 9. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/PGLanguageRelease2pt1pt1 + 10. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1#Upgrading_WeBWorK + 11. http://webhost.math.rochester.edu/webworkdocs/discuss/ + 12. http://bugs.webwork.rochester.edu/ + 13. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKCVS + 14. http://devel.webwork.rochester.edu/twiki/bin/view/Main/SamHathaway Index: LICENSE =================================================================== RCS file: /webwork/cvs/system/webwork2/LICENSE,v retrieving revision 1.5.2.2 retrieving revision 1.5.2.3 diff -LLICENSE -LLICENSE -u -r1.5.2.2 -r1.5.2.3 --- LICENSE +++ LICENSE @@ -1,7 +1,7 @@ WeBWorK Online Homework Delivery System - Version 2.1.1 + Version 2.1.2 Copyright 2000-2005, The WeBWorK Project All rights reserved. |
From: Mike G. v. a. <we...@ma...> - 2005-02-19 17:04:39
|
Log Message: ----------- Small correction that makes sure that _init subroutines are read properly. The original works on some versions of perl, but not on others. Modified Files: -------------- pg/macros: dangerousMacros.pl Revision Data ------------- Index: dangerousMacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/dangerousMacros.pl,v retrieving revision 1.32 retrieving revision 1.33 diff -Lmacros/dangerousMacros.pl -Lmacros/dangerousMacros.pl -u -r1.32 -r1.33 --- macros/dangerousMacros.pl +++ macros/dangerousMacros.pl @@ -206,7 +206,7 @@ warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON; if ( defined($init_subroutine) && defined( &{$init_subroutine} ) ) { - warn "dangerousMacros: initializing $macro_file_name" if $debugON; + warn "dangerousMacros: initializing $macro_file_name" if $debugON; &$init_subroutine(); } } @@ -246,7 +246,7 @@ #compile initialization subroutine. (5.6.1 version) also works with 5.6.0 # no strict; - my $init_subroutine = eval { \&{$init_subroutine_name} }; + my $init_subroutine = eval { \&{'main::'.$init_subroutine_name} }; # use strict; ############################################################################### @@ -254,7 +254,7 @@ # and then in the webwork $courseScripts directory. my $macro_file_loaded = defined($init_subroutine) && defined(&$init_subroutine); - warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON; + warn "dangerousMacros: macro init $init_subroutine_name defined |$init_subroutine| |$macro_file_loaded|" if $debugON; unless ($macro_file_loaded) { #print STDERR "loadMacros: loading macro file $fileName\n"; if (-r "$macroDirectory$fileName") { |
From: Gavin L. v. a. <we...@ma...> - 2005-02-18 16:01:41
|
Log Message: ----------- Add javascript to gateway template file for active timer on gateway quizzes. Tags: ---- rel-2-1-a1 Modified Files: -------------- webwork2/conf/templates: gw.template Revision Data ------------- Index: gw.template =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/templates/Attic/gw.template,v retrieving revision 1.1.2.1 retrieving revision 1.1.2.2 diff -Lconf/templates/gw.template -Lconf/templates/gw.template -u -r1.1.2.1 -r1.1.2.2 --- conf/templates/gw.template +++ conf/templates/gw.template @@ -148,7 +148,12 @@ /******************/ div.gwMessage { background-color:#dddddd; color: black; } -div.gwTiming { background-color:#ddddff; color: black; } +div.gwTiming { + background-color:#ddddff; + color: black; + margin-top: 10px; + margin-bottom: 0px; +} div.gwWarning { background-color:#ffffdd; color: black; } span.resultsWithError { background-color: #ffcccc; color: black; } @@ -167,7 +172,7 @@ /* background-color: #e0e0ff; */ background-color: transparent; color: black; -/* padding: 2px; */ +/* padding: 2px; */, /* border: dashed black 1px; */ } div.gwSoln b { color: navy; } @@ -195,15 +200,16 @@ </style> <script language="javascript" type="text/javascript"> -function jumpTo(ref) { +function jumpTo(ref) { // scrolling javascript functin if ( ref ) { + var pn = ref - 1; // we start anchors at 1, not zero if ( navigator.appName == "Netscape" && parseFloat(navigator.appVersion) < 5 ) { - var xpos = document.anchors[ref].x; - var ypos = document.anchors[ref].y; + var xpos = document.anchors[pn].x; + var ypos = document.anchors[pn].y; } else { - var xpos = document.anchors[ref].offsetLeft; - var ypos = document.anchors[ref].offsetTop; + var xpos = document.anchors[pn].offsetLeft; + var ypos = document.anchors[pn].offsetTop; } if ( window.scrollTo == null ) { // cover for anyone window.scroll(xpos,ypos); // lacking js1.2 @@ -213,9 +219,51 @@ } return false; // prevent link from being followed } + +// timer for gateway +var theTime = -1; // -1 before we've initialized + +function runtimer() { +// aesthetically this is displeasing: we're assuming that the +// ContentGenerator will put the appropriate form elements in that +// page for us to manipulate. even with error checking, it seems sort +// of odd. + if ( document.gwtimer == null ) { // no timer + return; + } else { + var tm = document.gwtimer.gwtime; + var st = document.gwtimer.gwpagetimeleft.value; + + if ( st == 0 ) { // no time limit + return; + } else { + if ( theTime == -1 ) { + theTime = st; + tm.value = toMinSec(theTime); + setTimeout("runtimer()", 1000); // 1000 ms = 1 sec + } else { + theTime--; + tm.value = toMinSec(theTime); + setTimeout("runtimer()", 1000); // 1000 ms = 1 sec + } + } + } +} +function toMinSec(t) { +// convert to min:sec + mn = Math.floor(t/60); + sc = t - 60*mn; + if ( mn < 10 && mn > -1 ) { + mn = "0" + mn; + } + if ( sc < 10 ) { + sc = "0" + sc; + } + return mn + ":" + sc; +} </script> </head> -<body> +<body onload="runtimer();"> <table width="100%" cellpadding="10" cellspacing="0" border="0"> <tr valign="top"> <!-- removed left sidebar --> |
From: Gavin L. v. a. <we...@ma...> - 2005-02-18 16:01:01
|
Log Message: ----------- Add active javascript timer to gateway quiz module. Tags: ---- rel-2-1-a1 Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: GatewayQuiz.pm Revision Data ------------- Index: GatewayQuiz.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm,v retrieving revision 1.9.4.7 retrieving revision 1.9.4.8 diff -Llib/WeBWorK/ContentGenerator/GatewayQuiz.pm -Llib/WeBWorK/ContentGenerator/GatewayQuiz.pm -u -r1.9.4.7 -r1.9.4.8 --- lib/WeBWorK/ContentGenerator/GatewayQuiz.pm +++ lib/WeBWorK/ContentGenerator/GatewayQuiz.pm @@ -1246,21 +1246,33 @@ " right or wrong."), "\n\n"; print CGI::end_div(); } else { + +# FIXME: This assumes that there IS a time limit! +# FIXME: We need to drop this out gracefully if there isn't! +# set up a timer + my $timeNow = time(); + my $timeLeft = $set->due_date() - $timeNow; # this is in seconds print CGI::start_div({class=>"gwTiming"}); - print CGI::p(CGI::strong("This version started ", - scalar(localtime($set->open_date())), - CGI::br(),"\nTime limit : ", - ($set->version_time_limit()/60), - " minutes (must be completed by: ", - scalar(localtime($set->due_date())), ")", CGI::br(), - "The current time is ", scalar(localtime()))), "\n\n"; - print CGI::end_div(); - if ( $set->due_date() - $set->version_time_limit() < 1 ) { - print CGI::start_div({class=>"gwWarning"}); - print CGI::p(CGI::strong("You have less than 1 minute to ", - "complete this version.\n")); - print CGI::end_div(); + print CGI::startform({-name=>"gwtimer", -method=>"POST", + -action=>$r->uri}), "\n"; + print CGI::hidden({-name=>"gwpagetimeleft", -value=>$timeLeft}), "\n"; + print CGI::strong("Time Remaining:"); + print CGI::textfield({-name=>'gwtime', -default=>0, -size=>8}), + CGI::strong("min:sec"), CGI::br(), "\n"; + print CGI::endform(); + if ( $timeLeft < 1 ) { + print CGI::span({-class=>"resultsWithError"}, + CGI::b("You have less than 1 minute to ", + "complete this test.\n")); } + print CGI::end_div(); +# print CGI::strong("Time Remaining: +# scalar(localtime($set->open_date())), +# CGI::br(),"\nTime limit : ", +# ($set->version_time_limit()/60), +# " minutes (must be completed by: ", +# scalar(localtime($set->due_date())), ")", CGI::br(), +# "The current time is ", scalar(localtime())), "\n\n"; } @@ -1279,7 +1291,7 @@ my $jumpLinks = "Jump to problem: "; for my $i ( 0 .. $#pg_results ) { my $pn = $i+1; - $jumpLinks .= "/ " . CGI::a({-href=>".", -onclick=>"jumpTo($i);return false;"}, "$pn") . " /"; + $jumpLinks .= "/ " . CGI::a({-href=>".", -onclick=>"jumpTo($pn);return false;"}, "$pn") . " /"; } print CGI::p($jumpLinks,"\n"); @@ -1366,7 +1378,8 @@ } print CGI::start_div({class=>"gwProblem"}); - print CGI::a({-name=>"#$i"},""); + my $i1 = $i+1; + print CGI::a({-name=>"#$i1"},""); print CGI::strong("Problem $problemNumber."), "\n", $recordMessage; print CGI::p($pg->{body_text}), CGI::p($pg->{result}->{msg} ? CGI::b("Note: ") : "", |
From: dpvc v. a. <we...@ma...> - 2005-02-18 00:21:11
|
Log Message: ----------- Missed one situation when making the previous update. Fixed it. Modified Files: -------------- pg/lib/Value: Formula.pm Revision Data ------------- Index: Formula.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Formula.pm,v retrieving revision 1.17 retrieving revision 1.18 diff -Llib/Value/Formula.pm -Llib/Value/Formula.pm -u -r1.17 -r1.18 --- lib/Value/Formula.pm +++ lib/Value/Formula.pm @@ -103,7 +103,7 @@ sub dot { my ($l,$r,$flag) = @_; if ($l->promotePrecedence($r)) {return $r->compare($l,!$flag)} - return bop(@_,'.') if $l->type eq 'Vector' && + return bop('.',@_) if $l->type eq 'Vector' && Value::isValue($r) && $r->type eq 'Vector'; Value::_dot(@_); } |
From: Mike G. v. a. <we...@ma...> - 2005-02-17 18:15:47
|
Log Message: ----------- Changes fixing a problem that prevented a new problem from being added to a set. Tags: ---- rel-2-1-patches Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm Revision Data ------------- Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.48.2.1 retrieving revision 1.48.2.2 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.48.2.1 -r1.48.2.2 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -31,6 +31,7 @@ use Apache::Constants qw(:common REDIRECT); use HTML::Entities; use URI::Escape; +use WeBWorK::Utils; use WeBWorK::Utils::Tasks qw(fake_set fake_problem); ########################################################### @@ -346,7 +347,8 @@ my $targetSetName = $r->param('target_set'); my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", - courseID => $courseName, setID => $targetSetName, problemID => scalar($r->db->listGlobalProblems($targetSetName)) + courseID => $courseName, setID => $targetSetName, + problemID => WeBWorK::Utils::max( $r->db->listGlobalProblems($targetSetName)) ); $viewURL = $self->systemLink($problemPage, params => { @@ -952,7 +954,7 @@ ($action eq 'add_problem_to_set') and do { my $sourceFile = $editFilePath; my $targetSetName = $r->param('target_set'); - my $freeProblemID = WeBWorK::Utils::max($db->listGlobalProblems($setName)) + 1; + my $freeProblemID = WeBWorK::Utils::max($db->listGlobalProblems($targetSetName)) + 1; $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; my $problemRecord = $self->addProblemToSet( setName => $targetSetName, |
From: Mike G. v. a. <we...@ma...> - 2005-02-17 17:59:42
|
Log Message: ----------- Fixed error in PGProblemEditor.pm that prevented you from adding new problem in a set. Added comment to ProblemSetDetail.pm Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm ProblemSetDetail.pm Revision Data ------------- Index: ProblemSetDetail.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm,v retrieving revision 1.17 retrieving revision 1.18 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -u -r1.17 -r1.18 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm @@ -1199,8 +1199,6 @@ ])); } - my $editNewProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID =>'new_problem' }); - my $editNewProblemLink = $self->systemLink($editNewProblemPage, params => { make_local_copy => 1, file_type => 'blank_problem' }); # print final lines print CGI::end_table(); @@ -1211,7 +1209,6 @@ CGI::br(); print CGI::input({type=>"submit", name=>"submit_changes", value=>"Save Changes"}); print CGI::input({type=>"submit", name=>"handle_numbers", value=>"Reorder problems only"}) . "(Any unsaved changes will be lost.)"; - print CGI::p( CGI::a({href=>$editNewProblemLink},'Create'). 'a new blank problem'); print CGI::p(<<HERE); Any time problem numbers are intentionally changed, the problems will always be renumbered consecutively, starting from one. When deleting @@ -1225,6 +1222,11 @@ } else { print CGI::p(CGI::b("This set doesn't contain any problems yet.")); } + # always allow one to add a new problem. + my $editNewProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID =>'new_problem' }); + my $editNewProblemLink = $self->systemLink($editNewProblemPage, params => { make_local_copy => 1, file_type => 'blank_problem' }); + + print CGI::p( CGI::a({href=>$editNewProblemLink},'Create'). 'a new blank problem'); print CGI::end_form(); Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.52 retrieving revision 1.53 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.52 -r1.53 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -31,6 +31,7 @@ use Apache::Constants qw(:common REDIRECT); use HTML::Entities; use URI::Escape; +use WeBWorK::Utils; use WeBWorK::Utils::Tasks qw(fake_set fake_problem); ########################################################### @@ -42,6 +43,7 @@ # The course information and problems are located in the course templates directory. # Course information has the name defined by courseFiles->{course_info} # +# Only files under the template directory ( or linked to this location) can be edited. # # editMode = temporaryFile (view the temp file defined by course_info.txt.user_name.tmp # instead of the file course_info.txt) @@ -345,7 +347,8 @@ my $targetSetName = $r->param('target_set'); my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", - courseID => $courseName, setID => $targetSetName, problemID => scalar($r->db->listGlobalProblems($targetSetName)) + courseID => $courseName, setID => $targetSetName, + problemID => WeBWorK::Utils::max( $r->db->listGlobalProblems($targetSetName)) ); $viewURL = $self->systemLink($problemPage, params => { @@ -951,7 +954,7 @@ ($action eq 'add_problem_to_set') and do { my $sourceFile = $editFilePath; my $targetSetName = $r->param('target_set'); - my $freeProblemID = WeBWorK::Utils::max($db->listGlobalProblems($setName)) + 1; + my $freeProblemID = WeBWorK::Utils::max($db->listGlobalProblems($targetSetName)) + 1; $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; my $problemRecord = $self->addProblemToSet( setName => $targetSetName, |
From: dpvc v. a. <we...@ma...> - 2005-02-17 16:29:53
|
Log Message: ----------- Fixed it so that direct calls to the add, sub, mult, etc functions would work as expected (even though there is no need to call them directly). Modified Files: -------------- pg/lib/Value: Formula.pm Revision Data ------------- Index: Formula.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Formula.pm,v retrieving revision 1.16 retrieving revision 1.17 diff -Llib/Value/Formula.pm -Llib/Value/Formula.pm -u -r1.16 -r1.17 --- lib/Value/Formula.pm +++ lib/Value/Formula.pm @@ -63,7 +63,7 @@ # Evaluate the formula if it is constant. # sub bop { - my ($l,$r,$flag,$bop) = @_; + my ($bop,$l,$r,$flag) = @_; if ($l->promotePrecedence($r)) {return $r->add($l,!$flag)} if ($flag) {my $tmp = $l; $l = $r; $r = $tmp} my $formula = $pkg->blank; my $parser = $formula->{context}{parser}; @@ -90,12 +90,12 @@ return $formula; } -sub add {bop(@_,'+')} -sub sub {bop(@_,'-')} -sub mult {bop(@_,'*')} -sub div {bop(@_,'/')} -sub power {bop(@_,'**')} -sub cross {bop(@_,'><')} +sub add {bop('+',@_)} +sub sub {bop('-',@_)} +sub mult {bop('*',@_)} +sub div {bop('/',@_)} +sub power {bop('**',@_)} +sub cross {bop('><',@_)} # # Make dot work for vector operands |
From: Gavin L. v. a. <we...@ma...> - 2005-02-16 14:21:27
|
Log Message: ----------- roll back accidentally committed local change to ur.template Tags: ---- rel-2-1-a1 Modified Files: -------------- webwork2/conf/templates: ur.template Revision Data ------------- Index: ur.template =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/templates/ur.template,v retrieving revision 1.29.2.3 retrieving revision 1.29.2.4 diff -Lconf/templates/ur.template -Lconf/templates/ur.template -u -r1.29.2.3 -r1.29.2.4 --- conf/templates/ur.template +++ conf/templates/ur.template @@ -60,7 +60,7 @@ td.ContentPanel a:active { color: red; } /* contains info */ -td.InfoPanel { background-color: #DDDDDD; color: black; width: 15% } +td.InfoPanel { background-color: #DDDDDD; color: black; width: 30% } td.InfoPanel a:link, td.InfoPanel a:visited { color: blue; } td.InfoPanel a:active { color: red; } |
From: dpvc v. a. <we...@ma...> - 2005-02-15 22:15:53
|
Log Message: ----------- Updated the answer checkers so that you can more easily specify how the correct answer shoudl be displayed. In the past, you could use something like Real(sqrt(2))->cmp(correct_ans=>"sqrt(2)") to do this, but that is awkward. Now the Compute() function (which parses and then evaluates a string) sets things up so that the original string will be what is used as the correct answer. That means Compute("sqrt(2)")->cmp will have the same result as the example above. You can also set the {correct_ans} properly of any Parser object to have that value used as the correct answer. For example $x = Real(sqrt(2)); $x->{correct_ans} = "sqrt(2)"; ANS($x->cmp) would also produce the same answer checker as the two previous examples. All three methods should work. Use the one that is most convenient for you. Modified Files: -------------- pg/macros: Parser.pl pg/lib/Value: AnswerChecker.pm Revision Data ------------- Index: Parser.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/Parser.pl,v retrieving revision 1.4 retrieving revision 1.5 diff -Lmacros/Parser.pl -Lmacros/Parser.pl -u -r1.4 -r1.5 --- macros/Parser.pl +++ macros/Parser.pl @@ -30,8 +30,10 @@ # Parse a formula and evaluate it # sub Compute { - my $formula = Formula(shift); - return $formula->eval(@_); + my $string = shift; + my $formula = Formula($string)->eval(@_); + $formula->{correct_ans} = $string; + return $formula; } # Index: AnswerChecker.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/AnswerChecker.pm,v retrieving revision 1.38 retrieving revision 1.39 diff -Llib/Value/AnswerChecker.pm -Llib/Value/AnswerChecker.pm -u -r1.38 -r1.39 --- lib/Value/AnswerChecker.pm +++ lib/Value/AnswerChecker.pm @@ -26,9 +26,11 @@ sub cmp { my $self = shift; my $ans = new AnswerEvaluator; + my $correct = $self->{correct_ans}; + $correct = $self->string unless defined($correct); $ans->ans_hash( type => "Value (".$self->class.")", - correct_ans => protectHTML($self->string), + correct_ans => protectHTML($correct), correct_value => $self, $self->cmp_defaults, @_ @@ -489,7 +491,8 @@ my $cmp = $self->SUPER::cmp(@_); if ($cmp->{rh_ans}{removeParens}) { $self->{open} = $self->{close} = ''; - $cmp->ans_hash(correct_ans => $self->stringify); + $cmp->ans_hash(correct_ans => $self->stringify) + unless defined($self->{correct_ans}); } return $cmp; } @@ -703,7 +706,8 @@ my $cmp = $self->SUPER::cmp(@_); if ($cmp->{rh_ans}{removeParens} && $self->type eq 'List') { $self->{tree}{open} = $self->{tree}{close} = ''; - $cmp->ans_hash(correct_ans => $self->stringify); + $cmp->ans_hash(correct_ans => $self->stringify) + unless defined($self->{correct_ans}); } if ($cmp->{rh_ans}{eval} && $self->isConstant) { $cmp->ans_hash(correct_value => $self->eval); |
From: dpvc v. a. <we...@ma...> - 2005-02-15 22:10:19
|
Log Message: ----------- Improved the Real(), Complex(), Point(), Vector(), Matrix() and String() constructors so that they will process formulas passed to them as strings rather than requiring perl objects for these. For example, you can use Real("2/3") rather than Real(2/3) if you want. Also, Real("1+x") will return a formula returning a real (essentially the same as Formula("1+x") in this case). Modified Files: -------------- pg/lib: Value.pm pg/lib/Value: Complex.pm Matrix.pm Point.pm Real.pm String.pm Vector.pm Revision Data ------------- Index: Value.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value.pm,v retrieving revision 1.31 retrieving revision 1.32 diff -Llib/Value.pm -Llib/Value.pm -u -r1.31 -r1.32 --- lib/Value.pm +++ lib/Value.pm @@ -134,23 +134,29 @@ # Convert non-Value objects to Values, if possible # sub makeValue { - my $x = shift; - return $x if ref($x); + my $x = shift; my %params = (showError => 0, makeFormula => 1, @_); + return $x if ref($x) || $x eq ''; return Value::Real->make($x) if matchNumber($x); if (matchInfinite($x)) { my $I = Value::Infinity->new(); $I = $I->neg if $x =~ m/^$$Value::context->{pattern}{-infinity}$/; return $I; } - if ($Parser::installed) {return $x unless $$Value::context->{strings}{$x}} - return Value::String->make($x); + return Value::String->make($x) + if (!$Parser::installed || $$Value::context->{strings}{$x}); + return $x if !$params{makeFormula}; + Value::Error("String constant '$x' is not defined in this context") + if $params{showError}; + $x = Value::Formula->new($x); + $x = $x->eval if $x->isConstant; + return $x; } # # Get a printable version of the class of an object # sub showClass { - my $value = makeValue(shift); + my $value = makeValue(shift,makeFormula=>0); return "'".$value."'" unless Value::isValue($value); my $class = class($value); return showType($value) if ($class eq 'List'); @@ -255,7 +261,7 @@ # # Convert a list of values (and open and close parens) # to a formula whose type is the list type associated with -# the parens. If the formula is constant, evaluate it. +# the parens. # sub formula { my $self = shift; my $values = shift; @@ -269,26 +275,10 @@ $formula->{tree} = $formula->{context}{parser}{List}->new($formula,[@coords],0, $formula->{context}{parens}{$paren},$coords[0]->typeRef,$open,$close); $formula->{autoFormula} = 1; # mark that this was generated automatically -# return $formula->eval if scalar(%{$formula->{variables}}) == 0; return $formula; } # -# Parse a string and return the resulting formula if it of the right -# type. If the formula is constant, return the value rather than the -# formula. -# -sub parseFormula { - my $self = shift; my $class = ref($self) ? $self->type : class($self); - $class = "Number" if $class eq 'Real' || $class eq "Complex"; - my $f = (scalar(@_) > 1) ? join(',',@_) : shift; - $f = Value::Formula->new($f); $f = $f->eval() if $f->isConstant; - Value::Error("Can't convert ".Value::showClass($f)." to ".Value::showClass($self)) - if ($f->type ne $class); - return $f; -} - -# # A shortcut for new() that creates an instance of the object, # but doesn't do the error checking. We assume the data are already # known to be good. @@ -517,7 +507,7 @@ # For debugging # sub traceback { - my $frame = 2; + my $frame = shift; $frame = 2 unless defined($frame); my $trace = ''; while (my ($pkg,$file,$line,$subname) = caller($frame++)) {$trace .= " in $subname at line $line of $file\n"} Index: Vector.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Vector.pm,v retrieving revision 1.13 retrieving revision 1.14 diff -Llib/Value/Vector.pm -Llib/Value/Vector.pm -u -r1.13 -r1.14 --- lib/Value/Vector.pm +++ lib/Value/Vector.pm @@ -34,21 +34,22 @@ sub new { my $self = shift; my $class = ref($self) || $self; my $p = shift; $p = [$p,@_] if (scalar(@_) > 0); + $p = Value::makeValue($p) if (defined($p) && !ref($p)); + return $p if (Value::isFormula($p) && $p->type eq Value::class($self)); my $pclass = Value::class($p); my $isFormula = 0; my @d; @d = $p->dimensions if $pclass eq 'Matrix'; if ($pclass =~ m/Point|Vector/) {$p = $p->data} elsif ($pclass eq 'Matrix' && scalar(@d) == 1) {$p = [$p->value]} elsif ($pclass eq 'Matrix' && scalar(@d) == 2 && $d[0] == 1) {$p = ($p->value)[0]} elsif ($pclass eq 'Matrix' && scalar(@d) == 2 && $d[1] == 1) {$p = ($p->transpose->value)[0]} - elsif (defined($p) && ref($p) eq "") {return $self->parseFormula($p)} else { $p = [$p] if (defined($p) && ref($p) ne 'ARRAY'); Value::Error("Vectors must have at least one coordinate") unless defined($p) && scalar(@{$p}) > 0; foreach my $x (@{$p}) { + $x = Value::makeValue($x); $isFormula = 1 if Value::isFormula($x); Value::Error("Coordinate of Vector can't be ".Value::showClass($x)) unless Value::isNumber($x); - $x = Value::Real->make($x) unless ref($x); } } return $self->formula($p) if $isFormula; Index: String.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/String.pm,v retrieving revision 1.3 retrieving revision 1.4 diff -Llib/Value/String.pm -Llib/Value/String.pm -u -r1.3 -r1.4 --- lib/Value/String.pm +++ lib/Value/String.pm @@ -21,7 +21,7 @@ my $self = shift; my $class = ref($self) || $self; my $x = join('',@_); if ($Parser::installed) { - Value::Error("Unrecognized string '$x'") + Value::Error("String constant '$x' is not defined in this context") unless $$Value::context->{strings}{$x}; } bless {data => [$x]}, $class; @@ -44,7 +44,7 @@ # sub promote { my $x = shift; $x = [$x,@_] if scalar(@_) > 0; - $x = Value::makeValue($x); $x = join('',@{$x}) if ref($x) eq 'ARRAY'; + $x = Value::makeValue($x,showError=>1); $x = join('',@{$x}) if ref($x) eq 'ARRAY'; $x = $pkg->make($x) unless Value::isValue($x); return $x; } Index: Real.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Real.pm,v retrieving revision 1.12 retrieving revision 1.13 diff -Llib/Value/Real.pm -Llib/Value/Real.pm -u -r1.12 -r1.13 --- lib/Value/Real.pm +++ lib/Value/Real.pm @@ -38,13 +38,17 @@ sub new { my $self = shift; my $class = ref($self) || $self; my $x = shift; $x = [$x,@_] if scalar(@_) > 0; - $x = $x->data if ref($x) eq $pkg; + return $x if ref($x) eq $pkg; $x = [$x] unless ref($x) eq 'ARRAY'; - Value::Error("Can't convert ARRAY of length ".scalar(@{$x})." to a Real Number") + Value::Error("Can't convert ARRAY of length ".scalar(@{$x})." to ".Value::showClass($class)) unless (scalar(@{$x}) == 1); - return $self->parseFormula(@{$x}) unless Value::isRealNumber($x->[0]); - return $self->formula($x->[0]) if Value::isFormula($x->[0]); - bless {data => $x}, $class; + if (Value::isRealNumber($x->[0])) { + return $self->formula($x->[0]) if Value::isFormula($x->[0]); + return (bless {data => $x}, $class); + } + $x = Value::makeValue($x->[0]); + return $x if Value::isRealNumber($x); + Value::Error("Can't convert ".Value::showClass($x)." to ".Value::showClass($class)); } # Index: Point.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Point.pm,v retrieving revision 1.11 retrieving revision 1.12 diff -Llib/Value/Point.pm -Llib/Value/Point.pm -u -r1.11 -r1.12 --- lib/Value/Point.pm +++ lib/Value/Point.pm @@ -34,22 +34,23 @@ sub new { my $self = shift; my $class = ref($self) || $self; my $p = shift; $p = [$p,@_] if (scalar(@_) > 0); + $p = Value::makeValue($p) if (defined($p) && !ref($p)); + return $p if (Value::isFormula($p) && $p->type eq Value::class($self)); my $pclass = Value::class($p); my $isFormula = 0; my @d; @d = $p->dimensions if $pclass eq 'Matrix'; if ($pclass =~ m/Point|Vector/) {$p = $p->data} elsif ($pclass eq 'Matrix' && scalar(@d) == 1) {$p = [$p->value]} elsif ($pclass eq 'Matrix' && scalar(@d) == 2 && $d[0] == 1) {$p = ($p->value)[0]} elsif ($pclass eq 'Matrix' && scalar(@d) == 2 && $d[1] == 1) {$p = ($p->transpose->value)[0]} - elsif (defined($p) && ref($p) eq "") {return $self->parseFormula($p)} else { $p = [$p] if (defined($p) && ref($p) ne 'ARRAY'); Value::Error("Points must have at least one coordinate") unless defined($p) && scalar(@{$p}) > 0; foreach my $x (@{$p}) { + $x = Value::makeValue($x); $isFormula = 1 if Value::isFormula($x); Value::Error("Coordinate of Point can't be ".Value::showClass($x)) unless Value::isNumber($x); - $x = Value::Real->make($x) unless ref($x); } } return $self->formula($p) if $isFormula; Index: Complex.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Complex.pm,v retrieving revision 1.11 retrieving revision 1.12 diff -Llib/Value/Complex.pm -Llib/Value/Complex.pm -u -r1.11 -r1.12 --- lib/Value/Complex.pm +++ lib/Value/Complex.pm @@ -34,6 +34,7 @@ # one or two real numbers, or # an array ref of one or two reals, or # a Value::Complex object +# a formula returning a real or complex # Make a formula if either part is a formula # sub new { @@ -44,6 +45,7 @@ Value::Error("Can't convert ARRAY of length ".scalar(@{$x})." to a Complex Number") unless (scalar(@{$x}) == 2); $x->[0] = Value::makeValue($x->[0]); $x->[1] = Value::makeValue($x->[1]); + return $x->[0] if Value::isComplex($x->[0]) && scalar(@_) == 0; Value::Error("Real part can't be ".Value::showClass($x->[0])) unless (Value::isRealNumber($x->[0])); Value::Error("Imaginary part can't be ".Value::showClass($x->[1])) Index: Matrix.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Matrix.pm,v retrieving revision 1.15 retrieving revision 1.16 diff -Llib/Value/Matrix.pm -Llib/Value/Matrix.pm -u -r1.15 -r1.16 --- lib/Value/Matrix.pm +++ lib/Value/Matrix.pm @@ -33,15 +33,14 @@ # sub new { my $self = shift; my $class = ref($self) || $self; - my $M = shift; + my $M = shift; $M = Value::makeValue($M) if !ref($M) && scalar(@_) == 0; return bless {data => $M->data}, $class if (Value::class($M) =~ m/Point|Vector|Matrix/ && scalar(@_) == 0); - return $M if (Value::isFormula($M) && $M->type eq "Matrix"); - $M = [$M,@_] if ((defined($M) && ref($M) ne 'ARRAY') || scalar(@_) > 0); - Value::Error("Matrices must have at least one entry") unless defined($M) && scalar(@{$M}) > 0; - return $self->numberMatrix(@{$M}) if Value::isNumber($M->[0]); + return $M if (Value::isFormula($M) && $M->type eq Value::class($self)); + $M = [$M,@_] if (ref($M) ne 'ARRAY' || scalar(@_) > 0); + Value::Error("Matrices must have at least one entry") unless scalar(@{$M}) > 0; return $self->matrixMatrix(@{$M}) if ref($M->[0]) =~ m/ARRAY|Matrix/; - return $self->parseFormula(@{$M}); + return $self->numberMatrix(@{$M}); } # @@ -75,8 +74,8 @@ my $self = shift; my $class = ref($self) || $self; my @M = (); my $isFormula = 0; foreach my $x (@_) { - Value::Error("Matrix row entries must be numbers") unless (Value::isNumber($x)); - $x = Value::Real->make($x) if !ref($x); + $x = Value::makeValue($x); + Value::Error("Matrix row entries must be numbers") unless Value::isNumber($x); push(@M,$x); $isFormula = 1 if Value::isFormula($x); } return $self->formula([@M]) if $isFormula; |
From: Gavin L. v. a. <we...@ma...> - 2005-02-15 21:39:35
|
Log Message: ----------- major look and feel modification of GatewayQuiz module, to include jump to scrolling links, preview links for each problem, rearranged preview and correct answer output. Tags: ---- rel-2-1-a1 Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator: GatewayQuiz.pm ProblemSet.pm Revision Data ------------- Index: ProblemSet.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/ProblemSet.pm,v retrieving revision 1.54.2.3 retrieving revision 1.54.2.4 diff -Llib/WeBWorK/ContentGenerator/ProblemSet.pm -Llib/WeBWorK/ContentGenerator/ProblemSet.pm -u -r1.54.2.3 -r1.54.2.4 --- lib/WeBWorK/ContentGenerator/ProblemSet.pm +++ lib/WeBWorK/ContentGenerator/ProblemSet.pm @@ -102,7 +102,7 @@ my @setIDs = sortByName(undef, $db->listUserSets($eUserID)); # do not show unpublished siblings unless user is allowed to view unpublished sets unless ($authz->hasPermissions($user, "view_unpublished_sets") ) { - @setIDs = grep {my $visible = $db->getGlobalSet( $_)->published; (defined($visible))? $visible : 1} + @setIDs = grep {my $vset = $db->getGlobalSet($_); my $visible = defined($vset) ? $vset->published : 0; (defined($visible))? $visible : 1} @setIDs; } print CGI::start_ul({class=>"LinksMenu"}); Index: GatewayQuiz.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/GatewayQuiz.pm,v retrieving revision 1.9.4.6 retrieving revision 1.9.4.7 diff -Llib/WeBWorK/ContentGenerator/GatewayQuiz.pm -Llib/WeBWorK/ContentGenerator/GatewayQuiz.pm -u -r1.9.4.6 -r1.9.4.7 --- lib/WeBWorK/ContentGenerator/GatewayQuiz.pm +++ lib/WeBWorK/ContentGenerator/GatewayQuiz.pm @@ -48,6 +48,12 @@ use WeBWorK::ContentGenerator::Instructor qw(assignSetVersionToUser); +# template method +sub templateName { + return "gateway"; +} + + ################################################################################ # "can" methods ################################################################################ @@ -190,14 +196,12 @@ sub after { return time >= $_[0] } sub between { my $t = time; return $t > $_[0] && $t < $_[1] } -# *BeginPPM* ################################################################### -# this code taken from Problem.pm; excerpted section ends at *EndPPM* -# modifications are flagged with comments *GW* - ################################################################################ # output utilities ################################################################################ +# subroutine is modified from that in Problem.pm to produce a different +# table format sub attemptResults { my $self = shift; my $pg = shift; @@ -215,7 +219,7 @@ my $showMessages = $showAttemptAnswers && grep { $pg->{answers}->{$_}->{ans_message} } @answerNames; my $basename = "equation-" . $self->{set}->psvn. "." . $self->{problem}->problem_id . "-preview"; - + # to make grabbing these options easier, we'll pull them out now... my %imagesModeOptions = %{$ce->{pg}->{displayModeOptions}->{images}}; @@ -230,16 +234,21 @@ dvipng_align => $imagesModeOptions{dvipng_align}, dvipng_depth_db => $imagesModeOptions{dvipng_depth_db}, ); - - my $header; - #$header .= CGI::th("Part"); - $header .= $showAttemptAnswers ? CGI::th("Entered") : ""; - $header .= $showAttemptPreview ? CGI::th("Answer Preview") : ""; - $header .= $showCorrectAnswers ? CGI::th("Correct") : ""; - $header .= $showAttemptResults ? CGI::th("Result") : ""; - $header .= $showMessages ? CGI::th("Messages") : ""; - my @tableRows = ( $header ); + + my %resultsData = (); + $resultsData{'Entered'} = CGI::td({-class=>"label"}, "Your answer parses as:"); + $resultsData{'Preview'} = CGI::td({-class=>"label"}, "Your answer previews as:"); + $resultsData{'Correct'} = CGI::td({-class=>"label"}, "The correct answer is:"); + $resultsData{'Results'} = CGI::td({-class=>"label"}, "Result:"); + $resultsData{'Messages'} = CGI::td({-class=>"label"}, "Messages:"); + + my %resultsRows = (); + foreach ( qw( Entered Preview Correct Results Messages ) ) { + $resultsRows{$_} = ""; + } + my $numCorrect = 0; + my $numAns = 0; foreach my $name (@answerNames) { my $answerResult = $pg->{answers}->{$name}; my $studentAnswer = $answerResult->{student_ans}; # original_student_ans @@ -257,14 +266,29 @@ # of the answer names is changeable. this only fixes it for "AnSwEr" #$name =~ s/^AnSwEr//; - my $row; - #$row .= CGI::td($name); - $row .= $showAttemptAnswers ? CGI::td($self->nbsp($studentAnswer)) : ""; - $row .= $showAttemptPreview ? CGI::td($self->nbsp($preview)) : ""; - $row .= $showCorrectAnswers ? CGI::td($self->nbsp($correctAnswer)) : ""; - $row .= $showAttemptResults ? CGI::td($self->nbsp($resultString)) : ""; - $row .= $showMessages ? CGI::td($self->nbsp($answerMessage)) : ""; - push @tableRows, $row; + my $pre = $numAns ? CGI::td(" ") : ""; + + $resultsRows{'Entered'} .= $showAttemptAnswers ? + CGI::Tr( $pre . $resultsData{'Entered'} . + CGI::td({-class=>"output"}, $self->nbsp($studentAnswer))) : ""; + $resultsData{'Entered'} = ''; + $resultsRows{'Preview'} .= $showAttemptPreview ? + CGI::Tr( $pre . $resultsData{'Preview'} . + CGI::td({-class=>"output"}, $self->nbsp($preview)) ) : ""; + $resultsData{'Preview'} = ''; + $resultsRows{'Correct'} .= $showCorrectAnswers ? + CGI::Tr( $pre . $resultsData{'Correct'} . + CGI::td({-class=>"output"}, $self->nbsp($correctAnswer)) ) : ""; + $resultsData{'Correct'} = ''; + $resultsRows{'Results'} .= $showAttemptResults ? + CGI::Tr( $pre . $resultsData{'Results'} . + CGI::td({-class=>"output"}, $self->nbsp($resultString)) ) : ""; + $resultsRows{'Results'} = ''; + $resultsRows{'Messages'} .= $showMessages ? + CGI::Tr( $pre . $resultsData{'Messages'} . + CGI::td({-class=>"output"}, $self->nbsp($answerMessage)) ) : ""; + + $numAns++; } # render equation images @@ -282,23 +306,32 @@ my $summary = ""; if (scalar @answerNames == 1) { if ($numCorrect == scalar @answerNames) { - $summary .= CGI::div({class=>"ResultsWithoutError"},"The above answer is correct."); + $summary .= CGI::div({class=>"gwCorrect"},"This answer is correct."); } else { - $summary .= CGI::div({class=>"ResultsWithError"},"The above answer is NOT correct."); + $summary .= CGI::div({class=>"gwIncorrect"},"This answer is NOT correct."); } } else { if ($numCorrect == scalar @answerNames) { - $summary .= CGI::div({class=>"ResultsWithoutError"},"All of the above answers are correct."); + $summary .= CGI::div({class=>"gwCorrect"},"All of these answers are correct."); } else { - $summary .= CGI::div({class=>"ResultsWithError"},"At least one of the above answers is NOT correct."); + $summary .= CGI::div({class=>"gwIncorrect"},"At least one of these answers is NOT correct."); } } return - CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows)) - . ($showSummary ? CGI::p({class=>'emphasis'},$summary) : ""); +# CGI::table({-class=>"attemptResults"}, $resultsRows{'Entered'}, + CGI::table({-class=>"gwAttemptResults"}, $resultsRows{'Entered'}, + $resultsRows{'Preview'}, $resultsRows{'Correct'}, + $resultsRows{'Results'}, $resultsRows{'Messages'}) . + ($showSummary ? CGI::p({class=>'emphasis'},$summary) : ""); +# CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows)) +# . ($showSummary ? CGI::p({class=>'emphasis'},$summary) : ""); } +# *BeginPPM* ################################################################### +# this code taken from Problem.pm; excerpted section ends at *EndPPM* +# modifications are flagged with comments *GW* + sub viewOptions { my ($self) = @_; my $ce = $self->r->ce; @@ -411,6 +444,12 @@ my $effectiveUserName = $r->param('effectiveUser'); my $key = $r->param('key'); +# this is a horrible hack to allow use of a javascript link to trigger +# the preview of the page: set previewAnswers to yes if either the +# "previewAnswers" or "previewhack" inputs are set + my $prevOr = $r->param('previewAnswers') || $r->param('previewHack'); + $r->param('previewAnswers', $prevOr) if ( defined( $prevOr ) ); + my $User = $db->getUser($userName); die "record for user $userName (real user) does not exist." unless defined $User; @@ -431,9 +470,6 @@ # with that version. we do this after we've validated that the user is # assigned the set, below - # set a gateway template? FIXME - # $self->{templateName} = "gateway"; - ################################### # gateway content generator tests ################################### @@ -1198,7 +1234,7 @@ } if ( ! $can{recordAnswersNextTime} ) { - print CGI::start_div({style=>"background-color:#dddddd;"}); + print CGI::start_div({class=>"gwMessage"}); my $mesg = ( $requestedVersion ) ? '' : ", because you have used all available attempts on it or " . "because its time limit has expired.\n" . @@ -1210,7 +1246,7 @@ " right or wrong."), "\n\n"; print CGI::end_div(); } else { - print CGI::start_div({style=>"background-color:#ddddff;"}); + print CGI::start_div({class=>"gwTiming"}); print CGI::p(CGI::strong("This version started ", scalar(localtime($set->open_date())), CGI::br(),"\nTime limit : ", @@ -1220,7 +1256,7 @@ "The current time is ", scalar(localtime()))), "\n\n"; print CGI::end_div(); if ( $set->due_date() - $set->version_time_limit() < 1 ) { - print CGI::start_div({style=>"background-color:#ffffdd;"}); + print CGI::start_div({class=>"gwWarning"}); print CGI::p(CGI::strong("You have less than 1 minute to ", "complete this version.\n")); print CGI::end_div(); @@ -1228,9 +1264,25 @@ } - print CGI::startform("POST", $r->uri), $self->hidden_authen_fields, + print CGI::startform({-name=>"gwquiz", -method=>"POST", -action=>$r->uri}), $self->hidden_authen_fields, $self->hidden_proctor_authen_fields; +# FIXME RETURNTO +# this is a horrible hack to try and let us use a javascript link to +# trigger previews + print CGI::hidden({-name=>'previewHack', -value=>''}), CGI::br(); +# and the text for the link + my $jsprevlink = 'javascript:document.gwquiz.previewHack.value="1";' . + 'document.gwquiz.submit();'; + +# some links to easily move between problems + my $jumpLinks = "Jump to problem: "; + for my $i ( 0 .. $#pg_results ) { + my $pn = $i+1; + $jumpLinks .= "/ " . CGI::a({-href=>".", -onclick=>"jumpTo($i);return false;"}, "$pn") . " /"; + } + print CGI::p($jumpLinks,"\n"); + # print out problems and attempt results, as appropriate # note: args to attemptResults are (self,) $pg, $showAttemptAnswers, # $showCorrectAnswers, $showAttemptResults (and-ed with @@ -1262,42 +1314,74 @@ $problemNumber++; # print "pCA = >", (defined($pg->{flags}->{showPartialCorrectAnswers}) ? $pg->{flags}->{showPartialCorrectAnswers} : "undef"), "<"; - print CGI::start_div({class=>"problemHeader"}); + + my $recordMessage = ''; + my $resultsTable = ''; + +# print CGI::start_div({class=>"problemHeader", -style=>"border: solid black 1px;"}); + +# FIXME: I've coded the "ResultsWithError" style into the recordMessage strings +# FIXME: manually; this should be pushed out to the template or stylesheet. + if ($pg->{flags}->{showPartialCorrectAnswers} >= 0 && $submitAnswers) { if ( $scoreRecordedMessage[$probOrder[$i]] ne "Your score on this problem was recorded." ) { - print CGI::div({class=>'ResultsWithError'}, - "ANSWERS NOT RECORDED --", CGI::br(), - $scoreRecordedMessage[$probOrder[$i]],CGI::br()); + $recordMessage = CGI::span({class=>"resultsWithError"}, + "ANSWERS NOT RECORDED --", + $scoreRecordedMessage[$probOrder[$i]]); +# print CGI::div({class=>'ResultsWithError'}, +# "ANSWERS NOT RECORDED --", +# $scoreRecordedMessage[$probOrder[$i]],CGI::br()); } - print + $resultsTable = $self->attemptResults($pg, 1, $will{showCorrectAnswers}, $pg->{flags}->{showPartialCorrectAnswers}, 1, 1); +# print $self->attemptResults($pg, 1, $will{showCorrectAnswers}, +# $pg->{flags}->{showPartialCorrectAnswers}, +# 1, 1); + } elsif ( $checkAnswers ) { - print CGI::div({class=>'ResultsWithError'}, - "ANSWERS ONLY CHECKED -- ", CGI::br(), - "ANSWERS NOT RECORDED", CGI::br() ); - print + $recordMessage = CGI::span({class=>"resultsWithError"}, + "ANSWERS ONLY CHECKED -- ", + "ANSWERS NOT RECORDED"); +# print CGI::div({class=>'ResultsWithError'}, +# "ANSWERS ONLY CHECKED -- ", +# "ANSWERS NOT RECORDED", CGI::br() ); + $resultsTable = $self->attemptResults($pg, 1, $will{showCorrectAnswers}, $pg->{flags}->{showPartialCorrectAnswers}, 1, 1); +# print +# $self->attemptResults($pg, 1, $will{showCorrectAnswers}, +# $pg->{flags}->{showPartialCorrectAnswers}, +# 1, 1); } elsif ( $previewAnswers ) { - print CGI::div({class=>'ResultsWithError'}, - "PREVIEW ONLY -- ", CGI::br(), - "ANSWERS NOT RECORDED", CGI::br() ); - print $self->attemptResults($pg, 1, 0, 0, 0, 1); + $recordMessage = CGI::span({class=>"resultsWithError"}, + "PREVIEW ONLY -- ANSWERS NOT RECORDED"); +# print CGI::div({class=>'ResultsWithError'}, +# "PREVIEW ONLY -- ANSWERS NOT RECORDED", CGI::br() ); + $resultsTable = $self->attemptResults($pg, 1, 0, 0, 0, 1); +# print $self->attemptResults($pg, 1, 0, 0, 0, 1); } - print CGI::end_div(); - print CGI::start_div({class=>"problem"}); - print CGI::strong("Problem $problemNumber."), "\n"; + print CGI::start_div({class=>"gwProblem"}); + print CGI::a({-name=>"#$i"},""); + print CGI::strong("Problem $problemNumber."), "\n", $recordMessage; print CGI::p($pg->{body_text}), CGI::p($pg->{result}->{msg} ? CGI::b("Note: ") : "", CGI::i($pg->{result}->{msg})); + print CGI::p({class=>"gwPreview"}, + CGI::a({-href=>"$jsprevlink"}, "preview problems")); +# print CGI::end_div(); + + print $resultsTable if $resultsTable; + print CGI::end_div(); + print "\n", CGI::hr(), "\n"; } + print CGI::p($jumpLinks, "\n"); if ($can{showCorrectAnswers}) { print CGI::checkbox(-name => "showCorrectAnswers", |
From: Gavin L. v. a. <we...@ma...> - 2005-02-15 21:35:50
|
Log Message: ----------- new template file: gw.template for gateway tests Tags: ---- rel-2-1-a1 Modified Files: -------------- webwork2/conf/templates: ur.template Added Files: ----------- webwork2/conf/templates: gw.template Revision Data ------------- Index: ur.template =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D RCS file: /webwork/cvs/system/webwork2/conf/templates/ur.template,v retrieving revision 1.29.2.2 retrieving revision 1.29.2.3 diff -Lconf/templates/ur.template -Lconf/templates/ur.template -u -r1.29.= 2.2 -r1.29.2.3 --- conf/templates/ur.template +++ conf/templates/ur.template @@ -60,7 +60,7 @@ td.ContentPanel a:active { color: red; } =20 /* contains info */ -td.InfoPanel { background-color: #DDDDDD; color: black; width: 30% } +td.InfoPanel { background-color: #DDDDDD; color: black; width: 15% } td.InfoPanel a:link, td.InfoPanel a:visited { color: blue; } td.InfoPanel a:active { color: red; } --- /dev/null +++ conf/templates/gw.template @@ -0,0 +1,301 @@ +<!DOCTYPE html + PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" + SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + +<!-- +########################################################################= ######## +# WeBWorK Online Homework Delivery System +# Copyright =A9 2000-2003 The WeBWorK Project, http://openwebwork.sf.net= / +# $CVSHeader: webwork2/conf/templates/gw.template,v 1.1.2.1 2005/02/15 2= 1:18:58 glarose Exp $ +#=20 +# This program is free software; you can redistribute it and/or modify i= t under +# the terms of either: (a) the GNU General Public License as published b= y the +# Free Software Foundation; either version 2, or (at your option) any la= ter +# version, or (b) the "Artistic License" which comes with this package. +#=20 +# This program is distributed in the hope that it will be useful, but WI= THOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or = FITNESS +# FOR A PARTICULAR PURPOSE. See either the GNU General Public License o= r the +# Artistic License for more details. +########################################################################= ######## +--> + +<html xmlns=3D"http://www.w3.org/1999/xhtml" lang=3D"en-US"> +<head> +<title><!--#path style=3D"text" text=3D" : " textonly=3D"1"--></title> +<!--#head--> +<style type=3D"text/css"> + +/********************/ +/* template classes */ +/********************/ + +body { margin: 0px; } + +/* left table cell, contains logo and menus */ +td.LeftPanel { background-color: #003366; color: white; white-space: now= rap; width: 1em; } +td.LeftPanel a:link, +td.LeftPanel a:visited { color: #FF9933; } + +div.Logo { } +div.Links { font-size: small; } +div.Siblings { font-size: small; } +div.Options { font-size: small; } + +/* top table cell, contains login message and path */ +td.TopPanel { background-color: #003366; color: white; height: 1; } +td.TopPanel a:link, +td.TopPanel a:visited { color: #FF9933; } + + +*.LoginStatus { text-align: right; font-size: small; position:absolute; = right: 0; } +td.Timestamp { text-align: left; font-size: small; font-style: italic= ; } + +*.Path { } + +/* main content panel, contains body */ +td.ContentPanel { background-color: white; color: black; } +td.ContentPanel a:link, +td.ContentPanel a:visited { color: blue; } +td.ContentPanel a:active { color: red; } + +/* contains info */ +td.InfoPanel { background-color: #DDDDDD; color: black; width: 15% } +td.InfoPanel a:link, +td.InfoPanel a:visited { color: blue; } +td.InfoPanel a:active { color: red; } + +div.Info { } +div.Nav { } +div.Title { font-size: 16pt; } +div.SubmitError { color: red; font-style: italic; } +div.Message { font-style: italic; } +div.Body { } +div.Warnings { } + +/* background colors for success and failure messages */ +div.ResultsWithoutError { background-color: #66ff99 } /* light green */ +div.ResultsWithError { background-color: #ffcccc } /* light red */ + +/* text colors for published and unpublished sets */ +font.Published { font-style: normal; font-weight: bold; color: #0000= 00; } /* black */ +font.Unpublished { font-style: italic; font-weight: normal; color: #aaaa= aa; } /* light grey */ + +/* text colors for Auditing, Current, and Dropped students */ +div.Audit { font-style: normal; color: black; } +div.Enrolled { font-weight: bold; color: black; } +div.Drop { font-style: italic; color: grey; } + +/*******************/ +/* general classes */ +/*******************/ + +p.emphasis { font-style:italic; } + +/************************/ +/* new standard classes */ +/************************/ + +/* tables used for laying out form fields shouldn't have a border */ +table.FormLayout { border: 0; } +table.FormLayout tr { vertical-align: top; } +table.FormLayout th.LeftHeader { text-align: right; white-space: nowrap;= } +table.FormLayout tr.ButtonRow { text-align: left; } +table.FormLayout tr.ButtonRowCenter { text-align: center; } + +/* for problems which are rendered by themselves, e.g., by Set Maker */ +div.RenderSolo { background-color: #E0E0E0; color: black; } + +/* minimal style for lists of links (generated by the links escape) */ +ul.LinksMenu { list-style: none; margin-left: 0; padding-left: 0; } +ul.LinksMenu ul { list-style: none; margin-left: 0.5em; padding-left: 0;= } + +/*************************/ +/* WeBWorK::HTML widgets */ +/*************************/ + +/* WeBWorK::HTML::ScrollingRecordList */ +div.ScrollingRecordList { padding: 1em; white-space: nowrap; border: thi= n solid gray; } +div.ScrollingRecordList select.ScrollingRecordList { width: 99%; } + +/***********************************************************************= **/ +/* classes used in Problem.pm (replace these with new standard classes?)= */ +/***********************************************************************= **/ + +table.attemptResults { + border-style: outset;=20 + border-width: 1px;=20 + margin: 0px 10pt;=20 + border-spacing: 1px; +} +table.attemptResults td, +table.attemptResults th { + border-style: inset;=20 + border-width: 1px;=20 + text-align: center;=20 + width: 15ex; + background-color: #DDDDDD; +} +div.problemHeader { float: left; } +div.problem { clear: both; background-color: #E0E0E0; color: black; } +.parsehilight { background-color:yellow; } + +</style> + +<style type=3D"text/css"> +/******************/ +/* gateway styles */ +/******************/ + +div.gwMessage { background-color:#dddddd; color: black; } +div.gwTiming { background-color:#ddddff; color: black; } +div.gwWarning { background-color:#ffffdd; color: black; } + +span.resultsWithError { background-color: #ffcccc; color: black; } +span.resultsWithoutError { background-color: #66ff99; color: black; } +div.gwCorrect { background-color: #66ff99; color: black; } +div.gwIncorrect { background-color: #ff9999; color: black; } + +div.gwProblem { + clear: both;=20 + background-color: #E0E0E0;=20 + color: black; + padding: 3px; + border: solid black 1px; +} +div.gwSoln { +/* background-color: #e0e0ff; */ + background-color: transparent; + color: black; +/* padding: 2px; */ +/* border: dashed black 1px; */ +} +div.gwSoln b { color: navy; } + +p.gwPreview {=20 + font-size: smaller; + text-align: right;=20 + margin-top: 0px; + margin-bottom: 0px; +} + +table.gwAttemptResults { + border-width: 0px; +} +table.gwAttemptResults td.label {=20 + font-weight: bold;=20 + background-color: transparent; + color: navy; +} +table.gwAttemptResults td.output { + padding: 2px; + border: solid black 1px; + background-color: #eeeeee; +} + +</style> +<script language=3D"javascript" type=3D"text/javascript"> +function jumpTo(ref) { + if ( ref ) { + if ( navigator.appName =3D=3D "Netscape" &&=20 + parseFloat(navigator.appVersion) < 5 ) { + var xpos =3D document.anchors[ref].x; + var ypos =3D document.anchors[ref].y; + } else { + var xpos =3D document.anchors[ref].offsetLeft; + var ypos =3D document.anchors[ref].offsetTop; + } + if ( window.scrollTo =3D=3D null ) { // cover for anyone + window.scroll(xpos,ypos); // lacking js1.2 + } else { + window.scrollTo(xpos,ypos); + } + } + return false; // prevent link from being followed +} +</script> +</head> +<body> +<table width=3D"100%" cellpadding=3D"10" cellspacing=3D"0" border=3D"0"> + <tr valign=3D"top"> +<!-- removed left sidebar --> + <!--#if can=3D"info"--> + <td class=3D"TopPanel" colspan=3D"2"> + <!--#else--> + <td class=3D"TopPanel" > + <!--#endif--> + <div style=3D"position:relative; width:100%;"> + <!--#if can=3D"path"--> + <span class=3D"Path"> + <!--#path style=3D"text" image=3D"/webwork2_files/images/right_arro= w.png" text=3D" > "--> + </span> + <!--#endif--> + =09 + <!--#if loggedin=3D"1"--> + <!--#if can=3D"loginstatus"--> + =09 + <span class=3D"LoginStatus"> =09 + =09 + <!--#loginstatus--> + </span> + <!--#endif--> + <!--#endif--> + </div> + </td> + </tr> + <tr valign=3D"top"> + <!--#if warnings=3D"1"--> + <td class=3D"ContentPanelError" bgcolor=3D"#ffcccc"> + <!--#else--> + <td class=3D"ContentPanel" bgcolor=3D"#ffffff"> + <!--#endif-->=09 +<!-- removed nav button to go up --> + <!--#if can=3D"title"--> + <div class=3D"Title"> + <!--#title--> + </div> + <!--#endif--> + <!--#if can=3D"message"--> + <div class=3D"Message"> + <!--#message--> + </div> + <!--#endif--> + + <!--#if can=3D"submiterror"--> + <div class=3D"SubmitError"> + <!--#submiterror--> + </div> + <!--#endif--> + <!--#if can=3D"body"--> + <div class=3D"Body"> + <!--#body--> + </div> + <!--#endif--> + <!--#if warnings=3D"1"--> + <hr> + <div class=3D"Warnings"> + <!--#warnings--> + </div> + <!--#endif--> + <!--#if can=3D"message"--> + <div class=3D"Message"> + <!--#message--> + </div> + <!--#endif--> + </td> + <!--#if can=3D"info"--> + <td class=3D"InfoPanel"> + <div class=3D"Info"> + <!--#info--> + </div> + </td> + <!--#endif--> + </tr> + <tr> + <td class =3D "Timestamp", colspan=3D3> + Updated: <!--#timestamp--> + </td> + </tr> +</table> +</body> +</html> |
From: Gavin L. v. a. <we...@ma...> - 2005-02-15 21:34:10
|
Log Message: ----------- global.conf.dist: added gateway template to supported templates Tags: ---- rel-2-1-a1 Modified Files: -------------- webwork2/conf: global.conf.dist Revision Data ------------- Index: global.conf.dist =================================================================== RCS file: /webwork/cvs/system/webwork2/conf/global.conf.dist,v retrieving revision 1.72.2.3 retrieving revision 1.72.2.4 diff -Lconf/global.conf.dist -Lconf/global.conf.dist -u -r1.72.2.3 -r1.72.2.4 --- conf/global.conf.dist +++ conf/global.conf.dist @@ -369,6 +369,7 @@ %templates = ( system => "$webworkDirs{conf}/templates/ur.template", + gateway => "$webworkDirs{conf}/templates/gw.template", ); ################################################################################ |
From: Mike G. v. a. <we...@ma...> - 2005-02-15 05:28:43
|
Log Message: ----------- Added changes to allow create new blank problem to work. These include significant cleanup of PGproblemEditor.pm Tags: ---- rel-2-1-patches Modified Files: -------------- webwork2/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm ProblemSetDetail.pm Revision Data ------------- Index: ProblemSetDetail.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm,v retrieving revision 1.13.2.3 retrieving revision 1.13.2.4 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -u -r1.13.2.3 -r1.13.2.4 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm @@ -1199,8 +1199,6 @@ ])); } - my $editNewProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID =>'new_problem' }); - my $editNewProblemLink = $self->systemLink($editNewProblemPage, params => { make_local_copy => 1, file_type => 'blank_problem' }); # print final lines print CGI::end_table(); @@ -1211,7 +1209,6 @@ CGI::br(); print CGI::input({type=>"submit", name=>"submit_changes", value=>"Save Changes"}); print CGI::input({type=>"submit", name=>"handle_numbers", value=>"Reorder problems only"}) . "(Any unsaved changes will be lost.)"; - print CGI::p( CGI::a({href=>$editNewProblemLink},'Create'). 'a new blank problem'); print CGI::p(<<HERE); Any time problem numbers are intentionally changed, the problems will always be renumbered consecutively, starting from one. When deleting @@ -1225,6 +1222,11 @@ } else { print CGI::p(CGI::b("This set doesn't contain any problems yet.")); } + # always allow one to add a new problem. + my $editNewProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID =>'new_problem' }); + my $editNewProblemLink = $self->systemLink($editNewProblemPage, params => { make_local_copy => 1, file_type => 'blank_problem' }); + + print CGI::p( CGI::a({href=>$editNewProblemLink},'Create'). 'a new blank problem'); print CGI::end_form(); Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.48 retrieving revision 1.48.2.1 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.48 -r1.48.2.1 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -30,6 +30,7 @@ use WeBWorK::Utils qw(readFile surePathToFile); use Apache::Constants qw(:common REDIRECT); use HTML::Entities; +use URI::Escape; use WeBWorK::Utils::Tasks qw(fake_set fake_problem); ########################################################### @@ -45,140 +46,450 @@ # # editMode = temporaryFile (view the temp file defined by course_info.txt.user_name.tmp # instead of the file course_info.txt) -# The editFileSuffix is "user_name.tmp" by default. It's definition should be moved to Instructor.pm #FIXME +# this flag is read by Problem.pm and ProblemSet.pm, perhaps others +# The TEMPFILESUFFIX is "user_name.tmp" by default. It's definition should be moved to Instructor.pm #FIXME ########################################################### +########################################################### +# The behavior of this module is essentially defined +# by the values of $file_type and the submit button which is placed in $action +############################################################# +# File types which can be edited +# +# file_type eq 'problem' +# this is the most common type -- this editor can be called by an instructor when viewing any problem. +# the information for retrieving the source file is found using the problemID in order to look +# look up the source file path. +# +# file_type eq 'problem_with_source' +# This is the same as the 'problem' file type except that the source for the problem is found in +# the parameter $r->param('sourceFilePath'). +# +# file_type eq 'set_header' +# This is a special case of editing the problem. The set header is often listed as problem 0 in the set's list of problems. +# +# file_type eq 'hardcopy_header' +# This is a special case of editing the problem. The hardcopy_header is often listed as problem 0 in the set's list of problems. +# But it is used instead of set_header when producing a hardcopy of the problem set in the TeX format, instead of producing HTML +# formatted version for use on the computer screen. +# +# filte_type eq 'course_info +# This allows editing of the course_info.txt file which gives general information about the course. It is called from the +# ProblemSets.pm module. +# +# file_type eq 'blank_problem' +# This is a special call which allows one to create and edit a new PG problem. The "stationery" source for this problem is +# stored in the conf/snippets directory and defined in global.conf by $webworkFiles{screenSnippets}{blankProblem} +############################################################# +# submit button actions -- these and the file_type determine the state of the module +# Save ---- action = save +# Save as ---- action = save_as +# View Problem ---- action = refresh +# Add this problem to: ---- action = add_problem_to_set +# Make this set header for: ---- action = add_set_header_to_set +# Revert ---- action = revert +# no submit button defined ---- action = fresh_edit +################################################### +# +# Determining which is the correct path to the file is a mess!!! FIXME +# The path to the file to be edited is eventually put in tempFilePath +# +# $problemPath is also used as is editFilePath. let's try to regularize these. +#(sourceFile) (problemPath)(tempFilePath)(editFilePath)(forcedSourceFile)(problemPath) +#input parameter can be: sourceFilePath +################################################################# +# params read +# user +# effectiveUser +# submit +# file_type +# problemSeed +# displayMode +# edit_level +# make_local_copy +# sourceFilePath +# problemContents +# save_to_new_file +# + #our $libraryName; #our $rowheight; +our $TEMPFILESUFFIX; sub pre_header_initialize { - my ($self) = @_; - my $r = $self->r; - my $ce = $r->ce; - my $urlpath = $r->urlpath; - my $authz = $r->authz; - my $user = $r->param('user'); - - my $submit_button = $r->param('submit'); # obtain submit command from form - my $file_type = $r->param("file_type") || ''; - + my ($self) = @_; + my $r = $self->r; + my $ce = $r->ce; + my $urlpath = $r->urlpath; + my $authz = $r->authz; + my $user = $r->param('user'); + $TEMPFILESUFFIX = $user.'.tmp'; + + my $submit_button = $r->param('submit'); # obtain submit command from form + my $file_type = $r->param("file_type") || ''; + my $setName = $r->urlpath->arg("setID") ; # using $r->urlpath->arg("setID") ||'' causes trouble with set 0!!! + my $problemNumber = $r->urlpath->arg("problemID"); + # Check permissions return unless ($authz->hasPermissions($user, "access_instructor_tools")); return unless ($authz->hasPermissions($user, "modify_problem_sets")); + + ############################################################################# + # Save file to permanent or temporary file, then redirect for viewing + ############################################################################# + # + # Any file "saved as" should be assigned to "Undefined_Set" and redirectoed to be viewed again in the editor + # + # Problems "saved" or 'refreshed' are to be redirected to the Problem.pm module + # Set headers which are "saved" are to be redirected to the ProblemSet.pm page + # Hardcopy headers which are "saved" are aso to be redirected to the ProblemSet.pm page + # Course_info files are redirected to the ProblemSets.pm page + ############################################################################## + - # Save problem to permanent or temporary file, then redirect for viewing - if (defined($submit_button) and - ($submit_button eq 'Save' or $submit_button eq 'Refresh' - or ($submit_button eq 'Save as' and $file_type eq 'problem'))) { - my $setName = $r->urlpath->arg("setID"); - my $problemNumber = $r->urlpath->arg("problemID"); - - # write the necessary files - # return file path for viewing problem in $self->{currentSourceFilePath} - # obtain the appropriate seed - $self->saveFileChanges($setName, $problemNumber); - - ##### calculate redirect URL based on file type ##### - - # get some information - #my $hostname = $r->hostname(); - #my $port = $r->get_server_port(); - #my $uri = $r->uri; - my $courseName = $urlpath->arg("courseID"); - my $problemSeed = ($r->param('problemSeed')) ? $r->param('problemSeed') : ''; - my $displayMode = ($r->param('displayMode')) ? $r->param('displayMode') : ''; - - my $viewURL = ''; - - if($self->{file_type} eq 'problem') { - if($submit_button eq 'Save as') { # redirect to myself - my $sourceFile = $self->{problemPath}; - # strip off template directory prefix + + ###################################### + # Insure that file_type is defined + ###################################### + # We have already read in the file_type parameter from the form + # + # If this has not been defined we are dealing with a set header + # or regular problem + if (defined($file_type) and ($file_type =~/\S/)) { #file_type is defined and is not blank + # file type is already defined -- do nothing + } else { + # if "sourcFilePath" is defined in the form, then we are getting the path directly. + # if the problem number is defined and is 0 + # then we are dealing with some kind of + # header file. The default is 'set_header' which prints properly + # to the screen. + # If the problem number is not zero, we are dealing with a real problem + ###################################### + if ( defined($r->param('sourceFilePath') and $r->param('sourceFilePath') =~/\S/) ) { + $file_type ='source_path_for_problem_file'; + } elsif ( defined($problemNumber) ) { + if ( $problemNumber =~/^\d+$/ and $problemNumber == 0 ) { # if problem number is numeric and zero + $file_type = 'set_header' unless $file_type eq 'set_header' + or $file_type eq 'hardcopy_header'; + } else { + $file_type = 'problem'; + } + + } + } + die "The file_type variable has not been defined or is blank." unless defined($file_type) and $file_type =~/\S/; + $self->{file_type} = $file_type; + + ########################################## + # File type is one of: blank_problem course_info problem set_header hardcopy_header problem_with_source + ########################################## + # + # Determine the path to the file + # + ########################################### + $self->getFilePaths($setName, $problemNumber, $file_type,$TEMPFILESUFFIX); + # result stored in $self->{editFilePath}, and $self->{tempFilePath} + ########################################## + # + # Determine action + # + ########################################### + # Submit button is one of: "add this problem to" , "add this set header to ", "Refresh" "Revert" "Save" "Save As" + $submit_button = $r->param('submit'); + SUBMIT_CASE: { + (! defined($submit_button) ) and do { # fresh problem to edit + $self->{action} = 'fresh_edit'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Add this problem to: ') and do { + $self->{action} = 'add_problem_to_set'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Make this the set header for: ') and do { + $self->{action} = 'add_set_header_to_set'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'View problem') and do { + $self->{action} ='refresh'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Revert') and do { + $self->{action} = 'revert'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Save') and do { + $self->{action} = 'save'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Save as') and do { + $self->{action} = 'save_as'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Add problem to: ') and do { + $self->{action} = 'add_problem_to_set'; + last SUBMIT_CASE; + }; + # else + die "Unrecognized submit command: |$submit_button|"; + + } # END SUBMIT_CASE + + + ########################################### + # Save file + ###################################### + + # The subroutine below writes the necessary files and obtains the appropriate seed. + # and returns + # $self->{problemPath} --- file path for viewing problem in $self->{problemPath} + # $self->{failure} + + + $self->saveFileChanges($setName, $problemNumber, $file_type,$TEMPFILESUFFIX); + + ############################################################################## + # displayMode and problemSeed + # + # Determine the display mode + # If $self->{problemSeed} was obtained within saveFileChanges from the problem_record + # then it can be overridden by the value obtained from the form. + # Insure that $self->{problemSeed} has some non-empty value + # displayMode and problemSeed + # will be needed for viewing the problem via redirect. + # They are also two of the parameters which can be set by the editor + ############################################################################## + + if (defined $r->param('displayMode')) { + $self->{displayMode} = $r->param('displayMode'); + } else { + $self->{displayMode} = $ce->{pg}->{options}->{displayMode}; + } + + # form version of problemSeed overrides version obtained from the the problem_record + # inside saveFileChanges + $self->{problemSeed} = $r->param('problemSeed') if (defined $r->param('problemSeed')); + # Make sure that the problem seed has some value + $self->{problemSeed} = '123456' unless defined $self->{problemSeed} and $self->{problemSeed} =~/\S/; + + ############################################################################## + # Return + # If file saving fails or + # if no redirects are required. No further processing takes place in this subroutine. + # Redirects are required only for the following submit values + # 'Save' + # 'Save as' + # 'Refresh' + # add problem to set + # add set header to set + # + ######################################### + + return if $self->{failure}; + # FIXME: even with an error we still open a new page because of the target specified in the form + + + # Some cases do not need a redirect: revert, fresh_edit + my $action = $self->{action}; + + return unless $action eq 'save' + or $action eq 'refresh' + or $action eq 'save_as' + or $action eq 'add_problem_to_set' + or $action eq 'add_set_header_to_set'; + + + ###################################### + # calculate redirect URL based on file type + ###################################### + my $courseName = $urlpath->arg("courseID"); + my $problemSeed = ($r->param('problemSeed')) ? $r->param('problemSeed') : ''; + my $displayMode = ($r->param('displayMode')) ? $r->param('displayMode') : ''; + + my $viewURL = ''; + + ###################################### + # problem file_type + # redirect to Problem.pm with setID = "Undefined_Set if "Save As" option is chosen + # redirect to Problem.pm with setID = current $setID if "Save" or "Revert" or "Refresh is chosen" + ###################################### + REDIRECT_CASES: { + ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file' or $file_type eq 'blank_problem') and do { + my $sourceFilePath = $self->{problemPath}; + # strip off template directory prefix + $sourceFilePath =~ s|^$ce->{courseDirs}->{templates}/||; + if ($action eq 'save_as') { # redirect to myself my $edit_level = $r->param("edit_level") || 0; $edit_level++; - $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", - courseID => $courseName, setID => 'Undefined_Set', problemID => $problemNumber); - $viewURL = $self->systemLink($problemPage, params=>{sourceFilePath => $sourceFile, edit_level=>$edit_level}); - } else { # other problems redirect to Problem.pm + courseID => $courseName, setID => 'Undefined_Set', problemID => 'Undefined_Problem' + ); + $viewURL = $self->systemLink($problemPage, + params=>{ + sourceFilePath => $sourceFilePath, + edit_level => $edit_level, + file_type => 'source_path_for_problem_file', + status_message => uri_escape($self->{status_message}) + + } + ); + + + } elsif ( $action eq 'add_problem_to_set') { + + my $targetSetName = $r->param('target_set'); + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", + courseID => $courseName, setID => $targetSetName, problemID => scalar($r->db->listGlobalProblems($targetSetName)) + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => "savedFile", + sourceFilePath => $sourceFilePath, + status_message => uri_escape($self->{status_message}) + + } + ); + } elsif ( $action eq 'add_set_header_to_set') { + my $targetSetName = $r->param('target_set'); + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $targetSetName + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + editMode => "savedFile", + status_message => uri_escape($self->{status_message}) + } + ); + } else { # saved problems and refreshed problems redirect to Problem.pm my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", - courseID => $courseName, setID => $setName, problemID => $problemNumber); - $self->{currentSourceFilePath} =~ s|^$ce->{courseDirs}->{templates}/||; + courseID => $courseName, setID => $setName, problemID => $problemNumber + ); $viewURL = $self->systemLink($problemPage, params => { displayMode => $displayMode, problemSeed => $problemSeed, - editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), - sourceFilePath => $self->{currentSourceFilePath}, - success => $self->{sucess}, - failure => $self->{failure}, + editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), + sourceFilePath => $sourceFilePath, + status_message => uri_escape($self->{status_message}) + } ); } - } + last REDIRECT_CASES; + }; + ###################################### + # blank_problem file_type + # redirect to Problem.pm + ###################################### - # set headers redirect to ProblemSet.pm - $self->{file_type} eq 'set_header' and do { - my $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", - courseID => $courseName, setID => $setName); - $viewURL = $self->systemLink($problemSetPage, - params => { - displayMode => $displayMode, - problemSeed => $problemSeed, - editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), - } - ); + $file_type eq 'blank_problem' and do { + return; # no redirect is needed }; - # course info redirects to ProblemSets.pm - $self->{file_type} eq 'course_info' and do { + ###################################### + # set headers file_type + # redirect to ProblemSet.pm + ###################################### + + ($file_type eq 'set_header' or $file_type eq 'hardcopy_header' ) and do { + if ($action eq 'save_as') { # redirect to myself + my $sourceFilePath = $self->{problemPath}; + # strip off template directory prefix + $sourceFilePath =~ s|^$ce->{courseDirs}->{templates}/||; + + my $edit_level = $r->param("edit_level") || 0; + $edit_level++; + + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", + courseID => $courseName, setID => 'Undefined_Set', problemID => 'Undefined_Problem' + ); + $viewURL = $self->systemLink($problemPage, + params=>{ + sourceFilePath => $sourceFilePath, + edit_level => $edit_level, + file_type => 'source_path_for_problem_file', + status_message => uri_escape($self->{status_message}) + } + ); + } elsif ( $action eq 'add_set_header_to_set') { + my $targetSetName = $r->param('target_set'); + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $targetSetName + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + editMode => "savedFile", + status_message => uri_escape($self->{status_message}) + } + ); + } else { + my $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $setName); + $viewURL = $self->systemLink($problemSetPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), + status_message => uri_escape($self->{status_message}) + } + ); + } + last REDIRECT_CASES; + }; + ###################################### + # course_info file type + # redirect to ProblemSets.pm + ###################################### + $file_type eq 'course_info' and do { my $problemSetsPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", courseID => $courseName); $viewURL = $self->systemLink($problemSetsPage, params => { - editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), + editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), + status_message => uri_escape($self->{status_message}) } ); + last REDIRECT_CASES; }; - - # don't redirect on bad save attempts - # FIXME: even with an error we still open a new page because of the target specified in the form - return if $self->{failure}; - - if ($viewURL) { - $self->reply_with_redirect($viewURL); - } else { - die "Invalid file_type ", $self->{file_type}, " specified by saveFileChanges"; - } + # else if no redirect needed -- there must be an error. + die "The file_type $file_type does not have a defined redirect procedure."; + } # End REDIRECT_CASES + + if ($viewURL) { + $self->reply_with_redirect($viewURL); + } else { + die "Invalid file_type $file_type specified by saveFileChanges"; } } + sub initialize { my ($self) = @_; my $r = $self->r; my $authz = $r->authz; my $user = $r->param('user'); - my $setName = $r->urlpath->arg("setID"); - my $problemNumber = $r->urlpath->arg("problemID"); - # Check permissions return unless ($authz->hasPermissions($user, "access_instructor_tools")); return unless ($authz->hasPermissions($user, "modify_problem_sets")); - - # if we got to initialize(), then saveFileChanges was not called in pre_header_initialize(). - # therefore we call it here unless there has been an error already: - $self->saveFileChanges($setName, $problemNumber) unless $self->{failure}; - # this seems like a good place to deal with the add to set - my $submit_button = $r->param('submit') || ''; - if($submit_button eq 'Add this problem to: ') { - my $ce = $r->ce; - my $sourcePath = $self->{problemPath}; - $sourcePath =~ s|^$ce->{courseDirs}->{templates}/||; - $self->addProblemToSet(setName => $r->param('target_set'), - sourceFile => $sourcePath); - $self->addgoodmessage("Added $sourcePath to ". $r->param('target_set') ); - } + + my $tempFilePath = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file) + my $inputFilePath = $self->{inputFilePath}; # path to the file for input, (might be a .tmp file) + my $protected_file = (not -w $inputFilePath ) and -e $inputFilePath; #FIXME -- let's try to insure that the input file always exists, even for revert. + $self->addbadmessage("Changes in this file have not yet been permanently saved.") if -r $tempFilePath; + $self->addbadmessage("This file |$inputFilePath| is protected! To edit this text you must first use 'Save As' to save it to another file.") if $protected_file; + } sub path { @@ -197,11 +508,6 @@ "$problemNumber", $r->location."/$courseName/$setName/$problemNumber", "Editor", "" ); -# do { -# unshift @path, $urlpath->name, $r->location . $urlpath->path; -# } while ($urlpath = $urlpath->parent); -# -# $path[$#path] = ""; # we don't want the last path element to be a link #print "\n<!-- BEGIN " . __PACKAGE__ . "::path -->\n"; print $self->pathMacro($args, @path); @@ -228,28 +534,56 @@ my $authz = $r->authz; my $user = $r->param('user'); my $make_local_copy = $r->param('make_local_copy'); - + # Check permissions return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") - unless $authz->hasPermissions($r->param("user"), "access_instructor_tools"); + unless $authz->hasPermissions($user, "access_instructor_tools"); return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify problems.") - unless $authz->hasPermissions($r->param("user"), "modify_student_data"); + unless $authz->hasPermissions($user, "modify_student_data"); # Gathering info - my $editFilePath = $self->{problemPath}; # path to the permanent file to be edited - my $inputFilePath = $self->{inputFilePath}; # path to the file currently being worked with (might be a .tmp file) - - my $header = CGI::i("Editing problem: $inputFilePath"); - + my $editFilePath = $self->{editFilePath}; # path to the permanent file to be edited + my $tempFilePath = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file) + my $inputFilePath = $self->{inputFilePath}; # path to the file for input, (might be a .tmp file) + my $setName = $r->urlpath->arg("setID") ; + my $problemNumber = $r->urlpath->arg("problemID") ; + $setName = defined($setName) ? $setName : ''; # we need this instead of using the || construction + # to keep set 0 from being set to the + # empty string. + $problemNumber = defined($problemNumber) ? $problemNumber : ''; + + ######################################################################### # Find the text for the problem, either in the tmp file, if it exists # or in the original file in the template directory + # or in the problem contents gathered in the initialization phase. ######################################################################### my $problemContents = ${$self->{r_problemContents}}; + unless ( $problemContents =~/\S/) { # non-empty contents + if (-r $tempFilePath and not -d $tempFilePath) { + eval { $problemContents = WeBWorK::Utils::readFile($tempFilePath) }; + $problemContents = $@ if $@; + $inputFilePath = $tempFilePath; + } elsif (-r $editFilePath and not -d $editFilePath) { + eval { $problemContents = WeBWorK::Utils::readFile($editFilePath) }; + $problemContents = $@ if $@; + $inputFilePath = $editFilePath; + } else { # file not existing is not an error + #warn "No file exists"; + $problemContents = ''; + } + } else { + #warn "obtaining input from r_problemContents"; + } + + my $protected_file = not -w $inputFilePath; + my $header = CGI::i("Editing problem".CGI::b("set $setName/ problem $problemNumber</emphasis>").CGI::br()." in file $inputFilePath"); + $header = ($inputFilePath =~ /$TEMPFILESUFFIX/) ? CGI::div({class=>'temporaryFile'},$header) : $header; # use colors if temporary file + ######################################################################### # Format the page ######################################################################### @@ -257,13 +591,14 @@ # Define parameters for textarea # FIXME # Should the seed be set from some particular user instance?? - my $rows = 20; - my $columns = 80; - my $mode_list = $ce->{pg}->{displayModes}; - my $displayMode = $self->{displayMode}; - my $problemSeed = $self->{problemSeed}; - my $uri = $r->uri; - my $edit_level = $r->param('edit_level') || 0; + my $rows = 20; + my $columns = 80; + my $mode_list = $ce->{pg}->{displayModes}; + my $displayMode = $self->{displayMode}; + my $problemSeed = $self->{problemSeed}; + my $uri = $r->uri; + my $edit_level = $r->param('edit_level') || 0; + my $file_type = $self->{file_type}; my $force_field = defined($r->param('sourceFilePath')) ? CGI::hidden(-name=>'sourceFilePath', @@ -274,25 +609,30 @@ $allSetNames[$j] =~ s|^set||; $allSetNames[$j] =~ s|\.def||; } - # next, the content of our "add to stuff", which only appears if we are a problem - my $add_to_stuff = ''; - if($self->{file_type} eq 'problem') { - # second form which does not open a new window - $add_to_stuff = CGI::start_form(-method=>"POST", -action=>"$uri"). + my $target = "problem$edit_level"; + # Prepare Preview button + my $view_problem_form = CGI::start_form({method=>"POST", name=>"editor", action=>"$uri", target=>$target, enctype=>"application/x-www-form-urlencoded"}). $self->hidden_authen_fields. $force_field. - CGI::hidden(-name=>'file_type',-default=>$self->{file_type}). - CGI::hidden(-name=>'problemSeed',-default=>$problemSeed). - CGI::hidden(-name=>'displayMode',-default=>$displayMode). - CGI::hidden(-name=>'problemContents',-default=>$problemContents). - CGI::p( - CGI::submit(-value=>'Add this problem to: ',-name=>'submit'), - CGI::popup_menu(-name=>'target_set',-values=>\@allSetNames) - ). + CGI::hidden(-name=>'file_type',-default=>$self->{file_type}). + CGI::hidden(-name=>'problemSeed',-default=>$problemSeed). + CGI::hidden(-name=>'displayMode',-default=>$displayMode). + CGI::hidden(-name=>'problemContents',-default=>$problemContents). + CGI::submit(-value=>'View problem',-name=>'submit'). CGI::end_form(); + # Prepare add to set buttons + my $add_files_to_set_buttons = ''; + if ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file' ) { + $add_files_to_set_buttons .= CGI::submit(-value=>'Add problem to: ',-name=>'submit' ) ; + } + if ($file_type eq 'set_header' # set header or the problem number is not a regular positive number + or ( $file_type =~ /problem/ and ($problemNumber =~ /\D|^0$|^$/ )) ){ + $add_files_to_set_buttons .=CGI::submit(-value=>'Make this the set header for: ',-name=>'submit' ); } - - my $target = "problem$edit_level"; + # Add pop-up menu for the target set if either of these buttons has been revealed. + $add_files_to_set_buttons .= CGI::popup_menu(-name=>'target_set',-values=>\@allSetNames) if $add_files_to_set_buttons; + + return CGI::p($header), CGI::start_form({method=>"POST", name=>"editor", action=>"$uri", target=>$target, enctype=>"application/x-www-form-urlencoded"}), $self->hidden_authen_fields, @@ -314,14 +654,18 @@ ), ), CGI::p( - CGI::submit(-value=>'Refresh',-name=>'submit'), - $make_local_copy ? CGI::submit(-value=>'Save',-name=>'submit', -disabled=>1) : CGI::submit(-value=>'Save',-name=>'submit'), + $add_files_to_set_buttons, + CGI::br(), + CGI::submit(-value=>'View problem',-name=>'submit'), + $protected_file ? CGI::submit(-value=>'Save',-name=>'submit', -disabled=>1) : CGI::submit(-value=>'Save',-name=>'submit'), CGI::submit(-value=>'Revert', -name=>'submit'), CGI::submit(-value=>'Save as',-name=>'submit'), CGI::textfield(-name=>'save_to_new_file', -size=>40, -value=>""), + ), - CGI::end_form(), - $add_to_stuff; + CGI::end_form(); + + } ################################################################################ @@ -334,228 +678,316 @@ # # it actually does a lot more than save changes to the file being edited, and # sometimes less. - -sub saveFileChanges { - my ($self, $setName, $problemNumber) = @_; +sub getFilePaths { + my ($self, $setName, $problemNumber, $file_type, $TEMPFILESUFFIX) = @_; my $r = $self->r; my $ce = $r->ce; my $db = $r->db; my $urlpath = $r->urlpath; - my $courseName = $urlpath->arg("courseID"); my $user = $r->param('user'); my $effectiveUserName = $r->param('effectiveUser'); - + $setName = '' unless defined $setName; $problemNumber = '' unless defined $problemNumber; - - ##### Determine path to the file to be edited. ##### - + die 'Internal error to PGProblemEditor -- file type is not defined' unless defined $file_type; + + ########################################################## + # Determine path to the input file to be edited. + # set EditFilePath to this value + # + # There are potentially four files in play + # The permanent path of the input file == $editFilePath == $self->{problemPath} + # A temporary path to the input file == $tempFilePath== "$editFilePath.$TEMPFILESUFFIX"== $self->{problemPath} + ########################################################## + # Relevant parameters + # $r->param("displayMode") + # $r->param('problemSeed') + # $r->param('submit') + # $r->param('make_local_copy') + # $r->param('sourceFilePath') + # $r->param('problemContents') + # $r->param('save_to_new_file') + ########################################################################## + # Define the following variables + # path to regular file -- $self->{problemPath} = $editFilePath; + # path to file being read (temporary or permanent) + # --- $self->{problemPath} = $problemPath; + # contents of the file being read --- $problemContents + # $self->{r_problemContents} = \$problemContents; + # $self->{TEMPFILESUFFIX} = $TEMPFILESUFFIX; + ########################################################################### + my $editFilePath = $ce->{courseDirs}->{templates}; - my $problem_record = undef; - my $file_type = $r->param("file_type") || ''; - - if ($file_type eq 'course_info') { - # we are editing the course_info file - $self->{file_type} = 'course_info'; + ########################################################################## + # Determine path to regular file, place it in $editFilePath + # problemSeed is defined for the file_type = 'problem' and 'source_path_to_problem' + ########################################################################## + CASE: + { + ($file_type eq 'course_info') and do { + # we are editing the course_info file + # value of courseFiles::course_info is relative to templates directory + $editFilePath .= '/' . $ce->{courseFiles}->{course_info}; + last CASE; + }; - # value of courseFiles::course_info is relative to templates directory - $editFilePath .= '/' . $ce->{courseFiles}->{course_info}; - } else { - # we are editing a problem file or a set header file + ($file_type eq 'blank_problem') and do { + $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{blankProblem}; + $self->addbadmessage("$editFilePath is blank problem template file and cannot be edited directly."); + $self->addbadmessage("Any changes you make will have to be saved as another file."); + last CASE; + }; - # FIXME there is a discrepancy in the way that the problems are found. - # FIXME more error checking is needed in case the problem doesn't exist. - # (i wonder what the above comments mean... -sam) - - if (defined $problemNumber) { - if ($problemNumber == 0) { - # we are editing a header file - if ($file_type eq 'set_header' or $file_type eq 'hardcopy_header') { - $self->{file_type} = $file_type - } else { - $self->{file_type} = 'set_header'; - } - - # first try getting the merged set for the effective user - my $set_record = $db->getMergedSet($effectiveUserName, $setName); # checked - - # if that doesn't work (the set is not yet assigned), get the global record - $set_record = $db->getGlobalSet($setName); # checked - - # bail if no set is found - die "Cannot find a set record for set $setName" unless defined($set_record); - - my $header_file = ""; - $header_file = $set_record->{$self->{file_type}}; - if ($header_file && $header_file ne "") { + ($file_type eq 'set_header' or $file_type eq 'hardcopy_header') and do { + # first try getting the merged set for the effective user + my $set_record = $db->getMergedSet($effectiveUserName, $setName); # checked + # if that doesn't work (the set is not yet assigned), get the global record + $set_record = $db->getGlobalSet($setName); # checked + # bail if no set is found + die "Cannot find a set record for set $setName" unless defined($set_record); + + my $header_file = ""; + $header_file = $set_record->{$file_type}; + if ($header_file && $header_file ne "") { $editFilePath .= '/' . $header_file; - } else { + } else { # if the set record doesn't specify the filename - # then the set uses the deafult from snippets + # then the set uses the default from snippets # so we'll load that file, but change where it will be saved # to and grey out the "Save" button - if ($r->param('make_local_copy')) { - $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{setHeader} if $self->{file_type} eq 'set_header'; - $editFilePath = $ce->{webworkFiles}->{hardcopySnippets}->{setHeader} if $self->{file_type} eq 'hardcopy_header'; + # FIXME why does the make_local_copy variable need to be checked? + # Isn't it automatic that a local copy has to be made? + #if ($r->param('make_local_copy')) { + $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{setHeader} if $file_type eq 'set_header'; + $editFilePath = $ce->{webworkFiles}->{hardcopySnippets}->{setHeader} if $file_type eq 'hardcopy_header'; $self->addbadmessage("$editFilePath is the default header file and cannot be edited directly."); $self->addbadmessage("Any changes you make will have to be saved as another file."); - } - } - - - } else { - # we are editing a "real" problem - $self->{file_type} = 'problem'; - - # first try getting the merged problem for the effective user - $problem_record = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked - - # if that doesn't work (the problem is not yet assigned), get the global record - $problem_record = $db->getGlobalProblem($setName, $problemNumber) unless defined($problem_record); # checked - - - if(not defined($problem_record)) { - my $forcedSourceFile = $r->param('sourceFilePath'); - # bail if no problem is found and we aren't faking it - die "Cannot find a problem record for set $setName / problem $problemNumber" unless defined($forcedSourceFile); - $problem_record = fake_problem($db); - $problem_record->problem_id($problemNumber); - $problem_record->source_file($forcedSourceFile); - } - - - $editFilePath .= '/' . $problem_record->source_file; + #} } - } - } + last CASE; + }; #end 'set_header, hardcopy_header' case + + ($file_type eq 'problem') and do { + + # first try getting the merged problem for the effective user + my $problem_record = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked + + # if that doesn't work (the problem is not yet assigned), get the global record + $problem_record = $db->getGlobalProblem($setName, $problemNumber) unless defined($problem_record); # checked + # bail if no source path for the problem is found ; + die "Cannot find a problem record for set $setName / problem $problemNumber" unless defined($problem_record); + $editFilePath .= '/' . $problem_record->source_file; + # define the problem seed for later use + $self->{problemSeed}= $problem_record->problem_seed if defined($problem_record) and $problem_record->can('problem_seed') ; + last CASE; + }; # end 'problem' case + + ($file_type eq 'source_path_for_problem_file') and do { + my $forcedSourceFile = $r->param('sourceFilePath'); + # bail if no source path for the problem is found ; + die "Cannot find a file path to save to" unless( defined($forcedSourceFile) and ($forcedSourceFile =~ /\S/) ); + $self->{problemSeed} = 1234; + $editFilePath .= '/' . $forcedSourceFile; + last CASE; + }; # end 'source_path_for_problem_file' case + } # end CASE: statement + # if a set record or problem record contains an empty blank for a header or problem source_file # we could find ourselves trying to edit /blah/templates/.toenail.tmp or something similar # which is almost undoubtedly NOT desirable if (-d $editFilePath) { - $self->{failure} = "The file $editFilePath is a directory!"; - $self->addbadmessage("The file $editFilePath is a directory!"); - } - - - my $editFileSuffix = $user.'.tmp'; - my $submit_button = $r->param('submit'); - - ############################################################################## - # Determine the display mode - # try to get problem seed from the input parameter, or from the problem record - # This will be needed for viewing the problem via redirect. - # They are also two of the parameters which can be set by the editor - ############################################################################## - - my $displayMode; - if (defined $r->param('displayMode')) { - $displayMode = $r->param('displayMode'); - } else { - $displayMode = $ce->{pg}->{options}->{displayMode}; + my $msg = "The file $editFilePath is a directory!"; + $self->{failure} = 1; + $self->addbadmessage($msg); } - - my $problemSeed; - if (defined $r->param('problemSeed')) { - $problemSeed = $r->param('problemSeed'); - } elsif (defined($problem_record) and $problem_record->can('problem_seed')) { - $problemSeed = $problem_record->problem_seed; + if (-e $editFilePath and not -r $editFilePath) { #it's ok if the file doesn't exist, perhaps we're going to create it + # with save as + my $msg = "The file $editFilePath cannot be read!"; + $self->{failure} = 1; + $self->addbadmessage($msg); } + ################################################# + # The path to the permanent file is now verified and stored in $editFilePath + # Whew!!! + ################################################# + + my $tempFilePath = "$editFilePath.$TEMPFILESUFFIX"; + $self->{editFilePath} = $editFilePath; + $self->{tempFilePath} = $tempFilePath; + $self->{inputFilePath} = (-r "$editFilePath.$TEMPFILESUFFIX") ? $tempFilePath : $editFilePath; + +} +sub saveFileChanges { + my ($self, $setName, $problemNumber, $file_type, $TEMPFILESUFFIX) = @_; + my $r = $self->r; + my $ce = $r->ce; + my $db = $r->db; + my $urlpath = $r->urlpath; - # make absolutely sure that the problem seed is defined, if it hasn't been. - $problemSeed = '123456' unless defined $problemSeed and $problemSeed =~/\S/; - + my $courseName = $urlpath->arg("courseID"); + my $user = $r->param('user'); + my $effectiveUserName = $r->param('effectiveUser'); + + $setName = '' unless defined $setName; + $problemNumber = '' unless defined $problemNumber; + $file_type = '' unless defined $file_type; + + my $action = $self->{action}; + my $editFilePath = $self->{editFilePath}; + my $tempFilePath = $self->{tempFilePath}; ############################################################################## # read and update the targetFile and targetFile.tmp files in the directory # if a .tmp file already exists use that, unless the revert button has been pressed. # These .tmp files are # removed when the file is finally saved. + # Place the path of the file to be read in $problemPath. ############################################################################## + my $problemContents = ''; - my $currentSourceFilePath = ''; + my $outputFilePath = undef; # this is actually the output file for this subroutine + # it is then read in as source in the body of this module + my $do_not_save = 0; # flag to prevent saving of file my $editErrors = ''; - - my $inputFilePath; - if (-r "$editFilePath.$editFileSuffix") { - $inputFilePath = "$editFilePath.$editFileSuffix"; - } else { - $inputFilePath = $editFilePath; - } - - $inputFilePath = $editFilePath if defined($submit_button) and $submit_button eq 'Revert'; - - ##### handle button clicks ##### - - if (not defined $submit_button or $submit_button eq 'Revert' ) { - # this is a fresh editing job - # copy the pg file to a new file with the same name with .tmp added - # store this name in the $self->currentSourceFilePath for use in body - - # try to read file - if(-e $inputFilePath and not -d $inputFilePath) { - eval { $problemContents = WeBWorK::Utils::readFile($inputFilePath) }; - $problemContents = $@ if $@; - } else { # file not existing is not an error - $problemContents = ''; - } + + ########################################################################## + # For each of the actions define the following variables: + # + # path to permanent file -- $self->{problemPath} = $editFilePath; + # path to file being read (temporary or permanent) + # --- $self->{problemPath} = $problemPath; + # contents of the file being read --- $problemContents + # $self->{r_problemContents} = \$problemContents; + # + ################################# + # handle button clicks ##### + # Read contents of file + ################################# + ACTION_CASES: { + ($action eq 'fresh_edit') and do { + # this is a fresh editing job + # the original file will be read in the body + last ACTION_CASES; + }; + + ($action eq 'revert') and do { + # this is also fresh editing job + $outputFilePath = undef; + $self->addgoodmessage("Reverting to original file $editFilePath"); + $self->{problemPath} = $editFilePath; + last ACTION_CASES; + }; - $currentSourceFilePath = "$editFilePath.$editFileSuffix"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } elsif ($submit_button eq 'Refresh') { - # grab the problemContents from the form in order to save it to the tmp file - # store tmp file name in the $self->currentSourceFilePath for use in body - $problemContents = $r->param('problemContents'); - - $currentSourceFilePath = "$editFilePath.$editFileSuffix"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } elsif ($submit_button eq 'Save') { - # grab the problemContents from the form in order to save it to the permanent file - # later we will unlink (delete) the temporary file - # store permanent file name in the $self->currentSourceFilePath for use in body - $problemContents = $r->param('problemContents'); - - $currentSourceFilePath = "$editFilePath"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } elsif ($submit_button eq 'Save as') { - # grab the problemContents from the form in order to save it to a new permanent file - # later we will unlink (delete) the current temporary file - # store new permanent file name in the $self->currentSourceFilePath for use in body - $problemContents = $r->param('problemContents'); - # Save the user in case they forgot to end the file with .pg - if($self->{file_type} eq 'problem') { - my $save_to_new_file = $r->param('save_to_new_file'); - $save_to_new_file =~ s/\.pg$//; # remove it if it is there - $save_to_new_file .= '.pg'; # put it there - $r->param('save_to_new_file', $save_to_new_file); - } - $currentSourceFilePath = $ce->{courseDirs}->{templates} . '/' . $r->param('save_to_new_file'); - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $currentSourceFilePath; - } elsif ($submit_button eq 'Add this problem to: ') { - $problemContents = $r->param('problemContents'); - $currentSourceFilePath = "$editFilePath.$editFileSuffix"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } else { - die "Unrecognized submit command: $submit_button"; - } + ($action eq 'refresh') and do { + # grab the problemContents from the form in order to save it to the tmp file + # store tmp file name in the $self->problemPath for use in body + + $problemContents = $r->param('problemContents'); + $outputFilePath = "$editFilePath.$TEMPFILESUFFIX"; + $self->{problemPath} = $outputFilePath; + last ACTION_CASES; + }; - # Handle the problem of line endings. Make sure that all of the line endings. Convert \r\n to \n - $problemContents =~ s/\r\n/\n/g; - $problemContents =~ s/\r/\n/g; - - # FIXME convert all double returns to paragraphs for .txt files - # instead of doing this here, it should be done n the PLACE WHERE THE FILE IS DISPLAYED!!! - #if ($self->{file_type} eq 'course_info' ) { - # $problemContents =~ s/\n\n/\n<p>\n/g; - #} + ($action eq 'save') and do { + # grab the problemContents from the form in order to save it to the permanent file + # later we will unlink (delete) the temporary file + # store permanent file name in the $self->problemPath for use in body + $problemContents = $r->param('problemContents'); + $outputFilePath = "$editFilePath"; + $self->{problemPath} = $outputFilePath; + #$self->addgoodmessage("Saving to file $outputFilePath"); + last ACTION_CASES; + }; + + ($action eq 'save_as') and do { + my $new_file_name =$r->param('save_to_new_file') || ''; + ################################################# + #bail unless this new file name has been defined + ################################################# + if ( $new_file_name !~ /\S/) { # need a non-blank file name + # setting $self->{failure} stops saving and any redirects + $do_not_save = 1; + warn "new file name is $new_file_name"; + $self->addbadmessage(CGI::p("Please specify a file to save to.")); + last ACTION_CASES; #stop processing + } + ################################################# + # grab the problemContents from the form in order to save it to a new permanent file + # later we will unlink (delete) the current temporary file + # store new permanent file name in the $self->problemPath for use in body + ################################################# + $problemContents = $r->param('problemContents'); + + ################################################# + # Rescue the user in case they forgot to end the file name with .pg + ################################################# + if($self->{file_type} eq 'problem' + or $self->{file_type} eq 'blank_problem' + or $self->{file_type} eq 'set_header') { + $new_file_name =~ s/\.pg$//; # remove it if it is there + $new_file_name .= '.pg'; # put it there + + } + + ################################################# + # check to prevent overwrites: + ################################################# + $outputFilePath = $ce->{courseDirs}->{templates} . '/' . + $new_file_name; + + if (defined $outputFilePath and -e $outputFilePath) { + # setting $do_not_save stops saving and any redirects + $do_not_save = 1; + $self->addbadmessage(CGI::p("File $outputFilePath exists. File not saved.")); + } else { + #$self->addgoodmessage("Saving to file $outputFilePath."); + } + $self->{problemPath} = $outputFilePath; + last ACTION_CASES; + }; + ($action eq 'add_problem_to_set') and do { + my $sourceFile = $editFilePath; + my $targetSetName = $r->param('target_set'); + my $freeProblemID = WeBWorK::Utils::max($db->listGlobalProblems($setName)) + 1; + $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; + my $problemRecord = $self->addProblemToSet( + setName => $targetSetName, + sourceFile => $sourceFile, + problemID => $freeProblemID + ); + $self->assignProblemToAllSetUsers($problemRecord); + $self->addgoodmessage("Added $sourceFile to ". $targetSetName. " as problem $freeProblemID") ; + $outputFilePath = undef; # don't save any files + $self->{problemPath} = $editFilePath; + + }; + ($action eq 'add_set_header_to_set') and do { + my $sourceFile = $editFilePath; + my $targetSetName = $r->param('target_set'); + $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; + my $setRecord = $db->getGlobalSet($targetSetName); + $setRecord->set_header($sourceFile); + if( $db->putGlobalSet($setRecord) ) { + $self->addgoodmessage("Added $sourceFile to ". $targetSetName. " as new set header ") ; + } else { + $do_not_save = 1 ; + $self->addbadmessage("Unable to make $sourceFile the set header for $targetSetName"); + } + # change file type to set_header if it not already so + $self->{file_type} = 'set_header'; + $outputFilePath = undef; # don't save any files + $self->{problemPath} = $editFilePath; + + }; + last ACTION_CASES; + + die "Unrecognized action command: $action"; + }; # end ACTION_CASES + ############################################################################## @@ -564,36 +996,39 @@ # Make sure that the warning is being transmitted properly. ############################################################################## - # FIXME set a local state rather continue to call on the submit button. - if (defined $submit_button and $submit_button eq 'Save as' and $r->param('save_to_new_file') !~ /\w/) { - # setting $self->{failure} stops any future redirects - $self->{failure} = "Please specify a file to save to."; - $self->addbadmessage(CGI::p("Please specify a file to save to.")); - } elsif (defined $submit_button and $submit_button eq 'Save as' and defined $currentSourceFilePath and -e $currentSourceFilePath) { - # setting $self->{failure} stops any future redirects - $self->{failure} = "File $currentSourceFilePath exists. File not saved."; - $self->addbadmessage(CGI::p("File $currentSourceFilePath exists. File not saved.")); - } else { + my $writeFileErrors; + if ( defined($outputFilePath) and $outputFilePath =~/\S/ and ! $do_not_save ) { # save file + # Handle the problem of line endings. + # Make sure that all of the line endings are of unix type. + # Convert \r\n to \n + $problemContents =~ s/\r\n/\n/g; + $problemContents =~ s/\r/\n/g; + # make sure any missing directories are created - $currentSourceFilePath = WeBWorK::Utils::surePathToFile($ce->{courseDirs}->{templates},$currentSourceFilePath); + $outputFilePath = WeBWorK::Utils::surePathToFile($ce->{courseDirs}->{templates}, + $outputFilePath); + eval { local *OUTPUTFILE; - open OUTPUTFILE, ">", $currentSourceFilePath - or die "Failed to open $currentSourceFilePath"; + open OUTPUTFILE, ">", $outputFilePath + or die "Failed to open $outputFilePath"; print OUTPUTFILE $problemContents; close OUTPUTFILE; }; # any errors are caught in the next block - } + + $writeFileErrors = $@ if $@; + } ########################################################### # Catch errors in saving files, clean up temp files ########################################################### - - my $openTempFileErrors = $@ if $@; - - if ($openTempFileErrors) { - $currentSourceFilePath =~ m|^(/.*?/)[^/]+$|; + $self->{failure} = $do_not_save; # don't do redirects if the file was not saved. + # don't unlink files or send success messages + + if ($writeFileErrors) { + # get the current directory from the outputFilePath + $outputFilePath =~ m|^(/.*?/)[^/]+$|; my $currentDirectory = $1; my $errorMessage; @@ -602,28 +1037,37 @@ $errorMessage = "Write permissions have not been enabled in the templates directory. No changes can be made."; } elsif ( not -w $currentDirectory ) { $errorMessage = "Write permissions have not been enabled in $currentDirectory. Changes must be saved to a different directory for viewing."; - } elsif ( -e $currentSourceFilePath and not -w $currentSourceFilePath ) { - $errorMessage = "Write permissions have not been enabled for $currentSourceFilePath. Changes must be saved to another file for viewing."; + } elsif ( -e $outputFilePath and not -w $outputFilePath ) { + $errorMessage = "Write permissions have not been enabled for $outputFilePath. Changes must be saved to another file for viewing."; } else { - $errorMessage = "Unable to write to $currentSourceFilePath: $openTempFileErrors"; + $errorMessage = "Unable to write to $outputFilePath: $writeFileErrors"; } - $self->{failure} = $errorMessage; + $self->{failure} = 1; $self->addbadmessage(CGI::p($errorMessage)); - } else { - $self->{success} = "Problem saved to: $currentSourceFilePath"; + } + unless( $writeFileErrors or $do_not_save) { # everything worked! unlink and announce success! # unlink the temporary file if there are no errors and the save button has been pushed - unlink("$editFilePath.$editFileSuffix") - if defined $submit_button and ($submit_button eq 'Save' or $submit_button eq 'Save as'); + if ($action eq 'save' or $action eq 'save_as' or $action eq 'revert') { + unlink($self->{tempFilePath}) ; + } + if ( defined($outputFilePath) and ! $self->{failure} ) { + my $msg = "Saved to file: $outputFilePath"; + $self->addgoodmessage($msg); + } + } # return values for use in the body subroutine - $self->{inputFilePath} = $inputFilePath; - $self->{displayMode} = $displayMode; - $self->{problemSeed} = $problemSeed; + # The path to the current permanent file being edited: + # $self->{problemPath} = $editFilePath; + # The path to the current temporary file (if any). If no temporary file this the same + # as the permanent file path: + # $self->{outputFilePath} = $outputFilePath; + # + $self->{r_problemContents} = \$problemContents; - $self->{editFileSuffix} = $editFileSuffix; -} +} # end saveFileChanges 1; |
From: dpvc v. a. <we...@ma...> - 2005-02-15 02:37:48
|
Log Message: ----------- Fixed an error with Matrix() that could cause it to loop infinitely when bad data is passed to it. Also, allow Matrix(), Point(), Vector(), and Real() to accept string values that are evaluated to produce the value returned. (Sorry, accidentally committed with a blank message.) Modified Files: -------------- pg/lib: Value.pm pg/lib/Value: Matrix.pm Real.pm Point.pm Vector.pm Revision Data ------------- Index: Value.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value.pm,v retrieving revision 1.30 retrieving revision 1.31 diff -Llib/Value.pm -Llib/Value.pm -u -r1.30 -r1.31 --- lib/Value.pm +++ lib/Value.pm @@ -274,6 +274,21 @@ } # +# Parse a string and return the resulting formula if it of the right +# type. If the formula is constant, return the value rather than the +# formula. +# +sub parseFormula { + my $self = shift; my $class = ref($self) ? $self->type : class($self); + $class = "Number" if $class eq 'Real' || $class eq "Complex"; + my $f = (scalar(@_) > 1) ? join(',',@_) : shift; + $f = Value::Formula->new($f); $f = $f->eval() if $f->isConstant; + Value::Error("Can't convert ".Value::showClass($f)." to ".Value::showClass($self)) + if ($f->type ne $class); + return $f; +} + +# # A shortcut for new() that creates an instance of the object, # but doesn't do the error checking. We assume the data are already # known to be good. Index: Vector.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Vector.pm,v retrieving revision 1.12 retrieving revision 1.13 diff -Llib/Value/Vector.pm -Llib/Value/Vector.pm -u -r1.12 -r1.13 --- lib/Value/Vector.pm +++ lib/Value/Vector.pm @@ -29,6 +29,7 @@ # a list of numbers, or an reference to an array of numbers # a point or vector object (demote a vector) # a matrix if it is n x 1 or 1 x n +# a string that parses to a vector # sub new { my $self = shift; my $class = ref($self) || $self; Index: Point.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Point.pm,v retrieving revision 1.10 retrieving revision 1.11 diff -Llib/Value/Point.pm -Llib/Value/Point.pm -u -r1.10 -r1.11 --- lib/Value/Point.pm +++ lib/Value/Point.pm @@ -29,6 +29,7 @@ # a list of numbers, or an reference to an array of numbers # a point or vector object (demote a vector) # a matrix if it is n x 1 or 1 x n +# a string that evaluates to a point # sub new { my $self = shift; my $class = ref($self) || $self; Index: Real.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Real.pm,v retrieving revision 1.11 retrieving revision 1.12 diff -Llib/Value/Real.pm -Llib/Value/Real.pm -u -r1.11 -r1.12 --- lib/Value/Real.pm +++ lib/Value/Real.pm @@ -33,7 +33,7 @@ # # Check that the input is a real number or a formula -# Make a formula if either part is a formula +# or a string that evaluates to a number # sub new { my $self = shift; my $class = ref($self) || $self; Index: Matrix.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Matrix.pm,v retrieving revision 1.14 retrieving revision 1.15 diff -Llib/Value/Matrix.pm -Llib/Value/Matrix.pm -u -r1.14 -r1.15 --- lib/Value/Matrix.pm +++ lib/Value/Matrix.pm @@ -29,7 +29,7 @@ # Convert a value to a matrix. The value can be: # a list of numbers or list of (nested) references to arrays of numbers # a point, vector or matrix object, a matrix-valued formula, or a string -# containing a matrix expression in the current context. +# that evaluates to a matrix # sub new { my $self = shift; my $class = ref($self) || $self; |
From: dpvc v. a. <we...@ma...> - 2005-02-15 02:33:02
|
Log Message: ----------- Modified Files: -------------- pg/lib/Value: Matrix.pm Point.pm Real.pm Vector.pm Revision Data ------------- Index: Vector.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Vector.pm,v retrieving revision 1.11 retrieving revision 1.12 diff -Llib/Value/Vector.pm -Llib/Value/Vector.pm -u -r1.11 -r1.12 --- lib/Value/Vector.pm +++ lib/Value/Vector.pm @@ -39,6 +39,7 @@ elsif ($pclass eq 'Matrix' && scalar(@d) == 1) {$p = [$p->value]} elsif ($pclass eq 'Matrix' && scalar(@d) == 2 && $d[0] == 1) {$p = ($p->value)[0]} elsif ($pclass eq 'Matrix' && scalar(@d) == 2 && $d[1] == 1) {$p = ($p->transpose->value)[0]} + elsif (defined($p) && ref($p) eq "") {return $self->parseFormula($p)} else { $p = [$p] if (defined($p) && ref($p) ne 'ARRAY'); Value::Error("Vectors must have at least one coordinate") unless defined($p) && scalar(@{$p}) > 0; Index: Real.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Real.pm,v retrieving revision 1.10 retrieving revision 1.11 diff -Llib/Value/Real.pm -Llib/Value/Real.pm -u -r1.10 -r1.11 --- lib/Value/Real.pm +++ lib/Value/Real.pm @@ -42,8 +42,7 @@ $x = [$x] unless ref($x) eq 'ARRAY'; Value::Error("Can't convert ARRAY of length ".scalar(@{$x})." to a Real Number") unless (scalar(@{$x}) == 1); - Value::Error("Real Number can't be ".Value::showClass($x->[0])) - unless (Value::isRealNumber($x->[0])); + return $self->parseFormula(@{$x}) unless Value::isRealNumber($x->[0]); return $self->formula($x->[0]) if Value::isFormula($x->[0]); bless {data => $x}, $class; } Index: Point.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Point.pm,v retrieving revision 1.9 retrieving revision 1.10 diff -Llib/Value/Point.pm -Llib/Value/Point.pm -u -r1.9 -r1.10 --- lib/Value/Point.pm +++ lib/Value/Point.pm @@ -39,6 +39,7 @@ elsif ($pclass eq 'Matrix' && scalar(@d) == 1) {$p = [$p->value]} elsif ($pclass eq 'Matrix' && scalar(@d) == 2 && $d[0] == 1) {$p = ($p->value)[0]} elsif ($pclass eq 'Matrix' && scalar(@d) == 2 && $d[1] == 1) {$p = ($p->transpose->value)[0]} + elsif (defined($p) && ref($p) eq "") {return $self->parseFormula($p)} else { $p = [$p] if (defined($p) && ref($p) ne 'ARRAY'); Value::Error("Points must have at least one coordinate") Index: Matrix.pm =================================================================== RCS file: /webwork/cvs/system/pg/lib/Value/Matrix.pm,v retrieving revision 1.13 retrieving revision 1.14 diff -Llib/Value/Matrix.pm -Llib/Value/Matrix.pm -u -r1.13 -r1.14 --- lib/Value/Matrix.pm +++ lib/Value/Matrix.pm @@ -28,21 +28,24 @@ # # Convert a value to a matrix. The value can be: # a list of numbers or list of (nested) references to arrays of numbers -# a point, vector or matrix object +# a point, vector or matrix object, a matrix-valued formula, or a string +# containing a matrix expression in the current context. # sub new { my $self = shift; my $class = ref($self) || $self; my $M = shift; return bless {data => $M->data}, $class if (Value::class($M) =~ m/Point|Vector|Matrix/ && scalar(@_) == 0); + return $M if (Value::isFormula($M) && $M->type eq "Matrix"); $M = [$M,@_] if ((defined($M) && ref($M) ne 'ARRAY') || scalar(@_) > 0); Value::Error("Matrices must have at least one entry") unless defined($M) && scalar(@{$M}) > 0; return $self->numberMatrix(@{$M}) if Value::isNumber($M->[0]); - return $self->matrixMatrix(@{$M}); + return $self->matrixMatrix(@{$M}) if ref($M->[0]) =~ m/ARRAY|Matrix/; + return $self->parseFormula(@{$M}); } # -# (Recusrively) make a matrix from a list of array refs +# (Recursively) make a matrix from a list of array refs # and report errors about the entry types # sub matrixMatrix { |
From: Sam H. v. a. <we...@ma...> - 2005-02-14 21:08:39
|
Log Message: ----------- it's 2005 now. (actually we should update this on the headers of all files that were modified in 2005 as well). Tags: ---- rel-2-1-patches Modified Files: -------------- webwork2: LICENSE README Revision Data ------------- Index: README =================================================================== RCS file: /webwork/cvs/system/webwork2/README,v retrieving revision 1.10.2.1 retrieving revision 1.10.2.2 diff -LREADME -LREADME -u -r1.10.2.1 -r1.10.2.2 --- README +++ README @@ -3,7 +3,7 @@ Online Homework Delivery System Version 2.1.1 - Copyright 2000-2004, The WeBWorK Project + Copyright 2000-2005, The WeBWorK Project All rights reserved. Introduction Index: LICENSE =================================================================== RCS file: /webwork/cvs/system/webwork2/LICENSE,v retrieving revision 1.5.2.1 retrieving revision 1.5.2.2 diff -LLICENSE -LLICENSE -u -r1.5.2.1 -r1.5.2.2 --- LICENSE +++ LICENSE @@ -3,7 +3,7 @@ Online Homework Delivery System Version 2.1.1 - Copyright 2000-2004, The WeBWorK Project + Copyright 2000-2005, The WeBWorK Project All rights reserved. This program is free software; you can redistribute it and/or modify |
From: Sam H. v. a. <we...@ma...> - 2005-02-14 21:07:48
|
Log Message: ----------- updated README from PGLanguageRelease2pt1pt1, updated LICENSE with new version number and year. Tags: ---- rel-2-1-patches Modified Files: -------------- pg: LICENSE README Revision Data ------------- Index: README =================================================================== RCS file: /webwork/cvs/system/pg/README,v retrieving revision 1.1 retrieving revision 1.1.2.1 diff -LREADME -LREADME -u -r1.1 -r1.1.2.1 --- README +++ README @@ -1,58 +1,59 @@ WeBWorK Program Generation Language - Version 2.1 + Version 2.1.1 - Copyright 2000-2004, The WeBWorK Project + Copyright 2000-2005, The WeBWorK Project All rights reserved. Introduction - The [7]WeBWorKTeam is pleased to announce the release of version 2.1 + The [7]WeBWorKTeam is pleased to announce the release of version 2.1.1 of the WeBWorK Program Generating Language (PG). This release includes - new features and bug fixes: + several bug fixes: - * New [8]mathematical-expression parser (for use in writing PG - problems). - * Fixed generation of LaTeX version of sqrt. - * Fixed implementation of useBaseTenLog flag. - * Make combinations function C(n,k) return 0 when k>n, which is - standard. - * Fixed error when using cplx_cmp. - - (Contributiors: please feel free to add anything I've forgotten.) + 1. Fixed a bug in processing absolute values with implicit + multiplication + 2. Fixed a bug in which relative tolerance was not being used + correctly + 3. Fixed a bug in which the names of images converted from GIF to PNG + during hardcopy generation could collide + 4. Fixed a bug in which undefined values caused warnings + 5. Added a check to handle the case when the $errors flag was not + defined + 6. Fixed an index problem with matrix multiplication Availability PG is distributed as a tarball. It is available on our SourceForge project page: - [9]http://sourceforge.net/project/showfiles.php?group_id=93112 + [8]http://sourceforge.net/project/showfiles.php?group_id=93112 To use this library, you will probably want to download WeBWorK. See - [10]AvailableVersions for more information. + [9]AvailableVersions for more information. - Installation directly from CVS is also possible. Refer to the - [11]InstallationManualV2pt1 for more information. + Installing directly from CVS is also possible. Refer to the + [10]InstallationManualV2pt1 for more information. Installation Documentation on installing and configuring PG is available in the - WeBWorK [12]InstallationManualV2pt1. If you are upgrading an existing - installation, be sure to read the section [13]Upgrading an existing + WeBWorK [11]InstallationManualV2pt1. If you are upgrading an existing + installation, be sure to read the section [12]Upgrading an existing WeBWorK installation. Help - If you need help installing or using PG 2.1, consult [14]WeBWorKDocs - and [15]WeBWorKFAQs. + If you need help installing or using PG 2.1, consult [13]WeBWorKDocs + and [14]WeBWorKFAQs. - You can also visit the [16]WeBWorK discussion group and post your + You can also visit the [15]WeBWorK discussion group and post your question there. The developers monitor this forum. Bug Reports & Feature Requests Submit bug reports and feature requests at - [17]http://bugs.webwork.rochester.edu/. We can't fix bugs and add + [16]http://bugs.webwork.rochester.edu/. We can't fix bugs and add features if you don't tell us about them! Patches @@ -61,22 +62,21 @@ CVS code, you save us and yourself time. A bug in this release may be fixed in CVS, and we can more easily handle patches against the latest code. Check out the latest development version from CVS and patch - against that. Consult the [18]WeBWorKCVS topic for more information. + against that. Consult the [17]WeBWorKCVS topic for more information. - -- [19]SamHathaway - 28 Oct 2004 + -- [18]SamHathaway - 08 Feb 2005 References 7. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKTeam - 8. http://devel.webwork.rochester.edu/doc/cvs/webwork2_rel-2-1/doc/parser/ - 9. http://sourceforge.net/project/showfiles.php?group_id=93112 - 10. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/AvailableVersions + 8. http://sourceforge.net/project/showfiles.php?group_id=93112 + 9. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/AvailableVersions + 10. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1 11. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1 - 12. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1 - 13. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1#Upgrading_an_existing_WeBWorK_in - 14. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKDocs - 15. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKFAQs - 16. http://webhost.math.rochester.edu/webworkdocs/discuss/ - 17. http://bugs.webwork.rochester.edu/ - 18. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKCVS - 19. http://devel.webwork.rochester.edu/twiki/bin/view/Main/SamHathaway + 12. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1#Upgrading_an_existing_WeBWorK_in + 13. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKDocs + 14. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKFAQs + 15. http://webhost.math.rochester.edu/webworkdocs/discuss/ + 16. http://bugs.webwork.rochester.edu/ + 17. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKCVS + 18. http://devel.webwork.rochester.edu/twiki/bin/view/Main/SamHathaway Index: LICENSE =================================================================== RCS file: /webwork/cvs/system/pg/LICENSE,v retrieving revision 1.1 retrieving revision 1.1.2.1 diff -LLICENSE -LLICENSE -u -r1.1 -r1.1.2.1 --- LICENSE +++ LICENSE @@ -1,9 +1,9 @@ WeBWorK Program Generation Language - Version 2.1 + Version 2.1.1 - Copyright 2000-2004, The WeBWorK Project + Copyright 2000-2005, The WeBWorK Project All rights reserved. This program is free software; you can redistribute it and/or modify |
From: Sam H. v. a. <we...@ma...> - 2005-02-14 21:05:31
|
Log Message: ----------- updated README from TWiki topic WeBWorKRelease2pt1pt1, updated LICENCE with new version number Tags: ---- rel-2-1-patches Modified Files: -------------- webwork2: LICENSE README Revision Data ------------- Index: README =================================================================== RCS file: /webwork/cvs/system/webwork2/README,v retrieving revision 1.10 retrieving revision 1.10.2.1 diff -LREADME -LREADME -u -r1.10 -r1.10.2.1 --- README +++ README @@ -1,81 +1,100 @@ WeBWorK Online Homework Delivery System - Version 2.1 + Version 2.1.1 Copyright 2000-2004, The WeBWorK Project All rights reserved. Introduction - The [8]WeBWorKTeam is pleased to announce the release of WeBWorK 2.1. - This release includes new functionality and enhancements to the user - interface, database system, and PG language: - - * New sql_single database layout, for more efficient access to SQL - databases. - * New [9]mathematical-expression parser (for use in writing PG - problems). - * New File Manager module provides full access to course files. - (Will replace File Xfer module in a future release.) - * New Problem Set Detail module allows editing of set data (dates, - headers), editing of problems (source file, value, etc.), and - reordering of problems. - * Improvements to jsMath display mode. - * Many usability and cosmetic improvements. - * Many bugfixes. + This release introduces several improvements and release fixes many + bugs in WeBWorK 2.1. Among the changes made are the following: - (Contributiors: please feel free to add anything I've forgotten.) + Enhancements + + 1. Added a login privilege to the permissions system + 2. Update jsMath to 1.7e, including support for additional LaTeX + environments + 3. Improved the mail merge user interface + 4. Improved the default stylesheet + 5. Added an option to rename the Change Display Settings submit + buttton + 6. Added summary column to the scoring module for the total value of + WeBWorK problems done + 7. Optimized checking of permission levels by caching the currently + logged-in user's permission level + 8. Added several features to the classlist editor + + ability to search on any data field ( bug_small.gif [9]Bug + #664) + + ability to define a subsort (i.e. first sort by last name, + then by first name) ( bug_small.gif [10]Bug #654) + + warning is given when users with duplicate names are imported + ( bug_small.gif [11]Bug #684) + 9. Added option to file manager to convert newlines on uploaded files + 10. Feedback messages now include course ID + 11. Leading and trailing whitespace is now removed on the add users + page + 12. Added the ability to add a new, blank problem to an existing + problem set + 13. Made changes to preserve problem display options across all pages + 14. Modified the default template to refer to the stylesheet by + reference, reducing page load times + + Bug fixes + + 1. Fixed a bug where the Show saved answers option could not be + turned off + 2. Fixed a big where an incorrect error message would be given when a + database error occurred + 3. Fixed "uninitialized value" error in grading ( bug_small.gif + [12]Bug #733) + 4. Fixed a bug where a weight of 0 would appear as a blank ( + bug_small.gif [13]Bug #730) + 5. Fixed a bug where fun_cmp would give the error "This answer is the + same as the one you just submitted" after a preview operation ( + bug_small.gif [14]Bug #557) + 6. Fixed a bug where the parameter user would be defined multiple + times in a form or URL + 7. Fixed a bug where non-overridden date values would be displayed as + 12/31/1969 on the problem set detail page + 8. Fixed several bugs in the file manager + 9. Fixed a bug where the scoring page would crash and burn if no set + was selected ( bug_small.gif [15]Bug #750) Availability - WeBWorK 2.1 requires [10]PGLanguageRelease2pt1. Both WeBWorK and PG is - available on our SourceForge project page: - [11]http://sourceforge.net/project/showfiles.php?group_id=93112 + WeBWorK 2.1.1 is available our CVS repository. Read + [16]WeBWorKCVSReadOnly for more information on how to set up a CVS + connection. For those who already have a CVS connection, this update + can be obtained by updating to the release: rel-2-1-patches + + WeBWorK 2.1.1 is also available in tarball form from our SourceForge + project page: + [17]http://sourceforge.net/project/showfiles.php?group_id=93112 - Installation directly from CVS is also possible. Refer to the - [12]InstallationManualV2pt1 for more information. + We recommend you also install [18]PGLanguageRelease2pt1pt1 at the same + time you install WeBWorK 2.1.1. Installation - Documentation on installing and configuring WeBWorK is available in - the [13]InstallationManualV2pt1. If you are upgrading an existing - installation, be sure to read the section [14]Upgrading an existing - WeBWorK installation. - - Using the new 'sql_single' database layout - - The new sql_single database layout stores the data for all courses - using the layout in a single database, reducing the number of - connections that need to be made to the SQL server and eliminates the - need for database to be created and deleted as courses are created and - deleted. It also adds indexing to data tables, improving system speed. - - To learn more about the database options available in WeBWorK 2.1, - including the sql_single layout, consult the [15]Database - configuration section of [16]InstallationManualV2pt1 and the - [17]DatabaseLayoutManual. - - Before creating courses using the sql_single layout (or converting - existing courses), you must [18]create a single database and grant - access to it. - - We suggest that you convert all courses using the sql database layout - to use sql_single instead and add indexing to converted courses. - Instructions are available in the [19]CourseAdministrationManual. + Read the section in the installation manual on [19]Upgrading WeBWorK. -Help + Changes were made to the configuration global.conf.dist in this + release. Be sure to compare your existing global.conf file with the + new global.conf.dist file and make any changes necessary. The utility + diff3 may be useful to you. - If you need help installing or using WeBWorK 2.1, consult - [20]WeBWorKDocs and [21]WeBWorKFAQs. +Help - You can also visit the [22]WeBWorK discussion group and post your - question there. The developers monitor this forum. + If you need help installing or using WeBWorK 2.1.1, visit the + [20]WeBWorK discussion group and post your question there. The + developers monitor this forum. Bug Reports & Feature Requests Submit bug reports and feature requests at - [23]http://bugs.webwork.rochester.edu/. We can't fix bugs and add + [21]http://bugs.webwork.rochester.edu/. We can't fix bugs and add features if you don't tell us about them! Patches @@ -84,27 +103,24 @@ CVS code, you save us and yourself time. A bug in this release may be fixed in CVS, and we can more easily handle patches against the latest code. Check out the latest development version from CVS and patch - against that. Consult the [24]WeBWorKCVS topic for more information. + against that. Consult the [22]WeBWorKCVS topic for more information. - -- [25]SamHathaway - 28 Oct 2004 + -- [23]SamHathaway - 08 Feb 2005 References - 8. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKTeam - 9. http://devel.webwork.rochester.edu/doc/cvs/webwork2_rel-2-1/doc/parser/ - 10. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/PGLanguageRelease2pt1 - 11. http://sourceforge.net/project/showfiles.php?group_id=93112 - 12. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1 - 13. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1 - 14. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1#Upgrading_an_existing_WeBWorK_in - 15. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1#Database_configuration - 16. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1 - 17. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/DatabaseLayoutManual - 18. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1#Configration_for_the_sql_single_ - 19. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/CourseAdministrationManual - 20. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKDocs - 21. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKFAQs - 22. http://webhost.math.rochester.edu/webworkdocs/discuss/ - 23. http://bugs.webwork.rochester.edu/ - 24. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKCVS - 25. http://devel.webwork.rochester.edu/twiki/bin/view/Main/SamHathaway + 9. http://bugs.webwork.rochester.edu/show_bug.cgi?id=664 + 10. http://bugs.webwork.rochester.edu/show_bug.cgi?id=654 + 11. http://bugs.webwork.rochester.edu/show_bug.cgi?id=684 + 12. http://bugs.webwork.rochester.edu/show_bug.cgi?id=733 + 13. http://bugs.webwork.rochester.edu/show_bug.cgi?id=730 + 14. http://bugs.webwork.rochester.edu/show_bug.cgi?id=557 + 15. http://bugs.webwork.rochester.edu/show_bug.cgi?id=750 + 16. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKCVSReadOnly + 17. http://sourceforge.net/project/showfiles.php?group_id=93112 + 18. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/PGLanguageRelease2pt1pt1 + 19. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/InstallationManualV2pt1#Upgrading_WeBWorK + 20. http://webhost.math.rochester.edu/webworkdocs/discuss/ + 21. http://bugs.webwork.rochester.edu/ + 22. http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/WeBWorKCVS + 23. http://devel.webwork.rochester.edu/twiki/bin/view/Main/SamHathaway Index: LICENSE =================================================================== RCS file: /webwork/cvs/system/webwork2/LICENSE,v retrieving revision 1.5 retrieving revision 1.5.2.1 diff -LLICENSE -LLICENSE -u -r1.5 -r1.5.2.1 --- LICENSE +++ LICENSE @@ -1,7 +1,7 @@ WeBWorK Online Homework Delivery System - Version 2.1 + Version 2.1.1 Copyright 2000-2004, The WeBWorK Project All rights reserved. |
From: Mike G. v. a. <we...@ma...> - 2005-02-13 18:07:46
|
Log Message: ----------- Initialize dates to be now+ one week for the open date and now + two weeks for the due and answer dates. This makes it less likely that the date will be undefined. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: ProblemSetList.pm Revision Data ------------- Index: ProblemSetList.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm,v retrieving revision 1.77 retrieving revision 1.78 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm -u -r1.77 -r1.78 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetList.pm @@ -84,6 +84,7 @@ use constant HIDE_SETS_THRESHOLD => 50; use constant DEFAULT_PUBLISHED_STATE => 1; +use constant ONE_WEEK => 60*60*24*7; use constant EDIT_FORMS => [qw(cancelEdit saveEdit)]; use constant VIEW_FORMS => [qw(filter sort edit publish import export score create delete)]; @@ -912,13 +913,16 @@ return CGI::div({class => "ResultsWithError"}, "Failed to create new set: no set name specified!") unless $newSetID =~ /\S/; my $type = $actionParams->{"action.create.type"}->[0]; + # It's convenient to set the open date one week from now so that it is + # not accidentally available to students. We set the due and answer date + # to be two weeks from now. if ($type eq "empty") { $newSetRecord->set_id($newSetID); $newSetRecord->set_header(""); $newSetRecord->hardcopy_header(""); - $newSetRecord->open_date("0"); - $newSetRecord->due_date("0"); - $newSetRecord->answer_date("0"); + $newSetRecord->open_date(time + ONE_WEEK); + $newSetRecord->due_date(time + 2*ONE_WEEK ); + $newSetRecord->answer_date(time + 2*ONE_WEEK ); $newSetRecord->published(DEFAULT_PUBLISHED_STATE); # don't want students to see an empty set $db->addGlobalSet($newSetRecord); } elsif ($type eq "copy") { @@ -1264,10 +1268,24 @@ sub bySetID { $a->set_id cmp $b->set_id } sub bySetHeader { $a->set_header cmp $b->set_header } sub byHardcopyHeader { $a->hardcopy_header cmp $b->hardcopy_header } -sub byOpenDate { $a->open_date <=> $b->open_date } -sub byDueDate { $a->due_date <=> $b->due_date } -sub byAnswerDate { $a->answer_date <=> $b->answer_date } -sub byPublished { $a->published cmp $b->published } +#FIXME eventually we may be able to remove these checks, if we can trust +# that the dates are always defined +sub byOpenDate { my $result = eval{$a->open_date <=> $b->open_date }; + return $result unless $@; + return 0; +} +sub byDueDate { my $result = eval{$a->due_date <=> $b->due_date }; + return $result unless $@; + return 0; +} +sub byAnswerDate { my $result = eval{$a->answer_date <=> $b->answer_date }; + return $result unless $@; + return 0; +} +sub byPublished { my $result = eval{$a->published cmp $b->published }; + return $result unless $@; + return 0; +} sub byOpenDue { &byOpenDate || &byDueDate } |
From: dpvc v. a. <we...@ma...> - 2005-02-10 22:50:45
|
Log Message: ----------- Fixed a problem that caused errors to occur when ImplicitPlane() was called with a formula whose constant term is a negative number that is produced by a computation. Modified Files: -------------- pg/macros: parserImplicitPlane.pl Revision Data ------------- Index: parserImplicitPlane.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/parserImplicitPlane.pl,v retrieving revision 1.3 retrieving revision 1.4 diff -Lmacros/parserImplicitPlane.pl -Lmacros/parserImplicitPlane.pl -u -r1.3 -r1.4 --- macros/parserImplicitPlane.pl +++ macros/parserImplicitPlane.pl @@ -104,8 +104,8 @@ # # Find the coefficients of the formula # - my $f = Value::Formula->new($plane->{tree}{lop}) - - Value::Formula->new($plane->{tree}{rop}); + my $f = (Value::Formula->new($plane->{tree}{lop}) - + Value::Formula->new($plane->{tree}{rop}))->reduce; my $F = $f->perlFunction(undef,[@{$vars}]); my @v = split('','0' x scalar(@{$vars})); $d = -&$F(@v); my @coeff = (@v); |
From: Arnie P. v. a. <we...@ma...> - 2005-02-09 18:53:06
|
Log Message: ----------- Allow labels A - ZZ Arnie Modified Files: -------------- pg/macros: PGbasicmacros.pl PGchoicemacros.pl Revision Data ------------- Index: PGbasicmacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGbasicmacros.pl,v retrieving revision 1.36 retrieving revision 1.37 diff -Lmacros/PGbasicmacros.pl -Lmacros/PGbasicmacros.pl -u -r1.36 -r1.37 --- macros/PGbasicmacros.pl +++ macros/PGbasicmacros.pl @@ -1660,7 +1660,7 @@ sub OL { my(@array) = @_; my $i = 0; - my @alpha = ("A".."Z"); + my @alpha = ('A'..'Z', 'AA'..'ZZ'); my $letter; my $out= &M3( "\\begin{enumerate}\n", Index: PGchoicemacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGchoicemacros.pl,v retrieving revision 1.6 retrieving revision 1.7 diff -Lmacros/PGchoicemacros.pl -Lmacros/PGchoicemacros.pl -u -r1.6 -r1.7 --- macros/PGchoicemacros.pl +++ macros/PGchoicemacros.pl @@ -528,7 +528,7 @@ my $self = shift; my(@array) = @_; my $i = 0; - my @alpha = ("A".."Z"); + my @alpha = ('A'..'Z', 'AA'..'ZZ'); my $letter; my $out= &main::M3( "\\begin{enumerate}\n", |