--- loncom/homework/lonhomework.pm 2008/01/21 17:20:45 1.290 +++ loncom/homework/lonhomework.pm 2011/12/08 01:34:47 1.332 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Homework handler # -# $Id: lonhomework.pm,v 1.290 2008/01/21 17:20:45 www Exp $ +# $Id: lonhomework.pm,v 1.332 2011/12/08 01:34:47 www Exp $ # # Copyright Michigan State University Board of Trustees # @@ -47,6 +47,7 @@ use Apache::externalresponse(); use Apache::rankresponse(); use Apache::matchresponse(); use Apache::chemresponse(); +use Apache::functionplotresponse(); use Apache::drawimage(); use Apache::Constants qw(:common); use Apache::loncommon(); @@ -111,7 +112,11 @@ sub get_target { } elsif ( $env{'form.grade_target'} eq 'webgrade' && ($Apache::lonhomework::queuegrade eq 'F' )) { return ($env{'form.grade_target'}); - } + } elsif ($env{'form.grade_target'} eq 'answer') { + if ($env{'form.answer_output_mode'} eq 'tex') { + return ($env{'form.grade_target'}); + } + } if ($env{'form.webgrade'} && ($Apache::lonhomework::modifygrades eq 'F' || $Apache::lonhomework::queuegrade eq 'F' )) { @@ -124,43 +129,46 @@ sub get_target { return ('web'); } } elsif ($env{'request.state'} eq "construct") { +# +# We are in construction space, editing and testing problems +# if ( defined($env{'form.grade_target'}) ) { return ($env{'form.grade_target'}); } if ( defined($env{'form.preview'})) { if ( defined($env{'form.submitted'})) { +# +# We are doing a problem preview +# return ('grade', 'web'); } else { return ('web'); } } else { if ($env{'form.problemstate'} eq 'WEB_GRADE') { - #$env{'form.webgrade'} = 'yes'; return ('grade','webgrade','answer'); - } elsif (($env{'form.problemmode'} eq 'view') || - ($env{'form.problemmode'} eq 'discard')) { - if ( defined($env{'form.submitted'}) && - (!defined($env{'form.resetdata'})) && - (!defined($env{'form.newrandomization'}))) { - return ('grade', 'web','answer'); - } else { - return ('web','answer'); - } - } elsif ($env{'form.problemmode'} eq 'edit') { - if ( $env{'form.submitted'} eq 'edit' ) { - if ( $env{'form.submitbutton'} eq &mt('Save and View') ) { - return ('modified','web','answer'); - } else { - return ('modified','no_output_web','edit'); - } - } else { - return ('no_output_web','edit'); - } + } elsif ($env{'form.problemmode'} eq 'view') { + return ('grade','web','answer'); + } elsif ($env{'form.problemmode'} eq 'saveview') { + return ('modified','web','answer'); + } elsif ($env{'form.problemmode'} eq 'discard') { + return ('web','answer'); + } elsif (($env{'form.problemmode'} eq 'saveedit') || + ($env{'form.problemmode'} eq 'undo')) { + return ('modified','no_output_web','edit'); + } elsif ($env{'form.problemmode'} eq 'edit') { + return ('no_output_web','edit'); } else { return ('web'); } - } + } +# +# End of Construction Space +# } +# +# Huh? We are nowhere, so do nothing. +# return (); } @@ -170,16 +178,6 @@ sub setup_vars { # return ';$external::target='.$target.';'; } -sub createmenu { - my ($which,$request)=@_; - if ($which eq 'grade') { - $request->print(''); - } -} - sub proctor_checked_in { my ($slot_name,$slot,$type)=@_; my @possible_proctors=split(",",$slot->{'proctor'}); @@ -209,58 +207,6 @@ sub proctor_checked_in { return 0; } -sub check_ip_acc { - my ($acc)=@_; - &Apache::lonxml::debug("acc is $acc"); - if (!defined($acc) || $acc =~ /^\s*$/ || $acc =~/^\s*no\s*$/i) { - return 1; - } - my $allowed=0; - my $ip=$env{'request.host'} || $ENV{'REMOTE_ADDR'}; - - my $name; - foreach my $pattern (split(',',$acc)) { - $pattern =~ s/^\s*//; - $pattern =~ s/\s*$//; - 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; -} - sub check_slot_access { my ($id,$type)=@_; @@ -307,7 +253,7 @@ sub check_slot_access { &Apache::lonhomework::showhash(%slot); if ($slot{'starttime'} < time && $slot{'endtime'} > time && - &check_ip_acc($slot{'ip'})) { + &Apache::loncommon::check_ip_acc($slot{'ip'})) { &Apache::lonxml::debug("$slot is good"); $slotstatus='NEEDS_CHECKIN'; $returned_slot=\%slot; @@ -317,8 +263,8 @@ sub check_slot_access { } if ($slotstatus eq 'NEEDS_CHECKIN' && &proctor_checked_in($slot_name,$returned_slot,$type)) { - &Apache::lonxml::debug("protoctor checked in"); - $slotstatus='CAN_ANSWER'; + &Apache::lonxml::debug("proctor checked in"); + $slotstatus=$status; } my ($is_correct,$got_grade,$checkedin); @@ -349,12 +295,15 @@ sub check_slot_access { return ('WAITING_FOR_GRADE'); } - # no slot is currently open, and has been checked in for this version - # previous slot is therefore CLOSED, so therefore the problem is + # Previously used slot is no longer open, and has been checked in for this version. + # However, the problem is not closed, and potentially, another slot might be + # used to gain access to it to work on it, until the due date is reached, and the + # problem then becomes CLOSED. Therefore return the slotstatus - + # (which will be NOT_IN_SLOT). if (!defined($slot_name) && $checkedin && $type eq 'problem') { - return ('CLOSED',$datemsg); + return ($slotstatus); } if ($slotstatus eq 'NOT_IN_A_SLOT' @@ -421,13 +370,18 @@ sub check_access { #if ($env{'request.state'} ne "construct" && $symb ne '') { if ($env{'request.state'} ne "construct") { my $idacc = &Apache::lonnet::EXT("resource.$id.acc"); - my $allowed=&check_ip_acc($idacc); + my $allowed=&Apache::loncommon::check_ip_acc($idacc); if (!$allowed && ($Apache::lonhomework::browse ne 'F')) { $status='INVALID_ACCESS'; $date=&mt("can not be accessed from your location."); return($status,$date); } - + if ($env{'form.grade_imsexport'}) { + if (($env{'request.course.id'}) && + (&Apache::lonnet::allowed('mdc',$env{'request.course.id'}))) { + return ('SHOW_ANSWER'); + } + } foreach my $temp ("opendate","duedate","answerdate") { $lastdate = $date; if ($temp eq 'duedate') { @@ -485,13 +439,16 @@ sub check_access { if ( $tries eq '' ) { $tries = '0'; } if ( $maxtries eq '' && $env{'request.state'} ne 'construct') { $maxtries = '2'; } + $Apache::lonhomework::results{'resource.'.$id.'.maxtries'}=$maxtries; 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/ - && - &show_problem_status()) - || - $Apache::lonhomework::history{"resource.$id.solved"}=~/^excused/) { + if ( ($Apache::lonhomework::history{"resource.$id.solved"}=~/^correct/) + && (&show_problem_status()) ) { + if (&Apache::inputtags::grading_is_nonlenient($id) || + $Apache::lonhomework::history{"resource.$id.awarded"} == 1) { + $status = 'CANNOT_ANSWER'; + } + } elsif ($Apache::lonhomework::history{"resource.$id.solved"}=~/^excused/) { $status = 'CANNOT_ANSWER'; } if ($status eq 'CANNOT_ANSWER' @@ -682,11 +639,12 @@ sub setupheader { sub handle_save_or_undo { my ($request,$problem,$result) = @_; + my $file = &Apache::lonnet::filelocation("",$request->uri); my $filebak =$file.".bak"; my $filetmp =$file.".tmp"; my $error=0; - if ($env{'form.Undo'} eq &mt('undo')) { + if (($env{'form.problemmode'} eq 'undo') || ($env{'form.problemmode'} eq 'undoxml')) { my $error=0; if (!&File::Copy::copy($file,$filetmp)) { $error=1; } if ((!$error) && (!&File::Copy::copy($filebak,$file))) { $error=1; } @@ -709,6 +667,7 @@ sub handle_save_or_undo { } } else { &Apache::lonnet::correct_line_ends($result); + my $fs=Apache::File->new(">$filebak"); if (defined($fs)) { print $fs $$problem; @@ -737,12 +696,24 @@ sub handle_save_or_undo { sub analyze_header { my ($request) = @_; my $js = &Apache::structuretags::setmode_javascript(); - my $result = - &Apache::loncommon::start_page('Analyzing a problem',$js); + # Breadcrumbs + my $brcrum = [{'href' => &Apache::loncommon::authorspace($request->uri), + 'text' => 'Construction Space'}, + {'href' => '', + 'text' => 'Problem Testing'}, + {'href' => '', + 'text' => 'Analyzing a problem'}]; + + my $result = + &Apache::loncommon::start_page('Analyzing a problem', + $js, + {'bread_crumbs' => $brcrum,}) + .&Apache::loncommon::head_subbox( + &Apache::loncommon::CSTR_pageheader()); $result .= &Apache::lonxml::message_location().' -
'. ''. @@ -752,10 +723,10 @@ sub analyze_header { 'onclick="javascript:setmode(this.form,'."'editxml'".')" /> -
+
-
+
'; &Apache::lonxml::add_messages(\$result); @@ -829,37 +800,62 @@ sub analyze { } &Apache::lonhtmlcommon::Update_PrgWin($request,\%prog_state, &mt('Analyzing Results')); - $request->print('
'.&mt('List of possible answers').': '); + $request->print('
' + .'

