--- loncom/interface/lonnavmaps.pm 2003/02/06 22:58:12 1.139
+++ loncom/interface/lonnavmaps.pm 2003/06/23 15:53:35 1.209
@@ -1,8 +1,7 @@
-
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.139 2003/02/06 22:58:12 bowersj2 Exp $
+# $Id: lonnavmaps.pm,v 1.209 2003/06/23 15:53:35 bowersj2 Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -38,16 +37,17 @@
# YEAR=2002
# 1/1 Gerd Kortemeyer
# Oct-Nov Jeremy Bowers
+# YEAR=2003
+# Jeremy Bowers ... lots of days
package Apache::lonnavmaps;
use strict;
use Apache::Constants qw(:common :http);
use Apache::loncommon();
+use Apache::lonmenu();
use POSIX qw (floor strftime);
-
-my %navmaphash;
-my %parmhash;
+use Data::Dumper; # for debugging, not always used
# symbolic constants
sub SYMB { return 1; }
@@ -56,21 +56,24 @@ sub NOTHING { return 3; }
# Some data
+my $resObj = "Apache::lonnavmaps::resource";
+
# 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' );
+ ( $resObj->NETWORK_FAILURE => '',
+ $resObj->NOTHING_SET => '',
+ $resObj->CORRECT => 'navmap.correct.gif',
+ $resObj->EXCUSED => 'navmap.correct.gif',
+ $resObj->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif',
+ $resObj->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif',
+ $resObj->ANSWER_OPEN => 'navmap.wrong.gif',
+ $resObj->OPEN_LATER => '',
+ $resObj->TRIES_LEFT => 'navmap.open.gif',
+ $resObj->INCORRECT => 'navmap.wrong.gif',
+ $resObj->OPEN => 'navmap.open.gif',
+ $resObj->ATTEMPTED => 'navmap.ellipsis.gif',
+ $resObj->ANSWER_SUBMITTED => '' );
my %iconAltTags =
( 'navmap.correct.gif' => 'Correct',
@@ -79,36 +82,24 @@ my %iconAltTags =
# 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 => '' );
+ ( $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 => '',
+ $resObj->ATTEMPTED => '',
+ $resObj->ANSWER_SUBMITTED => ''
+ );
# 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)){
- &Apache::lonnet::logthis('Cleanup navmaps: navmaphash');
- unless (untie(%navmaphash)) {
- &Apache::lonnet::logthis('Failed cleanup navmaps: navmaphash');
- }
- }
- if (tied(%parmhash)){
- &Apache::lonnet::logthis('Cleanup navmaps: parmhash');
- unless (untie(%parmhash)) {
- &Apache::lonnet::logthis('Failed cleanup navmaps: parmhash');
- }
- }
-}
-
sub handler {
my $r = shift;
real_handler($r);
@@ -117,8 +108,6 @@ sub handler {
sub real_handler {
my $r = shift;
- &Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});
-
# Handle header-only request
if ($r->header_only) {
if ($ENV{'browser.mathml'}) {
@@ -152,11 +141,22 @@ sub real_handler {
}
$r->print("
\n");
- $r->print("Navigate Course Contents");
+ $r->print("Navigate Course Contents");
+# ------------------------------------------------------------ Get query string
+ &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register']);
+
+# ----------------------------------------------------- Force menu registration
+ my $addentries='';
+ if ($ENV{'form.register'}) {
+ $addentries=' onLoad="'.&Apache::lonmenu::loadevents().
+ '" onUnload="'.&Apache::lonmenu::unloadevents().'"';
+ $r->print(&Apache::lonmenu::registerurl(1));
+ }
# Header
- $r->print(&Apache::loncommon::bodytag('Navigate Course Contents','',
- ''));
+ $r->print(''.
+ &Apache::loncommon::bodytag('Navigate Course Contents','',
+ $addentries,'','',$ENV{'form.register'}));
$r->print('');
$r->rflush();
@@ -164,85 +164,6 @@ sub real_handler {
# Now that we've displayed some stuff to the user, init the navmap
$navmap->init();
- $r->print('');
-
- my $condition = 0;
- if ($ENV{'form.condition'}) {
- $condition = 1;
- }
-
- # Determine where the "here" marker is and where the screen jumps to.
- my $hereType; # the type of marker, SYMB, URL, or NOTHING
- my $here; # the actual URL or SYMB for the here marker
- my $jumpType; # The type of the thing we have a jump for, SYMB or URL
- my $jump; # the SYMB/URL of the resource we need to jump to
-
- if ( $ENV{'form.alreadyHere'} ) { # we came from a user's manipulation of the nav page
- # If this is a click on a folder or something, we want to preserve the "here"
- # from the querystring, and get the new "jump" marker
- $hereType = $ENV{'form.hereType'};
- $here = $ENV{'form.here'};
- $jumpType = $ENV{'form.jumpType'} || NOTHING();
- $jump = $ENV{'form.jump'};
- } else { # the user is visiting the nav map from the remote
- # We're coming from the remote. We have either a url, a symb, or nothing,
- # and we need to figure out what.
- # Preference: Symb
-
- if ($ENV{'form.symb'}) {
- $hereType = $jumpType = SYMB();
- $here = $jump = $ENV{'form.symb'};
- } elsif ($ENV{'form.postdata'}) {
- # couldn't find a symb, is there a URL?
- my $currenturl = $ENV{'form.postdata'};
- $currenturl=~s/^http\:\/\///;
- $currenturl=~s/^[^\/]+//;
-
- $hereType = $jumpType = URL;
- $here = $jump = $currenturl;
- } else {
- # Nothing
- $hereType = $jumpType = NOTHING();
- }
- }
-
-
- # alreadyHere allows us to only open the maps necessary to view
- # the current location once, while at the same time remembering
- # the current location. Without that check, the user would never
- # be able to close those maps; the user would close it, and the
- # currenturl scan would re-open it.
- my $queryAdd = "&alreadyHere=1";
-
- if ($condition) {
- $r->print("Close All Folders");
- } else {
- $r->print("Open All Folders");
- }
-
- $r->print('
');
$r->rflush();
# Check that it's defined
@@ -252,104 +173,125 @@ sub real_handler {
return OK;
}
- my $res = "Apache::lonnavmaps::resource";
-
- my %filterHash;
- # Figure out what we're not displaying
- foreach (split(/\,/, $ENV{"form.filter"})) {
- if ($_) {
- $filterHash{$_} = "1";
+ # See if there's only one map in the top-level, if we don't
+ # already have a filter... if so, automatically display it
+ if ($ENV{QUERY_STRING} !~ /filter/) {
+ my $iterator = $navmap->getIterator(undef, undef, undef, 0);
+ my $depth = 1;
+ $iterator->next();
+ my $curRes = $iterator->next();
+ my $sequenceCount = 0;
+ my $sequenceId;
+ while ($depth > 0) {
+ if ($curRes == $iterator->BEGIN_MAP()) { $depth++; }
+ if ($curRes == $iterator->END_MAP()) { $depth--; }
+
+ if (ref($curRes) && $curRes->is_sequence()) {
+ $sequenceCount++;
+ $sequenceId = $curRes->map_pc();
+ }
+
+ $curRes = $iterator->next();
+ }
+
+ if ($sequenceCount == 1) {
+ # The automatic iterator creation in the render call
+ # will pick this up. We know the condition because
+ # the defined($ENV{'form.filter'}) also ensures this
+ # is a fresh call.
+ $ENV{'form.filter'} = "$sequenceId";
}
}
- # Begin the HTML table
- # four cols: resource + indent, chat+feedback, icon, text string
- $r->print('' ."\n");
-
- # This needs to be updated to use symbs from the remote,
- # 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, open necessary maps
-
- my $mapIterator = $navmap->getIterator(undef, undef, undef, 1);
- my $found = 0;
- my $depth = 1;
- 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
- $mapIterator->next(); # discard the first BEGIN_MAP
- my $curRes = $mapIterator->next();
- my $counter = 0;
- my $foundJump = ($jumpType == NOTHING()); # look for jump point if we have one
- my $looped = 0;
+ my $jumpToFirstHomework = 0;
+ # Check to see if the student is jumping to next open, do-able problem
+ if ($ENV{QUERY_STRING} eq 'jumpToFirstHomework') {
+ $jumpToFirstHomework = 1;
+ # Find the next homework problem that they can do.
+ my $iterator = $navmap->getIterator(undef, undef, undef, 1);
+ my $depth = 1;
+ $iterator->next();
+ my $curRes = $iterator->next();
+ my $foundDoableProblem = 0;
+ my $problemRes;
+
+ while ($depth > 0 && !$foundDoableProblem) {
+ if ($curRes == $iterator->BEGIN_MAP()) { $depth++; }
+ if ($curRes == $iterator->END_MAP()) { $depth--; }
- # We only need to do this if we need to open the maps to show the
- # current position. This will change the counter so we can't count
- # for the jump marker with this loop.
- while ($depth > 0 && !$ENV{'form.alreadyHere'}) {
- if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; }
- if ($curRes == $mapIterator->END_MAP()) { $depth--; }
+ if (ref($curRes) && $curRes->is_problem()) {
+ my $status = $curRes->status();
+ if ($curRes->completable()) {
+ $problemRes = $curRes;
+ $foundDoableProblem = 1;
+
+ # Pop open all previous maps
+ my $stack = $iterator->getStack();
+ pop @$stack; # last resource in the stack is the problem
+ # itself, which we don't need in the map stack
+ my @mapPcs = map {$_->map_pc()} @$stack;
+ $ENV{'form.filter'} = join(',', @mapPcs);
- if (ref($curRes) && !$ENV{'form.alreadyHere'} &&
- ($hereType == SYMB() && $curRes->symb() eq $here) ||
- (ref($curRes) && $hereType == URL() && $curRes->src() eq $here)) {
- my $mapStack = $mapIterator->getStack();
-
- # Ensure the parent maps are open
- for my $map (@{$mapStack}) {
- if ($condition) {
- undef $filterHash{$map->map_pc()};
- } else {
- $filterHash{$map->map_pc()} = 1;
+ # Mark as both "here" and "jump"
+ $ENV{'form.postsymb'} = $curRes->symb();
}
}
- $ENV{'form.alreadyHere'} = 1;
+ } continue {
+ $curRes = $iterator->next();
}
- $looped = 1;
-
- $curRes = $mapIterator->next();
- }
-
- $mapIterator = $navmap->getIterator(undef, undef, \%filterHash, 0);
- $depth = 1;
- $mapIterator->next();
- $curRes = $mapIterator->next();
-
- while ($depth > 0 && !$foundJump) {
- if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; }
- if ($curRes == $mapIterator->END_MAP()) { $depth--; }
- if (ref($curRes)) { $counter++; }
-
- if (ref($curRes) &&
- (($jumpType == SYMB() && $curRes->symb() eq $jump) ||
- ($jumpType == URL() && $curRes->src() eq $jump))) {
- # If this is the correct resource, be sure to
- # show it by making sure the containing maps
- # are open.
- # This is why we have to use the main iterator instead of the
- # potentially faster DFS: The count has to be the same, so
- # the order has to be the same, which DFS won't give us.
- $currentJumpIndex = $counter;
- $foundJump = 1;
+ # If we found no problems, print a note to that effect.
+ if (!$foundDoableProblem) {
+ $r->print("All homework assignments have been completed.
");
}
+ } else {
+ $r->print("" .
+ "Go To My First Homework Problem ");
+ }
- $curRes = $mapIterator->next();
+ my $suppressEmptySequences = 0;
+ my $filterFunc = undef;
+ my $resource_no_folder_link = 0;
+
+ # Display only due homework.
+ my $showOnlyHomework = 0;
+ if ($ENV{QUERY_STRING} eq 'showOnlyHomework') {
+ $showOnlyHomework = 1;
+ $suppressEmptySequences = 1;
+ $filterFunc = sub { my $res = shift;
+ return $res->completable() || $res->is_map();
+ };
+ $r->print("Uncompleted Homework
");
+ $ENV{'form.filter'} = '';
+ $ENV{'form.condition'} = 1;
+ $resource_no_folder_link = 1;
+ } else {
+ $r->print("" .
+ "Show Only Uncompleted Homework ");
}
-
- # 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',
- 'currentJumpIndex' => $currentJumpIndex});
- $r->print($render);
+ # renderer call
+ my $renderArgs = { 'cols' => [0,1,2,3],
+ 'url' => '/adm/navmaps',
+ 'navmap' => $navmap,
+ 'suppressNavmap' => 1,
+ 'suppressEmptySequences' => $suppressEmptySequences,
+ 'filterFunc' => $filterFunc,
+ 'resource_no_folder_link' => $resource_no_folder_link,
+ 'r' => $r};
+ my $render = render($renderArgs);
$navmap->untieHashes();
+ # If no resources were printed, print a reassuring message so the
+ # user knows there was no error.
+ if ($renderArgs->{'counter'} == 0) {
+ if ($showOnlyHomework) {
+ $r->print("All homework is currently completed.
");
+ } else { # both jumpToFirstHomework and normal use the same: course must be empty
+ $r->print("This course is empty.
");
+ }
+ }
+
$r->print("