--- loncom/interface/lonnavmaps.pm 2003/02/04 16:14:27 1.134 +++ loncom/interface/lonnavmaps.pm 2003/02/06 22:58:12 1.139 @@ -2,7 +2,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.134 2003/02/04 16:14:27 bowersj2 Exp $ +# $Id: lonnavmaps.pm,v 1.139 2003/02/06 22:58:12 bowersj2 Exp $ # # Copyright Michigan State University Board of Trustees # @@ -56,6 +56,43 @@ sub NOTHING { return 3; } # Some data +# Keep these mappings in sync with lonquickgrades, which uses the colors +# instead of the icons. +my %statusIconMap = + ( Apache::lonnavmaps::resource->NETWORK_FAILURE => '', + Apache::lonnavmaps::resource->NOTHING_SET => '', + Apache::lonnavmaps::resource->CORRECT => 'navmap.correct.gif', + Apache::lonnavmaps::resource->EXCUSED => 'navmap.correct.gif', + Apache::lonnavmaps::resource->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif', + Apache::lonnavmaps::resource->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif', + Apache::lonnavmaps::resource->ANSWER_OPEN => 'navmap.wrong.gif', + Apache::lonnavmaps::resource->OPEN_LATER => '', + Apache::lonnavmaps::resource->TRIES_LEFT => 'navmap.open.gif', + Apache::lonnavmaps::resource->INCORRECT => 'navmap.wrong.gif', + Apache::lonnavmaps::resource->OPEN => 'navmap.open.gif', + Apache::lonnavmaps::resource->ATTEMPTED => 'navmap.open.gif' ); + +my %iconAltTags = + ( 'navmap.correct.gif' => 'Correct', + 'navmap.wrong.gif' => 'Incorrect', + 'navmap.open.gif' => 'Open' ); + +# Defines a status->color mapping, null string means don't color +my %colormap = + ( Apache::lonnavmaps::resource->NETWORK_FAILURE => '', + Apache::lonnavmaps::resource->CORRECT => '', + Apache::lonnavmaps::resource->EXCUSED => '#3333FF', + Apache::lonnavmaps::resource->PAST_DUE_ANSWER_LATER => '', + Apache::lonnavmaps::resource->PAST_DUE_NO_ANSWER => '', + Apache::lonnavmaps::resource->ANSWER_OPEN => '#006600', + Apache::lonnavmaps::resource->OPEN_LATER => '', + Apache::lonnavmaps::resource->TRIES_LEFT => '', + Apache::lonnavmaps::resource->INCORRECT => '', + Apache::lonnavmaps::resource->OPEN => '', + Apache::lonnavmaps::resource->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 = "#FF0000"; sub cleanup { if (tied(%navmaphash)){ @@ -215,55 +252,7 @@ sub real_handler { return OK; } - # Grab a resource object so we have access to the constants; this - # is technically not proper, but should be harmless - my $res = $navmap->firstResource(); - - # These are some data tables, which make it easy to change some of - # of the specific visualization parameters if desired. - - # Defines a status->color mapping, null string means don't color - my %colormap = - ( $res->NETWORK_FAILURE => '', - $res->CORRECT => '', - $res->EXCUSED => '#3333FF', - $res->PAST_DUE_ANSWER_LATER => '', - $res->PAST_DUE_NO_ANSWER => '', - $res->ANSWER_OPEN => '#006600', - $res->OPEN_LATER => '', - $res->TRIES_LEFT => '', - $res->INCORRECT => '', - $res->OPEN => '', - $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 = "#FF0000"; - - # Keep these mappings in sync with lonquickgrades, which uses the colors - # instead of the icons. - my %statusIconMap = - ( $res->NETWORK_FAILURE => '', - $res->NOTHING_SET => '', - $res->CORRECT => 'navmap.correct.gif', - $res->EXCUSED => 'navmap.correct.gif', - $res->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif', - $res->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif', - $res->ANSWER_OPEN => 'navmap.wrong.gif', - $res->OPEN_LATER => '', - $res->TRIES_LEFT => 'navmap.open.gif', - $res->INCORRECT => 'navmap.wrong.gif', - $res->OPEN => 'navmap.open.gif', - $res->ATTEMPTED => 'navmap.open.gif' ); - - my %iconAltTags = - ( 'navmap.correct.gif' => 'Correct', - 'navmap.wrong.gif' => 'Incorrect', - 'navmap.open.gif' => 'Open' ); - - my %condenseStatuses = - ( $res->NETWORK_FAILURE => 1, - $res->NOTHING_SET => 1, - $res->CORRECT => 1 ); + my $res = "Apache::lonnavmaps::resource"; my %filterHash; # Figure out what we're not displaying @@ -273,10 +262,6 @@ sub real_handler { } } - # 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"); - # Begin the HTML table # four cols: resource + indent, chat+feedback, icon, text string $r->print('' ."\n"); @@ -285,7 +270,7 @@ sub real_handler { # instead of uris. The changes to this and the main rendering # loop should be obvious. # Here's a simple example of the iterator. - # Preprocess the map: Look for current URL, force inlined maps to display + # Preprocess the map: Look for current URL, open necessary maps my $mapIterator = $navmap->getIterator(undef, undef, undef, 1); my $found = 0; @@ -355,364 +340,13 @@ sub real_handler { $curRes = $mapIterator->next(); } - undef $res; # so we don't accidentally use it later - my $indentLevel = 0; - my $indentString = ""; - - my $isNewBranch = 0; - my $now = time(); - my $in24Hours = $now + 24 * 60 * 60; - my $displayedHereMarker = 0; - my $displayedJumpMarker = 0; - - # We know the first thing is a BEGIN_MAP (see "$self->{STARTED}" - # code in iterator->next), so ignore the first one - $mapIterator = $navmap->getIterator(undef, undef, \%filterHash, - $condition); - $mapIterator->next(); - $curRes = $mapIterator->next(); - $depth = 1; - - my @backgroundColors = ("#FFFFFF", "#F6F6F6"); - my $rowNum = 0; - - $counter = 0; - - while ($depth > 0) { - if ($curRes == $mapIterator->BEGIN_MAP() || - $curRes == $mapIterator->BEGIN_BRANCH()) { - $indentLevel++; - } - if ($curRes == $mapIterator->END_MAP() || - $curRes == $mapIterator->END_BRANCH()) { - $indentLevel--; - } - if ($curRes == $mapIterator->BEGIN_BRANCH()) { - $isNewBranch = 1; - } - if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; } - if ($curRes == $mapIterator->END_MAP()) { $depth--; } - - if (ref($curRes)) { $counter++; } - - if (ref($curRes)) { - - my $deltalevel = $isNewBranch? 1 : 0; # reserves space for branch icon - - if ($indentLevel - $deltalevel < 0) { - # If this would be at a negative depth (top-level maps in - # new-style courses, we want to suppress their title display) - # then ignore it. - $curRes = $mapIterator->next(); - next; - } - - # Step one: Decide which parts to show - my @parts = @{$curRes->parts()}; - my $multipart = scalar(@parts) > 1; - my $condensed = 0; - - if ($curRes->is_problem()) { - - # Is it multipart? - if ($multipart) { - # If it's multipart, see if part 0 is "open" - # if it is, display all parts, if it isn't, - # just display first - if (!$curRes->opendate("0")) { - # no parts are open, display as one part - @parts = ("0"); - $condensed = 1; - } else { - # Otherwise, only display part 0 if we want to - # attach feedback or email information to it - if ($curRes->hasDiscussion() || $curRes->getFeedback()) { - # Is this right? I think this will toss it - # if it DOES have discussion, not if it doesn't? - # - Jeremy (yes, commenting on his own code) - shift @parts; - } else { - # Now, we decide whether to condense the - # parts due to similarity - my $status = $curRes->status($parts[1]); - my $due = $curRes->duedate($parts[1]); - my $open = $curRes->opendate($parts[1]); - my $statusAllSame = 1; - my $dueAllSame = 1; - my $openAllSame = 1; - for (my $i = 2; $i < scalar(@parts); $i++) { - if ($curRes->status($parts[$i]) != $status){ - $statusAllSame = 0; - } - if ($curRes->duedate($parts[$i]) != $due ) { - $dueAllSame = 0; - } - if ($curRes->opendate($parts[$i]) != $open) { - $openAllSame = 0; - } - } - - # $*allSame is true if all the statuses were - # the same. Now, if they are all the same and - # match one of the statuses to condense, or they - # are all open with the same due date, or they are - # all OPEN_LATER with the same open date, display the - # status of the first non-zero part (to get the 'correct' - # status right, since 0 is never 'correct' or 'open'). - if (($statusAllSame && defined($condenseStatuses{$status})) || - ($dueAllSame && $status == $curRes->OPEN && $statusAllSame)|| - ($openAllSame && $status == $curRes->OPEN_LATER && $statusAllSame) ){ - @parts = ($parts[1]); - $condensed = 1; - } - } - } - } - - } else { - $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, and it simplifies the looping - } - - # 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, since we can't - # retreive the metadata to count the parts - if ($curRes->{RESOURCE_ERROR}) { - @parts = ("0"); - } - - # Step Two: Print the actual data. - - # For each part we intend to display... - foreach my $part (@parts) { - - my $nonLinkedText = ""; # unlinked stuff after title - - my $stack = $mapIterator->getStack(); - my $src = getLinkForResource($stack); - - # Pass the correct symb on the querystring, so the - # remote will figure out where we are if we click a link - my $srcHasQuestion = $src =~ /\?/; - my $link = $src. - ($srcHasQuestion?'&':'?') . - 'symb='.&Apache::lonnet::escape($curRes->symb()). - '"'; - - my $title = $curRes->compTitle(); - if ($src=~/^\/uploaded\//) { - $nonLinkedText=$title; - $title=''; - } - my $partLabel = ""; - my $newBranchText = ""; - - # If this is a new branch, label it so - if ($isNewBranch) { - $newBranchText = ""; - $isNewBranch = 0; - } - - # links to open and close the folders - my $linkopen = ""; - my $linkclose = ""; - - my $icon = "\"\""; - if ($curRes->is_problem()) { - if ($part eq "0" || $condensed) { - $icon = ''; - } else { - $icon = $indentString; - } - } - - # Display the correct icon, link to open or shut map - if ($curRes->is_map()) { - my $mapId = $curRes->map_pc(); - my $nowOpen = (!defined($filterHash{$mapId})); - if ($condition) {$nowOpen = !$nowOpen;} - $icon = $nowOpen ? - "navmap.folder.closed.gif" : "navmap.folder.open.gif"; - $icon = "\"\""; - $linkopen = ""; - $linkclose = ""; - - } - - my $colorizer = ""; - my $color; - if ($curRes->is_problem()) { - $color = $colormap{$curRes->status}; - - if (dueInLessThen24Hours($curRes, $part) || - lastTry($curRes, $part)) { - $color = $hurryUpColor; - } - - if ($color ne "") { - $colorizer = "bgcolor=\"$color\""; - } - } - - if ($curRes->randomout()) { - $nonLinkedText .= ' (hidden) '; - } - - $rowNum++; - my $backgroundColor = $backgroundColors[$rowNum % scalar(@backgroundColors)]; - - # FIRST COL: The resource indentation, branch icon, name, and anchor - $r->print(" \n"); - - # SECOND COL: Is there text, feedback, errors?? - my $discussionHTML = ""; my $feedbackHTML = ""; my $errorHTML = ""; - - if ($curRes->hasDiscussion()) { - $discussionHTML = $linkopen . - '' . - $linkclose; - } - - if ($curRes->getFeedback()) { - my $feedback = $curRes->getFeedback(); - foreach (split(/\,/, $feedback)) { - if ($_) { - $feedbackHTML .= ' ' - . ''; - } - } - } - - if ($curRes->getErrors()) { - my $errors = $curRes->getErrors(); - foreach (split(/,/, $errors)) { - if ($_) { - $errorHTML .= ' ' - . ''; - } - } - } - - $r->print(""); - - # Is this the first displayed part of a multi-part problem - # that has not been condensed, so we should suppress these two - # columns so we don't display useless status info about part - # "0"? - my $firstDisplayed = !$condensed && $multipart && $part eq "0"; - - # THIRD COL: Problem status icon - if ($curRes->is_problem() && - !$firstDisplayed) { - my $icon = $statusIconMap{$curRes->status($part)}; - my $alt = $iconAltTags{$icon}; - if ($icon) { - $r->print("\n"); - } else { - $r->print("\n"); - } - } else { # not problem, no icon - $r->print("\n"); - } - - # FOURTH COL: Text description - $r->print("\n"); - - if (!($counter % 20)) { $r->rflush(); } - if ($counter == 2) { $r->rflush(); } - } - } - $curRes = $mapIterator->next(); - } - - $r->print("
\n"); - - # Print the anchor if necessary - if ($counter == $currentJumpIndex - $currentJumpDelta ) { - $r->print(''); - $displayedJumpMarker = 1; - } - - # print indentation - for (my $i = 0; $i < $indentLevel - $deltalevel; $i++) { - $r->print($indentString); - } - - $r->print(" ${newBranchText}${linkopen}$icon${linkclose}\n"); - - my $curMarkerBegin = ""; - my $curMarkerEnd = ""; - - # Is this the current resource? - if (!$displayedHereMarker && - (($hereType == SYMB() && $curRes->symb eq $here) || - ($hereType == URL() && $curRes->src eq $here))) { - $curMarkerBegin = '> '; - $curMarkerEnd = ' <'; - $displayedHereMarker = 1; - } - - if ($curRes->is_problem() && $part ne "0" && !$condensed) { - $partLabel = " (Part $part)"; - $title = ""; - } - if ($multipart && $condensed) { - $nonLinkedText .= ' (' . $curRes->countParts() . ' parts)'; - } - - $r->print(" $curMarkerBegin$title$partLabel $curMarkerEnd $nonLinkedText"); - - #if ($curRes->{RESOURCE_ERROR}) { - # $r->print(&Apache::loncommon::help_open_topic ("Navmap_Host_Down", - # 'Host down')); - # } - - $r->print("$discussionHTML$feedbackHTML$errorHTML $linkopen\"$alt\"$linkclose  \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(" 
"); - - # Print out the part that jumps to #curloc if it exists - if ($displayedJumpMarker) { - $r->print(''); - } - # renderer call $mapIterator = $navmap->getIterator(undef, undef, \%filterHash, 0); my $render = render({ 'cols' => [0,1,2,3], 'iterator' => $mapIterator, 'url' => '/adm/navmaps', - 'queryString' => 'alreadyHere=1' }); - $r->print('|' . $render . '|'); + 'queryString' => 'alreadyHere=1', + 'currentJumpIndex' => $currentJumpIndex}); + $r->print($render); $navmap->untieHashes(); @@ -1042,6 +676,10 @@ Note these functions are responsible for =item * B: The url the folders will link to, which should be the current page. Required if the resource info column is shown. +=item * B: Describes the currently-open row number to cause the browser to jump to, because the user just opened that folder. + +=item * B: The standard Apache response object. If you pass this to the render, it will use it to flush the table every 20 rows and handle the rendering itself. + =back =head2 Additional Info @@ -1061,24 +699,6 @@ sub long_status { return 3; } # Data for render_resource -my $resObj = 'Apache::lonnavmaps::resource'; -# Defines a status->color mapping, null string means don't color -my %colormap = - ( $resObj->NETWORK_FAILURE => '', - $resObj->CORRECT => '', - $resObj->EXCUSED => '#3333FF', - $resObj->PAST_DUE_ANSWER_LATER => '', - $resObj->PAST_DUE_NO_ANSWER => '', - $resObj->ANSWER_OPEN => '#006600', - $resObj->OPEN_LATER => '', - $resObj->TRIES_LEFT => '', - $resObj->INCORRECT => '', - $resObj->OPEN => '', - $resObj->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 = "#FF0000"; - sub render_resource { my ($resource, $part, $params) = @_; @@ -1137,7 +757,7 @@ sub render_resource { . $params->{'hereType'} . '&here=' . &Apache::lonnet::escape($params->{'here'}) . '&jumpType=' . SYMB() . '&jump=' . - &Apache::lonnet::escape($params->{$resource->symb()}) . "'>"; + &Apache::lonnet::escape($resource->symb()) . "'>"; } if ($resource->randomout()) { @@ -1227,11 +847,59 @@ sub render_communication_status { } sub render_quick_status { my ($resource, $part, $params) = @_; - return "quick_status"; + my $result = ""; + my $firstDisplayed = !$params->{'condensed'} && + $params->{'multipart'} && $part eq "0"; + + my $link = $params->{"resourceLink"}; + my $linkopen = ""; + my $linkclose = ""; + + if ($resource->is_problem() && + !$firstDisplayed) { + my $icon = $statusIconMap{$resource->status($part)}; + my $alt = $iconAltTags{$icon}; + if ($icon) { + $result .= "$linkopen$alt$linkclose\n"; + } else { + $result .= " \n"; + } + } else { # not problem, no icon + $result .= " \n"; + } + + return $result; } sub render_long_status { my ($resource, $part, $params) = @_; - return "long_status"; + my $result = "\n"; + my $firstDisplayed = !$params->{'condensed'} && + $params->{'multipart'} && $part eq "0"; + + my $color; + if ($resource->is_problem()) { + $color = $colormap{$resource->status}; + + if (dueInLessThen24Hours($resource, $part) || + lastTry($resource, $part)) { + $color = $hurryUpColor; + } + } + + if ($resource->kind() eq "res" && + $resource->is_problem() && + !$firstDisplayed) { + if ($color) {$result .= ""; } + $result .= getDescription($resource, $part); + if ($color) {$result .= ""; } + } + if ($resource->is_map() && advancedUser() && $resource->randompick()) { + $result .= '(randomly select ' . $resource->randompick() .')'; + } + + $result .= " \n"; + + return $result; } my @preparedColumns = (\&render_resource, \&render_communication_status, @@ -1264,9 +932,15 @@ sub render { my $jumpToSymb = $args->{'jumpToSymb'}; my $hereURL = $args->{'hereURL'}; my $hereSymb = $args->{'hereSymb'}; + # keeps track of when the current resource is found, + # so we can back up a few and put the anchor above the + # current resource + my $currentJumpIndex = setDefault($args->{'currentJumpIndex'}, 0); + my $currentJumpDelta = 2; # change this to change how many resources are displayed + # before the current resource when using #current + my $r = $args->{'r'}; + - #if (defined($jumpToURL)) { - # $args->{'jumpType'} = # End parameter setting @@ -1278,11 +952,6 @@ sub render { $res->NOTHING_SET => 1, $res->CORRECT => 1 ); my @backgroundColors = ("#FFFFFF", "#F6F6F6"); - my $currentJumpIndex = 0; # keeps track of when the current resource is found, - # so we can back up a few and put the anchor above the - # current resource - my $currentJumpDelta = 2; # change this to change how many resources are displayed - # before the current resource when using #current # Shared variables $args->{'counter'} = 0; # counts the rows @@ -1421,40 +1090,44 @@ sub render { my $srcHasQuestion = $src =~ /\?/; $args->{"resourceLink"} = $src. ($srcHasQuestion?'&':'?') . - 'symb=' . &Apache::lonnet::escape($curRes->symb()). - '"'; + 'symb=' . &Apache::lonnet::escape($curRes->symb()); # Now, display each column. foreach my $col (@$cols) { + my $colHTML = ''; + if (ref($col)) { + $colHTML .= &$col($curRes, $part, $args); + } else { + $colHTML .= &{$preparedColumns[$col]}($curRes, $part, $args); + } # If this is the first column and it's time to print # the anchor, do so if ($col == $cols->[0] && $args->{'counter'} == $args->{'currentJumpIndex'} - - $args->{'currentJumpDelta'}) { - $result .= ''; + $currentJumpDelta) { + # Jam the anchor after the tag; + # necessary for valid HTML (which Mozilla requires) + $colHTML =~ s/\>/\>\/; $displayedJumpMarker = 1; } - - - if (ref($col)) { - $result .= &$col($curRes, $part, $args); - } else { - $result .= &{$preparedColumns[$col]}($curRes, $part, $args); - } - + $result .= $colHTML . "\n"; } - - $result .= " \n"; + $result .= " \n"; + } + + if ($r && $rownum % 20 == 0) { + $r->print($result); + $result = ""; + $r->rflush(); } - $curRes = $it->next(); } # Print out the part that jumps to #curloc if it exists - if ($args->{"displayedJumpMarker"}) { - $result .= "\n"; + if ($displayedJumpMarker) { + $result .= "\n"; } $result .= ""; @@ -2205,7 +1878,9 @@ sub next { # If this is a blank resource, don't actually return it. # Should you ever find you need it, make sure to add an option to the code # that you can use; other things depend on this behavior. - if (!$self->{HERE}->src() || !$self->{HERE}->browsePriv()) { + my $browsePriv = $self->{HERE}->browsePriv(); + if (!$self->{HERE}->src() || + (!($browsePriv eq 'F') && !($browsePriv eq '2')) ) { return $self->next(); }