Diff for /loncom/interface/lonnavmaps.pm between versions 1.509.2.10 and 1.546

version 1.509.2.10, 2019/02/07 00:49:53 version 1.546, 2019/01/16 20:23:37
Line 2 Line 2
 # Navigate Maps Handler  # Navigate Maps Handler
 #  #
 # $Id$  # $Id$
   
 #  #
 # Copyright Michigan State University Board of Trustees  # Copyright Michigan State University Board of Trustees
 #  #
Line 52  described at http://www.lon-capa.org. Line 51  described at http://www.lon-capa.org.
 X<lonnavmaps, overview> When a user enters a course, LON-CAPA examines the  X<lonnavmaps, overview> When a user enters a course, LON-CAPA examines the
 course structure and caches it in what is often referred to as the  course structure and caches it in what is often referred to as the
 "big hash" X<big hash>. You can see it if you are logged into  "big hash" X<big hash>. You can see it if you are logged into
 LON-CAPA, in a course, by going to /adm/test. (You may need to  LON-CAPA, in a course, by going to /adm/test. The content of 
 tweak the /home/httpd/lonTabs/htpasswd file to view it.) The  the hash will be under the heading "Big Hash".
 content of the hash will be under the heading "Big Hash".  
   Access to /adm/test is controlled by a domain configuration, 
   which a Domain Coordinator will set for a server's default domain
   via: Main Menu > Set domain configuration > Display (Access to 
   server status pages checked), and entering a username:domain
   or IP address in the "Show user environment" row. Users with
   an unexpired domain coordinator role in the server's domain
   automatically receive access to /adm/test.
   
 Big Hash contains, among other things, how resources are related  Big Hash contains, among other things, how resources are related
 to each other (next/previous), what resources are maps, which   to each other (next/previous), what resources are maps, which 
Line 65  processed. Line 71  processed.
   
 Apache::lonnavmaps provides an object model for manipulating this  Apache::lonnavmaps provides an object model for manipulating this
 information in a higher-level fashion than directly manipulating   information in a higher-level fashion than directly manipulating 
 the hash. It also provides access to several auxilary functions   the hash. It also provides access to several auxiliary functions 
 that aren't necessarily stored in the Big Hash, but are a per-  that aren't necessarily stored in the Big Hash, but are a per-
 resource sort of value, like whether there is any feedback on   resource sort of value, like whether there is any feedback on 
 a given resource.  a given resource.
Line 78  Apache::lonnavmaps also provides fairly Line 84  Apache::lonnavmaps also provides fairly
 rendering navmaps, and last but not least, provides the navmaps  rendering navmaps, and last but not least, provides the navmaps
 view for when the user clicks the NAV button.  view for when the user clicks the NAV button.
   
 B<Note>: Apache::lonnavmaps I<only> works for the "currently  B<Note>: Apache::lonnavmaps by default will show information 
 logged in user"; if you want things like "due dates for another  for the "currently logged in user".  However, if information
 student" lonnavmaps can not directly retrieve information like  about resources is needed for a different user, e.g., a bubblesheet
 that. You need the EXT function. This module can still help,  exam which uses randomorder, or randompick needs to be printed or 
 because many things, such as the course structure, are constant  graded for named user(s) or specific CODEs, then the username,
   domain, or CODE can be passed as arguments when creating a new 
   navmap object.
   
   Note if you want things like "due dates for another student",
   you would use the EXT function instead of lonnavmaps.
   That said, the lonnavmaps module can still help, because many
   things, such as the course structure, are usually constant
 between users, and Apache::lonnavmaps can help by providing  between users, and Apache::lonnavmaps can help by providing
 symbs for the EXT call.  symbs for the EXT call.
   
Line 92  all, then documents the Apache::lonnavma Line 105  all, then documents the Apache::lonnavma
 is the key to accessing the Big Hash information, covers the use  is the key to accessing the Big Hash information, covers the use
 of the Iterator (which provides the logic for traversing the   of the Iterator (which provides the logic for traversing the 
 somewhat-complicated Big Hash data structure), documents the  somewhat-complicated Big Hash data structure), documents the
 Apache::lonnavmaps::Resource objects that are returned by   Apache::lonnavmaps::Resource objects that are returned singularly
   by: getBySymb(), getById(), getByMapPc(), and getResourceByUrl()
   (can also be as an array), or in an array by retrieveResources().
   
 =head1 Subroutine: render  =head1 Subroutine: render
   
