--- loncom/homework/lonhomework.pm 2003/09/18 19:59:15 1.145 +++ loncom/homework/lonhomework.pm 2004/10/18 18:08:54 1.182 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Homework handler # -# $Id: lonhomework.pm,v 1.145 2003/09/18 19:59:15 albertel Exp $ +# $Id: lonhomework.pm,v 1.182 2004/10/18 18:08:54 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -24,11 +24,7 @@ # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ -# -# Guy Albertelli -# 11/30 Gerd Kortemeyer -# 6/1,8/17,8/18 Gerd Kortemeyer -# 7/18 Jeremy Bowers + package Apache::lonhomework; use strict; @@ -51,10 +47,12 @@ use Apache::externalresponse(); use Apache::rankresponse(); use Apache::matchresponse(); use Apache::chemresponse(); +use Apache::drawimage(); use Apache::Constants qw(:common); use HTML::Entities(); use Apache::loncommon(); -#use Time::HiRes qw( gettimeofday tv_interval ); +use Apache::lonlocal; +use Time::HiRes qw( gettimeofday tv_interval ); BEGIN { &Apache::lonxml::register_insert(); @@ -89,8 +87,8 @@ sub get_target { return ('web'); } } else { - if ( $ENV{'form.problemmode'} eq 'View' || - $ENV{'form.problemmode'} eq 'Discard Edits and View') { + if ( $ENV{'form.problemmode'} eq &mt('View') || + $ENV{'form.problemmode'} eq &mt('Discard Edits and View')) { if ( defined($ENV{'form.submitted'}) && (!defined($ENV{'form.resetdata'})) && (!defined($ENV{'form.newrandomization'}))) { @@ -98,9 +96,9 @@ sub get_target { } else { return ('web','answer'); } - } elsif ( $ENV{'form.problemmode'} eq 'Edit' ) { + } elsif ( $ENV{'form.problemmode'} eq &mt('Edit') ) { if ( $ENV{'form.submitted'} eq 'edit' ) { - if ( $ENV{'form.submit'} eq 'Submit Changes and View' ) { + if ( $ENV{'form.submit'} eq &mt('Submit Changes and View') ) { return ('modified','web','answer'); } else { return ('modified','edit'); @@ -146,6 +144,51 @@ sub send_footer { $Apache::lonxml::browse=''; +sub check_ip_acc { + my ($acc)=@_; + if (!defined($acc) || $acc =~ /^\s*$/) { return 1; } + my $allowed=0; + my $ip=$ENV{'REMOTE_ADDR'}; + my $name; + foreach my $pattern (split(',',$acc)) { + if ($pattern =~ /\*$/) { + #35.8.* + $pattern=~s/\*//; + if ($ip =~ /^\Q$pattern\E/) { $allowed=1; } + } elsif ($pattern =~ /(\d+\.\d+\.\d+)\.\[(\d+)-(\d+)\]$/) { + #35.8.3.[34-56] + my $low=$2; + my $high=$3; + $pattern=$1; + if ($ip =~ /^\Q$pattern\E/) { + my $last=(split(/\./,$ip))[3]; + if ($last <=$high && $last >=$low) { $allowed=1; } + } + } elsif ($pattern =~ /^\*/) { + #*.msu.edu + $pattern=~s/\*//; + if (!defined($name)) { + use Socket; + my $netaddr=inet_aton($ip); + ($name)=gethostbyaddr($netaddr,AF_INET); + } + if ($name =~ /\Q$pattern\E$/i) { $allowed=1; } + } elsif ($pattern =~ /\d+\.\d+\.\d+\.\d+/) { + #127.0.0.1 + if ($ip =~ /^\Q$pattern\E/) { $allowed=1; } + } else { + #some.name.com + if (!defined($name)) { + use Socket; + my $netaddr=inet_aton($ip); + ($name)=gethostbyaddr($netaddr,AF_INET); + } + if ($name =~ /\Q$pattern\E$/i) { $allowed=1; } + } + if ($allowed) { last; } + } + return $allowed; +} # JB, 9/24/2002: Any changes in this function may require a change # in lonnavmaps::resource::getDateStatus. sub check_access { @@ -159,64 +202,87 @@ sub check_access { my $passed; if ($ENV{'request.state'} eq "construct") { + if ($ENV{'form.problemstate'}) { + if ($ENV{'form.problemstate'} =~ /^CANNOT_ANSWER/) { + if ( ! ($ENV{'form.problemstate'} eq 'CANNOT_ANSWER_correct' && + lc($Apache::lonhomework::problemstatus) eq 'no')) { + return ('CANNOT_ANSWER', + &mt('is in this state due to author settings.')); + } + } else { + return ($ENV{'form.problemstate'}, + &mt('is in this state due to author settings.')); + } + } &Apache::lonxml::debug("in construction ignoring dates"); $status='CAN_ANSWER'; - $datemsg='is in under construction'; - return ($status,$datemsg); + $datemsg=&mt('is in under construction'); +# return ($status,$datemsg); } &Apache::lonxml::debug("checking for part :$id:"); &Apache::lonxml::debug("time:".time); - foreach $temp ("opendate","duedate","answerdate") { - $lastdate = $date; - $date = &Apache::lonnet::EXT("resource.$id.$temp"); - my $thistype = &Apache::lonnet::EXT("resource.$id.$temp.type"); - if ($thistype =~ /^(con_lost|no_such_host)/ || - $date =~ /^(con_lost|no_such_host)/) { - $status='UNAVAILABLE'; - $date="may open later."; + + if ($ENV{'request.state'} ne "construct") { + my $allowed=&check_ip_acc(&Apache::lonnet::EXT("resource.$id.acc")); + if (!$allowed && ($Apache::lonhomework::browse ne 'F')) { + $status='INVALID_ACCESS'; + $date=&mt("can not be accessed from your location."); return($status,$date); } - if ($thistype eq 'date_interval') { - if ($temp eq 'opendate') { - $date=&Apache::lonnet::EXT("resource.$id.duedate")-$date; - } - if ($temp eq 'answerdate') { - $date=&Apache::lonnet::EXT("resource.$id.duedate")+$date; + + foreach $temp ("opendate","duedate","answerdate") { + $lastdate = $date; + $date = &Apache::lonnet::EXT("resource.$id.$temp"); + my $thistype = &Apache::lonnet::EXT("resource.$id.$temp.type"); + if ($thistype =~ /^(con_lost|no_such_host)/ || + $date =~ /^(con_lost|no_such_host)/) { + $status='UNAVAILABLE'; + $date=&mt("may open later."); + return($status,$date); + } + if ($thistype eq 'date_interval') { + if ($temp eq 'opendate') { + $date=&Apache::lonnet::EXT("resource.$id.duedate")-$date; + } + if ($temp eq 'answerdate') { + $date=&Apache::lonnet::EXT("resource.$id.duedate")+$date; + } + } + &Apache::lonxml::debug("found :$date: for :$temp:"); + if ($date eq '') { + $date = &mt("an unknown date"); $passed = 0; + } elsif ($date eq 'con_lost') { + $date = &mt("an indeterminate date"); $passed = 0; + } else { + if (time < $date) { $passed = 0; } else { $passed = 1; } + $date = localtime $date; } + if (!$passed) { $type=$temp; last; } } - &Apache::lonxml::debug("found :$date: for :$temp:"); - if ($date eq '') { - $date = "an unknown date"; $passed = 0; - } elsif ($date eq 'con_lost') { - $date = "an indeterminate date"; $passed = 0; - } else { - if (time < $date) { $passed = 0; } else { $passed = 1; } - $date = localtime $date; + &Apache::lonxml::debug("have :$type:$passed:"); + if ($passed) { + $status='SHOW_ANSWER'; + $datemsg=$date; + } elsif ($type eq 'opendate') { + $status='CLOSED'; + $datemsg = &mt("will open on")." $date"; + } elsif ($type eq 'duedate') { + $status='CAN_ANSWER'; + $datemsg = &mt("is due at")." $date"; + } elsif ($type eq 'answerdate') { + $status='CLOSED'; + $datemsg = &mt("was due on")." $lastdate".&mt(", and answers will be available on")." $date"; } - if (!$passed) { $type=$temp; last; } - } - &Apache::lonxml::debug("have :$type:$passed:"); - if ($passed) { - $status='SHOW_ANSWER'; - $datemsg=$date; - } elsif ($type eq 'opendate') { - $status='CLOSED'; - $datemsg = "will open on $date"; - } elsif ($type eq 'duedate') { - $status='CAN_ANSWER'; - $datemsg = "is due at $date"; - } elsif ($type eq 'answerdate') { - $status='CLOSED'; - $datemsg = "was due on $lastdate, and answers will be available on $date"; } if ($status eq 'CAN_ANSWER') { #check #tries, and if correct. my $tries = $Apache::lonhomework::history{"resource.$id.tries"}; my $maxtries = &Apache::lonnet::EXT("resource.$id.maxtries"); if ( $tries eq '' ) { $tries = '0'; } - if ( $maxtries eq '' ) { $maxtries = '2'; } - if ($tries >= $maxtries) { $status = 'CANNOT_ANSWER'; } + if ( $maxtries eq '' && + $ENV{'request.state'} ne 'construct') { $maxtries = '2'; } + if ($maxtries && $tries >= $maxtries) { $status = 'CANNOT_ANSWER'; } # if (correct and show prob status) or excused then CANNOT_ANSWER if(($Apache::lonhomework::history{"resource.$id.solved"}=~/^correct/ && @@ -226,7 +292,26 @@ sub check_access { $status = 'CANNOT_ANSWER'; } } - + if ($status eq 'CAN_ANSWER' || $status eq 'CANNOT_ANSWER') { + my $interval=&Apache::lonnet::EXT("resource.$id.interval"); + &Apache::lonxml::debug("looking for interval $interval"); + if ($interval) { + my $first_access=&Apache::lonnet::get_first_access('map'); + &Apache::lonxml::debug("looking for accesstime $first_access"); + if (!$first_access) { + $status='NOT_YET_VIEWED'; + $datemsg=' '.$interval.' seconds'; + } else { + my $newdate=localtime($first_access+$interval); + if (time > ($first_access+$interval)) { + $status='CLOSED'; + $datemsg = &mt("was due on")." $newdate".&mt(", and answers will be available on")." $date"; + } else { + $datemsg = &mt("is due at")." $newdate"; + } + } + } + } #if (($status ne 'CLOSED') && ($Apache::lonhomework::type eq 'exam') && # (!$Apache::lonhomework::history{"resource.0.outtoken"})) { # return ('UNCHECKEDOUT','needs to be checked out'); @@ -237,7 +322,7 @@ sub check_access { if (($Apache::lonhomework::browse eq 'F') && ($status eq 'CLOSED')) { &Apache::lonxml::debug("should be allowed to browse a resource when closed"); $status='CAN_ANSWER'; - $datemsg='is closed but you are allowed to view it'; + $datemsg=&mt('is closed but you are allowed to view it'); } return ($status,$datemsg); @@ -306,11 +391,10 @@ sub setuppermissions { sub setupheader { my $request=$_[0]; if ($ENV{'browser.mathml'}) { - $request->content_type('text/xml'); + &Apache::loncommon::content_type($request,'text/xml'); } else { - $request->content_type('text/html'); + &Apache::loncommon::content_type($request,'text/html'); } - $request->content_encoding('UTF-8'); if (!$Apache::lonxml::debug && ($ENV{'REQUEST_METHOD'} eq 'GET')) { &Apache::loncommon::no_cache($request); } @@ -326,32 +410,37 @@ sub handle_save_or_undo { my $filetmp =$file.".tmp"; my $error=0; - if ($ENV{'form.Undo'} eq 'undo') { + &Apache::lonnet::correct_line_ends($result); + + if ($ENV{'form.Undo'} eq &mt('undo')) { my $error=0; if (!copy($file,$filetmp)) { $error=1; } if ((!$error) && (!copy($filebak,$file))) { $error=1; } if ((!$error) && (!move($filetmp,$filebak))) { $error=1; } if (!$error) { - $request->print("

