--- loncom/lonnet/perl/lonnet.pm	2016/03/02 14:14:14	1.1301
+++ loncom/lonnet/perl/lonnet.pm	2016/04/02 04:31:03	1.1305
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1301 2016/03/02 14:14:14 raeburn Exp $
+# $Id: lonnet.pm,v 1.1305 2016/04/02 04:31:03 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -2190,7 +2190,7 @@ sub get_domain_defaults {
                                   'coursedefaults','usersessions',
                                   'requestauthor','selfenrollment',
                                   'coursecategories'],$domain);
-    my @coursetypes = ('official','unofficial','community','textbook');
+    my @coursetypes = ('official','unofficial','community','textbook','placement');
     if (ref($domconfig{'defaults'}) eq 'HASH') {
         $domdefaults{'lang_def'} = $domconfig{'defaults'}{'lang_def'}; 
         $domdefaults{'auth_def'} = $domconfig{'defaults'}{'auth_def'};
@@ -2220,7 +2220,7 @@ sub get_domain_defaults {
         }
     }
     if (ref($domconfig{'requestcourses'}) eq 'HASH') {
-        foreach my $item ('official','unofficial','community','textbook') {
+        foreach my $item ('official','unofficial','community','textbook','placement') {
             $domdefaults{$item} = $domconfig{'requestcourses'}{$item};
         }
     }
@@ -6530,6 +6530,7 @@ sub usertools_access {
                       unofficial => 1,
                       community  => 1,
                       textbook   => 1,
+                      placement  => 1,
                  );
     } elsif ($context eq 'requestauthor') {
         %tools = (
@@ -7263,7 +7264,7 @@ sub allowed {
        my $unamedom=$env{'user.name'}.':'.$env{'user.domain'};
        if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.roles.denied'}
 	   =~/\Q$rolecode\E/) {
-	   if (($priv ne 'pch') && ($priv ne 'plc')) { 
+	   if (($priv ne 'pch') && ($priv ne 'plc') && ($priv ne 'pac')) {
 	       &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.':'.
 			'Denied by role: '.$priv.' for '.$uri.' as '.$rolecode.' in '.
 			$env{'request.course.id'});
@@ -7273,7 +7274,7 @@ sub allowed {
 
        if ($env{'course.'.$env{'request.course.id'}.'.'.$priv.'.users.denied'}
 	   =~/\Q$unamedom\E/) {
-	   if (($priv ne 'pch') && ($priv ne 'plc')) { 
+	   if (($priv ne 'pch') && ($priv ne 'plc') && ($priv ne 'pac')) {
 	       &logthis($env{'user.domain'}.':'.$env{'user.name'}.':'.$env{'user.home'}.
 			'Denied by user: '.$priv.' for '.$uri.' as '.$unamedom.' in '.
 			$env{'request.course.id'});
@@ -8277,6 +8278,11 @@ sub auto_crsreq_update {
     return \%crsreqresponse;
 }
 
+sub auto_export_grades {
+    my ($cnum,$cdom,$gradesref) = @_;
+    return;
+}
+
 sub check_instcode_cloning {
     my ($codedefaults,$code_order,$cloner,$clonefromcode,$clonetocode) = @_;
     unless ((ref($codedefaults) eq 'HASH') && (ref($code_order) eq 'ARRAY')) {
@@ -8498,6 +8504,7 @@ sub plaintext {
     my %rolenames = (
                       Course    => 'std',
                       Community => 'alt1',
+                      Placement => 'std',
                     );
     if ($cid ne '') {
         if ($env{'course.'.$cid.'.'.$short.'.plaintext'} ne '') {
@@ -10341,7 +10348,7 @@ sub resdata {
         if ($item->[1] eq 'course') {
             if ((ref($recurseup) eq 'ARRAY') && (ref($recursed) eq 'SCALAR')) {
                 unless ($$recursed) {
-                    @{$recurseup} = &get_map_hierarchy($mapp);
+                    @{$recurseup} = &get_map_hierarchy($mapp,$courseid);
                     $$recursed = 1;
                 }
                 foreach my $item (@${recurseup}) {
@@ -10402,6 +10409,16 @@ sub get_numsuppfiles {
 # EXT resource caching routines
 #
 
+{
+# Cache (5 seconds) of map hierarchy for speedup of navmaps display
+#
+# The course for which we cache
+my $cachedmapkey='';
+# The cached recursive maps for this course
+my %cachedmaps=();
+# When this was last done
+my $cachedmaptime='';
+
 sub clear_EXT_cache_status {
     &delenv('cache.EXT.');
 }
@@ -10592,7 +10609,6 @@ sub EXT {
 # ----------------------------------------------------- Cascading lookup scheme
 	    my $symbp=$symbparm;
 	    $mapp=&deversion((&decode_symb($symbp))[0]);
-            @recurseup=();
 	    my $symbparm=$symbp.'.'.$spacequalifierrest;
             my $recurseparm=$mapp.'___(rec).'.$spacequalifierrest;
 	    my $mapparm=$mapp.'___(all).'.$spacequalifierrest;
@@ -10764,18 +10780,29 @@ sub check_group_parms {
 }
 
 sub get_map_hierarchy {
-    my ($mapname) = @_;
-    my @recurseup = (); 
+    my ($mapname,$courseid) = @_;
+    my @recurseup = ();
     if ($mapname) {
+        if (($cachedmapkey eq $courseid) &&
+            (abs($cachedmaptime-time)<5)) {
+            if (ref($cachedmaps{$mapname}) eq 'ARRAY') {
+                return @{$cachedmaps{$mapname}};
+            }
+        }
         my $navmap = Apache::lonnavmaps::navmap->new();
         if (ref($navmap)) {
             @recurseup = $navmap->recurseup_maps($mapname);
             undef($navmap);
+            $cachedmaps{$mapname} = \@recurseup;
+            $cachedmaptime=time;
+            $cachedmapkey=$courseid;
         }
     }
     return @recurseup;
 }
 
+}
+
 sub sort_course_groups { # Sort groups based on defined rankings. Default is sort().
     my ($courseid,@groups) = @_;
     @groups = sort(@groups);
@@ -13064,10 +13091,17 @@ BEGIN {
                 my $name = $token->[2]{'name'};
                 my $value = $token->[2]{'value'};
                 my $valuematch = $token->[2]{'valuematch'};
-                if ($item ne '' && $name ne '' && ($value ne '' || $valuematch ne '')) {
+                my $namematch = $token->[2]{'namematch'};
+                if ($item eq 'parameter') {
+                    if (($namematch ne '') || (($name ne '') && ($value ne '' || $valuematch ne ''))) {
+                        my $release = $parser->get_text();
+                        $release =~ s/(^\s*|\s*$ )//gx;
+                        $needsrelease{$item.':'.$name.':'.$value.':'.$valuematch.':'.$namematch} = $release;
+                    }
+                } elsif ($item ne '' && $name ne '') {
                     my $release = $parser->get_text();
                     $release =~ s/(^\s*|\s*$ )//gx;
-                    $needsrelease{$item.':'.$name.':'.$value.':'.$valuematch} = $release;
+                    $needsrelease{$item.':'.$name.':'.$value} = $release;
                 }
             }
         }
@@ -14127,7 +14161,7 @@ requestcourses: ability to request cours
 =over
 
 =item
-official, unofficial, community, textbook
+official, unofficial, community, textbook, placement
 
 =back
 
@@ -14149,7 +14183,7 @@ for course's uploaded content.
 
 =item
 canuse_pdfforms, officialcredits, unofficialcredits, textbookcredits, officialquota, unofficialquota, 
-communityquota, textbookquota
+communityquota, textbookquota, placementquota
 
 =back