");
+ }
+ 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
+#
}
- }
+#
+# 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('');
- $request->print(&Apache::lontexconvert::footer());
-}
-
-sub getfilenothere {
- my ($filename) = @_;
- my $a="";
- if (! -e $filename ) {
- &Apache::lonnet::subscribe($filename);
- &Apache::lonnet::repcopy($filename);
- }
- {
- my $fh=Apache::File->new($filename);
+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 check_slot_access {
+ my ($id,$type,$symb,$partlist)=@_;
+
+ # 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) || ($ENV{'REMOTE_ADDR'} eq '127.0.0.1'))) {
+ $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');
+ }
+ }
+ } elsif (($type eq 'problem') &&
+ ($Apache::lonhomework::browse eq 'F') &&
+ ($ENV{'REMOTE_ADDR'} eq '127.0.0.1') &&
+ ($env{'form.grade_courseid'} eq $env{'request.course.id'}) &&
+ (&Apache::lonnet::allowed('mgr',$env{'request.course.id'}))) {
+ return ($status,$datemsg);
+ }
+
+ 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) {
+ $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) {
+ ($slotstatus,$datemsg) = &check_reservable_slot($slotstatus,$symb,$now,$checkedin,
+ $consumed_uniq);
+ }
+ }
+ 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);
+}
+
+sub check_reservable_slot {
+ my ($slotstatus,$symb,$now,$checkedin,$consumed_uniq) = @_;
+ my $datemsg;
+ if ($slotstatus eq 'NOT_IN_A_SLOT') {
+ 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);
+}
+
+# JB, 9/24/2002: Any changes in this function may require a change
+# in lonnavmaps::resource::getDateStatus.
+sub check_access {
+ 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);
+ }
+ }
+ }
+
+ #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 {
+ $date = $due_date;
+ }
+ return $date;
+}
+
+sub seconds_to_human_length {
+ my ($length)=@_;
+
+ 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 '';
+}
+
+sub showarray {
+ my ($array)=@_;
+ my $string="(";
+ foreach my $elm (@{ $array }) {
+ if (ref($elm) eq 'ARRAY') {
+ $string.=&showarray($elm);
+ } elsif (ref($elm) eq 'HASH') {
+ $string.= "HASH --- \n
";
+ $string.= &showhashsubset($elm,'.');
+ } else {
+ $string.="$elm,"
+ }
+ }
+ chop($string);
+ $string.=")";
+ return $string;
+}
+
+sub showhashsubset {
+ 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 (ref($$hash{$resultkey}) eq 'HASH' ) {
+ &Apache::lonxml::debug("$resultkey ---- $$hash{$resultkey}");
+ &showhashsubset($$hash{$resultkey},'.');
+ } else {
+ &Apache::lonxml::debug("$resultkey ---- $$hash{$resultkey}");
+ }
+ }
+ &Apache::lonxml::debug("\n
restored values^\n");
+ return '';
+}
+
+sub setuppermissions {
+ $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'});
+ if ($viewgrades) {
+ $Apache::lonhomework::viewgradessec = $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'});
+ if ($modifygrades) {
+ $Apache::lonhomework::modifygradessec = $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::modifygradessec);
+ undef($Apache::lonhomework::viewgrades);
+ undef($Apache::lonhomework::viewgradessec);
+ undef($Apache::lonhomework::browse);
+}
+
+sub setupheader {
+ my $request=$_[0];
+ &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,$getobjref) = @_;
+
+ my $file = &Apache::lonnet::filelocation("",$request->uri);
+ my $filebak =$file.".bak";
+ my $filetmp =$file.".tmp";
+ my $error=0;
+ 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 {
+ &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;
+}
+
+sub analyze_header {
+ my ($request) = @_;
+ my $js = &Apache::structuretags::setmode_javascript();
+
+ # Breadcrumbs
+ my $text = 'Authoring Space';
+ my $href = &Apache::loncommon::authorspace($request->uri);
+ if ($env{'request.course.id'}) {
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ if ($href eq "/priv/$cdom/$cnum/") {
+ $text = 'Course Authoring Space';
+ }
+ }
+ my $brcrum = [{'href' => $href,
+ 'text' => $text},
+ {'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::add_messages(\$result);
+ $request->print($result);
+ $request->rflush();
+}
+
+sub analyze_footer {
+ my ($request) = @_;
+ $request->print(&Apache::loncommon::end_page());
+ $request->rflush();
+}
+
+sub analyze {
+ my ($request,$file) = @_;
+ &Apache::lonxml::debug("Analyze");
+ my $result;
+ my %overall;
+ my %seedexample;
+ my %allparts;
+ my $rndseed=$env{'form.rndseed'};
+ &analyze_header($request);
+ my %prog_state=
+ &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' => $thisseed));
+ (my $garbage,$subresult)=split(/_HASH_REF__/,$subresult,2);
+ my %analyze=&Apache::lonnet::str2hash($subresult);
+ my @parts;
+ if (ref($analyze{'parts'}) eq 'ARRAY') {
+ @parts=@{ $analyze{'parts'} };
+ }
+ foreach my $part (@parts) {
+ if (!exists($allparts{$part})) {$allparts{$part}=1;};
+ if ($analyze{$part.'.type'} eq 'numericalresponse' ||
+ $analyze{$part.'.type'} eq 'stringresponse' ||
+ $analyze{$part.'.type'} eq 'formularesponse' ) {
+ 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,&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()
+ .''
+ .&mt('Part').' '.$part
+ );
+ if (scalar(@{ $overall{$part.'.answer'} }) > 1) {
+ $request->print(' '.&mt('Answer [_1]',$i+1));
+ }
+ $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(&Apache::loncommon::start_data_table_header_row()
+ .''.&mt('Answer').' | '
+ .''.&mt('Frequency').' '
+ .'('.&mt('click for example').') | '
+ .&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()
+ .''
+ .join(' | ',split("\0",$answer))
+ .' | '
+ .''
+ .''.$frequency{$answer}.''
+ .' | '
+ .&Apache::loncommon::end_data_table_row()
+ );
+ }
+ $request->print(&Apache::loncommon::end_data_table());
+ }
+ } else {
+ $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(
+ ''
+ .&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 $text = 'Authoring Space';
+ my $href = &Apache::loncommon::authorspace($request->uri);
+ if ($env{'request.course.id'}) {
+ my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
+ my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
+ if ($href eq "/priv/$cdom/$cnum/") {
+ $text = 'Course Authoring Space';
+ }
+ }
+ my $brcrum = [{'href' => $href,
+ 'text' => $text},
+ {'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).
+ '';
+
+ my $resource = $env{'request.ambiguous'};
+ unless($env{'environment.nocodemirror'}){
+ $result .= '
+
+ ",
+ mode: CodeMirror.getMode(config, "perl"),
+ delimStyle: "tag",
+ }
+ );
+ });
+ var cm = CodeMirror.fromTextArea(document.getElementById("LC_editxmltext"),
+ {
+ mode: "mixedmode",
+ lineWrapping: true,
+ lineNumbers: true,
+ tabSize: 4,
+ indentUnit: 4,
+
+ autoCloseTags: true,
+ autoCloseBrackets: true,
+ height: "auto",
+ styleActiveLine: true,
+
+ extraKeys: {
+ "Tab": "indentMore",
+ "Shift-Tab": "indentLess",
+ }
+ });
+ restoreScrollPosition("'.$resource.'");
+ ';
+ }
+
+ $result .= &Apache::loncommon::end_page();
+ &Apache::lonxml::add_messages(\$result);
+ $request->print($result);
+ }
+ return '';
+}
+
+#
+# Render the page in whatever target desired.
+#
+sub renderpage {
+ my ($request,$file,$targets,$return_string,$donebuttonmsg,$viewasuser,$symb) = @_;
+
+ 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;
+ if ($target eq 'analyze') { %Apache::lonhomework::analyze=(); }
+ if ($target eq 'answer') { &showhash(%Apache::lonhomework::history); }
+ if ($target eq 'web') {&Apache::lonhomework::showhashsubset(\%env,'^form');}
+
+ if (($target eq 'web') && ($viewasuser ne '') && ($symb ne '')) {
+ $env{'request.user_in_effect'} = $viewasuser;
+ }
+
+ &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{