--- loncom/interface/lonnavmaps.pm 2015/04/13 16:46:43 1.506
+++ loncom/interface/lonnavmaps.pm 2016/08/10 21:29:52 1.509.2.3
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.506 2015/04/13 16:46:43 raeburn Exp $
+# $Id: lonnavmaps.pm,v 1.509.2.3 2016/08/10 21:29:52 raeburn Exp $
#
# Copyright Michigan State University Board of Trustees
@@ -624,44 +624,52 @@ sub getDescription {
if ($status == $res->OPEN_LATER) {
return &mt("Open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($open,'start'),$res->symb(),'opendate',$part));
}
+ my $slotinfo;
if ($res->simpleStatus($part) == $res->OPEN) {
unless (&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) {
my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
+ my $slotmsg;
if ($slot_status == $res->UNKNOWN) {
- return &mt('Reservation status unknown');
+ $slotmsg = &mt('Reservation status unknown');
} elsif ($slot_status == $res->RESERVED) {
- return &mt('Reserved - ends [_1]',
+ $slotmsg = &mt('Reserved - ends [_1]',
timeToHumanString($slot_time,'end'));
} elsif ($slot_status == $res->RESERVED_LOCATION) {
- return &mt('Reserved - specific location(s) - ends [_1]',
+ $slotmsg = &mt('Reserved - specific location(s) - ends [_1]',
timeToHumanString($slot_time,'end'));
} elsif ($slot_status == $res->RESERVED_LATER) {
- return &mt('Reserved - next open [_1]',
+ $slotmsg = &mt('Reserved - next open [_1]',
timeToHumanString($slot_time,'start'));
} elsif ($slot_status == $res->RESERVABLE) {
- return &mt('Reservable, reservations close [_1]',
+ $slotmsg = &mt('Reservable, reservations close [_1]',
timeToHumanString($slot_time,'end'));
} elsif ($slot_status == $res->RESERVABLE_LATER) {
- return &mt('Reservable, reservations open [_1]',
+ $slotmsg = &mt('Reservable, reservations open [_1]',
timeToHumanString($slot_time,'start'));
} elsif ($slot_status == $res->NOT_IN_A_SLOT) {
- return &mt('Reserve a time/place to work');
+ $slotmsg = &mt('Reserve a time/place to work');
} elsif ($slot_status == $res->NOTRESERVABLE) {
- return &mt('Reservation not available');
+ $slotmsg = &mt('Reservation not available');
} elsif ($slot_status == $res->WAITING_FOR_GRADE) {
- return &mt('Submission in grading queue');
+ $slotmsg = &mt('Submission in grading queue');
+ }
+ if ($slotmsg) {
+ if ($res->is_task() || !$due) {
+ return $slotmsg;
+ }
+ $slotinfo = (' ' x 2).'('.$slotmsg.')';
}
}
}
if ($status == $res->OPEN) {
if ($due) {
if ($res->is_practice()) {
- return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'duedate',$part));
+ return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'duedate',$part)).$slotinfo;
} else {
- return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part));
+ return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part)).$slotinfo;
}
} else {
- return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part);
+ return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part).$slotinfo;
}
}
if ($status == $res->PAST_DUE_ANSWER_LATER) {
@@ -907,6 +915,9 @@ sub render_resource {
my $nonLinkedText = ''; # stuff after resource title not in link
my $link = $params->{"resourceLink"};
+ if ($resource->ext()) {
+ $link =~ s/\#.+(\?)/$1/g;
+ }
# The URL part is not escaped at this point, but the symb is...
@@ -995,10 +1006,13 @@ sub render_resource {
'';
}
}
- }
-
- if ($resource->randomout()) {
- $nonLinkedText .= ' ('.&mt('hidden').') ';
+ if ($params->{'mapHidden'} || $resource->randomout()) {
+ $nonLinkedText .= ' ('.&mt('hidden').') ';
+ }
+ } else {
+ if ($resource->randomout()) {
+ $nonLinkedText .= ' ('.&mt('hidden').') ';
+ }
}
if (!$resource->condval()) {
$nonLinkedText .= ' ('.&mt('conditionally hidden').') ';
@@ -1573,41 +1587,46 @@ END
$args->{'indentString'} = setDefault($args->{'indentString'}, "
");
$args->{'displayedHereMarker'} = 0;
- # If we're suppressing empty sequences, look for them here. Use DFS for speed,
- # since structure actually doesn't matter, except what map has what resources.
- if ($args->{'suppressEmptySequences'}) {
- my $dfsit = Apache::lonnavmaps::DFSiterator->new($navmap,
- $it->{FIRST_RESOURCE},
- $it->{FINISH_RESOURCE},
- {}, undef, 1);
- my $depth = 0;
- $dfsit->next();
- my $curRes = $dfsit->next();
- while ($depth > -1) {
- if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }
- if ($curRes == $dfsit->END_MAP()) { $depth--; }
-
- if (ref($curRes)) {
- # Parallel pre-processing: Do sequences have non-filtered-out children?
- if ($curRes->is_map()) {
- $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;
- # Sequences themselves do not count as visible children,
- # unless those sequences also have visible children.
- # This means if a sequence appears, there's a "promise"
- # that there's something under it if you open it, somewhere.
- } else {
- # Not a sequence: if it's filtered, ignore it, otherwise
- # rise up the stack and mark the sequences as having children
- if (&$filterFunc($curRes)) {
- for my $sequence (@{$dfsit->getStack()}) {
- $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;
- }
+ # If we're suppressing empty sequences, look for them here.
+ # We also do this even if $args->{'suppressEmptySequences'}
+ # is not true, so we can hide empty sequences for which the
+ # hiddenresource parameter is set to yes (at map level), or
+ # mark as hidden for users who have $userCanSeeHidden.
+ # Use DFS for speed, since structure actually doesn't matter,
+ # except what map has what resources.
+
+ my $dfsit = Apache::lonnavmaps::DFSiterator->new($navmap,
+ $it->{FIRST_RESOURCE},
+ $it->{FINISH_RESOURCE},
+ {}, undef, 1);
+
+ my $depth = 0;
+ $dfsit->next();
+ my $curRes = $dfsit->next();
+ while ($depth > -1) {
+ if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }
+ if ($curRes == $dfsit->END_MAP()) { $depth--; }
+
+ if (ref($curRes)) {
+ # Parallel pre-processing: Do sequences have non-filtered-out children?
+ if ($curRes->is_map()) {
+ $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;
+ # Sequences themselves do not count as visible children,
+ # unless those sequences also have visible children.
+ # This means if a sequence appears, there's a "promise"
+ # that there's something under it if you open it, somewhere.
+ } elsif ($curRes->src()) {
+ # Not a sequence: if it's filtered, ignore it, otherwise
+ # rise up the stack and mark the sequences as having children
+ if (&$filterFunc($curRes)) {
+ for my $sequence (@{$dfsit->getStack()}) {
+ $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;
}
}
}
- } continue {
- $curRes = $dfsit->next();
}
+ } continue {
+ $curRes = $dfsit->next();
}
my $displayedJumpMarker = 0;
@@ -1700,9 +1719,21 @@ END
}
# If this is an empty sequence and we're filtering them, continue on
- if ($curRes->is_map() && $args->{'suppressEmptySequences'} &&
- !$curRes->{DATA}->{HAS_VISIBLE_CHILDREN}) {
- next;
+ $args->{'mapHidden'} = 0;
+ if (($curRes->is_map()) && (!$curRes->{DATA}->{HAS_VISIBLE_CHILDREN})) {
+ if ($args->{'suppressEmptySequences'}) {
+ next;
+ } else {
+ my $mapname = &Apache::lonnet::declutter($curRes->src());
+ $mapname = &Apache::lonnet::deversion($mapname);
+ if (lc($navmap->get_mapparam(undef,$mapname,"0.hiddenresource")) eq 'yes') {
+ if ($userCanSeeHidden) {
+ $args->{'mapHidden'} = 1;
+ } else {
+ next;
+ }
+ }
+ }
}
# If we're suppressing navmaps and this is a navmap, continue on
@@ -2712,15 +2743,15 @@ sub map_printdates {
- my $opendate = $self->get_mapparam($res->symb(), "$part.printstartdate");
- my $closedate= $self->get_mapparam($res->symb(), "$part.printenddate");
+ my $opendate = $self->get_mapparam($res->symb(),'',"$part.printstartdate");
+ my $closedate= $self->get_mapparam($res->symb(),'', "$part.printenddate");
return ($opendate, $closedate);
}
sub get_mapparam {
- my ($self, $symb, $what) = @_;
+ my ($self, $symb, $mapname, $what) = @_;
# Ensure the course option hash is populated:
@@ -2739,15 +2770,17 @@ sub get_mapparam {
my $uname=$self->{USERNAME};
my $udom=$self->{DOMAIN};
- unless ($symb) { return ['']; }
+ unless ($symb || $mapname) { return; }
my $result='';
-
+ my ($recursed,@recurseup);
# Figure out which map we are in.
- my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
- $mapname = &Apache::lonnet::deversion($mapname);
-
+ if ($symb && !$mapname) {
+ my ($id,$fn);
+ ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
+ $mapname = &Apache::lonnet::deversion($mapname);
+ }
my $rwhat=$what;
$what=~s/^parameter\_//;
@@ -2804,9 +2837,12 @@ sub get_mapparam {
}
# Check the map parameters themselves:
- my $thisparm = $$parmhash{$symbparm};
- if (defined($thisparm)) {
- return $thisparm;
+ if ($symb) {
+ my $symbparm=$symb.'.'.$what;
+ my $thisparm = $$parmhash{$symbparm};
+ if (defined($thisparm)) {
+ return $thisparm;
+ }
}
@@ -2871,7 +2907,7 @@ sub getcourseparam {
#
# We want the course level stuff from the way
# parmval_real operates
- # TODO: Fator some of this stuff out of
+ # TODO: Factor some of this stuff out of
# both parmval_real and here
#
my $courselevel = $cid . '.' . $what;
@@ -2888,7 +2924,7 @@ sub getcourseparam {
}
# Try for the group's course level option:
- if ($uname ne '' and defined($courseopt)) {
+ if ($cgroup ne '' and defined($courseopt)) {
if (defined($$courseopt{$grplevel})) {
return $$courseopt{$grplevel};
}
@@ -2896,12 +2932,12 @@ sub getcourseparam {
# Try for section level parameters:
- if ($csec and defined($courseopt)) {
+ if ($csec ne '' and defined($courseopt)) {
if (defined($$courseopt{$seclevel})) {
return $$courseopt{$seclevel};
}
}
- # Try for 'additional' course parameterse:
+ # Try for 'additional' course parameters:
if (defined($courseopt)) {
if (defined($$courseopt{$courselevel})) {
@@ -3860,9 +3896,12 @@ sub new {
# about this resource in. Not used by the resource object
# directly.
$self->{DATA} = {};
-
+
bless($self);
+ # This is a speed optimization, to avoid calling symb() too often.
+ $self->{SYMB} = $self->symb();
+
return $self;
}
@@ -3974,8 +4013,8 @@ sub src {
}
sub shown_symb {
my $self=shift;
- if ($self->encrypted()) {return &Apache::lonenc::encrypted($self->symb());}
- return $self->symb();
+ if ($self->encrypted()) {return &Apache::lonenc::encrypted($self->{SYMB});}
+ return $self->{SYMB};
}
sub id {
my $self=shift;
@@ -3996,7 +4035,7 @@ sub symb {
}
sub wrap_symb {
my $self = shift;
- return $self->{NAV_MAP}->wrap_symb($self->symb());
+ return $self->{NAV_MAP}->wrap_symb($self->{SYMB});
}
sub title {
my $self=shift;
@@ -4219,7 +4258,7 @@ sub parmval {
if (!defined($part)) {
$part = '0';
}
- return $self->{NAV_MAP}->parmval($part.'.'.$what, $self->symb());
+ return $self->{NAV_MAP}->parmval($part.'.'.$what, $self->{SYMB});
}
=pod
@@ -4472,25 +4511,25 @@ sub awarded {
my $self = shift; my $part = shift;
$self->{NAV_MAP}->get_user_data();
if (!defined($part)) { $part = '0'; }
- return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.awarded'};
+ return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}}->{'resource.'.$part.'.awarded'};
}
sub taskversion {
my $self = shift; my $part = shift;
$self->{NAV_MAP}->get_user_data();
if (!defined($part)) { $part = '0'; }
- return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.version'};
+ return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}}->{'resource.'.$part.'.version'};
}
sub taskstatus {
my $self = shift; my $part = shift;
$self->{NAV_MAP}->get_user_data();
if (!defined($part)) { $part = '0'; }
- return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$self->taskversion($part).'.'.$part.'.status'};
+ return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}}->{'resource.'.$self->taskversion($part).'.'.$part.'.status'};
}
sub solved {
my $self = shift; my $part = shift;
$self->{NAV_MAP}->get_user_data();
if (!defined($part)) { $part = '0'; }
- return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.solved'};
+ return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}}->{'resource.'.$part.'.solved'};
}
sub checkedin {
my $self = shift; my $part = shift;
@@ -4498,9 +4537,9 @@ sub checkedin {
if (!defined($part)) { $part = '0'; }
if ($self->is_task()) {
my $version = $self->taskversion($part);
- return ($self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$version .'.'.$part.'.checkedin'},$self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$version .'.'.$part.'.checkedin.slot'});
+ return ($self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}}->{'resource.'.$version .'.'.$part.'.checkedin'},$self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}}->{'resource.'.$version .'.'.$part.'.checkedin.slot'});
} else {
- return ($self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.checkedin'},$self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.checkedin.slot'});
+ return ($self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}}->{'resource.'.$part.'.checkedin'},$self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}}->{'resource.'.$part.'.checkedin.slot'});
}
}
# this should work exactly like the copy in lonhomework.pm
@@ -4519,7 +4558,7 @@ sub duedate {
my $due_date=$self->parmval("duedate", $part);
if ($interval[0] =~ /\d+/) {
my $first_access=&Apache::lonnet::get_first_access($interval[1],
- $self->symb);
+ $self->{SYMB});
if (defined($first_access)) {
my $interval = $first_access+$interval[0];
$date = (!$due_date || $interval < $due_date) ? $interval
@@ -4592,7 +4631,7 @@ sub weight {
my $self = shift; my $part = shift;
if (!defined($part)) { $part = '0'; }
my $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight',
- $self->symb(), $self->{DOMAIN},
+ $self->{SYMB}, $self->{DOMAIN},
$self->{USERNAME},
$env{'request.course.sec'});
return $weight;
@@ -4601,7 +4640,7 @@ sub part_display {
my $self= shift(); my $partID = shift();
if (! defined($partID)) { $partID = '0'; }
my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',
- $self->symb);
+ $self->{SYMB});
if (! defined($display) || $display eq '') {
$display = $partID;
}
@@ -4621,7 +4660,7 @@ sub getReturnHash {
my $self = shift;
if (!defined($self->{RETURN_HASH})) {
- my %tmpHash = &Apache::lonnet::restore($self->symb(),undef,$self->{DOMAIN},$self->{USERNAME});
+ my %tmpHash = &Apache::lonnet::restore($self->{SYMB},undef,$self->{DOMAIN},$self->{USERNAME});
$self->{RETURN_HASH} = \%tmpHash;
}
}
@@ -4686,23 +4725,23 @@ and use the link as appropriate.
sub hasDiscussion {
my $self = shift;
- return $self->{NAV_MAP}->hasDiscussion($self->symb());
+ return $self->{NAV_MAP}->hasDiscussion($self->{SYMB});
}
sub last_post_time {
my $self = shift;
- return $self->{NAV_MAP}->last_post_time($self->symb());
+ return $self->{NAV_MAP}->last_post_time($self->{SYMB});
}
sub discussion_info {
my ($self,$filter) = @_;
- return $self->{NAV_MAP}->discussion_info($self->symb(),$filter);
+ return $self->{NAV_MAP}->discussion_info($self->{SYMB},$filter);
}
sub getFeedback {
my $self = shift;
my $source = $self->src();
- my $symb = $self->symb();
+ my $symb = $self->{SYMB};
if ($source =~ /^\/res\//) { $source = substr $source, 5; }
return $self->{NAV_MAP}->getFeedback($symb,$source);
}
@@ -4710,7 +4749,7 @@ sub getFeedback {
sub getErrors {
my $self = shift;
my $source = $self->src();
- my $symb = $self->symb();
+ my $symb = $self->{SYMB};
if ($source =~ /^\/res\//) { $source = substr $source, 5; }
return $self->{NAV_MAP}->getErrors($symb,$source);
}
@@ -4860,7 +4899,7 @@ sub extractParts {
if ($partorder) {
my @parts;
for my $part (split (/,/,$partorder)) {
- if (!Apache::loncommon::check_if_partid_hidden($part, $self->symb())) {
+ if (!Apache::loncommon::check_if_partid_hidden($part, $self->{SYMB})) {
push @parts, $part;
$parts{$part} = 1;
}
@@ -4878,12 +4917,12 @@ sub extractParts {
my $part = $1;
# This floods the logs if it blows up
if (defined($parts{$part})) {
- &Apache::lonnet::logthis("$part multiply defined in metadata for " . $self->symb());
+ &Apache::lonnet::logthis("$part multiply defined in metadata for " . $self->{SYMB});
}
# check to see if part is turned off.
- if (!Apache::loncommon::check_if_partid_hidden($part, $self->symb())) {
+ if (!Apache::loncommon::check_if_partid_hidden($part, $self->{SYMB})) {
$parts{$part} = 1;
}
}
@@ -5388,7 +5427,7 @@ sub status {
sub check_for_slot {
my $self = shift;
my $part = shift;
- my $symb = $self->symb();
+ my $symb = $self->{SYMB};
my ($use_slots,$available,$availablestudent) = $self->slot_control($part);
if (($use_slots ne '') && ($use_slots !~ /^\s*no\s*$/i)) {
my @slots = (split(/:/,$availablestudent),split(/:/,$available));
@@ -5397,13 +5436,13 @@ sub check_for_slot {
my $cnum=$env{'course.'.$cid.'.num'};
my $now = time;
my $num_usable_slots = 0;
+ my ($checkedin,$checkedinslot,%consumed_uniq,%slots);
if (@slots > 0) {
- my %slots=&Apache::lonnet::get('slots',[@slots],$cdom,$cnum);
+ %slots=&Apache::lonnet::get('slots',[@slots],$cdom,$cnum);
if (&Apache::lonnet::error(%slots)) {
return (UNKNOWN);
}
my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%slots,'starttime');
- my ($checkedin,$checkedinslot);
foreach my $slot_name (@sorted_slots) {
next if (!defined($slots{$slot_name}) || !ref($slots{$slot_name}));
my $end = $slots{$slot_name}->{'endtime'};
@@ -5437,19 +5476,30 @@ sub check_for_slot {
$num_usable_slots ++;
}
}
- my ($is_correct,$got_grade);
+ my ($is_correct,$wait_for_grade);
if ($self->is_task()) {
my $taskstatus = $self->taskstatus();
$is_correct = (($taskstatus eq 'pass') ||
($self->solved() =~ /^correct_/));
- $got_grade = ($taskstatus =~ /^(?:pass|fail)$/);
+ unless ($taskstatus =~ /^(?:pass|fail)$/) {
+ $wait_for_grade = 1;
+ }
} else {
- $got_grade = 1;
- $is_correct = ($self->solved() =~ /^correct_/);
+ unless ($self->completable()) {
+ $wait_for_grade = 1;
+ }
+ unless (($self->problemstatus($part) eq 'no') ||
+ ($self->problemstatus($part) eq 'no_feedback_ever')) {
+ $is_correct = ($self->solved($part) =~ /^correct_/);
+ $wait_for_grade = 0;
+ }
}
($checkedin,$checkedinslot) = $self->checkedin();
if ($checkedin) {
- if (!$got_grade) {
+ if (ref($slots{$checkedinslot}) eq 'HASH') {
+ $consumed_uniq{$checkedinslot} = $slots{$checkedinslot}{'uniqueperiod'};
+ }
+ if ($wait_for_grade) {
return (WAITING_FOR_GRADE);
} elsif ($is_correct) {
return (CORRECT);
@@ -5464,16 +5514,46 @@ sub check_for_slot {
if (ref($reservable) eq 'HASH') {
if ((ref($reservable->{'now_order'}) eq 'ARRAY') && (ref($reservable->{'now'}) eq 'HASH')) {
foreach my $slot (reverse (@{$reservable->{'now_order'}})) {
+ my $canuse;
if (($reservable->{'now'}{$slot}{'symb'} eq '') ||
($reservable->{'now'}{$slot}{'symb'} eq $symb)) {
+ $canuse = 1;
+ }
+ if ($canuse) {
+ if ($checkedin) {
+ if (ref($consumed_uniq{$checkedinslot}) eq 'ARRAY') {
+ my ($uniqstart,$uniqend)=@{$consumed_uniq{$checkedinslot}};
+ 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 ));
+ }
+ }
+ }
return(RESERVABLE,$reservable->{'now'}{$slot}{'endreserve'});
}
}
}
if ((ref($reservable->{'future_order'}) eq 'ARRAY') && (ref($reservable->{'future'}) eq 'HASH')) {
foreach my $slot (@{$reservable->{'future_order'}}) {
+ my $canuse;
if (($reservable->{'future'}{$slot}{'symb'} eq '') ||
($reservable->{'future'}{$slot}{'symb'} eq $symb)) {
+ $canuse = 1;
+ }
+ if ($canuse) {
+ if ($checkedin) {
+ if (ref($consumed_uniq{$checkedinslot}) eq 'ARRAY') {
+ my ($uniqstart,$uniqend)=@{$consumed_uniq{$checkedinslot}};
+ 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 ));
+ }
+ }
+ }
return(RESERVABLE_LATER,$reservable->{'future'}{$slot}{'startreserve'});
}
}
@@ -5624,6 +5704,39 @@ sub completable {
=pod
+B
+
+The answerable method differs from the completable method in its handling of problem parts
+for which feedback on correctness is suppressed, but the student still has tries left, and
+the problem part is not past due, (i.e., the student could submit a different answer if
+he/she so chose). For that case completable will return 0, whereas answerable will return 1.
+
+=cut
+
+sub answerable {
+ my $self = shift;
+ if (!$self->is_problem()) { return 0; }
+ my $partCount = $self->countParts();
+ foreach my $part (@{$self->parts()}) {
+ if ($part eq '0' && $partCount != 1) { next; }
+ my $status = $self->status($part);
+ if ($self->getCompletionStatus($part) == ATTEMPTED() ||
+ $self->getCompletionStatus($part) == CREDIT_ATTEMPTED() ||
+ $status == ANSWER_SUBMITTED() ) {
+ if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) {
+ return 1;
+ }
+ }
+ if ($status == OPEN() || $status == TRIES_LEFT() || $status == NETWORK_FAILURE()) {
+ return 1;
+ }
+ }
+ # None of the parts were answerable, so neither is this problem.
+ return 0;
+}
+
+=pod
+
=head2 Resource/Nav Map Navigation
=over 4
@@ -5677,7 +5790,7 @@ sub browsePriv {
}
$self->{BROWSE_PRIV} = &Apache::lonnet::allowed('bre',$self->src(),
- $self->symb(),undef,
+ $self->{SYMB},undef,
undef,$noblockcheck);
}