Diff for /loncom/interface/lonnavmaps.pm between versions 1.509.2.14.2.12 and 1.548

version 1.509.2.14.2.12, 2025/05/28 13:42:14 version 1.548, 2020/03/03 01:17:48
Line 2 Line 2
 # Navigate Maps Handler  # Navigate Maps Handler
 #  #
 # $Id$  # $Id$
   
 #  #
 # Copyright Michigan State University Board of Trustees  # Copyright Michigan State University Board of Trustees
 #  #
Line 52  described at http://www.lon-capa.org. Line 51  described at http://www.lon-capa.org.
 X<lonnavmaps, overview> When a user enters a course, LON-CAPA examines the  X<lonnavmaps, overview> When a user enters a course, LON-CAPA examines the
 course structure and caches it in what is often referred to as the  course structure and caches it in what is often referred to as the
 "big hash" X<big hash>. You can see it if you are logged into  "big hash" X<big hash>. You can see it if you are logged into
 LON-CAPA, in a course, by going to /adm/test. (You may need to  LON-CAPA, in a course, by going to /adm/test. The content of 
 tweak the /home/httpd/lonTabs/htpasswd file to view it.) The  the hash will be under the heading "Big Hash".
 content of the hash will be under the heading "Big Hash".  
   Access to /adm/test is controlled by a domain configuration, 
   which a Domain Coordinator will set for a server's default domain
   via: Main Menu > Set domain configuration > Display (Access to 
   server status pages checked), and entering a username:domain
   or IP address in the "Show user environment" row. Users with
   an unexpired domain coordinator role in the server's domain
   automatically receive access to /adm/test.
   
 Big Hash contains, among other things, how resources are related  Big Hash contains, among other things, how resources are related
 to each other (next/previous), what resources are maps, which   to each other (next/previous), what resources are maps, which 
Line 65  processed. Line 71  processed.
   
 Apache::lonnavmaps provides an object model for manipulating this  Apache::lonnavmaps provides an object model for manipulating this
 information in a higher-level fashion than directly manipulating   information in a higher-level fashion than directly manipulating 
 the hash. It also provides access to several auxilary functions   the hash. It also provides access to several auxiliary functions 
 that aren't necessarily stored in the Big Hash, but are a per-  that aren't necessarily stored in the Big Hash, but are a per-
 resource sort of value, like whether there is any feedback on   resource sort of value, like whether there is any feedback on 
 a given resource.  a given resource.
Line 78  Apache::lonnavmaps also provides fairly Line 84  Apache::lonnavmaps also provides fairly
 rendering navmaps, and last but not least, provides the navmaps  rendering navmaps, and last but not least, provides the navmaps
 view for when the user clicks the NAV button.  view for when the user clicks the NAV button.
   
 B<Note>: Apache::lonnavmaps I<only> works for the "currently  B<Note>: Apache::lonnavmaps by default will show information 
 logged in user"; if you want things like "due dates for another  for the "currently logged in user".  However, if information
 student" lonnavmaps can not directly retrieve information like  about resources is needed for a different user, e.g., a bubblesheet
 that. You need the EXT function. This module can still help,  exam which uses randomorder, or randompick needs to be printed or 
 because many things, such as the course structure, are constant  graded for named user(s) or specific CODEs, then the username,
   domain, or CODE can be passed as arguments when creating a new 
   navmap object.
   
   Note if you want things like "due dates for another student",
   you would use the EXT function instead of lonnavmaps.
   That said, the lonnavmaps module can still help, because many
   things, such as the course structure, are usually constant
 between users, and Apache::lonnavmaps can help by providing  between users, and Apache::lonnavmaps can help by providing
 symbs for the EXT call.  symbs for the EXT call.
   
Line 92  all, then documents the Apache::lonnavma Line 105  all, then documents the Apache::lonnavma
 is the key to accessing the Big Hash information, covers the use  is the key to accessing the Big Hash information, covers the use
 of the Iterator (which provides the logic for traversing the   of the Iterator (which provides the logic for traversing the 
 somewhat-complicated Big Hash data structure), documents the  somewhat-complicated Big Hash data structure), documents the
 Apache::lonnavmaps::Resource objects that are returned by   Apache::lonnavmaps::Resource objects that are returned singularly
   by: getBySymb(), getById(), getByMapPc(), and getResourceByUrl()
   (can also be as an array), or in an array by retrieveResources().
   
 =head1 Subroutine: render  =head1 Subroutine: render
   
Line 342  user into thinking that if the sequence Line 357  user into thinking that if the sequence
 under it; for example, see the "Show Uncompleted Homework" view on the  under it; for example, see the "Show Uncompleted Homework" view on the
 B<NAV> screen.  B<NAV> screen.
   
 =item * B<suppressNavmap>: default: false  =item * B<suppressNavmaps>: default: false
   
 If true, will not display Navigate Content resources.   If true, will not display Navigate Content resources. 
   
Line 534  my %colormap = Line 549  my %colormap =
       $resObj->EXCUSED                => '#3333FF',        $resObj->EXCUSED                => '#3333FF',
       $resObj->PAST_DUE_ANSWER_LATER  => '',        $resObj->PAST_DUE_ANSWER_LATER  => '',
       $resObj->PAST_DUE_NO_ANSWER     => '',        $resObj->PAST_DUE_NO_ANSWER     => '',
       $resObj->PAST_DUE_ATMPT_ANS     => '',  
       $resObj->PAST_DUE_ATMPT_NOANS   => '',  
       $resObj->PAST_DUE_NO_ATMT_ANS   => '',  
       $resObj->PAST_DUE_NO_ATMT_NOANS => '',  
       $resObj->ANSWER_OPEN            => '#006600',        $resObj->ANSWER_OPEN            => '#006600',
       $resObj->OPEN_LATER             => '',        $resObj->OPEN_LATER             => '',
       $resObj->TRIES_LEFT             => '',        $resObj->TRIES_LEFT             => '',
