--- loncom/lonnet/perl/lonnet.pm	2015/03/11 04:21:21	1.1172.2.60
+++ loncom/lonnet/perl/lonnet.pm	2015/05/22 18:06:13	1.1172.2.68
@@ -1,7 +1,7 @@
 # The LearningOnline Network
 # TCP networking package
 #
-# $Id: lonnet.pm,v 1.1172.2.60 2015/03/11 04:21:21 raeburn Exp $
+# $Id: lonnet.pm,v 1.1172.2.68 2015/05/22 18:06:13 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -844,10 +844,8 @@ sub spareserver {
     if (ref($spareshash) eq 'HASH') {
         if (ref($spareshash->{'primary'}) eq 'ARRAY') {
             foreach my $try_server (@{ $spareshash->{'primary'} }) {
-                if ($uint_dom) {
-                    next unless (&spare_can_host($udom,$uint_dom,$remotesessions,
-                                                 $try_server));
-                }
+                next unless (&spare_can_host($udom,$uint_dom,$remotesessions,
+                                             $try_server));
 	        ($spare_server, $lowest_load) =
 	            &compare_server_load($try_server, $spare_server, $lowest_load);
             }
@@ -858,10 +856,8 @@ sub spareserver {
         if (!$found_server) {
             if (ref($spareshash->{'default'}) eq 'ARRAY') { 
 	        foreach my $try_server (@{ $spareshash->{'default'} }) {
-                    if ($uint_dom) {
-                        next unless (&spare_can_host($udom,$uint_dom,
-                                                     $remotesessions,$try_server));
-                    }
+                    next unless (&spare_can_host($udom,$uint_dom,
+                                                 $remotesessions,$try_server));
 	            ($spare_server, $lowest_load) =
 		        &compare_server_load($try_server, $spare_server, $lowest_load);
                 }
@@ -1173,19 +1169,27 @@ sub can_host_session {
 sub spare_can_host {
     my ($udom,$uint_dom,$remotesessions,$try_server)=@_;
     my $canhost=1;
-    my @intdoms;
-    my $internet_names = &Apache::lonnet::get_internet_names($try_server);
-    if (ref($internet_names) eq 'ARRAY') {
-        @intdoms = @{$internet_names};
-    }
-    unless (grep(/^\Q$uint_dom\E$/,@intdoms)) {
-        my $serverhomeID = &Apache::lonnet::get_server_homeID($try_server);
-        my $serverhomedom = &Apache::lonnet::host_domain($serverhomeID);
-        my %defdomdefaults = &Apache::lonnet::get_domain_defaults($serverhomedom);
-        my $remoterev = &Apache::lonnet::get_server_loncaparev(undef,$try_server);
-        $canhost = &can_host_session($udom,$try_server,$remoterev,
-                                     $remotesessions,
-                                     $defdomdefaults{'hostedsessions'});
+    my $try_server_hostname = &hostname($try_server);
+    my $serverhomeID = &get_server_homeID($try_server_hostname);
+    my $serverhomedom = &host_domain($serverhomeID);
+    my %defdomdefaults = &get_domain_defaults($serverhomedom);
+    if (ref($defdomdefaults{'offloadnow'}) eq 'HASH') {
+        if ($defdomdefaults{'offloadnow'}{$try_server}) {
+            $canhost = 0;
+        }
+    }
+    if (($canhost) && ($uint_dom)) {
+        my @intdoms;
+        my $internet_names = &get_internet_names($try_server);
+        if (ref($internet_names) eq 'ARRAY') {
+            @intdoms = @{$internet_names};
+        }
+        unless (grep(/^\Q$uint_dom\E$/,@intdoms)) {
+            my $remoterev = &get_server_loncaparev(undef,$try_server);
+            $canhost = &can_host_session($udom,$try_server,$remoterev,
+                                         $remotesessions,
+                                         $defdomdefaults{'hostedsessions'});
+        }
     }
     return $canhost;
 }
@@ -2091,6 +2095,16 @@ sub get_domain_defaults {
                 }
             }
         }
+        if (ref($domconfig{'coursedefaults'}{'canclone'}) eq 'HASH') {
+            if (ref($domconfig{'coursedefaults'}{'canclone'}{'instcode'}) eq 'ARRAY') {
+                my @clonecodes = @{$domconfig{'coursedefaults'}{'canclone'}{'instcode'}};
+                if (@clonecodes) {
+                    $domdefaults{'canclone'} = join('+',@clonecodes);
+                }
+            }
+        } elsif ($domconfig{'coursedefaults'}{'canclone'}) {
+            $domdefaults{'canclone'}=$domconfig{'coursedefaults'}{'canclone'};
+        }
     }
     if (ref($domconfig{'usersessions'}) eq 'HASH') {
         if (ref($domconfig{'usersessions'}{'remote'}) eq 'HASH') {
@@ -2099,6 +2113,9 @@ sub get_domain_defaults {
         if (ref($domconfig{'usersessions'}{'hosted'}) eq 'HASH') {
             $domdefaults{'hostedsessions'} = $domconfig{'usersessions'}{'hosted'};
         }
+        if (ref($domconfig{'usersessions'}{'offloadnow'}) eq 'HASH') {
+            $domdefaults{'offloadnow'} = $domconfig{'usersessions'}{'offloadnow'};
+        }
     }
     if (ref($domconfig{'selfenrollment'}) eq 'HASH') {
         if (ref($domconfig{'selfenrollment'}{'admin'}) eq 'HASH') {
@@ -4254,7 +4271,7 @@ sub courseiddump {
         $coursefilter,$hostidflag,$hostidref,$typefilter,$regexp_ok,
         $selfenrollonly,$catfilter,$showhidden,$caller,$cloner,$cc_clone,
         $cloneonly,$createdbefore,$createdafter,$creationcontext,$domcloner,
-        $hasuniquecode)=@_;
+        $hasuniquecode,$reqcrsdom,$reqinstcode)=@_;
     my $as_hash = 1;
     my %returnhash;
     if (!$domfilter) { $domfilter=''; }
@@ -4277,7 +4294,8 @@ sub courseiddump {
                                 &escape($catfilter), $showhidden, $caller,
                                 &escape($cloner), &escape($cc_clone), $cloneonly,
                                 &escape($createdbefore), &escape($createdafter),
-                                &escape($creationcontext), $domcloner, $hasuniquecode)));
+                                &escape($creationcontext),$domcloner,$hasuniquecode,
+                                $reqcrsdom,&escape($reqinstcode))));
                 } else {
                     $rep = &reply('courseiddump:'.&host_domain($tryserver).':'.
                              $sincefilter.':'.&escape($descfilter).':'.
@@ -4288,8 +4306,8 @@ sub courseiddump {
                              $showhidden.':'.$caller.':'.&escape($cloner).':'.
                              &escape($cc_clone).':'.$cloneonly.':'.
                              &escape($createdbefore).':'.&escape($createdafter).':'.
-                             &escape($creationcontext).':'.$domcloner.':'.$hasuniquecode,
-                             $tryserver);
+                             &escape($creationcontext).':'.$domcloner.':'.$hasuniquecode.
+                             ':'.$reqcrsdom.':'.&escape($reqinstcode),$tryserver);
                 }
 
                 my @pairs=split(/\&/,$rep);
@@ -4428,7 +4446,7 @@ my $cachedkey='';
 # The cached times for this user
 my %cachedtimes=();
 # When this was last done
-my $cachedtime=();
+my $cachedtime='';
 
 sub load_all_first_access {
     my ($uname,$udom)=@_;
@@ -4946,7 +4964,7 @@ sub tmprestore {
 # ----------------------------------------------------------------------- Store
 
 sub store {
-    my ($storehash,$symb,$namespace,$domain,$stuname) = @_;
+    my ($storehash,$symb,$namespace,$domain,$stuname,$laststore) = @_;
     my $home='';
 
     if ($stuname) { $home=&homeserver($stuname,$domain); }
@@ -4976,13 +4994,13 @@ sub store {
     }
     $namevalue=~s/\&$//;
     &courselog($symb.':'.$stuname.':'.$domain.':STORE:'.$namevalue);
-    return reply("store:$domain:$stuname:$namespace:$symb:$namevalue","$home");
+    return reply("store:$domain:$stuname:$namespace:$symb:$namevalue:$laststore","$home");
 }
 
 # -------------------------------------------------------------- Critical Store
 
 sub cstore {
-    my ($storehash,$symb,$namespace,$domain,$stuname) = @_;
+    my ($storehash,$symb,$namespace,$domain,$stuname,$laststore) = @_;
     my $home='';
 
     if ($stuname) { $home=&homeserver($stuname,$domain); }
@@ -5013,7 +5031,7 @@ sub cstore {
     $namevalue=~s/\&$//;
     &courselog($symb.':'.$stuname.':'.$domain.':CSTORE:'.$namevalue);
     return critical
-                ("store:$domain:$stuname:$namespace:$symb:$namevalue","$home");
+                ("store:$domain:$stuname:$namespace:$symb:$namevalue:$laststore","$home");
 }
 
 # --------------------------------------------------------------------- Restore
@@ -6692,7 +6710,7 @@ sub customaccess {
 # ------------------------------------------------- Check for a user privilege
 
 sub allowed {
-    my ($priv,$uri,$symb,$role)=@_;
+    my ($priv,$uri,$symb,$role,$clientip,$noblockcheck)=@_;
     my $ver_orguri=$uri;
     $uri=&deversion($uri);
     my $orguri=$uri;
@@ -6887,11 +6905,16 @@ sub allowed {
         if ($match) {
             if ($env{'user.priv.'.$env{'request.role'}.'./'}
                   =~/\Q$priv\E\&([^\:]*)/) {
-                my @blockers = &has_comm_blocking($priv,$symb,$uri);
-                if (@blockers > 0) {
-                    $thisallowed = 'B';
+                my $value = $1;
+                if ($noblockcheck) {
+                    $thisallowed.=$value;
                 } else {
-                    $thisallowed.=$1;
+                    my @blockers = &has_comm_blocking($priv,$symb,$uri);
+                    if (@blockers > 0) {
+                        $thisallowed = 'B';
+                    } else {
+                        $thisallowed.=$value;
+                    }
                 }
             }
         } else {
@@ -6903,11 +6926,15 @@ sub allowed {
                     $refuri=&declutter($refuri);
                     my ($match) = &is_on_map($refuri);
                     if ($match) {
-                        my @blockers = &has_comm_blocking($priv,$symb,$refuri);
-                        if (@blockers > 0) {
-                            $thisallowed = 'B';
-                        } else {
+                        if ($noblockcheck) {
                             $thisallowed='F';
+                        } else {
+                            my @blockers = &has_comm_blocking($priv,$symb,$refuri);
+                            if (@blockers > 0) {
+                                $thisallowed = 'B';
+                            } else {
+                                $thisallowed='F';
+                            }
                         }
                     }
                 }
@@ -6962,11 +6989,15 @@ sub allowed {
                =~/\Q$priv\E\&([^\:]*)/) {
                my $value = $1;
                if ($priv eq 'bre') {
-                   my @blockers = &has_comm_blocking($priv,$symb,$uri);
-                   if (@blockers > 0) {
-                       $thisallowed = 'B';
-                   } else {
+                   if ($noblockcheck) {
                        $thisallowed.=$value;
+                   } else {
+                       my @blockers = &has_comm_blocking($priv,$symb,$uri);
+                       if (@blockers > 0) {
+                           $thisallowed = 'B';
+                       } else {
+                           $thisallowed.=$value;
+                       }
                    }
                } else {
                    $thisallowed.=$value;
@@ -7000,11 +7031,15 @@ sub allowed {
                   =~/\Q$priv\E\&([^\:]*)/) {
                   my $value = $1;
                   if ($priv eq 'bre') {
-                      my @blockers = &has_comm_blocking($priv,$symb,$refuri);
-                      if (@blockers > 0) {
-                          $thisallowed = 'B';
-                      } else {
+                      if ($noblockcheck) {
                           $thisallowed.=$value;
+                      } else {
+                          my @blockers = &has_comm_blocking($priv,$symb,$refuri);
+                          if (@blockers > 0) {
+                              $thisallowed = 'B';
+                          } else {
+                              $thisallowed.=$value;
+                          }
                       }
                   } else {
                       $thisallowed.=$value;
@@ -7235,6 +7270,32 @@ sub constructaccess {
     return '';
 }
 
+# ----------------------------------------------------------- Content Blocking
+
+{
+# Caches for faster Course Contents display where content blocking
+# is in operation (i.e., interval param set) for timed quiz.
+#
+# User for whom data are being temporarily cached.
+my $cacheduser='';
+# Cached blockers for this user (a hash of blocking items).
+my %cachedblockers=();
+# When the data were last cached.
+my $cachedlast='';
+
+sub load_all_blockers {
+    my ($uname,$udom,$blocks)=@_;
+    if (($uname ne '') && ($udom ne '')) {
+        if (($cacheduser eq $uname.':'.$udom) &&
+            (abs($cachedlast-time)<5)) {
+            return;
+        }
+    }
+    $cachedlast=time;
+    $cacheduser=$uname.':'.$udom;
+    %cachedblockers = &get_commblock_resources($blocks);
+}
+
 sub get_comm_blocks {
     my ($cdom,$cnum) = @_;
     if ($cdom eq '' || $cnum eq '') {
@@ -7255,27 +7316,21 @@ sub get_comm_blocks {
     return %commblocks;
 }
 
-sub has_comm_blocking {
-    my ($priv,$symb,$uri,$blocks) = @_;
-    return unless ($env{'request.course.id'});
-    return unless ($priv eq 'bre');
-    return if ($env{'user.priv.'.$env{'request.role'}} =~/evb\&([^\:]*)/);
+sub get_commblock_resources {
+    my ($blocks) = @_;
+    my %blockers = ();
+    return %blockers unless ($env{'request.course.id'});
+    return %blockers if ($env{'user.priv.'.$env{'request.role'}} =~/evb\&([^\:]*)/);
     my %commblocks;
     if (ref($blocks) eq 'HASH') {
         %commblocks = %{$blocks};
     } else {
         %commblocks = &get_comm_blocks();
     }
-    return unless (keys(%commblocks) > 0);
-    if (!$symb) { $symb=&symbread($uri,1); }
-    my ($map,$resid,undef)=&decode_symb($symb);
-    my %tocheck = (
-                    maps      => $map,
-                    resources => $symb,
-                  );
-    my @blockers;
-    my $now = time;
+    return %blockers unless (keys(%commblocks) > 0);
     my $navmap = Apache::lonnavmaps::navmap->new();
+    return %blockers unless (ref($navmap));
+    my $now = time;
     foreach my $block (keys(%commblocks)) {
         if ($block =~ /^(\d+)____(\d+)$/) {
             my ($start,$end) = ($1,$2);
@@ -7283,17 +7338,13 @@ sub has_comm_blocking {
                 if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
                     if (ref($commblocks{$block}{'blocks'}{'docs'}) eq 'HASH') {
                         if (ref($commblocks{$block}{'blocks'}{'docs'}{'maps'}) eq 'HASH') {
-                            if ($commblocks{$block}{'blocks'}{'docs'}{'maps'}{$map}) {
-                                unless (grep(/^\Q$block\E$/,@blockers)) {
-                                    push(@blockers,$block);
-                                }
+                            if (keys(%{$commblocks{$block}{'blocks'}{'docs'}{'maps'}})) {
+                                $blockers{$block}{maps} = $commblocks{$block}{'blocks'}{'docs'}{'maps'};
                             }
                         }
                         if (ref($commblocks{$block}{'blocks'}{'docs'}{'resources'}) eq 'HASH') {
-                            if ($commblocks{$block}{'blocks'}{'docs'}{'resources'}{$symb}) {
-                                unless (grep(/^\Q$block\E$/,@blockers)) {  
-                                    push(@blockers,$block);
-                                }
+                            if (keys(%{$commblocks{$block}{'blocks'}{'docs'}{'resources'}})) {
+                                $blockers{$block}{'resources'} = $commblocks{$block}{'blocks'}{'docs'}{'resources'};
                             }
                         }
                     }
@@ -7304,32 +7355,31 @@ sub has_comm_blocking {
             my @to_test;
             if (ref($commblocks{$block}{'blocks'}) eq 'HASH') {
                 if (ref($commblocks{$block}{'blocks'}{'docs'}) eq 'HASH') {
-                    my $check_interval;
-                    if (&check_docs_block($commblocks{$block}{'blocks'}{'docs'},\%tocheck)) {
-                        my @interval;
-                        my $type = 'map';
-                        if ($item eq 'course') {
-                            $type = 'course';
-                            @interval=&EXT("resource.0.interval");
+                    my @interval;
+                    my $type = 'map';
+                    if ($item eq 'course') {
+                        $type = 'course';
+                        @interval=&EXT("resource.0.interval");
+                    } else {
+                        if ($item =~ /___\d+___/) {
+                            $type = 'resource';
+                            @interval=&EXT("resource.0.interval",$item);
+                            if (ref($navmap)) {
+                                my $res = $navmap->getBySymb($item);
+                                push(@to_test,$res);
+                            }
                         } else {
-                            if ($item =~ /___\d+___/) {
-                                $type = 'resource';
-                                @interval=&EXT("resource.0.interval",$item);
-                                if (ref($navmap)) {                        
-                                    my $res = $navmap->getBySymb($item); 
-                                    push(@to_test,$res);
-                                }
-                            } else {
-                                my $mapsymb = &symbread($item,1);
-                                if ($mapsymb) {
-                                    if (ref($navmap)) {
-                                        my $mapres = $navmap->getBySymb($mapsymb);
-                                        @to_test = $mapres->retrieveResources($mapres,undef,0,1);
-                                        foreach my $res (@to_test) {
-                                            my $symb = $res->symb();
-                                            next if ($symb eq $mapsymb);
-                                            if ($symb ne '') {
-                                                @interval=&EXT("resource.0.interval",$symb);
+                            my $mapsymb = &symbread($item,1);
+                            if ($mapsymb) {
+                                if (ref($navmap)) {
+                                    my $mapres = $navmap->getBySymb($mapsymb);
+                                    @to_test = $mapres->retrieveResources($mapres,undef,0,0,0,1);
+                                    foreach my $res (@to_test) {
+                                        my $symb = $res->symb();
+                                        next if ($symb eq $mapsymb);
+                                        if ($symb ne '') {
+                                            @interval=&EXT("resource.0.interval",$symb);
+                                            if ($interval[1] eq 'map') {
                                                 last;
                                             }
                                         }
@@ -7337,26 +7387,35 @@ sub has_comm_blocking {
                                 }
                             }
                         }
-                        if ($interval[0] =~ /\d+/) {
-                            my $first_access;
-                            if ($type eq 'resource') {
-                                $first_access=&get_first_access($interval[1],$item);
-                            } elsif ($type eq 'map') {
-                                $first_access=&get_first_access($interval[1],undef,$item);
-                            } else {
-                                $first_access=&get_first_access($interval[1]);
-                            }
-                            if ($first_access) {
-                                my $timesup = $first_access+$interval[0];
-                                if ($timesup > $now) {
-                                    foreach my $res (@to_test) {
-                                        if ($res->is_problem()) {
-                                            if ($res->completable()) {
-                                                unless (grep(/^\Q$block\E$/,@blockers)) {
-                                                    push(@blockers,$block);
-                                                }
-                                                last;
-                                            }
+                    }
+                    if ($interval[0] =~ /^\d+$/) {
+                        my $first_access;
+                        if ($type eq 'resource') {
+                            $first_access=&get_first_access($interval[1],$item);
+                        } elsif ($type eq 'map') {
+                            $first_access=&get_first_access($interval[1],undef,$item);
+                        } else {
+                            $first_access=&get_first_access($interval[1]);
+                        }
+                        if ($first_access) {
+                            my $timesup = $first_access+$interval[0];
+                            if ($timesup > $now) {
+                                my $activeblock;
+                                foreach my $res (@to_test) {
+                                    if ($res->answerable()) {
+                                        $activeblock = 1;
+                                        last;
+                                    }
+                                }
+                                if ($activeblock) {
+                                    if (ref($commblocks{$block}{'blocks'}{'docs'}{'maps'}) eq 'HASH') {
+                                         if (keys(%{$commblocks{$block}{'blocks'}{'docs'}{'maps'}})) {
+                                             $blockers{$block}{'maps'} = $commblocks{$block}{'blocks'}{'docs'}{'maps'};
+                                         }
+                                    }
+                                    if (ref($commblocks{$block}{'blocks'}{'docs'}{'resources'}) eq 'HASH') {
+                                        if (keys(%{$commblocks{$block}{'blocks'}{'docs'}{'resources'}})) {
+                                            $blockers{$block}{'resources'} = $commblocks{$block}{'blocks'}{'docs'}{'resources'};
                                         }
                                     }
                                 }
@@ -7367,33 +7426,66 @@ sub has_comm_blocking {
             }
         }
     }
-    return @blockers;
+    return %blockers;
 }
 
-sub check_docs_block {
-    my ($docsblock,$tocheck) =@_;
-    if ((ref($docsblock) ne 'HASH') || (ref($tocheck) ne 'HASH')) {
-        return;
+sub has_comm_blocking {
+    my ($priv,$symb,$uri,$blocks) = @_;
+    my @blockers;
+    return unless ($env{'request.course.id'});
+    return unless ($priv eq 'bre');
+    return if ($env{'user.priv.'.$env{'request.role'}} =~/evb\&([^\:]*)/);
+    return if ($env{'request.state'} eq 'construct');
+    &load_all_blockers($env{'user.name'},$env{'user.domain'},$blocks);
+    return unless (keys(%cachedblockers) > 0);
+    my (%possibles,@symbs);
+    if (!$symb) {
+        $symb = &symbread($uri,1,1,1,\%possibles);
     }
-    if (ref($docsblock->{'maps'}) eq 'HASH') {
-        if ($tocheck->{'maps'}) {
-            if ($docsblock->{'maps'}{$tocheck->{'maps'}}) {
-                return 1;
+    if ($symb) {
+        @symbs = ($symb);
+    } elsif (keys(%possibles)) {
+        @symbs = keys(%possibles);
+    }
+    my $noblock;
+    foreach my $symb (@symbs) {
+        last if ($noblock);
+        my ($map,$resid,$resurl)=&decode_symb($symb);
+        foreach my $block (keys(%cachedblockers)) {
+            if ($block =~ /^firstaccess____(.+)$/) {
+                my $item = $1;
+                if (($item eq $map) || ($item eq $symb)) {
+                    $noblock = 1;
+                    last;
+                }
             }
-        }
-    }
-    if (ref($docsblock->{'resources'}) eq 'HASH') {
-        if ($tocheck->{'resources'}) {
-            if ($docsblock->{'resources'}{$tocheck->{'resources'}}) {
-                return 1;
+            if (ref($cachedblockers{$block}) eq 'HASH') {
+                if (ref($cachedblockers{$block}{'resources'}) eq 'HASH') {
+                    if ($cachedblockers{$block}{'resources'}{$symb}) {
+                        unless (grep(/^\Q$block\E$/,@blockers)) {
+                            push(@blockers,$block);
+                        }
+                    }
+                }
+            }
+            if (ref($cachedblockers{$block}{'maps'}) eq 'HASH') {
+                if ($cachedblockers{$block}{'maps'}{$map}) {
+                    unless (grep(/^\Q$block\E$/,@blockers)) {
+                        push(@blockers,$block);
+                    }
+                }
             }
         }
     }
-    return;
+    return if ($noblock);
+    return @blockers;
 }
+}
+
+# -------------------------------- Deversion and split uri into path an filename
 
 #
-#   Removes the versino from a URI and
+#   Removes the version from a URI and
 #   splits it in to its filename and path to the filename.
 #   Seems like File::Basename could have done this more clearly.
 #   Parameters:
@@ -8067,6 +8159,80 @@ sub auto_crsreq_update {
     return \%crsreqresponse;
 }
 
+sub check_instcode_cloning {
+    my ($codedefaults,$code_order,$cloner,$clonefromcode,$clonetocode) = @_;
+    unless ((ref($codedefaults) eq 'HASH') && (ref($code_order) eq 'ARRAY')) {
+        return;
+    }
+    my $canclone;
+    if (@{$code_order} > 0) {
+        my $instcoderegexp ='^';
+        my @clonecodes = split(/\&/,$cloner);
+        foreach my $item (@{$code_order}) {
+            if (grep(/^\Q$item\E=/,@clonecodes)) {
+                foreach my $pair (@clonecodes) {
+                    my ($key,$val) = split(/\=/,$pair,2);
+                    $val = &unescape($val);
+                    if ($key eq $item) {
+                        $instcoderegexp .= '('.$val.')';
+                        last;
+                    }
+                }
+            } else {
+                $instcoderegexp .= $codedefaults->{$item};
+            }
+        }
+        $instcoderegexp .= '$';
+        my (@from,@to);
+        eval {
+               (@from) = ($clonefromcode =~ /$instcoderegexp/);
+               (@to) = ($clonetocode =~ /$instcoderegexp/);
+        };
+        if ((@from > 0) && (@to > 0)) {
+            my @diffs = &Apache::loncommon::compare_arrays(\@from,\@to);
+            if (!@diffs) {
+                $canclone = 1;
+            }
+        }
+    }
+    return $canclone;
+}
+
+sub default_instcode_cloning {
+    my ($clonedom,$domdefclone,$clonefromcode,$clonetocode,$codedefaultsref,$codeorderref) = @_;
+    my (%codedefaults,@code_order,$canclone);
+    if ((ref($codedefaultsref) eq 'HASH') && (ref($codeorderref) eq 'ARRAY')) {
+        %codedefaults = %{$codedefaultsref};
+        @code_order = @{$codeorderref};
+    } elsif ($clonedom) {
+        &auto_instcode_defaults($clonedom,\%codedefaults,\@code_order);
+    }
+    if (($domdefclone) && (@code_order)) {
+        my @clonecodes = split(/\+/,$domdefclone);
+        my $instcoderegexp ='^';
+        foreach my $item (@code_order) {
+            if (grep(/^\Q$item\E$/,@clonecodes)) {
+                $instcoderegexp .= '('.$codedefaults{$item}.')';
+            } else {
+                $instcoderegexp .= $codedefaults{$item};
+            }
+        }
+        $instcoderegexp .= '$';
+        my (@from,@to);
+        eval {
+            (@from) = ($clonefromcode =~ /$instcoderegexp/);
+            (@to) = ($clonetocode =~ /$instcoderegexp/);
+        };
+        if ((@from > 0) && (@to > 0)) {
+            my @diffs = &Apache::loncommon::compare_arrays(\@from,\@to);
+            if (!@diffs) {
+                $canclone = 1;
+            }
+        }
+    }
+    return $canclone;
+}
+
 # ------------------------------------------------------- Course Group routines
 
 sub get_coursegroups {
@@ -10974,9 +11140,15 @@ sub deversion {
 # ------------------------------------------------------ Return symb list entry
 
 sub symbread {
-    my ($thisfn,$donotrecurse)=@_;
+    my ($thisfn,$donotrecurse,$ignorecachednull,$checkforblock,$possibles)=@_;
     my $cache_str='request.symbread.cached.'.$thisfn;
-    if (defined($env{$cache_str})) { return $env{$cache_str}; }
+    if (defined($env{$cache_str})) {
+        if ($ignorecachednull) {
+            return $env{$cache_str} unless ($env{$cache_str} eq '');
+        } else {
+            return $env{$cache_str};
+        }
+    }
 # no filename provided? try from environment
     unless ($thisfn) {
         if ($env{'request.symb'}) {
@@ -11038,18 +11210,46 @@ sub symbread {
 		     my ($mapid,$resid)=split(/\./,$ids);
 		     $syval=&encode_symb($bighash{'map_id_'.$mapid},
 						    $resid,$thisfn);
-                 } elsif (!$donotrecurse) {
+                     if (ref($possibles) eq 'HASH') {
+                         $possibles->{$syval} = 1;
+                     }
+                     if ($checkforblock) {
+                         my @blockers = &has_comm_blocking('bre',$syval,$bighash{'src_'.$ids});
+                         if (@blockers) {
+                             $syval = '';
+                             return;
+                         }
+                     }
+                 } elsif ((!$donotrecurse) || ($checkforblock) || (ref($possibles) eq 'HASH')) {
 # ------------------------------------------ There is more than one possibility
                      my $realpossible=0;
                      foreach my $id (@possibilities) {
 			 my $file=$bighash{'src_'.$id};
-                         if (&allowed('bre',$file)) {
-         		    my ($mapid,$resid)=split(/\./,$id);
-                            if ($bighash{'map_type_'.$mapid} ne 'page') {
-				$realpossible++;
-                                $syval=&encode_symb($bighash{'map_id_'.$mapid},
-						    $resid,$thisfn);
-                            }
+                         my $canaccess;
+                         if (($donotrecurse) || ($checkforblock) || (ref($possibles) eq 'HASH')) {
+                             $canaccess = 1;
+                         } else {
+                             $canaccess = &allowed('bre',$file);
+                         }
+                         if ($canaccess) {
+         		     my ($mapid,$resid)=split(/\./,$id);
+                             if ($bighash{'map_type_'.$mapid} ne 'page') {
+                                 my $poss_syval=&encode_symb($bighash{'map_id_'.$mapid},
+                                                             $resid,$thisfn);
+                                 if (ref($possibles) eq 'HASH') {
+                                     $possibles->{$syval} = 1;
+                                 }
+                                 if ($checkforblock) {
+                                     my @blockers = &has_comm_blocking('bre',$poss_syval,$file);
+                                     unless (@blockers > 0) {
+                                         $syval = $poss_syval;
+                                         $realpossible++;
+                                     }
+                                 } else {
+                                     $syval = $poss_syval;
+                                     $realpossible++;
+                                 }
+                             }
 			 }
                      }
 		     if ($realpossible!=1) { $syval=''; }
@@ -11057,7 +11257,7 @@ sub symbread {
                      $syval='';
                  }
 	      }
-              untie(%bighash)
+              untie(%bighash);
            }
         }
         if ($syval) {
@@ -12691,7 +12891,7 @@ were new keys. I.E. 1:foo will become 1:
 Calling convention:
 
  my %record=&Apache::lonnet::restore($symb,$courseid,$domain,$uname);
- &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$uname);
+ &Apache::lonnet::cstore(\%newrecord,$symb,$courseid,$domain,$uname,$laststore);
 
 For more detailed information, see lonnet specific documentation.
 
@@ -12827,13 +13027,29 @@ escaped strings of the action recorded i
 
 =item *
 
-allowed($priv,$uri,$symb,$role) : check for a user privilege; returns codes for allowed actions
+allowed($priv,$uri,$symb,$role,$clientip,$noblockcheck) : check for a user privilege; 
+returns codes for allowed actions.
+
+The first argument is required, all others are optional.
+
+$priv is the privilege being checked.
+$uri contains additional information about what is being checked for access (e.g.,
+URL, course ID etc.).
+$symb is the unique resource instance identifier in a course; if needed,
+but not provided, it will be retrieved via a call to &symbread().
+$role is the role for which a priv is being checked (only used if priv is evb).
+$clientip is the user's IP address (only used when checking for access to portfolio
+files).
+$noblockcheck, if true, skips calls to &has_comm_blocking() for the bre priv. This
+prevents recursive calls to &allowed.
+
  F: full access
  U,I,K: authentication modes (cxx only)
  '': forbidden
  1: user needs to choose course
  2: browse allowed
  A: passphrase authentication needed
+ B: access temporarily blocked because of a blocking event in a course.
 
 =item *
 
@@ -13227,7 +13443,20 @@ will be stored for query
 
 =item *
 
-symbread($filename) : return symbolic list entry (filename argument optional);
+symbread($filename,$donotrecurse,$ignorecachednull,$checkforblock,$possibles) :
+return symbolic list entry (all arguments optional).
+
+Args: filename is the filename (including path) for the file for which a symb
+is required; donotrecurse, if true will prevent calls to allowed() being made
+to check access status if more than one resource was found in the bighash
+(see rev. 1.249) to avoid an infinite loop if an ambiguous resource is part of
+a randompick); ignorecachednull, if true will prevent a symb of '' being
+returned if $env{$cache_str} is defined as ''; checkforblock if true will
+cause possible symbs to be checked to determine if they are subject to content
+blocking, if so they will not be included as possible symbs; possibles is a
+ref to a hash, which, as a side effect, will be populated with all possible
+symbs (content blocking not tested).
+
 returns the data handle
 
 =item *
@@ -13328,15 +13557,21 @@ homeserver.
 
 =item *
 
-store($storehash,$symb,$namespace,$udom,$uname) : stores hash permanently
-for this url; hashref needs to be given and should be a \%hashname; the
-remaining args aren't required and if they aren't passed or are '' they will
-be derived from the env
+store($storehash,$symb,$namespace,$udom,$uname,$laststore) : stores hash 
+permanently for this url; hashref needs to be given and should be a \%hashname;
+the remaining args aren't required and if they aren't passed or are '' they will
+be derived from the env (with the exception of $laststore, which is an
+optional arg used when a user's submission is stored in grading).
+$laststore is $version=$timestamp, where $version is the most recent version
+number retrieved for the corresponding $symb in the $namespace db file, and
+$timestamp is the timestamp for that transaction (UNIX time).
+$laststore is currently only passed when cstore() is called by
+structuretags::finalize_storage().
 
 =item *
 
-cstore($storehash,$symb,$namespace,$udom,$uname) : same as store but
-uses critical subroutine
+cstore($storehash,$symb,$namespace,$udom,$uname,$laststore) : same as store 
+but uses critical subroutine
 
 =item *
 
@@ -13528,7 +13763,8 @@ for course's uploaded content.
 =over
 
 =item
-canuse_pdfforms, officialcredits, unofficialcredits, textbookcredits, officialquota, unofficialquota, communityquota, textbookquota
+canuse_pdfforms, officialcredits, unofficialcredits, textbookcredits, officialquota, unofficialquota,
+communityquota, textbookquota
 
 =back