Diff for /loncom/interface/lonnavmaps.pm between versions 1.101 and 1.114

version 1.101, 2002/11/08 20:58:13 version 1.114, 2002/11/18 21:12:39
Line 37 Line 37
 # 3/1/1,6/1,17/1,29/1,30/1,2/8,9/21,9/24,9/25 Gerd Kortemeyer  # 3/1/1,6/1,17/1,29/1,30/1,2/8,9/21,9/24,9/25 Gerd Kortemeyer
 # YEAR=2002  # YEAR=2002
 # 1/1 Gerd Kortemeyer  # 1/1 Gerd Kortemeyer
 #  # Oct-Nov Jeremy Bowers
   
 package Apache::lonnavmaps;  package Apache::lonnavmaps;
   
 use strict;  use strict;
 use Apache::Constants qw(:common :http);  use Apache::Constants qw(:common :http);
 use Apache::lonnet();  
 use Apache::loncommon();  use Apache::loncommon();
 use GDBM_File;  
 use POSIX qw (floor strftime);  use POSIX qw (floor strftime);
   
 sub handler {  sub handler {
Line 73  sub handler { Line 71  sub handler {
     &Apache::loncommon::no_cache($r);      &Apache::loncommon::no_cache($r);
     $r->send_http_header;      $r->send_http_header;
   
     # Create the nav map the nav map      # Create the nav map
     my $navmap = Apache::lonnavmaps::navmap->new(      my $navmap = Apache::lonnavmaps::navmap->new(
                         $ENV{"request.course.fn"}.".db",                          $ENV{"request.course.fn"}.".db",
                         $ENV{"request.course.fn"}."_parms.db", 1, 1);                          $ENV{"request.course.fn"}."_parms.db", 1, 1);
Line 85  sub handler { Line 83  sub handler {
         return HTTP_NOT_ACCEPTABLE;          return HTTP_NOT_ACCEPTABLE;
     }      }
   
       $r->print("<html><head>\n");
       $r->print("<title>Navigate Course Contents</title>");
   
     # Header      # Header
     $r->print(&Apache::loncommon::bodytag('Navigate Course Map','',      $r->print(&Apache::loncommon::bodytag('Navigate Course Contents','',
                                           ''));                                            ''));
     $r->print('<script>window.focus();</script>');      $r->print('<script>window.focus();</script>');
   
Line 115  sub handler { Line 116  sub handler {
         $condition = 1;          $condition = 1;
     }      }
   
       my $currenturl = $ENV{'form.postdata'};
       $currenturl=~s/^http\:\/\///;
       $currenturl=~s/^[^\/]+//;
   
       # 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 = "postdata=" . &Apache::lonnet::escape($currenturl) .
           "&alreadyHere=1";
   
     if ($condition) {      if ($condition) {
         $r->print('<a href="navmaps?condition=0&filter=">Close All Folders</a>');          $r->print("<a href=\"navmaps?condition=0&filter=&$queryAdd\">Close All Folders</a>");
     } else {      } else {
         $r->print('<a href="navmaps?condition=1&filter=">Open All Folders</a>');          $r->print("<a href=\"navmaps?condition=1&filter=&$queryAdd\">Open All Folders</a>");
     }      }
   
     $r->print('<br>&nbsp;');      $r->print('<br>&nbsp;');
Line 145  sub handler { Line 158  sub handler {
     my %colormap =       my %colormap = 
     ( $res->NETWORK_FAILURE        => '',      ( $res->NETWORK_FAILURE        => '',
       $res->CORRECT                => '',        $res->CORRECT                => '',
       $res->EXCUSED                => '#BBBBFF',        $res->EXCUSED                => '#3333FF',
       $res->PAST_DUE_ANSWER_LATER  => '',        $res->PAST_DUE_ANSWER_LATER  => '',
       $res->PAST_DUE_NO_ANSWER     => '',        $res->PAST_DUE_NO_ANSWER     => '',
       $res->ANSWER_OPEN            => '#CCFFCC',        $res->ANSWER_OPEN            => '#006600',
       $res->OPEN_LATER             => '',        $res->OPEN_LATER             => '',
       $res->TRIES_LEFT             => '',        $res->TRIES_LEFT             => '',
       $res->INCORRECT              => '',        $res->INCORRECT              => '',
Line 194  sub handler { Line 207  sub handler {
     # maps in their own folders, in favor of "inlining" them.      # maps in their own folders, in favor of "inlining" them.
     my $topResource = $navmap->getById("0.0");      my $topResource = $navmap->getById("0.0");
     my $inlineTopLevelMaps = $topResource->src() =~ m|^/uploaded/.*default\.sequence$|;      my $inlineTopLevelMaps = $topResource->src() =~ m|^/uploaded/.*default\.sequence$|;
       my $inlinedelta = $inlineTopLevelMaps? -1 : 0;
     my $currenturl = $ENV{'form.postdata'};  
     $currenturl=~s/^http\:\/\///;  
     $currenturl=~s/^[^\/]+//;  
   
     # 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 = "postdata=" . &Apache::lonnet::escape($currenturl) .  
         "&alreadyHere=1";  
   
     # Begin the HTML table      # Begin the HTML table
     # four cols: resource + indent, chat+feedback, icon, text string      # four cols: resource + indent, chat+feedback, icon, text string
Line 287  sub handler { Line 289  sub handler {
                                            $condition);                                             $condition);
     $mapIterator->next();      $mapIterator->next();
     $curRes = $mapIterator->next();      $curRes = $mapIterator->next();
     my $deltadepth = 0;  
     $depth = 1;      $depth = 1;
   
     my @backgroundColors = ("#FFFFFF", "#F6F6F6");      my @backgroundColors = ("#FFFFFF", "#F6F6F6");
Line 301  sub handler { Line 302  sub handler {
     }      }
   
     while ($depth > 0) {      while ($depth > 0) {
         # If this is an inlined map, cancel the shift to the right,  
         # which has the effect of making the map look inlined  
         if ($inlineTopLevelMaps && scalar(@{$mapIterator->getStack()}) == 1 &&  
             ref($curRes) && $curRes->is_map()) {  
             $deltadepth = -1;  
             $curRes = $mapIterator->next();  
             next;  
         }  
   
         if ($curRes == $mapIterator->BEGIN_MAP() ||          if ($curRes == $mapIterator->BEGIN_MAP() ||
             $curRes == $mapIterator->BEGIN_BRANCH()) {              $curRes == $mapIterator->BEGIN_BRANCH()) {
             $indentLevel++;              $indentLevel++;
Line 326  sub handler { Line 318  sub handler {
   
         if (ref($curRes)) { $counter++; }          if (ref($curRes)) { $counter++; }
   
         if ($depth == 1) { $deltadepth = 0; } # we're done shifting, because we're  
                                               # out of the inlined map  
   
         # Is this resource being ignored because it is in a random-out          # Is this resource being ignored because it is in a random-out
         # map and it was not selected?          # map and it was not selected?
         if (ref($curRes) && !advancedUser() && $curRes->randomout()) {          if (ref($curRes) && !advancedUser() && $curRes->randomout()) {
Line 338  sub handler { Line 327  sub handler {
   
         if (ref($curRes) && $curRes->src()) {          if (ref($curRes) && $curRes->src()) {
   
               my $deltalevel = $isNewBranch? 1 : 0; # reserves space for branch icon
   
               if ($indentLevel - $deltalevel + $inlinedelta < 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              # Step one: Decide which parts to show
             my @parts = @{$curRes->parts()};              my @parts = @{$curRes->parts()};
             my $multipart = scalar(@parts) > 1;              my $multipart = scalar(@parts) > 1;
Line 423  sub handler { Line 422  sub handler {
             # For each part we intend to display...              # For each part we intend to display...
             foreach my $part (@parts) {              foreach my $part (@parts) {
   
                 my $deltalevel = 0; # for inserting the branch icon  
                 my $nonLinkedText = ""; # unlinked stuff after title                  my $nonLinkedText = ""; # unlinked stuff after title
                                   
                 my $stack = $mapIterator->getStack();                  my $stack = $mapIterator->getStack();
Line 437  sub handler { Line 435  sub handler {
                     'symb='.&Apache::lonnet::escape($curRes->symb()).                      'symb='.&Apache::lonnet::escape($curRes->symb()).
                     '"';                      '"';
   
                 my $title = $curRes->title();                  my $title = $curRes->compTitle();
                 if (!$title) {  
                     $title = $curRes->src();  
                     $title = substr ($title, rindex($title, "/") + 1);  
                 }  
                 my $partLabel = "";                  my $partLabel = "";
                 my $newBranchText = "";                  my $newBranchText = "";
   
Line 449  sub handler { Line 443  sub handler {
                 if ($isNewBranch) {                  if ($isNewBranch) {
                     $newBranchText = "<img src=\"/adm/lonIcons/branch.gif\" border=\"0\">";                      $newBranchText = "<img src=\"/adm/lonIcons/branch.gif\" border=\"0\">";
                     $isNewBranch = 0;                      $isNewBranch = 0;
                     $deltalevel = 1; # reserves space for the branch icon  
                 }                  }
   
                 # links to open and close the folders                  # links to open and close the folders
Line 527  sub handler { Line 520  sub handler {
                 }                  }
   
                 # print indentation                  # print indentation
                 for (my $i = 0; $i < $indentLevel - $deltalevel + $deltadepth; $i++) {                  for (my $i = 0; $i < $indentLevel - $deltalevel + $inlinedelta; $i++) {
                     $r->print($indentString);                      $r->print($indentString);
                 }                  }
   
Line 558  sub handler { Line 551  sub handler {
                                               '<font size="-1">Host down</font>'));                                                '<font size="-1">Host down</font>'));
                     }                      }
   
                   $r->print("</td>\n");
   
                 # SECOND COL: Is there text, feedback, errors??                  # SECOND COL: Is there text, feedback, errors??
                 my $discussionHTML = ""; my $feedbackHTML = "";                  my $discussionHTML = ""; my $feedbackHTML = "";
   
Line 616  sub handler { Line 611  sub handler {
                 }                  }
   
                 $r->print("&nbsp;</td></tr>\n");                  $r->print("&nbsp;</td></tr>\n");
   
                   if (!($counter % 20)) { $r->rflush(); }
                   if ($counter == 2) { $r->rflush(); }
             }              }
         }          }
         $curRes = $mapIterator->next();          $curRes = $mapIterator->next();
Line 1289  sub min { Line 1287  sub min {
     if ($a < $b) { return $a; } else { return $b; }      if ($a < $b) { return $a; } else { return $b; }
 }  }
   
   # In the CVS repository, documentation of this algorithm is included 
   # in /doc/lonnavdocs, as a PDF and .tex source. Markers like **1**
   # will reference the same location in the text as the part of the
   # algorithm is running through.
   
 sub new {  sub new {
     # magic invocation to create a class instance      # magic invocation to create a class instance
     my $proto = shift;      my $proto = shift;
Line 1331  sub new { Line 1334  sub new {
   
     my $maxDepth = 0; # tracks max depth      my $maxDepth = 0; # tracks max depth
   
       # **1**
   
     foreach my $pass (@iterations) {      foreach my $pass (@iterations) {
         my $direction = $pass->[0];          my $direction = $pass->[0];
         my $valName = $pass->[1];          my $valName = $pass->[1];
Line 1355  sub new { Line 1360  sub new {
                 my $resultingVal = $curRes->{DATA}->{$valName};                  my $resultingVal = $curRes->{DATA}->{$valName};
                 my $nextResources = $curRes->$nextResourceMethod();                  my $nextResources = $curRes->$nextResourceMethod();
                 my $resourceCount = scalar(@{$nextResources});                  my $resourceCount = scalar(@{$nextResources});
               
                 if ($resourceCount == 1) {                  if ($resourceCount == 1) { # **3**
                     my $current = $nextResources->[0]->{DATA}->{$valName} || 999999999;                      my $current = $nextResources->[0]->{DATA}->{$valName} || 999999999;
                     $nextResources->[0]->{DATA}->{$valName} = min($resultingVal, $current);                      $nextResources->[0]->{DATA}->{$valName} = min($resultingVal, $current);
                 }                  }
                                   
                 if ($resourceCount > 1) {                  if ($resourceCount > 1) { # **4**
                     foreach my $res (@{$nextResources}) {                      foreach my $res (@{$nextResources}) {
                         my $current = $res->{DATA}->{$valName} || 999999999;                          my $current = $res->{DATA}->{$valName} || 999999999;
                         $res->{DATA}->{$valName} = min($current, $resultingVal + 1);                          $res->{DATA}->{$valName} = min($current, $resultingVal + 1);
Line 1369  sub new { Line 1374  sub new {
                 }                  }
             }              }
                           
             # Assign the final val              # Assign the final val (**2**)
             if (ref($curRes) && $direction == BACKWARD()) {              if (ref($curRes) && $direction == BACKWARD()) {
                 my $finalDepth = min($curRes->{DATA}->{TOP_DOWN_VAL},                  my $finalDepth = min($curRes->{DATA}->{TOP_DOWN_VAL},
                                      $curRes->{DATA}->{BOT_UP_VAL});                                       $curRes->{DATA}->{BOT_UP_VAL});
Line 1391  sub new { Line 1396  sub new {
         push @{$self->{STACK}}, [];          push @{$self->{STACK}}, [];
     }      }
   
     # Prime the recursion w/ the first resource      # Prime the recursion w/ the first resource **5**
     push @{$self->{STACK}->[0]}, $self->{FIRST_RESOURCE};      push @{$self->{STACK}->[0]}, $self->{FIRST_RESOURCE};
     $self->{ALREADY_SEEN}->{$self->{FIRST_RESOURCE}->{ID}} = 1;      $self->{ALREADY_SEEN}->{$self->{FIRST_RESOURCE}->{ID}} = 1;
   
Line 1440  sub next { Line 1445  sub next {
     my $newDepth;      my $newDepth;
     my $here;      my $here;
     while ( $i >= 0 && !$found ) {      while ( $i >= 0 && !$found ) {
         if ( scalar(@{$self->{STACK}->[$i]}) > 0 ) {          if ( scalar(@{$self->{STACK}->[$i]}) > 0 ) { # **6**
             $here = $self->{HERE} = shift @{$self->{STACK}->[$i]};              $here = pop @{$self->{STACK}->[$i]}; # **7**
             $found = 1;              $found = 1;
             $newDepth = $i;              $newDepth = $i;
         }          }
Line 1459  sub next { Line 1464  sub next {
         }          }
     }      }
   
       # If this is not a resource, it must be an END_BRANCH marker we want
       # to return directly.
       if (!ref($here)) { # **8**
           if ($here == END_BRANCH()) { # paranoia, in case of later extension
               $self->{CURRENT_DEPTH}--;
               return $here;
           }
       }
   
       # Otherwise, it is a resource and it's safe to store in $self->{HERE}
       $self->{HERE} = $here;
   
     # Get to the right level      # Get to the right level
     if ( $self->{CURRENT_DEPTH} > $newDepth ) {      if ( $self->{CURRENT_DEPTH} > $newDepth ) {
         push @{$self->{STACK}->[$newDepth]}, $here;          push @{$self->{STACK}->[$newDepth]}, $here;
Line 1478  sub next { Line 1495  sub next {
     # So we need to look at all the resources we can get to from here,      # So we need to look at all the resources we can get to from here,
     # categorize them if we haven't seen them, remember if we have a new      # categorize them if we haven't seen them, remember if we have a new
     my $nextUnfiltered = $here->getNext();      my $nextUnfiltered = $here->getNext();
       my $maxDepthAdded = -1;
       
     for (@$nextUnfiltered) {      for (@$nextUnfiltered) {
         if (!defined($self->{ALREADY_SEEN}->{$_->{ID}})) {          if (!defined($self->{ALREADY_SEEN}->{$_->{ID}})) {
             push @{$self->{STACK}->[$_->{DATA}->{DISPLAY_DEPTH}]}, $_;              my $depth = $_->{DATA}->{DISPLAY_DEPTH};
               push @{$self->{STACK}->[$depth]}, $_;
             $self->{ALREADY_SEEN}->{$_->{ID}} = 1;              $self->{ALREADY_SEEN}->{$_->{ID}} = 1;
               if ($maxDepthAdded < $depth) { $maxDepthAdded = $depth; }
         }          }
     }      }
       
       # Is this the end of a branch? If so, all of the resources examined above
       # led to lower levels then the one we are currently at, so we push a END_BRANCH
       # marker onto the stack so we don't forget.
       # Example: For the usual A(BC)(DE)F case, when the iterator goes down the
       # BC branch and gets to C, it will see F as the only next resource, but it's
       # one level lower. Thus, this is the end of the branch, since there are no
       # more resources added to this level or above.
       # We don't do this if the examined resource is the finish resource,
       # because the condition given above is true, but the "END_MAP" will
       # take care of things and we should already be at depth 0.
       my $isEndOfBranch = $maxDepthAdded < $self->{CURRENT_DEPTH};
       if ($isEndOfBranch && $here != $self->{FINISH_RESOURCE}) { # **9**
           push @{$self->{STACK}->[$self->{CURRENT_DEPTH}]}, END_BRANCH();
       }
   
     # That ends the main iterator logic. Now, do we want to recurse      # That ends the main iterator logic. Now, do we want to recurse
     # down this map (if this resource is a map)?      # down this map (if this resource is a map)?
     if ($self->{HERE}->is_map() &&      if ($self->{HERE}->is_map() &&
Line 1757  These are methods that help you retrieve Line 1792  These are methods that help you retrieve
   
 =over 4  =over 4
   
   =item * B<compTitle>: Returns a "composite title", that is equal to $res->title() if the resource has a title, and is otherwise the last part of the URL (e.g., "problem.problem").
   
 =item * B<ext>: Returns true if the resource is external.  =item * B<ext>: Returns true if the resource is external.
   
 =item * B<goesto>: Returns the "goesto" value from the compiled nav map. (It is likely you want to use B<getNext> instead.)  =item * B<goesto>: Returns the "goesto" value from the compiled nav map. (It is likely you want to use B<getNext> instead.)
Line 1806  sub symb { Line 1843  sub symb {
 }  }
 sub title { my $self=shift; return $self->navHash("title_", 1); }  sub title { my $self=shift; return $self->navHash("title_", 1); }
 sub to { my $self=shift; return $self->navHash("to_", 1); }  sub to { my $self=shift; return $self->navHash("to_", 1); }
   sub compTitle {
       my $self = shift;
       my $title = $self->title();
       if (!$title) {
           $title = $self->src();
           $title = substr($title, rindex($title, '/') + 1);
       }
       return $title;
   }
 =pod  =pod
   
 B<Predicate Testing the Resource>  B<Predicate Testing the Resource>
Line 1966  sub answerdate { Line 2011  sub answerdate {
     }      }
     return $self->parmval("answerdate", $part);      return $self->parmval("answerdate", $part);
 }  }
   sub awarded { my $self = shift; return $self->queryRestoreHash('awarded', shift); }
 sub duedate {  sub duedate {
     (my $self, my $part) = @_;      (my $self, my $part) = @_;
     return $self->parmval("duedate", $part);      return $self->parmval("duedate", $part);
Line 1990  sub tol { Line 2036  sub tol {
     (my $self, my $part) = @_;      (my $self, my $part) = @_;
     return $self->parmval("tol", $part);      return $self->parmval("tol", $part);
 }  }
 sub tries {  sub tries { 
     my $self = shift;      my $self = shift; 
     my $part = shift;      my $tries = $self->queryRestoreHash('tries', shift);
     $part = '0' if (!defined($part));      if (!defined($tries)) { return '0';}
   
     # Make sure return hash is loaded, should error check  
     $self->getReturnHash();  
       
     my $tries = $self->{RETURN_HASH}->{'resource.'.$part.'.tries'};  
     if (!defined($tries)) {return '0';}  
     return $tries;      return $tries;
 }  }
 sub type {  sub type {
     (my $self, my $part) = @_;      (my $self, my $part) = @_;
     return $self->parmval("type", $part);      return $self->parmval("type", $part);
 }  }
 sub weight {  sub weight { 
     (my $self, my $part) = @_;      my $self = shift; my $part = shift;
     return $self->parmval("weight", $part);      return $self->parmval("weight", $part);
 }  }
   
Line 2269  sub ATTEMPTED             { return 16; } Line 2309  sub ATTEMPTED             { return 16; }
   
 sub getCompletionStatus {  sub getCompletionStatus {
     my $self = shift;      my $self = shift;
     my $part = shift;  
     $part = "0" if (!defined($part));  
     return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE});      return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE});
   
     # Make sure return hash exists      my $status = $self->queryRestoreHash('solved', shift);
     $self->getReturnHash();  
       
     my $status = $self->{RETURN_HASH}->{'resource.'.$part.'.solved'};  
   
     # Left as seperate if statements in case we ever do more with this      # Left as seperate if statements in case we ever do more with this
     if ($status eq 'correct_by_student') {return $self->CORRECT;}      if ($status eq 'correct_by_student') {return $self->CORRECT;}
Line 2288  sub getCompletionStatus { Line 2323  sub getCompletionStatus {
     return $self->NOT_ATTEMPTED;      return $self->NOT_ATTEMPTED;
 }  }
   
   sub queryRestoreHash {
       my $self = shift;
       my $hashentry = shift;
       my $part = shift;
       $part = "0" if (!defined($part));
       return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE});
   
       $self->getReturnHash();
   
       return $self->{RETURN_HASH}->{'resource.'.$part.'.'.$hashentry};
   }
   
 =pod  =pod
   
 B<Composite Status>  B<Composite Status>
Line 2413  sub getNext { Line 2460  sub getNext {
   
         # Don't remember it if the student doesn't have browse priviledges          # Don't remember it if the student doesn't have browse priviledges
         # future note: this may properly belong in the client of the resource          # future note: this may properly belong in the client of the resource
         my $browsePriv = &Apache::lonnet::allowed('bre', $self->src);          my $browsePriv = $self->{BROWSE_PRIV};
         if ($browsePriv ne '2' && $browsePriv ne 'F') {          if (!defined($browsePriv)) {
               $browsePriv = &Apache::lonnet::allowed('bre', $self->src);
               $self->{BROWSE_PRIV} = $browsePriv;
           }
           if (!($browsePriv ne '2' && $browsePriv ne 'F')) {
             push @branches, $next;              push @branches, $next;
         }          }
     }      }
Line 2432  sub getPrevious { Line 2483  sub getPrevious {
   
         # Don't remember it if the student doesn't have browse priviledges          # Don't remember it if the student doesn't have browse priviledges
         # future note: this may properly belong in the client of the resource          # future note: this may properly belong in the client of the resource
         my $browsePriv = &Apache::lonnet::allowed('bre', $self->src);          my $browsePriv = $self->{BROWSE_PRIV};
         if ($browsePriv ne '2' && $browsePriv ne 'F') {          if (!defined($browsePriv)) {
               $browsePriv = &Apache::lonnet::allowed('bre', $self->src);
               $self->{BROWSE_PRIV} = $browsePriv;
           }
           if (!($browsePriv ne '2' && $browsePriv ne 'F')) {
             push @branches, $prev;              push @branches, $prev;
         }          }
     }      }

Removed from v.1.101  
changed lines
  Added in v.1.114


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>