Line 687  sub getDescription { Line 702  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') {
         return &Apache::lonhtmlcommon::direct_parm_link(&mt("Answer available"),$res->symb(),'answerdate,duedate',$part);          my $msg = &mt('Answer available');
           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 938  sub render_resource { Line 963  sub render_resource {
         $newBranchText = "<img src='$location/branch.gif' alt=".mt('Branch')." />";          $newBranchText = "<img src='$location/branch.gif' alt=".mt('Branch')." />";
     }      }
   
     # links to open and close the folder  
   
     my $whitespace = $location.'/whitespace_21.gif';      my $whitespace = $location.'/whitespace_21.gif';
     my $linkopen = "<img src='$whitespace' alt='' />";      my ($nomodal,$linkopen,$linkclose);
     my $nomodal;      unless ($resource->is_map() || $params->{'resource_nolink'}) {
     if (($params->{'modalLink'}) && (!$resource->is_sequence())) {          $linkopen = "<img src='$whitespace' alt='' />";
         if ($link =~m{^(?:|/adm/wrapper)/ext/([^#]+)}) {          $linkclose = "</a>";
             my $exturl = $1;          if (($params->{'modalLink'}) && (!$resource->is_sequence())) {
             if (($ENV{'SERVER_PORT'} == 443) && ($exturl !~ /^https:/)) {              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;                  $nomodal = 1;
             }              }
         } elsif (($link eq "/public/$LONCAPA::match_domain/$LONCAPA::match_courseid/syllabus") &&              my $esclink = &js_escape($link);
                  ($env{'request.course.id'}) && ($ENV{'SERVER_PORT'} == 443) &&              if ($nomodal) {
                  ($env{'course.'.$env{'request.course.id'}.'.externalsyllabus'} =~ m{^http://})) {                  $linkopen .= "<a href=\"#\" onclick=\"javascript:window.open('$esclink','resourcepreview','height=400,width=500,scrollbars=1,resizable=1,menubar=0,location=1'); return false;\" />";
              $nomodal = 1;              } else {
         }                  $linkopen .= "<a href=\"$link\" onclick=\"javascript:openMyModal('$esclink',600,500,'yes','true'); return false;\">";
         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 {          } else {
             $linkopen .= "<a href=\"$link\" onclick=\"javascript:openMyModal('$esclink',600,500,'yes','true'); return false;\">";              $linkopen .= "<a href=\"$link\">";
         }          }
     } else {  
         $linkopen .= "<a href=\"$link\">";  
     }      }
     my $linkclose = "</a>";  
   
     # 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 1036  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') {              if ($params->{'caller'} eq 'sequence') {
                 $linkopen = "<a href=\"$link\">";                  $linkopen = "<a href=\"$link\">";
                   $linkclose = '</a>';
             } else {              } else {
                 $linkopen = "";                  $linkopen = "";
                 $linkclose = "";                  $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'}))) &&                (&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 1036  sub render_resource { Line 1062  sub render_resource {
         }          }
         if ($params->{'mapHidden'} || $resource->randomout()) {          if ($params->{'mapHidden'} || $resource->randomout()) {
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';              $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';
           } elsif ($params->{'mapUnlisted'}) {
               $nonLinkedText .= ' <span class="LC_warning">('.&mt('unlisted').')</span> ';
         }          }
     } else {      } else {
         if ($resource->randomout()) {          if ($resource->randomout()) {
             $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';              $nonLinkedText .= ' <span class="LC_warning">('.&mt('hidden').')</span> ';
           } elsif (($resource->deeplink($params->{caller}) eq 'absent') ||
                    ($resource->deeplink($params->{caller}) eq 'grades')) {
               $nonLinkedText .= ' <span class="LC_warning">('.&mt('unlisted').')</span> ';
         }          }
     }      }
     if (!$resource->condval()) {      if (!$resource->condval()) {
Line 1164  sub render_quick_status { Line 1195  sub render_quick_status {
     my $linkclose = "</a>";      my $linkclose = "</a>";
   
  $result .= '<td class="LC_middle">';   $result .= '<td class="LC_middle">';
     if ($resource->is_problem() &&      if ($resource->is_gradable() &&
         !$firstDisplayed) {          !$firstDisplayed) {
         my $icon = $statusIconMap{$resource->simpleStatus($part)};          my $icon = $statusIconMap{$resource->simpleStatus($part)};
         my $alt = $iconAltTags{$icon};          my $alt = $iconAltTags{$icon};
Line 1189  sub render_long_status { Line 1220  sub render_long_status {
                                   
     my $color;      my $color;
     my $info = '';      my $info = '';
     if ($resource->is_problem() || $resource->is_practice()) {      if ($resource->is_gradable() || $resource->is_practice()) {
         $color = $colormap{$resource->status};          $color = $colormap{$resource->status};
   
         if (dueInLessThan24Hours($resource, $part)) {          if (dueInLessThan24Hours($resource, $part)) {
Line 1203  sub render_long_status { Line 1234  sub render_long_status {
             }              }
          }           }
     }      }
       
     if ($resource->kind() eq "res" &&      if (($resource->kind() eq "res") &&
         $resource->is_raw_problem() &&          ($resource->is_raw_problem() || $resource->is_gradable()) &&
         !$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 1252  my @statuses = ($resObj->CORRECT, $resOb Line 1283  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_problem() && !$resource->contains_problem) { return '<td></td>'; }      if (!$resource->is_gradable() && !$resource->contains_problem) { return '<td></td>'; }
     if ($params->{showParts}) {       if ($params->{showParts}) { 
  return '<td></td>';   return '<td></td>';
     }      }
Line 1363  sub render { Line 1394  sub render {
         # Without renaming the filterfunc, the server seems to go into          # Without renaming the filterfunc, the server seems to go into
         # an infinite loop          # an infinite loop
         my $oldFilterFunc = $filterFunc;          my $oldFilterFunc = $filterFunc;
         $filterFunc = sub { my $res = shift; return !$res->randomout() &&           $filterFunc = sub { my $res = shift; return !$res->randomout() &&
                                   ($res->deeplink($args->{'caller'}) ne 'absent') &&
                                   ($res->deeplink($args->{'caller'}) ne 'grades') &&
                                 &$oldFilterFunc($res);};                                  &$oldFilterFunc($res);};
     }      }
   
Line 1393  sub render { Line 1426  sub render {
             my $currenturl = $env{'form.postdata'};              my $currenturl = $env{'form.postdata'};
             #$currenturl=~s/^http\:\/\///;              #$currenturl=~s/^http\:\/\///;
             #$currenturl=~s/^[^\/]+//;              #$currenturl=~s/^[^\/]+//;
             unless ($args->{'caller'} eq 'sequence') {               unless ($args->{'caller'} eq 'sequence') {
                 $here = $jump = &Apache::lonnet::symbread($currenturl);                  $here = $jump = &Apache::lonnet::symbread($currenturl);
             }              }
  }   }
  if (($here eq '') && ($args->{'caller'} ne 'sequence')) {    if (($here eq '') && ($args->{'caller'} ne 'sequence')) {
     my $last;      my $last;
     if (tie(my %hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db',      if (tie(my %hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db',
                     &GDBM_READER(),0640)) {                      &GDBM_READER(),0640)) {
Line 1633  END Line 1666  END
     # We also do this even if $args->{'suppressEmptySequences'}      # We also do this even if $args->{'suppressEmptySequences'}
     # is not true, so we can hide empty sequences for which the      # is not true, so we can hide empty sequences for which the
     # hiddenresource parameter is set to yes (at map level), or      # hiddenresource parameter is set to yes (at map level), or
     # mark as hidden for users who have $userCanSeeHidden.      # mark as hidden for users who have $userCanSeeHidden.  
     # Use DFS for speed, since structure actually doesn't matter,      # Use DFS for speed, since structure actually doesn't matter,
     # except what map has what resources.      # except what map has what resources.
   
Line 1641  END Line 1674  END
                                                      $it->{FIRST_RESOURCE},                                                       $it->{FIRST_RESOURCE},
                                                      $it->{FINISH_RESOURCE},                                                       $it->{FINISH_RESOURCE},
                                                      {}, undef, 1);                                                       {}, undef, 1);
   
     my $depth = 0;      my $depth = 0;
     $dfsit->next();      $dfsit->next();
     my $curRes = $dfsit->next();      my $curRes = $dfsit->next();
Line 1649  END Line 1681  END
         if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }          if ($curRes == $dfsit->BEGIN_MAP()) { $depth++; }
         if ($curRes == $dfsit->END_MAP()) { $depth--; }          if ($curRes == $dfsit->END_MAP()) { $depth--; }
   
         if (ref($curRes)) {          if (ref($curRes)) { 
             # Parallel pre-processing: Do sequences have non-filtered-out children?              # Parallel pre-processing: Do sequences have non-filtered-out children?
             if ($curRes->is_map()) {              if ($curRes->is_map()) {
                 $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;                  $curRes->{DATA}->{HAS_VISIBLE_CHILDREN} = 0;
Line 1726  END Line 1758  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;      my $inhibitmenu;
     if ($args->{'modalLink'}) {      if ($args->{'modalLink'}) {
Line 1767  END Line 1816  END
   
         # If this is an empty sequence and we're filtering them, continue on          # If this is an empty sequence and we're filtering them, continue on
         $args->{'mapHidden'} = 0;          $args->{'mapHidden'} = 0;
           $args->{'mapUnlisted'} = 0;
         if (($curRes->is_map()) && (!$curRes->{DATA}->{HAS_VISIBLE_CHILDREN})) {          if (($curRes->is_map()) && (!$curRes->{DATA}->{HAS_VISIBLE_CHILDREN})) {
             if ($args->{'suppressEmptySequences'}) {              if ($args->{'suppressEmptySequences'}) {
                 next;                  next;
             } else {              } else {
                 my $mapname = &Apache::lonnet::declutter($curRes->src());                  my $mapname = &Apache::lonnet::declutter($curRes->src());
                 $mapname = &Apache::lonnet::deversion($mapname);                  $mapname = &Apache::lonnet::deversion($mapname); 
                 if (lc($navmap->get_mapparam(undef,$mapname,"0.hiddenresource")) eq 'yes') {                  if (lc($navmap->get_mapparam(undef,$mapname,"0.hiddenresource")) eq 'yes') {
                     if ($userCanSeeHidden) {                      if ($userCanSeeHidden) {
                         $args->{'mapHidden'} = 1;                          $args->{'mapHidden'} = 1;
                     } else {                      } else {
                         next;                          next;
                     }                      }
                   } else {
                       my $deeplink = $navmap->get_mapparam(undef,$mapname,"0.deeplink");
                       if (($deeplink eq 'absent') || ($deeplink eq 'grades')) {
                           if ($userCanSeeHidden) {
                               $args->{'mapUnlisted'} = 1;
                           } else {
                               next;
                           }
                       }
                 }                  }
             }              }
         }          }
Line 1841  END Line 1900  END
     $args->{'condensed'} = 1;      $args->{'condensed'} = 1;
  }   }
             }              }
         }           }
                       # If deep-link parameter is set (and is not set to full) suppress link
           # unless priviliged user, or calling context is sequence, and parameter
           # set at map level
           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) {
             $args->{'multipart'} = 0;              $args->{'multipart'} = 0;
Line 1864  END Line 1932  END
  $stack=$it->getStack();   $stack=$it->getStack();
     }      }
     ($src,$symb,$anchor)=getLinkForResource($stack);      ($src,$symb,$anchor)=getLinkForResource($stack);
     my $srcHasQuestion = $src =~ /\?/;              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()) {
                           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()) {
                           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())) {      if (($args->{'caller'} eq 'sequence') && ($curRes->is_map())) {
                 $args->{"resourceLink"} = $src.($srcHasQuestion?'&amp;':'?') .'navmap=1';          $args->{"resourceLink"} = $src.($srcHasQuestion?'&amp;':'?') .'navmap=1';
             } else {      } else {
         $args->{"resourceLink"} = $src.          $args->{"resourceLink"} = $src.
     ($srcHasQuestion?'&amp;':'?') .      ($srcHasQuestion?'&amp;':'?') .
     'symb=' . &escape($symb).$inhibitmenu.$anchor;      'symb=' . &escape($symb).$inhibitmenu.$anchor;
             }      }
  }   }
         # Now, we've decided what parts to show. Loop through them and          # Now, we've decided what parts to show. Loop through them and
         # show them.          # show them.
Line 2193  sub change_user { Line 2281  sub change_user {
   
   
   
     # Now clear the parm cache and reconstruct the parm hash fromt he big_hash      # Now clear the parm cache and reconstruct the parm hash from the big_hash
     # param.xxxx keys.      # param.xxxx keys.
   
     $self->{PARM_CACHE} = {};      $self->{PARM_CACHE} = {};
Line 2658  sub parmval { Line 2746  sub parmval {
             return $self->{PARM_CACHE}->{$hashkey};              return $self->{PARM_CACHE}->{$hashkey};
         }          }
     }      }
   
     my $result = $self->parmval_real($what, $symb, $recurse);      my $result = $self->parmval_real($what, $symb, $recurse);
     $self->{PARM_CACHE}->{$hashkey} = $result;      $self->{PARM_CACHE}->{$hashkey} = $result;
     if (wantarray) {      if (wantarray) {
Line 2691  sub parmval_real { Line 2780  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;
       }
       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 2725  sub parmval_real { Line 2824  sub parmval_real {
     if ($uname and defined($useropt)) {      if ($uname and defined($useropt)) {
         if (defined($$useropt{$courselevelr})) { return [$$useropt{$courselevelr},'resource']; }          if (defined($$useropt{$courselevelr})) { return [$$useropt{$courselevelr},'resource']; }
         if (defined($$useropt{$courselevelm})) { return [$$useropt{$courselevelm},'map']; }          if (defined($$useropt{$courselevelm})) { return [$$useropt{$courselevelm},'map']; }
           if (defined($$useropt{$courseleveli})) { return [$$useropt{$courseleveli},'map']; }
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
               if (defined($$useropt{$norecursechk})) {
                   if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return [$$useropt{$norecursechk},'map'];
                   } else {
                       last;
                   }
               }
               my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
               if (defined($$useropt{$recursechk})) { return [$$useropt{$recursechk},'map']; } 
           }
         if (defined($$useropt{$courselevel})) { return [$$useropt{$courselevel},'course']; }          if (defined($$useropt{$courselevel})) { return [$$useropt{$courselevel},'course']; }
     }      }
   
Line 2732  sub parmval_real { Line 2848  sub parmval_real {
     if ($cgroup ne '' and defined($courseopt)) {      if ($cgroup ne '' and defined($courseopt)) {
         if (defined($$courseopt{$grplevelr})) { return [$$courseopt{$grplevelr},'resource']; }          if (defined($$courseopt{$grplevelr})) { return [$$courseopt{$grplevelr},'resource']; }
         if (defined($$courseopt{$grplevelm})) { return [$$courseopt{$grplevelm},'map']; }          if (defined($$courseopt{$grplevelm})) { return [$$courseopt{$grplevelm},'map']; }
           if (defined($$courseopt{$grpleveli})) { return [$$courseopt{$grpleveli},'map']; } 
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what;
               if (defined($$courseopt{$norecursechk})) {
                   if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return [$$courseopt{$norecursechk},'map'];
                   } else {
                      last;
                   }
               }
               my $recursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) { return [$$courseopt{$recursechk},'map']; }      
           }
         if (defined($$courseopt{$grplevel})) { return [$$courseopt{$grplevel},'course']; }          if (defined($$courseopt{$grplevel})) { return [$$courseopt{$grplevel},'course']; }
     }      }
   
     if ($csec and defined($courseopt)) {      if ($csec ne '' and defined($courseopt)) {
         if (defined($$courseopt{$seclevelr})) { return [$$courseopt{$seclevelr},'resource']; }          if (defined($$courseopt{$seclevelr})) { return [$$courseopt{$seclevelr},'resource']; }
         if (defined($$courseopt{$seclevelm})) { return [$$courseopt{$seclevelm},'map']; }          if (defined($$courseopt{$seclevelm})) { return [$$courseopt{$seclevelm},'map']; }
           if (defined($$courseopt{$secleveli})) { return [$$courseopt{$secleveli},'map']; } 
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what;
               if (defined($$courseopt{$norecursechk})) {
                   if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return [$$courseopt{$norecursechk},'map'];
                   } else {
                       last;
                   }
               }
               my $recursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) { return [$$courseopt{$recursechk},'map']; }
           }
         if (defined($$courseopt{$seclevel})) { return [$$courseopt{$seclevel},'course']; }          if (defined($$courseopt{$seclevel})) { return [$$courseopt{$seclevel},'course']; }
     }      }
   
Line 2754  sub parmval_real { Line 2904  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);      my $default=&Apache::lonnet::metadata($fn,$meta_rwhat,$toolsymb);
     if (defined($default)) { return [$default,'resource']}      if (defined($default)) { return [$default,'resource']}
     $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat);      $default=&Apache::lonnet::metadata($fn,'parameter_'.$meta_rwhat,$toolsymb);
     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)) {
         if (defined($$courseopt{$courselevelm})) { return [$$courseopt{$courselevelm},'map']; }          if (defined($$courseopt{$courselevelm})) { return [$$courseopt{$courselevelm},'map']; }
           if (defined($$courseopt{$courseleveli})) { return [$$courseopt{$courseleveli},'map']; }
           unless ($recursed) {
               @recurseup = $self->recurseup_maps($mapname);
               $recursed = 1;
           }
           foreach my $item (@recurseup) {
               my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
               if (defined($$courseopt{$norecursechk})) {
                   if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return [$$courseopt{$norecursechk},'map'];
                   } else {
                       last;
                   }
               }
               my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) {
                   return [$$courseopt{$recursechk},'map'];
               }
           }
         if (defined($$courseopt{$courselevel})) {          if (defined($$courseopt{$courselevel})) {
            my $ret = [$$courseopt{$courselevel},'course'];             my $ret = [$$courseopt{$courselevel},'course'];
            return $ret;             return $ret;
Line 2779  sub parmval_real { Line 2948  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);      my $pack_def=&Apache::lonnet::packages_tab_default($fn,'resource.'.$rwhat,$toolsymb);
     if (defined($pack_def)) { return [$pack_def,'resource']; }      if (defined($pack_def)) { return [$pack_def,'resource']; }
     return [''];      return [''];
 }  }