Undid changes, Switched $filebak and $file

"); + &Apache::lonxml::info("

".&mt("Undid changes, Switched")." $filebak ".&mt("and")." $file

"); } else { - $request->print("

Unable to undo, unable to switch $filebak and $file

"); + &Apache::lonxml::info("

".&mt("Unable to undo, unable to switch")." $filebak ".&mt("and")." $file

"); $error=1; } } else { my $fs=Apache::File->new(">$filebak"); if (defined($fs)) { print $fs $$problem; - $request->print("Making Backup to $filebak
"); + &Apache::lonxml::info("".&mt("Making Backup to"). + " $filebak"); } else { - $request->print("Unable to make backup $filebak"); + &Apache::lonxml::info("".&mt("Unable to make backup")." $filebak"); $error=2; } my $fh=Apache::File->new(">$file"); if (defined($fh)) { print $fh $$result; - $request->print("Saving Modifications to $file
"); + &Apache::lonxml::info("".&mt("Saving Modifications to"). + " $file"); } else { - $request->print("Unable to write to $file"); + &Apache::lonxml::info("". + &mt("Unable to write to")." $file"); $error|=4; } } @@ -361,17 +450,18 @@ sub handle_save_or_undo { sub analyze_header { my ($request) = @_; my $result.=' - Analyzing a problem - + '.&mt("Analyzing a problem").' + '.&Apache::lonxml::message_location().'
- - + &HTML::Entities::encode($ENV{'request.uri'},'<>&"').'">'. + &Apache::structuretags::remember_problem_state().' + +
- +
- List of possible answers:
'; + &Apache::lonxml::add_messages(\$result); $request->print($result); $request->rflush(); } @@ -392,12 +482,14 @@ sub analyze { my $rndseed=$ENV{'form.rndseed'}; &analyze_header($request); my %prog_state= - &Apache::lonhtmlcommon::Create_PrgWin($request,'Analyze Progress', - 'Getting Problem Variants', - $ENV{'form.numtoanalyze'}); + &Apache::lonhtmlcommon::Create_PrgWin($request,&mt('Analyze Progress'), + &mt('Getting Problem Variants'), + $ENV{'form.numtoanalyze'}, + 'inline',undef); for(my $i=1;$i<$ENV{'form.numtoanalyze'}+1;$i++) { &Apache::lonhtmlcommon::Increment_PrgWin($request,\%prog_state, - 'last problem'); + &mt('last problem')); + if (&Apache::loncommon::connection_aborted($request)) { return; } my $subresult=&Apache::lonnet::ssi($request->uri, ('grade_target' => 'analyze'), ('rndseed' => $i+$rndseed)); @@ -418,16 +510,17 @@ sub analyze { } } &Apache::lonhtmlcommon::Update_PrgWin($request,\%prog_state, - 'Analyzing Results'); + &mt('Analyzing Results')); + $request->print('
'.&mt('List of possible answers').': '); foreach my $part (sort(keys(%allparts))) { if (defined(@{ $overall{$part.'.answer'} })) { my $num_cols=scalar(@{ $overall{$part.'.answer'}->[0] }); - $request->print(''); + $request->print('
Part '.$part.'
'); my %frequency; foreach my $answer (sort {$a->[0] <=> $b->[0]} (@{ $overall{$part.'.answer'} })) { $frequency{join("\0",@{ $answer })}++; } - $request->print(''); + $request->print(''); foreach my $answer (sort {(split("\0",$a))[0] <=> (split("\0",$b))[0]} (keys(%frequency))) { $request->print('
'.&mt('Part').' '.$part.'
AnswerFrequency
'.&mt('Answer').''.&mt('Frequency').'
'. join('',split("\0",$answer)). @@ -436,14 +529,12 @@ sub analyze { } $request->print('
'); } else { - $request->print('