' + .&mt('List of possible answers') + .'

' + ); foreach my $part (sort(keys(%allparts))) { if (defined(@{ $overall{$part.'.answer'} })) { for (my $i=0;$iprint(''); + $request->print('' + .&Apache::loncommon::end_data_table_header_row() + ); my %frequency; foreach my $answer (sort {$a->[0] <=> $b->[0]} (@{ $overall{$part.'.answer'}[$i] })) { $frequency{join("\0",@{ $answer })}++; } - $request->print(''); + $request->print(&Apache::loncommon::start_data_table_header_row() + .'' + .'' + .&Apache::loncommon::end_data_table_header_row() + ); foreach my $answer (sort {(split("\0",$a))[0] <=> (split("\0",$b))[0]} (keys(%frequency))) { - $request->print(''); + $request->print(&Apache::loncommon::start_data_table_row() + .'' + .'' + .&Apache::loncommon::end_data_table_row() + ); } - $request->print('
'.&mt('Part').' '.$part); + $request->print(&Apache::loncommon::start_data_table() + .&Apache::loncommon::start_data_table_header_row() + .'' + .&mt('Part').' '.$part + ); if (scalar(@{ $overall{$part.'.answer'} }) > 1) { - $request->print(&mt(' Answer [_1]',$i+1)); + $request->print(' '.&mt('Answer [_1]',$i+1)); } - $request->print('
'.&mt('Answer').''.&mt('Frequency').'
(' - .&mt('click for example').')
'.&mt('Answer').''.&mt('Frequency').'
' + .'('.&mt('click for example').')
'. - join('',split("\0",$answer)). - ''.$frequency{$answer}. - '
' + .join('',split("\0",$answer)) + .'' + .''.$frequency{$answer}.'' + .'
'); + $request->print(&Apache::loncommon::end_data_table()); } } else { - $request->print('

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