Line 2825  sub recursed_crumbs { Line 2994  sub recursed_crumbs {
         my $pc = $map->map_pc();          my $pc = $map->map_pc();
         next if ((!$pc) || ($pc == 1));          next if ((!$pc) || ($pc == 1));
         push(@links,$map);          push(@links,$map);
         push(@revmapinfo,{'href' => $map->link().'?navmap=1','text' => $map->title(),'no_mt' => 1,});          push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $map->title(),'no_mt' => 1,});
         $totallength += length($map->title());          $totallength += length($map->title());
     }      }
     my $numlinks = scalar(@links);      my $numlinks = scalar(@links);
Line 2840  sub recursed_crumbs { Line 3009  sub recursed_crumbs {
             foreach my $map (@links) {              foreach my $map (@links) {
                 my $showntitle = &truncate_crumb_text($map->title(),$avg);                  my $showntitle = &truncate_crumb_text($map->title(),$avg);
                 if ($showntitle ne '') {                  if ($showntitle ne '') {
                     push(@revmapinfo,{'href' => $map->link().'?navmap=1','text' => $showntitle,'no_mt' => 1,});                      push(@revmapinfo,{'href' => $env{'request.use_absolute'}.$map->link().'?navmap=1','text' => $showntitle,'no_mt' => 1,});
                 }                  }
             }              }
         }          }
