Diff for /loncom/interface/lonnavmaps.pm between versions 1.141 and 1.158

version 1.141, 2003/02/07 22:16:53 version 1.158, 2003/03/13 19:57:10
Line 38 Line 38
 # YEAR=2002  # YEAR=2002
 # 1/1 Gerd Kortemeyer  # 1/1 Gerd Kortemeyer
 # Oct-Nov Jeremy Bowers  # Oct-Nov Jeremy Bowers
   # YEAR=2003
   # Jeremy Bowers ... lots of days
   
 package Apache::lonnavmaps;  package Apache::lonnavmaps;
   
 use strict;  use strict;
 use Apache::Constants qw(:common :http);  use Apache::Constants qw(:common :http);
 use Apache::loncommon();  use Apache::loncommon();
   use Apache::lonmenu();
 use POSIX qw (floor strftime);  use POSIX qw (floor strftime);
   
 my %navmaphash;  my %navmaphash;
Line 152  sub real_handler { Line 155  sub real_handler {
     }      }
   
     $r->print("<html><head>\n");      $r->print("<html><head>\n");
     $r->print("<title>Navigate Course Contents</title></head>");      $r->print("<title>Navigate Course Contents</title>");
   # ------------------------------------------------------------ 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      # Header
     $r->print(&Apache::loncommon::bodytag('Navigate Course Contents','',      $r->print('</head>'.
                                           ''));                &Apache::loncommon::bodytag('Navigate Course Contents','',
                                       $addentries,'','',$ENV{'form.register'}));
     $r->print('<script>window.focus();</script>');      $r->print('<script>window.focus();</script>');
   
     $r->rflush();      $r->rflush();
