Diff for /loncom/interface/lonnavmaps.pm between versions 1.509.2.14.2.9 and 1.510

version 1.509.2.14.2.9, 2023/07/06 20:32:17 version 1.510, 2015/10/05 01:52:10
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 342  user into thinking that if the sequence Line 341  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 486  use Apache::lonlocal; Line 485  use Apache::lonlocal;
 use Apache::lonnet;  use Apache::lonnet;
 use Apache::lonmap;  use Apache::lonmap;
   
 use POSIX qw (ceil floor strftime);  use POSIX qw (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 534  my %colormap = Line 533  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 576  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 651  sub getDescription { Line 642  sub getDescription {
             } elsif ($slot_status == $res->RESERVABLE) {              } elsif ($slot_status == $res->RESERVABLE) {
                 $slotmsg = &mt('Reservable, reservations close [_1]',                  $slotmsg = &mt('Reservable, reservations close [_1]',
                            timeToHumanString($slot_time,'end'));                             timeToHumanString($slot_time,'end'));
             } elsif ($slot_status == $res->NEEDS_CHECKIN) {  
                 $slotmsg = &mt('Reserved, check-in needed - ends [_1]',  
                            timeToHumanString($slot_time,'end'));  
             } elsif ($slot_status == $res->RESERVABLE_LATER) {              } elsif ($slot_status == $res->RESERVABLE_LATER) {
                 $slotmsg = &mt('Reservable, reservations open [_1]',                  $slotmsg = &mt('Reservable, reservations open [_1]',
                            timeToHumanString($slot_time,'start'));                             timeToHumanString($slot_time,'start'));
Line 683  sub getDescription { Line 671  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 695  sub getDescription { Line 683  sub getDescription {
     }      }
     if (($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT)      if (($status == $res->ANSWER_OPEN || $status == $res->PARTIALLY_CORRECT)
  && $res->handgrade($part) ne 'yes') {   && $res->handgrade($part) ne 'yes') {
         my $msg = &mt('Answer available');          return &Apache::lonhtmlcommon::direct_parm_link(&mt("Answer available"),$res->symb(),'answerdate,duedate',$part);
         my $parmlist = 'answerdate,duedate';  
         if (($res->is_tool) && ($res->is_gradable())) {  
             if (($status == $res->PARTIALLY_CORRECT) && ($res->parmval('retrypartial',$part))) {  
                 $msg = &mt('Grade received');  
                 $parmlist = 'retrypartial';  
             } else {  
                 $msg = &mt('Grade available');  
             }  
         }  
         return &Apache::lonhtmlcommon::direct_parm_link($msg,$res->symb(),$parmlist,$part);  
     }      }
     if ($status == $res->EXCUSED) {      if ($status == $res->EXCUSED) {
         return &mt("Excused by instructor");          return &mt("Excused by instructor");
Line 936  sub render_resource { Line 914  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 959  sub render_resource { Line 934  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 ($nomodal,$linkopen,$linkclose);      my $linkopen = "<img src='$whitespace' alt='' />"."<a href=\"$link\">";
     unless ($resource->is_map() || $params->{'resource_nolink'}) {      my $linkclose = "</a>";
         $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 1011  sub render_resource { Line 963  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 1035  sub render_resource { Line 983  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 = "<a href=\"$link\">";              $linkopen = "";
                 $linkclose = '</a>';              $linkclose = "";
             } 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 1059  sub render_resource { Line 1002  sub render_resource {
                          '</a>';                           '</a>';
             }              }
         }          }
         if ($params->{'mapHidden'} || $resource->randomout()) {      }
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';  
         } elsif ($params->{'mapUnlisted'}) {      if ($resource->randomout()) {
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('unlisted').')</span> ';          $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</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 1135  sub render_resource { Line 1055  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) {
         $linkclose = '</a>';          $result .= "$curMarkerBegin<a href=\"$link\">$title$partLabel</a>$curMarkerEnd$editmapLink$nonLinkedText</td>";
         if ($params->{'modalLink'}) {      } else {
             my $esclink = &js_escape($link);          $result .= "$curMarkerBegin$linkopen$title$partLabel</a>$curMarkerEnd$editmapLink$nonLinkedText</td>";
             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 1209  sub render_quick_status { Line 1120  sub render_quick_status {
     my $linkclose = "</a>";      my $linkclose = "</a>";
   
  $result .= '<td class="LC_middle">';   $result .= '<td class="LC_middle">';
     if ($resource->is_gradable() &&      if ($resource->is_problem() &&
         !$firstDisplayed) {          !$firstDisplayed) {
         my $icon = $statusIconMap{$resource->simpleStatus($part)};          my $icon = $statusIconMap{$resource->simpleStatus($part)};
         my $alt = $iconAltTags{$icon};          my $alt = $iconAltTags{$icon};
Line 1234  sub render_long_status { Line 1145  sub render_long_status {
                                   
     my $color;      my $color;
     my $info = '';      my $info = '';
     if ($resource->is_gradable() || $resource->is_practice()) {      if ($resource->is_problem() || $resource->is_practice()) {
         $color = $colormap{$resource->status};          $color = $colormap{$resource->status};
   
         if (dueInLessThan24Hours($resource, $part)) {          if (dueInLessThan24Hours($resource, $part)) {
Line 1249  sub render_long_status { Line 1160  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() &&
         !$firstDisplayed) {          !$firstDisplayed) {
         if ($color) {$result .= '<span style="color:'.$color.'"'.$info.'><b>'; }          if ($color) {$result .= '<span style="color:'.$color.'"'.$info.'><b>'; }
         $result .= getDescription($resource, $part);          $result .= getDescription($resource, $part);
Line 1297  my @statuses = ($resObj->CORRECT, $resOb Line 1208  my @statuses = ($resObj->CORRECT, $resOb
   
 sub render_parts_summary_status {  sub render_parts_summary_status {
     my ($resource, $part, $params) = @_;      my ($resource, $part, $params) = @_;
     if (!$resource->is_gradable() && !$resource->contains_problem) { return '<td></td>'; }      if (!$resource->is_problem() && !$resource->contains_problem) { return '<td></td>'; }
     if ($params->{showParts}) {       if ($params->{showParts}) { 
  return '<td></td>';   return '<td></td>';
     }      }
Line 1409  sub render { Line 1320  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'}) && !$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 1472  sub render { Line 1349  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 '') && ($args->{'caller'} ne 'sequence')) {    if ($here eq '') {
     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 1490  sub render { Line 1366  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 1540  sub render { Line 1412  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);
             if (ref($map)) {              my $firstResource = $map->map_start();
                 my $firstResource = $map->map_start();              my $finishResource = $map->map_finish();
                 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 1615  sub render { Line 1484  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 1638  sub render { Line 1510  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 1658  END Line 1533  END
  }   }
  $result.='</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'}))) {
         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');  
             }  
         }          }
     }      }
   
Line 1712  END Line 1580  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.      # If we're suppressing empty sequences, look for them here. Use DFS for speed,
     # We also do this even if $args->{'suppressEmptySequences'}      # since structure actually doesn't matter, except what map has what resources.
     # is not true, so we can hide empty sequences for which the      if ($args->{'suppressEmptySequences'}) {
     # hiddenresource parameter is set to yes (at map level), or          my $dfsit = Apache::lonnavmaps::DFSiterator->new($navmap,
     # mark as hidden for users who have $userCanSeeHidden.                                                           $it->{FIRST_RESOURCE},
     # Use DFS for speed, since structure actually doesn't matter,                                                           $it->{FINISH_RESOURCE},
     # except what map has what resources.                                                           {}, undef, 1);
     #          my $depth = 0;
     # To ensure the "Selected Resources from selected folder in course"          $dfsit->next();
     # printout generation option will work in sessions launched via a          my $curRes = $dfsit->next();
     # deep link, the value of $args->{'filterFunc'} included in the          while ($depth > -1) {
     # call to lonnavmaps::render() is omitted from the filter function              if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }
     # used with the DFS Iterator when $args->{'caller'} is 'printout'.              if ($curRes == $dfsit->END_MAP()) { $depth--; }
     #  
     # As a result $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} can be              if (ref($curRes)) { 
     # set to 1 for folder(s) which include resources only accessible                  # Parallel pre-processing: Do sequences have non-filtered-out children?
     # for sessions launched via a deep link, when the current session                  if ($curRes->is_map()) {
     # is of that type.                      $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;
                       # Sequences themselves do not count as visible children,
     my $dfsit = Apache::lonnavmaps::DFSiterator->new($navmap,                      # unless those sequences also have visible children.
                                                      $it->{FIRST_RESOURCE},                      # This means if a sequence appears, there's a "promise"
                                                      $it->{FINISH_RESOURCE},                      # that there's something under it if you open it, somewhere.
                                                      {}, undef, 1);                  } else {
                       # Not a sequence: if it's filtered, ignore it, otherwise
     my $dfsFilterFunc;                      # rise up the stack and mark the sequences as having children
     if ($args->{'caller'} eq 'printout') {                      if (&$filterFunc($curRes)) {
         $dfsFilterFunc = sub { my $res = shift; return !$res->randomout() &&                          for my $sequence (@{$dfsit->getStack()}) {
                               ($res->deeplink($args->{'caller'}) ne 'absent') &&                              $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;
                               ($res->deeplink($args->{'caller'}) ne 'grades') &&                          }
                               !$res->deeplinkout();};  
     } else {  
         $dfsFilterFunc = $filterFunc;  
     }  
     my $depth = 0;  
     $dfsit->next();  
     my $curRes = $dfsit->next();  
     while ($depth > -1) {  
         if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }  
         if ($curRes == $dfsit->END_MAP()) { $depth--; }  
   
         if (ref($curRes)) {  
             # Parallel pre-processing: Do sequences have non-filtered-out children?  
             if ($curRes->is_map()) {  
                 $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;  
                 # Sequences themselves do not count as visible children,  
                 # unless those sequences also have visible children.  
                 # This means if a sequence appears, there's a "promise"  
                 # 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 (&$dfsFilterFunc($curRes)) {  
                     for my $sequence (@{$dfsit->getStack()}) {  
                         next unless ($sequence->is_map());  
                         $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;  
                     }                      }
                 }                  }
             }              }
           } continue {
               $curRes = $dfsit->next();
         }          }
     } continue {  
         $curRes = $dfsit->next();  
     }      }
   
     my $displayedJumpMarker = 0;      my $displayedJumpMarker = 0;
Line 1830  END Line 1672  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 1887  END Line 1707  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;          if ($curRes->is_map() && $args->{'suppressEmptySequences'} &&
         $args->{'mapUnlisted'} = 0;              !$curRes->{DATA}->{HAS_VISIBLE_CHILDREN}) {
         $args->{'mapHiddenDeepLink'} = 0;              next;
         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 1980  END Line 1770  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 2012  END Line 1793  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; }
             if (($args->{'caller'} eq 'sequence') && ($curRes->is_map())) {      my $srcHasQuestion = $src =~ /\?/;
                 $args->{"resourceLink"} = $src.($srcHasQuestion?'&amp;':'?') .'navmap=1';      $args->{"resourceLink"} = $src.
             } else {   ($srcHasQuestion?'&amp;':'?') .
         $args->{"resourceLink"} = $src.   'symb=' . &escape($symb).$anchor;
     ($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 2068  END Line 1825  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" \>\<\/a\>/;                      $colHTML =~ s/\>/\>\<a name="curloc" \/\>/;
                     $displayedJumpMarker = 1;                      $displayedJumpMarker = 1;
                 }                  }
                 $result .= $colHTML . "\n";                  $result .= $colHTML . "\n";
Line 2142  sub show_linkitems_toolbar { Line 1899  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 2454  sub generate_email_discuss_status { Line 2211  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(&LONCAPA::escape($msgid));                  $symb,$error) = &Apache::lonmsg::unpackmsgid($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 2555  sub getIterator { Line 2312  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 2859  sub parmval_real { Line 2616  sub parmval_real {
   
     my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);      my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
     $mapname = &Apache::lonnet::deversion($mapname);      $mapname = &Apache::lonnet::deversion($mapname);
     my $toolsymb = '';  
     if ($fn =~ /ext\.tool$/) {  
         $toolsymb = $symb;  
     }  
 # ----------------------------------------------------- Cascading lookup scheme  # ----------------------------------------------------- Cascading lookup scheme
     my $rwhat=$what;      my $rwhat=$what;
     $what=~s/^parameter\_//;      $what=~s/^parameter\_//;
Line 2926  sub parmval_real { Line 2679  sub parmval_real {
   
     my $meta_rwhat=$rwhat;      my $meta_rwhat=$rwhat;
     $meta_rwhat=~s/\./_/g;      $meta_rwhat=~s/\./_/g;
     my $default=&Apache::lonnet::metadata($fn,$meta_rwhat,$toolsymb);      my $default=&Apache::lonnet::metadata($fn,$meta_rwhat);
     if (defined($default)) { return [$default,'resource']}      if (defined($default)) { return [$default,'resource']}
     $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat,$toolsymb);      $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat);
     if (defined($default)) { return [$default,'resource']}      if (defined($default)) { return [$default,'resource']}
 # --------------------------------------------------- fifth, check more course  # --------------------------------------------------- fifth, check more course
     if (defined($courseopt)) {      if (defined($courseopt)) {
Line 2951  sub parmval_real { Line 2704  sub parmval_real {
        if (defined($partgeneral[0])) { return \@partgeneral; }         if (defined($partgeneral[0])) { return \@partgeneral; }
     }      }
     if ($recurse) { return []; }      if ($recurse) { return []; }
     my $pack_def=&Apache::lonnet::packages_tab_default($fn,'resource.'.$rwhat,$toolsymb);      my $pack_def=&Apache::lonnet::packages_tab_default($fn,'resource.'.$rwhat);
     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 3076  sub map_printdates { Line 2719  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, $mapname, $what) = @_;      my ($self, $symb, $what) = @_;
   
     # Ensure the course option hash is populated:      # Ensure the course option hash is populated:
   
Line 3103  sub get_mapparam { Line 2746  sub get_mapparam {
     my $uname=$self->{USERNAME};      my $uname=$self->{USERNAME};
     my $udom=$self->{DOMAIN};      my $udom=$self->{DOMAIN};
   
     unless ($symb || $mapname) { return; }      unless ($symb) { return ['']; }
     my $result='';      my $result='';
     my ($recursed,@recurseup);  
   
     # Figure out which map we are in.      # Figure out which map we are in.
   
     if ($symb && !$mapname) {      my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
         my ($id,$fn);      $mapname = &Apache::lonnet::deversion($mapname);
         ($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 3145  sub get_mapparam { Line 2786  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 3169  sub get_mapparam { Line 2796  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 3195  sub get_mapparam { Line 2808  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:
   
     if ($symb) {      my $thisparm = $$parmhash{$symbparm};
         my $symbparm=$symb.'.'.$what;      if (defined($thisparm)) {
         my $thisparm = $$parmhash{$symbparm};   return $thisparm;
         if (defined($thisparm)) {  
             return $thisparm;  
         }  
     }      }
   
   
Line 3227  sub get_mapparam { Line 2823  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 3296  sub getcourseparam { Line 2878  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: Factor some of this stuff out of      # TODO: Fator 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 3313  sub getcourseparam { Line 2895  sub getcourseparam {
     }      }
     # Try for the group's course level option:      # Try for the group's course level option:
   
     if ($cgroup ne '' and defined($courseopt)) {      if ($uname ne '' and defined($courseopt)) {
  if (defined($$courseopt{$grplevel})) {   if (defined($$courseopt{$grplevel})) {
     return $$courseopt{$grplevel};      return $$courseopt{$grplevel};
  }   }
Line 3321  sub getcourseparam { Line 2903  sub getcourseparam {
   
     #  Try for section level parameters:      #  Try for section level parameters:
   
     if ($csec ne '' and defined($courseopt)) {      if ($csec and defined($courseopt)) {
  if (defined($$courseopt{$seclevel})) {   if (defined($$courseopt{$seclevel})) {
     return $$courseopt{$seclevel};      return $$courseopt{$seclevel};
  }   }
     }      }
     # Try for 'additional' course parameters:      # Try for 'additional' course parameterse:
   
     if (defined($courseopt)) {      if (defined($courseopt)) {
  if (defined($$courseopt{$courselevel})) {   if (defined($$courseopt{$courselevel})) {
Line 3503  sub usedVersion { Line 3085  sub usedVersion {
     return $self->navhash("version_$linkurl");      return $self->navhash("version_$linkurl");
 }  }
   
 sub isFirstResource {  
     my $self = shift;  
     my $map = shift;  
     my $symb = shift;  
     return unless (ref($map));  
     my $isfirst;  
     my $firstResource = $map->map_start();  
     if (ref($firstResource)) {  
         if ((!$firstResource->is_map()) && ($firstResource->src() ne ''))  {  
             if ($firstResource->symb() eq $symb) {  
                 $isfirst = 1;  
             } else {  
                 $isfirst = 0;  
             }  
         } else {  
             my $it = $self->getIterator($firstResource,undef,undef,1);  
             while ( my $res=$it->next()) {  
                 if ((ref($res)) && ($res->src() ne '') && (!$res->is_map())) {  
                     if ($res->symb() eq $symb) {  
                         $isfirst = 1;  
                     } else {  
                         $isfirst = 0;  
                     }  
                     last;  
                 }  
             }  
         }  
     }  
     return $isfirst;  
 }  
   
 sub isLastResource {  
     my $self = shift;  
     my $map = shift;  
     my $symb = shift;  
     return unless (ref($map));  
     my $islast;  
     my $lastResource = $map->map_finish();  
     if (ref($lastResource)) {  
         if ((!$lastResource->is_map()) && ($lastResource->src() ne ''))  {  
             if ($lastResource->symb() eq $symb) {  
                 $islast = 1;  
             } else {  
                 $islast = 0;  
             }  
         } else {  
             my $currRes = $self->getBySymb($symb);  
             if (ref($currRes)) {  
                 my $it = $self->getIterator($currRes,undef,undef,1);  
                 while ( my $res=$it->next()) {  
                     if ((ref($res)) && ($res->src() ne '') && (!$res->is_map())) {  
                         if ($res->symb() eq $symb) {  
                             $islast = 1;  
                         } else {  
                             $islast = 0;  
                         }  
                         last;  
                     }  
                 }  
             }  
         }  
     }  
     return $islast;  
 }  
   
 1;  1;
   
 package Apache::lonnavmaps::iterator;  package Apache::lonnavmaps::iterator;
Line 3598  getIterator behaves as follows: Line 3115  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 3615  that is not just a single, 'redirecting' Line 3132  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 3755  sub new { Line 3269  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 3850  sub new { Line 3360  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 4011  sub next { Line 3520  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 4021  sub next { Line 3529  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 4456  sub from { my $self=shift; return $self- Line 3963  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 4492  sub enclosing_map_src { Line 3998  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 4625  sub is_problem { Line 4130  sub is_problem {
     }      }
     return 0;      return 0;
 }  }
 sub is_tool {  
     my $self=shift;  
     my $src = $self->src();  
     return ($src =~ /ext\.tool$/);  
 }  
 sub is_gradable {  
     my $self=shift;  
     my $src = $self->src();  
     if (($src =~ /$LONCAPA::assess_re/) ||  
         (($self->is_tool()) && ($self->parmval('gradable',0) =~ /^yes$/i))) {  
         return !($self->is_practice());  
     }  
 }  
 #  #
 #  The has below is the set of status that are considered 'incomplete'  #  The has below is the set of status that are considered 'incomplete'
 #  #
Line 4696  sub is_sequence { Line 4188  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 4730  sub is_task { Line 4217  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 4780  Returns a string with a comma-separated Line 4268  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 4821  sub map_hierarchy { Line 4303  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 5050  sub duedate { Line 4527  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 $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+$interval[0];
     $date = (!$due_date || $interval < $due_date) ? $interval       $date = (!$due_date || $interval < $due_date) ? $interval 
                                                           : $due_date;                                                            : $due_date;
  } else {   } else {
Line 5135  sub part_display { Line 4611  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 5149  sub slot_control { Line 4625  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 5337  sub parts { Line 4773  sub parts {
     my $self = shift;      my $self = shift;
   
     if ($self->ext) { return []; }      if ($self->ext) { return []; }
     if (($self->is_tool()) &&  
         ($self->is_gradable())) { return ['0']; }  
   
     $self->extractParts();      $self->extractParts();
     return $self->{PARTS};      return $self->{PARTS};
Line 5429  sub extractParts { Line 4863  sub extractParts {
     my %parts;      my %parts;
   
     # Retrieve part count, if this is a problem      # Retrieve part count, if this is a problem
     if ($self->is_raw_problem()) {      if ($self->is_problem()) {
  my $partorder = &Apache::lonnet::metadata($self->src(), 'partorder');   my $partorder = &Apache::lonnet::metadata($self->src(), 'partorder');
         my $metadata = &Apache::lonnet::metadata($self->src(), 'packages');          my $metadata = &Apache::lonnet::metadata($self->src(), 'packages');
   
Line 5596  The problem will be opened later. Line 5030  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 5608  The due date has passed and there is no Line 5043  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 5643  sub PAST_DUE_NO_ANSWER     { return 2; } Line 5058  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 5736  Attempted, and not yet graded. Line 5147  Attempted, and not yet graded.
   
 Attempted, and credit received for attempt (survey and anonymous survey only).  Attempted, and credit received for attempt (survey and anonymous survey only).
   
 =item * B<INCORRECT_BY_PASSBACK>:  
   
 Attempted, but wrong for LTI Tool Provider by passback of grade  
   
 =item * B<CORRECT_BY_PASSBACK>:  
   
 Correct for LTI Tool Provider by passback of grade  
   
 =back  =back
   
 =cut  =cut
Line 5756  sub CORRECT_BY_OVERRIDE   { return 14; } Line 5159  sub CORRECT_BY_OVERRIDE   { return 14; }
 sub EXCUSED               { return 15; }  sub EXCUSED               { return 15; }
 sub ATTEMPTED             { return 16; }  sub ATTEMPTED             { return 16; }
 sub CREDIT_ATTEMPTED      { return 17; }  sub CREDIT_ATTEMPTED      { return 17; }
 sub INCORRECT_BY_PASSBACK { return 18; }  
 sub CORRECT_BY_PASSBACK   { return 19; }  
   
 sub getCompletionStatus {  sub getCompletionStatus {
     my $self = shift;      my $self = shift;
Line 5772  sub getCompletionStatus { Line 5173  sub getCompletionStatus {
     if ($status eq 'correct_by_override') {      if ($status eq 'correct_by_override') {
  return $self->CORRECT_BY_OVERRIDE;   return $self->CORRECT_BY_OVERRIDE;
     }      }
     if ($status eq 'correct_by_passback') {  
         return $self->CORRECT_BY_PASSBACK;  
     }  
     if ($status eq 'incorrect_attempted') {return $self->INCORRECT; }      if ($status eq 'incorrect_attempted') {return $self->INCORRECT; }
     if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; }      if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; }
     if ($status eq 'incorrect_by_passback') {return $self->INCORRECT_BY_PASSBACK; }  
     if ($status eq 'excused') {return $self->EXCUSED; }      if ($status eq 'excused') {return $self->EXCUSED; }
     if ($status eq 'ungraded_attempted') {return $self->ATTEMPTED; }      if ($status eq 'ungraded_attempted') {return $self->ATTEMPTED; }
     if ($status eq 'credit_attempted') {      if ($status eq 'credit_attempted') {
Line 5850  set. Line 5247  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 5951  sub status { Line 5328  sub status {
   
     # There are a few whole rows we can dispose of:      # There are a few whole rows we can dispose of:
     if ($completionStatus == CORRECT ||      if ($completionStatus == CORRECT ||
         $completionStatus == CORRECT_BY_OVERRIDE ||          $completionStatus == CORRECT_BY_OVERRIDE ) {
         $completionStatus == CORRECT_BY_PASSBACK ) {   if ( $suppressFeedback ) { return ANSWER_SUBMITTED }
  if ( $suppressFeedback ) {  
             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 5976  sub status { Line 5341  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)  
  && (!$self->opendate($part) ||  $self->opendate($part) > time()) ) {   && (!$self->opendate($part) ||  $self->opendate($part) > time()) ) {
  return INCORRECT;   return INCORRECT;
     }      }
Line 6004  sub status { Line 5368  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 6035  sub status { Line 5383  sub status {
     }      }
   
     # If it's WRONG...      # If it's WRONG...
     if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE ||      if ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE) {
         $completionStatus == INCORRECT_BY_PASSBACK) {  
         # and there are TRIES LEFT:          # and there are TRIES LEFT:
         if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) {          if ($self->tries($part) < $self->maxtries($part) || !$self->maxtries($part)) {
             return $suppressFeedback ? ANSWER_SUBMITTED : TRIES_LEFT;              return $suppressFeedback ? ANSWER_SUBMITTED : TRIES_LEFT;
Line 6060  sub check_for_slot { Line 5407  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) {
             %slots=&Apache::lonnet::get('slots',[@slots],$cdom,$cnum);              my %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);
               my ($map) = &Apache::lonnet::decode_symb($symb);
             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 6090  sub check_for_slot { Line 5438  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,$end,$slot_name);                                       return (NEEDS_CHECKIN,undef,$slot_name); 
                                 }                                  }
                             }                              }
                             return (RESERVED,$end,$slot_name);                              return (RESERVED,$end,$slot_name);
Line 6100  sub check_for_slot { Line 5448  sub check_for_slot {
                     $num_usable_slots ++;                      $num_usable_slots ++;
                 }                  }
             }              }
             my ($is_correct,$wait_for_grade);              my ($is_correct,$got_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_/));
                 unless ($taskstatus =~ /^(?:pass|fail)$/) {                  $got_grade = ($taskstatus =~ /^(?:pass|fail)$/);
                     $wait_for_grade = 1;  
                 }  
             } else {              } else {
                 unless ($self->completable()) {                  $got_grade = 1;
                     $wait_for_grade = 1;                  $is_correct = ($self->solved() =~ /^correct_/);   
                 }  
                 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 (ref($slots{$checkedinslot}) eq 'HASH') {                  if (!$got_grade) {
                     $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 6139  sub check_for_slot { Line 5476  sub check_for_slot {
             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 {
                     if ($canuse) {                          my %oksymbs;
                         if ($checkedin) {                          my @slotsymbs = split(/\s*,\s*/,$reservable->{'now'}{$slot}{'symb'});
                             if (ref($consumed_uniq{$checkedinslot}) eq 'ARRAY') {                          map { $oksymbs{$_} = 1; } @slotsymbs;
                                 my ($uniqstart,$uniqend)=@{$consumed_uniq{$checkedinslot}};                          if ($oksymbs{$symb}) {
                                 if ($reservable->{'now'}{$slot}{'uniqueperiod'} =~ /^(\d+),(\d+)$/) {                              $canuse = 1;
                                     my ($new_uniq_start,$new_uniq_end) = ($1,$2);                          } else {
                                     next if (!                              foreach my $item (@slotsymbs) {
                                         ($uniqstart < $new_uniq_start && $uniqend < $new_uniq_start) ||                                  if ($item =~ /\.(page|sequence)$/) {
                                         ($uniqstart > $new_uniq_end   &&  $uniqend > $new_uniq_end  ));                                      (undef,undef, my $sloturl) = &Apache::lonnet::decode_symb($item);
                                       if (($map ne '') && ($map eq $sloturl)) {
                                           $canuse = 1;
                                           last;
                                       }
                                 }                                  }
                             }                              }
                         }                          }
                       }
                       if ($canuse) {
                         return(RESERVABLE,$reservable->{'now'}{$slot}{'endreserve'});                          return(RESERVABLE,$reservable->{'now'}{$slot}{'endreserve'});
                     }                      }
                 }                  }
Line 6162  sub check_for_slot { Line 5504  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;                          $canuse = 1;
                     }                      } elsif ($reservable->{'future'}{$slot}{'symb'} =~ /,/) {
                     if ($canuse) {                          my %oksymbs;
                         if ($checkedin) {                          my @slotsymbs = split(/\s*,\s*/,$reservable->{'future'}{$slot}{'symb'});
                             if (ref($consumed_uniq{$checkedinslot}) eq 'ARRAY') {                          map { $oksymbs{$_} = 1; } @slotsymbs;
                                 my ($uniqstart,$uniqend)=@{$consumed_uniq{$checkedinslot}};                          if ($oksymbs{$symb}) {
                                 if ($reservable->{'future'}{$slot}{'uniqueperiod'} =~ /^(\d+),(\d+)$/) {                              $canuse = 1;
                                     my ($new_uniq_start,$new_uniq_end) = ($1,$2);                          } else {
                                     next if (!                              foreach my $item (@slotsymbs) {
                                         ($uniqstart < $new_uniq_start && $uniqend < $new_uniq_start) ||                                  if ($item =~ /\.(page|sequence)$/) {
                                         ($uniqstart > $new_uniq_end   &&  $uniqend > $new_uniq_end  ));                                      (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;
                       }
                       if ($canuse) {
                         return(RESERVABLE_LATER,$reservable->{'future'}{$slot}{'startreserve'});                          return(RESERVABLE_LATER,$reservable->{'future'}{$slot}{'startreserve'});
                     }                      }
                 }                  }
Line 6231  my %compositeToSimple = Line 5580  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 6413  sub getPrevious { Line 5758  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.9  
changed lines
  Added in v.1.510


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