Diff for /loncom/interface/lonnavmaps.pm between versions 1.508 and 1.509.2.14.2.6

version 1.508, 2015/06/19 15:36:53 version 1.509.2.14.2.6, 2023/01/20 22:45:51
Line 342  user into thinking that if the sequence Line 342  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<suppressNavmaps>: default: false  =item * B<suppressNavmap>: default: false
   
 If true, will not display Navigate Content resources.   If true, will not display Navigate Content resources. 
   
Line 486  use Apache::lonlocal; Line 486  use Apache::lonlocal;
 use Apache::lonnet;  use Apache::lonnet;
 use Apache::lonmap;  use Apache::lonmap;
   
 use POSIX qw (floor strftime);  use POSIX qw (ceil floor strftime);
 use Time::HiRes qw( gettimeofday tv_interval );  use Time::HiRes qw( gettimeofday tv_interval );
 use LONCAPA;  use LONCAPA;
 use DateTime();  use DateTime();
Line 577  sub getLinkForResource { Line 577  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; }  }
  $anchor=&escape($anchor->shown_symb());   if ($anchor->encrypted() && !&advancedUser()) {
       $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 624  sub getDescription { Line 628  sub getDescription {
     if ($status == $res->OPEN_LATER) {      if ($status == $res->OPEN_LATER) {
         return &mt("Open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($open,'start'),$res->symb(),'opendate',$part));          return &mt("Open [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($open,'start'),$res->symb(),'opendate',$part));
     }      }
       my $slotinfo;
     if ($res->simpleStatus($part) == $res->OPEN) {      if ($res->simpleStatus($part) == $res->OPEN) {
         unless (&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) {          unless (&Apache::lonnet::allowed('mgr',$env{'request.course.id'})) {
             my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);              my ($slot_status,$slot_time,$slot_name)=$res->check_for_slot($part);
               my $slotmsg;
             if ($slot_status == $res->UNKNOWN) {              if ($slot_status == $res->UNKNOWN) {
                 return &mt('Reservation status unknown');                  $slotmsg = &mt('Reservation status unknown');
             } elsif ($slot_status == $res->RESERVED) {              } elsif ($slot_status == $res->RESERVED) {
                 return &mt('Reserved - ends [_1]',                  $slotmsg = &mt('Reserved - ends [_1]',
                            timeToHumanString($slot_time,'end'));                             timeToHumanString($slot_time,'end'));
             } elsif ($slot_status == $res->RESERVED_LOCATION) {              } elsif ($slot_status == $res->RESERVED_LOCATION) {
                 return &mt('Reserved - specific location(s) - ends [_1]',                  $slotmsg = &mt('Reserved - specific location(s) - ends [_1]',
                            timeToHumanString($slot_time,'end'));                             timeToHumanString($slot_time,'end'));
             } elsif ($slot_status == $res->RESERVED_LATER) {              } elsif ($slot_status == $res->RESERVED_LATER) {
                 return &mt('Reserved - next open [_1]',                  $slotmsg = &mt('Reserved - next open [_1]',
                            timeToHumanString($slot_time,'start'));                             timeToHumanString($slot_time,'start'));
             } elsif ($slot_status == $res->RESERVABLE) {              } elsif ($slot_status == $res->RESERVABLE) {
                 return &mt('Reservable, reservations close [_1]',                  $slotmsg = &mt('Reservable, reservations close [_1]',
                              timeToHumanString($slot_time,'end'));
               } elsif ($slot_status == $res->NEEDS_CHECKIN) {
                   $slotmsg = &mt('Reserved, check-in needed - ends [_1]',
                            timeToHumanString($slot_time,'end'));                             timeToHumanString($slot_time,'end'));
             } elsif ($slot_status == $res->RESERVABLE_LATER) {              } elsif ($slot_status == $res->RESERVABLE_LATER) {
                 return &mt('Reservable, reservations open [_1]',                  $slotmsg = &mt('Reservable, reservations open [_1]',
                            timeToHumanString($slot_time,'start'));                             timeToHumanString($slot_time,'start'));
             } elsif ($slot_status == $res->NOT_IN_A_SLOT) {              } elsif ($slot_status == $res->NOT_IN_A_SLOT) {
                 return &mt('Reserve a time/place to work');                  $slotmsg = &mt('Reserve a time/place to work');
             } elsif ($slot_status == $res->NOTRESERVABLE) {              } elsif ($slot_status == $res->NOTRESERVABLE) {
                 return &mt('Reservation not available');                  $slotmsg = &mt('Reservation not available');
             } elsif ($slot_status == $res->WAITING_FOR_GRADE) {              } elsif ($slot_status == $res->WAITING_FOR_GRADE) {
                 return &mt('Submission in grading queue');                  $slotmsg = &mt('Submission in grading queue');
               }
               if ($slotmsg) {
                   if ($res->is_task() || !$due) {
                        return $slotmsg;
                   }
                   $slotinfo = ('&nbsp;' x 2).'('.$slotmsg.')';
             }              }
         }          }
     }      }
     if ($status == $res->OPEN) {      if ($status == $res->OPEN) {
         if ($due) {          if ($due) {
     if ($res->is_practice()) {      if ($res->is_practice()) {
  return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'duedate',$part));   return &mt("Closes [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'start'),$res->symb(),'duedate',$part)).$slotinfo;
     } else {      } else {
  return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part));   return &mt("Due [_1]",&Apache::lonhtmlcommon::direct_parm_link(&timeToHumanString($due,'end'),$res->symb(),'duedate',$part)).$slotinfo;
     }      }
         } else {          } else {
             return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part);              return &Apache::lonhtmlcommon::direct_parm_link(&mt("Open, no due date"),$res->symb(),'duedate',$part).$slotinfo;
         }          }
     }      }
     if ($status == $res->PAST_DUE_ANSWER_LATER) {      if ($status == $res->PAST_DUE_ANSWER_LATER) {
Line 907  sub render_resource { Line 922  sub render_resource {
     my $nonLinkedText = ''; # stuff after resource title not in link      my $nonLinkedText = ''; # stuff after resource title not in link
   
     my $link = $params->{"resourceLink"};      my $link = $params->{"resourceLink"};
       if ($resource->ext()) {
           $link =~ s/\#.+(\?)/$1/g;
       }
   
     #  The URL part is not escaped at this point, but the symb is...       #  The URL part is not escaped at this point, but the symb is... 
   
Line 927  sub render_resource { Line 945  sub render_resource {
     # links to open and close the folder      # links to open and close the folder
   
     my $whitespace = $location.'/whitespace_21.gif';      my $whitespace = $location.'/whitespace_21.gif';
     my $linkopen = "<img src='$whitespace' alt='' />"."<a href=\"$link\">";      my ($nomodal,$linkopen,$linkclose);
     my $linkclose = "</a>";      unless ($resource->is_map() || $params->{'resource_nolink'}) {
           $linkopen = "<img src='$whitespace' alt='' />";
           $linkclose = "</a>";
           if (($params->{'modalLink'}) && (!$resource->is_sequence())) {
               if ($link =~m{^(?:|/adm/wrapper)/ext/([^#]+)}) {
                   my $exturl = $1;
                   if (($ENV{'SERVER_PORT'} == 443) && ($exturl !~ /^https:/)) {
                       $nomodal = 1;
                   }
               } elsif (($link eq "/public/$LONCAPA::match_domain/$LONCAPA::match_courseid/syllabus") &&
                        ($env{'request.course.id'}) && ($ENV{'SERVER_PORT'} == 443) &&
                        ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) {
                   $nomodal = 1;
               }
               my $esclink = &js_escape($link);
               if ($nomodal) {
                   $linkopen .= "<a href=\"#\" onclick=\"javascript:window.open('$esclink','resourcepreview','height=400,width=500,scrollbars=1,resizable=1,menubar=0,location=1'); return false;\" />";
               } else {
                   $linkopen .= "<a href=\"$link\" onclick=\"javascript:openMyModal('$esclink',600,500,'yes','true'); return false;\">";
               }
           } else {
               $linkopen .= "<a href=\"$link\">";
           }
       }
   
     # Default icon: unknown page      # Default icon: unknown page
     my $icon = "<img class=\"LC_contentImage\" src='$location/unknown.gif' alt='' />";      my $icon = "<img class=\"LC_contentImage\" src='$location/unknown.gif' alt='' />";
Line 956  sub render_resource { Line 997  sub render_resource {
         if ($it->{CONDITION}) {          if ($it->{CONDITION}) {
             $nowOpen = !$nowOpen;              $nowOpen = !$nowOpen;
         }          }
    my $folderType;
  my $folderType = $resource->is_sequence() ? 'folder' : 'page';   if (&advancedUser() && $resource->is_missing_map()) {
       $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 976  sub render_resource { Line 1021  sub render_resource {
                 '&amp;jump=' .                  '&amp;jump=' .
                 &escape($resource->symb()) .                   &escape($resource->symb()) . 
                 "&amp;folderManip=1\">";                  "&amp;folderManip=1\">";
               $linkclose = '</a>';
         } else {          } else {
             # Don't allow users to manipulate folder              # Don't allow users to manipulate folder
             $icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif';              $icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif';
             $icon = "<img class=\"LC_space\" src='$whitespace' alt='' />"."<img class=\"LC_contentImage\" src='$location/$icon' alt=\"".($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" />";              $icon = "<img class=\"LC_space\" src='$whitespace' alt='' />"."<img class=\"LC_contentImage\" src='$location/$icon' alt=\"".($nowOpen ? &mt('Open Folder') : &mt('Close Folder')).' '.$title."\" />";
               if ($params->{'caller'} eq 'sequence') {
             $linkopen = "";                  $linkopen = "<a href=\"$link\">";
             $linkclose = "";                  $linkclose = '</a>';
               } else {
                   $linkopen = "";
                   $linkclose = "";
               }
         }          }
         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'}))) &&
             ($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 995  sub render_resource { Line 1045  sub render_resource {
                          '</a>';                           '</a>';
             }              }
         }          }
     }          if ($params->{'mapHidden'} || $resource->randomout()) {
               $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';
     if ($resource->randomout()) {          } elsif ($params->{'mapUnlisted'}) {
         $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';              $nonLinkedText .= ' <span class="LC_warning">('.&mt('unlisted').')</span> ';
           } elsif ($params->{'mapHiddenDeepLink'} || $resource->deeplinkout()) {
               $nonLinkedText .= ' <span class="LC_warning">('.&mt('not shown').')</span> ';
           }
       } else {
           if ($resource->randomout()) {
               $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';
           } elsif ($resource->deeplinkout()) {
               $nonLinkedText .= ' <span class="LC_warning">('.&mt('not shown').')</span> ';
           } else {
               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()) {
         $nonLinkedText .= ' <span class="LC_info">('.&mt('conditionally hidden').')</span> ';          $nonLinkedText .= ' <span class="LC_info">('.&mt('conditionally hidden').')</span> ';
Line 1048  sub render_resource { Line 1121  sub render_resource {
     }      }
   
     if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) {      if (!$params->{'resource_nolink'} && !$resource->is_sequence() && !$resource->is_empty_sequence) {
         $result .= "$curMarkerBegin<a href=\"$link\">$title$partLabel</a>$curMarkerEnd$editmapLink$nonLinkedText</td>";          $linkclose = '</a>';
     } else {          if ($params->{'modalLink'}) {
         $result .= "$curMarkerBegin$linkopen$title$partLabel</a>$curMarkerEnd$editmapLink$nonLinkedText</td>";              my $esclink = &js_escape($link);
               if ($nomodal) {
                   $linkopen = "<a href=\"#\" onclick=\"javascript:window.open('$esclink','resourcepreview','height=400,width=500,scrollbars=1,resizable=1,menubar=0,location=1'); return false;\" />";
               } else {
                   $linkopen = "<a href=\"$link\" onclick=\"javascript:openMyModal('$esclink',600,500,'yes','true'); return false;\">";
               }
           } else {
               $linkopen = "<a href=\"$link\">";
           }
     }      }
       $result .= "$curMarkerBegin$linkopen$title$partLabel$linkclose$curMarkerEnd$editmapLink$nonLinkedText</td>";
   
     return $result;      return $result;
 }  }
Line 1313  sub render { Line 1395  sub render {
         # 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 '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'})) {      if (!$env{'form.folderManip'} && !defined($args->{'iterator'}) && !$args->{'nocurrloc'}) {
         # 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 1342  sub render { Line 1458  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') { 
             $here = $jump = &Apache::lonnet::symbread($currenturl);                  $here = $jump = &Apache::lonnet::symbread($currenturl);
               }
  }   }
  if ($here eq '') {   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 1359  sub render { Line 1476  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) {          while ($here && ($curRes = $mapIterator->next()) && !$found && !$here_is_navmaps) {
             if (ref($curRes) && $curRes->symb() eq $here) {              if (ref($curRes) && $curRes->symb() eq $here) {
                 my $mapStack = $mapIterator->getStack();                  my $mapStack = $mapIterator->getStack();
                                   
Line 1405  sub render { Line 1526  sub render {
         if ($args->{'iterator_map'}) {          if ($args->{'iterator_map'}) {
             my $map = $args->{'iterator_map'};              my $map = $args->{'iterator_map'};
             $map = $navmap->getResourceByUrl($map);              $map = $navmap->getResourceByUrl($map);
             my $firstResource = $map->map_start();              if (ref($map)) {
             my $finishResource = $map->map_finish();                  my $firstResource = $map->map_start();
                   my $finishResource = $map->map_finish();
             $args->{'iterator'} = $it = $navmap->getIterator($firstResource, $finishResource, $filterHash, $condition);                  $args->{'iterator'} = $it = $navmap->getIterator($firstResource, $finishResource, $filterHash, $condition);
               } else {
                   return;
               }
         } else {          } else {
             $args->{'iterator'} = $it = $navmap->getIterator(undef, undef, $filterHash, $condition,undef,$args->{'include_top_level_map'});              $args->{'iterator'} = $it = $navmap->getIterator(undef, undef, $filterHash, $condition,undef,$args->{'include_top_level_map'});
         }          }
Line 1477  sub render { Line 1601  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',
Line 1503  sub render { Line 1624  sub render {
  <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 1526  END Line 1644  END
  }   }
  $result.='</form>';   $result.='</form>';
     }      }
     if (($args->{'caller'} eq 'navmapsdisplay') &&      if (($args->{'caller'} eq 'navmapsdisplay') && ($env{'request.course.id'})) {
         (&Apache::lonnet::allowed('mdc',$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") {
             &add_linkitem($args->{'linkitems'},'edittoplevel',              if ((&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) ||
                           "javascript:gocmd('/adm/coursedocs','editdocs');",                  (&Apache::lonnet::allowed('cev',$env{'request.course.id'}))) {
                           'Content Editor');                  &add_linkitem($args->{'linkitems'},'edittoplevel',
                                 "javascript:gocmd('/adm/coursedocs','editdocs');",
                                 'Content Editor');
               }
               if ($counter) {
                   &add_linkitem($args->{'linkitems'},'printout',
                                 "javascript:gopost('/adm/printout','/adm/navmaps');",
                                 'Prepare a printable document');
               }
         }          }
     }      }
   
Line 1573  END Line 1698  END
     $args->{'indentString'} = setDefault($args->{'indentString'}, "<img src='$location' alt='' />");      $args->{'indentString'} = setDefault($args->{'indentString'}, "<img src='$location' alt='' />");
     $args->{'displayedHereMarker'} = 0;      $args->{'displayedHereMarker'} = 0;
   
     # If we're suppressing empty sequences, look for them here. Use DFS for speed,      # If we're suppressing empty sequences, look for them here.
     # since structure actually doesn't matter, except what map has what resources.      # We also do this even if $args->{'suppressEmptySequences'}
     if ($args->{'suppressEmptySequences'}) {      # is not true, so we can hide empty sequences for which the
         my $dfsit = Apache::lonnavmaps::DFSiterator->new($navmap,      # hiddenresource parameter is set to yes (at map level), or
                                                          $it->{FIRST_RESOURCE},      # mark as hidden for users who have $userCanSeeHidden.
                                                          $it->{FINISH_RESOURCE},      # Use DFS for speed, since structure actually doesn't matter,
                                                          {}, undef, 1);      # except what map has what resources.
         my $depth = 0;  
         $dfsit->next();      my $dfsit = Apache::lonnavmaps::DFSiterator->new($navmap,
         my $curRes = $dfsit->next();                                                       $it->{FIRST_RESOURCE},
         while ($depth > -1) {                                                       $it->{FINISH_RESOURCE},
             if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }                                                       {}, undef, 1);
             if ($curRes == $dfsit->END_MAP()) { $depth--; }  
       my $depth = 0;
             if (ref($curRes)) {       $dfsit->next();
                 # Parallel pre-processing: Do sequences have non-filtered-out children?      my $curRes = $dfsit->next();
                 if ($curRes->is_map()) {      while ($depth > -1) {
                     $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;          if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }
                     # Sequences themselves do not count as visible children,          if ($curRes == $dfsit->END_MAP()) { $depth--; }
                     # unless those sequences also have visible children.  
                     # This means if a sequence appears, there's a "promise"          if (ref($curRes)) {
                     # that there's something under it if you open it, somewhere.              # Parallel pre-processing: Do sequences have non-filtered-out children?
                 } else {              if ($curRes->is_map()) {
                     # Not a sequence: if it's filtered, ignore it, otherwise                  $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;
                     # rise up the stack and mark the sequences as having children                  # Sequences themselves do not count as visible children,
                     if (&$filterFunc($curRes)) {                  # unless those sequences also have visible children.
                         for my $sequence (@{$dfsit->getStack()}) {                  # This means if a sequence appears, there's a "promise"
                             $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;                  # that there's something under it if you open it, somewhere.
                         }              } elsif ($curRes->src()) {
                   # Not a sequence: if it's filtered, ignore it, otherwise
                   # rise up the stack and mark the sequences as having children
                   if (&$filterFunc($curRes)) {
                       for my $sequence (@{$dfsit->getStack()}) {
                           $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;
                     }                      }
                 }                  }
             }              }
         } continue {  
             $curRes = $dfsit->next();  
         }          }
       } continue {
           $curRes = $dfsit->next();
     }      }
   
     my $displayedJumpMarker = 0;      my $displayedJumpMarker = 0;
Line 1665  END Line 1795  END
  undef($args->{'sort'});   undef($args->{'sort'});
     }      }
   
       # Determine if page will be served with https in case
       # it contains a syllabus which uses an external URL
       # which points at an http site.
   
       my ($is_ssl,$cdom,$cnum,$hostname);
       if ($ENV{'SERVER_PORT'} == 443) {
           $is_ssl = 1;
           if ($r) {
               $hostname = $r->hostname();
           } else {
               $hostname = $ENV{'SERVER_NAME'};
           }
       }
       if ($env{'request.course.id'}) {
           $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
           $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
       }
   
       my $inhibitmenu;
       if ($args->{'modalLink'}) {
           $inhibitmenu = '&amp;inhibitmenu=yes';
       }
   
     while (1) {      while (1) {
  if ($args->{'sort'}) {   if ($args->{'sort'}) {
Line 1700  END Line 1852  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
         if ($curRes->is_map() && $args->{'suppressEmptySequences'} &&          $args->{'mapHidden'} = 0;
             !$curRes->{DATA}->{HAS_VISIBLE_CHILDREN}) {          $args->{'mapUnlisted'} = 0;
             next;          $args->{'mapHiddenDeepLink'} = 0;
           if (($curRes->is_map()) && (!$curRes->{DATA}->{HAS_VISIBLE_CHILDREN})) {
               if ($args->{'suppressEmptySequences'}) {
                   next;
               } else {
                   my $mapname = &Apache::lonnet::declutter($curRes->src());
                   $mapname = &Apache::lonnet::deversion($mapname);
                   if (lc($navmap->get_mapparam(undef,$mapname,"0.hiddenresource")) eq 'yes') {
                       if ($userCanSeeHidden) {
                           $args->{'mapHidden'} = 1;
                       } else {
                           next;
                       }
                   } elsif ($curRes->deeplinkout) {
                       if ($userCanSeeHidden) {
                           $args->{'mapHiddenDeepLink'} = 1;
                       } else {
                           next;
                       }
                   } else {
                       my $deeplink = $navmap->get_mapparam(undef,$mapname,"0.deeplink");
                       my ($state,$others,$listed) = split(/,/,$deeplink);
                       if (($listed eq 'absent') || ($listed eq 'grades')) {
                           if ($userCanSeeHidden) {
                               $args->{'mapUnlisted'} = 1;
                           } else {
                               next;
                           }
                       }
                   }
               }
         }          }
   
         # If we're suppressing navmaps and this is a navmap, continue on          # If we're suppressing navmaps and this is a navmap, continue on
Line 1763  END Line 1945  END
     $args->{'condensed'} = 1;      $args->{'condensed'} = 1;
  }   }
             }              }
         }           }
           # 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
           # the resource is within the map.
           if ((!$curRes->deeplink($args->{'caller'})) ||
               ($curRes->deeplink($args->{'caller'}) eq 'full') || &advancedUser()) {
               $args->{'resource_nolink'} = 0;
           } else {
               $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) {
Line 1786  END Line 1977  END
  $stack=$it->getStack();   $stack=$it->getStack();
     }      }
     ($src,$symb,$anchor)=getLinkForResource($stack);      ($src,$symb,$anchor)=getLinkForResource($stack);
               my $srcHasQuestion = $src =~ /\?/;
               if ($env{'request.course.id'}) {
                   if (($is_ssl) && ($src =~ m{^\Q/public/$cdom/$cnum/syllabus\E($|\?)}) &&
                       ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) {
                       unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) {
                           if ($hostname ne '') {
                               $src = 'http://'.$hostname.$src;
                           }
                           $src .= ($srcHasQuestion? '&amp;' : '?') . 'usehttp=1';
                           $srcHasQuestion = 1;
                       }
                   } elsif (($is_ssl) && ($src =~ m{^\Q/adm/wrapper/ext/\E(?!https:)})) {
                       unless ((&Apache::lonnet::uses_sts()) || (&Apache::lonnet::waf_allssl($hostname))) {
                           if ($hostname ne '') {
                               $src = 'http://'.$hostname.$src;
                           }
                           $src .= ($srcHasQuestion? '&amp;' : '?') . 'usehttp=1';
                           $srcHasQuestion = 1;
                       }
                   }
               }
     if (defined($anchor)) { $anchor='#'.$anchor; }      if (defined($anchor)) { $anchor='#'.$anchor; }
     my $srcHasQuestion = $src =~ /\?/;              if (($args->{'caller'} eq 'sequence') && ($curRes->is_map())) {
     $args->{"resourceLink"} = $src.                  $args->{"resourceLink"} = $src.($srcHasQuestion?'&amp;':'?') .'navmap=1';
  ($srcHasQuestion?'&amp;':'?') .              } else {
  'symb=' . &escape($symb).$anchor;          $args->{"resourceLink"} = $src.
       ($srcHasQuestion?'&amp;':'?') .
       '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 1818  END Line 2033  END
                     $currentJumpDelta) {                      $currentJumpDelta) {
                     # Jam the anchor after the <td> tag;                      # Jam the anchor after the <td> tag;
                     # necessary for valid HTML (which Mozilla requires)                      # necessary for valid HTML (which Mozilla requires)
                     $colHTML =~ s/\>/\>\<a name="curloc" \/\>/;                      $colHTML =~ s/\>/\>\<a name="curloc" \>\<\/a\>/;
                     $displayedJumpMarker = 1;                      $displayedJumpMarker = 1;
                 }                  }
                 $result .= $colHTML . "\n";                  $result .= $colHTML . "\n";
Line 1892  sub show_linkitems_toolbar { Line 2107  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','edittoplevel');                               'changefolder','clearbubbles','printout','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 2204  sub generate_email_discuss_status { Line 2419  sub generate_email_discuss_status {
     foreach my $msgid (@keys) {      foreach my $msgid (@keys) {
  if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) {   if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) {
             my ($sendtime,$shortsubj,$fromname,$fromdomain,$status,$fromcid,              my ($sendtime,$shortsubj,$fromname,$fromdomain,$status,$fromcid,
                 $symb,$error) = &Apache::lonmsg::unpackmsgid($msgid);                  $symb,$error) = &Apache::lonmsg::unpackmsgid(&LONCAPA::escape($msgid));
             &Apache::lonenc::check_decrypt(\$symb);               &Apache::lonenc::check_decrypt(\$symb); 
             if (($fromcid ne '') && ($fromcid ne $cid)) {              if (($fromcid ne '') && ($fromcid ne $cid)) {
                 next;                  next;
Line 2305  sub getIterator { Line 2520  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 2701  sub parmval_real { Line 2916  sub parmval_real {
     if (defined($pack_def)) { return [$pack_def,'resource']; }      if (defined($pack_def)) { return [$pack_def,'resource']; }
     return [''];      return [''];
 }  }
   
   sub recurseup_maps {
       my ($self,$mapname) = @_;
       my @recurseup;
       if ($mapname) {
           my $res = $self->getResourceByUrl($mapname);
           if (ref($res)) {
               my @pcs = split(/,/,$res->map_hierarchy());
               shift(@pcs);
               if (@pcs) {
                   @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->src()); } reverse(@pcs);
               }
           }
       }
       return @recurseup;
   }
   
   sub recursed_crumbs {
       my ($self,$mapurl,$restitle) = @_;
       my (@revmapinfo,@revmapres);
       my $mapres = $self->getResourceByUrl($mapurl);
       if (ref($mapres)) {
           @revmapres = map { $self->getByMapPc($_); } split(/,/,$mapres->map_breadcrumbs());
           shift(@revmapres);
       }
       my $allowedlength = 60;
       my $minlength = 5;
       my $allowedtitle = 30;
       if (($env{'environment.icons'} eq 'iconsonly') && (!$env{'browser.mobile'})) {
           $allowedlength = 100;
           $allowedtitle = 70;
       }
       if (length($restitle) > $allowedtitle) {
           $restitle = &truncate_crumb_text($restitle,$allowedtitle);
       }
       my $totallength = length($restitle);
       my @links;
   
       foreach my $map (@revmapres) {
           my $pc = $map->map_pc();
           next if ((!$pc) || ($pc == 1));
           push(@links,$map);
           my $text = $map->title();
           if ($text eq '') {
               $text = '...';
           }
           push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $text,'no_mt' => 1,});
           $totallength += length($text);
       }
       my $numlinks = scalar(@links);
       if ($numlinks) {
           if ($totallength - $allowedlength > 0) {
               my $available = $allowedlength - length($restitle);
               my $avg = POSIX::ceil($available/$numlinks);
               if ($avg < $minlength) {
                   $avg = $minlength;
               }
               @revmapinfo = ();
               foreach my $map (@links) {
                   my $title = $map->title();
                   if ($title eq '') {
                       $title = '...';
                   }
                   my $showntitle = &truncate_crumb_text($title,$avg);
                   if ($showntitle ne '') {
                       push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $showntitle,'no_mt' => 1,});
                   }
               }
           }
       }
       if ($restitle ne '') {
           push(@revmapinfo,{'text' => $restitle, 'no_mt' => 1});
       }
       return @revmapinfo;
   }
   
   sub truncate_crumb_text {
       my ($title,$limit) = @_;
       my $showntitle = '';
       if (length($title) > $limit) {
           my @words = split(/\b\s*/,$title);
           if (@words == 1) {
               $showntitle = substr($title,0,$limit).' ...';
           } else {
               my $linklength = 0;
               my $num = 0;
               foreach my $word (@words) {
                   $linklength += 1+length($word);
                   if ($word eq '-') {
                       $showntitle =~ s/ $//;
                       $showntitle .= $word;
                   } elsif ($linklength > $limit) {
                       if ($num < @words) {
                           $showntitle .= $word.' ...';
                           last;
                       } else {
                           $showntitle .= $word;
                       }
                   } else {
                       $showntitle .= $word.' ';
                   }
               }
               $showntitle =~ s/ $//;
           }
           return $showntitle;
       } else {
           return $title;
       }
   }
   
 #  #
 #  Determines the open/close dates for printing a map that  #  Determines the open/close dates for printing a map that
 #  encloses a resource.  #  encloses a resource.
Line 2712  sub map_printdates { Line 3037  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);
 }  }
   
 sub get_mapparam {  sub get_mapparam {
     my ($self, $symb, $what) = @_;      my ($self, $symb, $mapname, $what) = @_;
   
     # Ensure the course option hash is populated:      # Ensure the course option hash is populated:
   
Line 2739  sub get_mapparam { Line 3064  sub get_mapparam {
     my $uname=$self->{USERNAME};      my $uname=$self->{USERNAME};
     my $udom=$self->{DOMAIN};      my $udom=$self->{DOMAIN};
   
     unless ($symb) { return ['']; }      unless ($symb || $mapname) { return; }
     my $result='';      my $result='';
       my ($recursed,@recurseup);
   
     # Figure out which map we are in.      # Figure out which map we are in.
   
     my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);      if ($symb && !$mapname) {
     $mapname = &Apache::lonnet::deversion($mapname);          my ($id,$fn);
           ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
           $mapname = &Apache::lonnet::deversion($mapname);
       }
   
     my $rwhat=$what;      my $rwhat=$what;
     $what=~s/^parameter\_//;      $what=~s/^parameter\_//;
Line 2779  sub get_mapparam { Line 3106  sub get_mapparam {
  if (defined($$useropt{$courselevel})) {   if (defined($$useropt{$courselevel})) {
     return $$useropt{$courselevel};      return $$useropt{$courselevel};
  }   }
           if ($what =~ /\.(encrypturl|hiddenresource)$/) {
               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};
                       }
                   }
               }
           }
     }      }
   
     # Check course -- group      # Check course -- group
Line 2789  sub get_mapparam { Line 3130  sub get_mapparam {
  if (defined($$courseopt{$grplevel})) {   if (defined($$courseopt{$grplevel})) {
     return $$courseopt{$grplevel};      return $$courseopt{$grplevel};
  }   }
           if ($what =~ /\.(encrypturl|hiddenresource)$/) {
               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};
                       }
                   }
               }
           }
     }      }
   
     # Check course -- section      # Check course -- section