'); + $request->print('

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

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

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

'); + $request->print('

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

' + ); } &Apache::lonhtmlcommon::Close_PrgWin($request,\%prog_state); &analyze_footer($request); @@ -907,72 +903,75 @@ sub editxmlmode { my $result; my $problem=&Apache::lonnet::getfile($file); if ($problem eq -1) { - &Apache::lonxml::error(" ".&mt('Unable to find'). - " $file"); + &Apache::lonxml::error( + '

' + .&mt('Unable to find [_1]', + ''.$file.'') + .'

'); + $problem=''; } - if (defined($env{'form.editxmltext'}) || defined($env{'form.Undo'})) { + + if (($env{'form.problemmode'} eq 'saveeditxml') || + ($env{'form.problemmode'} eq 'saveviewxml') || + ($env{'form.problemmode'} eq 'undoxml')) { my $error=&handle_save_or_undo($request,\$problem, \$env{'form.editxmltext'}); if (!$error) { $problem=&Apache::lonnet::getfile($file); } } &Apache::lonhomework::showhashsubset(\%env,'^form'); - if ( $env{'form.submitbutton'} eq &mt('Save and View') ) { + if ($env{'form.problemmode'} eq 'saveviewxml') { &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"). - ''. - &Apache::loncommon::help_open_menu(undef,undef,5,'Authoring'). - '
'; if ($cols > 80) { $cols = 80; } if ($cols < 70) { $cols = 70; } if ($rows < 20) { $rows = 20; } my $js = &Apache::edit::js_change_detection(). &Apache::loncommon::resize_textarea_js(). - &Apache::structuretags::setmode_javascript(); - my $only_body = ($env{'environment.remote'} eq 'off')? 0 : 1; + &Apache::structuretags::setmode_javascript(). + &Apache::lonhtmlcommon::dragmath_js("EditMathPopup"); + + # Breadcrumbs + my $brcrum = [{'href' => &Apache::loncommon::authorspace($request->uri), + 'text' => 'Construction Space'}, + {'href' => '', + 'text' => 'Problem Editing'}]; + my $start_page = &Apache::loncommon::start_page(&mt("EditXML [_1]",$file),$js, {'no_auto_mt_title' => 1, - 'only_body' => $only_body, + 'only_body' => 0, 'add_entries' => { 'onresize' => q[resize_textarea('LC_editxmltext','LC_aftertextarea')], 'onload' => q[resize_textarea('LC_editxmltext','LC_aftertextarea')], - }}); - $result.=$start_page. - &renderpage($request,$file,['no_output_web'],1). - '
'. &Apache::structuretags::remember_problem_state().'
-
- '.&mt('Problem Editing').&Apache::loncommon::help_open_topic('Problem_Editor_XML_Index').' +