Line 581  sub getLinkForResource { Line 592  sub getLinkForResource {
     my $anchor;      my $anchor;
     if ($res->is_page()) {      if ($res->is_page()) {
  foreach my $item (@$stack) { if (defined($item)) { $anchor = $item; }  }   foreach my $item (@$stack) { if (defined($item)) { $anchor = $item; }  }
  if ($anchor->encrypted() && !&advancedUser()) {   $anchor=&escape($anchor->shown_symb());
     $anchor='LC_'.$anchor->id();  
  } else {  
     $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"
Line 683  sub getDescription { Line 690  sub getDescription {
             return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part).$slotinfo;              return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part).$slotinfo;
         }          }
     }      }
     if (($status == $res->PAST_DUE_ANSWER_LATER) || ($status == $res->PAST_DUE_ATMPT_ANS) || ($status == $res->PAST_DUE_NO_ATMT_ANS)) {      if ($status == $res->PAST_DUE_ANSWER_LATER) {
         return &mt("Answer open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($answer,'start'),$res->symb(),'answerdate',$part));          return &mt("Answer open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($answer,'start'),$res->symb(),'answerdate',$part));
     }      }
     if (($status == $res->PAST_DUE_NO_ANSWER) || ($status == $res->PAST_DUE_ATMPT_NOANS) || ($status == $res->PAST_DUE_NO_ATMT_NOANS)) {      if ($status == $res->PAST_DUE_NO_ANSWER) {
  if ($res->is_practice()) {   if ($res->is_practice()) {
     return &mt("Closed [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'answerdate,duedate',$part));      return &mt("Closed [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'answerdate,duedate',$part));
  } else {   } else {
Line 956  sub render_resource { Line 963  sub render_resource {
         $newBranchText = "<img src='$location/branch.gif' alt=".mt('Branch')." />";          $newBranchText = "<img src='$location/branch.gif' alt=".mt('Branch')." />";
     }      }
   
     # links to open and close the folder  
   
     my $whitespace = $location.'/whitespace_21.gif';      my $whitespace = $location.'/whitespace_21.gif';
     my ($nomodal,$linkopen,$linkclose);      my ($nomodal,$linkopen,$linkclose);
     unless ($resource->is_map() || $params->{'resource_nolink'}) {      unless ($resource->is_map() || $params->{'resource_nolink'}) {
Line 1011  sub render_resource { Line 1016  sub render_resource {
         if ($it->{CONDITION}) {          if ($it->{CONDITION}) {
             $nowOpen = !$nowOpen;              $nowOpen = !$nowOpen;
         }          }
  my $folderType;  
  if (&advancedUser() && $resource->is_missing_map()) {   my $folderType = $resource->is_sequence() ? 'folder' : 'page';
     $folderType = 'none';  
  } else {  
     $folderType = $resource->is_sequence() ? 'folder' : 'page';  
  }  
         my $title=$resource->title;          my $title=$resource->title;
  $title=~s/\"/\&qout;/g;   $title=~s/\"/\&qout;/g;
         if (!$params->{'resource_no_folder_link'}) {          if (!$params->{'resource_no_folder_link'}) {
Line 1049  sub render_resource { Line 1050  sub render_resource {
             }              }
         }          }
         if (((&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) ||          if (((&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) ||
              (&Apache::lonnet::allowed('cev',$env{'request.course.id'}))) &&                (&Apache::lonnet::allowed('cev',$env{'request.course.id'}))) &&
             ($resource->symb=~/\_\_\_[^\_]+\_\_\_uploaded/)) {              ($resource->symb=~/\_\_\_[^\_]+\_\_\_uploaded/)) {
             if (!$params->{'map_no_edit_link'}) {              if (!$params->{'map_no_edit_link'}) {
                 my $icon = &Apache::loncommon::lonhttpdurl('/res/adm/pages').'/editmap.png';                  my $icon = &Apache::loncommon::lonhttpdurl('/res/adm/pages').'/editmap.png';
Line 1063  sub render_resource { Line 1064  sub render_resource {
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';              $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';
         } elsif ($params->{'mapUnlisted'}) {          } elsif ($params->{'mapUnlisted'}) {
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('unlisted').')</span> ';              $nonLinkedText .= ' <span class="LC_warning">('.&mt('unlisted').')</span> ';
         } elsif ($params->{'mapHiddenDeepLink'} || $resource->deeplinkout()) {  
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('not shown').')</span> ';  
         }          }
     } else {      } else {
         if ($resource->randomout()) {          if ($resource->randomout()) {
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';              $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';
         } elsif ($resource->deeplinkout()) {          } elsif (($resource->deeplink($params->{caller}) eq 'absent') ||
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('not shown').')</span> ';                   ($resource->deeplink($params->{caller}) eq 'grades')) {
         } else {              $nonLinkedText .= ' <span class="LC_warning">('.&mt('unlisted').')</span> ';
             my $deeplink = $resource->deeplink($params->{caller});  
             if ((($deeplink eq 'absent') || ($deeplink eq 'grades')) &&  
                   &advancedUser()) {  
                 $nonLinkedText .= ' <span class="LC_warning">('.&mt('unlisted').')</span> ';  
             } elsif (($deeplink) && ($deeplink) ne 'full') {  
                 if (&advancedUser()) {  
                     $nonLinkedText .= ' <span class="LC_warning">('.&mt('deep-link access').  
                                       ')</span> ';  
                 } else {  
                     $nonLinkedText .= ' <span class="LC_warning">('.&mt('access via external site').  
                                       ')</span> ';  
                 }  
             }  
         }          }
     }      }
     if (!$resource->condval()) {      if (!$resource->condval()) {
Line 1248  sub render_long_status { Line 1234  sub render_long_status {
             }              }
          }           }
     }      }
       
     if (($resource->kind() eq "res") &&      if (($resource->kind() eq "res") &&
         ($resource->is_raw_problem() || $resource->is_gradable()) &&          ($resource->is_raw_problem() || $resource->is_gradable()) &&
         !$firstDisplayed) {          !$firstDisplayed) {
Line 1366  sub cmp_title { Line 1352  sub cmp_title {
 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 = '';
     # Configure the renderer.      # Configure the renderer.
     my $cols = $args->{'cols'};      my $cols = $args->{'cols'};
     if (!defined($cols)) {      if (!defined($cols)) {
         # no columns, no nav maps.          # no columns, no nav maps.
         return '';          return '';
     }      }
     my $legend = '';  
     my $tools = '';  
     my $result = '';  
     my $tools_printed = 0;  
     my $tablestarted = 0;  
     my $navmap;      my $navmap;
     if (defined($args->{'navmap'})) {      if (defined($args->{'navmap'})) {
         $navmap = $args->{'navmap'};          $navmap = $args->{'navmap'};
Line 1412  sub render { Line 1394  sub render {
         # Without renaming the filterfunc, the server seems to go into          # Without renaming the filterfunc, the server seems to go into
         # an infinite loop          # an infinite loop
         my $oldFilterFunc = $filterFunc;          my $oldFilterFunc = $filterFunc;
         $filterFunc = sub { my $res = shift; return !$res->randomout() &&           $filterFunc = sub { my $res = shift; return !$res->randomout() &&
                                 ($res->deeplink($args->{'caller'}) ne 'absent') &&                                  ($res->deeplink($args->{'caller'}) ne 'absent') &&
                                 ($res->deeplink($args->{'caller'}) ne 'grades') &&                                  ($res->deeplink($args->{'caller'}) ne 'grades') &&
                                 !$res->deeplinkout() &&  
                                 &$oldFilterFunc($res);};                                  &$oldFilterFunc($res);};
     }      }
   
     my $condition = 0;      my $condition = 0;
     if ($env{'form.condition'}) {      if ($env{'form.condition'}) {
         $condition = 1;          $condition = 1;
     } elsif (($env{'request.deeplink.login'}) && ($env{'request.course.id'}) && (!$userCanSeeHidden)) {  
         if (!defined($navmap)) {  
             $navmap = Apache::lonnavmaps::navmap->new();  
         }  
         if (defined($navmap)) {  
             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};  
             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};  
             my $symb = &Apache::loncommon::symb_from_tinyurl($env{'request.deeplink.login'},$cnum,$cdom);  
             if ($symb) {  
                 my $deeplink;  
                 my $res = $navmap->getBySymb($symb);  
                 if ($res->is_map()) {  
                     my $mapname = &Apache::lonnet::declutter($res->src());  
                     $mapname = &Apache::lonnet::deversion($mapname);  
                     $deeplink = $navmap->get_mapparam(undef,$mapname,"0.deeplink");  
                 } else {  
                     $deeplink = $res->deeplink();  
                 }  
                 if ($deeplink ne '') {  
                     if ((split(/,/,$deeplink))[1] eq 'hide') {  
                         if ($res->is_map()) {  
                             map { $filterHash->{$_} = 1 if $_ } split(/,/,$res->map_hierarchy());  
                         } else {  
                             my $mapurl = (&Apache::lonnet::decode_symb($symb))[0];  
                             my $map = $navmap->getResourceByUrl($mapurl);  
                             map { $filterHash->{$_} = 1 if $_ } split(/,/,$map->map_hierarchy());  
                         }  
                     }  
                 }  
             }  
         }  
     }      }
   
     if (!$env{'form.folderManip'} && !defined($args->{'iterator'}) && !$args->{'nocurrloc'}) {      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 1476  sub render { Line 1426  sub render {
             my $currenturl = $env{'form.postdata'};              my $currenturl = $env{'form.postdata'};
             #$currenturl=~s/^http\:\/\///;              #$currenturl=~s/^http\:\/\///;
             #$currenturl=~s/^[^\/]+//;              #$currenturl=~s/^[^\/]+//;
             unless ($args->{'caller'} eq 'sequence') {               unless ($args->{'caller'} eq 'sequence') {
                 $here = $jump = &Apache::lonnet::symbread($currenturl);                  $here = $jump = &Apache::lonnet::symbread($currenturl);
             }              }
  }   }
  if (($here eq '') && ($args->{'caller'} ne 'sequence')) {    if (($here eq '') && ($args->{'caller'} ne 'sequence')) {
     my $last;      my $last;
     if (tie(my %hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db',      if (tie(my %hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db',
                     &GDBM_READER(),0640)) {                      &GDBM_READER(),0640)) {
Line 1494  sub render { Line 1444  sub render {
         my $mapIterator = $navmap->getIterator(undef, undef, undef, 1);          my $mapIterator = $navmap->getIterator(undef, undef, undef, 1);
         my $curRes;          my $curRes;
         my $found = 0;          my $found = 0;
         my $here_is_navmaps = 0;  
         if ($here =~ m{___\d+___adm/navmaps$}) {  
             $here_is_navmaps = 1;  
         }  
                   
         # 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 ($here && ($curRes = $mapIterator->next()) && !$found && !$here_is_navmaps) {          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 1588  sub render { Line 1534  sub render {
         
     # Print key?      # Print key?
     if ($printKey) {      if ($printKey) {
         $legend = '<table border="0" cellpadding="2" cellspacing="0">';          $result .= '<table border="0" cellpadding="2" cellspacing="0">';
         $legend .= '<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");   my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc");
         if ($navmap->{LAST_CHECK}) {          if ($navmap->{LAST_CHECK}) {
             $legend .=               $result .= 
                 '<img src="'.$location.'/chat.gif" alt="" /> '.&mt('New discussion since').' '.                  '<img src="'.$location.'/chat.gif" alt="" /> '.&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="'.$location.'/feedback.gif" alt="" /> '.&mt('New message (click to open)').'<p>'.                  '<img src="'.$location.'/feedback.gif" alt="" /> '.&mt('New message (click to open)').'<p>'.
                 '</td>';                   '</td>'; 
         } else {          } else {
             $legend .= '<td align="center" valign="bottom">&nbsp;&nbsp;'.              $result .= '<td align="center" valign="bottom">&nbsp;&nbsp;'.
                 '<img src="'.$location.'/chat.gif" alt="" /> '.&mt('Discussions').'</td><td align="center" valign="bottom">'.                  '<img src="'.$location.'/chat.gif" alt="" /> '.&mt('Discussions').'</td><td align="center" valign="bottom">'.
                 '&nbsp;&nbsp;<img src="'.$location.'/feedback.gif" alt="" /> '.&mt('New message (click to open)').                  '&nbsp;&nbsp;<img src="'.$location.'/feedback.gif" alt="" /> '.&mt('New message (click to open)').
                 '</td>';                   '</td>'; 
         }          }
   
         $legend .= '</tr></table>';          $result .= '</tr></table>';
     }      }
   
     if ($printCloseAll && !$args->{'resource_no_folder_link'}) {      if ($printCloseAll && !$args->{'resource_no_folder_link'}) {
Line 1619  sub render { Line 1565  sub render {
  '&amp;here='.&escape($here);   '&amp;here='.&escape($here);
     $text='Open all folders';      $text='Open all folders';
         }          }
           if ($env{'form.register'}) {
               $link .= '&amp;register='.$env{'form.register'};
           }
  if ($args->{'caller'} eq 'navmapsdisplay') {   if ($args->{'caller'} eq 'navmapsdisplay') {
             unless ($args->{'notools'}) {              unless ($args->{'notools'}) {
                 &add_linkitem($args->{'linkitems'},'changefolder',                  &add_linkitem($args->{'linkitems'},'changefolder',
                               "location.href='$link'",$text);                                "location.href='$link'",$text);
             }              }
  } else {   } else {
     $tools = '<a href="'.$link.'">'.&mt($text).'</a>';      $result.= '<a href="'.$link.'">'.&mt($text).'</a>';
  }   }
         $tools .= "\n";          $result .= "\n";
     }      }
   
     # Check for any unread discussions in all resources.      # Check for any unread discussions in all resources.
Line 1637  sub render { Line 1586  sub render {
       'Mark all posts read');        'Mark all posts read');
  my $time=time;   my $time=time;
         my $querystr = &HTML::Entities::encode($ENV{'QUERY_STRING'},'<>&"');          my $querystr = &HTML::Entities::encode($ENV{'QUERY_STRING'},'<>&"');
  $tools .= (<<END);   $result .= (<<END);
     <form name="clearbubbles" method="post" action="/adm/feedback">      <form name="clearbubbles" method="post" action="/adm/feedback">
  <input type="hidden" name="navurl" value="$querystr" />   <input type="hidden" name="navurl" value="$querystr" />
  <input type="hidden" name="navtime" value="$time" />   <input type="hidden" name="navtime" value="$time" />
 END  END
           if ($env{'form.register'}) {
               $result .= '<input type="hidden" name="register" value="'.$env{'form.register'}.'" />';
           }
         if ($args->{'sort'} eq 'discussion') {           if ($args->{'sort'} eq 'discussion') { 
     my $totdisc = 0;      my $totdisc = 0;
     my $haveDisc = '';      my $haveDisc = '';
Line 1654  END Line 1606  END
     }      }
     if ($totdisc > 0) {      if ($totdisc > 0) {
  $haveDisc =~ s/:$//;   $haveDisc =~ s/:$//;
  $tools .= (<<END);   $result .= (<<END);
  <input type="hidden" name="navmaps" value="$haveDisc" />   <input type="hidden" name="navmaps" value="$haveDisc" />
     </form>      </form>
 END  END
             }              }
  }   }
  $tools .= '</form>';   $result.='</form>';
     }      }
     if (($args->{'caller'} eq 'navmapsdisplay') && ($env{'request.course.id'})) {      if (($args->{'caller'} eq 'navmapsdisplay') &&
           ((&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) ||
            (&Apache::lonnet::allowed('cev',$env{'request.course.id'})))) {
         my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};          my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
         my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};          my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
         if ($env{'course.'.$env{'request.course.id'}.'.url'} eq          if ($env{'course.'.$env{'request.course.id'}.'.url'} eq 
             "uploaded/$cdom/$cnum/default.sequence") {              "uploaded/$cdom/$cnum/default.sequence") {
             if ((&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) ||              &add_linkitem($args->{'linkitems'},'edittoplevel',
                 (&Apache::lonnet::allowed('cev',$env{'request.course.id'}))) {                            "javascript:gocmd('/adm/coursedocs','editdocs');",
                 &add_linkitem($args->{'linkitems'},'edittoplevel',                            'Content Editor');
                               "javascript:gocmd('/adm/coursedocs','editdocs');",  
                               'Content Editor');  
             }  
             if ($counter) {  
                 &add_linkitem($args->{'linkitems'},'printout',  
                               "javascript:gopost('/adm/printout','/adm/navmaps');",  
                               'Prepare a printable document');  
             }  
         }          }
     }      }
   
     if ($args->{'caller'} eq 'navmapsdisplay') {      if ($args->{'caller'} eq 'navmapsdisplay') {
         $tools .= &show_linkitems_toolbar($args,$condition);          $result .= &show_linkitems_toolbar($args,$condition);
     } elsif ($args->{'sort_html'}) {       } elsif ($args->{'sort_html'}) { 
         $tools .= $args->{'sort_html'};           $result.=$args->{'sort_html'}; 
     }      }
   
     #$tools .= "<br />\n";      #$result .= "<br />\n";
       if ($r) {
           $r->print($result);
           $r->rflush();
           $result = "";
       }
     # End parameter setting      # End parameter setting
       
       $result .= "<br />\n";
   
     # Data      # Data
       $result.=&Apache::loncommon::start_data_table("LC_tableOfContent");    
   
     my $res = "Apache::lonnavmaps::resource";      my $res = "Apache::lonnavmaps::resource";
     my %condenseStatuses =      my %condenseStatuses =
Line 1702  END Line 1656  END
     $args->{'counter'} = 0; # counts the rows      $args->{'counter'} = 0; # counts the rows
     $args->{'indentLevel'} = 0;      $args->{'indentLevel'} = 0;
     $args->{'isNewBranch'} = 0;      $args->{'isNewBranch'} = 0;
     $args->{'condensed'} = 0;      $args->{'condensed'} = 0;   
     $args->{'deeplinknolist'} = 0;  
   
     my $location = &Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace_21.gif");      my $location = &Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace_21.gif");
     $args->{'indentString'} = setDefault($args->{'indentString'}, "<img src='$location' alt='' />");      $args->{'indentString'} = setDefault($args->{'indentString'}, "<img src='$location' alt='' />");
Line 1713  END Line 1666  END
     # We also do this even if $args->{'suppressEmptySequences'}      # We also do this even if $args->{'suppressEmptySequences'}
     # is not true, so we can hide empty sequences for which the      # is not true, so we can hide empty sequences for which the
     # hiddenresource parameter is set to yes (at map level), or      # hiddenresource parameter is set to yes (at map level), or
     # mark as hidden for users who have $userCanSeeHidden.      # mark as hidden for users who have $userCanSeeHidden.  
     # Use DFS for speed, since structure actually doesn't matter,      # Use DFS for speed, since structure actually doesn't matter,
     # except what map has what resources.      # except what map has what resources.
     #  
     # To ensure the "Selected Resources from selected folder in course"  
     # printout generation option will work in sessions launched via a  
     # deep link, the value of $args->{'filterFunc'} included in the  
     # call to lonnavmaps::render() is omitted from the filter function  
     # used with the DFS Iterator when $args->{'caller'} is 'printout'.  
     #  
     # As a result $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} can be  
     # set to 1 for folder(s) which include resources only accessible  
     # for sessions launched via a deep link, when the current session  
     # is of that type.  
   
     my $dfsit = Apache::lonnavmaps::DFSiterator->new($navmap,      my $dfsit = Apache::lonnavmaps::DFSiterator->new($navmap,
                                                      $it->{FIRST_RESOURCE},                                                       $it->{FIRST_RESOURCE},
                                                      $it->{FINISH_RESOURCE},                                                       $it->{FINISH_RESOURCE},
                                                      {}, undef, 1);                                                       {}, undef, 1);
   
     my $dfsFilterFunc;  
     if ($args->{'caller'} eq 'printout') {  
         $dfsFilterFunc = sub { my $res = shift; return !$res->randomout() &&  
                               ($res->deeplink($args->{'caller'}) ne 'absent') &&  
                               ($res->deeplink($args->{'caller'}) ne 'grades') &&  
                               !$res->deeplinkout();};  
     } else {  
         $dfsFilterFunc = $filterFunc;  
     }  
     my $depth = 0;      my $depth = 0;
     $dfsit->next();      $dfsit->next();
     my $curRes = $dfsit->next();      my $curRes = $dfsit->next();
Line 1749  END Line 1681  END
         if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }          if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }
         if ($curRes == $dfsit->END_MAP()) { $depth--; }          if ($curRes == $dfsit->END_MAP()) { $depth--; }
   
         if (ref($curRes)) {          if (ref($curRes)) { 
             # Parallel pre-processing: Do sequences have non-filtered-out children?              # Parallel pre-processing: Do sequences have non-filtered-out children?
             if ($curRes->is_map()) {              if ($curRes->is_map()) {
                 $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;                  $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;
Line 1760  END Line 1692  END
             } elsif ($curRes->src()) {              } elsif ($curRes->src()) {
                 # Not a sequence: if it's filtered, ignore it, otherwise                  # Not a sequence: if it's filtered, ignore it, otherwise
                 # rise up the stack and mark the sequences as having children                  # rise up the stack and mark the sequences as having children
                 if (&$dfsFilterFunc($curRes)) {                  if (&$filterFunc($curRes)) {
                     for my $sequence (@{$dfsit->getStack()}) {                      for my $sequence (@{$dfsit->getStack()}) {
                         next unless ($sequence->is_map());  
                         $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;                          $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;
                     }                      }
                 }                  }
Line 1879  END Line 1810  END
   
         # If this has been filtered out, continue on          # If this has been filtered out, continue on
         if (!(&$filterFunc($curRes))) {          if (!(&$filterFunc($curRes))) {
             if (!$userCanSeeHidden && !$curRes->randomout && $curRes->deeplinkout) {  
                 $args->{'deeplinknolist'} ++;  
             }  
             $args->{'isNewBranch'} = 0; # Don't falsely remember this              $args->{'isNewBranch'} = 0; # Don't falsely remember this
             next;              next;
         }           } 
Line 1889  END Line 1817  END
         # If this is an empty sequence and we're filtering them, continue on          # If this is an empty sequence and we're filtering them, continue on
         $args->{'mapHidden'} = 0;          $args->{'mapHidden'} = 0;
         $args->{'mapUnlisted'} = 0;          $args->{'mapUnlisted'} = 0;
         $args->{'mapHiddenDeepLink'} = 0;  
         if (($curRes->is_map()) && (!$curRes->{DATA}->{HAS_VISIBLE_CHILDREN})) {          if (($curRes->is_map()) && (!$curRes->{DATA}->{HAS_VISIBLE_CHILDREN})) {
             if ($args->{'suppressEmptySequences'}) {              if ($args->{'suppressEmptySequences'}) {
                 next;                  next;
             } else {              } else {
                 my $mapname = &Apache::lonnet::declutter($curRes->src());                  my $mapname = &Apache::lonnet::declutter($curRes->src());
                 $mapname = &Apache::lonnet::deversion($mapname);                  $mapname = &Apache::lonnet::deversion($mapname); 
                 if (lc($navmap->get_mapparam(undef,$mapname,"0.hiddenresource")) eq 'yes') {                  if (lc($navmap->get_mapparam(undef,$mapname,"0.hiddenresource")) eq 'yes') {
                     if ($userCanSeeHidden) {                      if ($userCanSeeHidden) {
                         $args->{'mapHidden'} = 1;                          $args->{'mapHidden'} = 1;
                     } else {                      } else {
                         next;                          next;
                     }                      }
                 } elsif ($curRes->deeplinkout) {  
                     if ($userCanSeeHidden) {  
                         $args->{'mapHiddenDeepLink'} = 1;  
                     } else {  
                         $args->{'deeplinknolist'} ++;  
                         next;  
                     }  
                 } else {                  } else {
                     my $deeplink = $navmap->get_mapparam(undef,$mapname,"0.deeplink");                      my $deeplink = $navmap->get_mapparam(undef,$mapname,"0.deeplink");
                     my ($state,$others,$listed) = split(/,/,$deeplink);                      if ($deeplink =~ /^(absent|grades),/) {
                     if (($listed eq 'absent') || ($listed eq 'grades')) {  
                         if ($userCanSeeHidden) {                          if ($userCanSeeHidden) {
                             $args->{'mapUnlisted'} = 1;                              $args->{'mapUnlisted'} = 1;
                         } else {                          } else {
                             $args->{'deeplinknolist'} ++;  
                             next;                              next;
                         }                          }
                     }                      }
Line 1930  END Line 1848  END
         }          }
   
         $args->{'counter'}++;          $args->{'counter'}++;
         unless ($tablestarted) {  
             $result .= "<br />\n".&Apache::loncommon::start_data_table("LC_tableOfContent");  
             $tablestarted = 1;  
         }  
   
         # Does it have multiple parts?          # Does it have multiple parts?
         $args->{'multipart'} = 0;          $args->{'multipart'} = 0;
Line 1988  END Line 1902  END
             }              }
         }          }
         # If deep-link parameter is set (and is not set to full) suppress link          # If deep-link parameter is set (and is not set to full) suppress link
         # unless privileged user, tinyurl used for login resolved to a map, and          # unless privileged user, or calling context is sequence, and parameter
         # the resource is within the map.          # set at map level
         if ((!$curRes->deeplink($args->{'caller'})) ||          if ((!$curRes->deeplink($args->{'caller'})) ||
             ($curRes->deeplink($args->{'caller'}) eq 'full') || &advancedUser()) {              ($curRes->deeplink($args->{'caller'}) =~ /^full,/) || &advancedUser()) {
             $args->{'resource_nolink'} = 0;              $args->{'resource_nolink'} = 0;
         } else {          } else {
             $args->{'resource_nolink'} = 1;              $args->{'resource_nolink'} = 1;
         }          }
               
         # If the multipart problem was condensed, "forget" it was multipart          # If the multipart problem was condensed, "forget" it was multipart
         if (scalar(@parts) == 1) {          if (scalar(@parts) == 1) {
             $args->{'multipart'} = 0;              $args->{'multipart'} = 0;
Line 2022  END Line 1936  END
             if ($env{'request.course.id'}) {              if ($env{'request.course.id'}) {
                 if (($is_ssl) && ($src =~ m{^\Q/public/$cdom/$cnum/syllabus\E($|\?)}) &&                  if (($is_ssl) && ($src =~ m{^\Q/public/$cdom/$cnum/syllabus\E($|\?)}) &&
                     ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) {                      ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) {
                     unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) {                      unless (&Apache::lonnet::uses_sts()) {
                         if ($hostname ne '') {                          if ($hostname ne '') {
                             $src = 'http://'.$hostname.$src;                              $src = 'http://'.$hostname.$src;
                         }                          }
Line 2030  END Line 1944  END
                         $srcHasQuestion = 1;                          $srcHasQuestion = 1;
                     }                      }
                 } elsif (($is_ssl) && ($src =~ m{^\Q/adm/wrapper/ext/\E(?!https:)})) {                  } elsif (($is_ssl) && ($src =~ m{^\Q/adm/wrapper/ext/\E(?!https:)})) {
                     unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) {                      unless (&Apache::lonnet::uses_sts()) {
                         if ($hostname ne '') {                          if ($hostname ne '') {
                             $src = 'http://'.$hostname.$src;                              $src = 'http://'.$hostname.$src;
                         }                          }
Line 2040  END Line 1954  END
                 }                  }
             }              }
     if (defined($anchor)) { $anchor='#'.$anchor; }      if (defined($anchor)) { $anchor='#'.$anchor; }
             if (($args->{'caller'} eq 'sequence') && ($curRes->is_map())) {      if (($args->{'caller'} eq 'sequence') && ($curRes->is_map())) {
                 $args->{"resourceLink"} = $src.($srcHasQuestion?'&amp;':'?') .'navmap=1';          $args->{"resourceLink"} = $src.($srcHasQuestion?'&amp;':'?') .'navmap=1';
             } else {      } else {
         $args->{"resourceLink"} = $src.          $args->{"resourceLink"} = $src.
     ($srcHasQuestion?'&amp;':'?') .      ($srcHasQuestion?'&amp;':'?') .
     'symb=' . &escape($symb).$inhibitmenu.$anchor;      'symb=' . &escape($symb).$inhibitmenu.$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 2084  END Line 1998  END
         }          }
   
         if ($r && $rownum % 20 == 0) {          if ($r && $rownum % 20 == 0) {
             unless ($tools_printed) {  
                 $r->print($legend.$tools);  
                 $legend = "";  
                 $tools = "";  
                 $tools_printed = 1;  
             }  
             $r->print($result);              $r->print($result);
             $result = "";              $result = "";
             $r->rflush();              $r->rflush();
Line 2105  END Line 2013  END
  }   }
     }      }
   
     if ($tablestarted) {      $result.=&Apache::loncommon::end_data_table();
         $result.=&Apache::loncommon::end_data_table();      
     }  
   
     # Print out the part that jumps to #curloc if it exists      # Print out the part that jumps to #curloc if it exists
     # delay needed because the browser is processing the jump before      # delay needed because the browser is processing the jump before
     # it finishes rendering, so it goes to the wrong place!      # it finishes rendering, so it goes to the wrong place!
Line 2125  if (location.href.indexOf('#curloc')==-1 Line 2031  if (location.href.indexOf('#curloc')==-1
     }      }
   
     if ($r) {      if ($r) {
         unless ($tools_printed) {  
             if (($args->{'counter'}) || ($userCanSeeHidden) ||  
                 (($args->{'caller'} eq 'navmapsdisplay') &&  
                  ($env{'form.showOnlyHomework'} ||  
                   $ENV{QUERY_STRING} =~ /^jumpToFirstHomework/))) {  
                 $r->print($legend.$tools);  
             }  
             $legend = "";  
             $tools = "";  
             $tools_printed = 1;  
         }  
         $r->print($result);          $r->print($result);
         $result = "";          $result = "";
         $r->rflush();          $r->rflush();
     }      }
                   
     return $legend.$tools.$result;      return $result;
 }  }
   
 sub add_linkitem {  sub add_linkitem {
Line 2167  sub show_linkitems_toolbar { Line 2062  sub show_linkitems_toolbar {
             $result .= '<td align="left">'."\n".              $result .= '<td align="left">'."\n".
                        '<ul id="LC_toolbar">';                         '<ul id="LC_toolbar">';
             my @linkorder = ('firsthomework','everything','uncompleted',              my @linkorder = ('firsthomework','everything','uncompleted',
                              'changefolder','clearbubbles','printout','edittoplevel');                               'changefolder','clearbubbles','edittoplevel');
             foreach my $link (@linkorder) {              foreach my $link (@linkorder) {
                 if (ref($args->{'linkitems'}{$link}) eq 'HASH') {                  if (ref($args->{'linkitems'}{$link}) eq 'HASH') {
                     if ($args->{'linkitems'}{$link}{'text'} ne '') {                      if ($args->{'linkitems'}{$link}{'text'} ne '') {
Line 2299  sub new { Line 2194  sub new {
   
     $self->{USERNAME} = shift || $env{'user.name'};      $self->{USERNAME} = shift || $env{'user.name'};
     $self->{DOMAIN}   = shift || $env{'user.domain'};      $self->{DOMAIN}   = shift || $env{'user.domain'};
     $self->{SECTION}  = shift;  
     $self->{CODE}     = shift;      $self->{CODE}     = shift;
     $self->{NOHIDE}   = shift;      $self->{NOHIDE} = shift;
   
   
     if (($self->{SECTION} eq '') && ($env{'request.course.sec'} ne '')) {  
         if (($self->{USERNAME} eq $env{'user.name'}) &&  
             ($self->{USERNAME} eq $env{'user.domain'})) {  
             $self->{SECTION} = $env{'request.course.sec'};  
         }  
     }  
   
     # Resource cache stores navmap resources as we reference them. We generate      # Resource cache stores navmap resources as we reference them. We generate
     # them on-demand so we don't pay for creating resources unless we use them.      # them on-demand so we don't pay for creating resources unless we use them.
Line 2351  sub new { Line 2239  sub new {
  $self->{PARM_HASH} = \%parmhash;   $self->{PARM_HASH} = \%parmhash;
  $self->{PARM_CACHE} = {};   $self->{PARM_CACHE} = {};
     } else {      } else {
  $self->change_user($self->{USERNAME}, $self->{DOMAIN}, $self->{SECTION}, $self->{CODE}, $self->{NOHIDE});   $self->change_user($self->{USERNAME}, $self->{DOMAIN},  $self->{CODE}, $self->{NOHIDE});
     }      }
   
     return $self;      return $self;
Line 2362  sub new { Line 2250  sub new {
 # username/domain associated with a navmap (e.g. to navigate for someone  # username/domain associated with a navmap (e.g. to navigate for someone
 # else besides the current user...if sufficiently privileged.  # else besides the current user...if sufficiently privileged.
 # Parameters:  # Parameters:
 #    user    - New user.  #    user  - New user.
 #    domain  - Domain to which the user belongs.  #    domain- Domain the user belongs to.
 #    section - Section to which the user belongs.  #    code  - Anonymous CODE in use.
 #    code    - Anonymous CODE in use.  
 # Implicit inputs:  # Implicit inputs:
 #     #   
 sub change_user {  sub change_user {
     my $self = shift;      my $self = shift;
     $self->{USERNAME} = shift;      $self->{USERNAME} = shift;
     $self->{DOMAIN}   = shift;      $self->{DOMAIN}   = shift;
     $self->{SECTION}  = shift;  
     $self->{CODE}     = shift;      $self->{CODE}     = shift;
     $self->{NOHIDE}   = shift;      $self->{NOHIDE}   = shift;
   
Line 2395  sub change_user { Line 2281  sub change_user {
   
   
   
     # Now clear the parm cache and reconstruct the parm hash fromt he big_hash      # Now clear the parm cache and reconstruct the parm hash from the big_hash
     # param.xxxx keys.      # param.xxxx keys.
   
     $self->{PARM_CACHE} = {};      $self->{PARM_CACHE} = {};
Line 2589  sub getIterator { Line 2475  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, shift);       shift, shift);
     return $iterator;      return $iterator;
 }  }
   
Line 2860  sub parmval { Line 2746  sub parmval {
             return $self->{PARM_CACHE}->{$hashkey};              return $self->{PARM_CACHE}->{$hashkey};
         }          }
     }      }
   
     my $result = $self->parmval_real($what, $symb, $recurse);      my $result = $self->parmval_real($what, $symb, $recurse);
     $self->{PARM_CACHE}->{$hashkey} = $result;      $self->{PARM_CACHE}->{$hashkey} = $result;
     if (wantarray) {      if (wantarray) {
Line 2878  sub parmval_real { Line 2765  sub parmval_real {
     $self->generate_course_user_opt();      $self->generate_course_user_opt();
   
     my $cid=$env{'request.course.id'};      my $cid=$env{'request.course.id'};
     my $csec=$self->{SECTION};      my $csec=$env{'request.course.sec'};
     my $cgroup='';      my $cgroup='';
     my @cgrps=split(/:/,$env{'request.course.groups'});      my @cgrps=split(/:/,$env{'request.course.groups'});
     if (@cgrps > 0) {      if (@cgrps > 0) {
Line 2897  sub parmval_real { Line 2784  sub parmval_real {
     if ($fn =~ /ext\.tool$/) {      if ($fn =~ /ext\.tool$/) {
         $toolsymb = $symb;          $toolsymb = $symb;
     }      }
       my ($recursed,@recurseup); 
       
 # ----------------------------------------------------- Cascading lookup scheme  # ----------------------------------------------------- Cascading lookup scheme
     my $rwhat=$what;      my $rwhat=$what;
     $what=~s/^parameter\_//;      $what=~s/^parameter\_//;
     $what=~s/\_/\./;      $what=~s/\_/\./;
   
     my $symbparm=$symb.'.'.$what;      my $symbparm=$symb.'.'.$what;
       my $recurseparm=$mapname.'___(rec).'.$what;
     my $mapparm=$mapname.'___(all).'.$what;      my $mapparm=$mapname.'___(all).'.$what;
     my $usercourseprefix=$cid;      my $usercourseprefix=$cid;
       
   
   
     my $grplevel=$usercourseprefix.'.['.$cgroup.'].'.$what;      my $grplevel=$usercourseprefix.'.['.$cgroup.'].'.$what;
     my $grplevelr=$usercourseprefix.'.['.$cgroup.'].'.$symbparm;      my $grplevelr=$usercourseprefix.'.['.$cgroup.'].'.$symbparm;
       my $grpleveli=$usercourseprefix.'.['.$cgroup.'].'.$recurseparm;
     my $grplevelm=$usercourseprefix.'.['.$cgroup.'].'.$mapparm;      my $grplevelm=$usercourseprefix.'.['.$cgroup.'].'.$mapparm;
   
   
     my $seclevel= $usercourseprefix.'.['.$csec.'].'.$what;      my $seclevel= $usercourseprefix.'.['.$csec.'].'.$what;
     my $seclevelr=$usercourseprefix.'.['.$csec.'].'.$symbparm;      my $seclevelr=$usercourseprefix.'.['.$csec.'].'.$symbparm;
       my $secleveli=$usercourseprefix.'.['.$csec.'].'.$recurseparm;
     my $seclevelm=$usercourseprefix.'.['.$csec.'].'.$mapparm;      my $seclevelm=$usercourseprefix.'.['.$csec.'].'.$mapparm;
   
   
     my $courselevel= $usercourseprefix.'.'.$what;      my $courselevel= $usercourseprefix.'.'.$what;
     my $courselevelr=$usercourseprefix.'.'.$symbparm;      my $courselevelr=$usercourseprefix.'.'.$symbparm;
       my $courseleveli=$usercourseprefix.'.'.$recurseparm;
     my $courselevelm=$usercourseprefix.'.'.$mapparm;      my $courselevelm=$usercourseprefix.'.'.$mapparm;
   
   
Line 2931  sub parmval_real { Line 2824  sub parmval_real {
     if ($uname and defined($useropt)) {      if ($uname and defined($useropt)) {
         if (defined($$useropt{$courselevelr})) { return [$$useropt{$courselevelr},'resource']; }          if (defined($$useropt{$courselevelr})) { return [$$useropt{$courselevelr},'resource']; }
         if (defined($$useropt{$courselevelm})) { return [$$useropt{$courselevelm},'map']; }          if (defined($$useropt{$courselevelm})) { return [$$useropt{$courselevelm},'map']; }
           if (defined($$useropt{$courseleveli})) { return [$$useropt{$courseleveli},'map']; }
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
               if (defined($$useropt{$norecursechk})) {
                   if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return [$$useropt{$norecursechk},'map'];
                   } else {
                       last;
                   }
               }
               my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
               if (defined($$useropt{$recursechk})) { return [$$useropt{$recursechk},'map']; } 
           }
         if (defined($$useropt{$courselevel})) { return [$$useropt{$courselevel},'course']; }          if (defined($$useropt{$courselevel})) { return [$$useropt{$courselevel},'course']; }
     }      }
   
Line 2938  sub parmval_real { Line 2848  sub parmval_real {
     if ($cgroup ne '' and defined($courseopt)) {      if ($cgroup ne '' and defined($courseopt)) {
         if (defined($$courseopt{$grplevelr})) { return [$$courseopt{$grplevelr},'resource']; }          if (defined($$courseopt{$grplevelr})) { return [$$courseopt{$grplevelr},'resource']; }
         if (defined($$courseopt{$grplevelm})) { return [$$courseopt{$grplevelm},'map']; }          if (defined($$courseopt{$grplevelm})) { return [$$courseopt{$grplevelm},'map']; }
           if (defined($$courseopt{$grpleveli})) { return [$$courseopt{$grpleveli},'map']; } 
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what;
               if (defined($$courseopt{$norecursechk})) {
                   if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return [$$courseopt{$norecursechk},'map'];
                   } else {
                      last;
                   }
               }
               my $recursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) { return [$$courseopt{$recursechk},'map']; }      
           }
         if (defined($$courseopt{$grplevel})) { return [$$courseopt{$grplevel},'course']; }          if (defined($$courseopt{$grplevel})) { return [$$courseopt{$grplevel},'course']; }
     }      }
   
     if ($csec and defined($courseopt)) {      if ($csec ne '' and defined($courseopt)) {
         if (defined($$courseopt{$seclevelr})) { return [$$courseopt{$seclevelr},'resource']; }          if (defined($$courseopt{$seclevelr})) { return [$$courseopt{$seclevelr},'resource']; }
         if (defined($$courseopt{$seclevelm})) { return [$$courseopt{$seclevelm},'map']; }          if (defined($$courseopt{$seclevelm})) { return [$$courseopt{$seclevelm},'map']; }
           if (defined($$courseopt{$secleveli})) { return [$$courseopt{$secleveli},'map']; } 
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what;
               if (defined($$courseopt{$norecursechk})) {
                   if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return [$$courseopt{$norecursechk},'map'];
                   } else {
                       last;
                   }
               }
               my $recursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) { return [$$courseopt{$recursechk},'map']; }
           }
         if (defined($$courseopt{$seclevel})) { return [$$courseopt{$seclevel},'course']; }          if (defined($$courseopt{$seclevel})) { return [$$courseopt{$seclevel},'course']; }
     }      }
   
Line 2967  sub parmval_real { Line 2911  sub parmval_real {
 # --------------------------------------------------- fifth, check more course  # --------------------------------------------------- fifth, check more course
     if (defined($courseopt)) {      if (defined($courseopt)) {
         if (defined($$courseopt{$courselevelm})) { return [$$courseopt{$courselevelm},'map']; }          if (defined($$courseopt{$courselevelm})) { return [$$courseopt{$courselevelm},'map']; }
           if (defined($$courseopt{$courseleveli})) { return [$$courseopt{$courseleveli},'map']; }
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
               if (defined($$courseopt{$norecursechk})) {
                   if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return [$$courseopt{$norecursechk},'map'];
                   } else {
                       last;
                   }
               }
               my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) {
                   return [$$courseopt{$recursechk},'map'];
               }
           }
         if (defined($$courseopt{$courselevel})) {          if (defined($$courseopt{$courselevel})) {
            my $ret = [$$courseopt{$courselevel},'course'];             my $ret = [$$courseopt{$courselevel},'course'];
            return $ret;             return $ret;
Line 2991  sub parmval_real { Line 2954  sub parmval_real {
 }  }
   
 sub recurseup_maps {  sub recurseup_maps {
     my ($self,$mapname,$getsymb) = @_;      my ($self,$mapname) = @_;
     my @recurseup;      my @recurseup;
     if ($mapname) {      if ($mapname) {
         my $res = $self->getResourceByUrl($mapname);          my $res = $self->getResourceByUrl($mapname);
Line 2999  sub recurseup_maps { Line 2962  sub recurseup_maps {
             my @pcs = split(/,/,$res->map_hierarchy());              my @pcs = split(/,/,$res->map_hierarchy());
             shift(@pcs);              shift(@pcs);
             if (@pcs) {              if (@pcs) {
                 if ($getsymb) {                  @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->src()); } reverse(@pcs);
                     @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->symb()); } reverse(@pcs);  
                 } else {  
                     @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->src()); } reverse(@pcs);  
                 }  
             }              }
         }          }
     }      }
Line 3035  sub recursed_crumbs { Line 2994  sub recursed_crumbs {
         my $pc = $map->map_pc();          my $pc = $map->map_pc();
         next if ((!$pc) || ($pc == 1));          next if ((!$pc) || ($pc == 1));
         push(@links,$map);          push(@links,$map);
         my $text = $map->title();          push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $map->title(),'no_mt' => 1,});
         if ($text eq '') {          $totallength += length($map->title());
             $text = '...';  
         }  
         push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $text,'no_mt' => 1,});  
         $totallength += length($text);  
     }      }
     my $numlinks = scalar(@links);      my $numlinks = scalar(@links);
     if ($numlinks) {      if ($numlinks) {
Line 3052  sub recursed_crumbs { Line 3007  sub recursed_crumbs {
             }              }
             @revmapinfo = ();              @revmapinfo = ();
             foreach my $map (@links) {              foreach my $map (@links) {
                 my $title = $map->title();                  my $showntitle = &truncate_crumb_text($map->title(),$avg);
                 if ($title eq '') {  
                     $title = '...';  
                 }  
                 my $showntitle = &truncate_crumb_text($title,$avg);  
                 if ($showntitle ne '') {                  if ($showntitle ne '') {
                     push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $showntitle,'no_mt' => 1,});                      push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $showntitle,'no_mt' => 1,});
                 }                  }
Line 3115  sub map_printdates { Line 3066  sub map_printdates {
   
   
     my $opendate = $self->get_mapparam($res->symb(),'',"$part.printstartdate");      my $opendate = $self->get_mapparam($res->symb(),'',"$part.printstartdate");
     my $closedate= $self->get_mapparam($res->symb(),'', "$part.printenddate");      my $closedate= $self->get_mapparam($res->symb(),'',"$part.printenddate");
   
   
     return ($opendate, $closedate);      return ($opendate, $closedate);
Line 3131  sub get_mapparam { Line 3082  sub get_mapparam {
     # Get the course id and section if there is one.      # Get the course id and section if there is one.
   
     my $cid=$env{'request.course.id'};      my $cid=$env{'request.course.id'};
     my $csec=$self->{SECTION};      my $csec=$env{'request.course.sec'};
     my $cgroup='';      my $cgroup='';
     my @cgrps=split(/:/,$env{'request.course.groups'});      my @cgrps=split(/:/,$env{'request.course.groups'});
     if (@cgrps > 0) {      if (@cgrps > 0) {
Line 3145  sub get_mapparam { Line 3096  sub get_mapparam {
     my $result='';      my $result='';
     my ($recursed,@recurseup);      my ($recursed,@recurseup);
   
   
     # Figure out which map we are in.      # Figure out which map we are in.
   
     if ($symb && !$mapname) {      if ($symb && !$mapname) {
Line 3153  sub get_mapparam { Line 3105  sub get_mapparam {
         $mapname = &Apache::lonnet::deversion($mapname);          $mapname = &Apache::lonnet::deversion($mapname);
     }      }
   
   
     my $rwhat=$what;      my $rwhat=$what;
     $what=~s/^parameter\_//;      $what=~s/^parameter\_//;
     $what=~s/\_/\./;      $what=~s/\_/\./;
   
     # Build the hash keys for the lookup:      # Build the hash keys for the lookup:
   
     my $symbparm=$symb.'.'.$what;  
     my $mapparm=$mapname.'___(all).'.$what;      my $mapparm=$mapname.'___(all).'.$what;
       my $recurseparm=$mapname.'___(rec).'.$what; 
     my $usercourseprefix=$cid;      my $usercourseprefix=$cid;
   
   
     my $grplevel    = "$usercourseprefix.[$cgroup].$mapparm";      my $grplevelm    = "$usercourseprefix.[$cgroup].$mapparm";
     my $seclevel    = "$usercourseprefix.[$csec].$mapparm";      my $seclevelm    = "$usercourseprefix.[$csec].$mapparm";
     my $courselevel = "$usercourseprefix.$mapparm";      my $courselevelm = "$usercourseprefix.$mapparm";
   
       my $grpleveli    = "$usercourseprefix.[$cgroup].$recurseparm";
       my $secleveli    = "$usercourseprefix.[$csec].$recurseparm";
       my $courseleveli = "$usercourseprefix.$recurseparm";
   
     # Get handy references to the hashes we need in $self:      # Get handy references to the hashes we need in $self:
   
Line 3180  sub get_mapparam { Line 3136  sub get_mapparam {
   
   
     if ($uname and defined($useropt)) {      if ($uname and defined($useropt)) {
  if (defined($$useropt{$courselevel})) {   if (defined($$useropt{$courselevelm})) {
     return $$useropt{$courselevel};      return $$useropt{$courselevelm};
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          if (defined($$useropt{$courseleveli})) {
             unless ($recursed) {              return $$useropt{$courseleveli};
                 @recurseup = $self->recurseup_maps($mapname);          }
                 $recursed = 1;          unless ($recursed) {
             }              @recurseup = $self->recurseup_maps($mapname);
             foreach my $item (@recurseup) {              $recursed = 1;
                 my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;          }
                 if (defined($$useropt{$norecursechk})) {          foreach my $item (@recurseup) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {              my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
                         return $$useropt{$norecursechk};              if (defined($$useropt{$norecursechk})) {
                     }                  if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return $$useropt{$norecursechk};
                   } else {
                       last;
                 }                  }
             }              }
               my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
               if (defined($$useropt{$recursechk})) {
                   return $$useropt{$recursechk};
               }
         }          }
     }      }
   
Line 3204  sub get_mapparam { Line 3167  sub get_mapparam {
   
   
     if ($cgroup ne '' and defined ($courseopt)) {      if ($cgroup ne '' and defined ($courseopt)) {
  if (defined($$courseopt{$grplevel})) {   if (defined($$courseopt{$grplevelm})) {
     return $$courseopt{$grplevel};      return $$courseopt{$grplevelm};
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          if (defined($$courseopt{$grpleveli})) {
             unless ($recursed) {              return $$courseopt{$grpleveli};
                 @recurseup = $self->recurseup_maps($mapname);          }
                 $recursed = 1;          unless ($recursed) {
             }              @recurseup = $self->recurseup_maps($mapname);
             foreach my $item (@recurseup) {              $recursed = 1;
                 my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what;          }
                 if (defined($$courseopt{$norecursechk})) {          foreach my $item (@recurseup) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {              my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what;
                         return $$courseopt{$norecursechk};              if (defined($$courseopt{$norecursechk})) {
                     }                  if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return $$courseopt{$norecursechk};
                   } else {
                       last;
                 }                  }
             }              }
               my $recursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) {
                   return $$courseopt{$recursechk};
               }
         }          }
     }      }
   
     # Check course -- section      # Check course -- section
   
   
       if ($csec ne '' and defined($courseopt)) {
    if (defined($$courseopt{$seclevelm})) {
       return $$courseopt{$seclevelm};
     if ($csec and defined($courseopt)) {  
  if (defined($$courseopt{$seclevel})) {  
     return $$courseopt{$seclevel};  
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          if (defined($$courseopt{$secleveli})) {
             unless ($recursed) {              return $$courseopt{$secleveli};
                 @recurseup = $self->recurseup_maps($mapname);          }
                 $recursed = 1;          unless ($recursed) {
             }              @recurseup = $self->recurseup_maps($mapname);
             foreach my $item (@recurseup) {              $recursed = 1;
                 my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what;          }
                 if (defined($$courseopt{$norecursechk})) {          foreach my $item (@recurseup) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {              my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what;
                         return $$courseopt{$norecursechk};              if (defined($$courseopt{$norecursechk})) {
                     }                  if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return $$courseopt{$norecursechk};
                   } else {
                       last;
                 }                  }
             }              }
               my $recursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) {
                   return $$courseopt{$recursechk};
               }
         }          }
     }      }
     # Check the map parameters themselves:      # Check the map parameters themselves:
Line 3254  sub get_mapparam { Line 3228  sub get_mapparam {
         my $symbparm=$symb.'.'.$what;          my $symbparm=$symb.'.'.$what;
         my $thisparm = $$parmhash{$symbparm};          my $thisparm = $$parmhash{$symbparm};
         if (defined($thisparm)) {          if (defined($thisparm)) {
             return $thisparm;      return $thisparm;
         }          }
     }      }
   
Line 3262  sub get_mapparam { Line 3236  sub get_mapparam {
     # Additional course parameters:      # Additional course parameters:
   
     if (defined($courseopt)) {      if (defined($courseopt)) {
  if (defined($$courseopt{$courselevel})) {   if (defined($$courseopt{$courselevelm})) {
     return $$courseopt{$courselevel};      return $$courseopt{$courselevelm};
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          unless ($recursed) {
             unless ($recursed) {              @recurseup = $self->recurseup_maps($mapname);
                 @recurseup = $self->recurseup_maps($mapname);              $recursed = 1;
                 $recursed = 1;          }
             }          if (@recurseup) {
             foreach my $item (@recurseup) {              foreach my $item (@recurseup) {
                 my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;                  my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
                 if (defined($$courseopt{$norecursechk})) {                  if (defined($$courseopt{$norecursechk})) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {                      if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                         return $$courseopt{$norecursechk};                          return $$courseopt{$norecursechk};
                       } else {
                           last;
                     }                      }
                 }                  }
                   my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
                   if (defined($$courseopt{$recursechk})) {
                       return $$courseopt{$recursechk};
                   }
             }              }
         }          }
     }      }
     return undef; # Unefined if we got here.      return undef; # Undefined if we got here.
 }  }
   
 sub course_printdates {  sub course_printdates {
Line 3300  sub getcourseparam { Line 3280  sub getcourseparam {
   
     my $uname = $self->{USERNAME};      my $uname = $self->{USERNAME};
     my $udom  = $self->{DOMAIN};      my $udom  = $self->{DOMAIN};
     my $csec  = $self->{SECTION};  
           
     # Course and group ids come from the env:      # Course, section, group ids come from the env:
   
     my $cid   = $env{'request.course.id'};      my $cid   = $env{'request.course.id'};
       my $csec  = $env{'request.course.sec'};
     my $cgroup = ''; # Assume no group      my $cgroup = ''; # Assume no group
   
     my @cgroups = split(/:/, $env{'request.course.groups'});      my @cgroups = split(/:/, $env{'request.course.groups'});
Line 3322  sub getcourseparam { Line 3302  sub getcourseparam {
     $what=~s/^parameter\_//;      $what=~s/^parameter\_//;
     $what=~s/\_/\./;      $what=~s/\_/\./;
   
   
     my $symbparm = $symb . '.' . $what;  
     my $mapparm=$mapname.'___(all).'.$what;  
   
     # Local refs to the hashes we're going to look at:      # Local refs to the hashes we're going to look at:
   
     my $useropt   = $self->{USER_OPT};      my $useropt   = $self->{USER_OPT};
Line 3636  getIterator behaves as follows: Line 3612  getIterator behaves as follows:
   
 =over 4  =over 4
   
 =item * B<getIterator>(firstResource, finishResource, filterHash, condition, forceTop, returnTopMap, $deeplinklisted):  =item * B<getIterator>(firstResource, finishResource, filterHash, condition, forceTop, returnTopMap):
   
 All parameters are optional. firstResource is a resource reference  All parameters are optional. firstResource is a resource reference
 corresponding to where the iterator should start. It defaults to  corresponding to where the iterator should start. It defaults to
Line 3653  that is not just a single, 'redirecting' Line 3629  that is not just a single, 'redirecting'
 will return all information, starting with the top-level map,  will return all information, starting with the top-level map,
 regardless of content. returnTopMap, if true (default false), will  regardless of content. returnTopMap, if true (default false), will
 cause the iterator to return the top-level map object (resource 0.0)  cause the iterator to return the top-level map object (resource 0.0)
 before anything else. deeplinklisted if true (default false), will  before anything else.
 check "listed" status of a resource with a deeplink, and unless "absent"  
 will exclude deeplink checking when retrieving the browsePriv from  
 lonnet::allowed().  
   
 Thus, by default, only top-level resources will be shown. Change the  Thus, by default, only top-level resources will be shown. Change the
 condition to a 1 without changing the hash, and all resources will be  condition to a 1 without changing the hash, and all resources will be
Line 3793  sub new { Line 3766  sub new {
     # have we done that yet?      # have we done that yet?
     $self->{HAVE_RETURNED_0} = 0;      $self->{HAVE_RETURNED_0} = 0;
   
     # Do we want to check the "listed" status for a resource for which  
     # deeplinking applies.  
     $self->{DEEPLINKLISTED} = shift;  
   
     # Now, we need to pre-process the map, by walking forward and backward      # Now, we need to pre-process the map, by walking forward and backward
     # over the parts of the map we're going to look at.      # over the parts of the map we're going to look at.
   
Line 3888  sub new { Line 3857  sub new {
  $finishResource, $self->{FILTER},   $finishResource, $self->{FILTER},
  $self->{ALREADY_SEEN},    $self->{ALREADY_SEEN}, 
  $self->{CONDITION},   $self->{CONDITION},
  $self->{FORCE_TOP},   $self->{FORCE_TOP});
                                                  undef,$self->{DEEPLINKLISTED});  
     }      }
   
     # Set up some bookkeeping information.      # Set up some bookkeeping information.
Line 4049  sub next { Line 4017  sub next {
     # That ends the main iterator logic. Now, do we want to recurse      # That ends the main iterator logic. Now, do we want to recurse
     # down this map (if this resource is a map)?      # down this map (if this resource is a map)?
     if ( ($self->{HERE}->is_sequence() || (!$closeAllPages && $self->{HERE}->is_page())) &&      if ( ($self->{HERE}->is_sequence() || (!$closeAllPages && $self->{HERE}->is_page())) &&
         (defined($self->{FILTER}->{$self->{HERE}->map_pc()}) xor $self->{CONDITION}) &&          (defined($self->{FILTER}->{$self->{HERE}->map_pc()}) xor $self->{CONDITION})) {
         ($env{'request.role.adv'} || !$self->{HERE}->randomout())) {  
         $self->{RECURSIVE_ITERATOR_FLAG} = 1;          $self->{RECURSIVE_ITERATOR_FLAG} = 1;
         my $firstResource = $self->{HERE}->map_start();          my $firstResource = $self->{HERE}->map_start();
         my $finishResource = $self->{HERE}->map_finish();          my $finishResource = $self->{HERE}->map_finish();
Line 4059  sub next { Line 4026  sub next {
                                               $finishResource, $self->{FILTER},                                                $finishResource, $self->{FILTER},
                                               $self->{ALREADY_SEEN},                                                $self->{ALREADY_SEEN},
       $self->{CONDITION},        $self->{CONDITION},
       $self->{FORCE_TOP},        $self->{FORCE_TOP});
                                               undef,$self->{DEEPLINKLISTED});  
     }      }
   
     # If this is a blank resource, don't actually return it.      # If this is a blank resource, don't actually return it.
     # Should you ever find you need it, make sure to add an option to the code      # Should you ever find you need it, make sure to add an option to the code
     #  that you can use; other things depend on this behavior.      #  that you can use; other things depend on this behavior.
     my $browsePriv = $self->{HERE}->browsePriv($noblockcheck,$self->{DEEPLINKLISTED});      my $browsePriv = $self->{HERE}->browsePriv($noblockcheck);
     if (!$self->{HERE}->src() ||       if (!$self->{HERE}->src() || 
         (!($browsePriv eq 'F') && !($browsePriv eq '2')) ) {          (!($browsePriv eq 'F') && !($browsePriv eq '2')) ) {
         return $self->next($closeAllPages);          return $self->next($closeAllPages);
Line 4403  sub new { Line 4369  sub new {
           
     # This is a speed optimization, to avoid calling symb() too often.      # This is a speed optimization, to avoid calling symb() too often.
     $self->{SYMB} = $self->symb();      $self->{SYMB} = $self->symb();
      
     return $self;      return $self;
 }  }
   
Line 4494  sub from { my $self=shift; return $self- Line 4460  sub from { my $self=shift; return $self-
 sub goesto { my $self=shift; return $self->navHash("goesto_", 1); }  sub goesto { my $self=shift; return $self->navHash("goesto_", 1); }
 sub kind { my $self=shift; return $self->navHash("kind_", 1); }  sub kind { my $self=shift; return $self->navHash("kind_", 1); }
 sub randomout { my $self=shift; return $self->navHash("randomout_", 1); }  sub randomout { my $self=shift; return $self->navHash("randomout_", 1); }
 sub deeplinkout { my $self=shift; return $self->navHash("deeplinkout_", 1); }  
 sub randompick {   sub randompick { 
     my $self = shift;      my $self = shift;
     my $randompick = $self->parmval('randompick');      my $randompick = $self->parmval('randompick');
Line 4530  sub enclosing_map_src { Line 4495  sub enclosing_map_src {
 }  }
 sub symb {  sub symb {
     my $self=shift;      my $self=shift;
     if (defined($self->{SYMB})) { return $self->{SYMB}; }      if (defined $self->{SYMB}) { return $self->{SYMB}; }
     (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;      (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;
     my $symbSrc = &Apache::lonnet::declutter($self->src());      my $symbSrc = &Apache::lonnet::declutter($self->src());
     my $symb = &Apache::lonnet::declutter($self->navHash('map_id_'.$first))       my $symb = &Apache::lonnet::declutter($self->navHash('map_id_'.$first)) 
Line 4734  sub is_sequence { Line 4699  sub is_sequence {
     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';
 }  }
 sub is_missing_map {  
     my $self=shift;  
     return $self->navHash("is_map_", 1) &&  
     $self->navHash("map_type_" . $self->map_pc()) eq 'none';  
 }  
 sub is_survey {  sub is_survey {
     my $self = shift();      my $self = shift();
     my $part = shift();      my $part = shift();
Line 5088  sub duedate { Line 5048  sub duedate {
     my $date;      my $date;
     my @interval=$self->parmval("interval", $part);      my @interval=$self->parmval("interval", $part);
     my $due_date=$self->parmval("duedate", $part);      my $due_date=$self->parmval("duedate", $part);
     if ($interval[0] =~ /(\d+)/) {      if ($interval[0] =~ /^(\d+)/) {
         my $timelimit = $1;          my $timelimit = $1;
         my $first_access=&Apache::lonnet::get_first_access($interval[1],          my $first_access=&Apache::lonnet::get_first_access($interval[1],
                                                           $self->{SYMB});                                                             $self->{SYMB});
  if (defined($first_access)) {   if (defined($first_access)) {
             my $interval = $first_access+$timelimit;              my $interval = $first_access+$timelimit;
     $date = (!$due_date || $interval < $due_date) ? $interval       $date = (!$due_date || $interval < $due_date) ? $interval 
Line 5166  sub weight { Line 5126  sub weight {
     my $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight',      my $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight',
                                 $self->{SYMB}, $self->{DOMAIN},                                  $self->{SYMB}, $self->{DOMAIN},
                                 $self->{USERNAME},                                  $self->{USERNAME},
                                 $self->{SECTION});                                  $env{'request.course.sec'});
     return $weight;      return $weight;
 }  }
 sub part_display {  sub part_display {
Line 5188  sub slot_control { Line 5148  sub slot_control {
     return ($useslots,$availablestudent,$available);      return ($useslots,$availablestudent,$available);
 }  }
 sub deeplink {  sub deeplink {
     my ($self,$caller,$action) = @_;      my ($self,$caller) = @_;
     my $deeplink = $self->parmval("deeplink");      my $value = $self->parmval("deeplink");
     if ($deeplink) {      if ($value) {
         my ($state,$others,$listed,$scope) = split(/,/,$deeplink);          my @deeplink = split(/,/,$value);
         if ($action eq 'getlisted') {          if ($caller eq 'sequence') {
             return $listed;              if ($deeplink[1] ne 'res') {
         }                  return;
         if ($env{'request.deeplink.login'}) {  
             my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};  
             my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};  
             my $deeplink_symb = &Apache::loncommon::deeplink_login_symb($cnum,$cdom);  
             if ($deeplink_symb) {  
                 my ($loginmap,$mapname);  
                 if ($deeplink_symb =~ /\.(page|sequence)$/) {  
                     $mapname = $self->enclosing_map_src();  
                     $loginmap = &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($deeplink_symb))[2]);  
                     return if ($mapname eq $loginmap);  
                 } else {  
                     return if ($deeplink_symb eq $self->symb());  
                     if (($scope eq 'map') || ($scope eq 'rec')) {  
                         $mapname = $self->enclosing_map_src();  
                         $loginmap = &Apache::lonnet::clutter((&Apache::lonnet::decode_symb($deeplink_symb))[0]);  
                         return if ($mapname eq $loginmap);  
                     }  
                 }  
                 if ($scope eq 'rec') {  
                     my $map_pc = $self->navHash('map_pc_'.$mapname);  
                     my @recurseup = split(/,/,$self->navHash('map_hierarchy_'.$map_pc));  
                     my $login_pc = $self->navHash('map_pc_'.$loginmap);  
                     return if (grep(/^\Q$login_pc\E$/,@recurseup));  
                 }  
             }              }
         }          }
         unless (($caller eq 'sequence') || ($state eq 'both')) {          return $deeplink[0];
             return $listed;  
         }  
     }      }
     return;      return;
 }  }
Line 5233  sub getReturnHash { Line 5167  sub getReturnHash {
     my $self = shift;      my $self = shift;
           
     if (!defined($self->{RETURN_HASH})) {      if (!defined($self->{RETURN_HASH})) {
         my %tmpHash  = &Apache::lonnet::restore($self->{SYMB},undef,$self->{DOMAIN},$self->{USERNAME});          #my %tmpHash  = &Apache::lonnet::restore($self->{SYMB},undef,$self->{DOMAIN},$self->{USERNAME});
         $self->{RETURN_HASH} = \%tmpHash;          #$self->{RETURN_HASH} = \%tmpHash;
           # When info is retrieved for several resources (as when rendering a directory),
           # it is much faster to use the user profile dump and avoid repeated lonnet requests
           # (especially since lonnet::currentdump is using Lond directly whenever possible,
           # and lonnet::restore is not at this point).
           $self->{NAV_MAP}->get_user_data();
           $self->{RETURN_HASH} = $self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}};
     }      }
 }         }       
   
Line 5634  The problem will be opened later. Line 5574  The problem will be opened later.
   
 Open and not yet due.  Open and not yet due.
   
   
 =item * B<PAST_DUE_ANSWER_LATER>:  =item * B<PAST_DUE_ANSWER_LATER>:
   
 The due date has passed, but the answer date has not yet arrived.  The due date has passed, but the answer date has not yet arrived.
Line 5646  The due date has passed and there is no Line 5587  The due date has passed and there is no
   
 The answer date is here.  The answer date is here.
   
 =item * B<NOTHING_SET>:  
   
 No dates have been set for this problem at all.  
   
 =item * B<PAST_DUE_ATMPT_ANS>:  
   
 The due date has passed, feedback is suppressed, the problem was attempted, and the answer date has not yet arrived.  
   
 =item * B<PAST_DUE_ATMPT_NOANS>:  
   
 The due date has passed, feedback is suppressed, the problem was attempted, and there is no answer opening date set.  
   
 =item * B<PAST_DUE_NO_ATMT_ANS>:  
   
 The due date has passed, feedback is suppressed, the problem was not attempted, and the answer date has not yet arrived.  
   
 =item * B<PAST_DUE_NO_ATMT_NOANS>:  
   
 The due date has passed, feedback is suppressed, the problem was not attempted, and there is no answer opening date set.  
   
 =item * B<NETWORK_FAILURE>:  =item * B<NETWORK_FAILURE>:
   
 The information is unknown due to network failure.  The information is unknown due to network failure.
Line 5681  sub PAST_DUE_NO_ANSWER     { return 2; } Line 5602  sub PAST_DUE_NO_ANSWER     { return 2; }
 sub PAST_DUE_ANSWER_LATER  { return 3; }  sub PAST_DUE_ANSWER_LATER  { return 3; }
 sub ANSWER_OPEN            { return 4; }  sub ANSWER_OPEN            { return 4; }
 sub NOTHING_SET            { return 5; }  sub NOTHING_SET            { return 5; }
 sub PAST_DUE_ATMPT_ANS     { return 6; }  
 sub PAST_DUE_ATMPT_NOANS   { return 7; }  
 sub PAST_DUE_NO_ATMT_ANS   { return 8; }  
 sub PAST_DUE_NO_ATMT_NOANS { return 9; }  
 sub NETWORK_FAILURE        { return 100; }  sub NETWORK_FAILURE        { return 100; }
   
 # getDateStatus gets the date status for a given problem part.   # getDateStatus gets the date status for a given problem part. 
Line 5888  set. Line 5805  set.
 The problem is past due, not considered correct, and an answer date in  The problem is past due, not considered correct, and an answer date in
 the future is set.  the future is set.
   
 =item * B<PAST_DUE_ATMPT_ANS>:  
   
 The problem is past due, feedback is suppressed, the problem was  
 attempted and an answer date in the future is set.  
   
 =item * B<PAST_DUE_ATMPT_NOANS>:  
   
 The problem is past due, feedback is suppressed, the problem was  
 attempted and no answer date is set.  
   
 =item * B<PAST_DUE_NO_ATMT_ANS>:  
   
 The problem is past due, feedback is suppressed, the problem was  
 not attempted and an answer date in the future is set.  
   
 =item * B<PAST_DUE_NO_ATMT_NOANS>:  
   
 The problem is past due, feedback is suppressed, the problem was  
 not attempted and no answer date is set.  
   
 =item * B<ANSWER_OPEN>:  =item * B<ANSWER_OPEN>:
   
 The problem is past due, not correct, and the answer is now available.  The problem is past due, not correct, and the answer is now available.
Line 5991  sub status { Line 5888  sub status {
     if ($completionStatus == CORRECT ||      if ($completionStatus == CORRECT ||
         $completionStatus == CORRECT_BY_OVERRIDE ||          $completionStatus == CORRECT_BY_OVERRIDE ||
         $completionStatus == CORRECT_BY_PASSBACK ) {          $completionStatus == CORRECT_BY_PASSBACK ) {
  if ( $suppressFeedback ) {   if ( $suppressFeedback ) { return ANSWER_SUBMITTED }
             if ($dateStatus == PAST_DUE_ANSWER_LATER ||  
                 $dateStatus == PAST_DUE_NO_ANSWER ) {  
                 if ($dateStatus == PAST_DUE_ANSWER_LATER) {  
                     return PAST_DUE_ATMPT_ANS;  
                 } else {  
                     return PAST_DUE_ATMPT_NOANS;  
                 }  
             } else {  
                 return ANSWER_SUBMITTED;  
             }  
         }  
  my $awarded=$self->awarded($part);   my $awarded=$self->awarded($part);
  if ($awarded < 1 && $awarded > 0) {   if ($awarded < 1 && $awarded > 0) {
             return PARTIALLY_CORRECT;              return PARTIALLY_CORRECT;
Line 6015  sub status { Line 5901  sub status {
     # If it's WRONG... and not open      # If it's WRONG... and not open
     if ( ($completionStatus == INCORRECT ||       if ( ($completionStatus == INCORRECT || 
   $completionStatus == INCORRECT_BY_OVERRIDE ||    $completionStatus == INCORRECT_BY_OVERRIDE ||
           $completionStatus == INCORRECT_BY_PASSBACK)    $completionStatus == INCORRECT_BY_PASSBACK)
  && (!$self->opendate($part) ||  $self->opendate($part) > time()) ) {   && (!$self->opendate($part) ||  $self->opendate($part) > time()) ) {
  return INCORRECT;   return INCORRECT;
     }      }
Line 6042  sub status { Line 5928  sub status {
   
     if ($dateStatus == PAST_DUE_ANSWER_LATER ||      if ($dateStatus == PAST_DUE_ANSWER_LATER ||
         $dateStatus == PAST_DUE_NO_ANSWER ) {          $dateStatus == PAST_DUE_NO_ANSWER ) {
         if ($suppressFeedback) {          return $suppressFeedback ? ANSWER_SUBMITTED : $dateStatus; 
             if ($completionStatus == NOT_ATTEMPTED) {  
                 if ($dateStatus == PAST_DUE_ANSWER_LATER) {  
                     return PAST_DUE_NO_ATMT_ANS;  
                 } else {  
                     return PAST_DUE_NO_ATMT_NOANS;  
                 }  
             } else {  
                 if ($dateStatus == PAST_DUE_ANSWER_LATER) {  
                     return PAST_DUE_ATMPT_ANS;  
                 } else {  
                     return PAST_DUE_ATMPT_NOANS;  
                 }  
             }  
         } else {  
             return $dateStatus;  
         }  
     }      }
   
     if ($dateStatus == ANSWER_OPEN) {      if ($dateStatus == ANSWER_OPEN) {
Line 6174  sub check_for_slot { Line 6044  sub check_for_slot {
         my $reservable = &Apache::lonnet::get_reservable_slots($cnum,$cdom,$env{'user.name'},          my $reservable = &Apache::lonnet::get_reservable_slots($cnum,$cdom,$env{'user.name'},
                                                                $env{'user.domain'});                                                                 $env{'user.domain'});
         if (ref($reservable) eq 'HASH') {          if (ref($reservable) eq 'HASH') {
               my ($map) = &Apache::lonnet::decode_symb($symb);
             if ((ref($reservable->{'now_order'}) eq 'ARRAY') && (ref($reservable->{'now'}) eq 'HASH')) {              if ((ref($reservable->{'now_order'}) eq 'ARRAY') && (ref($reservable->{'now'}) eq 'HASH')) {
                 foreach my $slot (reverse (@{$reservable->{'now_order'}})) {                  foreach my $slot (reverse (@{$reservable->{'now_order'}})) {
                     my $canuse;                      my $canuse;
                     if (($reservable->{'now'}{$slot}{'symb'} eq '') ||                      if ($reservable->{'now'}{$slot}{'symb'} eq '') {
                         ($reservable->{'now'}{$slot}{'symb'} eq $symb)) {  
                         $canuse = 1;                          $canuse = 1;
                       } else {
                           my %oksymbs;
                           my @slotsymbs = split(/\s*,\s*/,$reservable->{'now'}{$slot}{'symb'});
                           map { $oksymbs{$_} = 1; } @slotsymbs;
                           if ($oksymbs{$symb}) {
                               $canuse = 1;
                           } else {
                               foreach my $item (@slotsymbs) {
                                   if ($item =~ /\.(page|sequence)$/) {
                                       (undef,undef, my $sloturl) = &Apache::lonnet::decode_symb($item);
                                       if (($map ne '') && ($map eq $sloturl)) {
                                           $canuse = 1;
                                           last;
                                       }
                                   }
                               }
                           }
                     }                      }
                     if ($canuse) {                      if ($canuse) {
                         if ($checkedin) {                          if ($checkedin) {
Line 6200  sub check_for_slot { Line 6087  sub check_for_slot {
             if ((ref($reservable->{'future_order'}) eq 'ARRAY') && (ref($reservable->{'future'}) eq 'HASH')) {              if ((ref($reservable->{'future_order'}) eq 'ARRAY') && (ref($reservable->{'future'}) eq 'HASH')) {
                 foreach my $slot (@{$reservable->{'future_order'}}) {                  foreach my $slot (@{$reservable->{'future_order'}}) {
                     my $canuse;                      my $canuse;
                     if (($reservable->{'future'}{$slot}{'symb'} eq '') ||                      if ($reservable->{'future'}{$slot}{'symb'} eq '') {
                         ($reservable->{'future'}{$slot}{'symb'} eq $symb)) {                          $canuse = 1;
                       } elsif ($reservable->{'future'}{$slot}{'symb'} =~ /,/) {
                           my %oksymbs;
                           my @slotsymbs = split(/\s*,\s*/,$reservable->{'future'}{$slot}{'symb'});
                           map { $oksymbs{$_} = 1; } @slotsymbs;
                           if ($oksymbs{$symb}) {
                               $canuse = 1;
                           } else {
                               foreach my $item (@slotsymbs) {
                                   if ($item =~ /\.(page|sequence)$/) {
                                       (undef,undef, my $sloturl) = &Apache::lonnet::decode_symb($item);
                                       if (($map ne '') && ($map eq $sloturl)) {
                                           $canuse = 1;
                                           last;
                                       }
                                   }
                               }
                           }
                       } elsif ($reservable->{'future'}{$slot}{'symb'} eq $symb) {
                         $canuse = 1;                          $canuse = 1;
                     }                      }
                     if ($canuse) {                      if ($canuse) {
Line 6269  my %compositeToSimple = Line 6174  my %compositeToSimple =
       EXCUSED()               => CORRECT,        EXCUSED()               => CORRECT,
       PAST_DUE_NO_ANSWER()    => INCORRECT,        PAST_DUE_NO_ANSWER()    => INCORRECT,
       PAST_DUE_ANSWER_LATER() => INCORRECT,        PAST_DUE_ANSWER_LATER() => INCORRECT,
       PAST_DUE_ATMPT_ANS()    => ATTEMPTED,  
       PAST_DUE_ATMPT_NOANS()  => ATTEMPTED,  
       PAST_DUE_NO_ATMT_ANS()  => CLOSED,  
       PAST_DUE_NO_ATMT_NOANS() => CLOSED,  
       ANSWER_OPEN()           => INCORRECT,        ANSWER_OPEN()           => INCORRECT,
       OPEN_LATER()            => CLOSED,        OPEN_LATER()            => CLOSED,
       TRIES_LEFT()            => OPEN,        TRIES_LEFT()            => OPEN,
Line 6451  sub getPrevious { Line 6352  sub getPrevious {
 sub browsePriv {  sub browsePriv {
     my $self = shift;      my $self = shift;
     my $noblockcheck = shift;      my $noblockcheck = shift;
     my $deeplinklisted = shift;  
     if (defined($self->{BROWSE_PRIV})) {      if (defined($self->{BROWSE_PRIV})) {
         return $self->{BROWSE_PRIV};          return $self->{BROWSE_PRIV};
     }      }
     my ($nodeeplinkcheck,$nodeeplinkout);  
     if ($deeplinklisted) {  
         my $deeplink = $self->deeplink(undef,'getlisted');  
         if (($deeplink) && ($deeplink ne 'absent')) {  
             $nodeeplinkcheck = 1;  
         }  
         $nodeeplinkout = 1;  
     }  
     $self->{BROWSE_PRIV} = &Apache::lonnet::allowed('bre',$self->src(),      $self->{BROWSE_PRIV} = &Apache::lonnet::allowed('bre',$self->src(),
     $self->{SYMB},undef,      $self->{SYMB},undef,
                                                     undef,$noblockcheck,                                                      undef,$noblockcheck);
                                                     undef,$nodeeplinkcheck,  
                                                     $nodeeplinkout);  
 }  }
   
 =pod  =pod

Removed from v.1.509.2.14.2.12  
changed lines
  Added in v.1.548


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