Line 2801  sub get_mapparam { Line 3156  sub get_mapparam {
  if (defined($$courseopt{$seclevel})) {   if (defined($$courseopt{$seclevel})) {
     return $$courseopt{$seclevel};      return $$courseopt{$seclevel};
  }   }
           if ($what =~ /\.(encrypturl|hiddenresource)$/) {
               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};
                       }
                   }
               }
           }
     }      }
     # Check the map parameters themselves:      # Check the map parameters themselves:
   
     my $thisparm = $$parmhash{$symbparm};      if ($symb) {
     if (defined($thisparm)) {          my $symbparm=$symb.'.'.$what;
  return $thisparm;          my $thisparm = $$parmhash{$symbparm};
           if (defined($thisparm)) {
               return $thisparm;
           }
     }      }
   
   
Line 2816  sub get_mapparam { Line 3188  sub get_mapparam {
  if (defined($$courseopt{$courselevel})) {   if (defined($$courseopt{$courselevel})) {
     return $$courseopt{$courselevel};      return $$courseopt{$courselevel};
  }   }
           if ($what =~ /\.(encrypturl|hiddenresource)$/) {
               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};
                       }
                   }
               }
           }
     }      }
     return undef; # Unefined if we got here.      return undef; # Unefined if we got here.
 }  }
