--- loncom/homework/lonhomework.pm 2003/05/16 17:41:17 1.128 +++ loncom/homework/lonhomework.pm 2017/12/22 02:00:39 1.369 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Homework handler # -# $Id: lonhomework.pm,v 1.128 2003/05/16 17:41:17 albertel Exp $ +# $Id: lonhomework.pm,v 1.369 2017/12/22 02:00:39 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -24,17 +24,13 @@ # /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; use Apache::style(); use Apache::lonxml(); -use Apache::lonnet(); +use Apache::lonnet; use Apache::lonplot(); use Apache::inputtags(); use Apache::structuretags(); @@ -50,207 +46,694 @@ use Apache::essayresponse(); use Apache::externalresponse(); use Apache::rankresponse(); use Apache::matchresponse(); -#use Apache::chemresponse(); +use Apache::chemresponse(); +use Apache::functionplotresponse(); +use Apache::drawimage(); +use Apache::loncapamath(); use Apache::Constants qw(:common); -use HTML::Entities(); use Apache::loncommon(); -#use Time::HiRes qw( gettimeofday tv_interval ); +use Apache::lonparmset(); +use Apache::lonlocal; +use Time::HiRes qw( gettimeofday tv_interval ); +use HTML::Entities(); +use File::Copy(); + +# FIXME - improve commenting + BEGIN { - &Apache::lonxml::register_insert(); + &Apache::lonxml::register_insert(); +} + + +=pod + +=item set_bubble_lines() + +Called at analysis time to set the bubble lines +hash for the problem.. This should be called in the +end_problemtype tag in analysis mode. + +We fetch the hash of part id counters from lonxml + and push them into analyze:{part_id.bubble_lines}. + +=cut + +sub set_bubble_lines { + my %bubble_counters = &Apache::lonxml::get_bubble_line_hash(); + + foreach my $key (keys(%bubble_counters)) { + $Apache::lonhomework::analyze{"$key.bubble_lines"} = + $bubble_counters{"$key"}; + } } +# +# Decides what targets to render for. +# Implicit inputs: +# Various session environment variables: +# request.state - published - is a /res/ resource +# uploaded - is a /uploaded/ resource +# contruct - is a /priv/ resource +# form.grade_target - a form parameter requesting a specific target sub get_target { - if (($ENV{'request.state'} eq "published") || - ($ENV{'request.state'} eq "uploaded")) { - if ( defined($ENV{'form.grade_target'} ) - && ($ENV{'form.grade_target'} eq 'tex')) { - return ($ENV{'form.grade_target'}); - } elsif ( defined($ENV{'form.grade_target'} ) - && ($Apache::lonhomework::viewgrades eq 'F' )) { - return ($ENV{'form.grade_target'}); - } - - if ( defined($ENV{'form.submitted'}) && - ( !defined($ENV{'form.resetdata'}))) { - return ('grade', 'web'); + &Apache::lonxml::debug("request.state = $env{'request.state'}"); + if( defined($env{'form.grade_target'})) { + &Apache::lonxml::debug("form.grade_target= $env{'form.grade_target'}"); } else { - return ('web'); + &Apache::lonxml::debug("form.grade_target "); } - } elsif ($ENV{'request.state'} eq "construct") { - if ( defined($ENV{'form.grade_target'}) ) { - return ($ENV{'form.grade_target'}); - } - if ( defined($ENV{'form.preview'})) { - if ( defined($ENV{'form.submitted'})) { - return ('grade', 'web'); - } else { - return ('web'); - } - } else { - if ( $ENV{'form.problemmode'} eq 'View' || - $ENV{'form.problemmode'} eq 'Discard Edits and View') { - if ( defined($ENV{'form.submitted'}) && - (!defined($ENV{'form.resetdata'})) ) { - return ('grade', 'web','answer'); - } else { - return ('web','answer'); - } - } elsif ( $ENV{'form.problemmode'} eq 'Edit' ) { - if ( $ENV{'form.submitted'} eq 'edit' ) { - if ( $ENV{'form.submit'} eq 'Submit Changes and View' ) { - return ('modified','web','answer'); - } else { - return ('modified','edit'); - } - } else { - return ('edit'); - } - } else { - return ('web'); - } + if (($env{'request.state'} eq "published") || + ($env{'request.state'} eq "uploaded")) { + if ( defined($env{'form.grade_target'} ) + && ($env{'form.grade_target'} eq 'tex')) { + return ($env{'form.grade_target'}); + } elsif ( defined($env{'form.grade_target'} ) + && ($Apache::lonhomework::viewgrades eq 'F' )) { + return ($env{'form.grade_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' )) { + return ('grade','webgrade'); + } + if ( defined($env{'form.submitted'}) && + ( !defined($env{'form.newrandomization'}))) { + return ('grade', 'web'); + } else { + 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') { + return ('grade','webgrade','answer'); + } 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 Authoring Space +# } - } - return (); +# +# Huh? We are nowhere, so do nothing. +# + return (); } sub setup_vars { - my ($target) = @_; - return ';' + my ($target) = @_; + return ';' # return ';$external::target='.$target.';'; } -sub send_header { - my ($request)= @_; - $request->print(&Apache::lontexconvert::header()); -# $request->print('
'); +sub proctor_checked_in { + my ($slot_name,$slot,$type)=@_; + my @possible_proctors=split(",",$slot->{'proctor'}); + + return 1 if (!@possible_proctors); + + my $key; + if ($type eq 'Task') { + my $version=$Apache::lonhomework::history{'resource.0.version'}; + $key ="resource.$version.0.checkedin"; + } elsif (($type eq 'problem') || ($type eq 'tool')) { + $key ='resource.0.checkedin'; + } + # backward compatability, used to be username@domain, + # now is username:domain + my $who = $Apache::lonhomework::history{$key}; + if ($who !~ /:/) { + $who =~ tr/@/:/; + } + foreach my $possible (@possible_proctors) { + if ($who eq $possible + && $Apache::lonhomework::history{$key.'.slot'} eq $slot_name) { + return 1; + } + } + return 0; } -sub createmenu { - my ($which,$request)=@_; - if ($which eq 'grade') { - $request->print(''); - } -} +sub check_slot_access { + my ($id,$type,$symb,$partlist)=@_; -sub send_footer { - my ($request)= @_; -# $request->print('
'); - $request->print(&Apache::lontexconvert::footer()); -} + # does it pass normal muster + my ($status,$datemsg)=&check_access($id,$symb); + + my $useslots = &Apache::lonnet::EXT("resource.0.useslots",$symb); + if ($useslots ne 'resource' && $useslots ne 'map' + && $useslots ne 'map_map') { + return ($status,$datemsg); + } + + my $checkin = 'resource.0.checkedin'; + my $version; + if ($type eq 'Task') { + $version=$Apache::lonhomework::history{'resource.version'}; + $checkin = "resource.$version.0.checkedin"; + } + my $checkedin = $Apache::lonhomework::history{$checkin}; + my ($returned_slot,$slot_name,$checkinslot,$ipused,$blockip,$now,$ip, + $consumed_uniq); + $now = time; + $ip=$ENV{'REMOTE_ADDR'} || $env{'request.host'}; + + if ($checkedin) { + $checkinslot = $Apache::lonhomework::history{"$checkin.slot"}; + my %slot=&Apache::lonnet::get_slot($checkinslot); + $consumed_uniq = $slot{'uniqueperiod'}; + if ($slot{'iptied'}) { + $ipused = $Apache::lonhomework::history{"$checkin.ip"}; + unless (($ip ne '') && ($ipused eq $ip)) { + $blockip = $slot{'iptied'}; + $slot_name = $checkinslot; + $returned_slot = \%slot; + } + } + } + + if ($status eq 'SHOW_ANSWER') { + if ($blockip eq 'answer') { + return ('NEED_DIFFERENT_IP','',$slot_name,$returned_slot,$ipused); + } else { + return ($status,$datemsg); + } + } + + if ($status eq 'CLOSED' || + $status eq 'INVALID_ACCESS' || + $status eq 'UNAVAILABLE') { + return ($status,$datemsg); + } + if ($env{'request.state'} eq "construct") { + return ($status,$datemsg); + } + + if ($type eq 'Task') { + if ($checkedin && + $Apache::lonhomework::history{"resource.$version.0.status"} eq 'pass') { + if ($blockip eq 'answer') { + return ('NEED_DIFFERENT_IP','',$slot_name,$returned_slot,$ipused); + } else { + return ('SHOW_ANSWER'); + } + } + } -$Apache::lonxml::browse=''; + my $availablestudent = &Apache::lonnet::EXT("resource.0.availablestudent",$symb); + my $available = &Apache::lonnet::EXT("resource.0.available",$symb); + my @slots= (split(':',$availablestudent),split(':',$available)); + +# if (!@slots) { +# return ($status,$datemsg); +# } + undef($returned_slot); + undef($slot_name); + my $slotstatus='NOT_IN_A_SLOT'; + my $num_usable_slots = 0; + if (!$symb) { + ($symb) = &Apache::lonnet::whichuser(); + } + foreach my $slot (@slots) { + $slot =~ s/(^\s*|\s*$)//g; + &Apache::lonxml::debug("getting $slot"); + my %slot=&Apache::lonnet::get_slot($slot); + &Apache::lonhomework::showhash(%slot); + next if ($slot{'endtime'} < $now); + $num_usable_slots ++; + if ($slot{'starttime'} < $now && + $slot{'endtime'} > $now && + &Apache::loncommon::check_ip_acc($slot{'ip'})) { + if ($slot{'iptied'}) { + if ($env{'request.course.id'}) { + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + if ($slot eq $checkinslot) { + if ($ip eq $ipused) { + &Apache::lonxml::debug("$slot is good"); + $slotstatus ='NEEDS_CHECKIN'; + } else { + $slotstatus = 'NEED_DIFFERENT_IP'; + $slot_name = $slot; + $returned_slot = \%slot; + last; + } + } elsif ($ip) { + my $uniqkey = "$slot\0$symb\0$ip"; + my %used_ip = &Apache::lonnet::get('slot_uniqueips',[$uniqkey],$cdom,$cnum); + if ($used_ip{$uniqkey}) { + $slotstatus = 'NEED_DIFFERENT_IP'; + } else { + &Apache::lonxml::debug("$slot is good"); + $slotstatus ='NEEDS_CHECKIN'; + } + } + } + } else { + &Apache::lonxml::debug("$slot is good"); + $slotstatus='NEEDS_CHECKIN'; + } + if ($slotstatus eq 'NEEDS_CHECKIN') { + $returned_slot=\%slot; + $slot_name=$slot; + last; + } + } + } + if ($slotstatus eq 'NEEDS_CHECKIN' && + &proctor_checked_in($slot_name,$returned_slot,$type)) { + &Apache::lonxml::debug("proctor checked in"); + $slotstatus=$status; + } + + my ($is_correct,$got_grade); + if ($type eq 'Task') { + my $version=$Apache::lonhomework::history{'resource.0.version'}; + $got_grade = + ($Apache::lonhomework::history{"resource.$version.0.status"} + =~ /^(?:pass|fail)$/); + $is_correct = + ($Apache::lonhomework::history{"resource.$version.0.status"} eq 'pass' + || $Apache::lonhomework::history{"resource.0.solved"} =~ /^correct_/ ); + } elsif (($type eq 'problem') || ($type eq 'tool')) { + if ((ref($partlist) eq 'ARRAY') && (@{$partlist} > 0)) { + my ($numcorrect,$numgraded) = (0,0); + foreach my $part (@{$partlist}) { + my $currtries = $Apache::lonhomework::history{"resource.$part.tries"}; + my $maxtries = &Apache::lonnet::EXT("resource.$part.maxtries",$symb); + my $probstatus = &Apache::structuretags::get_problem_status($part); + my $earlyout; + unless (($probstatus eq 'no') || + ($probstatus eq 'no_feedback_ever')) { + if ($Apache::lonhomework::history{"resource.$part.solved"} =~/^correct_/) { + $numcorrect ++; + } else { + $earlyout = 1; + } + } + if (($currtries == $maxtries) || ($is_correct)) { + $earlyout = 1; + } else { + $numgraded ++; + } + last if ($earlyout); + } + my $numparts = scalar(@{$partlist}); + if ($numparts == $numcorrect) { + $is_correct = 1; + } + if ($numparts == $numgraded) { + $got_grade = 1; + } + } else { + my $currtries = $Apache::lonhomework::history{"resource.0.tries"}; + my $maxtries = &Apache::lonnet::EXT("resource.0.maxtries",$symb); + my $probstatus = &Apache::structuretags::get_problem_status('0'); + unless (($probstatus eq 'no') || + ($probstatus eq 'no_feedback_ever')) { + $is_correct = + ($Apache::lonhomework::history{"resource.0.solved"} =~/^correct_/); + } + unless (($currtries == $maxtries) || ($is_correct)) { + $got_grade = 1; + } + } + } + + &Apache::lonxml::debug(" slot is $slotstatus checkedin ($checkedin) got_grade ($got_grade) is_correct ($is_correct)"); + + # no slot is currently open, and has been checked in for this version + # but hasn't got a grade, therefore must be awaiting a grade + if (!defined($slot_name) + && $checkedin + && !$got_grade) { + return ('WAITING_FOR_GRADE'); + } + + # 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 one of: NOT_IN_A_SLOT, RESERVABLE, RESERVABLE_LATER, or NOTRESERVABLE). + if (!defined($slot_name) && (($type eq 'problem') || ($type eq 'tool'))) { + if ($slotstatus eq 'NOT_IN_A_SLOT') { + if (!$num_usable_slots) { + if ($env{'request.course.id'}) { + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'}; + unless ($symb) { + ($symb)=&Apache::lonnet::whichuser(); + } + $slotstatus = 'NOTRESERVABLE'; + my ($reservable_now_order,$reservable_now,$reservable_future_order, + $reservable_future) = + &Apache::loncommon::get_future_slots($cnum,$cdom,$now,$symb); + if ((ref($reservable_now_order) eq 'ARRAY') && (ref($reservable_now) eq 'HASH')) { + if (@{$reservable_now_order} > 0) { + if ((!$checkedin) || (ref($consumed_uniq) ne 'ARRAY')) { + $slotstatus = 'RESERVABLE'; + $datemsg = $reservable_now->{$reservable_now_order->[-1]}{'endreserve'}; + } else { + my ($uniqstart,$uniqend,$useslot); + if (ref($consumed_uniq) eq 'ARRAY') { + ($uniqstart,$uniqend)=@{$consumed_uniq}; + } + foreach my $slot (reverse(@{$reservable_now_order})) { + if ($reservable_now->{$slot}{'uniqueperiod'} =~ /^(\d+)\,(\d+)$/) { + my ($new_uniq_start,$new_uniq_end) = ($1,$2); + next if (! + ($uniqstart < $new_uniq_start && $uniqend < $new_uniq_start) || + ($uniqstart > $new_uniq_end && $uniqend > $new_uniq_end )); + } + $useslot = $slot; + last; + } + if ($useslot) { + $slotstatus = 'RESERVABLE'; + $datemsg = $reservable_now->{$useslot}{'endreserve'}; + } + } + } + } + unless ($slotstatus eq 'RESERVABLE') { + if ((ref($reservable_future_order) eq 'ARRAY') && (ref($reservable_future) eq 'HASH')) { + if (@{$reservable_future_order} > 0) { + if ((!$checkedin) || (ref($consumed_uniq) ne 'ARRAY')) { + $slotstatus = 'RESERVABLE_LATER'; + $datemsg = $reservable_future->{$reservable_future_order->[0]}{'startreserve'}; + } else { + my ($uniqstart,$uniqend,$useslot); + if (ref($consumed_uniq) eq 'ARRAY') { + ($uniqstart,$uniqend)=@{$consumed_uniq}; + } + foreach my $slot (@{$reservable_future_order}) { + if ($reservable_future->{$slot}{'uniqueperiod'} =~ /^(\d+),(\d+)$/) { + my ($new_uniq_start,$new_uniq_end) = ($1,$2); + next if (! + ($uniqstart < $new_uniq_start && $uniqend < $new_uniq_start) || + ($uniqstart > $new_uniq_end && $uniqend > $new_uniq_end )); + } + $useslot = $slot; + last; + } + if ($useslot) { + $slotstatus = 'RESERVABLE_LATER'; + $datemsg = $reservable_future->{$useslot}{'startreserve'}; + } + } + } + } + } + } + } + } + return ($slotstatus,$datemsg); + } + + if ($slotstatus eq 'NOT_IN_A_SLOT' + && $checkedin ) { + + if ($got_grade) { + if ($blockip eq 'answer') { + return ('NEED_DIFFERENT_IP','',$slot_name,$returned_slot,$ipused); + } else { + return ('SHOW_ANSWER'); + } + } else { + return ('WAITING_FOR_GRADE'); + } + + } + + if (($is_correct) && ($blockip ne 'answer')) { + if (($type eq 'problem') || ($type eq 'tool')) { + return ($status); + } + return ('SHOW_ANSWER'); + } + + if ( $status eq 'CANNOT_ANSWER' && + ($slotstatus ne 'NEEDS_CHECKIN' && $slotstatus ne 'NOT_IN_A_SLOT' && + $slotstatus ne 'NEED_DIFFERENT_IP') ) { + return ($status,$datemsg); + } + return ($slotstatus,$datemsg,$slot_name,$returned_slot,$ipused); +} # JB, 9/24/2002: Any changes in this function may require a change # in lonnavmaps::resource::getDateStatus. sub check_access { - my ($id) = @_; - my $date =''; - my $status; - my $datemsg = ''; - my $lastdate = ''; - my $temp; - my $type; - my $passed; - - if ($ENV{'request.state'} eq "construct") { - &Apache::lonxml::debug("in construction ignoring dates"); - $status='CAN_ANSWER'; - $datemsg='is in under construction'; - return ($status,$datemsg); - } + my ($id,$symb) = @_; + my $date =''; + my $status; + my $datemsg = ''; + my $lastdate = ''; + my $type; + 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' + && &hide_problem_status())) { + 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=&mt('is in under construction'); +# return ($status,$datemsg); + } + + &Apache::lonxml::debug("checking for part :$id:"); + &Apache::lonxml::debug("time:".time); + + unless ($symb) { + ($symb)=&Apache::lonnet::whichuser(); + } + &Apache::lonxml::debug("symb:".$symb); + #if ($env{'request.state'} ne "construct" && $symb ne '') { + if ($env{'request.state'} ne "construct") { + my $idacc = &Apache::lonnet::EXT("resource.$id.acc",$symb); + 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') { + $date = &due_date($id,$symb); + } else { + $date = &Apache::lonnet::EXT("resource.$id.$temp",$symb); + } + + my $thistype = &Apache::lonnet::EXT("resource.$id.$temp.type",$symb); + 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",$symb)-$date; + } + if ($temp eq 'answerdate') { + $date=&Apache::lonnet::EXT("resource.$id.duedate",$symb)+$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 = &Apache::lonlocal::locallocaltime($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 = &mt('will open on [_1]',$date); + } elsif ($type eq 'duedate') { + $status='CAN_ANSWER'; + $datemsg = &mt('is due at [_1]',$date); + } elsif ($type eq 'answerdate') { + $status='CLOSED'; + $datemsg = &mt('was due on [_1], and answers will be available on [_2]', + $lastdate,$date); + } + } + if ($status eq 'CAN_ANSWER' || + (($Apache::lonhomework::browse eq 'F') && ($status eq 'CLOSED'))) { + #check #tries, and if correct. + my $tries = $Apache::lonhomework::history{"resource.$id.tries"}; + my $maxtries = &Apache::lonnet::EXT("resource.$id.maxtries",$symb); + if ( $tries eq '' ) { $tries = '0'; } + 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/) + && (&show_problem_status()) ) { + if (($Apache::lonhomework::history{"resource.$id.awarded"} >= 1) || + (&Apache::lonnet::EXT("resource.$id.retrypartial",$symb) !~/^1|on|yes$/i)) { + $status = 'CANNOT_ANSWER'; + } + } elsif ($Apache::lonhomework::history{"resource.$id.solved"}=~/^excused/) { + $status = 'CANNOT_ANSWER'; + } + if ($status eq 'CANNOT_ANSWER' + && &show_answer_problem_status()) { + $status = 'SHOW_ANSWER'; + } + } + if ($status eq 'CAN_ANSWER' || $status eq 'CANNOT_ANSWER') { + my @interval=&Apache::lonnet::EXT("resource.$id.interval",$symb); + &Apache::lonxml::debug("looking for interval @interval"); + if ($interval[0]=~ /^\d+/) { + my $first_access=&Apache::lonnet::get_first_access($interval[1],$symb); + &Apache::lonxml::debug("looking for accesstime $first_access"); + if (!$first_access) { + $status='NOT_YET_VIEWED'; + my $due_date = &due_date($id,$symb); + my $seconds_left = $due_date - time; + my ($timelimit) = ($interval[0] =~ /^(\d+)/); + if ($seconds_left > $timelimit || $due_date eq '') { + $seconds_left = $timelimit; + } + $datemsg=&seconds_to_human_length($seconds_left); + } + } + } - &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."; - 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 = "an unknown date"; $passed = 0; - } elsif ($date eq 'con_lost') { - $date = "an indeterminate date"; $passed = 0; + #if (($status ne 'CLOSED') && ($Apache::lonhomework::type eq 'exam') && + # (!$Apache::lonhomework::history{"resource.0.outtoken"})) { + # return ('UNCHECKEDOUT','needs to be checked out'); + #} + + &Apache::lonxml::debug("sending back :$status:$datemsg:"); + 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=&mt('is closed but you are allowed to view it'); + } + + return ($status,$datemsg); +} +# this should work exactly like the copy in lonnavmaps.pm +sub due_date { + my ($part_id,$symb,$udom,$uname)=@_; + my $date; + my @interval= &Apache::lonnet::EXT("resource.$part_id.interval",$symb, + $udom,$uname); + &Apache::lonxml::debug("looking for interval $part_id $symb @interval"); + my $due_date= &Apache::lonnet::EXT("resource.$part_id.duedate",$symb, + $udom,$uname); + &Apache::lonxml::debug("looking for due_date $part_id $symb $due_date"); + if ($interval[0] =~ /\d+/) { + my $first_access=&Apache::lonnet::get_first_access($interval[1],$symb); + &Apache::lonxml::debug("looking for first_access $first_access ($interval[1])"); + if (defined($first_access)) { + my ($timelimit) = ($interval[0] =~ /^(\d+)/); + my $interval = $first_access+$timelimit; + $date = (!$due_date || $interval < $due_date) ? $interval + : $due_date; + } else { + $date = $due_date; + } } else { - if (time < $date) { $passed = 0; } else { $passed = 1; } - $date = localtime $date; + $date = $due_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($Apache::lonhomework::history{"resource.$id.solved"}=~/^correct/ - && lc($Apache::lonhomework::problemstatus) ne 'no') { - $status = 'CANNOT_ANSWER'; - } - } - - if (($status ne 'CLOSED') && ($Apache::lonhomework::type eq 'exam') && - (!$Apache::lonhomework::history{"resource.0.outtoken"})) { - return ('UNCHECKEDOUT','needs to be checked out'); - } - - - &Apache::lonxml::debug("sending back :$status:$datemsg:"); - 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'; - } + return $date; +} + +sub seconds_to_human_length { + my ($length)=@_; - return ($status,$datemsg); + my $seconds=$length%60; $length=int($length/60); + my $minutes=$length%60; $length=int($length/60); + my $hours=$length%24; $length=int($length/24); + my $days=$length; + + my $timestr; + if ($days > 0) { $timestr.=&mt('[quant,_1,day]',$days); } + if ($hours > 0) { $timestr.=($timestr?", ":""). + &mt('[quant,_1,hour]',$hours); } + if ($minutes > 0) { $timestr.=($timestr?", ":""). + &mt('[quant,_1,minute]',$minutes); } + if ($seconds > 0) { $timestr.=($timestr?", ":""). + &mt('[quant,_1,second]',$seconds); } + return $timestr; } sub showhash { - my (%hash) = @_; - &showhashsubset(\%hash,'.'); - return ''; + my (%hash) = @_; + &showhashsubset(\%hash,'.'); + return ''; } sub showarray { my ($array)=@_; my $string="("; foreach my $elm (@{ $array }) { - if (ref($elm)) { - if ($elm =~ /ARRAY/ ) { - $string.=&showarray($elm); - } + if (ref($elm) eq 'ARRAY') { + $string.=&showarray($elm); + } elsif (ref($elm) eq 'HASH') { + $string.= "HASH --- \n
"; + $string.= &showhashsubset($elm,'.'); } else { $string.="$elm," } @@ -261,119 +744,211 @@ sub showarray { } sub showhashsubset { - my ($hash,$keyre) = @_; - my $resultkey; - foreach $resultkey (sort keys %$hash) { - if ($resultkey =~ /$keyre/) { - if (ref($$hash{$resultkey})) { - if ($$hash{$resultkey} =~ /ARRAY/ ) { + my ($hash,$keyre) = @_; + my $resultkey; + foreach $resultkey (sort(keys(%$hash))) { + if ($resultkey !~ /$keyre/) { next; } + if (ref($$hash{$resultkey}) eq 'ARRAY' ) { &Apache::lonxml::debug("$resultkey ---- ". &showarray($$hash{$resultkey})); - } elsif ($$hash{$resultkey} =~ /HASH/ ) { + } elsif (ref($$hash{$resultkey}) eq 'HASH' ) { &Apache::lonxml::debug("$resultkey ---- $$hash{$resultkey}"); &showhashsubset($$hash{$resultkey},'.'); } else { &Apache::lonxml::debug("$resultkey ---- $$hash{$resultkey}"); } - } else { - &Apache::lonxml::debug("$resultkey ---- $$hash{$resultkey}"); - } - } - } - &Apache::lonxml::debug("\n
restored values^
\n"); - return ''; + } + &Apache::lonxml::debug("\n
restored values^
\n"); + return ''; } sub setuppermissions { - $Apache::lonhomework::browse= &Apache::lonnet::allowed('bre',$ENV{'request.filename'}); - my $viewgrades = &Apache::lonnet::allowed('vgr',$ENV{'request.course.id'}); - if (! $viewgrades && - exists($ENV{'request.course.sec'}) && - $ENV{'request.course.sec'} !~ /^\s*$/) { - $viewgrades = &Apache::lonnet::allowed('vgr',$ENV{'request.course.id'}. - '/'.$ENV{'request.course.sec'}); - } - $Apache::lonhomework::viewgrades = $viewgrades; - return '' + $Apache::lonhomework::browse= &Apache::lonnet::allowed('bre',$env{'request.filename'}); + unless ($Apache::lonhomework::browse eq 'F') { + $Apache::lonhomework::browse=&Apache::lonnet::allowed('bro',$env{'request.filename'}); + } + my $viewgrades = &Apache::lonnet::allowed('vgr',$env{'request.course.id'}); + if (! $viewgrades && + exists($env{'request.course.sec'}) && + $env{'request.course.sec'} !~ /^\s*$/) { + $viewgrades = &Apache::lonnet::allowed('vgr',$env{'request.course.id'}. + '/'.$env{'request.course.sec'}); + } + $Apache::lonhomework::viewgrades = $viewgrades; + + if ($Apache::lonhomework::browse eq 'F' && + $env{'form.devalidatecourseresdata'} eq 'on') { + my (undef,$courseid) = &Apache::lonnet::whichuser(); + &Apache::lonnet::devalidatecourseresdata($env{"course.$courseid.num"}, + $env{"course.$courseid.domain"}); + } + + my $modifygrades = &Apache::lonnet::allowed('mgr',$env{'request.course.id'}); + if (! $modifygrades && + exists($env{'request.course.sec'}) && + $env{'request.course.sec'} !~ /^\s*$/) { + $modifygrades = + &Apache::lonnet::allowed('mgr',$env{'request.course.id'}. + '/'.$env{'request.course.sec'}); + } + $Apache::lonhomework::modifygrades = $modifygrades; + + my $queuegrade = &Apache::lonnet::allowed('mqg',$env{'request.course.id'}); + if (! $queuegrade && + exists($env{'request.course.sec'}) && + $env{'request.course.sec'} !~ /^\s*$/) { + $queuegrade = + &Apache::lonnet::allowed('qgr',$env{'request.course.id'}. + '/'.$env{'request.course.sec'}); + } + $Apache::lonhomework::queuegrade = $queuegrade; + return ''; +} + +sub unset_permissions { + undef($Apache::lonhomework::queuegrade); + undef($Apache::lonhomework::modifygrades); + undef($Apache::lonhomework::viewgrades); + undef($Apache::lonhomework::browse); } sub setupheader { my $request=$_[0]; - if ($ENV{'browser.mathml'}) { - $request->content_type('text/xml'); - } else { - $request->content_type('text/html'); - } - $request->content_encoding('UTF-8'); + &Apache::loncommon::content_type($request,'text/html'); if (!$Apache::lonxml::debug && ($ENV{'REQUEST_METHOD'} eq 'GET')) { &Apache::loncommon::no_cache($request); } +# $request->set_last_modified(&Apache::lonnet::metadata($request->uri, +# 'lastrevisiondate')); $request->send_http_header; return OK if $request->header_only; return '' } 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; + my ($request,$problem,$result,$getobjref) = @_; - if ($ENV{'form.Undo'} eq 'undo') { + my $file = &Apache::lonnet::filelocation("",$request->uri); + my $filebak =$file.".bak"; + my $filetmp =$file.".tmp"; 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

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

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