Part '.$part. - ' is not analyzable at this time

'); + $request->print('

'.&mt('Response').' '.$part.' '. + &mt('is not analyzable at this time').'

'); } } if (scalar(keys(%allparts)) == 0 ) { - $request->print('

Found no analyzable parts in this problem, - currently only Numerical, Formula and String response - styles are supported.

'); + $request->print('

'.&mt('Found no analyzable respones in this problem, currently only Numerical, Formula and String response styles are supported.').'

'); } &Apache::lonhtmlcommon::Close_PrgWin($request,\%prog_state); &analyze_footer($request); @@ -456,7 +547,8 @@ sub editxmlmode { my $result; my $problem=&Apache::lonnet::getfile($file); if ($problem eq -1) { - &Apache::lonxml::error(" Unable to find $file"); + &Apache::lonxml::error(" ".&mt('Unable to find'). + " $file"); $problem=''; } if (defined($ENV{'form.editxmltext'}) || defined($ENV{'form.Undo'})) { @@ -465,32 +557,39 @@ sub editxmlmode { if (!$error) { $problem=&Apache::lonnet::getfile($file); } } &Apache::lonhomework::showhashsubset(\%ENV,'^form'); - if ( $ENV{'form.submit'} eq 'Submit Changes and View' ) { + if ( $ENV{'form.submit'} eq &mt('Submit Changes and View') ) { &Apache::lonhomework::showhashsubset(\%ENV,'^form'); $ENV{'form.problemmode'}='View'; &renderpage($request,$file); } else { my ($rows,$cols) = &Apache::edit::textarea_sizes(\$problem); - my $xml_help = Apache::loncommon::helpLatexCheatsheet("Problem_Editor_XML_Index", - "Problem Editing Help"); + my $xml_help = '
'. + &Apache::loncommon::helpLatexCheatsheet("Problem_Editor_XML_Index", + "Problem Editing Help"). + ''. + &Apache::loncommon::help_open_menu('',undef,undef,undef,5,'Authoring'). + '
'; if ($cols > 80) { $cols = 80; } if ($cols < 70) { $cols = 70; } if ($rows < 20) { $rows = 20; } - $result.=' + $result.=''. + &Apache::lonxml::message_location().'
- - - + &HTML::Entities::encode($ENV{'request.uri'},'<>&"').'">'. + &Apache::structuretags::remember_problem_state().' + + +
- - - + + +
' . $xml_help . ' + &HTML::Entities::encode($problem,'<>&"').'
'; + &Apache::lonxml::add_messages(\$result); $request->print($result); } return ''; @@ -500,12 +599,14 @@ sub renderpage { my ($request,$file) = @_; my (@targets) = &get_target(); + &Apache::lonhomework::showhashsubset(\%ENV,'form.'); &Apache::lonxml::debug("Running targets ".join(':',@targets)); + my $overall_result; foreach my $target (@targets) { #my $t0 = [&gettimeofday()]; my $problem=&Apache::lonnet::getfile($file); if ($problem eq -1) { - &Apache::lonxml::error(" Unable to find $file"); + &Apache::lonxml::error(" ".&mt('Unable to find')." $file"); $problem=''; } @@ -532,12 +633,16 @@ sub renderpage { #$result =~ s:::; #$result.="
Spent $td seconds processing target $target\n"; #} - $request->print($result); - $request->rflush(); +# $request->print($result); + $overall_result.=$result; +# $request->rflush(); } #$request->print(":Result ends"); #my $td=&tv_interval($t0); } + &Apache::lonxml::add_messages(\$overall_result); + $request->print($overall_result); + $request->rflush(); } # with no arg it returns a HTML \n\n'; } return $result; @@ -586,28 +691,39 @@ sub newproblem { copy($templatefilename,$dest); &renderpage($request,$dest); } else { - my $url=$request->uri; + my $url=&HTML::Entities::encode($request->uri,'<>&"'); + my $shownurl=$url; + $shownurl=~s-^/~-/priv/-; my $dest = &Apache::lonnet::filelocation("",$request->uri); my $errormsg; if ($ENV{'form.newfile'}) { - $errormsg='