Line 2871  sub getcourseparam { Line 3257  sub getcourseparam {
     #       # 
     # We want the course level stuff from the way      # We want the course level stuff from the way
     # parmval_real operates       # parmval_real operates 
     # TODO: Fator some of this stuff out of      # TODO: Factor some of this stuff out of
     # both parmval_real and here      # both parmval_real and here
     #      #
     my $courselevel = $cid . '.' .  $what;      my $courselevel = $cid . '.' .  $what;
Line 2888  sub getcourseparam { Line 3274  sub getcourseparam {
     }      }
     # Try for the group's course level option:      # Try for the group's course level option:
   
     if ($uname ne '' and defined($courseopt)) {      if ($cgroup ne '' and defined($courseopt)) {
  if (defined($$courseopt{$grplevel})) {   if (defined($$courseopt{$grplevel})) {
     return $$courseopt{$grplevel};      return $$courseopt{$grplevel};
  }   }
Line 2896  sub getcourseparam { Line 3282  sub getcourseparam {
   
     #  Try for section level parameters:      #  Try for section level parameters:
   
     if ($csec and defined($courseopt)) {      if ($csec ne '' and defined($courseopt)) {
  if (defined($$courseopt{$seclevel})) {   if (defined($$courseopt{$seclevel})) {
     return $$courseopt{$seclevel};      return $$courseopt{$seclevel};
  }   }
     }      }
     # Try for 'additional' course parameterse:      # Try for 'additional' course parameters:
   
     if (defined($courseopt)) {      if (defined($courseopt)) {
  if (defined($$courseopt{$courselevel})) {   if (defined($$courseopt{$courselevel})) {
Line 3108  getIterator behaves as follows: Line 3494  getIterator behaves as follows:
   
 =over 4  =over 4
   
 =item * B<getIterator>(firstResource, finishResource, filterHash, condition, forceTop, returnTopMap):  =item * B<getIterator>(firstResource, finishResource, filterHash, condition, forceTop, returnTopMap, $deeplinklisted):
   
 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 3125  that is not just a single, 'redirecting' Line 3511  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.  before anything else. deeplinklisted if true (default false), will
   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 3262  sub new { Line 3651  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 3353  sub new { Line 3746  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 3513  sub next { Line 3907  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 3522  sub next { Line 3917  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);      my $browsePriv = $self->{HERE}->browsePriv($noblockcheck,$self->{DEEPLINKLISTED});
     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 3956  sub from { my $self=shift; return $self- Line 4352  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 3991  sub enclosing_map_src { Line 4388  sub enclosing_map_src {
 }  }
 sub symb {  sub symb {
     my $self=shift;      my $self=shift;
       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 4181  sub is_sequence { Line 4579  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 4210  sub is_task { Line 4613  sub is_task {
   
 sub is_empty_sequence {  sub is_empty_sequence {
     my $self=shift;      my $self=shift;
     my $src = $self->src();  
     return !$self->is_page() && $self->navHash("is_map_", 1) && !$self->navHash("map_type_" . $self->map_pc());      return !$self->is_page() && $self->navHash("is_map_", 1) && !$self->navHash("map_type_" . $self->map_pc());
 }  }
   
Line 4261  Returns a string with a comma-separated Line 4663  Returns a string with a comma-separated
 for the hierarchy of maps containing a map, with the top level  for the hierarchy of maps containing a map, with the top level
 map first, then descending to deeper levels, with the enclosing map last.  map first, then descending to deeper levels, with the enclosing map last.
   
   =item * B<map_breadcrumbs>:
   
   Same as map_hierarchy, except maps containing only a single itemm if
   it's a map, or containing no items are omitted, unless it's the top
   level map (map_pc = 1), which is always included.
   
 =back  =back
   
 =cut  =cut
Line 4296  sub map_hierarchy { Line 4704  sub map_hierarchy {
     my $pc = $self->map_pc();      my $pc = $self->map_pc();
     return $self->navHash("map_hierarchy_$pc", 0);      return $self->navHash("map_hierarchy_$pc", 0);
 }  }
   sub map_breadcrumbs {
       my $self = shift;
       my $pc = $self->map_pc();
       return $self->navHash("map_breadcrumbs_$pc", 0);
   }
   
 #####  #####
 # Property queries  # Property queries
Line 4520  sub duedate { Line 4933  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 $first_access=&Apache::lonnet::get_first_access($interval[1],          my $timelimit = $1;
                                                           $self->symb);          my $first_access=&Apache::lonnet::get_first_access($interval[1],
                                                             $self->{SYMB});
  if (defined($first_access)) {   if (defined($first_access)) {
            my $interval = $first_access+$interval[0];              my $interval = $first_access+$timelimit;
     $date = (!$due_date || $interval < $due_date) ? $interval       $date = (!$due_date || $interval < $due_date) ? $interval 
                                                           : $due_date;                                                            : $due_date;
  } else {   } else {
Line 4604  sub part_display { Line 5018  sub part_display {
     my $self= shift(); my $partID = shift();      my $self= shift(); my $partID = shift();
     if (! defined($partID)) { $partID = '0'; }      if (! defined($partID)) { $partID = '0'; }
     my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',      my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',
                                      $self->symb);                                       $self->{SYMB});
     if (! defined($display) || $display eq '') {      if (! defined($display) || $display eq '') {
         $display = $partID;          $display = $partID;
     }      }
Line 4618  sub slot_control { Line 5032  sub slot_control {
     my $available = $self->parmval("available", $part);       my $available = $self->parmval("available", $part); 
     return ($useslots,$availablestudent,$available);      return ($useslots,$availablestudent,$available);
 }  }
   sub deeplink {
       my ($self,$caller,$action) = @_;
       my $deeplink = $self->parmval("deeplink");
       if ($deeplink) {
           my ($state,$others,$listed,$scope) = split(/,/,$deeplink);
           if ($action eq 'getlisted') {
               return $listed;
           }
           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 $listed;
           }
       }
       return;
   }
   
 # Multiple things need this  # Multiple things need this
 sub getReturnHash {  sub getReturnHash {
Line 5400  sub check_for_slot { Line 5854  sub check_for_slot {
         my $cnum=$env{'course.'.$cid.'.num'};          my $cnum=$env{'course.'.$cid.'.num'};
         my $now = time;          my $now = time;
         my $num_usable_slots = 0;          my $num_usable_slots = 0;
           my ($checkedin,$checkedinslot,%consumed_uniq,%slots);
         if (@slots > 0) {          if (@slots > 0) {
             my %slots=&Apache::lonnet::get('slots',[@slots],$cdom,$cnum);              %slots=&Apache::lonnet::get('slots',[@slots],$cdom,$cnum);
             if (&Apache::lonnet::error(%slots)) {              if (&Apache::lonnet::error(%slots)) {
                 return (UNKNOWN);                  return (UNKNOWN);
             }              }
             my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%slots,'starttime');              my @sorted_slots = &Apache::loncommon::sorted_slots(\@slots,\%slots,'starttime');
             my ($checkedin,$checkedinslot);  
             foreach my $slot_name (@sorted_slots) {              foreach my $slot_name (@sorted_slots) {
                 next if (!defined($slots{$slot_name}) || !ref($slots{$slot_name}));                  next if (!defined($slots{$slot_name}) || !ref($slots{$slot_name}));
                 my $end = $slots{$slot_name}->{'endtime'};                  my $end = $slots{$slot_name}->{'endtime'};
Line 5430  sub check_for_slot { Line 5884  sub check_for_slot {
                                 ($checkedin,$checkedinslot) = $self->checkedin();                                  ($checkedin,$checkedinslot) = $self->checkedin();
                                 unless ((grep(/^\Q$checkedin\E/,@proctors)) &&                                  unless ((grep(/^\Q$checkedin\E/,@proctors)) &&
                                         ($checkedinslot eq $slot_name)) {                                          ($checkedinslot eq $slot_name)) {
                                     return (NEEDS_CHECKIN,undef,$slot_name);                                       return (NEEDS_CHECKIN,$end,$slot_name); 
                                 }                                  }
                             }                              }
                             return (RESERVED,$end,$slot_name);                              return (RESERVED,$end,$slot_name);
Line 5440  sub check_for_slot { Line 5894  sub check_for_slot {
                     $num_usable_slots ++;                      $num_usable_slots ++;
                 }                  }
             }              }
             my ($is_correct,$got_grade);              my ($is_correct,$wait_for_grade);
             if ($self->is_task()) {              if ($self->is_task()) {
                 my $taskstatus = $self->taskstatus();                  my $taskstatus = $self->taskstatus();
                 $is_correct = (($taskstatus eq 'pass') ||                   $is_correct = (($taskstatus eq 'pass') || 
                                ($self->solved() =~ /^correct_/));                                 ($self->solved() =~ /^correct_/));
                 $got_grade = ($taskstatus =~ /^(?:pass|fail)$/);                  unless ($taskstatus =~ /^(?:pass|fail)$/) {
                       $wait_for_grade = 1;
                   }
             } else {              } else {
                 $got_grade = 1;                  unless ($self->completable()) {
                 $is_correct = ($self->solved() =~ /^correct_/);                         $wait_for_grade = 1;
                   }
                   unless (($self->problemstatus($part) eq 'no') ||
                           ($self->problemstatus($part) eq 'no_feedback_ever')) {
                       $is_correct = ($self->solved($part) =~ /^correct_/);
                       $wait_for_grade = 0;
                   }
             }              }
             ($checkedin,$checkedinslot) = $self->checkedin();              ($checkedin,$checkedinslot) = $self->checkedin();
             if ($checkedin) {              if ($checkedin) {
                 if (!$got_grade) {                  if (ref($slots{$checkedinslot}) eq 'HASH') {
                       $consumed_uniq{$checkedinslot} = $slots{$checkedinslot}{'uniqueperiod'};
                   }
                   if ($wait_for_grade) {
                     return (WAITING_FOR_GRADE);                      return (WAITING_FOR_GRADE);
                 } elsif ($is_correct) {                  } elsif ($is_correct) {
                     return (CORRECT);                       return (CORRECT); 
Line 5467  sub check_for_slot { Line 5932  sub check_for_slot {
         if (ref($reservable) eq 'HASH') {          if (ref($reservable) eq 'HASH') {
             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;
                     if (($reservable->{'now'}{$slot}{'symb'} eq '') ||                      if (($reservable->{'now'}{$slot}{'symb'} eq '') ||
                         ($reservable->{'now'}{$slot}{'symb'} eq $symb)) {                          ($reservable->{'now'}{$slot}{'symb'} eq $symb)) {
                           $canuse = 1;
                       }
                       if ($canuse) {
                           if ($checkedin) {
                               if (ref($consumed_uniq{$checkedinslot}) eq 'ARRAY') {
                                   my ($uniqstart,$uniqend)=@{$consumed_uniq{$checkedinslot}};
                                   if ($reservable->{'now'}{$slot}{'uniqueperiod'} =~ /^(\d+),(\d+)$/) {
                                       my ($new_uniq_start,$new_uniq_end) = ($1,$2);
                                       next if (!
                                           ($uniqstart < $new_uniq_start && $uniqend < $new_uniq_start) ||
                                           ($uniqstart > $new_uniq_end   &&  $uniqend > $new_uniq_end  ));
                                   }
                               }
                           }
                         return(RESERVABLE,$reservable->{'now'}{$slot}{'endreserve'});                          return(RESERVABLE,$reservable->{'now'}{$slot}{'endreserve'});
                     }                      }
                 }                  }
             }              }
             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;
                     if (($reservable->{'future'}{$slot}{'symb'} eq '') ||                      if (($reservable->{'future'}{$slot}{'symb'} eq '') ||
                         ($reservable->{'future'}{$slot}{'symb'} eq $symb)) {                          ($reservable->{'future'}{$slot}{'symb'} eq $symb)) {
                           $canuse = 1;
                       }
                       if ($canuse) {
                           if ($checkedin) {
                               if (ref($consumed_uniq{$checkedinslot}) eq 'ARRAY') {
                                   my ($uniqstart,$uniqend)=@{$consumed_uniq{$checkedinslot}};
                                   if ($reservable->{'future'}{$slot}{'uniqueperiod'} =~ /^(\d+),(\d+)$/) {
                                       my ($new_uniq_start,$new_uniq_end) = ($1,$2);
                                       next if (!
                                           ($uniqstart < $new_uniq_start && $uniqend < $new_uniq_start) ||
                                           ($uniqstart > $new_uniq_end   &&  $uniqend > $new_uniq_end  ));
                                   }
                               }
                           }
                         return(RESERVABLE_LATER,$reservable->{'future'}{$slot}{'startreserve'});                          return(RESERVABLE_LATER,$reservable->{'future'}{$slot}{'startreserve'});
                     }                      }
                 }                  }
Line 5708  sub getPrevious { Line 6203  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.508  
changed lines
  Added in v.1.509.2.14.2.6


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