"); - $error=1; - } - } else { - my $fs=Apache::File->new(">$filebak"); - if (defined($fs)) { - print $fs $$problem; - $request->print("Making Backup to $filebak
"); - } else { - $request->print("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
"); + 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; } + if ((!$error) && (!&File::Copy::move($filetmp,$filebak))) { $error=1; } + if (!$error) { + &Apache::lonxml::info("

". + &mt("Undid changes, Switched [_1] and [_2]", + ''.$filebak. + '', + ''.$file. + '')."

"); + } else { + &Apache::lonxml::info("

". + &mt("Unable to undo, unable to switch [_1] and [_2]", + ''. + $filebak.'', + ''. + $file.'')."

"); + $error=1; + } } else { - $request->print("Unable to write to $file"); - $error|=4; + &Apache::lonnet::correct_line_ends($result); + + my $fs=Apache::File->new(">$filebak"); + if (defined($fs)) { + print $fs $$problem; + } else { + &Apache::lonxml::info("". + &mt("Unable to make backup [_1]", + ''. + $filebak.'').""); + $error=2; + } + my $fh=Apache::File->new(">$file"); + if (defined($fh)) { + print $fh $$result; + if (ref($getobjref) eq 'SCALAR') { + if ($file =~ m{([^/]+)\.(html?)$}) { + my $fname = $1; + my $ext = $2; + my $path = $file; + $path =~ s/\Q$fname\E\.\Q$ext\E$//; + my (%allfiles,%codebase); + &Apache::lonnet::extract_embedded_items($file,\%allfiles, + \%codebase,$result); + if (keys(%allfiles) > 0) { + my $url = $request->uri; + my $state = < + +STATE + $$getobjref = "

