Diff for /loncom/interface/lonnavmaps.pm between versions 1.509.2.16 and 1.517

version 1.509.2.16, 2024/07/02 15:53:16 version 1.517, 2016/03/17 13:20:35
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 949  sub render_resource { Line 937  sub render_resource {
     # links to open and close the folder      # links to open and close the folder
   
     my $whitespace = $location.'/whitespace_21.gif';      my $whitespace = $location.'/whitespace_21.gif';
     my $linkopen = "<img src='$whitespace' alt='' />";      my $linkopen = "<img src='$whitespace' alt='' />"."<a href=\"$link\">";
     my $nomodal;  
     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\">";  
     }  
     my $linkclose = "</a>";      my $linkclose = "</a>";
   
     # Default icon: unknown page      # Default icon: unknown page
Line 1024  sub render_resource { Line 991  sub render_resource {
             # 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 = "";
             } else {              $linkclose = "";
                 $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 1042  sub render_resource { Line 1005  sub render_resource {
                          '</a>';                           '</a>';
             }              }
         }          }
         if ($params->{'mapHidden'} || $resource->randomout()) {      }
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';  
         }      if ($resource->randomout()) {
     } else {          $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';
         if ($resource->randomout()) {  
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</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 1098  sub render_resource { Line 1058  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 1401  sub render { Line 1352  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 1419  sub render { Line 1369  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 1469  sub render { Line 1415  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 1544  sub render { Line 1487  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 1567  sub render { Line 1513  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 1588  END Line 1537  END
  $result.='</form>';   $result.='</form>';
     }      }
     if (($args->{'caller'} eq 'navmapsdisplay') &&      if (($args->{'caller'} eq 'navmapsdisplay') &&
         ((&Apache::lonnet::allowed('mdc',$env{'request.course.id'})) ||          (&Apache::lonnet::allowed('mdc',$env{'request.course.id'}))) {
          (&Apache::lonnet::allowed('cev',$env{'request.course.id'})))) {  
         my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};          my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'};
         my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};          my $cnum = $env{'course.'.$env{'request.course.id'}.'.num'};
         if ($env{'course.'.$env{'request.course.id'}.'.url'} eq           if ($env{'course.'.$env{'request.course.id'}.'.url'} eq 
Line 1635  END Line 1583  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;
     my $dfsit = Apache::lonnavmaps::DFSiterator->new($navmap,          $dfsit->next();
                                                      $it->{FIRST_RESOURCE},          my $curRes = $dfsit->next();
                                                      $it->{FINISH_RESOURCE},          while ($depth > -1) {
                                                      {}, undef, 1);              if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }
               if ($curRes == $dfsit->END_MAP()) { $depth--; }
     my $depth = 0;  
     $dfsit->next();              if (ref($curRes)) { 
     my $curRes = $dfsit->next();                  # Parallel pre-processing: Do sequences have non-filtered-out children?
     while ($depth > -1) {                  if ($curRes->is_map()) {
         if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }                      $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;
         if ($curRes == $dfsit->END_MAP()) { $depth--; }                      # Sequences themselves do not count as visible children,
                       # unless those sequences also have visible children.
         if (ref($curRes)) {                      # This means if a sequence appears, there's a "promise"
             # Parallel pre-processing: Do sequences have non-filtered-out children?                      # that there's something under it if you open it, somewhere.
             if ($curRes->is_map()) {                  } else {
                 $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;                      # Not a sequence: if it's filtered, ignore it, otherwise
                 # Sequences themselves do not count as visible children,                      # rise up the stack and mark the sequences as having children
                 # unless those sequences also have visible children.                      if (&$filterFunc($curRes)) {
                 # This means if a sequence appears, there's a "promise"                          for my $sequence (@{$dfsit->getStack()}) {
                 # that there's something under it if you open it, somewhere.                              $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;
             } elsif ($curRes->src()) {                          }
                 # Not a sequence: if it's filtered, ignore it, otherwise  
                 # rise up the stack and mark the sequences as having children  
                 if (&$filterFunc($curRes)) {  
                     for my $sequence (@{$dfsit->getStack()}) {  
                         $sequence->{DATA}->{HAS_VISIBLE_CHILDREN} = 1;  
                     }                      }
                 }                  }
             }              }
           } continue {
               $curRes = $dfsit->next();
         }          }
     } continue {  
         $curRes = $dfsit->next();  
     }      }
   
     my $displayedJumpMarker = 0;      my $displayedJumpMarker = 0;
Line 1732  END Line 1675  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 1789  END Line 1710  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'} &&
         if (($curRes->is_map()) && (!$curRes->{DATA}->{HAS_VISIBLE_CHILDREN})) {              !$curRes->{DATA}->{HAS_VISIBLE_CHILDREN}) {
             if ($args->{'suppressEmptySequences'}) {              next;
                 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;  
                     }  
                 }  
             }  
         }          }
   
         # 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 1887  END Line 1796  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 1943  END Line 1828  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 2236  sub change_user { Line 2121  sub change_user {
   
   
   
     # Now clear the parm cache and reconstruct the parm hash fromt he big_hash      # Now clear the parm cache and reconstruct the parm hash from the big_hash
     # param.xxxx keys.      # param.xxxx keys.
   
     $self->{PARM_CACHE} = {};      $self->{PARM_CACHE} = {};
