--- loncom/interface/lonnavmaps.pm 2023/07/06 20:32:17 1.509.2.14.2.9 +++ loncom/interface/lonnavmaps.pm 2025/02/03 19:07:54 1.571 @@ -1,8 +1,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.509.2.14.2.9 2023/07/06 20:32:17 raeburn Exp $ - +# $Id: lonnavmaps.pm,v 1.571 2025/02/03 19:07:54 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -49,12 +48,20 @@ described at http://www.lon-capa.org. =head1 OVERVIEW -X When a user enters a course, LON-CAPA examines the +X +When a user enters a course, LON-CAPA examines the course structure and caches it in what is often referred to as the "big hash" X. You can see it if you are logged into -LON-CAPA, in a course, by going to /adm/test. (You may need to -tweak the /home/httpd/lonTabs/htpasswd file to view it.) The -content of the hash will be under the heading "Big Hash". +LON-CAPA, in a course, by going to /adm/test. The 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 to each other (next/previous), what resources are maps, which @@ -65,7 +72,7 @@ processed. Apache::lonnavmaps provides an object model for manipulating this 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- resource sort of value, like whether there is any feedback on a given resource. @@ -78,11 +85,18 @@ Apache::lonnavmaps also provides fairly rendering navmaps, and last but not least, provides the navmaps view for when the user clicks the NAV button. -B: Apache::lonnavmaps I works for the "currently -logged in user"; if you want things like "due dates for another -student" lonnavmaps can not directly retrieve information like -that. You need the EXT function. This module can still help, -because many things, such as the course structure, are constant +B: Apache::lonnavmaps by default will show information +for the "currently logged in user". However, if information +about resources is needed for a different user, e.g., a bubblesheet +exam which uses randomorder, or randompick needs to be printed or +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 symbs for the EXT call. @@ -92,7 +106,9 @@ all, then documents the Apache::lonnavma is the key to accessing the Big Hash information, covers the use of the Iterator (which provides the logic for traversing 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 @@ -956,8 +972,6 @@ sub render_resource { $newBranchText = ".mt('Branch')."; } - # links to open and close the folder - my $whitespace = $location.'/whitespace_21.gif'; my ($nomodal,$linkopen,$linkclose); unless ($resource->is_map() || $params->{'resource_nolink'}) { @@ -1049,7 +1063,7 @@ sub render_resource { } } 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/)) { if (!$params->{'map_no_edit_link'}) { my $icon = &Apache::loncommon::lonhttpdurl('/res/adm/pages').'/editmap.png'; @@ -1107,7 +1121,7 @@ sub render_resource { } # Decide what to display - $result .= "$newBranchText$linkopen$icon$linkclose"; + $result .= "$newBranchText$linkopen$icon"; my $curMarkerBegin = ''; my $curMarkerEnd = ''; @@ -1147,7 +1161,7 @@ sub render_resource { $linkopen = ""; } } - $result .= "$curMarkerBegin$linkopen$title$partLabel$linkclose$curMarkerEnd$editmapLink$nonLinkedText"; + $result .= "$curMarkerBegin$title$partLabel$curMarkerEnd$linkclose$editmapLink$nonLinkedText"; return $result; } @@ -1248,7 +1262,7 @@ sub render_long_status { } } } - + if (($resource->kind() eq "res") && ($resource->is_raw_problem() || $resource->is_gradable()) && !$firstDisplayed) { @@ -1408,7 +1422,7 @@ sub render { # Without renaming the filterfunc, the server seems to go into # an infinite loop my $oldFilterFunc = $filterFunc; - $filterFunc = sub { my $res = shift; return !$res->randomout() && + $filterFunc = sub { my $res = shift; return !$res->randomout() && ($res->deeplink($args->{'caller'}) ne 'absent') && ($res->deeplink($args->{'caller'}) ne 'grades') && !$res->deeplinkout() && @@ -1472,11 +1486,11 @@ sub render { my $currenturl = $env{'form.postdata'}; #$currenturl=~s/^http\:\/\///; #$currenturl=~s/^[^\/]+//; - unless ($args->{'caller'} eq 'sequence') { + unless ($args->{'caller'} eq 'sequence') { $here = $jump = &Apache::lonnet::symbread($currenturl); } } - if (($here eq '') && ($args->{'caller'} ne 'sequence')) { + if (($here eq '') && ($args->{'caller'} ne 'sequence')) { my $last; if (tie(my %hash,'GDBM_File',$env{'request.course.fn'}.'_symb.db', &GDBM_READER(),0640)) { @@ -1490,7 +1504,7 @@ sub render { my $mapIterator = $navmap->getIterator(undef, undef, undef, 1); my $curRes; my $found = 0; - my $here_is_navmaps = 0; + my $here_is_navmaps = 0; if ($here =~ m{___\d+___adm/navmaps$}) { $here_is_navmaps = 1; } @@ -1559,10 +1573,15 @@ sub render { my $curRes; my $foundJump = 0; my $counter = 0; - + while (($curRes = $mapIterator->next()) && !$foundJump) { if (ref($curRes)) { $counter++; } - + # Speed up display after course initialization + # when $jump is empty. Note: we still need + # $counter to be 1 in that case if there is at + # least one resource. + last if (($jump eq '') && ($counter)); + if (ref($curRes) && $jump eq $curRes->symb()) { # This is why we have to use the main iterator instead of the @@ -1581,27 +1600,19 @@ sub render { my $printKey = $args->{'printKey'}; my $printCloseAll = $args->{'printCloseAll'}; if (!defined($printCloseAll)) { $printCloseAll = 1; } - + # Print key? if ($printKey) { - $result .= ''; - $result.=''; - my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc"); - if ($navmap->{LAST_CHECK}) { - $result .= - ' '.&mt('New discussion since').' '. - strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})). - ''; - } else { - $result .= ''; - } - - $result .= '
Key:    '. - ' '.&mt('New message (click to open)').'