".&mt("Reference Warning")."

". + "

".&mt("Completed upload of the file. This file contained references to other files.")."

". + "

".&mt("Please select the locations from which the referenced files are to be uploaded.")."

". + &Apache::loncommon::ask_for_embedded_content($url,$state,\%allfiles,\%codebase, + {'error_on_invalid_names' => 1, + 'ignore_remote_references' => 1,}); + } + } + } + } else { + &Apache::lonxml::info(''. + &mt("Unable to write to [_1]", + ''. + $file.''). + ''); + $error|=4; + } } - } - return $error; + return $error; } sub analyze_header { my ($request) = @_; - my $result.=' - Analyzing a problem - -
- - + my $js = &Apache::structuretags::setmode_javascript(); + + # Breadcrumbs + my $brcrum = [{'href' => &Apache::loncommon::authorspace($request->uri), + 'text' => 'Authoring 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::structuretags::remember_problem_state().' +
+ +
- +
- List of possible answers: - '; +
' + .&Apache::lonxml::message_location(). + ''; + &Apache::lonxml::add_messages(\$result); $request->print($result); $request->rflush(); } sub analyze_footer { my ($request) = @_; - my $result=''; - $request->print($result); + $request->print(&Apache::loncommon::end_page()); $request->rflush(); } @@ -382,24 +957,23 @@ sub analyze { &Apache::lonxml::debug("Analyze"); my $result; my %overall; + my %seedexample; my %allparts; - my $rndseed=$ENV{'form.rndseed'}; + my $rndseed=$env{'form.rndseed'}; &analyze_header($request); my %prog_state= - &Apache::lonhtmlcommon::Create_PrgWin($request,'Analyze Progress', - 'Getting Problem Variants', - $ENV{'form.numtoanalyze'}); - for(my $i=1;$i<$ENV{'form.numtoanalyze'}+1;$i++) { - &Apache::lonhtmlcommon::Increment_PrgWin($request,\%prog_state, - 'last problem'); + &Apache::lonhtmlcommon::Create_PrgWin($request,$env{'form.numtoanalyze'}); + for(my $i=1;$i<$env{'form.numtoanalyze'}+1;$i++) { + &Apache::lonhtmlcommon::Increment_PrgWin($request,\%prog_state,'last problem'); + if (&Apache::loncommon::connection_aborted($request)) { return; } + my $thisseed=$i+$rndseed; my $subresult=&Apache::lonnet::ssi($request->uri, ('grade_target' => 'analyze'), - ('rndseed' => $i)); - &Apache::lonxml::debug(":$subresult:"); + ('rndseed' => $thisseed)); (my $garbage,$subresult)=split(/_HASH_REF__/,$subresult,2); my %analyze=&Apache::lonnet::str2hash($subresult); my @parts; - if (defined(@{ $analyze{'parts'} })) { + if (ref($analyze{'parts'}) eq 'ARRAY') { @parts=@{ $analyze{'parts'} }; } foreach my $part (@parts) { @@ -407,279 +981,871 @@ sub analyze { if ($analyze{$part.'.type'} eq 'numericalresponse' || $analyze{$part.'.type'} eq 'stringresponse' || $analyze{$part.'.type'} eq 'formularesponse' ) { - push( @{ $overall{$part.'.answer'} }, - [@{ $analyze{$part.'.answer'} }]); + foreach my $name (keys(%{ $analyze{$part.'.answer'} })) { + my $i=0; + foreach my $answer_part (@{ $analyze{$part.'.answer'}{$name} }) { + push( @{ $overall{$part.'.answer'}[$i] }, + $answer_part); + my $concatanswer= join("\0",@{ $answer_part }); + if (($concatanswer eq '') || ($concatanswer=~/^\@/)) { + $answer_part = [''.&mt('Error').'']; + } + $seedexample{join("\0",$part,$i,@{$answer_part})}= + $thisseed; + $i++; + } + } + if (!keys(%{ $analyze{$part.'.answer'} })) { + my $answer_part = + [''.&mt('Error').'']; + $seedexample{join("\0",$part,0,@{$answer_part})}= + $thisseed; + push( @{ $overall{$part.'.answer'}[0] }, + $answer_part); + } } } } - &Apache::lonhtmlcommon::Update_PrgWin($request,\%prog_state, - 'Analyzing Results'); - foreach my $part (keys(%allparts)) { - if (defined(@{ $overall{$part.'.answer'} })) { - $request->print(''); - foreach my $answer (sort {$a->[0] <=> $b->[0]} (@{ $overall{$part.'.answer'} })) { - $request->print(''); + &Apache::lonhtmlcommon::Update_PrgWin($request,\%prog_state,&mt('Analyzing Results')); + $request->print('
' + .'

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

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

