Diff for /loncom/interface/lonnavmaps.pm between versions 1.361 and 1.391

version 1.361, 2006/02/16 19:38:27 version 1.391, 2006/10/10 20:16:26
Line 31  package Apache::lonnavmaps; Line 31  package Apache::lonnavmaps;
   
 use strict;  use strict;
 use GDBM_File;  use GDBM_File;
 use Apache::Constants qw(:common :http);  
 use Apache::loncommon();  use Apache::loncommon();
 use Apache::lonmenu();  
 use Apache::lonenc();  use Apache::lonenc();
 use Apache::lonlocal;  use Apache::lonlocal;
 use Apache::lonnet;  use Apache::lonnet;
 use POSIX qw (floor strftime);  use POSIX qw (floor strftime);
 use Data::Dumper; # for debugging, not always   use Data::Dumper; # for debugging, not always 
 use Time::HiRes qw( gettimeofday tv_interval );  use Time::HiRes qw( gettimeofday tv_interval );
   use lib '/home/httpd/lib/perl/';
   use LONCAPA;
   
 # symbolic constants  # symbolic constants
 sub SYMB { return 1; }  sub SYMB { return 1; }
Line 89  my %colormap = Line 89  my %colormap =
 # 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 {  
     my ($mode,$script,$toplinkitems,$firsttime)=@_;  
     my $result;  
     if ($script ne 'no') {  
  $result.='<script type="text/javascript">';  
     }  
     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\');  
                }';  
     }  
     if ($mode eq 'now') {  
  $result.="\nlaunch_navmapwin();\n";  
     }  
     if ($script ne 'no') {  
  $result.='</script>';  
     }  
     if ($mode eq 'link') {  
  &add_linkitem($toplinkitems,'launchnav','launch_navmapwin()',  
       "Launch navigation window");  
     }  
     return $result;  
 }  
   
 sub close {  sub close {
     if ($env{'environment.remotenavmap'} ne 'on') { return ''; }      if ($env{'environment.remotenavmap'} ne 'on') { return ''; }
     return(<<ENDCLOSE);      return(<<ENDCLOSE);
Line 145  this.document.navform.submit(); Line 117  this.document.navform.submit();
 ENDUPDATE  ENDUPDATE
 }  }
   
 sub handler {  
     my $r = shift;  
     real_handler($r);  
 }  
   
 sub real_handler {  
     my $r = shift;  
     #my $t0=[&gettimeofday()];  
     # Handle header-only request  
     if ($r->header_only) {  
         if ($env{'browser.mathml'}) {  
             &Apache::loncommon::content_type($r,'text/xml');  
         } else {  
             &Apache::loncommon::content_type($r,'text/html');  
         }  
         $r->send_http_header;  
         return OK;  
     }  
   
     # Send header, don't cache this page  
     if ($env{'browser.mathml'}) {  
         &Apache::loncommon::content_type($r,'text/xml');  
     } else {  
         &Apache::loncommon::content_type($r,'text/html');  
     }  
     &Apache::loncommon::no_cache($r);  
   
     my %toplinkitems=();  
     &add_linkitem(\%toplinkitems,'blank','',"Select Action");  
     if ($ENV{QUERY_STRING} eq 'collapseExternal') {  
  &Apache::lonnet::put('environment',{'remotenavmap' => 'off'});  
  &Apache::lonnet::appenv('environment.remotenavmap' => 'off');  
  my $menu=&Apache::lonmenu::reopenmenu();  
  my $navstatus=&Apache::lonmenu::get_nav_status();  
  if ($menu) {  
     $menu=(<<MENU)  
              swmenu=$menu  
              swmenu.clearTimeout(swmenu.menucltim);  
      $navstatus  
 MENU  
         } else {  
     my $nothing = &Apache::lonhtmlcommon::javascript_nothing();  
     my $mainwindow='window.open('.$nothing.',"loncapaclient","",false);';  
     $menu=(<<MENU)  
              swmenu=$mainwindow  
      $navstatus  
 MENU  
  }  
         $r->send_http_header;  
  my $html=&Apache::lonxml::xmlbegin();  
  $r->print(<<"ENDSUBM");  
  $html  
         <head>  
   <script type="text/javascript">  
      function submitthis() {  
     $menu  
     self.close();  
     }  
   
    </script>  
         </head>  
  <body bgcolor="#FFFFFF" onLoad="submitthis()"></body>  
         </html>  
 ENDSUBM  
         return OK;  
     }  
     if ($ENV{QUERY_STRING} =~ /^launchExternal/) {  
  &Apache::lonnet::put('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  
     my $navmap = Apache::lonnavmaps::navmap->new();  
   
     if (!defined($navmap)) {  
         my $requrl = $r->uri;  
         $env{'user.error.msg'} = "$requrl:bre:0:0:Course not initialized";  
         return HTTP_NOT_ACCEPTABLE;  
     }  
     $r->send_http_header;  
     my $html=&Apache::lonxml::xmlbegin();  
     $r->print("$html<head>\n");  
     $r->print("<title>".&mt('Navigate Course Contents')."</title>");  
 # ------------------------------------------------------------ Get query string  
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register','sort','showOnlyHomework','postsymb']);  
       
 # ----------------------------------------------------- Force menu registration  
     my $addentries='';  
     my $more_unload;  
     my $body_only='';  
     if ($env{'environment.remotenavmap'} eq 'on') {  
  $r->print('<script type="text/javascript">  
                       function collapse() {  
                          this.document.location="/adm/navmaps?collapseExternal";  
                       }  
                    </script>');  
 # FIXME need to be smarter to only catch window close events  
 # $more_unload="collapse()"  
  $body_only=1;  
     }  
     if ($env{'form.register'}) {  
  $addentries=' onLoad="'.&Apache::lonmenu::loadevents().  
     '" onUnload="'.&Apache::lonmenu::unloadevents().';'.  
     $more_unload.'"';  
  $r->print(&Apache::lonmenu::registerurl(1));  
     } else {  
  $addentries=' onUnload="'.$more_unload.'"';  
     }  
   
     # Header  
     $r->print('</head>'.  
               &Apache::loncommon::bodytag('Navigate Course Contents','',  
   $addentries,$body_only,'',  
   $env{'form.register'}));  
     $r->print('<script>window.focus();</script>');  
        
     $r->rflush();  
   
     # Check that it's defined  
     if (!($navmap->courseMapDefined())) {  
  $r->print(&Apache::loncommon::help_open_menu('','Navigation Screen','Navigation_Screen','',undef,'RAT'));  
         $r->print('<font size="+2" color="red">Coursemap undefined.</font>' .  
                   '</body></html>');  
         return OK;  
     }  
   
     # See if there's only one map in the top-level, if we don't  
     # already have a filter... if so, automatically display it  
     # (older code; should use retrieveResources)  
     if ($ENV{QUERY_STRING} !~ /filter/) {  
         my $iterator = $navmap->getIterator(undef, undef, undef, 0);  
         my $curRes;  
         my $sequenceCount = 0;  
         my $sequenceId;  
         while ($curRes = $iterator->next()) {  
             if (ref($curRes) && $curRes->is_sequence()) {  
                 $sequenceCount++;  
                 $sequenceId = $curRes->map_pc();  
             }  
         }  
           
         if ($sequenceCount == 1) {  
             # The automatic iterator creation in the render call   
             # will pick this up. We know the condition because  
             # the defined($env{'form.filter'}) also ensures this  
             # is a fresh call.  
             $env{'form.filter'} = "$sequenceId";  
         }  
     }  
   
     if ($ENV{QUERY_STRING} eq 'launchExternal') {  
  $r->print('  
           <form name="returnwin" action="/adm/flip?postdata=navlaunch%3a"   
                 method="post" target="loncapaclient">  
           </form>');  
  $r->print('  
           <script type="text/javascript">  
               this.document.returnwin.submit();  
           </script>');  
     }  
   
     if ($env{'environment.remotenavmap'} ne 'on') {  
  $r->print(&launch_win('link','yes',\%toplinkitems));  
     }   
     if ($env{'environment.remotenavmap'} eq 'on') {  
  &add_linkitem(\%toplinkitems,'closenav','collapse()',  
       "Close navigation window");  
     }   
   
     my $jumpToFirstHomework = 0;  
     # Check to see if the student is jumping to next open, do-able problem  
     if ($ENV{QUERY_STRING} =~ /^jumpToFirstHomework/) {  
         $jumpToFirstHomework = 1;  
         # Find the next homework problem that they can do.  
         my $iterator = $navmap->getIterator(undef, undef, undef, 1);  
         my $curRes;  
         my $foundDoableProblem = 0;  
         my $problemRes;  
           
         while (($curRes = $iterator->next()) && !$foundDoableProblem) {  
             if (ref($curRes) && $curRes->is_problem()) {  
                 my $status = $curRes->status();  
                 if ($curRes->completable()) {  
                     $problemRes = $curRes;  
                     $foundDoableProblem = 1;  
   
                     # Pop open all previous maps  
                     my $stack = $iterator->getStack();  
                     pop @$stack; # last resource in the stack is the problem  
                                  # itself, which we don't need in the map stack  
                     my @mapPcs = map {$_->map_pc()} @$stack;  
                     $env{'form.filter'} = join(',', @mapPcs);  
   
                     # Mark as both "here" and "jump"  
                     $env{'form.postsymb'} = $curRes->symb();  
                 }  
             }  
         }  
   
         # If we found no problems, print a note to that effect.  
         if (!$foundDoableProblem) {  
             $r->print("<font size='+2'>All homework assignments have been completed.</font><br /><br />");  
         }  
     } else {  
  &add_linkitem(\%toplinkitems,'firsthomework',  
       'location.href="navmaps?jumpToFirstHomework"',  
       "Show Me My First Homework Problem");  
     }  
   
     my $suppressEmptySequences = 0;  
     my $filterFunc = undef;  
     my $resource_no_folder_link = 0;  
   
     # Display only due homework.  
     my $showOnlyHomework = 0;  
     if ($env{'form.showOnlyHomework'} eq "1") {  
         $showOnlyHomework = 1;  
         $suppressEmptySequences = 1;  
         $filterFunc = sub { my $res = shift;   
                             return $res->completable() || $res->is_map();  
                         };  
  &add_linkitem(\%toplinkitems,'everything',  
      'location.href="navmaps?sort='.$env{'form.sort'}.'"',  
       "Show Everything");  
         $r->print("<p><font size='+2'>".&mt("Uncompleted Homework")."</font></p>");  
         $env{'form.filter'} = '';  
         $env{'form.condition'} = 1;  
  $resource_no_folder_link = 1;  
     } else {  
  &add_linkitem(\%toplinkitems,'uncompleted',  
       'location.href="navmaps?sort='.$env{'form.sort'}.  
           '&showOnlyHomework=1"',  
       "Show Only Uncompleted Homework");  
     }  
   
     my %selected=($env{'form.sort'} => 'selected=on');  
     my $sort_html=("<form>  
                  <nobr>  
                     <input type=\"hidden\" name=\"showOnlyHomework\" value=\"".$env{'form.showOnlyHomework'}."\" />  
                     <input type=\"submit\" value=\"".&mt('Sort by:')."\" />  
                     <select name=\"sort\">  
                        <option value=\"default\" $selected{'default'}>".&mt('Default')."</option>  
                        <option value=\"title\"   $selected{'title'}  >".&mt('Title')."</option>  
                        <option value=\"duedate\" $selected{'duedate'}>".&mt('Duedate')."</option>  
                        <option value=\"discussion\" $selected{'discussion'}>".&mt('Has New Discussion')."</option>  
                     </select>  
                  </nobr>  
                </form>");  
     # renderer call  
     my $renderArgs = { 'cols' => [0,1,2,3],  
        'sort' => $env{'form.sort'},  
                        'url' => '/adm/navmaps',  
                        'navmap' => $navmap,  
                        'suppressNavmap' => 1,  
                        'suppressEmptySequences' => $suppressEmptySequences,  
                        'filterFunc' => $filterFunc,  
        'resource_no_folder_link' => $resource_no_folder_link,  
        'sort_html'=> $sort_html,  
                        'r' => $r,  
                        'caller' => 'navmapsdisplay',  
                        'linkitems' => \%toplinkitems};  
     my $render = render($renderArgs);  
   
     # If no resources were printed, print a reassuring message so the  
     # user knows there was no error.  
     if ($renderArgs->{'counter'} == 0) {  
         if ($showOnlyHomework) {  
             $r->print("<p><font size='+1'>".&mt("All homework is currently completed").".</font></p>");  
         } else { # both jumpToFirstHomework and normal use the same: course must be empty  
             $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->rflush();  
   
     return OK;  
 }  
   
 # Convenience functions: Returns a string that adds or subtracts  # Convenience functions: Returns a string that adds or subtracts
 # the second argument from the first hash, appropriate for the   # the second argument from the first hash, appropriate for the 
 # query string that determines which folders to recurse on  # query string that determines which folders to recurse on
Line 473  sub getLinkForResource { Line 150  sub getLinkForResource {
     my $anchor;      my $anchor;
     if ($res->is_page()) {      if ($res->is_page()) {
  foreach (@$stack) { if (defined($_)) { $anchor = $_; }  }   foreach (@$stack) { if (defined($_)) { $anchor = $_; }  }
  $anchor=&Apache::lonnet::escape($anchor->shown_symb());   $anchor=&escape($anchor->shown_symb());
  return ($res->link(),$res->shown_symb(),$anchor);   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$/) {
  my $url=&Apache::lonnet::clutter($map);   my $url=&Apache::lonnet::clutter($map);
  $anchor=&Apache::lonnet::escape($src->shown_symb());   $anchor=&escape($src->shown_symb());
  return ($url,$res->shown_symb(),$anchor);   return ($url,$res->shown_symb(),$anchor);
     }      }
         }          }
Line 683  sub timeToHumanString { Line 360  sub timeToHumanString {
   
  if($format ne '') {   if($format ne '') {
     my $timeStr = strftime($format, localtime($time));      my $timeStr = strftime($format, localtime($time));
     return $timeStr.&Apache::lonlocal::gettimezone();      return $timeStr.&Apache::lonlocal::gettimezone($time);
  }   }
   
         # Less then 5 days away, display day of the week and          # Less then 5 days away, display day of the week and
Line 694  sub timeToHumanString { Line 371  sub timeToHumanString {
             $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 " : "this ") .              return ($inPast ? "last " : "this ") .
                 $timeStr.&Apache::lonlocal::gettimezone();                  $timeStr.&Apache::lonlocal::gettimezone($time);
         }          }
                   
  my $conjunction='on';   my $conjunction='on';
Line 709  sub timeToHumanString { Line 386  sub timeToHumanString {
             my $timeStr = strftime("$conjunction %A, %b %e at %I:%M %P", localtime($time));              my $timeStr = strftime("$conjunction %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 $timeStr.&Apache::lonlocal::gettimezone();              return $timeStr.&Apache::lonlocal::gettimezone($time);
         }          }
   
         # Not this year, so show the year          # Not this year, so show the year
         my $timeStr = strftime("$conjunction %A, %b %e %Y at %I:%M %P", localtime($time));          my $timeStr = strftime("$conjunction %A, %b %e %Y 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 $timeStr.&Apache::lonlocal::gettimezone();          return $timeStr.&Apache::lonlocal::gettimezone($time);
     }      }
 }  }
   
Line 1095  sub render_resource { Line 772  sub render_resource {
           
     if ($resource->is_problem()) {      if ($resource->is_problem()) {
         if ($part eq '0' || $params->{'condensed'}) {          if ($part eq '0' || $params->{'condensed'}) {
             $icon ='<img src="'.$location.'/problem.gif" alt="&nbsp;&nbsp;" border="0" />';      $icon = '<img src="'.$location.'/';
       if ($resource->is_task()) {
    $icon .= 'task.gif" alt="'.&mt('Task');
       } else {
    $icon .= 'problem.gif" alt="'.&mt('Problem');
       }
       $icon .='" border="0" />';
         } else {          } else {
             $icon = $params->{'indentString'};              $icon = $params->{'indentString'};
         }          }
Line 1112  sub render_resource { Line 795  sub render_resource {
         }          }
   
  my $folderType = $resource->is_sequence() ? 'folder' : 'page';   my $folderType = $resource->is_sequence() ? 'folder' : 'page';
           my $title=$resource->title;
           $title=~s/\"/\&quot;/g;
         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='$location/$icon' alt='".      $icon = "<img src='$location/$icon' alt=\"".
  ($nowOpen ? 'Open Folder' : 'Close Folder')."' border='0' />";   ($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" border='0' />";
   
             $linkopen = "<a href=\"" . $params->{'url'} . '?' .               $linkopen = "<a href=\"" . $params->{'url'} . '?' . 
                 $params->{'queryString'} . '&filter=';                  $params->{'queryString'} . '&filter=';
Line 1125  sub render_resource { Line 809  sub render_resource {
                 removeFromFilter($filter, $mapId);                  removeFromFilter($filter, $mapId);
             $linkopen .= "&condition=" . $it->{CONDITION} . '&hereType='              $linkopen .= "&condition=" . $it->{CONDITION} . '&hereType='
                 . $params->{'hereType'} . '&here=' .                  . $params->{'hereType'} . '&here=' .
                 &Apache::lonnet::escape($params->{'here'}) .                   &escape($params->{'here'}) . 
                 '&jump=' .                  '&jump=' .
                 &Apache::lonnet::escape($resource->symb()) .                   &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='$location/$icon' alt='".              $icon = "<img src='$location/$icon' alt=\"".
  ($nowOpen ? 'Open Folder' : 'Close Folder')."' border='0' />";   ($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" border='0' />";
   
             $linkopen = "";              $linkopen = "";
             $linkclose = "";              $linkclose = "";
Line 1143  sub render_resource { Line 827  sub render_resource {
     }      }
   
     if ($resource->randomout()) {      if ($resource->randomout()) {
         $nonLinkedText .= ' <i>(hidden)</i> ';          $nonLinkedText .= ' <i>('.&mt('hidden').')</i> ';
     }      }
     if (!$resource->condval()) {      if (!$resource->condval()) {
         $nonLinkedText .= ' <i>(conditionally hidden)</i> ';          $nonLinkedText .= ' <i>('.&mt('conditionally hidden').')</i> ';
     }      }
           
     # We're done preparing and finally ready to start the rendering      # We're done preparing and finally ready to start the rendering
Line 1178  sub render_resource { Line 862  sub render_resource {
     if ($resource->is_problem() && $part ne '0' &&       if ($resource->is_problem() && $part ne '0' && 
         !$params->{'condensed'}) {          !$params->{'condensed'}) {
  my $displaypart=$resource->part_display($part);   my $displaypart=$resource->part_display($part);
         $partLabel = " (Part: $displaypart)";          $partLabel = " (".&mt('Part: [_1]', $displaypart).")";
  if ($link!~/\#/) { $link.='#'.&Apache::lonnet::escape($part); }   if ($link!~/\#/) { $link.='#'.&escape($part); }
         $title = "";          $title = "";
     }      }
   
     if ($params->{'condensed'} && $resource->countParts() > 1) {      if ($params->{'condensed'} && $resource->countParts() > 1) {
         $nonLinkedText .= ' (' . $resource->countParts() . ' parts)';          $nonLinkedText .= ' ('.&mt('[_1] parts', $resource->countParts()).')';
     }      }
   
     my $target;      my $target;
Line 1223  sub render_communication_status { Line 907  sub render_communication_status {
         foreach (split(/\,/, $feedback)) {          foreach (split(/\,/, $feedback)) {
             if ($_) {              if ($_) {
                 $feedbackHTML .= '&nbsp;<a '.$target.' href="/adm/email?display='                  $feedbackHTML .= '&nbsp;<a '.$target.' href="/adm/email?display='
                     . &Apache::lonnet::escape($_) . '">'                      . &escape($_) . '">'
                     . '<img src="'.$location.'/feedback.gif" '                      . '<img src="'.$location.'/feedback.gif" '
                     . 'border="0" /></a>';                      . 'border="0" /></a>';
             }              }
Line 1238  sub render_communication_status { Line 922  sub render_communication_status {
             if ($_) {              if ($_) {
                 $errorcount++;                  $errorcount++;
                 $errorHTML .= '&nbsp;<a '.$target.' href="/adm/email?display='                  $errorHTML .= '&nbsp;<a '.$target.' href="/adm/email?display='
                     . &Apache::lonnet::escape($_) . '">'                      . &escape($_) . '">'
                     . '<img src="'.$location.'/bomb.gif" '                      . '<img src="'.$location.'/bomb.gif" '
                     . 'border="0" /></a>';                      . 'border="0" /></a>';
             }              }
Line 1472  sub render { Line 1156  sub render {
             $navmap = Apache::lonnavmaps::navmap->new();              $navmap = Apache::lonnavmaps::navmap->new();
     if (!defined($navmap)) {      if (!defined($navmap)) {
  # no londer in course   # no londer in course
  return '<font color="red">No course selected</font><br />   return '<span class="LC_error">'.&mt('No course selected').'</span><br />
                         <a href="/adm/roles">Select a course</a><br />';                          <a href="/adm/roles">'.&mt('Select a course').'</a><br />';
     }      }
  }   }
   
Line 1611  sub render { Line 1295  sub render {
  my ($link,$text);   my ($link,$text);
         if ($condition) {          if ($condition) {
     $link='"navmaps?condition=0&filter=&'.$queryString.      $link='"navmaps?condition=0&filter=&'.$queryString.
  '&here='.&Apache::lonnet::escape($here).'"';   '&here='.&escape($here).'"';
     $text='Close All Folders';      $text='Close all folders';
         } else {          } else {
     $link='"navmaps?condition=1&filter=&'.$queryString.      $link='"navmaps?condition=1&filter=&'.$queryString.
  '&here='.&Apache::lonnet::escape($here).'"';   '&here='.&escape($here).'"';
     $text='Open All Folders';      $text='Open all folders';
         }          }
  if ($args->{'caller'} eq 'navmapsdisplay') {   if ($args->{'caller'} eq 'navmapsdisplay') {
     &add_linkitem($args->{'linkitems'},'changefolder',      &add_linkitem($args->{'linkitems'},'changefolder',
Line 1661  END Line 1345  END
   
     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 {
Line 1926  END Line 1610  END
     my $srcHasQuestion = $src =~ /\?/;      my $srcHasQuestion = $src =~ /\?/;
     $args->{"resourceLink"} = $src.      $args->{"resourceLink"} = $src.
  ($srcHasQuestion?'&':'?') .   ($srcHasQuestion?'&':'?') .
  'symb=' . &Apache::lonnet::escape($symb).$anchor;   'symb=' . &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.
Line 2231  sub generate_email_discuss_status { Line 1915  sub generate_email_discuss_status {
     foreach my $msgid (@keys) {      foreach my $msgid (@keys) {
  if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) {   if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) {
     my $plain=      my $plain=
  &Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid));   &LONCAPA::unescape(&LONCAPA::unescape($msgid));
     if ($plain=~/ \[([^\]]+)\]\:/) {      if ($plain=~/ \[([^\]]+)\]\:/) {
  my $url=$1;   my $url=$1;
  if ($plain=~/\:Error \[/) {   if ($plain=~/\:Error \[/) {
Line 2270  sub get_user_data { Line 1954  sub get_user_data {
 sub get_discussion_data {  sub get_discussion_data {
     my $self = shift;      my $self = shift;
     if ($self->{RETRIEVED_DISCUSSION_DATA}) {      if ($self->{RETRIEVED_DISCUSSION_DATA}) {
          return  $self->{DISCUSSION_DATA};   return $self->{DISCUSSION_DATA};
     }      }
                                                                                   
       $self->generate_email_discuss_status();    
   
     my $cid=$env{'request.course.id'};      my $cid=$env{'request.course.id'};
     my $cdom=$env{'course.'.$cid.'.domain'};      my $cdom=$env{'course.'.$cid.'.domain'};
     my $cnum=$env{'course.'.$cid.'.num'};      my $cnum=$env{'course.'.$cid.'.num'};
                                                                                   
     # Retrieve discussion data for resources in course      # Retrieve discussion data for resources in course
     my %discussion_data = &Apache::lonnet::dump($cid,$cdom,$cnum);      my %discussion_data = &Apache::lonnet::dumpstore($cid,$cdom,$cnum);
                                                                                   
   
     $self->{DISCUSSION_DATA} = \%discussion_data;      $self->{DISCUSSION_DATA} = \%discussion_data;
     $self->{RETRIEVED_DISCUSSION_DATA} = 1;      $self->{RETRIEVED_DISCUSSION_DATA} = 1;
     return $self->{DISCUSSION_DATA};      return $self->{DISCUSSION_DATA};
Line 2341  sub hasDiscussion { Line 2027  sub hasDiscussion {
     }      }
 }  }
   
   sub last_post_time {
       my $self = shift;
       my $symb = shift;
       my $ressymb = $self->wrap_symb($symb);
       return $self->{DISCUSSION_TIME}->{$ressymb};
   }
   
   sub unread_discussion {
       my $self = shift;
       my $symb = shift;
   
       $self->get_discussion_data();
   
       my $ressymb = $self->wrap_symb($symb);
       # keys used to store bulletinboard postings use 'unwrapped' symb. 
       my $discsymb = $self->unwrap_symb($ressymb);
       my $version = $self->{DISCUSSION_DATA}{'version:'.$discsymb};
       if (!$version) { return; }
   
       my $prevread = $self->{LAST_READ}{$ressymb};
   
       my $unreadcount = 0;
       my $hiddenflag = 0;
       my $deletedflag = 0;
       my ($hidden,$deleted);
   
       my %subjects;
   
       for (my $id=$version; $id>0; $id--) {
    my $vkeys=$self->{DISCUSSION_DATA}{$id.':keys:'.$discsymb};
    my @keys=split(/:/,$vkeys);
    if (grep(/^hidden$/ ,@keys)) {
       if (!$hiddenflag) {
    $hidden = $self->{DISCUSSION_DATA}{$id.':'.$discsymb.':hidden'};
    $hiddenflag = 1;
       }
    } elsif (grep(/^deleted$/,@keys)) {
       if (!$deletedflag) {
    $deleted = $self->{DISCUSSION_DATA}{$id.':'.$discsymb.':deleted'};
    $deletedflag = 1;
       }
    } else {
       if (($hidden !~/\.$id\./) && ($deleted !~/\.$id\./)
    && $prevread < $self->{DISCUSSION_DATA}{$id.':'.$discsymb.':timestamp'}) {
       $unreadcount++;
       $subjects{$unreadcount}=
    $id.': '.$self->{DISCUSSION_DATA}{$id.':'.$discsymb.':subject'};
    }
    }
       }
       if (wantarray) {
    return ($unreadcount,\%subjects);
       }
       return $unreadcount
   }
   
 sub wrap_symb {  sub wrap_symb {
     my $self = shift;      my $self = shift;
     my $symb = shift;      my $symb = shift;
     if ($symb =~ m-___(adm/\w+/\w+/)(\d+)(/bulletinboard)$-) {      if ($symb =~ m-___(adm/[^/]+/[^/]+/)(\d+)(/bulletinboard)$-) {
         unless ($symb =~ m|adm/wrapper/adm|) {          unless ($symb =~ m|adm/wrapper/adm|) {
             $symb = 'bulletin___'.$2.'___adm/wrapper/'.$1.$2.$3;              $symb = 'bulletin___'.$2.'___adm/wrapper/'.$1.$2.$3;
         }          }
Line 2352  sub wrap_symb { Line 2094  sub wrap_symb {
     return $symb;      return $symb;
 }  }
   
   sub unwrap_symb {
       my $self = shift;
       my $ressymb = shift;
       my $discsymb = $ressymb;
       if ($ressymb =~ m-^(bulletin___\d+___)adm/wrapper/(adm/[^/]+/[^/]+/\d+/bulletinboard)$-) {
            $discsymb = $1.$2;
       }
       return $discsymb;
   }
   
 # 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.
Line 2509  sub parmval_real { Line 2261  sub parmval_real {
     my $result='';      my $result='';
   
     my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);      my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
       $mapname = &Apache::lonnet::deversion($mapname);
 # ----------------------------------------------------- Cascading lookup scheme  # ----------------------------------------------------- Cascading lookup scheme
     my $rwhat=$what;      my $rwhat=$what;
     $what=~s/^parameter\_//;      $what=~s/^parameter\_//;
Line 2755  sub usedVersion { Line 2507  sub usedVersion {
 1;  1;
   
 package Apache::lonnavmaps::iterator;  package Apache::lonnavmaps::iterator;
 use WeakRef;  use Scalar::Util qw(weaken);
 use Apache::lonnet;  use Apache::lonnet;
   
 =pod  =pod
Line 3236  sub populateStack { Line 2988  sub populateStack {
 1;  1;
   
 package Apache::lonnavmaps::DFSiterator;  package Apache::lonnavmaps::DFSiterator;
 use WeakRef;  use Scalar::Util qw(weaken);
 use Apache::lonnet;  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
Line 3420  sub populateStack { Line 3172  sub populateStack {
 1;  1;
   
 package Apache::lonnavmaps::resource;  package Apache::lonnavmaps::resource;
 use WeakRef;  use Scalar::Util qw(weaken);
 use Apache::lonnet;  use Apache::lonnet;
   
 =pod  =pod
Line 3599  sub kind { my $self=shift; return $self- Line 3351  sub kind { my $self=shift; return $self-
 sub randomout { my $self=shift; return $self->navHash("randomout_", 1); }  sub randomout { my $self=shift; return $self->navHash("randomout_", 1); }
 sub randompick {   sub randompick { 
     my $self = shift;      my $self = shift;
     return $self->{NAV_MAP}->{PARM_HASH}->{$self->symb .      return $self->parmval('randompick');
                                                '.0.parameter_randompick'};  
 }  }
 sub link {  sub link {
     my $self=shift;      my $self=shift;
Line 3718  sub retrieveResources { Line 3469  sub retrieveResources {
    return $self->{NAV_MAP}->retrieveResources(@_);     return $self->{NAV_MAP}->retrieveResources(@_);
 }  }
   
   sub is_exam {
       my ($self,$part) = @_;
       if ($self->parmval('type',$part) eq 'exam') {
           return 1;
       }
       if ($self->src() =~ /\.(exam)$/) {
           return 1;
       }
       return 0;
   }
 sub is_html {  sub is_html {
     my $self=shift;      my $self=shift;
     my $src = $self->src();      my $src = $self->src();
Line 3756  sub contains_problem { Line 3517  sub contains_problem {
 }  }
 sub is_sequence {  sub is_sequence {
     my $self=shift;      my $self=shift;
     my $src = $self->src();  
     return $self->navHash("is_map_", 1) &&       return $self->navHash("is_map_", 1) && 
  $self->navHash("map_type_" . $self->map_pc()) eq 'sequence';   $self->navHash("map_type_" . $self->map_pc()) eq 'sequence';
 }  }
Line 3831  Returns a string with the type of the ma Line 3591  Returns a string with the type of the ma
 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);      $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 3844  sub map_pc { Line 3604  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);      $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 3956  sub awarded { Line 3716  sub awarded {
     if (!defined($part)) { $part = '0'; }      if (!defined($part)) { $part = '0'; }
     return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.awarded'};      return $self->{NAV_MAP}->{STUDENT_DATA}->{$self->symb()}->{'resource.'.$part.'.awarded'};
 }  }
   # this should work exactly like the copy in lonhomework.pm
 sub duedate {  sub duedate {
     (my $self, my $part) = @_;      (my $self, my $part) = @_;
       my $date;
     my $interval=$self->parmval("interval", $part);      my $interval=$self->parmval("interval", $part);
     if ($interval) {      my $due_date=$self->parmval("duedate", $part);
       if ($interval =~ /\d+/) {
  my $first_access=&Apache::lonnet::get_first_access('map',$self->symb);   my $first_access=&Apache::lonnet::get_first_access('map',$self->symb);
  if ($first_access) { return ($first_access+$interval); }   if (defined($first_access)) {
       $interval = $first_access+$interval;
       $date = ($interval < $due_date)? $interval : $due_date;
    } else {
       $date = $due_date;
    }
       } else {
    $date = $due_date;
     }      }
     return $self->parmval("duedate", $part);      return $date;
 }  }
 sub handgrade {  sub handgrade {
     (my $self, my $part) = @_;      (my $self, my $part) = @_;
Line 4060  Returns a false value if there has been Line 3830  Returns a false value if there has been
 logged in, true if there has. Always returns false if the discussion  logged in, true if there has. Always returns false if the discussion
 data was not extracted when the nav map was constructed.  data was not extracted when the nav map was constructed.
   
   =item * B<last_post_time>:
   
   Returns a false value if there hasn't been discussion otherwise returns
   unix timestamp of last time a discussion posting (or edit) was made.
   
   =item * B<unread_discussion>:
   
   returns in scalar context the count of the number of unread discussion
   postings
   
   returns in list context both the count of postings and a hash ref
   containing the subjects of all unread postings
   
 =item * B<getFeedback>:  =item * B<getFeedback>:
   
 Gets the feedback for the resource and returns the raw feedback string  Gets the feedback for the resource and returns the raw feedback string
Line 4068  email data was not extracted when the na Line 3851  email data was not extracted when the na
 used like this:  used like this:
   
  for (split(/\,/, $res->getFeedback())) {   for (split(/\,/, $res->getFeedback())) {
     my $link = &Apache::lonnet::escape($_);      my $link = &escape($_);
     ...      ...
   
 and use the link as appropriate.  and use the link as appropriate.
Line 4080  sub hasDiscussion { Line 3863  sub hasDiscussion {
     return $self->{NAV_MAP}->hasDiscussion($self->symb());      return $self->{NAV_MAP}->hasDiscussion($self->symb());
 }  }
   
   sub last_post_time {
       my $self = shift;
       return $self->{NAV_MAP}->last_post_time($self->symb());
   }
   
   sub unread_discussion {
       my $self = shift;
       return $self->{NAV_MAP}->unread_discussion($self->symb());
   }
   
 sub getFeedback {  sub getFeedback {
     my $self = shift;      my $self = shift;
     my $source = $self->src();      my $source = $self->src();
Line 4177  sub countResponses { Line 3970  sub countResponses {
 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));
         }          }
Line 4288  sub extractParts { Line 4081  sub extractParts {
         # So we have to use our knowlege of part names to figure out           # So we have to use our knowlege of part names to figure out 
         # where the part names begin and end, and even then, it is possible          # where the part names begin and end, and even then, it is possible
         # to construct ambiguous situations.          # to construct ambiguous situations.
         foreach (split /,/, $metadata) {          foreach my $data (split /,/, $metadata) {
             if ($_ =~ /^([a-zA-Z]+)response_(.*)/              if ($data =~ /^([a-zA-Z]+)response_(.*)/
  || $_ =~ /^(Task)_(.*)/) {   || $data =~ /^(Task)_(.*)/) {
                 my $responseType = $1;                  my $responseType = $1;
                 my $partStuff = $2;                  my $partStuff = $2;
                 my $partIdSoFar = '';                  my $partIdSoFar = '';
Line 4302  sub extractParts { Line 4095  sub extractParts {
                     if ($parts{$partIdSoFar}) {                      if ($parts{$partIdSoFar}) {
                         my @otherChunks = @partChunks[$i+1..$#partChunks];                          my @otherChunks = @partChunks[$i+1..$#partChunks];
                         my $responseId = join('_', @otherChunks);                          my $responseId = join('_', @otherChunks);
                         push @{$responseIdHash{$partIdSoFar}}, $responseId;   if ($self->is_task()) {
                         push @{$responseTypeHash{$partIdSoFar}}, $responseType;      push(@{$responseIdHash{$partIdSoFar}},
    $partIdSoFar);
    } else {
       push(@{$responseIdHash{$partIdSoFar}},
    $responseId);
    }
                           push(@{$responseTypeHash{$partIdSoFar}},
        $responseType);
                     }                      }
                 }                  }
             }              }

Removed from v.1.361  
changed lines
  Added in v.1.391


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