Diff for /loncom/interface/lonnavmaps.pm between versions 1.84 and 1.95

version 1.84, 2002/10/17 19:25:27 version 1.95, 2002/11/01 18:47:26
Line 820  sub new_handle { Line 820  sub new_handle {
     &Apache::loncommon::no_cache($r);      &Apache::loncommon::no_cache($r);
     $r->send_http_header;      $r->send_http_header;
   
     # Initialize the nav map      # Create the nav map 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 837  sub new_handle { Line 837  sub new_handle {
                                           ''));                                            ''));
     $r->print('<script>window.focus();</script>');      $r->print('<script>window.focus();</script>');
     my $desc=$ENV{'course.'.$ENV{'request.course.id'}.'.description'};      my $desc=$ENV{'course.'.$ENV{'request.course.id'}.'.description'};
     if (defined($desc)) { $r->print("<h2>$desc</h2>\n"); }      $r->print('<table border="0" cellpadding="2" cellspacing="0">');
     my $date=localtime;      my $date=localtime;
     $r->print("<h3>$date</h3>\n");      $r->print('<tr><td align="right" valign="bottom">Key:&nbsp;&nbsp;</td>');
     $r->rflush();  
     if ($navmap->{LAST_CHECK}) {      if ($navmap->{LAST_CHECK}) {
         $r->print('<img src="/adm/lonMisc/chat.gif"> New discussion since '.          $r->print('<td align="center" valign="bottom">&nbsp;&nbsp;'.
                     '<img src="/adm/lonMisc/chat.gif"> New discussion since '.
                   strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})).                    strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})).
                   '<br><img src="/adm/lonMisc/feedback.gif"> New message (click to open)<p>');                     '</td><td align="center" valign="bottom">&nbsp;&nbsp;'.
                     '<img src="/adm/lonMisc/feedback.gif"> New message (click to open)<p>'.
                     '</td>'); 
       } else {
           $r->print('<td align="center" valign="bottom">&nbsp;&nbsp;'.
                     '<img src="/adm/lonMisc/chat.gif"> Discussions</td><td align="center" valign="bottom">'.
                     '&nbsp;&nbsp;<img src="/adm/lonMisc/feedback.gif"> New message (click to open)'.
                     '</td>'); 
       }
       $r->print('</tr></table>');
       my $condition = 0;
       if ($ENV{'form.condition'}) {
           $condition = 1;
       }
   
       if ($condition) {
           $r->print('<a href="navmaps?condition=0&filter=">Close All Folders</a>');
     } else {      } else {
         $r->print('<img src="/adm/lonMisc/chat.gif"> Discussions'.          $r->print('<a href="navmaps?condition=1&filter=">Open All Folders</a>');
                   '<br><img src="/adm/lonMisc/feedback.gif"> New message (click to open)<p>');   
     }      }
   
       $r->print('<br>&nbsp;');
       $r->rflush();
   
       # Now that we've displayed some stuff to the user, init the navmap
       $navmap->init();
   
     # Check that it's defined      # Check that it's defined
     if (!($navmap->courseMapDefined())) {      if (!($navmap->courseMapDefined())) {
         $r->print('<font size="+2" color="red">Coursemap undefined.</font>' .          $r->print('<font size="+2" color="red">Coursemap undefined.</font>' .
Line 876  sub new_handle { Line 897  sub new_handle {
       $res->NOTHING_SET            => ''        );        $res->NOTHING_SET            => ''        );
     # And a special case in the nav map; what to do when the assignment      # 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      # is not yet done and due in less then 24 hours
     my $hurryUpColor = "#FFCCCC";      my $hurryUpColor = "#FF0000";
   
     my %statusIconMap =       my %statusIconMap = 
         ( $res->NETWORK_FAILURE    => '',          ( $res->NETWORK_FAILURE    => '',
Line 890  sub new_handle { Line 911  sub new_handle {
           $res->TRIES_LEFT         => 'navmap.open.gif',            $res->TRIES_LEFT         => 'navmap.open.gif',
           $res->INCORRECT          => 'navmap.wrong.gif',            $res->INCORRECT          => 'navmap.wrong.gif',
           $res->OPEN               => 'navmap.open.gif',            $res->OPEN               => 'navmap.open.gif',
           $res->ATTEMPTED          => '' );            $res->ATTEMPTED          => 'navmap.open.gif' );
   
     my %iconAltTags =       my %iconAltTags = 
         ( 'navmap.correct.gif' => 'Correct',          ( 'navmap.correct.gif' => 'Correct',
Line 910  sub new_handle { Line 931  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'};      my $currenturl = $ENV{'form.postdata'};
     $currenturl=~s/^http\:\/\///;      $currenturl=~s/^http\:\/\///;
     $currenturl=~s/^[^\/]+//;      $currenturl=~s/^[^\/]+//;
Line 921  sub new_handle { Line 947  sub new_handle {
     my $queryAdd = "postdata=" . &Apache::lonnet::escape($currenturl) .      my $queryAdd = "postdata=" . &Apache::lonnet::escape($currenturl) .
         "&alreadyHere=1";          "&alreadyHere=1";
   
     $r->print('<a href="navmaps?condition=1&filter=">Show All Resources</a><br /><br />');  
   
     # 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
     $r->print('<table cellspacing="0" cellpadding="3" width="100%" border="0" bgcolor="#FFFFFF">' ."\n");      $r->print('<table cellspacing="0" cellpadding="3" border="0" bgcolor="#FFFFFF">' ."\n");
   
     my $condition = 0;  
     if ($ENV{'form.condition'}) {  
         $condition = 1;  
     }  
   
     # This needs to be updated to use symbs from the remote,       # This needs to be updated to use symbs from the remote, 
     # instead of uris. The changes to this and the main rendering      # instead of uris. The changes to this and the main rendering
     # loop should be obvious.      # loop should be obvious.
     # Here's a simple example of the iterator.      # Here's a simple example of the iterator.
     # If there is a current resource      # Preprocess the map: Look for current URL, force inlined maps to display
     if ($currenturl && !$ENV{'form.alreadyHere'}) {  
         # Give me every resource...      my $mapIterator = $navmap->getIterator(undef, undef, {}, 1);
         my $mapIterator = $navmap->getIterator(undef, undef, {}, 1);      my $found = 0;
         my $found = 0;      my $depth = 1;
         my $depth = 1;      my $currentUrlIndex = 0; # keeps track of when the current resource is found,
         $mapIterator->next(); # discard the first BEGIN_MAP                               # so we can back up a few and put the anchor above the
         my $curRes = $mapIterator->next();                               # current resource
               $mapIterator->next(); # discard the first BEGIN_MAP
         while ($depth > 0 && !$found) {      my $curRes = $mapIterator->next();
             if (ref($curRes) && $curRes->src() eq $currenturl) {      my $counter = 0;
                 # If this is the correct resource, be sure to       
                 # show it by making sure the containing maps      while ($depth > 0) {
                 # are open.          if ($curRes == $mapIterator->BEGIN_MAP()) {
               $depth++;
                 my $mapStack = $mapIterator->getStack();          }
                 for my $map (@{$mapStack}) {          if ($curRes == $mapIterator->END_MAP()) {
                     if ($condition) {              $depth--;
                         undef $filterHash{$map->map_pc()};          }
                     } else {  
                         $filterHash{$map->map_pc()} = 1;          if (ref($curRes)) { $counter++; }
                     }  
           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.
   
               $currentUrlIndex = $counter;
               
               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      undef $res; # so we don't accidentally use it later
     my $indentLevel = 0;      my $indentLevel = 0;
     my $indentString = "<img src=\"/adm/lonIcons/whitespace1.gif\" width=\"25\" height=\"1\" alt=\"\" border=\"0\" />";      my $indentString = "<img src=\"/adm/lonIcons/whitespace1.gif\" width=\"25\" height=\"1\" alt=\"\" border=\"0\" />";
Line 972  sub new_handle { Line 1017  sub new_handle {
     my $isNewBranch = 0;      my $isNewBranch = 0;
     my $now = time();      my $now = time();
     my $in24Hours = $now + 24 * 60 * 60;      my $in24Hours = $now + 24 * 60 * 60;
     my $depth = 1;  
     my $displayedHereMarker = 0;      my $displayedHereMarker = 0;
       
     # We know the first thing is a BEGIN_MAP (see "$self->{STARTED}"      # We know the first thing is a BEGIN_MAP (see "$self->{STARTED}"
     # code in iterator->next), so ignore the first one      # code in iterator->next), so ignore the first one
     my $mapIterator = $navmap->getIterator(undef, undef, \%filterHash,      $mapIterator = $navmap->getIterator(undef, undef, \%filterHash,
                                            $condition);                                             $condition);
     $mapIterator->next();      $mapIterator->next();
     my $curRes = $mapIterator->next();      $curRes = $mapIterator->next();
       my $deltadepth = 0;
       $depth = 1;
   
       my @backgroundColors = ("#FFFFFF", "#F6F6F6");
       my $rowNum = 0;
   
       $counter = 0;
   
     while ($depth > 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() ||          if ($curRes == $mapIterator->BEGIN_MAP() ||
             $curRes == $mapIterator->BEGIN_BRANCH()) {              $curRes == $mapIterator->BEGIN_BRANCH()) {
             $indentLevel++;              $indentLevel++;
Line 1001  sub new_handle { Line 1065  sub new_handle {
             $depth--;              $depth--;
         }          }
   
           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 blotted out?          # Is this resource being blotted out?
         if (ref($curRes) && !advancedUser() && $curRes->randomout()) {          if (ref($curRes) && !advancedUser() && $curRes->randomout()) {
             $curRes = $mapIterator->next();              $curRes = $mapIterator->next();
Line 1153  sub new_handle { Line 1222  sub new_handle {
                 }                  }
                                   
                 my $colorizer = "";                  my $colorizer = "";
                   my $color;
                 if ($curRes->is_problem()) {                  if ($curRes->is_problem()) {
                     my $status = $curRes->status($part);                      my $status = $curRes->status($part);
                     my $color = $colormap{$status};                      $color = $colormap{$status};
   
                     # Special case in the navmaps: If in less then                      # Special case in the navmaps: If in less then
                     # 24 hours, give it a bit of urgency                      # 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()+(24*60*60) && 
                         $curRes->duedate() > time()) {                          $curRes->duedate() > time()) {
                         $color = $hurryUpColor;                          $color = $hurryUpColor;
                     }                      }
                     # Special case: If this is the last try, and there is                      # 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 $tries = $curRes->tries($part);
                     my $maxtries = $curRes->maxtries($part);                      my $maxtries = $curRes->maxtries($part);
                     if ($tries && $maxtries && $maxtries > 1 &&                      if ($tries && $maxtries && $maxtries > 1 &&
                         $maxtries - $tries == 1) {                          $maxtries - $tries == 1 && $curRes->duedate() &&
                           $curRes->duedate() > time()) {
                         $color = $hurryUpColor;                          $color = $hurryUpColor;
                     }                      }
                     if ($color ne "") {                      if ($color ne "") {
Line 1181  sub new_handle { Line 1254  sub new_handle {
                     $nonLinkedText .= ' <i>(hidden)</i> ';                      $nonLinkedText .= ' <i>(hidden)</i> ';
                 }                  }
   
                 # FIRST COL: The resource indentation, branch icon, and name                  $rowNum++;
                 $r->print("  <tr><td align=\"left\" valign=\"center\" width=\"60%\">\n");                  my $backgroundColor = $backgroundColors[$rowNum % scalar(@backgroundColors)];
   
                   # FIRST COL: The resource indentation, branch icon, name, and anchor
                   $r->print("  <tr bgcolor=\"$backgroundColor\"><td align=\"left\" valign=\"center\" width=\"60%\">\n");
   
                   # anchor for current resource... - 5 is deliberate: If it's that
                   # high on the screen, don't bother focusing on it. Also this will
                   # print multiple anchors if this is an expanded multi-part problem...
                   # who cares?
                   if ($counter == $currentUrlIndex - 5) {
                       $r->print('<a name="current" />');
                   }
   
                 # print indentation                  # print indentation
                 for (my $i = 0; $i < $indentLevel - $deltalevel; $i++) {                  for (my $i = 0; $i < $indentLevel - $deltalevel + $deltadepth; $i++) {
                     $r->print($indentString);                      $r->print($indentString);
                 }                  }
   
Line 1218  sub new_handle { Line 1302  sub new_handle {
   
                 my $discussionHTML = ""; my $feedbackHTML = "";                  my $discussionHTML = ""; my $feedbackHTML = "";
   
                 # SECOND COL: Is there text or feedback?                  # SECOND COL: Is there text, feedback, errors??
                 if ($curRes->hasDiscussion()) {                  if ($curRes->hasDiscussion()) {
                     $discussionHTML = $linkopen .                      $discussionHTML = $linkopen .
                         '<img border="0" src="/adm/lonMisc/chat.gif" />' .                          '<img border="0" src="/adm/lonMisc/chat.gif" />' .
Line 1259  sub new_handle { Line 1343  sub new_handle {
                 }                  }
   
                 # FOURTH COL: Text description                  # FOURTH COL: Text description
                 $r->print("<td $colorizer align=\"right\" valign=\"center\">\n");                  #$r->print("<td $colorizer align=\"right\" valign=\"center\">\n");
                   $r->print("<td align=\"right\" valign=\"center\">\n");
                                   
                 if ($curRes->kind() eq "res" &&                  if ($curRes->kind() eq "res" &&
                     $curRes->is_problem() &&                      $curRes->is_problem() &&
                     !$firstDisplayed) {                      !$firstDisplayed) {
                       $r->print ("<font color=\"$color\"><b>") if ($color);
                     $r->print (getDescription($curRes, $part));                      $r->print (getDescription($curRes, $part));
                       $r->print ("</b></font>") if ($color);
                 }                  }
                 if ($curRes->is_map() && advancedUser() && $curRes->randompick()) {                  if ($curRes->is_map() && advancedUser() && $curRes->randompick()) {
                     $r->print('(randomly select ' . $curRes->randompick() .')');                      $r->print('(randomly select ' . $curRes->randompick() .')');
Line 1473  sub timeToHumanString { Line 1560  sub timeToHumanString {
         # Less then 5 days away, display day of the week and          # Less then 5 days away, display day of the week and
         # HH:MM          # HH:MM
         if ( $delta < $day * 5 ) {          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 am/midnight/;
             $timeStr =~ s/12:00 pm/noon/;              $timeStr =~ s/12:00 pm/noon/;
             return ($inPast ? "last " : "next ") .              return ($inPast ? "last " : "next ") .
Line 1563  sub new { Line 1650  sub new {
     $self->{PARM_HASH} = \%parmhash;      $self->{PARM_HASH} = \%parmhash;
     $self->{HASH_TIED} = 1;      $self->{HASH_TIED} = 1;
   
       bless($self);
           
       return $self;
   }
   
   sub init {
       my $self = shift;
   
     # If the course opt hash and the user opt hash should be generated,      # If the course opt hash and the user opt hash should be generated,
     # generate them      # generate them
     if ($self->{GENERATE_COURSE_USER_OPT}) {      if ($self->{GENERATE_COURSE_USER_OPT}) {
Line 1667  sub new { Line 1762  sub new {
     }          }    
   
     $self->{PARM_CACHE} = {};      $self->{PARM_CACHE} = {};
       
     bless($self);  
           
     return $self;  
 }  }
   
 # Checks to see if coursemap is defined, matching test in old lonnavmaps  # Checks to see if coursemap is defined, matching test in old lonnavmaps
Line 1685  sub courseMapDefined { Line 1776  sub courseMapDefined {
   
 sub getIterator {  sub getIterator {
     my $self = shift;      my $self = shift;
     my $iterator = Apache::lonnavmaps::iterator->new($self, shift, shift,      my $iterator = Apache::lonnavmaps::DFSiterator->new($self, shift, shift,
                                                      shift, undef, shift);                                                       shift, undef, shift, 
                                                        $ENV{'form.direction'});
     return $iterator;      return $iterator;
 }  }
   
Line 1910  getIterator behaves as follows: Line 2002  getIterator behaves as follows:
   
 =over 4  =over 4
   
 =item B<getIterator>(firstResource, finishResource, filterHash, condition): All parameters are optional. firstResource is a resource reference corresponding to where the iterator should start. It defaults to navmap->firstResource() for the corresponding nav map. finishResource corresponds to where you want the iterator to end, defaulting to navmap->finishResource(). filterHash is a hash used as a set containing strings representing the resource IDs, defaulting to empty. Condition is a 1 or 0 that sets what to do with the filter hash: If a 0, then only resource that exist IN the filterHash will be recursed on. If it is a 1, only resources NOT in the filterHash will be recursed on. Defaults to 0, which is to say, do not recurse unless explicitly asked to.  =item B<getIterator>(firstResource, finishResource, filterHash, condition, direction): All parameters are optional. firstResource is a resource reference corresponding to where the iterator should start. It defaults to navmap->firstResource() for the corresponding nav map. finishResource corresponds to where you want the iterator to end, defaulting to navmap->finishResource(). filterHash is a hash used as a set containing strings representing the resource IDs, defaulting to empty. Condition is a 1 or 0 that sets what to do with the filter hash: If a 0, then only resource that exist IN the filterHash will be recursed on. If it is a 1, only resources NOT in the filterHash will be recursed on. Defaults to 0, which is to say, do not recurse unless explicitly asked to. Direction specifies which direction to recurse, either FORWARD or BACKWARD, with FORWARD being default.
   
 Thus, by default, all resources will be shown. Change the condition to a 1 without changing the hash, and only the top level of the map will be shown. Changing the condition to 1 and including some values in the hash will allow you to selectively examine parts of the navmap, while leaving it on 0 and adding things to the hash will allow you to selectively ignore parts of the nav map. See the handler code for examples: By default, the condition is 0 and all folders are closed unless explicitly opened. Clicking "Show All Resources" will use a condition of 1 and an empty filterHash, resulting in all resources being shown.  Thus, by default, all resources will be shown. Change the condition to a 1 without changing the hash, and only the top level of the map will be shown. Changing the condition to 1 and including some values in the hash will allow you to selectively examine parts of the navmap, while leaving it on 0 and adding things to the hash will allow you to selectively ignore parts of the nav map. See the handler code for examples: By default, the condition is 0 and all folders are closed unless explicitly opened. Clicking "Show All Resources" will use a condition of 1 and an empty filterHash, resulting in all resources being shown.
   
Line 1940  sub BEGIN_MAP { return 1; }    # beginin Line 2032  sub BEGIN_MAP { return 1; }    # beginin
 sub END_MAP { return 2; }      # end of the map  sub END_MAP { return 2; }      # end of the map
 sub BEGIN_BRANCH { return 3; } # beginning of a branch  sub BEGIN_BRANCH { return 3; } # beginning of a branch
 sub END_BRANCH { return 4; }   # end of a branch  sub END_BRANCH { return 4; }   # end of a branch
   sub FORWARD { return 1; }      # go forward
   sub BACKWARD { return 2; }
   
   sub new {
       # magic invocation to create a class instance
       my $proto = shift;
       my $class = ref($proto) || $proto;
       my $self = {};
   
       $self->{NAV_MAP} = shift;
       return undef unless ($self->{NAV_MAP});
   
       # Handle the parameters
       $self->{FIRST_RESOURCE} = shift || $self->{NAV_MAP}->firstResource();
       $self->{FINISH_RESOURCE} = shift || $self->{NAV_MAP}->finishResource();
   
       # If the given resources are just the ID of the resource, get the
       # objects
       if (!ref($self->{FIRST_RESOURCE})) { $self->{FIRST_RESOURCE} = 
                $self->{NAV_MAP}->getById($self->{FIRST_RESOURCE}); }
       if (!ref($self->{FINISH_RESOURCE})) { $self->{FINISH_RESOURCE} = 
                $self->{NAV_MAP}->getById($self->{FINISH_RESOURCE}); }
   
       $self->{FILTER} = shift;
   
       # A hash, used as a set, of resource already seen
       $self->{ALREADY_SEEN} = shift;
       if (!defined($self->{ALREADY_SEEN})) { $self->{ALREADY_SEEN} = {} };
       $self->{CONDITION} = shift;
   
       # Now, we need to pre-process the map, by walking forward and backward
       # over the parts of the map we're going to look at.
   
       # Now we're ready to start iterating.
   }
   
   1;
   
   package Apache::lonnavmaps::DFSiterator;
   
   # UNDOCUMENTED: This is a private library, it should not generally be used
   # by the outside world. What it does is walk through the nav map in a 
   # depth-first fashion. This is not appropriate for most uses, but it is
   # used by the main iterator for pre-processing. It also is able to isolate
   # much of the complexity of the main iterator, so the main iterator is much
   # simpler.
   
   # Here are the tokens for the iterator, replicated from iterator for convenience:
   
   sub BEGIN_MAP { return 1; }    # begining of a new map
   sub END_MAP { return 2; }      # end of the map
   sub BEGIN_BRANCH { return 3; } # beginning of a branch
   sub END_BRANCH { return 4; }   # end of a branch
   sub FORWARD { return 1; }      # go forward
   sub BACKWARD { return 2; }
   
 # Params: nav map, start resource, end resource, filter, condition,   # Params: nav map, start resource, end resource, filter, condition, 
 # already seen hash ref  # already seen hash ref
Line 1970  sub new { Line 2117  sub new {
     $self->{ALREADY_SEEN} = shift;      $self->{ALREADY_SEEN} = shift;
     if (!defined($self->{ALREADY_SEEN})) { $self->{ALREADY_SEEN} = {} };      if (!defined($self->{ALREADY_SEEN})) { $self->{ALREADY_SEEN} = {} };
     $self->{CONDITION} = shift;      $self->{CONDITION} = shift;
       $self->{DIRECTION} = shift || FORWARD();
   
     # Flag: Have we started yet? If not, the first action is to return BEGIN_MAP.      # Flag: Have we started yet? If not, the first action is to return BEGIN_MAP.
     $self->{STARTED} = 0;      $self->{STARTED} = 0;
Line 1994  sub new { Line 2142  sub new {
     $self->{FORCE_NEXT} = undef;      $self->{FORCE_NEXT} = undef;
   
     # Start with the first resource      # Start with the first resource
     push @{$self->{BRANCH_STACK}}, $self->{FIRST_RESOURCE};      if ($self->{DIRECTION} == FORWARD) {
           push @{$self->{BRANCH_STACK}}, $self->{FIRST_RESOURCE};
       } else {
           push @{$self->{BRANCH_STACK}}, $self->{FINISH_RESOURCE};
       }
     $self->{BRANCH_STACK_SIZE} = 1;      $self->{BRANCH_STACK_SIZE} = 1;
   
     bless($self);      bless($self);
Line 2091  sub next { Line 2243  sub next {
     $self->{ALREADY_SEEN}->{$self->{HERE}->{ID}} = 1;      $self->{ALREADY_SEEN}->{$self->{HERE}->{ID}} = 1;
           
     # Get the next possible resources      # Get the next possible resources
     my $nextUnfiltered = $self->{HERE}->getNext();      my $nextUnfiltered;
       if ($self->{DIRECTION} == FORWARD()) {
           $nextUnfiltered = $self->{HERE}->getNext();
       } else {
           $nextUnfiltered = $self->{HERE}->getPrevious();
       }
     my $next = [];      my $next = [];
   
     # filter the next possibilities to remove things we've       # filter the next possibilities to remove things we've 
Line 2139  sub next { Line 2296  sub next {
         my $finishResource = $self->{HERE}->map_finish();          my $finishResource = $self->{HERE}->map_finish();
   
         $self->{RECURSIVE_ITERATOR} =          $self->{RECURSIVE_ITERATOR} =
           Apache::lonnavmaps::iterator->new ($self->{NAV_MAP}, $firstResource,             Apache::lonnavmaps::DFSiterator->new ($self->{NAV_MAP}, $firstResource, 
                      $finishResource, $self->{FILTER}, $self->{ALREADY_SEEN},                       $finishResource, $self->{FILTER}, $self->{ALREADY_SEEN},
                                              $self->{CONDITION});                                               $self->{CONDITION}, $self->{DIRECTION});
     }      }
   
     return $self->{HERE};      return $self->{HERE};
Line 2168  sub populateStack { Line 2325  sub populateStack {
     my $self=shift;      my $self=shift;
     my $stack = shift;      my $stack = shift;
   
     push @$stack, $self->{HERE};      push @$stack, $self->{HERE} if ($self->{HERE});
   
     if ($self->{RECURSIVE_ITERATOR_FLAG}) {      if ($self->{RECURSIVE_ITERATOR_FLAG}) {
         $self->{RECURSIVE_ITERATOR}->populateStack($stack);          $self->{RECURSIVE_ITERATOR}->populateStack($stack);
Line 2270  These are methods that help you retrieve Line 2427  These are methods that help you retrieve
   
 # These info functions can be used directly, as they don't return  # These info functions can be used directly, as they don't return
 # resource information.  # 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 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 goesto { my $self=shift; return $self->navHash("goesto_", 1); }
 sub kind { my $self=shift; return $self->navHash("kind_", 1); }  sub kind { my $self=shift; return $self->navHash("kind_", 1); }
 sub randomout { my $self=shift; return $self->navHash("randomout_", 1); }  sub randomout { my $self=shift; return $self->navHash("randomout_", 1); }
Line 2554  sub hasDiscussion { Line 2713  sub hasDiscussion {
   
 sub getFeedback {  sub getFeedback {
     my $self = shift;      my $self = shift;
     return $self->{NAV_MAP}->getFeedback($self->symb());      return $self->{NAV_MAP}->getFeedback($self->src());
 }  }
   
 =pod  =pod
Line 2884  sub status { Line 3043  sub status {
   
 =over 4  =over 4
   
 =item * B<getNext>(): Gets the next resource in the navmap after this one.  =item * B<getNext>($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<getPrevious>($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  =cut
 # resource, you need to look up the "to_" link in the nav hash, then  
 # follow that with the "goesto_" link.  
   
 sub getNext {  sub getNext {
     my $self = shift;      my $self = shift;
     my $alreadySeenHash = shift;      my $alreadySeenHash = shift;
     my @branches;      my @branches;
     my $to = $self->to();      my $to = $self->to();
     foreach my $branch ( split(/\,/, $to) ) {      foreach my $branch ( split(/,/, $to) ) {
         my $choice = $self->{NAV_MAP}->getById($branch);          my $choice = $self->{NAV_MAP}->getById($branch);
         my $next = $choice->goesto();          my $next = $choice->goesto();
         $next = $self->{NAV_MAP}->getById($next);          $next = $self->{NAV_MAP}->getById($next);
Line 2913  sub getNext { Line 3070  sub getNext {
     }      }
     return \@branches;      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  =pod
   

Removed from v.1.84  
changed lines
  Added in v.1.95


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