Part '.$part. - ' is not analyzabale 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.') + .'

' + ); + } &Apache::lonhtmlcommon::Close_PrgWin($request,\%prog_state); &analyze_footer($request); &Apache::lonhomework::showhash(%overall); return $result; } +{ + my $show_problem_status; + sub reset_show_problem_status { + undef($show_problem_status); + } + + sub set_show_problem_status { + my ($new_status) = @_; + $show_problem_status = lc($new_status); + } + + sub hide_problem_status { + return ($show_problem_status eq 'no' + || $show_problem_status eq 'no_feedback_ever'); + } + + sub show_problem_status { + return ($show_problem_status eq 'yes' + || $show_problem_status eq 'answer' + || $show_problem_status eq ''); + } + + sub show_some_problem_status { + return ($show_problem_status eq 'no'); + } + + sub show_no_problem_status { + return ($show_problem_status eq 'no_feedback_ever'); + } + + sub show_answer_problem_status { + return ($show_problem_status eq 'answer'); + } +} + sub editxmlmode { - my ($request,$file) = @_; - my $result; - my $problem=&Apache::lonnet::getfile($file); - if ($problem eq -1) { - &Apache::lonxml::error(" Unable to find $file"); - $problem=''; - } - if (defined($ENV{'form.editxmltext'}) || defined($ENV{'form.Undo'})) { - 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.submit'} eq '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::help_open_topic("Problem_Editor_XML_Index",'Problem Editing Help') - .''. - &Apache::loncommon::help_open_topic("Greek_Symbols",'Greek Symbols', - undef,undef,600) - .''. - &Apache::loncommon::help_open_topic("Other_Symbols",'Other Symbols', - undef,undef,600) - .'
'; - if ($cols > 80) { $cols = 80; } - if ($cols < 70) { $cols = 70; } - if ($rows < 20) { $rows = 20; } - $result.=' -
- - - -
- - - -
- ' . $xml_help . ' - -
'; + my ($request,$file) = @_; + my $result; + my $problem=&Apache::lonnet::getfile($file); + if ($problem eq -1) { + &Apache::lonxml::error( + '

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