You did not select a template.

'."\n"; + $errormsg='

'.&mt('You did not select a template.').'

'."\n"; } my $instructions; - if ($templatelist) { $instructions=", select a template from the pull-down menu below.
Then";} + my $bodytag=&Apache::loncommon::bodytag(undef,undef,undef,1); + if ($templatelist) { $instructions=&mt(", select a template from the pull-down menu below.").'
'.&mt("Then");} + my %lt=&Apache::lonlocal::texthash( 'create' => 'Creating a new', + 'resource' => 'resource', + 'requested' => 'The requested file', + 'not exist' => 'currently does not exist', + 'createnew' => 'To create a new', + 'click' => 'click on the', + 'Create' => 'Create', + 'button' => 'button'); $request->print(< -

Creating a new $extension resource

+$bodytag +

$lt{'create'} $extension $lt{'resource'}

$errormsg -The requested file $url currently does not exist. +$lt{'requested'} $shownurl $lt{'not exist'}.

-To create a new $extension$instructions click on the "Create $extension" button. +$lt{'createnew'} $extension$instructions $lt{'click'} "$lt{'Create'} $extension" $lt{'button'}.

ENDNEWPROBLEM if (defined($templatelist)) { $request->print(""); } - $request->print("
"); + $request->print("
"); $request->print("

"); } return ''; @@ -615,12 +731,18 @@ ENDNEWPROBLEM sub view_or_edit_menu { my ($request) = @_; - my $url=$request->uri; + my $url=&HTML::Entities::encode($request->uri,'<>&"'); + my %lt=&Apache::lonlocal::texthash( 'would' => 'Would you like to', + 'view' => 'View', + 'Edit' => 'edit', + 'or' => 'or', + 'the problem' => 'the problem'); $request->print(<
-Would you like to or - the problem. +$lt{'would'} +<{'or'} +<{'the problem'}.
EDITMENU @@ -631,13 +753,10 @@ sub handler { my $request=$_[0]; $Apache::lonxml::debug=$ENV{'user.debug'}; - if (&setupheader($request)) { return OK; } $ENV{'request.uri'}=$request->uri; - #setup permissions - $Apache::lonhomework::browse= &Apache::lonnet::allowed('bre',$ENV{'request.filename'}); - $Apache::lonhomework::viewgrades=&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'}); + &setuppermissions(); &Apache::lonxml::debug("Permissions:$Apache::lonhomework::browse:$Apache::lonhomework::viewgrades:"); # some times multiple problemmodes are submitted, need to select # the last one @@ -655,7 +774,8 @@ sub handler { #check if we know where we are if ($ENV{'request.course.fn'} && !&Apache::lonnet::symbread()) { # if we are browsing we might not be able to know where we are - if ($Apache::lonhomework::browse ne 'F') { + if ($Apache::lonhomework::browse ne 'F' && + $ENV{'request.state'} ne "construct") { #should know where we are, so ask if ( &Apache::lonnet::mod_perl_version() == 2 ) { &Apache::lonnet::cleanenv(); @@ -667,9 +787,9 @@ sub handler { my ($symb) = &Apache::lonxml::whichuser(); &Apache::lonxml::debug('symb is '.$symb); if ($ENV{'request.state'} eq "construct" || $symb eq '') { - if ($ENV{'form.resetdata'} eq 'Reset Submissions' || - $ENV{'form.resetdata'} eq 'New Problem Variation' || - $ENV{'form.newrandomization'} eq 'New Randomization') { + if ($ENV{'form.resetdata'} eq &mt('Reset Submissions') || + $ENV{'form.resetdata'} eq &mt('New Problem Variation') || + $ENV{'form.newrandomization'} eq &mt('New Randomization')) { my ($symb,$courseid,$domain,$name) = &Apache::lonxml::whichuser(); &Apache::lonnet::tmpreset($symb,'',$domain,$name); &Apache::lonxml::debug("Attempt reset"); @@ -684,9 +804,9 @@ sub handler { #&view_or_edit_menu($request); $ENV{'form.problemmode'}='View'; &renderpage($request,$file); - } elsif ($ENV{'form.problemmode'} eq 'EditXML') { + } elsif ($ENV{'form.problemmode'} eq &mt('EditXML')) { &editxmlmode($request,$file); - } elsif ($ENV{'form.problemmode'} eq 'Calculate answers') { + } elsif ($ENV{'form.problemmode'} eq &mt('Calculate answers')) { &analyze($request,$file); } else { &renderpage($request,$file);