Line 164  sub real_handler { Line 178  sub real_handler {
     # Now that we've displayed some stuff to the user, init the navmap      # Now that we've displayed some stuff to the user, init the navmap
     $navmap->init();      $navmap->init();
   
   
     $r->print('<br>&nbsp;');      $r->print('<br>&nbsp;');
     $r->rflush();      $r->rflush();
   
Line 175  sub real_handler { Line 188  sub real_handler {
         return OK;          return OK;
     }      }
   
       # See if there's only one map in the top-level... if so,
       # automatically display it
       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.
           $ENV{'form.filter'} = "$sequenceId";
       }
   
     # renderer call      # renderer call
     my $render = render({ 'cols' => [0,1,2,3],      my $render = render({ 'cols' => [0,1,2,3],
                           'url' => '/adm/navmaps',                            'url' => '/adm/navmaps',
                           'printKey' => 1,                            'suppressNavmap' => 1,
                           'r' => $r});                            'r' => $r});
   
     $navmap->untieHashes();      $navmap->untieHashes();
Line 322  sub lastTry { Line 361  sub lastTry {
 }  }
   
 # This puts a human-readable name on the ENV variable.  # This puts a human-readable name on the ENV variable.
   # FIXME: This needs better logic: Who gets the advanced view of navmaps?
   #   As of 3-13-03, it's an open question. Guy doesn't want to check
   #   roles directly because it should be a check of capabilities for future
   #   role compatibity. There is no capability that matches this one for
   #   now, so this is done. (A hack for 1.0 might be to simply check roles
   #   anyhow.)
 sub advancedUser {  sub advancedUser {
     return $ENV{'user.adv'};      return $ENV{'user.adv'};
 }  }
Line 463  The pre-packaged column names are refere Line 508  The pre-packaged column names are refere
   
 =item * B<resource_part_count>: If true (default), the resource will show a part count if the full part list is not displayed. If false, the resource will never show a part count.  =item * B<resource_part_count>: If true (default), the resource will show a part count if the full part list is not displayed. If false, the resource will never show a part count.
   
   =item * B<resource_no_folder_link>: If true, the resource's folder will not be clickable to open or close it. Default is false.
   
 =back  =back
   
 =item B<communication_status>: Whether there is discussion on the resource, email for the user, or (lumped in here) perl errors in the execution of the problem. This is the second column in the main nav map.  =item B<communication_status>: Whether there is discussion on the resource, email for the user, or (lumped in here) perl errors in the execution of the problem. This is the second column in the main nav map.
Line 491  Most of these parameters are only useful Line 538  Most of these parameters are only useful
   
 =over 4  =over 4
   
 =item * B<iterator>: A reference to a fresh ::iterator to use from the navmaps. The rendering will reflect the options passed to the iterator, so you can use that to just render a certain part of the course, if you like. If one is not passed, the renderer will attempt to construct one from ENV{'form.filter'} and ENV{'form.condition'} information.  =item * B<iterator>: A reference to a fresh ::iterator to use from the navmaps. The rendering will reflect the options passed to the iterator, so you can use that to just render a certain part of the course, if you like. If one is not passed, the renderer will attempt to construct one from ENV{'form.filter'} and ENV{'form.condition'} information, plus the 'iterator_map' parameter if any.
   
   =item * B<iterator_map>: If you are letting the renderer do the iterator handling, you can instruct the renderer to render only a particular map by passing it the source of the map you want to process, like '/res/103/jerf/navmap.course.sequence'.
   
 =item * B<navmap>: A reference to a navmap, used only if an iterator is not passed in. If this is necessary to make an iterator but it is not passed in, a new one will be constructed based on ENV info. This is useful to do basic error checking before passing it off to render.  =item * B<navmap>: A reference to a navmap, used only if an iterator is not passed in. If this is necessary to make an iterator but it is not passed in, a new one will be constructed based on ENV info. This is useful to do basic error checking before passing it off to render.
   
Line 503  Most of these parameters are only useful Line 552  Most of these parameters are only useful
   
 =item * B<jumpCount>: A string identifying the URL to place the anchor 'curloc' at. Default to no anchor at all. It is the responsibility of the renderer user to ensure that the #curloc is in the URL. By default, determined through the use of the ENV{} 'jump' and 'jumpType' information.  =item * B<jumpCount>: A string identifying the URL to place the anchor 'curloc' at. Default to no anchor at all. It is the responsibility of the renderer user to ensure that the #curloc is in the URL. By default, determined through the use of the ENV{} 'jump' and 'jumpType' information.
   
 =item * B<hereURL>: A URL identifying where to place the 'here' marker. By default, will pull this from the ENV{'form.here*'} info.  =item * B<here>: A Symb identifying where to place the 'here' marker. Default empty, which means no marker.
   
 =item * B<hereSymb>: A Symb identifying where to place the 'here' marker. Default same as hereURL.  
   
 =item * B<indentString>: A string identifying the indentation string to use. By default, this is a 25 pixel whitespace image with no alt text.  =item * B<indentString>: A string identifying the indentation string to use. By default, this is a 25 pixel whitespace image with no alt text.
   
Line 521  Most of these parameters are only useful Line 568  Most of these parameters are only useful
   
 =item * B<printCloseAll>: If true, print the "Close all folders" or "open all folders" links. Default is true.  =item * B<printCloseAll>: If true, print the "Close all folders" or "open all folders" links. Default is true.
   
   =item * B<filterFunc>: A function that takes the resource object as its only parameter and returns a true or false value. If true, the resource is displayed. If false, it is simply skipped in the display. By default, all resources are shown.
   
   =item * B<suppressNavmaps>: If true, will not display Navigate Content resources. Default to false.
   
 =back  =back
   
 =head2 Additional Info  =head2 Additional Info
   
 In addition to the parameters you can pass to the renderer, which will be passed through unchange to the column renderers, the renderer will generate the following information which your renderer may find useful:  In addition to the parameters you can pass to the renderer, which will be passed through unchange to the column renderers, the renderer will generate the following information which your renderer may find useful:
   
   If you want to know how many rows were printed, the 'counter' element of the hash passed into the render function will contain the count. You may want to check whether any resources were printed at all.
   
 =over 4  =over 4
   
 =back  =back
Line 571  sub render_resource { Line 624  sub render_resource {
     my $icon = "<img src='/adm/lonIcons/html.gif' alt='' border='0' />";      my $icon = "<img src='/adm/lonIcons/html.gif' alt='' border='0' />";
           
     if ($resource->is_problem()) {      if ($resource->is_problem()) {
         if ($part eq "0" || $params->{'condensed'}) {          if ($part eq "" || $params->{'condensed'}) {
             $icon = '<img src="/adm/lonIcons/problem.gif" alt="" border="0" />';              $icon = '<img src="/adm/lonIcons/problem.gif" alt="" border="0" />';
         } else {          } else {
             $icon = $params->{'indentString'};              $icon = $params->{'indentString'};
Line 585  sub render_resource { Line 638  sub render_resource {
         if ($it->{CONDITION}) {          if ($it->{CONDITION}) {
             $nowOpen = !$nowOpen;              $nowOpen = !$nowOpen;
         }          }
         $icon = 'navmap.folder.' . ($nowOpen ? 'closed' : 'open') . '.gif';  
         $icon = "<img src='/adm/lonIcons/$icon' alt='' border='0' />";          if (!$params->{'resource_no_folder_link'}) {
                       $icon = 'navmap.folder.' . ($nowOpen ? 'closed' : 'open') . '.gif';
         $linkopen = "<a href='" . $params->{'url'} . '?' .               $icon = "<img src='/adm/lonIcons/$icon' alt='' border='0' />";
             $params->{'queryString'} . '&filter=';  
         $linkopen .= ($nowOpen xor $it->{CONDITION}) ?              $linkopen = "<a href='" . $params->{'url'} . '?' . 
             addToFilter($filter, $mapId) :                  $params->{'queryString'} . '&filter=';
             removeFromFilter($filter, $mapId);              $linkopen .= ($nowOpen xor $it->{CONDITION}) ?
         $linkopen .= "&condition=" . $it->{CONDITION} . '&hereType='                  addToFilter($filter, $mapId) :
             . $params->{'hereType'} . '&here=' .                  removeFromFilter($filter, $mapId);
             &Apache::lonnet::escape($params->{'here'}) .               $linkopen .= "&condition=" . $it->{CONDITION} . '&hereType='
             '&jumpType=' . SYMB() . '&jump=' .                  . $params->{'hereType'} . '&here=' .
             &Apache::lonnet::escape($resource->symb()) .                   &Apache::lonnet::escape($params->{'here'}) . 
             "&folderManip=1'>";                  '&jumpType=' . SYMB() . '&jump=' .
                   &Apache::lonnet::escape($resource->symb()) . 
                   "&folderManip=1'>";
           } else {
               # Don't allow users to manipulate folder
               $icon = 'navmap.folder.' . ($nowOpen ? 'closed' : 'open') .
                   '.nomanip.gif';
               $icon = "<img src='/adm/lonIcons/$icon' alt='' border='0' />";
   
               $linkopen = "";
               $linkclose = "";
           }
     }      }
   
     if ($resource->randomout()) {      if ($resource->randomout()) {
Line 623  sub render_resource { Line 687  sub render_resource {
     my $curMarkerEnd = '';      my $curMarkerEnd = '';
   
     # Is this the current resource?      # Is this the current resource?
     if (!$params->{'displayedHereMarker'} &&      if (!$params->{'displayedHereMarker'} && 
         (($params->{'hereType'} == SYMB() &&           $resource->symb() eq $params->{'here'} ) {
           $resource->symb() eq $params->{'here'}) ||  
          ($params->{'hereType'} == URL() &&  
           $resource->src() eq $params->{'here'}))) {  
         $curMarkerBegin = '<font color="red" size="+2">&gt; </font>';          $curMarkerBegin = '<font color="red" size="+2">&gt; </font>';
         $curMarkerEnd = '<font color="red" size="+2">&lt;</font>';          $curMarkerEnd = '<font color="red" size="+2">&lt;</font>';
           $params->{'displayedHereMarker'} = 1;
     }      }
   
     if ($resource->is_problem() && $part ne "0" &&       if ($resource->is_problem() && $part ne "" && 
         !$params->{'condensed'}) {          !$params->{'condensed'}) {
         $partLabel = " (Part $part)";          $partLabel = " (Part $part)";
         $title = "";          $title = "";
Line 642  sub render_resource { Line 704  sub render_resource {
         $nonLinkedText .= ' (' . $resource->countParts() . ' parts)';          $nonLinkedText .= ' (' . $resource->countParts() . ' parts)';
     }      }
   
     $result .= "  $curMarkerBegin<a href='$link'>$title$partLabel</a>$curMarkerEnd $nonLinkedText</td>";      if (!$params->{'resource_nolink'}) {
           $result .= "  $curMarkerBegin<a href='$link'>$title$partLabel</a>$curMarkerEnd $nonLinkedText</td>";
       } else {
           $result .= "  $curMarkerBegin$title$partLabel$curMarkerEnd $nonLinkedText</td>";
       }
   
     return $result;      return $result;
 }  }
Line 771  sub render { Line 837  sub render {
         $navmap = $args->{'navmap'};          $navmap = $args->{'navmap'};
     }      }
   
       my $r = $args->{'r'};
     my $queryString = $args->{'queryString'};      my $queryString = $args->{'queryString'};
     my $jumpToURL = $args->{'jumpToURL'};      my $jumpToURL = $args->{'jumpToURL'};
     my $jumpToSymb = $args->{'jumpToSymb'};      my $jumpToSymb = $args->{'jumpToSymb'};
     my $jumpType;      my $jumpType;
     my $hereURL = $args->{'hereURL'};      my $here = $args->{'here'};
     my $hereSymb = $args->{'hereSymb'};  
     my $hereType;  
     my $here;  
     my $jump;      my $jump;
     my $currentJumpIndex = setDefault($args->{'currentJumpIndex'}, 0);          my $currentJumpIndex = setDefault($args->{'currentJumpIndex'}, 0);    
       my $suppressNavmap = setDefault($args->{'suppressNavmap'}, 0);
     my $currentJumpDelta = 2; # change this to change how many resources are displayed      my $currentJumpDelta = 2; # change this to change how many resources are displayed
                              # before the current resource when using #current                               # before the current resource when using #current
   
Line 819  sub render { Line 884  sub render {
         # Preference: Symb          # Preference: Symb
                   
         if ($ENV{'form.symb'}) {          if ($ENV{'form.symb'}) {
             $hereType = $jumpType = SYMB();  
             $here = $jump = $ENV{'form.symb'};              $here = $jump = $ENV{'form.symb'};
         } elsif ($ENV{'form.postdata'}) {          } elsif ($ENV{'form.postdata'}) {
             # couldn't find a symb, is there a URL?              # couldn't find a symb, is there a URL?
             my $currenturl = $ENV{'form.postdata'};              my $currenturl = $ENV{'form.postdata'};
             $currenturl=~s/^http\:\/\///;              #$currenturl=~s/^http\:\/\///;
             $currenturl=~s/^[^\/]+//;              #$currenturl=~s/^[^\/]+//;
                           
             $hereType = $jumpType = URL;              $here = $jump = &Apache::lonnet::symbread($currenturl);
             $here = $jump = $currenturl;  
         } else {  
             # Nothing  
             $hereType = $jumpType = NOTHING();  
         }          }
   
         # Step three: Ensure the folders are open          # Step three: Ensure the folders are open
         my $mapIterator = $navmap->getIterator(undef, undef, undef, 1);          my $mapIterator = $navmap->getIterator(undef, undef, undef, 1);
         my $depth = 1;          my $depth = 1;
Line 848  sub render { Line 909  sub render {
             if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; }              if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; }
             if ($curRes == $mapIterator->END_MAP()) { $depth--; }              if ($curRes == $mapIterator->END_MAP()) { $depth--; }
                           
             if (ref($curRes) &&               if (ref($curRes) && $curRes->symb() eq $here) {
                 ($hereType == SYMB() && $curRes->symb() eq $here) ||  
                 (ref($curRes) && $hereType == URL() && $curRes->src() eq $here)) {  
                 my $mapStack = $mapIterator->getStack();                  my $mapStack = $mapIterator->getStack();
                                   
                 # Ensure the parent maps are open                  # Ensure the parent maps are open
Line 897  sub render { Line 956  sub render {
     if ( !defined($args->{'iterator'}) && $ENV{'form.folderManip'} ) { # we came from a user's manipulation of the nav page      if ( !defined($args->{'iterator'}) && $ENV{'form.folderManip'} ) { # 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"          # 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          # from the querystring, and get the new "jump" marker
         $hereType = $ENV{'form.hereType'};  
         $here = $ENV{'form.here'};          $here = $ENV{'form.here'};
         $jumpType = $ENV{'form.jumpType'} || NOTHING();  
         $jump = $ENV{'form.jump'};          $jump = $ENV{'form.jump'};
     }       } 
           
Line 917  sub render { Line 974  sub render {
         # Paranoia: Make sure it's ready          # Paranoia: Make sure it's ready
         $navmap->init();          $navmap->init();
   
         $args->{'iterator'} = $it = $navmap->getIterator(undef, undef, $filterHash, $condition);          # See if we're being passed a specific map
           if ($args->{'iterator_map'}) {
               my $map = $args->{'iterator_map'};
               $map = $navmap->getResourceByUrl($map);
               my $firstResource = $map->map_start();
               my $finishResource = $map->map_finish();
   
               $args->{'iterator'} = $it = $navmap->getIterator($firstResource, $finishResource, $filterHash, $condition);
           } else {
               $args->{'iterator'} = $it = $navmap->getIterator(undef, undef, $filterHash, $condition);
           }
     }      }
           
     my $showParts = setDefault($args->{'showParts'}, 1);      my $showParts = setDefault($args->{'showParts'}, 1);
Line 925  sub render { Line 992  sub render {
     # keeps track of when the current resource is found,      # keeps track of when the current resource is found,
     # so we can back up a few and put the anchor above the      # so we can back up a few and put the anchor above the
     # current resource      # current resource
     my $r = $args->{'r'};  
     my $printKey = $args->{'printKey'};      my $printKey = $args->{'printKey'};
     my $printCloseAll = $args->{'printCloseAll'};      my $printCloseAll = $args->{'printCloseAll'};
     if (!defined($printCloseAll)) { $printCloseAll = 1; }      if (!defined($printCloseAll)) { $printCloseAll = 1; }
       my $filterFunc = setDefault($args->{'filterFunc'},
                                   sub {return 1;});
       
     # Print key?      # Print key?
     if ($printKey) {      if ($printKey) {
         $result .= '<table border="0" cellpadding="2" cellspacing="0">';          $result .= '<table border="0" cellpadding="2" cellspacing="0">';
Line 955  sub render { Line 1023  sub render {
     if ($printCloseAll) {      if ($printCloseAll) {
         if ($condition) {          if ($condition) {
             $result.="<a href=\"navmaps?condition=0&filter=&$queryString" .              $result.="<a href=\"navmaps?condition=0&filter=&$queryString" .
                 "&hereType=$hereType&here=" . Apache::lonnet::escape($here) .                  "&here=" . Apache::lonnet::escape($here) .
                 "\">Close All Folders</a>";                  "\">Close All Folders</a>";
         } else {          } else {
             $result.="<a href=\"navmaps?condition=1&filter=&$queryString" .              $result.="<a href=\"navmaps?condition=1&filter=&$queryString" .
                 "&hereType=$hereType&here=" . Apache::lonnet::escape($here) .                   "&here=" . Apache::lonnet::escape($here) . 
                 "\">Open All Folders</a>";                  "\">Open All Folders</a>";
         }          }
           $result .= "<br /><br />\n";
     }          }    
   
     if ($r) {      if ($r) {
Line 999  sub render { Line 1068  sub render {
   
     # export "here" marker information      # export "here" marker information
     $args->{'here'} = $here;      $args->{'here'} = $here;
     $args->{'hereType'} = $hereType;  
   
     while ($depth > 0) {      while ($depth > 0) {
         if ($curRes == $it->BEGIN_MAP()) { $depth++; }          if ($curRes == $it->BEGIN_MAP()) { $depth++; }
Line 1021  sub render { Line 1089  sub render {
   
         # If this isn't an actual resource, continue on          # If this isn't an actual resource, continue on
         if (!ref($curRes)) {          if (!ref($curRes)) {
             $curRes = $it->next();  
             next;              next;
         }          }
   
         $args->{'counter'}++;          $args->{'counter'}++;
   
           # If this has been filtered out, continue on
           if (!(&$filterFunc($curRes))) {
               $args->{'isNewBranch'} = 0; # Don't falsely remember this
               next;
           } 
   
           # If we're suppressing navmaps and this is a navmap, continue on
           if ($suppressNavmap && $curRes->src() =~ /^\/adm\/navmaps/) {
               next;
           }
   
         # Does it have multiple parts?          # Does it have multiple parts?
         $args->{'multipart'} = 0;          $args->{'multipart'} = 0;
         $args->{'condensed'} = 0;          $args->{'condensed'} = 0;
Line 1077  sub render { Line 1155  sub render {
                                           
                 }                  }
             }              }
           } 
                           
         } else {  
             # Not showing parts  
             @parts = ("0"); # show main part only  
         }  
   
         # If the multipart problem was condensed, "forget" it was multipart          # If the multipart problem was condensed, "forget" it was multipart
         if (scalar(@parts) == 1) {          if (scalar(@parts) == 1) {
             $args->{'multipart'} = 0;              $args->{'multipart'} = 0;
         }          }
   
         # In the event of a network error, display one part.  
         # 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");  
         }  
   
         # Now, we've decided what parts to show. Loop through them and          # Now, we've decided what parts to show. Loop through them and
         # show them.          # show them.
         foreach my $part (@parts) {          foreach my $part ('', @parts) {
               if ($part eq '0') {
                   next;
               }
             $rownum ++;              $rownum ++;
             my $backgroundColor = $backgroundColors[$rownum % scalar(@backgroundColors)];              my $backgroundColor = $backgroundColors[$rownum % scalar(@backgroundColors)];
                           
Line 1138  sub render { Line 1207  sub render {
             $result .= "    </tr>\n";              $result .= "    </tr>\n";
             $args->{'isNewBranch'} = 0;              $args->{'isNewBranch'} = 0;
         }          }
           
         if ($r && $rownum % 20 == 0) {          if ($r && $rownum % 20 == 0) {
             $r->print($result);              $r->print($result);
             $result = "";              $result = "";
             $r->rflush();              $r->rflush();
         }          }
               } continue {
         $curRes = $it->next();          $curRes = $it->next();
     }      }
           
Line 1570  sub parmval_real { Line 1639  sub parmval_real {
     return '';      return '';
 }  }
   
   =pod 
   
   =item * B<getResourceByUrl>(url): Retrieves a resource object by URL of the resource. If passed a resource object, it will simply return it, so it is safe to use this method in code like "$res = $navmap->getResourceByUrl($res)", if you're not sure if $res is already an object, or just a URL. If the resource appears multiple times in the course, only the first instance will be returned. As a result, this is probably useful only for maps.
   
   =item * B<retrieveResources>(map, filterFunc, recursive, bailout): The map is a specification of a map to retreive the resources from, either as a url or as an object. The filterFunc is a reference to a function that takes a resource object as its one argument and returns true if the resource should be included, or false if it should not be. If recursive is true, the map will be recursively examined, otherwise it will not be. If bailout is true, the function will return as soon as it finds a resource, if false it will finish. By default, the map is the top-level map of the course, filterFunc is a function that always returns 1, recursive is true, bailout is false. The resources will be returned in a list reference containing the resource objects for the corresponding resources, with B<no structure information> in the list; regardless of branching, recursion, etc., it will be a flat list. 
   
   Thus, this is suitable for cases where you don't want the structure, just a list of all resources. It is also suitable for finding out how many resources match a given description; for this use, if all you want to know is if I<any> resources match the description, the bailout parameter will allow you to avoid potentially expensive enumeration of all matching resources.
   
   =item * B<hasResources>(map, filterFunc, recursive): Convience method for 
   
    scalar(retrieveResources($map, $filterFunc, $recursive, 1)) > 0
   
   which will tell whether the map has resources matching the description in the filter function.
   
   =cut
   
   sub getResourceByUrl {
       my $self = shift;
       my $resUrl = shift;
   
       if (ref($resUrl)) { return $resUrl; }
   
       $resUrl = &Apache::lonnet::clutter($resUrl);
       my $resId = $self->{NAV_HASH}->{'ids_' . $resUrl};
       if ($resId =~ /,/) {
           $resId = (split (/,/, $resId))[0];
       }
       if (!$resId) { return ''; }
       return $self->getById($resId);
   }
   
   sub retrieveResources {
       my $self = shift;
       my $map = shift;
       my $filterFunc = shift;
       if (!defined ($filterFunc)) {
           $filterFunc = sub {return 1;};
       }
       my $recursive = shift;
       if (!defined($recursive)) { $recursive = 1; }
       my $bailout = shift;
       if (!defined($bailout)) { $bailout = 0; }
   
       # Create the necessary iterator.
       if (!ref($map)) { # assume it's a url of a map.
           $map = $self->getMapByUrl($map);
       }
   
       # Check the map's validity.
       if (!$map || !$map->is_map()) {
           # Oh, to throw an exception.... how I'd love that!
           return ();
       }
   
       # Get an iterator.
       my $it = $self->getIterator($map->map_start(), $map->map_finish(),
                                   !$recursive);
   
       my @resources = ();
   
       # Run down the iterator and collect the resources.
       my $depth = 1;
       $it->next();
       my $curRes = $it->next();
   
       while ($depth > 0) {
           if ($curRes == $it->BEGIN_MAP()) {
               $depth++;
           }
           if ($curRes == $it->END_MAP()) {
               $depth--;
           }
           
           if (ref($curRes)) {
               if (!&$filterFunc($curRes)) {
                   next;
               }
   
               push @resources, $curRes;
   
               if ($bailout) {
                   return @resources;
               }
           }
   
           $curRes = $it->next();
       }
   
       return @resources;
   }
   
   sub hasResource {
       my $self = shift;
       my $map = shift;
       my $filterFunc = shift;
       my $recursive = shift;
       
       return scalar($self->retrieveResources($map, $filterFunc, $recursive, 1)) > 0;
   }
   
 1;  1;
   
 package Apache::lonnavmaps::iterator;  package Apache::lonnavmaps::iterator;
Line 2312  These methods are useful for getting inf Line 2481  These methods are useful for getting inf
 sub map_finish {  sub map_finish {
     my $self = shift;      my $self = shift;
     my $src = $self->src();      my $src = $self->src();
       $src = Apache::lonnet::clutter($src);
     my $res = $self->navHash("map_finish_$src", 0);      my $res = $self->navHash("map_finish_$src", 0);
     $res = $self->{NAV_MAP}->getById($res);      $res = $self->{NAV_MAP}->getById($res);
     return $res;      return $res;
Line 2324  sub map_pc { Line 2494  sub map_pc {
 sub map_start {  sub map_start {
     my $self = shift;      my $self = shift;
     my $src = $self->src();      my $src = $self->src();
       $src = Apache::lonnet::clutter($src);
     my $res = $self->navHash("map_start_$src", 0);      my $res = $self->navHash("map_start_$src", 0);
     $res = $self->{NAV_MAP}->getById($res);      $res = $self->{NAV_MAP}->getById($res);
     return $res;      return $res;
Line 2500  sub getErrors { Line 2671  sub getErrors {
   
 =pod  =pod
   
 =item * B<parts>(): Returns a list reference containing sorted strings corresponding to each part of the problem. To count the number of parts, use the list in a scalar context, and subtract one if greater then two. (One part problems have a part 0. Multi-parts have a part 0, plus a part for each part. Filtering part 0 if you want it is up to you.)  =item * B<parts>(): Returns a list reference containing sorted strings corresponding to each part of the problem. To count the number of parts, use the list in a scalar context, and subtract one if greater than two. (One part problems have a part 0. Multi-parts have a part 0, plus a part for each part. Filtering part 0 if you want it is up to you.)
   
 =item * B<countParts>(): Returns the number of parts of the problem a student can answer. Thus, for single part problems, returns 1. For multipart, it returns the number of parts in the problem, not including psuedo-part 0. Thus, B<parts> may return an array with fewer parts in it then countParts might lead you to believe.  =item * B<countParts>(): Returns the number of parts of the problem a student can answer. Thus, for single part problems, returns 1. For multipart, it returns the number of parts in the problem, not including psuedo-part 0. Thus, B<parts> may return an array with fewer parts in it then countParts might lead you to believe.
   
Line 2535  sub countParts { Line 2706  sub countParts {
 sub extractParts {   sub extractParts { 
     my $self = shift;      my $self = shift;
           
     return if ($self->{PARTS});      return if (defined($self->{PARTS}));
     return if ($self->ext);      return if ($self->ext);
   
     $self->{PARTS} = [];      $self->{PARTS} = [];
   
     # Retrieve part count, if this is a problem      # Retrieve part count, if this is a problem
     if ($self->is_problem()) {      if ($self->is_problem()) {
         my $metadata = &Apache::lonnet::metadata($self->src(), 'allpossiblekeys');          my $metadata = &Apache::lonnet::metadata($self->src(), 'packages');
         if (!$metadata) {          if (!$metadata) {
             $self->{RESOURCE_ERROR} = 1;              $self->{RESOURCE_ERROR} = 1;
             $self->{PARTS} = [];              $self->{PARTS} = [];
             return;              return;
         }          }
           
         foreach (split(/\,/,$metadata)) {          foreach (split(/\,/,$metadata)) {
             if ($_ =~ /^parameter\_(.*)\_opendate$/) {              if ($_ =~ /^part_(.*)$/) {
                 push @{$self->{PARTS}}, $1;                  my $part = $1;
                   # check to see if part is turned off.
                   if (! Apache::loncommon::check_if_partid_hidden($part, $self->symb())) {
                       push @{$self->{PARTS}}, $1;
                   }
             }              }
         }          }
                   
                   
         # Is this possible to do in one line? - Jeremy  
         my @sortedParts = sort @{$self->{PARTS}};          my @sortedParts = sort @{$self->{PARTS}};
         $self->{PARTS} = \@sortedParts;          $self->{PARTS} = \@sortedParts;
     }      }

Removed from v.1.141  
changed lines
  Added in v.1.158


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