'); + + $problem=''; + } + 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.problemmode'} eq 'saveviewxml') { + &Apache::lonhomework::showhashsubset(\%env,'^form'); + $env{'form.problemmode'}='view'; + &renderpage($request,$file); + } else { + my ($rows,$cols) = &Apache::edit::textarea_sizes(\$problem); + 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(). + &Apache::lonhtmlcommon::dragmath_js("EditMathPopup"); + + # Breadcrumbs + my $brcrum = [{'href' => &Apache::loncommon::authorspace($request->uri), + 'text' => 'Authoring Space'}, + {'href' => '', + 'text' => 'Problem Editing'}]; + + my $start_page = + &Apache::loncommon::start_page(&mt("EditXML [_1]",$file),$js, + {'no_auto_mt_title' => 1, + 'only_body' => 0, + 'add_entries' => { + 'onresize' => q[resize_textarea('LC_editxmltext','LC_aftertextarea')], + 'onload' => q[resize_textarea('LC_editxmltext','LC_aftertextarea')], + }, + 'bread_crumbs' => $brcrum, + }); + + $result=$start_page + .&Apache::loncommon::head_subbox( + &Apache::loncommon::CSTR_pageheader()); + $result.=&renderpage($request,$file,['no_output_web'],1). + '
'. + &Apache::structuretags::remember_problem_state().' +
+
'. + &mt('Problem Editing').' '.&Apache::loncommon::help_open_topic('Problem_Editor_XML_Index'). + '
'; + + $result.=''. + &Apache::structuretags::problem_edit_buttons('editxml'); + $result.='
'; + + $result .= '
    '; + + unless ($env{'environment.nocodemirror'}) { + # dropdown menus + $result .= Apache::lonmenu::create_submenu("#", "", + &mt("Problem Templates"), template_dropdown_datastructure()); + + $result .= Apache::lonmenu::create_submenu("#", "", + &mt("Response Types"), responseblock_dropdown_datastructure()); + + $result .= Apache::lonmenu::create_submenu("#", "", + &mt("Conditional Blocks"), conditional_scripting_datastructure()); + + $result .= Apache::lonmenu::create_submenu("#", "", + &mt("Miscellaneous"), misc_datastructure()); + } + + $result .= Apache::lonmenu::create_submenu("#", "", + &mt("Help") . ' ' . &mt(', + helpmenu_datastructure(),""); + + $result.="
