--- loncom/interface/lonnavmaps.pm 2002/10/14 18:48:13 1.81 +++ loncom/interface/lonnavmaps.pm 2002/10/28 19:10:35 1.88 @@ -2,7 +2,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.81 2002/10/14 18:48:13 bowersj2 Exp $ +# $Id: lonnavmaps.pm,v 1.88 2002/10/28 19:10:35 bowersj2 Exp $ # # Copyright Michigan State University Board of Trustees # @@ -849,10 +849,6 @@ sub new_handle { $r->print(' Discussions'. '
New message (click to open)

'); } - #if (($currenturl=~/^\/res/) && - # ($currenturl!~/^\/res\/adm/)) { - # $r->print('Current Location

'); - #} # Check that it's defined if (!($navmap->courseMapDefined())) { @@ -880,7 +876,7 @@ sub new_handle { $res->NOTHING_SET => '' ); # And a special case in the nav map; what to do when the assignment # is not yet done and due in less then 24 hours - my $hurryUpColor = "#FFCCCC"; + my $hurryUpColor = "#FF0000"; my %statusIconMap = ( $res->NETWORK_FAILURE => '', @@ -894,7 +890,7 @@ sub new_handle { $res->TRIES_LEFT => 'navmap.open.gif', $res->INCORRECT => 'navmap.wrong.gif', $res->OPEN => 'navmap.open.gif', - $res->ATTEMPTED => '' ); + $res->ATTEMPTED => 'navmap.open.gif' ); my %iconAltTags = ( 'navmap.correct.gif' => 'Correct', @@ -914,6 +910,11 @@ sub new_handle { } } + # Is this a new-style course? If so, we want to suppress showing the top-level + # maps in their own folders, in favor of "inlining" them. + my $topResource = $navmap->getById("0.0"); + my $inlineTopLevelMaps = $topResource->src() =~ m|^/uploaded/.*default\.sequence$|; + my $currenturl = $ENV{'form.postdata'}; $currenturl=~s/^http\:\/\///; $currenturl=~s/^[^\/]+//; @@ -929,7 +930,7 @@ sub new_handle { # Begin the HTML table # four cols: resource + indent, chat+feedback, icon, text string - $r->print('' ."\n"); + $r->print('
' ."\n"); my $condition = 0; if ($ENV{'form.condition'}) { @@ -940,35 +941,53 @@ sub new_handle { # instead of uris. The changes to this and the main rendering # loop should be obvious. # Here's a simple example of the iterator. - # If there is a current resource - if ($currenturl && !$ENV{'form.alreadyHere'}) { - # Give me every resource... - my $mapIterator = $navmap->getIterator(undef, undef, {}, 1); - my $found != 0; - my $depth = 1; - $mapIterator->next(); # discard the first BEGIN_MAP - my $curRes = $mapIterator->next(); - - while ($depth > 0 && !$found) { - if (ref($curRes) && $curRes->src() eq $currenturl) { - # If this is the correct resource, be sure to - # show it by making sure the containing maps - # are open. - - my $mapStack = $mapIterator->getStack(); - for my $map (@{$mapStack}) { - if ($condition) { - undef $filterHash{$map->map_pc()}; - } else { - $filterHash{$map->map_pc()} = 1; - } + # Preprocess the map: Look for current URL, force inlined maps to display + + my $mapIterator = $navmap->getIterator(undef, undef, {}, 1); + my $found = 0; + my $depth = 1; + $mapIterator->next(); # discard the first BEGIN_MAP + my $curRes = $mapIterator->next(); + + while ($depth > 0) { + if ($curRes == $mapIterator->BEGIN_MAP()) { + $depth++; + } + if ($curRes == $mapIterator->END_MAP()) { + $depth--; + } + + my $mapStack = $mapIterator->getStack(); + if ($currenturl && !$ENV{'form.alreadyHere'} && ref($curRes) && + $curRes->src() eq $currenturl) { + # If this is the correct resource, be sure to + # show it by making sure the containing maps + # are open. + + for my $map (@{$mapStack}) { + if ($condition) { + undef $filterHash{$map->map_pc()}; + } else { + $filterHash{$map->map_pc()} = 1; } - $found = 1; } - $curRes = $mapIterator->next(); + $ENV{'form.alreadyHere'} = 1; + } + + # Preprocessing: If we're inlining nav maps into the top-level display, + # make sure we show this map! + if ($inlineTopLevelMaps && ref($curRes) && $curRes->is_map && + scalar(@{$mapStack}) == 1) { + if ($condition) { + undef $filterHash{$curRes->map_pc()}; + } else { + $filterHash{$curRes->map_pc()} = 1; + } } - } + $curRes = $mapIterator->next(); + } + undef $res; # so we don't accidentally use it later my $indentLevel = 0; my $indentString = "\"\""; @@ -978,15 +997,32 @@ sub new_handle { my $in24Hours = $now + 24 * 60 * 60; my $depth = 1; my $displayedHereMarker = 0; - + # We know the first thing is a BEGIN_MAP (see "$self->{STARTED}" # code in iterator->next), so ignore the first one my $mapIterator = $navmap->getIterator(undef, undef, \%filterHash, $condition); $mapIterator->next(); my $curRes = $mapIterator->next(); + my $deltadepth = 0; + + my @backgroundColors = ("#FFFFFF", "#F6F6F6"); + my $rowNum = 0; while ($depth > 0) { + # If we're in a new style course, and this is a BEGIN_MAP, END_MAP, or + # map resource and the stack depth is only one, just plain ignore this resource + # entirely. (This has the effect of inlining the resources in that map + # in the nav map.) + if ($inlineTopLevelMaps && scalar(@{$mapIterator->getStack()}) == 1 && + ref($curRes) && $curRes->is_map()) { + # We let the normal depth stuff occur, but we need to shift everything + # over by one to the left to make it look right. + $deltadepth = -1; + $curRes = $mapIterator->next(); + next; + } + if ($curRes == $mapIterator->BEGIN_MAP() || $curRes == $mapIterator->BEGIN_BRANCH()) { $indentLevel++; @@ -1005,6 +1041,9 @@ sub new_handle { $depth--; } + if ($depth == 1) { $deltadepth = 0; } # we're done shifting, because we're + # out of the inlined map + # Is this resource being blotted out? if (ref($curRes) && !advancedUser() && $curRes->randomout()) { $curRes = $mapIterator->next(); @@ -1075,11 +1114,18 @@ sub new_handle { } } else { - @parts[0] = "0"; # this is to get past foreach loop below + $parts[0] = "0"; # this is to get past foreach loop below # you can consider a non-problem resource as a resource # with only one part without loss } + # Is it a multipart problem with a single part, now in + # @parts with "0" filtered out? If so, forget it's a multi-part + # problem and treat it like a single-part problem. + if ( scalar(@parts) == 1 ) { + $multipart = 0; + } + # Display one part, in event of network error. # If this is a single part, we can at least show the correct # status, but if it's multipart, we're lost. @@ -1135,7 +1181,8 @@ sub new_handle { # Display the correct icon, link to open or shut map if ($curRes->is_map()) { my $mapId = $curRes->map_pc(); - my $nowOpen = !defined($filterHash{$mapId}); + my $nowOpen = (!defined($filterHash{$mapId})); + if ($condition) {$nowOpen = !$nowOpen;} $icon = $nowOpen ? "navmap.folder.closed.gif" : "navmap.folder.open.gif"; $icon = "\"\""; @@ -1149,23 +1196,27 @@ sub new_handle { } my $colorizer = ""; + my $color; if ($curRes->is_problem()) { my $status = $curRes->status($part); - my $color = $colormap{$status}; + $color = $colormap{$status}; # Special case in the navmaps: If in less then # 24 hours, give it a bit of urgency - if ($status == $curRes->OPEN() && $curRes->duedate() && + if (($status == $curRes->OPEN() || $status == $curRes->ATTEMPTED() || + $status == $curRes->TRIES_LEFT()) + && $curRes->duedate() && $curRes->duedate() < time()+(24*60*60) && $curRes->duedate() > time()) { $color = $hurryUpColor; } # Special case: If this is the last try, and there is - # more then one available, give a bit of urgency + # more then one available, and it's not due yet, give a bit of urgency my $tries = $curRes->tries($part); my $maxtries = $curRes->maxtries($part); if ($tries && $maxtries && $maxtries > 1 && - $maxtries - $tries == 1) { + $maxtries - $tries == 1 && $curRes->duedate() && + $curRes->duedate() > time()) { $color = $hurryUpColor; } if ($color ne "") { @@ -1177,11 +1228,14 @@ sub new_handle { $nonLinkedText .= ' (hidden) '; } + $rowNum++; + my $backgroundColor = $backgroundColors[$rowNum % scalar(@backgroundColors)]; + # FIRST COL: The resource indentation, branch icon, and name - $r->print(" "); + $r->print(""); # Is this the first displayed part of a multi-part problem # that has not been condensed, so we should suppress these two @@ -1246,27 +1300,30 @@ sub new_handle { my $icon = $statusIconMap{$curRes->status($part)}; my $alt = $iconAltTags{$icon}; if ($icon) { - $r->print("\n"); + $r->print("\n"); } else { - $r->print("\n"); + $r->print("\n"); } } else { # not problem, no icon - $r->print("\n"); + $r->print("\n"); } # FOURTH COL: Text description - $r->print("\n"); + $r->print(" \n"); } } $curRes = $mapIterator->next(); @@ -1469,7 +1526,7 @@ sub timeToHumanString { # Less then 5 days away, display day of the week and # HH:MM if ( $delta < $day * 5 ) { - my $timeStr = strftime("%A at %I:%M %P", localtime($time)); + my $timeStr = strftime("%A, %b %e at %I:%M %P", localtime($time)); $timeStr =~ s/12:00 am/midnight/; $timeStr =~ s/12:00 pm/noon/; return ($inPast ? "last " : "next ") . @@ -1661,6 +1718,8 @@ sub new { $self->{EMAIL_STATUS} = \%emailstatus; } + + $self->{PARM_CACHE} = {}; bless($self); @@ -1782,6 +1841,21 @@ sub finishResource { sub parmval { my $self = shift; my ($what,$symb)=@_; + my $hashkey = $what."|||".$symb; + + if (defined($self->{PARM_CACHE}->{$hashkey})) { + return $self->{PARM_CACHE}->{$hashkey}; + } + + my $result = $self->parmval_real($what, $symb); + $self->{PARM_CACHE}->{$hashkey} = $result; + return $result; +} + +sub parmval_real { + my $self = shift; + my ($what,$symb) = @_; + my $cid=$ENV{'request.course.id'}; my $csec=$ENV{'request.course.sec'}; my $uname=$ENV{'user.name'}; @@ -2147,7 +2221,7 @@ sub populateStack { my $self=shift; my $stack = shift; - push @$stack, $self->{HERE}; + push @$stack, $self->{HERE} if ($self->{HERE}); if ($self->{RECURSIVE_ITERATOR_FLAG}) { $self->{RECURSIVE_ITERATOR}->populateStack($stack); @@ -2243,15 +2317,15 @@ These are methods that help you retrieve =item * B: Returns the "to" value from the compiled nav map. (It is likely you want to use B instead.) -=item * B: Returns the type of the resource, "start", "normal", or "finish". - =back =cut # These info functions can be used directly, as they don't return # resource information. +sub comesfrom { my $self=shift; return $self->navHash("comesfrom_", 1); } sub ext { my $self=shift; return $self->navHash("ext_", 1) eq 'true:'; } +sub from { my $self=shift; return $self->navHash("from_", 1); } sub goesto { my $self=shift; return $self->navHash("goesto_", 1); } sub kind { my $self=shift; return $self->navHash("kind_", 1); } sub randomout { my $self=shift; return $self->navHash("randomout_", 1); } @@ -2274,7 +2348,6 @@ sub symb { } sub title { my $self=shift; return $self->navHash("title_", 1); } sub to { my $self=shift; return $self->navHash("to_", 1); } -sub type { my $self=shift; return $self->navHash("type_", 1); } =pod @@ -2490,7 +2563,7 @@ sub getReturnHash { my $self = shift; if (!defined($self->{RETURN_HASH})) { - my %tmpHash = &Apache::lonnet::restore($self->symb()); + my %tmpHash = &Apache::lonnet::restore($self->symb()); $self->{RETURN_HASH} = \%tmpHash; } } @@ -2536,7 +2609,7 @@ sub hasDiscussion { sub getFeedback { my $self = shift; - return $self->{NAV_MAP}->getFeedback($self->symb()); + return $self->{NAV_MAP}->getFeedback($self->src()); } =pod @@ -2581,25 +2654,27 @@ sub extractParts { $self->{PARTS} = []; - # Retrieve part count - my $metadata = &Apache::lonnet::metadata($self->src(), 'allpossiblekeys'); - if (!$metadata) { - $self->{RESOURCE_ERROR} = 1; - $self->{PARTS} = []; - return; - } - - foreach (split(/\,/,$metadata)) { - if ($_ =~ /^parameter\_(.*)\_opendate$/) { - push @{$self->{PARTS}}, $1; + # Retrieve part count, if this is a problem + if ($self->is_problem()) { + my $metadata = &Apache::lonnet::metadata($self->src(), 'allpossiblekeys'); + if (!$metadata) { + $self->{RESOURCE_ERROR} = 1; + $self->{PARTS} = []; + return; } + + foreach (split(/\,/,$metadata)) { + if ($_ =~ /^parameter\_(.*)\_opendate$/) { + push @{$self->{PARTS}}, $1; + } + } + + + # Is this possible to do in one line? - Jeremy + my @sortedParts = sort @{$self->{PARTS}}; + $self->{PARTS} = \@sortedParts; } - - # Is this possible to do in one line? - Jeremy - my @sortedParts = sort @{$self->{PARTS}}; - $self->{PARTS} = \@sortedParts; - return; } @@ -2864,20 +2939,18 @@ sub status { =over 4 -=item * B(): Gets the next resource in the navmap after this one. +=item * B($alreadySeenHashRef): Retreive an array of the possible next resources after this one. Always returns an array, even in the one- or zero-element case. The "alreadySeenHashRef" is an optional parameter that can be passed in to the method. If $$alreadySeenHashRef{$res->id()} is true in that hash, getNext will not return it in the list. In other words, you can use it to suppress resources you've already seen in the getNext method directly. -=cut +=item * B($alreadySeenHashRef): Retreive an array of the possible previous resources from this one. Always returns an array, even in the one- or zero-element case. $alreadySeenHashRef is the same as in getNext. -# For the simple single-link case, to get from a resource to the next -# resource, you need to look up the "to_" link in the nav hash, then -# follow that with the "goesto_" link. +=cut sub getNext { my $self = shift; my $alreadySeenHash = shift; my @branches; my $to = $self->to(); - foreach my $branch ( split(/\,/, $to) ) { + foreach my $branch ( split(/,/, $to) ) { my $choice = $self->{NAV_MAP}->getById($branch); my $next = $choice->goesto(); $next = $self->{NAV_MAP}->getById($next); @@ -2893,6 +2966,28 @@ sub getNext { } return \@branches; } + +sub getPrevious { + my $self = shift; + my $alreadySeenHash = shift; + my @branches; + my $from = $self->from(); + foreach my $branch ( split /,/, $from) { + my $choice = $self->{NAV_MAP}->getById($branch); + my $prev = $choice->comesfrom(); + $prev = $self->{NAV_MAP}->getById($prev); + + # Skip it if we've already seen it or the user doesn't have + # browse privs + my $browsePriv = &Apache::lonnet::allowed('bre', $self->src); + if (!defined($alreadySeenHash) || + !defined($alreadySeenHash->{$prev->{ID}}) || + ($browsePriv ne '2' && $browsePriv ne 'F')) { + push @branches, $prev; + } + } + return \@branches; +} =pod
\n"); + $r->print("
\n"); # print indentation - for (my $i = 0; $i < $indentLevel - $deltalevel; $i++) { + for (my $i = 0; $i < $indentLevel - $deltalevel + $deltadepth; $i++) { $r->print($indentString); } @@ -1209,12 +1263,12 @@ sub new_handle { if ($curRes->{RESOURCE_ERROR}) { $r->print(&Apache::loncommon::help_open_topic ("Navmap_Host_Down", - 'Host down')); + 'Host down')); } my $discussionHTML = ""; my $feedbackHTML = ""; - # SECOND COL: Is there text or feedback? + # SECOND COL: Is there text, feedback, errors?? if ($curRes->hasDiscussion()) { $discussionHTML = $linkopen . '' . @@ -1233,7 +1287,7 @@ sub new_handle { } } - $r->print("$discussionHTML$feedbackHTML$discussionHTML$feedbackHTML $linkopen\"$alt\"$linkclose$linkopen\"$alt\"$linkclose  \n"); + #$r->print("\n"); + $r->print("\n"); if ($curRes->kind() eq "res" && $curRes->is_problem() && !$firstDisplayed) { + $r->print ("") if ($color); $r->print (getDescription($curRes, $part)); + $r->print ("") if ($color); } if ($curRes->is_map() && advancedUser() && $curRes->randompick()) { $r->print('(randomly select ' . $curRes->randompick() .')'); } - $r->print("