Line 2329  sub generate_email_discuss_status { Line 2214  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 2701  sub parmval { Line 2586  sub parmval {
             return $self->{PARM_CACHE}->{$hashkey};              return $self->{PARM_CACHE}->{$hashkey};
         }          }
     }      }
   
     my $result = $self->parmval_real($what, $symb, $recurse);      my $result = $self->parmval_real($what, $symb, $recurse);
     $self->{PARM_CACHE}->{$hashkey} = $result;      $self->{PARM_CACHE}->{$hashkey} = $result;
     if (wantarray) {      if (wantarray) {
Line 2734  sub parmval_real { Line 2620  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 ($recursed,@recurseup); 
       
 # ----------------------------------------------------- Cascading lookup scheme  # ----------------------------------------------------- Cascading lookup scheme
     my $rwhat=$what;      my $rwhat=$what;
     $what=~s/^parameter\_//;      $what=~s/^parameter\_//;
     $what=~s/\_/\./;      $what=~s/\_/\./;
   
     my $symbparm=$symb.'.'.$what;      my $symbparm=$symb.'.'.$what;
       my $recurseparm=$mapname.'___(rec).'.$what;
     my $mapparm=$mapname.'___(all).'.$what;      my $mapparm=$mapname.'___(all).'.$what;
     my $usercourseprefix=$cid;      my $usercourseprefix=$cid;
       
   
   
     my $grplevel=$usercourseprefix.'.['.$cgroup.'].'.$what;      my $grplevel=$usercourseprefix.'.['.$cgroup.'].'.$what;
     my $grplevelr=$usercourseprefix.'.['.$cgroup.'].'.$symbparm;      my $grplevelr=$usercourseprefix.'.['.$cgroup.'].'.$symbparm;
       my $grpleveli=$usercourseprefix.'.['.$cgroup.'].'.$recurseparm;
     my $grplevelm=$usercourseprefix.'.['.$cgroup.'].'.$mapparm;      my $grplevelm=$usercourseprefix.'.['.$cgroup.'].'.$mapparm;
   
   
     my $seclevel= $usercourseprefix.'.['.$csec.'].'.$what;      my $seclevel= $usercourseprefix.'.['.$csec.'].'.$what;
     my $seclevelr=$usercourseprefix.'.['.$csec.'].'.$symbparm;      my $seclevelr=$usercourseprefix.'.['.$csec.'].'.$symbparm;
       my $secleveli=$usercourseprefix.'.['.$csec.'].'.$recurseparm;
     my $seclevelm=$usercourseprefix.'.['.$csec.'].'.$mapparm;      my $seclevelm=$usercourseprefix.'.['.$csec.'].'.$mapparm;
   
   
     my $courselevel= $usercourseprefix.'.'.$what;      my $courselevel= $usercourseprefix.'.'.$what;
     my $courselevelr=$usercourseprefix.'.'.$symbparm;      my $courselevelr=$usercourseprefix.'.'.$symbparm;
       my $courseleveli=$usercourseprefix.'.'.$recurseparm;
     my $courselevelm=$usercourseprefix.'.'.$mapparm;      my $courselevelm=$usercourseprefix.'.'.$mapparm;
   
   
Line 2768  sub parmval_real { Line 2660  sub parmval_real {
     if ($uname and defined($useropt)) {      if ($uname and defined($useropt)) {
         if (defined($$useropt{$courselevelr})) { return [$$useropt{$courselevelr},'resource']; }          if (defined($$useropt{$courselevelr})) { return [$$useropt{$courselevelr},'resource']; }
         if (defined($$useropt{$courselevelm})) { return [$$useropt{$courselevelm},'map']; }          if (defined($$useropt{$courselevelm})) { return [$$useropt{$courselevelm},'map']; }
           if (defined($$useropt{$courseleveli})) { return [$$useropt{$courseleveli},'map']; }
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
               last if (defined($$useropt{$norecursechk}));
               my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
               if (defined($$useropt{$recursechk})) { return [$$useropt{$recursechk},'map']; } 
           }
         if (defined($$useropt{$courselevel})) { return [$$useropt{$courselevel},'course']; }          if (defined($$useropt{$courselevel})) { return [$$useropt{$courselevel},'course']; }
     }      }
   
Line 2775  sub parmval_real { Line 2678  sub parmval_real {
     if ($cgroup ne '' and defined($courseopt)) {      if ($cgroup ne '' and defined($courseopt)) {
         if (defined($$courseopt{$grplevelr})) { return [$$courseopt{$grplevelr},'resource']; }          if (defined($$courseopt{$grplevelr})) { return [$$courseopt{$grplevelr},'resource']; }
         if (defined($$courseopt{$grplevelm})) { return [$$courseopt{$grplevelm},'map']; }          if (defined($$courseopt{$grplevelm})) { return [$$courseopt{$grplevelm},'map']; }
           if (defined($$courseopt{$grpleveli})) { return [$$courseopt{$grpleveli},'map']; } 
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what;
               last if (defined($$courseopt{$norecursechk}));
               my $recursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) { return [$$courseopt{$recursechk},'map']; }      
           }
         if (defined($$courseopt{$grplevel})) { return [$$courseopt{$grplevel},'course']; }          if (defined($$courseopt{$grplevel})) { return [$$courseopt{$grplevel},'course']; }
     }      }
   
     if ($csec and defined($courseopt)) {      if ($csec ne '' and defined($courseopt)) {
         if (defined($$courseopt{$seclevelr})) { return [$$courseopt{$seclevelr},'resource']; }          if (defined($$courseopt{$seclevelr})) { return [$$courseopt{$seclevelr},'resource']; }
         if (defined($$courseopt{$seclevelm})) { return [$$courseopt{$seclevelm},'map']; }          if (defined($$courseopt{$seclevelm})) { return [$$courseopt{$seclevelm},'map']; }
           if (defined($$courseopt{$secleveli})) { return [$$courseopt{$secleveli},'map']; } 
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what;
               last if (defined($$courseopt{$norecursechk}));
               my $recursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) { return [$$courseopt{$recursechk},'map']; }
           }
         if (defined($$courseopt{$seclevel})) { return [$$courseopt{$seclevel},'course']; }          if (defined($$courseopt{$seclevel})) { return [$$courseopt{$seclevel},'course']; }
     }      }
   
Line 2804  sub parmval_real { Line 2729  sub parmval_real {
 # --------------------------------------------------- fifth, check more course  # --------------------------------------------------- fifth, check more course
     if (defined($courseopt)) {      if (defined($courseopt)) {
         if (defined($$courseopt{$courselevelm})) { return [$$courseopt{$courselevelm},'map']; }          if (defined($$courseopt{$courselevelm})) { return [$$courseopt{$courselevelm},'map']; }
           if (defined($$courseopt{$courseleveli})) { return [$$courseopt{$courseleveli},'map']; }
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
               last if (defined($$courseopt{$norecursechk}));
               my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) {
                   return [$$courseopt{$recursechk},'map'];
               }
           }
         if (defined($$courseopt{$courselevel})) {          if (defined($$courseopt{$courselevel})) {
            my $ret = [$$courseopt{$courselevel},'course'];             my $ret = [$$courseopt{$courselevel},'course'];
            return $ret;             return $ret;
Line 2831  sub recurseup_maps { Line 2769  sub recurseup_maps {
     my ($self,$mapname) = @_;      my ($self,$mapname) = @_;
     my @recurseup;      my @recurseup;
     if ($mapname) {      if ($mapname) {
         my $res = $self->getResourceByUrl($mapname);          my @pcs = split(/,/,$self->getResourceByUrl(&Apache::lonnet::clutter($mapname))->map_hierarchy());
         if (ref($res)) {          shift(@pcs);
             my @pcs = split(/,/,$res->map_hierarchy());          pop(@pcs);
             shift(@pcs);          if (@pcs) {
             if (@pcs) {              @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->src()); } reverse(@pcs);
                 @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->src()); } reverse(@pcs);  
             }  
         }          }
     }      }
     return @recurseup;      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);  
         push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $map->title(),'no_mt' => 1,});  
         $totallength += length($map->title());  
     }  
     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 $showntitle = &truncate_crumb_text($map->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 2939  sub map_printdates { Line 2790  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 2966  sub get_mapparam { Line 2817  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);      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 2986  sub get_mapparam { Line 2836  sub get_mapparam {
   
     my $symbparm=$symb.'.'.$what;      my $symbparm=$symb.'.'.$what;
     my $mapparm=$mapname.'___(all).'.$what;      my $mapparm=$mapname.'___(all).'.$what;
       my $recurseparm=$mapname.'___(rec).'.$what; 
     my $usercourseprefix=$cid;      my $usercourseprefix=$cid;
   
   
     my $grplevel    = "$usercourseprefix.[$cgroup].$mapparm";      my $grplevelm    = "$usercourseprefix.[$cgroup].$mapparm";
     my $seclevel    = "$usercourseprefix.[$csec].$mapparm";      my $seclevelm    = "$usercourseprefix.[$csec].$mapparm";
     my $courselevel = "$usercourseprefix.$mapparm";      my $courselevelm = "$usercourseprefix.$mapparm";
   
       my $grpleveli    = "$usercourseprefix.[$cgroup].$recurseparm";
       my $secleveli    = "$usercourseprefix.[$csec].$recurseparm";
       my $courseleveli = "$usercourseprefix.$recurseparm";
   
     # Get handy references to the hashes we need in $self:      # Get handy references to the hashes we need in $self:
   
Line 3005  sub get_mapparam { Line 2859  sub get_mapparam {
   
   
     if ($uname and defined($useropt)) {      if ($uname and defined($useropt)) {
  if (defined($$useropt{$courselevel})) {   if (defined($$useropt{$courselevelm})) {
     return $$useropt{$courselevel};      return $$useropt{$courselevelm};
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          if (defined($$useropt{$courseleveli})) {
             unless ($recursed) {              return $$useropt{$courseleveli};
                 @recurseup = $self->recurseup_maps($mapname);          }
                 $recursed = 1;          unless ($recursed) {
             }              @recurseup = $self->recurseup_maps($mapname);
             foreach my $item (@recurseup) {              $recursed = 1;
                 my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;          }
                 if (defined($$useropt{$norecursechk})) {          foreach my $item (@recurseup) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {              my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
                         return $$useropt{$norecursechk};              last if (defined($$useropt{$norecursechk}));
                     }              my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
                 }              if (defined($$useropt{$recursechk})) {
                   return $$useropt{$recursechk};
             }              }
         }          }
     }      }
Line 3029  sub get_mapparam { Line 2884  sub get_mapparam {
   
   
     if ($cgroup ne '' and defined ($courseopt)) {      if ($cgroup ne '' and defined ($courseopt)) {
  if (defined($$courseopt{$grplevel})) {   if (defined($$courseopt{$grplevelm})) {
     return $$courseopt{$grplevel};      return $$courseopt{$grplevelm};
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          if (defined($$courseopt{$grpleveli})) {
             unless ($recursed) {              return $$courseopt{$grpleveli};
                 @recurseup = $self->recurseup_maps($mapname);          }
                 $recursed = 1;          unless ($recursed) {
             }              @recurseup = $self->recurseup_maps($mapname);
             foreach my $item (@recurseup) {              $recursed = 1;
                 my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what;          }
                 if (defined($$courseopt{$norecursechk})) {          foreach my $item (@recurseup) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {              my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what;
                         return $$courseopt{$norecursechk};              last if (defined($$courseopt{$norecursechk}));
                     }              my $recursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(rec).'.$what;
                 }              if (defined($$courseopt{$recursechk})) {
                   return $$courseopt{$recursechk};
             }              }
         }          }
     }      }
Line 3051  sub get_mapparam { Line 2907  sub get_mapparam {
     # Check course -- section      # Check course -- section
   
   
       if ($csec ne '' and defined($courseopt)) {
    if (defined($$courseopt{$seclevelm})) {
       return $$courseopt{$seclevelm};
     if ($csec and defined($courseopt)) {  
  if (defined($$courseopt{$seclevel})) {  
     return $$courseopt{$seclevel};  
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          if (defined($$courseopt{$secleveli})) {
             unless ($recursed) {              return $$courseopt{$secleveli};
                 @recurseup = $self->recurseup_maps($mapname);          }
                 $recursed = 1;          unless ($recursed) {
             }              @recurseup = $self->recurseup_maps($mapname);
             foreach my $item (@recurseup) {              $recursed = 1;
                 my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what;          }
                 if (defined($$courseopt{$norecursechk})) {          foreach my $item (@recurseup) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {              my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what;
                         return $$courseopt{$norecursechk};              last if (defined($$courseopt{$norecursechk}));
                     }              my $recursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(rec).'.$what;
                 }              if (defined($$courseopt{$recursechk})) {
                   return $$courseopt{$recursechk};
             }              }
         }          }
     }      }
     # 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;  
         }  
     }      }
   
   
     # Additional course parameters:      # Additional course parameters:
   
     if (defined($courseopt)) {      if (defined($courseopt)) {
  if (defined($$courseopt{$courselevel})) {   if (defined($$courseopt{$courselevelm})) {
     return $$courseopt{$courselevel};      return $$courseopt{$courselevelm};
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          unless ($recursed) {
             unless ($recursed) {              @recurseup = $self->recurseup_maps($mapname);
                 @recurseup = $self->recurseup_maps($mapname);              $recursed = 1;
                 $recursed = 1;          }
             }          if (@recurseup) {
             foreach my $item (@recurseup) {              foreach my $item (@recurseup) {
                 my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;                  my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
                 if (defined($$courseopt{$norecursechk})) {                  last if (defined($$courseopt{$norecursechk}));
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {                  my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
                         return $$courseopt{$norecursechk};                  if (defined($$courseopt{$recursechk})) {
                     }                      return $$courseopt{$recursechk};
                 }                  }
             }              }
         }          }
     }      }
     return undef; # Unefined if we got here.      return undef; # Undefined if we got here.
 }  }
   
 sub course_printdates {  sub course_printdates {
Line 3147  sub getcourseparam { Line 2998  sub getcourseparam {
     $what=~s/^parameter\_//;      $what=~s/^parameter\_//;
     $what=~s/\_/\./;      $what=~s/\_/\./;
   
   
     my $symbparm = $symb . '.' . $what;  
     my $mapparm=$mapname.'___(all).'.$what;  
   
     # Local refs to the hashes we're going to look at:      # Local refs to the hashes we're going to look at:
   
     my $useropt   = $self->{USER_OPT};      my $useropt   = $self->{USER_OPT};
Line 3801  sub next { Line 3648  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 4154  sub new { Line 4000  sub new {
           
     # This is a speed optimization, to avoid calling symb() too often.      # This is a speed optimization, to avoid calling symb() too often.
     $self->{SYMB} = $self->symb();      $self->{SYMB} = $self->symb();
      
     return $self;      return $self;
 }  }
   
Line 4280  sub enclosing_map_src { Line 4126  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 4551  Returns a string with a comma-separated Line 4396  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 4592  sub map_hierarchy { Line 4431  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 5324  The problem will be opened later. Line 5158  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 5336  The due date has passed and there is no Line 5171  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 5371  sub PAST_DUE_NO_ANSWER     { return 2; } Line 5186  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 5564  set. Line 5375  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 5666  sub status { Line 5457  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 ) {
  if ( $suppressFeedback ) {   if ( $suppressFeedback ) { return ANSWER_SUBMITTED }
             if ($dateStatus == PAST_DUE_ANSWER_LATER ||  
                 $dateStatus == PAST_DUE_NO_ANSWER ) {  
                 if ($dateStatus == PAST_DUE_ANSWER_LATER) {  
                     return PAST_DUE_ATMPT_ANS;  
                 } else {  
                     return PAST_DUE_ATMPT_NOANS;  
                 }  
             } else {  
                 return ANSWER_SUBMITTED;  
             }  
         }  
  my $awarded=$self->awarded($part);   my $awarded=$self->awarded($part);
  if ($awarded < 1 && $awarded > 0) {   if ($awarded < 1 && $awarded > 0) {
             return PARTIALLY_CORRECT;              return PARTIALLY_CORRECT;
Line 5716  sub status { Line 5496  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 5771  sub check_for_slot { Line 5535  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);
             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 5801  sub check_for_slot { Line 5565  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 5811  sub check_for_slot { Line 5575  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 5847  sub check_for_slot { Line 5600  sub check_for_slot {
         my $reservable = &Apache::lonnet::get_reservable_slots($cnum,$cdom,$env{'user.name'},          my $reservable = &Apache::lonnet::get_reservable_slots($cnum,$cdom,$env{'user.name'},
                                                                $env{'user.domain'});                                                                 $env{'user.domain'});
         if (ref($reservable) eq 'HASH') {          if (ref($reservable) eq 'HASH') {
               my ($map) = &Apache::lonnet::decode_symb($symb);
             if ((ref($reservable->{'now_order'}) eq 'ARRAY') && (ref($reservable->{'now'}) eq 'HASH')) {              if ((ref($reservable->{'now_order'}) eq 'ARRAY') && (ref($reservable->{'now'}) eq 'HASH')) {
                 foreach my $slot (reverse (@{$reservable->{'now_order'}})) {                  foreach my $slot (reverse (@{$reservable->{'now_order'}})) {
                     my $canuse;                      my $canuse;
                     if (($reservable->{'now'}{$slot}{'symb'} eq '') ||                      if ($reservable->{'now'}{$slot}{'symb'} eq '') {
                         ($reservable->{'now'}{$slot}{'symb'} eq $symb)) {  
                         $canuse = 1;                          $canuse = 1;
                     }                      } else {
                     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 5873  sub check_for_slot { Line 5632  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 5942  my %compositeToSimple = Line 5708  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,

Removed from v.1.509.2.16  
changed lines
  Added in v.1.517


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