"; + + $result .= '
' . + &Apache::lonxml::message_location() . + &Apache::loncommon::xmleditor_js() . + '
'; + + my $resource = $env{'request.ambiguous'}; + unless($env{'environment.nocodemirror'}){ + $result .= ' + + '; + } + + $result .= &Apache::loncommon::end_page(); + &Apache::lonxml::add_messages(\$result); $request->print($result); - } - return ''; + } + return ''; } +# +# Render the page in whatever target desired. +# sub renderpage { - my ($request,$file) = @_; + my ($request,$file,$targets,$return_string,$donebuttonmsg) = @_; - my (@targets) = &get_target(); - &Apache::lonxml::debug("Running targets ".join(':',@targets)); - foreach my $target (@targets) { - #my $t0 = [&gettimeofday()]; - my $problem=&Apache::lonnet::getfile($file); - if ($problem eq -1) { - &Apache::lonxml::error(" Unable to find $file"); - $problem=''; - } + my @targets = @{$targets || [&get_target()]}; + &Apache::lonhomework::showhashsubset(\%env,'form.'); + &Apache::lonxml::debug("Running targets ".join(':',@targets)); + + my $overall_result; + foreach my $target (@targets) { + # FIXME need to do something intelligent when a problem goes + # from viewable to not viewable due to map conditions + #&setuppermissions(); + #if ( $Apache::lonhomework::browse ne '2' + # && $Apache::lonhomework::browse ne 'F' ) { + # $request->print(" You most likely shouldn't see me."); + #} + #my $t0 = [&gettimeofday()]; + my $output=1; + if ($target eq 'no_output_web') { + $target = 'web'; $output=0; + } + my $problem=&Apache::lonnet::getfile($file); + my $result; + if ($problem eq -1) { + $problem=''; + my $filename=(split('/',$file))[-1]; + my $error = + '

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