'.&mt('Problem Editing').' '.&Apache::loncommon::help_open_topic('Problem_Editor_XML_Index').'

- '.&Apache::loncommon::helpLatexCheatsheet().' + '.&Apache::loncommon::helpLatexCheatsheet('Problem_LON-CAPA_Functions','Script Functions').'
-
- - - - -
-
- - -
-
- '.&Apache::lonxml::message_location().' -
- ' . ' - @@ -1015,9 +1014,10 @@ sub renderpage { $problem=''; my $filename=(split('/',$file))[-1]; my $error = - " ".&mt('Unable to find [_1]', - ' '.$filename.'') - .""; + '

' + .&mt('Unable to find [_1]', + ''.$filename.'') + ."

"; $result.= &Apache::loncommon::simple_error_page($request,'Not available', $error); @@ -1079,8 +1079,10 @@ sub get_template_list { } my @files = glob($Apache::lonnet::perlvar{'lonIncludes'}. '/templates/*.'.$glob_extension); - @files = map {[$_,&mt(&Apache::lonnet::metadata($_, 'title'))]} (@files); - @files = sort {$a->[1] cmp $b->[1]} (@files); + @files = map {[$_,&mt(&Apache::lonnet::metadata($_, 'title')), + (&Apache::lonnet::metadata($_, 'category')?&mt(&Apache::lonnet::metadata($_, 'category')):&mt('Miscellaneous')), + &mt(&Apache::lonnet::metadata($_, 'help'))]} (@files); + @files = sort {$a->[2].$a->[1] cmp $b->[2].$b->[1]} (@files); my ($midpoint,$seconddiv,$numfiles); $numfiles = 0; foreach my $file (@files) { @@ -1095,19 +1097,40 @@ sub get_template_list { } } my $count = 0; + my $currentcategory=''; + my $first = 1; + my $londocroot = $Apache::lonnet::perlvar{'lonDocRoot'}; foreach my $file (@files) { next if ($file->[1] !~ /\S/); + if ($file->[2] ne $currentcategory) { + $currentcategory=$file->[2]; + if ((!$seconddiv) && ($count >= $midpoint)) { + $result .= ''."\n".'
'."\n"; + $seconddiv = 1; + } elsif (!$first) { + $result.='
'."\n"; + } else { + $first = 0; + } + $result.= '
'."\n" + .'

'.$currentcategory.'

'."\n"; + $count++; + } $result .= '
'."\n"; - $count ++; - if ((!$seconddiv) && ($count >= $midpoint)) { - $result .= '
'."\n".'
'."\n"; - $seconddiv = 1; + $file->[1].''; + if ($file->[3]) { + $result.=&Apache::loncommon::help_open_topic($file->[3]); } + my $filename=$file->[0]; + $filename=~s{^\Q$londocroot\E}{}; + $result.=' ' + .&Apache::loncommon::modal_link($filename.'?inhibitmenu=yes',&mt('Example'),600,420,'sample') + .'
'."\n"; + $count ++; } if ($numfiles > 0) { - $result .= '
'."\n".''."\n"; + $result .= ''."\n".''."\n"; } return $result; } @@ -1136,31 +1159,36 @@ sub newproblem { &renderpage($request,$dest); } else { my $url=&HTML::Entities::encode($request->uri,'<>&"'); - my $shownurl=$url; - $shownurl=~s-^/~-/priv/-; my $dest = &Apache::lonnet::filelocation("",$request->uri); my $errormsg; my $instructions; + my $brcrum = [{'href' => &Apache::loncommon::authorspace($request->uri), + 'text' => 'Construction Space'}, + {'href' => '', + 'text' => "Create New $extension"}]; my $start_page = - &Apache::loncommon::start_page("Create New $extension"); - $request->print(" -$start_page -

".&mt("Creating a new $extension resource.")."

+ &Apache::loncommon::start_page("Create New $extension", + undef, + {'bread_crumbs' => $brcrum,}); + $request->print( + $start_page + .&Apache::loncommon::head_subbox( + &Apache::loncommon::CSTR_pageheader()) + .'

'.&mt("Creating a new $extension resource.")."

$errormsg ".&mt("The requested file [_1] currently does not exist.", - "$shownurl")." -

- -".&mt("To create a new $extension, select a template from the". - " list below. Then click on the \"Create $extension\" button.")." -

"); + ''.$url.'').' +

+'.&mt("To create a new $extension, select a template from the". + " list below. Then click on the \"Create $extension\" button.").' +

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

".&Apache::loncommon::end_page()); + $request->print('
'.&Apache::loncommon::end_page()); } return; } @@ -1173,11 +1201,11 @@ sub update_construct_style { && !defined($env{'form.newrandomization'})) { if ((!$env{'form.style_file'} && $env{'construct.style'}) ||$env{'form.clear_style_file'}) { - &Apache::lonnet::delenv('construct\\.style'); + &Apache::lonnet::delenv('construct.style'); } elsif ($env{'form.style_file'} && $env{'construct.style'} ne $env{'form.style_file'}) { - &Apache::lonnet::appenv('construct.style' => - $env{'form.style_file'}); + &Apache::lonnet::appenv({'construct.style' => + $env{'form.style_file'}}); } } } @@ -1220,7 +1248,10 @@ sub handler { #first visit to problem in construction space $env{'form.problemmode'}= 'view'; &renderpage($request,$file); - } elsif ($env{'form.problemmode'} eq 'editxml') { + } elsif (($env{'form.problemmode'} eq 'editxml') || + ($env{'form.problemmode'} eq 'saveeditxml') || + ($env{'form.problemmode'} eq 'saveviewxml') || + ($env{'form.problemmode'} eq 'undoxml')) { &editxmlmode($request,$file); } elsif ($env{'form.problemmode'} eq 'calcanswers') { &analyze($request,$file);