'. - '

  '. - ' '.&mt('Discussions').''. - '   '.&mt('New message (click to open)'). - '
'; + my $location = &Apache::loncommon::lonhttpdurl("/adm/lonMisc"); + $result .= '
'."\n". + ''.&mt('Key').':'. + '  '. + ' '.&mt('Unread Discussion'). + '  '. + ' '. + &mt('New message (click to open)'). + '
'. + '
'."\n"; } if ($printCloseAll && !$args->{'resource_no_folder_link'}) { @@ -1628,17 +1639,19 @@ sub render { # Check for any unread discussions in all resources. if (($args->{'caller'} eq 'navmapsdisplay') && (!$args->{'notools'})) { + my $markread = 'Mark all posts read'; &add_linkitem($args->{'linkitems'},'clearbubbles', 'document.clearbubbles.submit()', - 'Mark all posts read'); + $markread); my $time=time; + my $submit = &mt($markread); my $querystr = &HTML::Entities::encode($ENV{'QUERY_STRING'},'<>&"'); $result .= (< +
'. - &mt('Content Editor').''; + $result .= ''; } } if ($args->{'sort_html'}) { - $result .= '   '. - ''.$args->{'sort_html'}.''; + $result .= '
   '. + $args->{'sort_html'}.'
'; } } if ($result) { - $result = "$result
"; + $result = '
'.$result.'
'."\n". + '
'."\n"; } return $result; } @@ -2274,10 +2292,17 @@ sub new { $self->{USERNAME} = shift || $env{'user.name'}; $self->{DOMAIN} = shift || $env{'user.domain'}; + $self->{SECTION} = shift; $self->{CODE} = shift; - $self->{NOHIDE} = shift; + $self->{NOHIDE} = shift; + if (($self->{SECTION} eq '') && ($env{'request.course.sec'} ne '')) { + if (($self->{USERNAME} eq $env{'user.name'}) && + ($self->{USERNAME} eq $env{'user.domain'})) { + $self->{SECTION} = $env{'request.course.sec'}; + } + } # Resource cache stores navmap resources as we reference them. We generate # them on-demand so we don't pay for creating resources unless we use them. @@ -2319,7 +2344,7 @@ sub new { $self->{PARM_HASH} = \%parmhash; $self->{PARM_CACHE} = {}; } else { - $self->change_user($self->{USERNAME}, $self->{DOMAIN}, $self->{CODE}, $self->{NOHIDE}); + $self->change_user($self->{USERNAME}, $self->{DOMAIN}, $self->{SECTION}, $self->{CODE}, $self->{NOHIDE}); } return $self; @@ -2330,15 +2355,17 @@ sub new { # username/domain associated with a navmap (e.g. to navigate for someone # else besides the current user...if sufficiently privileged. # Parameters: -# user - New user. -# domain- Domain the user belongs to. -# code - Anonymous CODE in use. +# user - New user. +# domain - Domain to which the user belongs. +# section - Section to which the user belongs. +# code - Anonymous CODE in use. # Implicit inputs: # sub change_user { my $self = shift; $self->{USERNAME} = shift; $self->{DOMAIN} = shift; + $self->{SECTION} = shift; $self->{CODE} = shift; $self->{NOHIDE} = shift; @@ -2361,7 +2388,7 @@ 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. $self->{PARM_CACHE} = {}; @@ -2826,6 +2853,7 @@ sub parmval { return $self->{PARM_CACHE}->{$hashkey}; } } + my $result = $self->parmval_real($what, $symb, $recurse); $self->{PARM_CACHE}->{$hashkey} = $result; if (wantarray) { @@ -2844,7 +2872,7 @@ sub parmval_real { $self->generate_course_user_opt(); my $cid=$env{'request.course.id'}; - my $csec=$env{'request.course.sec'}; + my $csec=$self->{SECTION}; my $cgroup=''; my @cgrps=split(/:/,$env{'request.course.groups'}); if (@cgrps > 0) { @@ -2863,29 +2891,35 @@ sub parmval_real { if ($fn =~ /ext\.tool$/) { $toolsymb = $symb; } + my ($recursed,@recurseup); + # ----------------------------------------------------- Cascading lookup scheme my $rwhat=$what; $what=~s/^parameter\_//; $what=~s/\_/\./; my $symbparm=$symb.'.'.$what; + my $recurseparm=$mapname.'___(rec).'.$what; my $mapparm=$mapname.'___(all).'.$what; my $usercourseprefix=$cid; - + my $grplevel=$usercourseprefix.'.['.$cgroup.'].'.$what; my $grplevelr=$usercourseprefix.'.['.$cgroup.'].'.$symbparm; + my $grpleveli=$usercourseprefix.'.['.$cgroup.'].'.$recurseparm; my $grplevelm=$usercourseprefix.'.['.$cgroup.'].'.$mapparm; my $seclevel= $usercourseprefix.'.['.$csec.'].'.$what; my $seclevelr=$usercourseprefix.'.['.$csec.'].'.$symbparm; + my $secleveli=$usercourseprefix.'.['.$csec.'].'.$recurseparm; my $seclevelm=$usercourseprefix.'.['.$csec.'].'.$mapparm; my $courselevel= $usercourseprefix.'.'.$what; my $courselevelr=$usercourseprefix.'.'.$symbparm; + my $courseleveli=$usercourseprefix.'.'.$recurseparm; my $courselevelm=$usercourseprefix.'.'.$mapparm; @@ -2897,6 +2931,23 @@ sub parmval_real { if ($uname and defined($useropt)) { if (defined($$useropt{$courselevelr})) { return [$$useropt{$courselevelr},'resource']; } 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']; } } @@ -2904,12 +2955,46 @@ sub parmval_real { if ($cgroup ne '' and defined($courseopt)) { if (defined($$courseopt{$grplevelr})) { return [$$courseopt{$grplevelr},'resource']; } 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 ($csec and defined($courseopt)) { + if ($csec ne '' and defined($courseopt)) { if (defined($$courseopt{$seclevelr})) { return [$$courseopt{$seclevelr},'resource']; } 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']; } } @@ -2933,6 +3018,25 @@ sub parmval_real { # --------------------------------------------------- fifth, check more course if (defined($courseopt)) { 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})) { my $ret = [$$courseopt{$courselevel},'course']; return $ret; @@ -2957,7 +3061,7 @@ sub parmval_real { } sub recurseup_maps { - my ($self,$mapname) = @_; + my ($self,$mapname,$getsymb) = @_; my @recurseup; if ($mapname) { my $res = $self->getResourceByUrl($mapname); @@ -2965,7 +3069,11 @@ sub recurseup_maps { my @pcs = split(/,/,$res->map_hierarchy()); shift(@pcs); if (@pcs) { - @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->src()); } reverse(@pcs); + if ($getsymb) { + @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->symb()); } reverse(@pcs); + } else { + @recurseup = map { &Apache::lonnet::declutter($self->getByMapPc($_)->src()); } reverse(@pcs); + } } } } @@ -3077,7 +3185,7 @@ sub map_printdates { 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); @@ -3093,7 +3201,7 @@ sub get_mapparam { # Get the course id and section if there is one. my $cid=$env{'request.course.id'}; - my $csec=$env{'request.course.sec'}; + my $csec=$self->{SECTION}; my $cgroup=''; my @cgrps=split(/:/,$env{'request.course.groups'}); if (@cgrps > 0) { @@ -3107,6 +3215,7 @@ sub get_mapparam { my $result=''; my ($recursed,@recurseup); + # Figure out which map we are in. if ($symb && !$mapname) { @@ -3115,21 +3224,25 @@ sub get_mapparam { $mapname = &Apache::lonnet::deversion($mapname); } + my $rwhat=$what; $what=~s/^parameter\_//; $what=~s/\_/\./; # Build the hash keys for the lookup: - my $symbparm=$symb.'.'.$what; my $mapparm=$mapname.'___(all).'.$what; + my $recurseparm=$mapname.'___(rec).'.$what; my $usercourseprefix=$cid; - my $grplevel = "$usercourseprefix.[$cgroup].$mapparm"; - my $seclevel = "$usercourseprefix.[$csec].$mapparm"; - my $courselevel = "$usercourseprefix.$mapparm"; - + my $grplevelm = "$usercourseprefix.[$cgroup].$mapparm"; + my $seclevelm = "$usercourseprefix.[$csec].$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: @@ -3142,22 +3255,29 @@ sub get_mapparam { if ($uname and defined($useropt)) { - if (defined($$useropt{$courselevel})) { - return $$useropt{$courselevel}; + if (defined($$useropt{$courselevelm})) { + return $$useropt{$courselevelm}; } - if ($what =~ /\.(encrypturl|hiddenresource)$/) { - unless ($recursed) { - @recurseup = $self->recurseup_maps($mapname); - $recursed = 1; - } - foreach my $item (@recurseup) { - my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what; - if (defined($$useropt{$norecursechk})) { - if ($what =~ /\.(encrypturl|hiddenresource)$/) { - return $$useropt{$norecursechk}; - } + if (defined($$useropt{$courseleveli})) { + return $$useropt{$courseleveli}; + } + 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}; + } else { + last; } } + my $recursechk=$usercourseprefix.'.'.$item.'___(rec).'.$what; + if (defined($$useropt{$recursechk})) { + return $$useropt{$recursechk}; + } } } @@ -3166,48 +3286,59 @@ sub get_mapparam { if ($cgroup ne '' and defined ($courseopt)) { - if (defined($$courseopt{$grplevel})) { - return $$courseopt{$grplevel}; + if (defined($$courseopt{$grplevelm})) { + return $$courseopt{$grplevelm}; } - if ($what =~ /\.(encrypturl|hiddenresource)$/) { - unless ($recursed) { - @recurseup = $self->recurseup_maps($mapname); - $recursed = 1; - } - foreach my $item (@recurseup) { - my $norecursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(all).'.$what; - if (defined($$courseopt{$norecursechk})) { - if ($what =~ /\.(encrypturl|hiddenresource)$/) { - return $$courseopt{$norecursechk}; - } + if (defined($$courseopt{$grpleveli})) { + return $$courseopt{$grpleveli}; + } + 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}; + } else { + last; } } + my $recursechk=$usercourseprefix.'.['.$cgroup.'].'.$item.'___(rec).'.$what; + if (defined($$courseopt{$recursechk})) { + return $$courseopt{$recursechk}; + } } } # Check course -- section - - - - if ($csec and defined($courseopt)) { - if (defined($$courseopt{$seclevel})) { - return $$courseopt{$seclevel}; + if ($csec ne '' and defined($courseopt)) { + if (defined($$courseopt{$seclevelm})) { + return $$courseopt{$seclevelm}; } - if ($what =~ /\.(encrypturl|hiddenresource)$/) { - unless ($recursed) { - @recurseup = $self->recurseup_maps($mapname); - $recursed = 1; - } - foreach my $item (@recurseup) { - my $norecursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(all).'.$what; - if (defined($$courseopt{$norecursechk})) { - if ($what =~ /\.(encrypturl|hiddenresource)$/) { - return $$courseopt{$norecursechk}; - } + if (defined($$courseopt{$secleveli})) { + return $$courseopt{$secleveli}; + } + 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}; + } else { + last; } } + my $recursechk=$usercourseprefix.'.['.$csec.'].'.$item.'___(rec).'.$what; + if (defined($$courseopt{$recursechk})) { + return $$courseopt{$recursechk}; + } } } # Check the map parameters themselves: @@ -3216,7 +3347,7 @@ sub get_mapparam { my $symbparm=$symb.'.'.$what; my $thisparm = $$parmhash{$symbparm}; if (defined($thisparm)) { - return $thisparm; + return $thisparm; } } @@ -3224,25 +3355,34 @@ sub get_mapparam { # Additional course parameters: if (defined($courseopt)) { - if (defined($$courseopt{$courselevel})) { - return $$courseopt{$courselevel}; + if (defined($$courseopt{$courselevelm})) { + return $$courseopt{$courselevelm}; } - if ($what =~ /\.(encrypturl|hiddenresource)$/) { - unless ($recursed) { - @recurseup = $self->recurseup_maps($mapname); - $recursed = 1; - } + if (defined($$courseopt{$courseleveli})) { + return $$courseopt{$courseleveli}; + } + unless ($recursed) { + @recurseup = $self->recurseup_maps($mapname); + $recursed = 1; + } + if (@recurseup) { foreach my $item (@recurseup) { my $norecursechk=$usercourseprefix.'.'.$item.'___(all).'.$what; if (defined($$courseopt{$norecursechk})) { if ($what =~ /\.(encrypturl|hiddenresource)$/) { 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 { @@ -3262,11 +3402,11 @@ sub getcourseparam { my $uname = $self->{USERNAME}; my $udom = $self->{DOMAIN}; + my $csec = $self->{SECTION}; - # Course, section, group ids come from the env: + # Course and group ids come from the env: my $cid = $env{'request.course.id'}; - my $csec = $env{'request.course.sec'}; my $cgroup = ''; # Assume no group my @cgroups = split(/:/, $env{'request.course.groups'}); @@ -3284,10 +3424,6 @@ sub getcourseparam { $what=~s/^parameter\_//; $what=~s/\_/\./; - - my $symbparm = $symb . '.' . $what; - my $mapparm=$mapname.'___(all).'.$what; - # Local refs to the hashes we're going to look at: my $useropt = $self->{USER_OPT}; @@ -4365,7 +4501,7 @@ sub new { # This is a speed optimization, to avoid calling symb() too often. $self->{SYMB} = $self->symb(); - + return $self; } @@ -4492,7 +4628,7 @@ sub enclosing_map_src { } sub symb { 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 $symbSrc = &Apache::lonnet::declutter($self->src()); my $symb = &Apache::lonnet::declutter($self->navHash('map_id_'.$first)) @@ -5050,10 +5186,10 @@ sub duedate { my $date; my @interval=$self->parmval("interval", $part); my $due_date=$self->parmval("duedate", $part); - if ($interval[0] =~ /(\d+)/) { + if ($interval[0] =~ /^(\d+)/) { my $timelimit = $1; my $first_access=&Apache::lonnet::get_first_access($interval[1], - $self->{SYMB}); + $self->{SYMB}); if (defined($first_access)) { my $interval = $first_access+$timelimit; $date = (!$due_date || $interval < $due_date) ? $interval @@ -5128,7 +5264,7 @@ sub weight { my $weight = &Apache::lonnet::EXT('resource.'.$part.'.weight', $self->{SYMB}, $self->{DOMAIN}, $self->{USERNAME}, - $env{'request.course.sec'}); + $self->{SECTION}); return $weight; } sub part_display { @@ -5195,8 +5331,14 @@ sub getReturnHash { my $self = shift; if (!defined($self->{RETURN_HASH})) { - my %tmpHash = &Apache::lonnet::restore($self->{SYMB},undef,$self->{DOMAIN},$self->{USERNAME}); - $self->{RETURN_HASH} = \%tmpHash; + #my %tmpHash = &Apache::lonnet::restore($self->{SYMB},undef,$self->{DOMAIN},$self->{USERNAME}); + #$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}}; } } @@ -5977,7 +6119,7 @@ sub status { # If it's WRONG... and not open if ( ($completionStatus == INCORRECT || $completionStatus == INCORRECT_BY_OVERRIDE || - $completionStatus == INCORRECT_BY_PASSBACK) + $completionStatus == INCORRECT_BY_PASSBACK) && (!$self->opendate($part) || $self->opendate($part) > time()) ) { return INCORRECT; } @@ -6136,12 +6278,29 @@ sub check_for_slot { my $reservable = &Apache::lonnet::get_reservable_slots($cnum,$cdom,$env{'user.name'}, $env{'user.domain'}); if (ref($reservable) eq 'HASH') { + my ($map) = &Apache::lonnet::decode_symb($symb); if ((ref($reservable->{'now_order'}) eq 'ARRAY') && (ref($reservable->{'now'}) eq 'HASH')) { foreach my $slot (reverse (@{$reservable->{'now_order'}})) { my $canuse; - if (($reservable->{'now'}{$slot}{'symb'} eq '') || - ($reservable->{'now'}{$slot}{'symb'} eq $symb)) { + if ($reservable->{'now'}{$slot}{'symb'} eq '') { $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 ($checkedin) { @@ -6162,8 +6321,26 @@ sub check_for_slot { if ((ref($reservable->{'future_order'}) eq 'ARRAY') && (ref($reservable->{'future'}) eq 'HASH')) { foreach my $slot (@{$reservable->{'future_order'}}) { my $canuse; - if (($reservable->{'future'}{$slot}{'symb'} eq '') || - ($reservable->{'future'}{$slot}{'symb'} eq $symb)) { + if ($reservable->{'future'}{$slot}{'symb'} eq '') { + $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; } if ($canuse) {