"; + $result.= + &Apache::loncommon::simple_error_page($request,'Not available', + $error,{'no_auto_mt_msg' => 1}); + return; + } - my %mystyle; - my $result = ''; - if ($target eq 'analyze') { %Apache::lonhomework::analyze=(); } - if ($target eq 'answer') { &showhash(%Apache::lonhomework::history); } - if ($target eq 'web') {&Apache::lonhomework::showhashsubset(\%ENV,'^form');} - - &Apache::lonxml::debug("Should be parsing now"); - $result = &Apache::lonxml::xmlparse($request, $target, $problem, - &setup_vars($target),%mystyle); - undef($Apache::lonhomework::parsing_a_problem); - #$request->print("Result follows:"); - if ($target eq 'modified') { - &handle_save_or_undo($request,\$problem,\$result); + my %mystyle; + if ($target eq 'analyze') { %Apache::lonhomework::analyze=(); } + if ($target eq 'answer') { &showhash(%Apache::lonhomework::history); } + if ($target eq 'web') {&Apache::lonhomework::showhashsubset(\%env,'^form');} + + &Apache::lonxml::debug("Should be parsing now"); + $result .= &Apache::lonxml::xmlparse($request, $target, $problem, + &setup_vars($target),%mystyle); + &finished_parsing(); + if (!$output) { $result = ''; } + #$request->print("Result follows:"); + if ($target eq 'modified') { + &handle_save_or_undo($request,\$problem,\$result); + } else { + if ($target eq 'analyze') { + $result=&Apache::lonnet::hashref2str(\%Apache::lonhomework::analyze); + undef(%Apache::lonhomework::analyze); + } elsif ($target eq 'web') { + if ($donebuttonmsg) { + $result =~ s{}{}; + $result.= &Apache::loncommon::confirmwrapper(&Apache::lonhtmlcommon::confirm_success($donebuttonmsg,1))."\n"; + } + } + #my $td=&tv_interval($t0); + #if ( $Apache::lonxml::debug) { + #$result =~ s:::; + #$result.="
Spent $td seconds processing target $target\n"; + #} +# $request->print($result); + $overall_result.=$result; +# $request->rflush(); + } + #$request->print(":Result ends"); + #my $td=&tv_interval($t0); + } + if (!$return_string) { + &Apache::lonxml::add_messages(\$overall_result); + $request->print($overall_result); + $request->rflush(); } else { - if ($target eq 'analyze') { - $result=&Apache::lonnet::hashref2str(\%Apache::lonhomework::analyze); - undef(%Apache::lonhomework::analyze); - } - #my $td=&tv_interval($t0); - #if ( $Apache::lonxml::debug) { - #$result =~ s:::; - #$result.="
Spent $td seconds processing target $target\n"; - #} - $request->print($result); - $request->rflush(); + return $overall_result; } - #$request->print(":Result ends"); - #my $td=&tv_interval($t0); - } } -# with no arg it returns a HTML