Line 2897  sub map_printdates { Line 3066  sub map_printdates {
   
   
     my $opendate = $self->get_mapparam($res->symb(),'',"$part.printstartdate");      my $opendate = $self->get_mapparam($res->symb(),'',"$part.printstartdate");
     my $closedate= $self->get_mapparam($res->symb(),'', "$part.printenddate");      my $closedate= $self->get_mapparam($res->symb(),'',"$part.printenddate");
   
   
     return ($opendate, $closedate);      return ($opendate, $closedate);
Line 2927  sub get_mapparam { Line 3096  sub get_mapparam {
     my $result='';      my $result='';
     my ($recursed,@recurseup);      my ($recursed,@recurseup);
   
   
     # Figure out which map we are in.      # Figure out which map we are in.
   
     if ($symb && !$mapname) {      if ($symb && !$mapname) {
Line 2935  sub get_mapparam { Line 3105  sub get_mapparam {
         $mapname = &Apache::lonnet::deversion($mapname);          $mapname = &Apache::lonnet::deversion($mapname);
     }      }
   
   
     my $rwhat=$what;      my $rwhat=$what;
     $what=~s/^parameter\_//;      $what=~s/^parameter\_//;
     $what=~s/\_/\./;      $what=~s/\_/\./;
   
     # Build the hash keys for the lookup:      # Build the hash keys for the lookup:
   
     my $symbparm=$symb.'.'.$what;  
     my $mapparm=$mapname.'___(all).'.$what;      my $mapparm=$mapname.'___(all).'.$what;
       my $recurseparm=$mapname.'___(rec).'.$what; 
     my $usercourseprefix=$cid;      my $usercourseprefix=$cid;
   
   
     my $grplevel    = "$usercourseprefix.[$cgroup].$mapparm";      my $grplevelm    = "$usercourseprefix.[$cgroup].$mapparm";
     my $seclevel    = "$usercourseprefix.[$csec].$mapparm";      my $seclevelm    = "$usercourseprefix.[$csec].$mapparm";
     my $courselevel = "$usercourseprefix.$mapparm";      my $courselevelm = "$usercourseprefix.$mapparm";
   
       my $grpleveli    = "$usercourseprefix.[$cgroup].$recurseparm";
       my $secleveli    = "$usercourseprefix.[$csec].$recurseparm";
       my $courseleveli = "$usercourseprefix.$recurseparm";
   
     # Get handy references to the hashes we need in $self:      # Get handy references to the hashes we need in $self:
   
Line 2962  sub get_mapparam { Line 3136  sub get_mapparam {
   
   
     if ($uname and defined($useropt)) {      if ($uname and defined($useropt)) {
  if (defined($$useropt{$courselevel})) {   if (defined($$useropt{$courselevelm})) {
     return $$useropt{$courselevel};      return $$useropt{$courselevelm};
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          if (defined($$useropt{$courseleveli})) {
             unless ($recursed) {              return $$useropt{$courseleveli};
                 @recurseup = $self->recurseup_maps($mapname);          }
                 $recursed = 1;          unless ($recursed) {
             }              @recurseup = $self->recurseup_maps($mapname);
             foreach my $item (@recurseup) {              $recursed = 1;
                 my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;          }
                 if (defined($$useropt{$norecursechk})) {          foreach my $item (@recurseup) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {              my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
                         return $$useropt{$norecursechk};              if (defined($$useropt{$norecursechk})) {
                     }                  if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return $$useropt{$norecursechk};
                   } else {
                       last;
                 }                  }
             }              }
               my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
               if (defined($$useropt{$recursechk})) {
                   return $$useropt{$recursechk};
               }
         }          }
     }      }
   
Line 2986  sub get_mapparam { Line 3167  sub get_mapparam {
   
   
     if ($cgroup ne '' and defined ($courseopt)) {      if ($cgroup ne '' and defined ($courseopt)) {
  if (defined($$courseopt{$grplevel})) {   if (defined($$courseopt{$grplevelm})) {
     return $$courseopt{$grplevel};      return $$courseopt{$grplevelm};
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          if (defined($$courseopt{$grpleveli})) {
             unless ($recursed) {              return $$courseopt{$grpleveli};
                 @recurseup = $self->recurseup_maps($mapname);          }
                 $recursed = 1;          unless ($recursed) {
             }              @recurseup = $self->recurseup_maps($mapname);
             foreach my $item (@recurseup) {              $recursed = 1;
                 my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what;          }
                 if (defined($$courseopt{$norecursechk})) {          foreach my $item (@recurseup) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {              my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what;
                         return $$courseopt{$norecursechk};              if (defined($$courseopt{$norecursechk})) {
                     }                  if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return $$courseopt{$norecursechk};
                   } else {
                       last;
                 }                  }
             }              }
               my $recursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) {
                   return $$courseopt{$recursechk};
               }
         }          }
     }      }
   
     # Check course -- section      # Check course -- section
   
   
       if ($csec ne '' and defined($courseopt)) {
    if (defined($$courseopt{$seclevelm})) {
       return $$courseopt{$seclevelm};
     if ($csec and defined($courseopt)) {  
  if (defined($$courseopt{$seclevel})) {  
     return $$courseopt{$seclevel};  
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          if (defined($$courseopt{$secleveli})) {
             unless ($recursed) {              return $$courseopt{$secleveli};
                 @recurseup = $self->recurseup_maps($mapname);          }
                 $recursed = 1;          unless ($recursed) {
             }              @recurseup = $self->recurseup_maps($mapname);
             foreach my $item (@recurseup) {              $recursed = 1;
                 my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what;          }
                 if (defined($$courseopt{$norecursechk})) {          foreach my $item (@recurseup) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {              my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what;
                         return $$courseopt{$norecursechk};              if (defined($$courseopt{$norecursechk})) {
                     }                  if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                       return $$courseopt{$norecursechk};
                   } else {
                       last;
                 }                  }
             }              }
               my $recursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(rec).'.$what;
               if (defined($$courseopt{$recursechk})) {
                   return $$courseopt{$recursechk};
               }
         }          }
     }      }
     # Check the map parameters themselves:      # Check the map parameters themselves:
Line 3036  sub get_mapparam { Line 3228  sub get_mapparam {
         my $symbparm=$symb.'.'.$what;          my $symbparm=$symb.'.'.$what;
         my $thisparm = $$parmhash{$symbparm};          my $thisparm = $$parmhash{$symbparm};
         if (defined($thisparm)) {          if (defined($thisparm)) {
             return $thisparm;      return $thisparm;
         }          }
     }      }
   
Line 3044  sub get_mapparam { Line 3236  sub get_mapparam {
     # Additional course parameters:      # Additional course parameters:
   
     if (defined($courseopt)) {      if (defined($courseopt)) {
  if (defined($$courseopt{$courselevel})) {   if (defined($$courseopt{$courselevelm})) {
     return $$courseopt{$courselevel};      return $$courseopt{$courselevelm};
  }   }
         if ($what =~ /\.(encrypturl|hiddenresource)$/) {          unless ($recursed) {
             unless ($recursed) {              @recurseup = $self->recurseup_maps($mapname);
                 @recurseup = $self->recurseup_maps($mapname);              $recursed = 1;
                 $recursed = 1;          }
             }          if (@recurseup) {
             foreach my $item (@recurseup) {              foreach my $item (@recurseup) {
                 my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;                  my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what;
                 if (defined($$courseopt{$norecursechk})) {                  if (defined($$courseopt{$norecursechk})) {
                     if ($what =~ /\.(encrypturl|hiddenresource)$/) {                      if ($what =~ /\.(encrypturl|hiddenresource)$/) {
                         return $$courseopt{$norecursechk};                          return $$courseopt{$norecursechk};
                       } else {
                           last;
                     }                      }
                 }                  }
                   my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what;
                   if (defined($$courseopt{$recursechk})) {
                       return $$courseopt{$recursechk};
                   }
             }              }
         }          }
     }      }
     return undef; # Unefined if we got here.      return undef; # Undefined if we got here.
 }  }
   
 sub course_printdates {  sub course_printdates {
Line 3104  sub getcourseparam { Line 3302  sub getcourseparam {
     $what=~s/^parameter\_//;      $what=~s/^parameter\_//;
     $what=~s/\_/\./;      $what=~s/\_/\./;
   
   
     my $symbparm = $symb . '.' . $what;  
     my $mapparm=$mapname.'___(all).'.$what;  
   
     # Local refs to the hashes we're going to look at:      # Local refs to the hashes we're going to look at:
   
     my $useropt   = $self->{USER_OPT};      my $useropt   = $self->{USER_OPT};
Line 3323  sub usedVersion { Line 3517  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 4110  sub new { Line 4369  sub new {
           
     # This is a speed optimization, to avoid calling symb() too often.      # This is a speed optimization, to avoid calling symb() too often.
     $self->{SYMB} = $self->symb();      $self->{SYMB} = $self->symb();
      
     return $self;      return $self;
 }  }
   
Line 4236  sub enclosing_map_src { Line 4495  sub enclosing_map_src {
 }  }
 sub symb {  sub symb {
     my $self=shift;      my $self=shift;
     if (defined($self->{SYMB})) { return $self->{SYMB}; }      if (defined $self->{SYMB}) { return $self->{SYMB}; }
     (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;      (my $first, my $second) = $self->{ID} =~ /(\d+).(\d+)/;
     my $symbSrc = &Apache::lonnet::declutter($self->src());      my $symbSrc = &Apache::lonnet::declutter($self->src());
     my $symb = &Apache::lonnet::declutter($self->navHash('map_id_'.$first))       my $symb = &Apache::lonnet::declutter($self->navHash('map_id_'.$first)) 
Line 4369  sub is_problem { Line 4628  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 4456  sub is_task { Line 4728  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 4777  sub duedate { Line 5048  sub duedate {
     my $date;      my $date;
     my @interval=$self->parmval("interval", $part);      my @interval=$self->parmval("interval", $part);
     my $due_date=$self->parmval("duedate", $part);      my $due_date=$self->parmval("duedate", $part);
     if ($interval[0] =~ /\d+/) {      if ($interval[0] =~ /^(\d+)/) {
        my $first_access=&Apache::lonnet::get_first_access($interval[1],          my $timelimit = $1;
                                                           $self->{SYMB});          my $first_access=&Apache::lonnet::get_first_access($interval[1],
                                                              $self->{SYMB});
  if (defined($first_access)) {   if (defined($first_access)) {
            my $interval = $first_access+$interval[0];              my $interval = $first_access+$timelimit;
     $date = (!$due_date || $interval < $due_date) ? $interval       $date = (!$due_date || $interval < $due_date) ? $interval 
                                                           : $due_date;                                                            : $due_date;
  } else {   } else {
Line 4875  sub slot_control { Line 5147  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) = @_;
       if ($caller eq 'sequence') {
           my @deeplink = $self->parmval("deeplink");
           if ($deeplink[1] eq 'resource') {
               return $deeplink[0];
           }
       } else {
           return $self->parmval("deeplink");
       }
   }
   
 # Multiple things need this  # Multiple things need this
 sub getReturnHash {  sub getReturnHash {
     my $self = shift;      my $self = shift;
           
     if (!defined($self->{RETURN_HASH})) {      if (!defined($self->{RETURN_HASH})) {
         my %tmpHash  = &Apache::lonnet::restore($self->{SYMB},undef,$self->{DOMAIN},$self->{USERNAME});          #my %tmpHash  = &Apache::lonnet::restore($self->{SYMB},undef,$self->{DOMAIN},$self->{USERNAME});
         $self->{RETURN_HASH} = \%tmpHash;          #$self->{RETURN_HASH} = \%tmpHash;
           # When info is retrieved for several resources (as when rendering a directory),
           # it is much faster to use the user profile dump and avoid repeated lonnet requests
           # (especially since lonnet::currentdump is using Lond directly whenever possible,
           # and lonnet::restore is not at this point).
           $self->{NAV_MAP}->get_user_data();
           $self->{RETURN_HASH} = $self->{NAV_MAP}->{STUDENT_DATA}->{$self->{SYMB}};
     }      }
 }         }       
   
Line 5023  sub parts { Line 5312  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 5113  sub extractParts { Line 5404  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_problem()) {      if ($self->is_raw_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 5397  Attempted, and not yet graded. Line 5688  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 5409  sub CORRECT_BY_OVERRIDE   { return 14; } Line 5708  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 5423  sub getCompletionStatus { Line 5724  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 5578  sub status { Line 5883  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 ) { return ANSWER_SUBMITTED }
  my $awarded=$self->awarded($part);   my $awarded=$self->awarded($part);
  if ($awarded < 1 && $awarded > 0) {   if ($awarded < 1 && $awarded > 0) {
Line 5591  sub status { Line 5897  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 5633  sub status { Line 5940  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 5733  sub check_for_slot { Line 6041  sub check_for_slot {
         my $reservable = &Apache::lonnet::get_reservable_slots($cnum,$cdom,$env{'user.name'},          my $reservable = &Apache::lonnet::get_reservable_slots($cnum,$cdom,$env{'user.name'},
                                                                $env{'user.domain'});                                                                 $env{'user.domain'});
         if (ref($reservable) eq 'HASH') {          if (ref($reservable) eq 'HASH') {
               my ($map) = &Apache::lonnet::decode_symb($symb);
             if ((ref($reservable->{'now_order'}) eq 'ARRAY') && (ref($reservable->{'now'}) eq 'HASH')) {              if ((ref($reservable->{'now_order'}) eq 'ARRAY') && (ref($reservable->{'now'}) eq 'HASH')) {
                 foreach my $slot (reverse (@{$reservable->{'now_order'}})) {                  foreach my $slot (reverse (@{$reservable->{'now_order'}})) {
                     my $canuse;                      my $canuse;
                     if (($reservable->{'now'}{$slot}{'symb'} eq '') ||                      if ($reservable->{'now'}{$slot}{'symb'} eq '') {
                         ($reservable->{'now'}{$slot}{'symb'} eq $symb)) {  
                         $canuse = 1;                          $canuse = 1;
                       } else {
                           my %oksymbs;
                           my @slotsymbs = split(/\s*,\s*/,$reservable->{'now'}{$slot}{'symb'});
                           map { $oksymbs{$_} = 1; } @slotsymbs;
                           if ($oksymbs{$symb}) {
                               $canuse = 1;
                           } else {
                               foreach my $item (@slotsymbs) {
                                   if ($item =~ /\.(page|sequence)$/) {
                                       (undef,undef, my $sloturl) = &Apache::lonnet::decode_symb($item);
                                       if (($map ne '') && ($map eq $sloturl)) {
                                           $canuse = 1;
                                           last;
                                       }
                                   }
                               }
                           }
                     }                      }
                     if ($canuse) {                      if ($canuse) {
                         if ($checkedin) {                          if ($checkedin) {
Line 5759  sub check_for_slot { Line 6084  sub check_for_slot {
             if ((ref($reservable->{'future_order'}) eq 'ARRAY') && (ref($reservable->{'future'}) eq 'HASH')) {              if ((ref($reservable->{'future_order'}) eq 'ARRAY') && (ref($reservable->{'future'}) eq 'HASH')) {
                 foreach my $slot (@{$reservable->{'future_order'}}) {                  foreach my $slot (@{$reservable->{'future_order'}}) {
                     my $canuse;                      my $canuse;
                     if (($reservable->{'future'}{$slot}{'symb'} eq '') ||                      if ($reservable->{'future'}{$slot}{'symb'} eq '') {
                         ($reservable->{'future'}{$slot}{'symb'} eq $symb)) {                          $canuse = 1;
                       } elsif ($reservable->{'future'}{$slot}{'symb'} =~ /,/) {
                           my %oksymbs;
                           my @slotsymbs = split(/\s*,\s*/,$reservable->{'future'}{$slot}{'symb'});
                           map { $oksymbs{$_} = 1; } @slotsymbs;
                           if ($oksymbs{$symb}) {
                               $canuse = 1;
                           } else {
                               foreach my $item (@slotsymbs) {
                                   if ($item =~ /\.(page|sequence)$/) {
                                       (undef,undef, my $sloturl) = &Apache::lonnet::decode_symb($item);
                                       if (($map ne '') && ($map eq $sloturl)) {
                                           $canuse = 1;
                                           last;
                                       }
                                   }
                               }
                           }
                       } elsif ($reservable->{'future'}{$slot}{'symb'} eq $symb) {
                         $canuse = 1;                          $canuse = 1;
                     }                      }
                     if ($canuse) {                      if ($canuse) {

Removed from v.1.509.2.10  
changed lines
  Added in v.1.546


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