Diff for /loncom/interface/lonnavmaps.pm between versions 1.290 and 1.337

version 1.290, 2004/09/15 21:10:11 version 1.337, 2005/09/20 07:52:03
Line 30 Line 30
 package Apache::lonnavmaps;  package Apache::lonnavmaps;
   
 use strict;  use strict;
   use GDBM_File;
 use Apache::Constants qw(:common :http);  use Apache::Constants qw(:common :http);
 use Apache::loncommon();  use Apache::loncommon();
 use Apache::lonmenu();  use Apache::lonmenu();
   use Apache::lonenc();
 use Apache::lonlocal;  use Apache::lonlocal;
   use Apache::lonnet;
 use POSIX qw (floor strftime);  use POSIX qw (floor strftime);
 use Data::Dumper; # for debugging, not always used  use Data::Dumper; # for debugging, not always 
   use Time::HiRes qw( gettimeofday tv_interval );
   
 # symbolic constants  # symbolic constants
 sub SYMB { return 1; }  sub SYMB { return 1; }
Line 53  my %statusIconMap = Line 57  my %statusIconMap =
      $resObj->CLOSED       => '',       $resObj->CLOSED       => '',
      $resObj->OPEN         => 'navmap.open.gif',       $resObj->OPEN         => 'navmap.open.gif',
      $resObj->CORRECT      => 'navmap.correct.gif',       $resObj->CORRECT      => 'navmap.correct.gif',
        $resObj->PARTIALLY_CORRECT      => 'navmap.ellipsis.gif',
      $resObj->INCORRECT    => 'navmap.wrong.gif',       $resObj->INCORRECT    => 'navmap.wrong.gif',
      $resObj->ATTEMPTED    => 'navmap.ellipsis.gif',       $resObj->ATTEMPTED    => 'navmap.ellipsis.gif',
      $resObj->ERROR        => ''       $resObj->ERROR        => ''
Line 77  my %colormap = Line 82  my %colormap =
       $resObj->OPEN                   => '',        $resObj->OPEN                   => '',
       $resObj->NOTHING_SET            => '',        $resObj->NOTHING_SET            => '',
       $resObj->ATTEMPTED              => '',        $resObj->ATTEMPTED              => '',
       $resObj->ANSWER_SUBMITTED       => ''        $resObj->ANSWER_SUBMITTED       => '',
         $resObj->PARTIALLY_CORRECT      => '#006600'
       );        );
 # 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 = "#FF0000";  my $hurryUpColor = "#FF0000";
   
 sub launch_win {  sub launch_win {
     my ($mode,$script,$toplinkitems)=@_;      my ($mode,$script,$toplinkitems,$firsttime)=@_;
     my $result;      my $result;
     if ($script ne 'no') {      if ($script ne 'no') {
  $result.='<script type="text/javascript">';   $result.='<script type="text/javascript">';
     }      }
     $result.='function launch_navmapwin() {      if ($firsttime) {
    $result.='function launch_navmapwin() {
                    newWindow=open(\'/adm/navmaps?launchExternalRoles\',\'loncapanav\',\'width=400,height=600,scrollbars=1\');
                  }';
       } else {
    $result.='function launch_navmapwin() {
                  newWindow=open(\'/adm/navmaps?launchExternal\',\'loncapanav\',\'width=400,height=600,scrollbars=1\');                   newWindow=open(\'/adm/navmaps?launchExternal\',\'loncapanav\',\'width=400,height=600,scrollbars=1\');
                }';                 }';
       }
     if ($mode eq 'now') {      if ($mode eq 'now') {
  $result.="\nlaunch_navmapwin();\n";   $result.="\nlaunch_navmapwin();\n";
     }      }
Line 106  sub launch_win { Line 118  sub launch_win {
 }  }
   
 sub close {  sub close {
     if ($ENV{'environment.remotenavmap'} ne 'on') { return ''; }      if ($env{'environment.remotenavmap'} ne 'on') { return ''; }
     return(<<ENDCLOSE);      return(<<ENDCLOSE);
 <script type="text/javascript">  <script type="text/javascript">
 window.status='Accessing Nav Control';  window.status='Accessing Nav Control';
Line 119  window.status='Done.'; Line 131  window.status='Done.';
 ENDCLOSE  ENDCLOSE
 }  }
   
 sub nav_control_js {  
     my $nav=($ENV{'environment.remotenavmap'} eq 'on');  
     return (<<NAVCONTROL);  
     var w_loncapanav_flag="$nav";  
   
   
 function gonav(url) {  
    if (w_loncapanav_flag != 1) {  
       go(url);  
    }  else {  
       navwindow=window.open(url,  
                   "loncapanav","height=600,width=400,scrollbars=1");   
    }  
 }  
 NAVCONTROL  
 }  
   
 sub update {  sub update {
     if ($ENV{'environment.remotenavmap'} ne 'on') { return ''; }      if ($env{'environment.remotenavmap'} ne 'on') { return ''; }
     if (!$ENV{'request.course.id'}) { return ''; }      if (!$env{'request.course.id'}) { return ''; }
     if ($ENV{'REQUEST_URI'}=~m|^/adm/navmaps|) { return ''; }      if ($ENV{'REQUEST_URI'}=~m|^/adm/navmaps|) { return ''; }
     return(<<ENDUPDATE);      return(<<ENDUPDATE);
 <form name="navform"></form>  <form name="navform"></form>
Line 157  sub handler { Line 152  sub handler {
   
 sub real_handler {  sub real_handler {
     my $r = shift;      my $r = shift;
       #my $t0=[&gettimeofday()];
     # Handle header-only request      # Handle header-only request
     if ($r->header_only) {      if ($r->header_only) {
         if ($ENV{'browser.mathml'}) {          if ($env{'browser.mathml'}) {
             &Apache::loncommon::content_type($r,'text/xml');              &Apache::loncommon::content_type($r,'text/xml');
         } else {          } else {
             &Apache::loncommon::content_type($r,'text/html');              &Apache::loncommon::content_type($r,'text/html');
Line 170  sub real_handler { Line 165  sub real_handler {
     }      }
   
     # Send header, don't cache this page      # Send header, don't cache this page
     if ($ENV{'browser.mathml'}) {      if ($env{'browser.mathml'}) {
         &Apache::loncommon::content_type($r,'text/xml');          &Apache::loncommon::content_type($r,'text/xml');
     } else {      } else {
         &Apache::loncommon::content_type($r,'text/html');          &Apache::loncommon::content_type($r,'text/html');
Line 179  sub real_handler { Line 174  sub real_handler {
     $r->send_http_header;      $r->send_http_header;
   
     my %toplinkitems=();      my %toplinkitems=();
       &add_linkitem(\%toplinkitems,'blank','',"Select Action");
     if ($ENV{QUERY_STRING} eq 'collapseExternal') {      if ($ENV{QUERY_STRING} eq 'collapseExternal') {
  &Apache::lonnet::put('environment',{'remotenavmap' => 'off'});   &Apache::lonnet::put('environment',{'remotenavmap' => 'off'});
  &Apache::lonnet::appenv('environment.remotenavmap' => 'off');   &Apache::lonnet::appenv('environment.remotenavmap' => 'off');
Line 199  MENU Line 194  MENU
      $navstatus       $navstatus
 MENU  MENU
  }   }
    my $html=&Apache::lonxml::xmlbegin();
  $r->print(<<"ENDSUBM");   $r->print(<<"ENDSUBM");
  <html>   $html
         <head>          <head>
   <script type="text/javascript">    <script type="text/javascript">
      function submitthis() {       function submitthis() {
Line 215  MENU Line 211  MENU
 ENDSUBM  ENDSUBM
         return;          return;
     }      }
     if ($ENV{QUERY_STRING} eq 'launchExternal') {      if ($ENV{QUERY_STRING} =~ /^launchExternal/) {
  &Apache::lonnet::put('environment',{'remotenavmap' => 'on'});   &Apache::lonnet::put('environment',{'remotenavmap' => 'on'});
  &Apache::lonnet::appenv('environment.remotenavmap' => 'on');   &Apache::lonnet::appenv('environment.remotenavmap' => 'on');
     my $menu=&Apache::lonmenu::reopenmenu();
    my $navstatus=&Apache::lonmenu::get_nav_status();
    if ($menu) {
       $r->print(<<MENU);
                <script type="text/javascript">
                swmenu=$menu
                swmenu.clearTimeout(swmenu.menucltim);
        $navstatus
                </script>
   MENU
           }
      }
       if ($ENV{QUERY_STRING} eq 'turningOffExternal') {
    $env{'environment.remotenavmap'}='off';
     }      }
   
     # Create the nav map      # Create the nav map
Line 225  ENDSUBM Line 235  ENDSUBM
   
     if (!defined($navmap)) {      if (!defined($navmap)) {
         my $requrl = $r->uri;          my $requrl = $r->uri;
         $ENV{'user.error.msg'} = "$requrl:bre:0:0:Course not initialized";          $env{'user.error.msg'} = "$requrl:bre:0:0:Course not initialized";
         return HTTP_NOT_ACCEPTABLE;          return HTTP_NOT_ACCEPTABLE;
     }      }
       my $html=&Apache::lonxml::xmlbegin();
     $r->print("<html><head>\n");      $r->print("$html<head>\n");
     $r->print("<title>".&mt('Navigate Course Contents')."</title>");      $r->print("<title>".&mt('Navigate Course Contents')."</title>");
 # ------------------------------------------------------------ Get query string  # ------------------------------------------------------------ Get query string
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register','sort','showOnlyHomework']);      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register','sort','showOnlyHomework','postsymb']);
           
 # ----------------------------------------------------- Force menu registration  # ----------------------------------------------------- Force menu registration
     my $addentries='';      my $addentries='';
     my $more_unload;      my $more_unload;
     my $body_only='';      my $body_only='';
     if ($ENV{'environment.remotenavmap'} eq 'on') {      if ($env{'environment.remotenavmap'} eq 'on') {
  $r->print('<script type="text/javascript">   $r->print('<script type="text/javascript">
                       function collapse() {                        function collapse() {
                          this.document.location="/adm/navmaps?collapseExternal";                           this.document.location="/adm/navmaps?collapseExternal";
Line 248  ENDSUBM Line 258  ENDSUBM
 # $more_unload="collapse()"  # $more_unload="collapse()"
  $body_only=1;   $body_only=1;
     }      }
     if ($ENV{'form.register'}) {      if ($env{'form.register'}) {
  $addentries=' onLoad="'.&Apache::lonmenu::loadevents().   $addentries=' onLoad="'.&Apache::lonmenu::loadevents().
     '" onUnload="'.&Apache::lonmenu::unloadevents().';'.      '" onUnload="'.&Apache::lonmenu::unloadevents().';'.
     $more_unload.'"';      $more_unload.'"';
Line 261  ENDSUBM Line 271  ENDSUBM
     $r->print('</head>'.      $r->print('</head>'.
               &Apache::loncommon::bodytag('Navigate Course Contents','',                &Apache::loncommon::bodytag('Navigate Course Contents','',
   $addentries,$body_only,'',    $addentries,$body_only,'',
   $ENV{'form.register'}));    $env{'form.register'}));
     $r->print('<script>window.focus();</script>');      $r->print('<script>window.focus();</script>');
             
     $r->rflush();      $r->rflush();
Line 292  ENDSUBM Line 302  ENDSUBM
         if ($sequenceCount == 1) {          if ($sequenceCount == 1) {
             # The automatic iterator creation in the render call               # The automatic iterator creation in the render call 
             # will pick this up. We know the condition because              # will pick this up. We know the condition because
             # the defined($ENV{'form.filter'}) also ensures this              # the defined($env{'form.filter'}) also ensures this
             # is a fresh call.              # is a fresh call.
             $ENV{'form.filter'} = "$sequenceId";              $env{'form.filter'} = "$sequenceId";
         }          }
     }      }
   
     if ($ENV{QUERY_STRING} eq 'launchExternal') {      if ($ENV{QUERY_STRING} eq 'launchExternal') {
  $r->print('   $r->print('
           <form name="returnwin" action="/adm/flip?postdata=return%3a"             <form name="returnwin" action="/adm/flip?postdata=navlaunch%3a" 
                 method="post" target="loncapaclient">                  method="post" target="loncapaclient">
           </form>');            </form>');
  $r->print('   $r->print('
Line 309  ENDSUBM Line 319  ENDSUBM
           </script>');            </script>');
     }      }
   
     if ($ENV{'environment.remotenavmap'} ne 'on') {      if ($env{'environment.remotenavmap'} ne 'on') {
  $r->print(&launch_win('link','yes',\%toplinkitems));   $r->print(&launch_win('link','yes',\%toplinkitems));
     }       } 
     if ($ENV{'environment.remotenavmap'} eq 'on') {      if ($env{'environment.remotenavmap'} eq 'on') {
  &add_linkitem(\%toplinkitems,'closenav','collapse()',   &add_linkitem(\%toplinkitems,'closenav','collapse()',
       "Close navigation window");        "Close navigation window");
     }       } 
Line 339  ENDSUBM Line 349  ENDSUBM
                     pop @$stack; # last resource in the stack is the problem                      pop @$stack; # last resource in the stack is the problem
                                  # itself, which we don't need in the map stack                                   # itself, which we don't need in the map stack
                     my @mapPcs = map {$_->map_pc()} @$stack;                      my @mapPcs = map {$_->map_pc()} @$stack;
                     $ENV{'form.filter'} = join(',', @mapPcs);                      $env{'form.filter'} = join(',', @mapPcs);
   
                     # Mark as both "here" and "jump"                      # Mark as both "here" and "jump"
                     $ENV{'form.postsymb'} = $curRes->symb();                      $env{'form.postsymb'} = $curRes->symb();
                 }                  }
             }              }
         }          }
Line 363  ENDSUBM Line 373  ENDSUBM
   
     # Display only due homework.      # Display only due homework.
     my $showOnlyHomework = 0;      my $showOnlyHomework = 0;
     if ($ENV{'form.showOnlyHomework'} eq "1") {      if ($env{'form.showOnlyHomework'} eq "1") {
         $showOnlyHomework = 1;          $showOnlyHomework = 1;
         $suppressEmptySequences = 1;          $suppressEmptySequences = 1;
         $filterFunc = sub { my $res = shift;           $filterFunc = sub { my $res = shift; 
                             return $res->completable() || $res->is_map();                              return $res->completable() || $res->is_map();
                         };                          };
  &add_linkitem(\%toplinkitems,'everything',   &add_linkitem(\%toplinkitems,'everything',
      'location.href="locatnavmaps?sort='.$ENV{'form.sort'}.'"',       'location.href="navmaps?sort='.$env{'form.sort'}.'"',
       "Show Everything");        "Show Everything");
         $r->print("<p><font size='+2'>".&mt("Uncompleted Homework")."</font></p>");          $r->print("<p><font size='+2'>".&mt("Uncompleted Homework")."</font></p>");
         $ENV{'form.filter'} = '';          $env{'form.filter'} = '';
         $ENV{'form.condition'} = 1;          $env{'form.condition'} = 1;
  $resource_no_folder_link = 1;   $resource_no_folder_link = 1;
     } else {      } else {
  &add_linkitem(\%toplinkitems,'uncompleted',   &add_linkitem(\%toplinkitems,'uncompleted',
       'location.href="navmaps?sort='.$ENV{'form.sort'}.        'location.href="navmaps?sort='.$env{'form.sort'}.
           '&showOnlyHomework=1"',            '&showOnlyHomework=1"',
       "Show Only Uncompleted Homework");        "Show Only Uncompleted Homework");
     }      }
   
     my %selected=($ENV{'form.sort'} => 'selected=on');      my %selected=($env{'form.sort'} => 'selected=on');
     my $sort_html=("<form>      my $sort_html=("<form>
                  <nobr>                   <nobr>
                     <input type=\"hidden\" name=\"showOnlyHomework\" value=\"".$ENV{'form.showOnlyHomework'}."\" />                      <input type=\"hidden\" name=\"showOnlyHomework\" value=\"".$env{'form.showOnlyHomework'}."\" />
                     <input type=\"submit\" value=\"".&mt('Sort by:')."\" />                      <input type=\"submit\" value=\"".&mt('Sort by:')."\" />
                     <select name=\"sort\">                      <select name=\"sort\">
                        <option value=\"default\" $selected{'default'}>".&mt('Default')."</option>                         <option value=\"default\" $selected{'default'}>".&mt('Default')."</option>
                        <option value=\"title\"   $selected{'title'}  >".&mt('Title')."</option>                         <option value=\"title\"   $selected{'title'}  >".&mt('Title')."</option>
                        <option value=\"duedate\" $selected{'duedate'}>".&mt('Duedate')."</option>                         <option value=\"duedate\" $selected{'duedate'}>".&mt('Duedate')."</option>
                          <option value=\"discussion\" $selected{'discussion'}>".&mt('Has New Discussion')."</option>
                     </select>                      </select>
                  </nobr>                   </nobr>
                </form>");                 </form>");
     # renderer call      # renderer call
     my $renderArgs = { 'cols' => [0,1,2,3],      my $renderArgs = { 'cols' => [0,1,2,3],
        'sort' => $ENV{'form.sort'},         'sort' => $env{'form.sort'},
                        'url' => '/adm/navmaps',                         'url' => '/adm/navmaps',
                        'navmap' => $navmap,                         'navmap' => $navmap,
                        'suppressNavmap' => 1,                         'suppressNavmap' => 1,
Line 409  ENDSUBM Line 420  ENDSUBM
                        'caller' => 'navmapsdisplay',                         'caller' => 'navmapsdisplay',
                        'linkitems' => \%toplinkitems};                         'linkitems' => \%toplinkitems};
     my $render = render($renderArgs);      my $render = render($renderArgs);
     $navmap->untieHashes();  
   
     # If no resources were printed, print a reassuring message so the      # If no resources were printed, print a reassuring message so the
     # user knows there was no error.      # user knows there was no error.
Line 420  ENDSUBM Line 430  ENDSUBM
             $r->print("<p><font size='+1'>This course is empty.</font></p>");              $r->print("<p><font size='+1'>This course is empty.</font></p>");
         }          }
     }      }
       #my $td=&tv_interval($t0);
       #$r->print("<br />$td");
   
     $r->print("</body></html>");      $r->print("</body></html>");
     $r->rflush();      $r->rflush();
Line 450  sub removeFromFilter { Line 462  sub removeFromFilter {
   
 # Convenience function: Given a stack returned from getStack on the iterator,  # Convenience function: Given a stack returned from getStack on the iterator,
 # return the correct src() value.  # return the correct src() value.
 # Later, this should add an anchor when we start putting anchors in pages.  
 sub getLinkForResource {  sub getLinkForResource {
     my $stack = shift;      my $stack = shift;
     my $res;      my $res;
Line 458  sub getLinkForResource { Line 469  sub getLinkForResource {
     # Check to see if there are any pages in the stack      # Check to see if there are any pages in the stack
     foreach $res (@$stack) {      foreach $res (@$stack) {
         if (defined($res)) {          if (defined($res)) {
       my $anchor;
     if ($res->is_page()) {      if ($res->is_page()) {
  return $res->src();   foreach (@$stack) { if (defined($_)) { $anchor = $_; }  }
    $anchor=&Apache::lonnet::escape($anchor->shown_symb());
    return ($res->link(),$res->shown_symb(),$anchor);
     }      }
             # in case folder was skipped over as "only sequence"              # in case folder was skipped over as "only sequence"
     my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb());      my ($map,$id,$src)=&Apache::lonnet::decode_symb($res->symb());
     if ($map=~/\.page$/) {      if ($map=~/\.page$/) {
  return &Apache::lonnet::clutter($map).'#'.   my $url=&Apache::lonnet::clutter($map);
     &Apache::lonnet::escape(&Apache::lonnet::declutter($src));   $anchor=&Apache::lonnet::escape($src->shown_symb());
    return ($url,$res->shown_symb(),$anchor);
     }      }
         }          }
     }      }
Line 478  sub getLinkForResource { Line 493  sub getLinkForResource {
         if (defined($_)) { $res = $_; }          if (defined($_)) { $res = $_; }
     }      }
   
     return $res->src();      return ($res->link(),$res->shown_symb());
 }  }
   
 # Convenience function: This separates the logic of how to create  # Convenience function: This separates the logic of how to create
Line 514  sub getDescription { Line 529  sub getDescription {
     if ($status == $res->PAST_DUE_NO_ANSWER) {      if ($status == $res->PAST_DUE_NO_ANSWER) {
         return &mt("Was due")." " . timeToHumanString($res->duedate($part));          return &mt("Was due")." " . timeToHumanString($res->duedate($part));
     }      }
     if ($status == $res->ANSWER_OPEN) {      if ($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT) {
         return &mt("Answer available");          return &mt("Answer available");
     }      }
     if ($status == $res->EXCUSED) {      if ($status == $res->EXCUSED) {
Line 572  sub lastTry { Line 587  sub lastTry {
         $res->duedate($part) > time();          $res->duedate($part) > time();
 }  }
   
 # This puts a human-readable name on the ENV variable.  # This puts a human-readable name on the env variable.
   
 sub advancedUser {  sub advancedUser {
     return $ENV{'request.role.adv'};      return $env{'request.role.adv'};
 }  }
   
   
Line 665  sub timeToHumanString { Line 680  sub timeToHumanString {
             my $timeStr = strftime("%A, %b %e at %I:%M %P", localtime($time));              my $timeStr = strftime("%A, %b %e at %I:%M %P", localtime($time));
             $timeStr =~ s/12:00 am/00:00/;              $timeStr =~ s/12:00 am/00:00/;
             $timeStr =~ s/12:00 pm/noon/;              $timeStr =~ s/12:00 pm/noon/;
             return ($inPast ? "last " : "next ") .              return ($inPast ? "last " : "this ") .
                 $timeStr;                  $timeStr;
         }          }
                   
Line 878  automatically. Line 893  automatically.
   
 =over 4  =over 4
   
 =item * B<iterator>: default: constructs one from %ENV  =item * B<iterator>: default: constructs one from %env
   
 A reference to a fresh ::iterator to use from the navmaps. The  A reference to a fresh ::iterator to use from the navmaps. The
 rendering will reflect the options passed to the iterator, so you can  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  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  one is not passed, the renderer will attempt to construct one from
 ENV{'form.filter'} and ENV{'form.condition'} information, plus the  env{'form.filter'} and env{'form.condition'} information, plus the
 'iterator_map' parameter if any.  'iterator_map' parameter if any.
   
 =item * B<iterator_map>: default: not used  =item * B<iterator_map>: default: not used
Line 894  instruct the renderer to render only a p Line 909  instruct the renderer to render only a p
 the source of the map you want to process, like  the source of the map you want to process, like
 '/res/103/jerf/navmap.course.sequence'.  '/res/103/jerf/navmap.course.sequence'.
   
 =item * B<navmap>: default: constructs one from %ENV  =item * B<navmap>: default: constructs one from %env
   
 A reference to a navmap, used only if an iterator is not passed in. If  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  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  one will be constructed based on env info. This is useful to do basic
 error checking before passing it off to render.  error checking before passing it off to render.
   
 =item * B<r>: default: must be passed in  =item * B<r>: default: must be passed in
Line 924  then only one line will be displayed for Line 939  then only one line will be displayed for
 all parts will always be displayed. If showParts is 0, this is  all parts will always be displayed. If showParts is 0, this is
 ignored.  ignored.
   
 =item * B<jumpCount>: default: determined from %ENV  =item * B<jumpCount>: default: determined from %env
   
 A string identifying the URL to place the anchor 'curloc' at.  A string identifying the URL to place the anchor 'curloc' at.
 It is the responsibility of the renderer user to  It is the responsibility of the renderer user to
 ensure that the #curloc is in the URL. By default, determined through  ensure that the #curloc is in the URL. By default, determined through
 the use of the ENV{} 'jump' information, and should normally "just  the use of the env{} 'jump' information, and should normally "just
 work" correctly.  work" correctly.
   
 =item * B<here>: default: empty string  =item * B<here>: default: empty string
Line 957  are allowing the user to open and close Line 972  are allowing the user to open and close
   
 Describes the currently-open row number to cause the browser to jump  Describes the currently-open row number to cause the browser to jump
 to, because the user just opened that folder. By default, pulled from  to, because the user just opened that folder. By default, pulled from
 the Jump information in the ENV{'form.*'}.  the Jump information in the env{'form.*'}.
   
 =item * B<printKey>: default: false  =item * B<printKey>: default: false
   
Line 1026  sub render_resource { Line 1041  sub render_resource {
     my $nonLinkedText = ''; # stuff after resource title not in link      my $nonLinkedText = ''; # stuff after resource title not in link
   
     my $link = $params->{"resourceLink"};      my $link = $params->{"resourceLink"};
   
       #  The URL part is not escaped at this point, but the symb is... 
       #  The stuff to the left of the ? must have ' replaced by \' since
       #  it will be quoted with ' in the href.
   
       my ($left,$right) = split(/\?/, $link);
       $left =~ s/'/\\'/g;
       $link = $left.'?'.$right;
   
     my $src = $resource->src();      my $src = $resource->src();
     my $it = $params->{"iterator"};      my $it = $params->{"iterator"};
     my $filter = $it->{FILTER};      my $filter = $it->{FILTER};
Line 1034  sub render_resource { Line 1058  sub render_resource {
   
     my $partLabel = "";      my $partLabel = "";
     my $newBranchText = "";      my $newBranchText = "";
           my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons");
     # If this is a new branch, label it so      # If this is a new branch, label it so
     if ($params->{'isNewBranch'}) {      if ($params->{'isNewBranch'}) {
         $newBranchText = "<img src='/adm/lonIcons/branch.gif' border='0' />";          $newBranchText = "<img src='$location/branch.gif' border='0' alt='Branch' />";
     }      }
   
     # links to open and close the folder      # links to open and close the folder
   
       
     my $linkopen = "<a href='$link'>";      my $linkopen = "<a href='$link'>";
   
   
     my $linkclose = "</a>";      my $linkclose = "</a>";
   
     # Default icon: unknown page      # Default icon: unknown page
     my $icon = "<img src='/adm/lonIcons/unknown.gif' alt='' border='0' />";      my $icon = "<img src='$location/unknown.gif' alt='' border='0' alt='&nbsp;&nbsp;' ' />";
           
     if ($resource->is_problem()) {      if ($resource->is_problem()) {
         if ($part eq '0' || $params->{'condensed'}) {          if ($part eq '0' || $params->{'condensed'}) {
             $icon = '<img src="/adm/lonIcons/problem.gif" alt="" border="0" />';              $icon ='<img src="'.$location.'/problem.gif" alt="&nbsp;&nbsp;" border="0" />';
         } else {          } else {
             $icon = $params->{'indentString'};              $icon = $params->{'indentString'};
         }          }
     } else {      } else {
  $icon = "<img src='".&Apache::loncommon::icon($resource->src).   $icon = "<img src='".&Apache::loncommon::icon($resource->src)."' alt='&nbsp;&nbsp;' border='0' />";
     "' alt='' border='0' />";  
     }      }
   
     # Display the correct map icon to open or shut map      # Display the correct map icon to open or shut map
Line 1070  sub render_resource { Line 1097  sub render_resource {
   
         if (!$params->{'resource_no_folder_link'}) {          if (!$params->{'resource_no_folder_link'}) {
             $icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif';              $icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif';
             $icon = "<img src='/adm/lonIcons/$icon' alt='' border='0' />";      $icon = "<img src='$location/$icon' alt='".
    ($nowOpen ? 'Open Folder' : 'Close Folder')."' border='0' />";
   
             $linkopen = "<a href='" . $params->{'url'} . '?' .               $linkopen = "<a href='" . $params->{'url'} . '?' . 
                 $params->{'queryString'} . '&filter=';                  $params->{'queryString'} . '&filter=';
Line 1083  sub render_resource { Line 1111  sub render_resource {
                 '&jump=' .                  '&jump=' .
                 &Apache::lonnet::escape($resource->symb()) .                   &Apache::lonnet::escape($resource->symb()) . 
                 "&folderManip=1'>";                  "&folderManip=1'>";
   
         } else {          } else {
             # Don't allow users to manipulate folder              # Don't allow users to manipulate folder
             $icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') .              $icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') .
                 '.nomanip.gif';                  '.nomanip.gif';
             $icon = "<img src='/adm/lonIcons/$icon' alt='' border='0' />";              $icon = "<img src='$location/$icon' alt='".
    ($nowOpen ? 'Open Folder' : 'Close Folder')."' border='0' />";
   
             $linkopen = "";              $linkopen = "";
             $linkclose = "";              $linkclose = "";
Line 1110  sub render_resource { Line 1140  sub render_resource {
     }      }
   
     # Decide what to display      # Decide what to display
   
     $result .= "$newBranchText$linkopen$icon$linkclose";      $result .= "$newBranchText$linkopen$icon$linkclose";
           
     my $curMarkerBegin = '';      my $curMarkerBegin = '';
Line 1127  sub render_resource { Line 1158  sub render_resource {
         !$params->{'condensed'}) {          !$params->{'condensed'}) {
  my $displaypart=$resource->part_display($part);   my $displaypart=$resource->part_display($part);
         $partLabel = " (Part: $displaypart)";          $partLabel = " (Part: $displaypart)";
  $link.='#'.&Apache::lonnet::escape($part);   if ($link!~/\#/) { $link.='#'.&Apache::lonnet::escape($part); }
         $title = "";          $title = "";
     }      }
   
Line 1136  sub render_resource { Line 1167  sub render_resource {
     }      }
   
     my $target;      my $target;
     if ($ENV{'environment.remotenavmap'} eq 'on') {      if ($env{'environment.remotenavmap'} eq 'on') {
  $target=' target="loncapaclient" ';   $target=' target="loncapaclient" ';
     }      }
     if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) {      if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) {
Line 1153  sub render_communication_status { Line 1184  sub render_communication_status {
     my $discussionHTML = ""; my $feedbackHTML = ""; my $errorHTML = "";      my $discussionHTML = ""; my $feedbackHTML = ""; my $errorHTML = "";
   
     my $link = $params->{"resourceLink"};      my $link = $params->{"resourceLink"};
     my $linkopen = "<a href='$link'>";      my $target;
       if ($env{'environment.remotenavmap'} eq 'on') {
    $target=' target="loncapaclient" ';
       }
       my $linkopen = "<a $target href='$link'>";
     my $linkclose = "</a>";      my $linkclose = "</a>";
       my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc");
     if ($resource->hasDiscussion()) {      if ($resource->hasDiscussion()) {
         $discussionHTML = $linkopen .          $discussionHTML = $linkopen .
             '<img border="0" src="/adm/lonMisc/chat.gif" />' .              '<img border="0" src="'.$location.'/chat.gif" />' .
             $linkclose;              $linkclose;
     }      }
           
Line 1166  sub render_communication_status { Line 1201  sub render_communication_status {
         my $feedback = $resource->getFeedback();          my $feedback = $resource->getFeedback();
         foreach (split(/\,/, $feedback)) {          foreach (split(/\,/, $feedback)) {
             if ($_) {              if ($_) {
                 $feedbackHTML .= '&nbsp;<a href="/adm/email?display='                  $feedbackHTML .= '&nbsp;<a '.$target.' href="/adm/email?display='
                     . &Apache::lonnet::escape($_) . '">'                      . &Apache::lonnet::escape($_) . '">'
                     . '<img src="/adm/lonMisc/feedback.gif" '                      . '<img src="'.$location.'/feedback.gif" '
                     . 'border="0" /></a>';                      . 'border="0" /></a>';
             }              }
         }          }
Line 1181  sub render_communication_status { Line 1216  sub render_communication_status {
             last if ($errorcount>=10); # Only output 10 bombs maximum              last if ($errorcount>=10); # Only output 10 bombs maximum
             if ($_) {              if ($_) {
                 $errorcount++;                  $errorcount++;
                 $errorHTML .= '&nbsp;<a href="/adm/email?display='                  $errorHTML .= '&nbsp;<a '.$target.' href="/adm/email?display='
                     . &Apache::lonnet::escape($_) . '">'                      . &Apache::lonnet::escape($_) . '">'
                     . '<img src="/adm/lonMisc/bomb.gif" '                      . '<img src="'.$location.'/bomb.gif" '
                     . 'border="0" /></a>';                      . 'border="0" /></a>';
             }              }
         }          }
Line 1203  sub render_quick_status { Line 1238  sub render_quick_status {
         $params->{'multipart'} && $part eq "0";          $params->{'multipart'} && $part eq "0";
   
     my $link = $params->{"resourceLink"};      my $link = $params->{"resourceLink"};
     my $linkopen = "<a href='$link'>";      my $target;
       if ($env{'environment.remotenavmap'} eq 'on') {
    $target=' target="loncapaclient" ';
       }
       my $linkopen = "<a $target href='$link'>";
     my $linkclose = "</a>";      my $linkclose = "</a>";
   
     if ($resource->is_problem() &&      if ($resource->is_problem() &&
Line 1212  sub render_quick_status { Line 1251  sub render_quick_status {
         my $icon = $statusIconMap{$resource->simpleStatus($part)};          my $icon = $statusIconMap{$resource->simpleStatus($part)};
         my $alt = $iconAltTags{$icon};          my $alt = $iconAltTags{$icon};
         if ($icon) {          if ($icon) {
             $result .= "<td width='30' valign='center' width='50' align='right'>$linkopen<img width='25' height='25' src='/adm/lonIcons/$icon' border='0' alt='$alt' />$linkclose</td>\n";      my $location=
    &Apache::loncommon::lonhttpdurl("/adm/lonIcons/$icon");
               $result .= "<td width='30' valign='center' width='50' align='right'>$linkopen<img width='25' height='25' src='$location' border='0' alt='$alt' />$linkclose</td>\n";
         } else {          } else {
             $result .= "<td width='30'>&nbsp;</td>\n";              $result .= "<td width='30'>&nbsp;</td>\n";
         }          }
Line 1343  sub setDefault { Line 1384  sub setDefault {
     return $val;      return $val;
 }  }
   
   sub cmp_title {
       my ($atitle,$btitle) = (lc($_[0]->compTitle),lc($_[1]->compTitle));
       $atitle=~s/^\s*//;
       $btitle=~s/^\s*//;
       return $atitle cmp $btitle;
   }
   
 sub render {  sub render {
     my $args = shift;      my $args = shift;
     &Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});      &Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});
     my $result = '';      my $result = '';
   
     # Configure the renderer.      # Configure the renderer.
     my $cols = $args->{'cols'};      my $cols = $args->{'cols'};
     if (!defined($cols)) {      if (!defined($cols)) {
Line 1375  sub render { Line 1422  sub render {
     # marker      # marker
     my $filterHash = {};      my $filterHash = {};
     # Figure out what we're not displaying      # Figure out what we're not displaying
     foreach (split(/\,/, $ENV{"form.filter"})) {      foreach (split(/\,/, $env{"form.filter"})) {
         if ($_) {          if ($_) {
             $filterHash->{$_} = "1";              $filterHash->{$_} = "1";
         }          }
Line 1395  sub render { Line 1442  sub render {
     }      }
   
     my $condition = 0;      my $condition = 0;
     if ($ENV{'form.condition'}) {      if ($env{'form.condition'}) {
         $condition = 1;          $condition = 1;
     }      }
   
     if (!$ENV{'form.folderManip'} && !defined($args->{'iterator'})) {      if (!$env{'form.folderManip'} && !defined($args->{'iterator'})) {
         # Step 1: Check to see if we have a navmap          # Step 1: Check to see if we have a navmap
         if (!defined($navmap)) {          if (!defined($navmap)) {
             $navmap = Apache::lonnavmaps::navmap->new();              $navmap = Apache::lonnavmaps::navmap->new();
Line 1409  sub render { Line 1456  sub render {
         # Step two: Locate what kind of here marker is necessary          # Step two: Locate what kind of here marker is necessary
         # Determine where the "here" marker is and where the screen jumps to.          # Determine where the "here" marker is and where the screen jumps to.
   
         if ($ENV{'form.postsymb'}) {          if ($env{'form.postsymb'} ne '') {
             $here = $jump = $ENV{'form.postsymb'};              $here = $jump = &Apache::lonnet::symbclean($env{'form.postsymb'});
         } elsif ($ENV{'form.postdata'}) {          } elsif ($env{'form.postdata'} ne '') {
             # 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/^[^\/]+//;
                           
             $here = $jump = &Apache::lonnet::symbread($currenturl);              $here = $jump = &Apache::lonnet::symbread($currenturl);
         }   }
    if ($here eq '') {
       my $last;
       if (tie(my %hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db',
                       &GDBM_READER(),0640)) {
    $last=$hash{'last_known'};
    untie(%hash);
       }
       if ($last) { $here = $jump = $last; }
    }
   
         # 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);
Line 1428  sub render { Line 1484  sub render {
         # We only need to do this if we need to open the maps to show the          # We only need to do this if we need to open the maps to show the
         # current position. This will change the counter so we can't count          # current position. This will change the counter so we can't count
         # for the jump marker with this loop.          # for the jump marker with this loop.
         while (($curRes = $mapIterator->next()) && !$found) {          while ($here && ($curRes = $mapIterator->next()) && !$found) {
             if (ref($curRes) && $curRes->symb() eq $here) {              if (ref($curRes) && $curRes->symb() eq $here) {
                 my $mapStack = $mapIterator->getStack();                  my $mapStack = $mapIterator->getStack();
                                   
Line 1445  sub render { Line 1501  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
         $here = $ENV{'form.here'};          $here = $env{'form.here'};
         $jump = $ENV{'form.jump'};          $jump = $env{'form.jump'};
     }       } 
           
     my $it = $args->{'iterator'};      my $it = $args->{'iterator'};
     if (!defined($it)) {      if (!defined($it)) {
         # Construct a default iterator based on $ENV{'form.'} information          # Construct a default iterator based on $env{'form.'} information
                   
         # Step 1: Check to see if we have a navmap          # Step 1: Check to see if we have a navmap
         if (!defined($navmap)) {          if (!defined($navmap)) {
Line 1504  sub render { Line 1560  sub render {
     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; }
       
     # Print key?      # Print key?
     if ($printKey) {      if ($printKey) {
         $result .= '<table border="0" cellpadding="2" cellspacing="0">';          $result .= '<table border="0" cellpadding="2" cellspacing="0">';
         my $date=localtime;          my $date=localtime;
         $result.='<tr><td align="right" valign="bottom">Key:&nbsp;&nbsp;</td>';          $result.='<tr><td align="right" valign="bottom">Key:&nbsp;&nbsp;</td>';
    my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc");
         if ($navmap->{LAST_CHECK}) {          if ($navmap->{LAST_CHECK}) {
             $result .=               $result .= 
                 '<img src="/adm/lonMisc/chat.gif"> '.&mt('New discussion since').' '.                  '<img src="'.$location.'/chat.gif"> '.&mt('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})).
                 '</td><td align="center" valign="bottom">&nbsp;&nbsp;'.                  '</td><td align="center" valign="bottom">&nbsp;&nbsp;'.
                 '<img src="/adm/lonMisc/feedback.gif"> '.&mt('New message (click to open)').'<p>'.                  '<img src="'.$location.'/feedback.gif"> '.&mt('New message (click to open)').'<p>'.
                 '</td>';                   '</td>'; 
         } else {          } else {
             $result .= '<td align="center" valign="bottom">&nbsp;&nbsp;'.              $result .= '<td align="center" valign="bottom">&nbsp;&nbsp;'.
                 '<img src="/adm/lonMisc/chat.gif"> '.&mt('Discussions').'</td><td align="center" valign="bottom">'.                  '<img src="'.$location.'/chat.gif"> '.&mt('Discussions').'</td><td align="center" valign="bottom">'.
                 '&nbsp;&nbsp;<img src="/adm/lonMisc/feedback.gif"> '.&mt('New message (click to open)').                  '&nbsp;&nbsp;<img src="'.$location.'/feedback.gif"> '.&mt('New message (click to open)').
                 '</td>';                   '</td>'; 
         }          }
   
Line 1549  sub render { Line 1606  sub render {
   
     # Check for any unread discussions in all resources.      # Check for any unread discussions in all resources.
     if ($args->{'caller'} eq 'navmapsdisplay') {      if ($args->{'caller'} eq 'navmapsdisplay') {
  my $totdisc = 0;   &add_linkitem($args->{'linkitems'},'clearbubbles',
  my $haveDisc = '';        'document.clearbubbles.submit()',
  my @allres=$navmap->retrieveResources();        'Mark all posts read');
  foreach my $resource (@allres) {   my $time=time;
     if ($resource->hasDiscussion()) {   $result .= (<<END);
  my $ressymb;      <form name="clearbubbles" method="post" action="/adm/feedback">
  if ($resource->symb() =~ m-(___adm/\w+/\w+)/(\d+)/bulletinboard$-) {   <input type="hidden" name="navurl" value="$ENV{'QUERY_STRING'}" />
     $ressymb = 'bulletin___'.$2.$1.'/'.$2.'/bulletinboard';   <input type="hidden" name="navtime" value="$time" />
  } else {  END
     $ressymb = $resource->symb();          if ($args->{'sort'} eq 'discussion') { 
       my $totdisc = 0;
       my $haveDisc = '';
       my @allres=$navmap->retrieveResources();
       foreach my $resource (@allres) {
    if ($resource->hasDiscussion()) {
       $haveDisc .= $resource->wrap_symb().':';
       $totdisc ++;
  }   }
  $haveDisc .= $ressymb.':';  
  $totdisc ++;  
     }      }
  }      if ($totdisc > 0) {
  if ($totdisc > 0) {   $haveDisc =~ s/:$//;
     $haveDisc =~ s/:$//;   $result .= (<<END);
             my $navurl = $ENV{'QUERY_STRING'};   <input type="hidden" name="navmaps" value="$haveDisc" />
     &add_linkitem($args->{'linkitems'},'clearbubbles',      </form>
   'document.clearbubbles.submit()',  
   'Mark all posts read');  
             $result .= (<<END);  
  <form name="clearbubbles" method="post" action="/adm/feedback">  
  <input type="hidden" name="navurl" value="$ENV{'QUERY_STRING'}" />  
  <input type="hidden" name="navmaps" value="$haveDisc" />  
  </form>  
 END  END
               }
  }   }
    $result.='</form>';
     }      }
   
     if ($args->{'caller'} eq 'navmapsdisplay') {      if ($args->{'caller'} eq 'navmapsdisplay') {
         $result .= '<table><tr><td>'.          $result .= '<table><tr><td>'.
                    &Apache::loncommon::help_open_menu('','Navigation Screen','Navigation_Screen','',undef,'RAT').'</td>';                     &Apache::loncommon::help_open_menu('','Navigation Screen','Navigation_Screen','',undef,'RAT').'</td>';
  if ($ENV{'environment.remotenavmap'} ne 'on') {   if ($env{'environment.remotenavmap'} ne 'on') {
     $result .= '<td>&nbsp;</td>';       $result .= '<td>&nbsp;</td>'; 
         } else {          } else {
     $result .= '</tr><tr>';       $result .= '</tr><tr>'; 
         }          }
  $result.=&show_linkitems($args->{'linkitems'});   $result.=&show_linkitems($args->{'linkitems'});
         if ($args->{'sort_html'}) {          if ($args->{'sort_html'}) {
     if ($ENV{'environment.remotenavmap'} ne 'on') {      if ($env{'environment.remotenavmap'} ne 'on') {
  $result.='<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>'.   $result.='<td>&nbsp;</td><td>&nbsp;</td><td>&nbsp;</td>'.
     '<td align="right">'.$args->{'sort_html'}.'</td></tr>';      '<td align="right">'.$args->{'sort_html'}.'</td></tr>';
     } else {      } else {
Line 1624  END Line 1681  END
     $args->{'indentLevel'} = 0;      $args->{'indentLevel'} = 0;
     $args->{'isNewBranch'} = 0;      $args->{'isNewBranch'} = 0;
     $args->{'condensed'} = 0;          $args->{'condensed'} = 0;    
     $args->{'indentString'} = setDefault($args->{'indentString'}, "<img src='/adm/lonIcons/whitespace1.gif' width='25' height='1' alt='' border='0' />");      my $location=
    &Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace1.gif");
       $args->{'indentString'} = setDefault($args->{'indentString'}, "<img src='$location' width='25' height='1' alt='&nbsp;&nbsp;' border='0' />");
     $args->{'displayedHereMarker'} = 0;      $args->{'displayedHereMarker'} = 0;
   
     # If we're suppressing empty sequences, look for them here. Use DFS for speed,      # If we're suppressing empty sequences, look for them here. Use DFS for speed,
Line 1685  END Line 1744  END
  return &$oldFilterFunc($res);   return &$oldFilterFunc($res);
     };      };
  @resources=$navmap->retrieveResources(undef,$filterFunc);   @resources=$navmap->retrieveResources(undef,$filterFunc);
  @resources= sort {   @resources= sort { &cmp_title($a,$b) } @resources;
     my ($atitle,$btitle) = (lc($a->compTitle),lc($b->compTitle));  
     $atitle=~s/^\s*//;  
     $btitle=~s/^\s*//;  
     return $atitle cmp $btitle  
     } @resources;  
     } elsif ($args->{'sort'} eq 'duedate') {      } elsif ($args->{'sort'} eq 'duedate') {
  @resources=$navmap->retrieveResources(undef,   my $oldFilterFunc = $filterFunc;
        sub { shift->is_problem(); });   my $filterFunc= 
  @resources= sort      sub {
  {   my ($res)=@_;
    if (!$res->is_problem()) { return 0;}
    return &$oldFilterFunc($res);
       };
    @resources=$navmap->retrieveResources(undef,$filterFunc);
    @resources= sort {
     if ($a->duedate ne $b->duedate) {      if ($a->duedate ne $b->duedate) {
         return $a->duedate cmp $b->duedate;          return $a->duedate cmp $b->duedate;
     } else {  
  lc($a->compTitle) cmp lc($b->compTitle)  
     }      }
       my $value=&cmp_title($a,$b);
       return $value;
  } @resources;   } @resources;
       } elsif ($args->{'sort'} eq 'discussion') {
    my $oldFilterFunc = $filterFunc;
    my $filterFunc= 
       sub {
    my ($res)=@_;
    if (!$res->hasDiscussion() &&
       !$res->getFeedback() &&
       !$res->getErrors()) { return 0;}
    return &$oldFilterFunc($res);
       };
    @resources=$navmap->retrieveResources(undef,$filterFunc);
    @resources= sort { &cmp_title($a,$b) } @resources;
     } else {      } else {
  #unknow sort mechanism or default   #unknow sort mechanism or default
  undef($args->{'sort'});   undef($args->{'sort'});
Line 1818  END Line 1889  END
             # Add part 0 so we display it correctly.              # Add part 0 so we display it correctly.
             unshift @parts, '0';              unshift @parts, '0';
         }          }
   
    {
       my ($src,$symb,$anchor,$stack);
       if ($args->{'sort'}) {
    my $it = $navmap->getIterator(undef, undef, undef, 1);
    while ( my $res=$it->next()) {
       if (ref($res) &&
    $res->symb() eq  $curRes->symb()) { last; }
    }
    $stack=$it->getStack();
       } else {
    $stack=$it->getStack();
       }
       ($src,$symb,$anchor)=getLinkForResource($stack);
       if (defined($anchor)) { $anchor='#'.$anchor; }
       my $srcHasQuestion = $src =~ /\?/;
       $args->{"resourceLink"} = $src.
    ($srcHasQuestion?'&':'?') .
    'symb=' . &Apache::lonnet::escape($symb).$anchor;
    }
         # 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) {
Line 1829  END Line 1919  END
   
             # Set up some data about the parts that the cols might want              # Set up some data about the parts that the cols might want
             my $filter = $it->{FILTER};              my $filter = $it->{FILTER};
     my $src;  
     if ($args->{'sort'}) {  
  $src = $curRes->src(); # FIXME this is wrong for .pages  
     } else {  
  my $stack = $it->getStack();  
  $src=getLinkForResource($stack);  
     }  
             my $anchor='';  
             if ($src=~s/(\#.*)$//) {  
  $anchor=$1;  
     }  
             my $srcHasQuestion = $src =~ /\?/;  
             $args->{"resourceLink"} = $src.  
                 ($srcHasQuestion?'&':'?') .  
                 'symb=' . &Apache::lonnet::escape($curRes->symb()).  
  $anchor;  
   
             # Now, display each column.              # Now, display each column.
             foreach my $col (@$cols) {              foreach my $col (@$cols) {
Line 1911  if (location.href.indexOf('#curloc')==-1 Line 1985  if (location.href.indexOf('#curloc')==-1
         $r->rflush();          $r->rflush();
     }      }
                   
     if ($mustCloseNavMap) { $navmap->untieHashes(); }   
   
     return $result;      return $result;
 }  }
   
Line 1924  sub add_linkitem { Line 1996  sub add_linkitem {
   
 sub show_linkitems {  sub show_linkitems {
     my ($linkitems)=@_;      my ($linkitems)=@_;
     my @linkorder = ("launchnav","closenav","firsthomework","everything",      my @linkorder = ("blank","launchnav","closenav","firsthomework",
      "uncompleted","changefolder","clearbubbles");       "everything","uncompleted","changefolder","clearbubbles");
           
     my $result .= (<<ENDBLOCK);      my $result .= (<<ENDBLOCK);
               <td align="left">                <td align="left">
Line 1952  ENDBLOCK Line 2024  ENDBLOCK
     $result .= '</select>&nbsp;<input type="button" name="chgnav"      $result .= '</select>&nbsp;<input type="button" name="chgnav"
                    value="Go" onClick="javascript:changeNavDisplay()" />                     value="Go" onClick="javascript:changeNavDisplay()" />
                 </nobr></form></td>'."\n";                  </nobr></form></td>'."\n";
   
     return $result;      return $result;
 }  }
   
Line 2012  successful, or B<undef> if not. Line 2085  successful, or B<undef> if not.
   
 =back  =back
   
 When you are done with the $navmap object, you I<must> call   
 $navmap->untieHashes(), or you'll prevent the current user from using that   
 course until the web server is restarted. (!)  
   
 =head2 Methods  =head2 Methods
   
 =over 4  =over 4
Line 2028  See iterator documentation below. Line 2097  See iterator documentation below.
   
 use strict;  use strict;
 use GDBM_File;  use GDBM_File;
   use Apache::lonnet;
   
 sub new {  sub new {
     # magic invocation to create a class instance      # magic invocation to create a class instance
Line 2047  sub new { Line 2117  sub new {
   
     my %navmaphash;      my %navmaphash;
     my %parmhash;      my %parmhash;
     my $courseFn = $ENV{"request.course.fn"};      my $courseFn = $env{"request.course.fn"};
     if (!(tie(%navmaphash, 'GDBM_File', "${courseFn}.db",      if (!(tie(%navmaphash, 'GDBM_File', "${courseFn}.db",
               &GDBM_READER(), 0640))) {                &GDBM_READER(), 0640))) {
         return undef;          return undef;
Line 2073  sub generate_course_user_opt { Line 2143  sub generate_course_user_opt {
     my $self = shift;      my $self = shift;
     if ($self->{COURSE_USER_OPT_GENERATED}) { return; }      if ($self->{COURSE_USER_OPT_GENERATED}) { return; }
   
     my $uname=$ENV{'user.name'};      my $uname=$env{'user.name'};
     my $udom=$ENV{'user.domain'};      my $udom=$env{'user.domain'};
     my $uhome=$ENV{'user.home'};      my $cid=$env{'request.course.id'};
     my $cid=$ENV{'request.course.id'};      my $cdom=$env{'course.'.$cid.'.domain'};
     my $chome=$ENV{'course.'.$cid.'.home'};      my $cnum=$env{'course.'.$cid.'.num'};
     my ($cdom,$cnum)=split(/\_/,$cid);  
           
     my $userprefix=$uname.'_'.$udom.'_';  
       
     my %courserdatas; my %useropt; my %courseopt; my %userrdatas;  
     unless ($uhome eq 'no_host') {   
 # ------------------------------------------------- Get coursedata (if present)  # ------------------------------------------------- Get coursedata (if present)
  unless ((time-$courserdatas{$cid.'.last_cache'})<240) {      my $courseopt=&Apache::lonnet::get_courseresdata($cnum,$cdom);
     my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.      # Check for network failure
      ':resourcedata',$chome);      if (!ref($courseopt)) {
     # Check for network failure   if ( $courseopt =~ /no.such.host/i || $courseopt =~ /con_lost/i) {
     if ( $reply =~ /no.such.host/i || $reply =~ /con_lost/i) {      $self->{NETWORK_FAILURE} = 1;
  $self->{NETWORK_FAILURE} = 1;  
     } elsif ($reply!~/^error\:/) {  
  $courserdatas{$cid}=$reply;  
  $courserdatas{$cid.'.last_cache'}=time;  
     }  
  }  
  foreach (split(/\&/,$courserdatas{$cid})) {  
     my ($name,$value)=split(/\=/,$_);  
     $courseopt{$userprefix.&Apache::lonnet::unescape($name)}=  
  &Apache::lonnet::unescape($value);  
  }   }
    undef($courseopt);
       }
   
 # --------------------------------------------------- Get userdata (if present)  # --------------------------------------------------- Get userdata (if present)
  unless ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) {  
     my $reply=&Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome);      my $useropt=&Apache::lonnet::get_userresdata($uname,$udom);
     if ($reply!~/^error\:/) {      # Check for network failure
  $userrdatas{$uname.'___'.$udom}=$reply;      if (!ref($useropt)) {
  $userrdatas{$uname.'___'.$udom.'.last_cache'}=time;   if ( $useropt =~ /no.such.host/i || $useropt =~ /con_lost/i) {
     }      $self->{NETWORK_FAILURE} = 1;
     # check to see if network failed  
     elsif ( $reply=~/no.such.host/i || $reply=~/con.*lost/i )  
     {  
  $self->{NETWORK_FAILURE} = 1;  
     }  
  }  
  foreach (split(/\&/,$userrdatas{$uname.'___'.$udom})) {  
     my ($name,$value)=split(/\=/,$_);  
     $useropt{$userprefix.&Apache::lonnet::unescape($name)}=  
  &Apache::lonnet::unescape($value);  
  }   }
  $self->{COURSE_OPT} = \%courseopt;   undef($useropt);
  $self->{USER_OPT} = \%useropt;  
     }      }
   
       $self->{COURSE_OPT} = $courseopt;
       $self->{USER_OPT} = $useropt;
   
     $self->{COURSE_USER_OPT_GENERATED} = 1;      $self->{COURSE_USER_OPT_GENERATED} = 1;
           
     return;      return;
Line 2133  sub generate_email_discuss_status { Line 2183  sub generate_email_discuss_status {
     my $symb = shift;      my $symb = shift;
     if ($self->{EMAIL_DISCUSS_GENERATED}) { return; }      if ($self->{EMAIL_DISCUSS_GENERATED}) { return; }
   
     my $cid=$ENV{'request.course.id'};      my $cid=$env{'request.course.id'};
     my ($cdom,$cnum)=split(/\_/,$cid);      my $cdom=$env{'course.'.$cid.'.domain'};
       my $cnum=$env{'course.'.$cid.'.num'};
           
     my %emailstatus = &Apache::lonnet::dump('email_status');      my %emailstatus = &Apache::lonnet::dump('email_status');
     my $logoutTime = $emailstatus{'logout'};      my $logoutTime = $emailstatus{'logout'};
     my $courseLeaveTime = $emailstatus{'logout_'.$ENV{'request.course.id'}};      my $courseLeaveTime = $emailstatus{'logout_'.$env{'request.course.id'}};
     $self->{LAST_CHECK} = (($courseLeaveTime > $logoutTime) ?      $self->{LAST_CHECK} = (($courseLeaveTime > $logoutTime) ?
    $courseLeaveTime : $logoutTime);     $courseLeaveTime : $logoutTime);
     my %discussiontime = &Apache::lonnet::dump('discussiontimes',       my %discussiontime = &Apache::lonnet::dump('discussiontimes', 
        $cdom, $cnum);         $cdom, $cnum);
     my %lastread = &Apache::lonnet::dump('nohist_'.$cid.'_discuss',      my %lastread = &Apache::lonnet::dump('nohist_'.$cid.'_discuss',
                                         $ENV{'user.domain'},$ENV{'user.name'},'lastread');                                          $env{'user.domain'},$env{'user.name'},'lastread');
     my %lastreadtime = ();      my %lastreadtime = ();
     foreach (keys %lastread) {      foreach (keys %lastread) {
         my $key = $_;          my $key = $_;
Line 2154  sub generate_email_discuss_status { Line 2205  sub generate_email_discuss_status {
   
     my %feedback=();      my %feedback=();
     my %error=();      my %error=();
     my $keys = &Apache::lonnet::reply('keys:'.      my @keys = &Apache::lonnet::getkeys('nohist_email',$env{'user.domain'},
       $ENV{'user.domain'}.':'.   $env{'user.name'});
       $ENV{'user.name'}.':nohist_email',  
       $ENV{'user.home'});  
           
     foreach my $msgid (split(/\&/, $keys)) {      foreach my $msgid (@keys) {
  $msgid=&Apache::lonnet::unescape($msgid);   if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) {
  my $plain=&Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid));      my $plain=
  if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) {   &Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid));
     my ($what,$url)=($1,$2);      if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) {
     my %status=   my ($what,$url)=($1,$2);
  &Apache::lonnet::get('email_status',[$msgid]);  
     if ($status{$msgid}=~/^error\:/) {   
  $status{$msgid}='';   
     }  
       
     if (($status{$msgid} eq 'new') ||   
  (!$status{$msgid})) {   
  if ($what eq 'Error') {   if ($what eq 'Error') {
     $error{$url}.=','.$msgid;       $error{$url}.=','.$msgid; 
  } else {   } else {
Line 2181  sub generate_email_discuss_status { Line 2223  sub generate_email_discuss_status {
  }   }
     }      }
           
       #url's of resources that have feedbacks
     $self->{FEEDBACK} = \%feedback;      $self->{FEEDBACK} = \%feedback;
     $self->{ERROR_MSG} = \%error; # what is this? JB      #or errors
       $self->{ERROR_MSG} = \%error;
     $self->{DISCUSSION_TIME} = \%discussiontime;      $self->{DISCUSSION_TIME} = \%discussiontime;
     $self->{EMAIL_STATUS} = \%emailstatus;      $self->{EMAIL_STATUS} = \%emailstatus;
     $self->{LAST_READ} = \%lastreadtime;      $self->{LAST_READ} = \%lastreadtime;
Line 2195  sub get_user_data { Line 2239  sub get_user_data {
     if ($self->{RETRIEVED_USER_DATA}) { return; }      if ($self->{RETRIEVED_USER_DATA}) { return; }
   
     # Retrieve performance data on problems      # Retrieve performance data on problems
     my %student_data = Apache::lonnet::currentdump($ENV{'request.course.id'},      my %student_data = Apache::lonnet::currentdump($env{'request.course.id'},
    $ENV{'user.domain'},     $env{'user.domain'},
    $ENV{'user.name'});     $env{'user.name'});
     $self->{STUDENT_DATA} = \%student_data;      $self->{STUDENT_DATA} = \%student_data;
   
     $self->{RETRIEVED_USER_DATA} = 1;      $self->{RETRIEVED_USER_DATA} = 1;
Line 2222  sub navhash { Line 2266  sub navhash {
 # Checks to see if coursemap is defined, matching test in old lonnavmaps  # Checks to see if coursemap is defined, matching test in old lonnavmaps
 sub courseMapDefined {  sub courseMapDefined {
     my $self = shift;      my $self = shift;
     my $uri = &Apache::lonnet::clutter($ENV{'request.course.uri'});      my $uri = &Apache::lonnet::clutter($env{'request.course.uri'});
   
     my $firstres = $self->navhash("map_start_$uri");      my $firstres = $self->navhash("map_start_$uri");
     my $lastres = $self->navhash("map_finish_$uri");      my $lastres = $self->navhash("map_finish_$uri");
Line 2232  sub courseMapDefined { Line 2276  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::iterator->new($self, shift, shift,
                                                      shift, undef, shift);                                                       shift, undef, shift,
        shift, shift);
     return $iterator;      return $iterator;
 }  }
   
 # unties the hash when done  
 sub untieHashes {  
     my $self = shift;  
     untie %{$self->{NAV_HASH}};  
     untie %{$self->{PARM_HASH}};  
 }  
   
 # Private method: Does the given resource (as a symb string) have  # Private method: Does the given resource (as a symb string) have
 # current discussion? Returns 0 if chat/mail data not extracted.  # current discussion? Returns 0 if chat/mail data not extracted.
 sub hasDiscussion {  sub hasDiscussion {
     my $self = shift;      my $self = shift;
     my $symb = shift;      my $symb = shift;
       
     $self->generate_email_discuss_status();      $self->generate_email_discuss_status();
   
     if (!defined($self->{DISCUSSION_TIME})) { return 0; }      if (!defined($self->{DISCUSSION_TIME})) { return 0; }
   
     #return defined($self->{DISCUSSION_TIME}->{$symb});      #return defined($self->{DISCUSSION_TIME}->{$symb});
   
 # backward compatibility (bulletin boards used to be 'wrapped')      # backward compatibility (bulletin boards used to be 'wrapped')
     my $ressymb = $symb;      my $ressymb = $self->wrap_symb($symb);
     if ($ressymb =~ m|adm/(\w+)/(\w+)/(\d+)/bulletinboard$|) {  
         unless ($ressymb =~ m|adm/wrapper/adm|) {  
             $ressymb = 'bulletin___'.$3.'___adm/wrapper/adm/'.$1.'/'.$2.'/'.$3.'/bulletinboard';  
         }  
     }  
   
     if ( defined ( $self->{LAST_READ}->{$ressymb} ) ) {      if ( defined ( $self->{LAST_READ}->{$ressymb} ) ) {
         return $self->{DISCUSSION_TIME}->{$ressymb} > $self->{LAST_READ}->{$ressymb};          return $self->{DISCUSSION_TIME}->{$ressymb} > $self->{LAST_READ}->{$ressymb};
     } else {      } else {
Line 2271  sub hasDiscussion { Line 2302  sub hasDiscussion {
     }      }
 }  }
   
   sub wrap_symb {
       my $self = shift;
       my $symb = shift;
       if ($symb =~ m-___(adm/\w+/\w+/)(\d+)(/bulletinboard)$-) {
           unless ($symb =~ m|adm/wrapper/adm|) {
               $symb = 'bulletin___'.$2.'___adm/wrapper/'.$1.$2.$3;
           }
       }
       return $symb;
   }
   
 # Private method: Does the given resource (as a symb string) have  # Private method: Does the given resource (as a symb string) have
 # current feedback? Returns the string in the feedback hash, which  # current feedback? Returns the string in the feedback hash, which
 # will be false if it does not exist.  # will be false if it does not exist.
   
 sub getFeedback {   sub getFeedback { 
     my $self = shift;      my $self = shift;
     my $symb = shift;      my $symb = shift;
Line 2369  resource in the navmap. Line 2412  resource in the navmap.
 sub firstResource {  sub firstResource {
     my $self = shift;      my $self = shift;
     my $firstResource = $self->navhash('map_start_' .      my $firstResource = $self->navhash('map_start_' .
                      &Apache::lonnet::clutter($ENV{'request.course.uri'}));                       &Apache::lonnet::clutter($env{'request.course.uri'}));
     return $self->getById($firstResource);      return $self->getById($firstResource);
 }  }
   
Line 2385  in the navmap. Line 2428  in the navmap.
 sub finishResource {  sub finishResource {
     my $self = shift;      my $self = shift;
     my $firstResource = $self->navhash('map_finish_' .      my $firstResource = $self->navhash('map_finish_' .
                      &Apache::lonnet::clutter($ENV{'request.course.uri'}));                       &Apache::lonnet::clutter($env{'request.course.uri'}));
     return $self->getById($firstResource);      return $self->getById($firstResource);
 }  }
   
Line 2412  sub parmval_real { Line 2455  sub parmval_real {
     # Make sure the {USER_OPT} and {COURSE_OPT} hashes are populated      # Make sure the {USER_OPT} and {COURSE_OPT} hashes are populated
     $self->generate_course_user_opt();      $self->generate_course_user_opt();
   
     my $cid=$ENV{'request.course.id'};      my $cid=$env{'request.course.id'};
     my $csec=$ENV{'request.course.sec'};      my $csec=$env{'request.course.sec'};
     my $uname=$ENV{'user.name'};      my $uname=$env{'user.name'};
     my $udom=$ENV{'user.domain'};      my $udom=$env{'user.domain'};
   
     unless ($symb) { return ''; }      unless ($symb) { return ''; }
     my $result='';      my $result='';
Line 2429  sub parmval_real { Line 2472  sub parmval_real {
   
     my $symbparm=$symb.'.'.$what;      my $symbparm=$symb.'.'.$what;
     my $mapparm=$mapname.'___(all).'.$what;      my $mapparm=$mapname.'___(all).'.$what;
     my $usercourseprefix=$uname.'_'.$udom.'_'.$cid;      my $usercourseprefix=$cid;
   
     my $seclevel= $usercourseprefix.'.['.$csec.'].'.$what;      my $seclevel= $usercourseprefix.'.['.$csec.'].'.$what;
     my $seclevelr=$usercourseprefix.'.['.$csec.'].'.$symbparm;      my $seclevelr=$usercourseprefix.'.['.$csec.'].'.$symbparm;
Line 2459  sub parmval_real { Line 2502  sub parmval_real {
   
     if (defined($courseopt)) {      if (defined($courseopt)) {
         if (defined($$courseopt{$courselevelr})) { return $$courseopt{$courselevelr}; }          if (defined($$courseopt{$courselevelr})) { return $$courseopt{$courselevelr}; }
         if (defined($$courseopt{$courselevelm})) { return $$courseopt{$courselevelm}; }  
         if (defined($$courseopt{$courselevel})) { return $$courseopt{$courselevel}; }  
     }      }
   
 # ----------------------------------------------------- third, check map parms  # ----------------------------------------------------- third, check map parms
Line 2477  sub parmval_real { Line 2518  sub parmval_real {
     $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat);      $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat);
     if (defined($default)) { return $default}      if (defined($default)) { return $default}
   
 # --------------------------------------------------- fifth , cascade up parts  # --------------------------------------------------- fifth, check more course
       if (defined($courseopt)) {
           if (defined($$courseopt{$courselevelm})) { return $$courseopt{$courselevelm}; }
           if (defined($$courseopt{$courselevel})) { return $$courseopt{$courselevel}; }
       }
   
   # --------------------------------------------------- sixth , cascade up parts
   
     my ($space,@qualifier)=split(/\./,$rwhat);      my ($space,@qualifier)=split(/\./,$rwhat);
     my $qualifier=join('.',@qualifier);      my $qualifier=join('.',@qualifier);
Line 2506  you're not sure if $res is already an ob Line 2553  you're not sure if $res is already an ob
 resource appears multiple times in the course, only the first instance  resource appears multiple times in the course, only the first instance
 will be returned. As a result, this is probably useful only for maps.  will be returned. As a result, this is probably useful only for maps.
   
 =item * B<retrieveResources>(map, filterFunc, recursive, bailout):  =item * B<retrieveResources>(map, filterFunc, recursive, bailout, showall):
   
 The map is a specification of a map to retreive the resources from,  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  either as a url or as an object. The filterFunc is a reference to a
Line 2514  function that takes a resource object as Line 2561  function that takes a resource object as
 true if the resource should be included, or false if it should not  true if the resource should be included, or false if it should not
 be. If recursive is true, the map will be recursively examined,  be. If recursive is true, the map will be recursively examined,
 otherwise it will not be. If bailout is true, the function will return  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,  as soon as it finds a resource, if false it will finish. If showall is
 the map is the top-level map of the course, filterFunc is a function  true it will not hide maps that contain nothing but one other map. By
 that always returns 1, recursive is true, bailout is false. The  default, the map is the top-level map of the course, filterFunc is a
 resources will be returned in a list containing the resource objects  function that always returns 1, recursive is true, bailout is false,
 for the corresponding resources, with B<no structure information> in  showall is false. The resources will be returned in a list containing
 the list; regardless of branching, recursion, etc., it will be a flat  the resource objects for the corresponding resources, with B<no
 list.  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,  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  just a list of all resources. It is also suitable for finding out how
Line 2529  want to know is if I<any> resources matc Line 2577  want to know is if I<any> resources matc
 parameter will allow you to avoid potentially expensive enumeration of  parameter will allow you to avoid potentially expensive enumeration of
 all matching resources.  all matching resources.
   
 =item * B<hasResource>(map, filterFunc, recursive):  =item * B<hasResource>(map, filterFunc, recursive, showall):
   
 Convience method for  Convience method for
   
  scalar(retrieveResources($map, $filterFunc, $recursive, 1)) > 0   scalar(retrieveResources($map, $filterFunc, $recursive, 1, $showall)) > 0
   
 which will tell whether the map has resources matching the description  which will tell whether the map has resources matching the description
 in the filter function.  in the filter function.
Line 2567  sub retrieveResources { Line 2615  sub retrieveResources {
     if (!defined($recursive)) { $recursive = 1; }      if (!defined($recursive)) { $recursive = 1; }
     my $bailout = shift;      my $bailout = shift;
     if (!defined($bailout)) { $bailout = 0; }      if (!defined($bailout)) { $bailout = 0; }
       my $showall = shift;
     # Create the necessary iterator.      # Create the necessary iterator.
     if (!ref($map)) { # assume it's a url of a map.      if (!ref($map)) { # assume it's a url of a map.
         $map = $self->getResourceByUrl($map);          $map = $self->getResourceByUrl($map);
Line 2586  sub retrieveResources { Line 2634  sub retrieveResources {
   
     # Get an iterator.      # Get an iterator.
     my $it = $self->getIterator($map->map_start(), $map->map_finish(),      my $it = $self->getIterator($map->map_start(), $map->map_finish(),
                                 undef, $recursive);                                  undef, $recursive, $showall);
   
     my @resources = ();      my @resources = ();
   
Line 2616  sub hasResource { Line 2664  sub hasResource {
     my $map = shift;      my $map = shift;
     my $filterFunc = shift;      my $filterFunc = shift;
     my $recursive = shift;      my $recursive = shift;
       my $showall = shift;
           
     return scalar($self->retrieveResources($map, $filterFunc, $recursive, 1)) > 0;      return scalar($self->retrieveResources($map, $filterFunc, $recursive, 1, $showall)) > 0;
 }  }
   
 1;  1;
   
 package Apache::lonnavmaps::iterator;  package Apache::lonnavmaps::iterator;
   use WeakRef;
   use Apache::lonnet;
   
 =pod  =pod
   
Line 2763  sub new { Line 2814  sub new {
     my $class = ref($proto) || $proto;      my $class = ref($proto) || $proto;
     my $self = {};      my $self = {};
   
     $self->{NAV_MAP} = shift;      weaken($self->{NAV_MAP} = shift);
     return undef unless ($self->{NAV_MAP});      return undef unless ($self->{NAV_MAP});
   
     # Handle the parameters      # Handle the parameters
Line 2883  sub new { Line 2934  sub new {
             Apache::lonnavmaps::iterator->new($self->{NAV_MAP}, $firstResource,              Apache::lonnavmaps::iterator->new($self->{NAV_MAP}, $firstResource,
                                               $finishResource, $self->{FILTER},                                                $finishResource, $self->{FILTER},
                                               $self->{ALREADY_SEEN},                                                 $self->{ALREADY_SEEN}, 
                                               $self->{CONDITION}, 0);                                                $self->{CONDITION},
         $self->{FORCE_TOP});
                   
     }      }
   
Line 3046  sub next { Line 3098  sub next {
         $self->{RECURSIVE_ITERATOR} =           $self->{RECURSIVE_ITERATOR} = 
             Apache::lonnavmaps::iterator->new($self->{NAV_MAP}, $firstResource,              Apache::lonnavmaps::iterator->new($self->{NAV_MAP}, $firstResource,
                                               $finishResource, $self->{FILTER},                                                $finishResource, $self->{FILTER},
                                               $self->{ALREADY_SEEN}, $self->{CONDITION});                                                $self->{ALREADY_SEEN},
         $self->{CONDITION},
         $self->{FORCE_TOP});
     }      }
   
     # If this is a blank resource, don't actually return it.      # If this is a blank resource, don't actually return it.
Line 3099  sub populateStack { Line 3153  sub populateStack {
 1;  1;
   
 package Apache::lonnavmaps::DFSiterator;  package Apache::lonnavmaps::DFSiterator;
   use WeakRef;
   use Apache::lonnet;
   
 # Not documented in the perldoc: This is a simple iterator that just walks  # Not documented in the perldoc: This is a simple iterator that just walks
 #  through the nav map and presents the resources in a depth-first search  #  through the nav map and presents the resources in a depth-first search
Line 3127  sub new { Line 3183  sub new {
     my $class = ref($proto) || $proto;      my $class = ref($proto) || $proto;
     my $self = {};      my $self = {};
   
     $self->{NAV_MAP} = shift;      weaken($self->{NAV_MAP} = shift);
     return undef unless ($self->{NAV_MAP});      return undef unless ($self->{NAV_MAP});
   
     $self->{FIRST_RESOURCE} = shift || $self->{NAV_MAP}->firstResource();      $self->{FIRST_RESOURCE} = shift || $self->{NAV_MAP}->firstResource();
Line 3281  sub populateStack { Line 3337  sub populateStack {
 1;  1;
   
 package Apache::lonnavmaps::resource;  package Apache::lonnavmaps::resource;
   use WeakRef;
 use Apache::lonnet;  use Apache::lonnet;
   
 =pod  =pod
Line 3363  sub new { Line 3419  sub new {
     my $class = ref($proto) || $proto;      my $class = ref($proto) || $proto;
     my $self = {};      my $self = {};
   
     $self->{NAV_MAP} = shift;      weaken($self->{NAV_MAP} = shift);
     $self->{ID} = shift;      $self->{ID} = shift;
   
     # Store this new resource in the parent nav map's cache.      # Store this new resource in the parent nav map's cache.
Line 3451  Returns the title of the resource. Line 3507  Returns the title of the resource.
 # 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 comesfrom { my $self=shift; return $self->navHash("comesfrom_", 1); }
   sub encrypted { my $self=shift; return $self->navHash("encrypted_", 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 from { my $self=shift; return $self->navHash("from_", 1); }
 # considered private and undocumented  # considered private and undocumented
Line 3462  sub randompick { Line 3519  sub randompick {
     return $self->{NAV_MAP}->{PARM_HASH}->{$self->symb .      return $self->{NAV_MAP}->{PARM_HASH}->{$self->symb .
                                                '.0.parameter_randompick'};                                                 '.0.parameter_randompick'};
 }  }
   sub link {
       my $self=shift;
       if ($self->encrypted()) { return &Apache::lonenc::encrypted($self->src); }
       return $self->src;
   }
 sub src {   sub src { 
     my $self=shift;      my $self=shift;
     return $self->navHash("src_", 1);      return $self->navHash("src_", 1);
 }  }
   sub shown_symb {
       my $self=shift;
       if ($self->encrypted()) {return &Apache::lonenc::encrypted($self->symb());}
       return $self->symb();
   }
   sub id {
       my $self=shift;
       return $self->{ID};
   }
   sub enclosing_map_src {
       my $self=shift;
       (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;
       return $self->navHash('map_id_'.$first);
   }
 sub symb {  sub symb {
     my $self=shift;      my $self=shift;
     (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;      (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;
Line 3474  sub symb { Line 3550  sub symb {
         . '___' . $second . '___' . $symbSrc;          . '___' . $second . '___' . $symbSrc;
     return &Apache::lonnet::symbclean($symb);      return &Apache::lonnet::symbclean($symb);
 }  }
   sub wrap_symb {
       my $self = shift;
       return $self->{NAV_MAP}->wrap_symb($self->symb());
   }
 sub title {   sub title { 
     my $self=shift;       my $self=shift; 
     if ($self->{ID} eq '0.0') {      if ($self->{ID} eq '0.0') {
  # If this is the top-level map, return the title of the course   # If this is the top-level map, return the title of the course
  # since this map can not be titled otherwise.   # since this map can not be titled otherwise.
  return $ENV{'course.'.$ENV{'request.course.id'}.'.description'};   return $env{'course.'.$env{'request.course.id'}.'.description'};
     }      }
     return $self->navHash("title_", 1); }      return $self->navHash("title_", 1); }
 # considered private and undocumented  # considered private and undocumented
 sub to { my $self=shift; return $self->navHash("to_", 1); }  sub to { my $self=shift; return $self->navHash("to_", 1); }
   sub condition {
       my $self=shift;
       my $undercond=$self->navHash("undercond_", 1);
       if (!defined($undercond)) { return 1; };
       my $condid=$self->navHash("condid_$undercond");
       if (!defined($condid)) { return 1; };
       my $condition=&Apache::lonnet::directcondval($condid);
       return $condition;
   }
   
 sub compTitle {  sub compTitle {
     my $self = shift;      my $self = shift;
     my $title = $self->title();      my $title = $self->title();
Line 3549  sub is_page { Line 3639  sub is_page {
 sub is_problem {  sub is_problem {
     my $self=shift;      my $self=shift;
     my $src = $self->src();      my $src = $self->src();
     return ($src =~ /\.(problem|exam|quiz|assess|survey|form|library)$/)      return ($src =~ /\.(problem|exam|quiz|assess|survey|form|library|task)$/)
 }  }
 sub contains_problem {  sub contains_problem {
     my $self=shift;      my $self=shift;
Line 3765  sub duedate { Line 3855  sub duedate {
     }      }
     return $self->parmval("duedate", $part);      return $self->parmval("duedate", $part);
 }  }
   sub handgrade {
       (my $self, my $part) = @_;
       return $self->parmval("handgrade", $part);
   }
 sub maxtries {  sub maxtries {
     (my $self, my $part) = @_;      (my $self, my $part) = @_;
     return $self->parmval("maxtries", $part);      return $self->parmval("maxtries", $part);
Line 3803  sub weight { Line 3897  sub weight {
     my $self = shift; my $part = shift;      my $self = shift; my $part = shift;
     if (!defined($part)) { $part = '0'; }      if (!defined($part)) { $part = '0'; }
     return &Apache::lonnet::EXT('resource.'.$part.'.weight',      return &Apache::lonnet::EXT('resource.'.$part.'.weight',
  $self->symb(), $ENV{'user.domain'},   $self->symb(), $env{'user.domain'},
  $ENV{'user.name'},    $env{'user.name'}, 
  $ENV{'request.course.sec'});   $env{'request.course.sec'});
 }  }
 sub part_display {  sub part_display {
     my $self= shift(); my $partID = shift();      my $self= shift(); my $partID = shift();
Line 3964  sub countParts { Line 4058  sub countParts {
 sub countResponses {  sub countResponses {
     my $self = shift;      my $self = shift;
     my $count;      my $count;
     foreach my $part ($self->parts()) {      foreach my $part (@{$self->parts()}) {
         $count+= $self->responseIds($part);          $count+= scalar($self->responseIds($part));
     }      }
     return $count;      return $count;
 }  }
   
 sub responseTypes {  sub responseTypes {
     my $self = shift;      my $self = shift;
     my %Responses;      my %responses;
     foreach my $part ($self->parts()) {      foreach my $part ($self->parts()) {
         foreach my $responsetype ($self->responseType($part)) {          foreach my $responsetype ($self->responseType($part)) {
             $Responses{$responsetype}++ if (defined($responsetype));              $responses{$responsetype}++ if (defined($responsetype));
         }          }
     }      }
     return %Responses;      return %responses;
 }  }
   
 sub multipart {  sub multipart {
Line 4049  sub extractParts { Line 4143  sub extractParts {
  return;   return;
     }      }
     foreach (split(/\,/,$metadata)) {      foreach (split(/\,/,$metadata)) {
  if ($_ =~ /^part_(.*)$/) {   if ($_ =~ /^(?:part|Task)_(.*)$/) {
       &Apache::lonnet::logthis($self->src()." part -> ".$1);
     my $part = $1;      my $part = $1;
     # This floods the logs if it blows up      # This floods the logs if it blows up
     if (defined($parts{$part})) {      if (defined($parts{$part})) {
Line 4314  sub ATTEMPTED             { return 16; } Line 4409  sub ATTEMPTED             { return 16; }
   
 sub getCompletionStatus {  sub getCompletionStatus {
     my $self = shift;      my $self = shift;
       my $part = shift;
     return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE});      return $self->NETWORK_FAILURE if ($self->{NAV_MAP}->{NETWORK_FAILURE});
   
     my $status = $self->queryRestoreHash('solved', shift);      my $status = $self->queryRestoreHash('solved', $part);
   
     # Left as separate if statements in case we ever do more with this      # Left as separate 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;}
     if ($status eq 'correct_by_scantron') {return $self->CORRECT;}      if ($status eq 'correct_by_scantron') {return $self->CORRECT;}
     if ($status eq 'correct_by_override') {return $self->CORRECT_BY_OVERRIDE; }      if ($status eq 'correct_by_override') {
    return $self->CORRECT_BY_OVERRIDE;
       }
     if ($status eq 'incorrect_attempted') {return $self->INCORRECT; }      if ($status eq 'incorrect_attempted') {return $self->INCORRECT; }
     if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; }      if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; }
     if ($status eq 'excused') {return $self->EXCUSED; }      if ($status eq 'excused') {return $self->EXCUSED; }
Line 4425  An answer has been submitted, but the st Line 4523  An answer has been submitted, but the st
   
 sub TRIES_LEFT       { return 20; }  sub TRIES_LEFT       { return 20; }
 sub ANSWER_SUBMITTED { return 21; }  sub ANSWER_SUBMITTED { return 21; }
   sub PARTIALLY_CORRECT{ return 22; }
   
 sub status {  sub status {
     my $self = shift;      my $self = shift;
Line 4443  sub status { Line 4542  sub status {
     my $suppressFeedback = $self->problemstatus($part) eq 'no';      my $suppressFeedback = $self->problemstatus($part) eq 'no';
     # If there's an answer date and we're past it, don't      # If there's an answer date and we're past it, don't
     # suppress the feedback; student should know      # suppress the feedback; student should know
     if ($self->answerdate($part) && $self->answerdate($part) < time()) {      if ($self->duedate($part) && $self->duedate($part) < time() &&
    $self->answerdate($part) && $self->answerdate($part) < time()) {
  $suppressFeedback = 0;   $suppressFeedback = 0;
     }      }
   
     # There are a few whole rows we can dispose of:      # There are a few whole rows we can dispose of:
     if ($completionStatus == CORRECT ||      if ($completionStatus == CORRECT ||
         $completionStatus == CORRECT_BY_OVERRIDE ) {          $completionStatus == CORRECT_BY_OVERRIDE ) {
         return $suppressFeedback? ANSWER_SUBMITTED : CORRECT;    if ( $suppressFeedback ) { return ANSWER_SUBMITTED }
    my $awarded=$self->awarded($part);
    if ($awarded < 1 && $awarded > 0) {
               return PARTIALLY_CORRECT;
    } elsif ($awarded<1) {
       return INCORRECT;
    }
    return CORRECT; 
     }      }
   
     if ($completionStatus == ATTEMPTED) {      if ($completionStatus == ATTEMPTED) {
Line 4537  my %compositeToSimple = Line 4644  my %compositeToSimple =
       NETWORK_FAILURE()       => ERROR,        NETWORK_FAILURE()       => ERROR,
       NOTHING_SET()           => CLOSED,        NOTHING_SET()           => CLOSED,
       CORRECT()               => CORRECT,        CORRECT()               => CORRECT,
         PARTIALLY_CORRECT()     => PARTIALLY_CORRECT,
       EXCUSED()               => CORRECT,        EXCUSED()               => CORRECT,
       PAST_DUE_NO_ANSWER()    => INCORRECT,        PAST_DUE_NO_ANSWER()    => INCORRECT,
       PAST_DUE_ANSWER_LATER() => INCORRECT,        PAST_DUE_ANSWER_LATER() => INCORRECT,
Line 4657  sub getNext { Line 4765  sub getNext {
     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);
           if (!$choice->condition()) { next; }
         my $next = $choice->goesto();          my $next = $choice->goesto();
         $next = $self->{NAV_MAP}->getById($next);          $next = $self->{NAV_MAP}->getById($next);
   
Line 4685  sub browsePriv { Line 4794  sub browsePriv {
         return $self->{BROWSE_PRIV};          return $self->{BROWSE_PRIV};
     }      }
   
     $self->{BROWSE_PRIV} = &Apache::lonnet::allowed('bre', $self->src());      $self->{BROWSE_PRIV} = &Apache::lonnet::allowed('bre',$self->src(),
       $self->symb());
 }  }
   
 =pod  =pod

Removed from v.1.290  
changed lines
  Added in v.1.337


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